4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/types.h>
26 #include <sys/fm/util.h>
36 #include <fmd_rpc_api.h>
37 #include <fmd_rpc_adm.h>
38 #include <rpc/svc_mt.h>
41 #include <fmd_error.h>
42 #include <fmd_thread.h>
48 * Define range of transient RPC program numbers to use for transient bindings.
49 * These are defined in the Solaris ONC+ Developer's Guide, Appendix B, but
50 * are cleverly not defined in any ONC+ standard system header file.
52 #define RPC_TRANS_MIN 0x40000000
53 #define RPC_TRANS_MAX 0x5fffffff
56 * We use our own private version of svc_create() which registers our services
57 * only on loopback transports and enables an option whereby Solaris ucreds
58 * are associated with each connection, permitting us to check privilege bits.
61 fmd_rpc_svc_create_local(void (*disp
)(struct svc_req
*, SVCXPRT
*),
62 rpcprog_t prog
, rpcvers_t vers
, uint_t ssz
, uint_t rsz
, int force
)
64 struct netconfig
*ncp
;
73 if ((hdl
= setnetconfig()) == NULL
) {
74 fmd_error(EFMD_RPC_REG
, "failed to iterate over "
75 "netconfig database: %s\n", nc_sperror());
76 return (fmd_set_errno(EFMD_RPC_REG
));
80 svc_unreg(prog
, vers
); /* clear stale rpcbind registrations */
82 buf
.buf
= alloca(_SS_MAXSIZE
);
83 buf
.maxlen
= _SS_MAXSIZE
;
86 while ((ncp
= getnetconfig(hdl
)) != NULL
) {
87 if (strcmp(ncp
->nc_protofmly
, NC_LOOPBACK
) != 0)
90 if (!force
&& rpcb_getaddr(prog
, vers
, ncp
, &buf
, HOST_SELF
)) {
91 (void) endnetconfig(hdl
);
92 return (fmd_set_errno(EFMD_RPC_BOUND
));
95 if ((fd
= t_open(ncp
->nc_device
, O_RDWR
, NULL
)) == -1) {
96 fmd_error(EFMD_RPC_REG
, "failed to open %s: %s\n",
97 ncp
->nc_device
, t_strerror(t_errno
));
101 svc_fd_negotiate_ucred(fd
); /* enable ucred option on xprt */
103 if ((xprt
= svc_tli_create(fd
, ncp
, NULL
, ssz
, rsz
)) == NULL
) {
108 if (svc_reg(xprt
, prog
, vers
, disp
, ncp
) == FALSE
) {
109 fmd_error(EFMD_RPC_REG
, "failed to register "
110 "rpc service on %s\n", ncp
->nc_netid
);
118 (void) endnetconfig(hdl
);
121 * If we failed to register services (n == 0) because rpcbind is down,
122 * then check to see if the RPC door file exists before attempting an
123 * svc_door_create(), which cleverly destroys any existing door file.
124 * The RPC APIs have no stable errnos, so we use rpcb_gettime() as a
125 * hack to determine if rpcbind itself is down.
127 if (!force
&& n
== 0 && rpcb_gettime(HOST_SELF
, &tm
) == FALSE
&&
128 snprintf(door
, sizeof (door
), RPC_DOOR_RENDEZVOUS
,
129 prog
, vers
) > 0 && access(door
, F_OK
) == 0)
130 return (fmd_set_errno(EFMD_RPC_BOUND
));
133 * Attempt to create a door server for the RPC program as well. Limit
134 * the maximum request size for the door transport to the receive size.
136 if ((xprt
= svc_door_create(disp
, prog
, vers
, ssz
)) == NULL
) {
137 fmd_error(EFMD_RPC_REG
, "failed to create door for "
138 "rpc service 0x%lx/0x%lx\n", prog
, vers
);
140 (void) svc_control(xprt
, SVCSET_CONNMAXREC
, &rsz
);
148 fmd_rpc_svc_init(void (*disp
)(struct svc_req
*, SVCXPRT
*),
149 const char *name
, const char *path
, const char *prop
,
150 rpcprog_t pmin
, rpcprog_t pmax
, rpcvers_t vers
,
151 uint_t sndsize
, uint_t rcvsize
, int force
)
157 for (prog
= pmin
; prog
<= pmax
; prog
++) {
158 if (fmd_rpc_svc_create_local(disp
, prog
, vers
,
159 sndsize
, rcvsize
, force
) > 0) {
160 fmd_dprintf(FMD_DBG_RPC
, "registered %s rpc service "
161 "as 0x%lx.%lx\n", name
, prog
, vers
);
164 * To aid simulator scripts, save our RPC "digits" in
165 * the specified file for rendezvous with libfmd_adm.
167 if (path
!= NULL
&& (fp
= fopen(path
, "w")) != NULL
) {
168 (void) fprintf(fp
, "%ld\n", prog
);
172 (void) snprintf(buf
, sizeof (buf
), "%ld", prog
);
173 (void) fmd_conf_setprop(fmd
.d_conf
, prop
, buf
);
179 return (-1); /* errno is set for us */
185 int err
, prog
, mode
= RPC_SVC_MT_USER
;
186 uint64_t sndsize
= 0, rcvsize
= 0;
189 if (rpc_control(RPC_SVC_MTMODE_SET
, &mode
) == FALSE
)
190 fmd_panic("failed to enable user-MT rpc mode");
192 (void) fmd_conf_getprop(fmd
.d_conf
, "rpc.sndsize", &sndsize
);
193 (void) fmd_conf_getprop(fmd
.d_conf
, "rpc.rcvsize", &rcvsize
);
196 * Infer whether we are the "default" fault manager or an alternate one
197 * based on whether the initial setting of rpc.adm.prog is non-zero.
199 (void) fmd_conf_getprop(fmd
.d_conf
, "rpc.adm.prog", &prog
);
200 (void) fmd_conf_getprop(fmd
.d_conf
, "rpc.adm.path", &s
);
203 err
= fmd_rpc_svc_init(fmd_adm_1
, "FMD_ADM", s
, "rpc.adm.prog",
204 FMD_ADM
, FMD_ADM
, FMD_ADM_VERSION_1
,
205 (uint_t
)sndsize
, (uint_t
)rcvsize
, TRUE
);
207 err
= fmd_rpc_svc_init(fmd_adm_1
, "FMD_ADM", s
, "rpc.adm.prog",
208 RPC_TRANS_MIN
, RPC_TRANS_MAX
, FMD_ADM_VERSION_1
,
209 (uint_t
)sndsize
, (uint_t
)rcvsize
, FALSE
);
213 fmd_error(EFMD_EXIT
, "failed to create rpc server bindings");
215 if (fmd_thread_create(fmd
.d_rmod
, (fmd_thread_f
*)svc_run
, 0) == NULL
)
216 fmd_error(EFMD_EXIT
, "failed to create rpc server thread");
224 svc_exit(); /* force svc_run() threads to exit */
226 (void) fmd_conf_getprop(fmd
.d_conf
, "rpc.adm.prog", &prog
);
227 svc_unreg(prog
, FMD_ADM_VERSION_1
);
229 (void) fmd_conf_getprop(fmd
.d_conf
, "rpc.api.prog", &prog
);
230 svc_unreg(prog
, FMD_API_VERSION_1
);
234 * Utillity function to fetch the XPRT's ucred and determine if we should deny
235 * the request. For now, we implement a simple policy of rejecting any caller
236 * who does not have the PRIV_SYS_ADMIN bit in their Effective privilege set,
237 * unless the caller is loading a module, which requires all privileges.
240 fmd_rpc_deny(struct svc_req
*rqp
)
242 ucred_t
*ucp
= alloca(ucred_size());
243 const priv_set_t
*psp
;
246 (void) pthread_mutex_lock(&fmd
.d_fmd_lock
);
247 while (!fmd
.d_booted
)
248 (void) pthread_cond_wait(&fmd
.d_fmd_cv
,
250 (void) pthread_mutex_unlock(&fmd
.d_fmd_lock
);
253 if (svc_getcallerucred(rqp
->rq_xprt
, &ucp
) != 0 ||
254 (psp
= ucred_getprivset(ucp
, PRIV_EFFECTIVE
)) == NULL
)
255 return (1); /* deny access if we can't get credentials */
259 * For convenience of testing, we only require all privileges for a
260 * module load when running a non-DEBUG fault management daemon.
262 if (rqp
->rq_proc
== FMD_ADM_MODLOAD
)
263 return (!priv_isfullset(psp
));
265 return (!priv_ismember(psp
, PRIV_SYS_ADMIN
));