1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * clnt_doors.c, Client side for doors IPC based RPC.
29 */
30
31 #include "mt.h"
32 #include "rpc_mt.h"
33 #include <rpc/rpc.h>
34 #include <errno.h>
35 #include <sys/poll.h>
36 #include <syslog.h>
37 #include <sys/types.h>
38 #include <sys/kstat.h>
39 #include <sys/time.h>
40 #include <door.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <alloca.h>
45 #include <rpc/svc_mt.h>
46 #include <sys/mman.h>
47 #include <atomic.h>
48
49
50 extern bool_t xdr_opaque_auth(XDR *, struct opaque_auth *);
51
52 static struct clnt_ops *clnt_door_ops();
53
54 extern int __rpc_default_door_buf_size;
55 extern int __rpc_min_door_buf_size;
56
57 /*
58 * Private data kept per client handle
59 */
60 struct cu_data {
61 int cu_fd; /* door fd */
62 bool_t cu_closeit; /* close it on destroy */
63 struct rpc_err cu_error;
64 uint_t cu_xdrpos;
65 uint_t cu_sendsz; /* send size */
66 char cu_header[32]; /* precreated header */
67 };
68
69 /*
70 * Door IPC based client creation routine.
71 *
72 * NB: The rpch->cl_auth is initialized to null authentication.
73 * Caller may wish to set this something more useful.
74 *
75 * sendsz is the maximum allowable packet size that can be sent.
76 * 0 will cause default to be used.
77 */
78 CLIENT *
clnt_door_create(const rpcprog_t program,const rpcvers_t version,const uint_t sendsz)79 clnt_door_create(const rpcprog_t program, const rpcvers_t version,
80 const uint_t sendsz)
81 {
82 CLIENT *cl = NULL; /* client handle */
83 struct cu_data *cu = NULL; /* private data */
84 struct rpc_msg call_msg;
85 char rendezvous[64];
86 int did;
87 struct door_info info;
88 XDR xdrs;
89 struct timeval now;
90 uint_t ssz;
91
92 (void) sprintf(rendezvous, RPC_DOOR_RENDEZVOUS, (int)program,
93 (int)version);
94 if ((did = open(rendezvous, O_RDONLY, 0)) < 0) {
95 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
96 rpc_createerr.cf_error.re_errno = errno;
97 rpc_createerr.cf_error.re_terrno = 0;
98 return (NULL);
99 }
100
101 if (door_info(did, &info) < 0 || (info.di_attributes & DOOR_REVOKED)) {
102 (void) close(did);
103 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
104 rpc_createerr.cf_error.re_errno = errno;
105 rpc_createerr.cf_error.re_terrno = 0;
106 return (NULL);
107 }
108
109 /*
110 * Determine send size
111 */
112 if (sendsz < __rpc_min_door_buf_size)
113 ssz = __rpc_default_door_buf_size;
114 else
115 ssz = RNDUP(sendsz);
116
117 if ((cl = malloc(sizeof (CLIENT))) == NULL ||
118 (cu = malloc(sizeof (*cu))) == NULL) {
119 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
120 rpc_createerr.cf_error.re_errno = errno;
121 goto err;
122 }
123
124 /*
125 * Precreate RPC header for performance reasons.
126 */
127 (void) gettimeofday(&now, NULL);
128 call_msg.rm_xid = getpid() ^ now.tv_sec ^ now.tv_usec;
129 call_msg.rm_call.cb_prog = program;
130 call_msg.rm_call.cb_vers = version;
131 xdrmem_create(&xdrs, cu->cu_header, sizeof (cu->cu_header), XDR_ENCODE);
132 if (!xdr_callhdr(&xdrs, &call_msg)) {
133 rpc_createerr.cf_stat = RPC_CANTENCODEARGS;
134 rpc_createerr.cf_error.re_errno = 0;
135 goto err;
136 }
137 cu->cu_xdrpos = XDR_GETPOS(&xdrs);
138
139 cu->cu_sendsz = ssz;
140 cu->cu_fd = did;
141 cu->cu_closeit = TRUE;
142 cl->cl_ops = clnt_door_ops();
143 cl->cl_private = (caddr_t)cu;
144 cl->cl_auth = authnone_create();
145 cl->cl_tp = strdup(rendezvous);
146 if (cl->cl_tp == NULL) {
147 syslog(LOG_ERR, "clnt_door_create: strdup failed");
148 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
149 rpc_createerr.cf_error.re_errno = errno;
150 goto err;
151 }
152 cl->cl_netid = strdup("door");
153 if (cl->cl_netid == NULL) {
154 syslog(LOG_ERR, "clnt_door_create: strdup failed");
155 if (cl->cl_tp)
156 free(cl->cl_tp);
157 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
158 rpc_createerr.cf_error.re_errno = errno;
159 goto err;
160 }
161 return (cl);
162 err:
163 rpc_createerr.cf_error.re_terrno = 0;
164 if (cl) {
165 free(cl);
166 if (cu)
167 free(cu);
168 }
169 (void) close(did);
170 return (NULL);
171 }
172
173 /* ARGSUSED */
174 static enum clnt_stat
clnt_door_call(CLIENT * cl,rpcproc_t proc,xdrproc_t xargs,caddr_t argsp,xdrproc_t xresults,caddr_t resultsp,struct timeval utimeout)175 clnt_door_call(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp,
176 xdrproc_t xresults, caddr_t resultsp, struct timeval utimeout)
177 {
178 /* LINTED pointer alignment */
179 struct cu_data *cu = (struct cu_data *)cl->cl_private;
180 XDR xdrs;
181 door_arg_t params;
182 char *outbuf_ref;
183 struct rpc_msg reply_msg;
184 bool_t need_to_unmap;
185 uint32_t xid;
186 int nrefreshes = 2; /* number of times to refresh cred */
187
188 rpc_callerr.re_errno = 0;
189 rpc_callerr.re_terrno = 0;
190
191 if ((params.rbuf = alloca(cu->cu_sendsz)) == NULL) {
192 rpc_callerr.re_terrno = 0;
193 rpc_callerr.re_errno = errno;
194 return (rpc_callerr.re_status = RPC_SYSTEMERROR);
195 }
196 outbuf_ref = params.rbuf;
197 params.rsize = cu->cu_sendsz;
198 if ((params.data_ptr = alloca(cu->cu_sendsz)) == NULL) {
199 rpc_callerr.re_terrno = 0;
200 rpc_callerr.re_errno = errno;
201 return (rpc_callerr.re_status = RPC_SYSTEMERROR);
202 }
203
204 call_again:
205 xdrmem_create(&xdrs, params.data_ptr, cu->cu_sendsz, XDR_ENCODE);
206 /* Increment XID (not really needed for RPC over doors...) */
207 /* LINTED pointer alignment */
208 xid = atomic_inc_uint_nv((uint32_t *)cu->cu_header);
209 (void) memcpy(params.data_ptr, cu->cu_header, cu->cu_xdrpos);
210 /* LINTED pointer alignment */
211 *(uint32_t *)params.data_ptr = xid;
212 XDR_SETPOS(&xdrs, cu->cu_xdrpos);
213
214 if ((!XDR_PUTINT32(&xdrs, (int32_t *)&proc)) ||
215 (!AUTH_MARSHALL(cl->cl_auth, &xdrs)) ||
216 (!(*xargs)(&xdrs, argsp))) {
217 return (rpc_callerr.re_status = RPC_CANTENCODEARGS);
218 }
219 params.data_size = (int)XDR_GETPOS(&xdrs);
220
221 params.desc_ptr = NULL;
222 params.desc_num = 0;
223 if (door_call(cu->cu_fd, ¶ms) < 0) {
224 rpc_callerr.re_errno = errno;
225 return (rpc_callerr.re_status = RPC_CANTSEND);
226 }
227
228 if (params.rbuf == NULL || params.rsize == 0) {
229 return (rpc_callerr.re_status = RPC_FAILED);
230 }
231 need_to_unmap = (params.rbuf != outbuf_ref);
232
233 /* LINTED pointer alignment */
234 if (*(uint32_t *)params.rbuf != xid) {
235 rpc_callerr.re_status = RPC_CANTDECODERES;
236 goto done;
237 }
238
239 xdrmem_create(&xdrs, params.rbuf, params.rsize, XDR_DECODE);
240 reply_msg.acpted_rply.ar_verf = _null_auth;
241 reply_msg.acpted_rply.ar_results.where = resultsp;
242 reply_msg.acpted_rply.ar_results.proc = xresults;
243
244 if (xdr_replymsg(&xdrs, &reply_msg)) {
245 if (reply_msg.rm_reply.rp_stat == MSG_ACCEPTED &&
246 reply_msg.acpted_rply.ar_stat == SUCCESS)
247 rpc_callerr.re_status = RPC_SUCCESS;
248 else
249 __seterr_reply(&reply_msg, &rpc_callerr);
250
251 if (rpc_callerr.re_status == RPC_SUCCESS) {
252 if (!AUTH_VALIDATE(cl->cl_auth,
253 &reply_msg.acpted_rply.ar_verf)) {
254 rpc_callerr.re_status = RPC_AUTHERROR;
255 rpc_callerr.re_why = AUTH_INVALIDRESP;
256 }
257 if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
258 xdrs.x_op = XDR_FREE;
259 (void) xdr_opaque_auth(&xdrs,
260 &(reply_msg.acpted_rply.ar_verf));
261 }
262 }
263 /*
264 * If unsuccesful AND error is an authentication error
265 * then refresh credentials and try again, else break
266 */
267 else if (rpc_callerr.re_status == RPC_AUTHERROR) {
268 /*
269 * maybe our credentials need to be refreshed ...
270 */
271 if (nrefreshes-- &&
272 AUTH_REFRESH(cl->cl_auth, &reply_msg)) {
273 if (need_to_unmap)
274 (void) munmap(params.rbuf,
275 params.rsize);
276 goto call_again;
277 } else
278 /*
279 * We are setting rpc_callerr here given that
280 * libnsl is not reentrant thereby
281 * reinitializing the TSD. If not set here then
282 * success could be returned even though refresh
283 * failed.
284 */
285 rpc_callerr.re_status = RPC_AUTHERROR;
286 }
287 } else
288 rpc_callerr.re_status = RPC_CANTDECODERES;
289
290 done:
291 if (need_to_unmap)
292 (void) munmap(params.rbuf, params.rsize);
293 return (rpc_callerr.re_status);
294 }
295
296 /* ARGSUSED */
297 static enum clnt_stat
clnt_door_send(CLIENT * cl,rpcproc_t proc,xdrproc_t xargs,caddr_t argsp)298 clnt_door_send(CLIENT *cl, rpcproc_t proc, xdrproc_t xargs, caddr_t argsp)
299 {
300 /* send() call not supported on doors */
301
302 rpc_callerr.re_errno = ENOTSUP;
303 rpc_callerr.re_terrno = 0;
304
305 return (rpc_callerr.re_status = RPC_FAILED);
306 }
307
308 static void
clnt_door_geterr(CLIENT * cl,struct rpc_err * errp)309 clnt_door_geterr(CLIENT *cl, struct rpc_err *errp)
310 {
311 /* LINTED pointer alignment */
312 struct cu_data *cu = (struct cu_data *)cl->cl_private;
313
314 *errp = rpc_callerr;
315 }
316
317 /* ARGSUSED */
318 static bool_t
clnt_door_freeres(CLIENT * cl,xdrproc_t xdr_res,caddr_t res_ptr)319 clnt_door_freeres(CLIENT *cl, xdrproc_t xdr_res, caddr_t res_ptr)
320 {
321 XDR xdrs;
322
323 (void) memset(&xdrs, 0, sizeof (xdrs));
324 xdrs.x_op = XDR_FREE;
325 return ((*xdr_res)(&xdrs, res_ptr));
326 }
327
328 static void
clnt_door_abort(CLIENT * cl)329 clnt_door_abort(CLIENT *cl)
330 {
331 cl = cl;
332 }
333
334 static bool_t
clnt_door_control(CLIENT * cl,int request,char * info)335 clnt_door_control(CLIENT *cl, int request, char *info)
336 {
337 /* LINTED pointer alignment */
338 struct cu_data *cu = (struct cu_data *)cl->cl_private;
339
340 switch (request) {
341 case CLSET_FD_CLOSE:
342 cu->cu_closeit = TRUE;
343 return (TRUE);
344
345 case CLSET_FD_NCLOSE:
346 cu->cu_closeit = FALSE;
347 return (TRUE);
348 }
349
350 /* for other requests which use info */
351 if (info == NULL)
352 return (FALSE);
353
354 switch (request) {
355 case CLGET_FD:
356 /* LINTED pointer alignment */
357 *(int *)info = cu->cu_fd;
358 break;
359
360 case CLGET_XID:
361 /*
362 * use the knowledge that xid is the
363 * first element in the call structure *.
364 * This will get the xid of the PREVIOUS call
365 */
366 /* LINTED pointer alignment */
367 *(uint32_t *)info = ntohl(*(uint32_t *)cu->cu_header);
368 break;
369
370 case CLSET_XID:
371 /* This will set the xid of the NEXT call */
372 /* LINTED pointer alignment */
373 *(uint32_t *)cu->cu_header = htonl(*(uint32_t *)info - 1);
374 /* decrement by 1 as clnt_door_call() increments once */
375 break;
376
377 case CLGET_VERS:
378 /*
379 * This RELIES on the information that, in the call body,
380 * the version number field is the fifth field from the
381 * begining of the RPC header. MUST be changed if the
382 * call_struct is changed
383 */
384 /* LINTED pointer alignment */
385 *(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_header +
386 4 * BYTES_PER_XDR_UNIT));
387 break;
388
389 case CLSET_VERS:
390 /* LINTED pointer alignment */
391 *(uint32_t *)(cu->cu_header + 4 * BYTES_PER_XDR_UNIT) =
392 /* LINTED pointer alignment */
393 htonl(*(uint32_t *)info);
394 break;
395
396 case CLGET_PROG:
397 /*
398 * This RELIES on the information that, in the call body,
399 * the program number field is the fourth field from the
400 * begining of the RPC header. MUST be changed if the
401 * call_struct is changed
402 */
403 /* LINTED pointer alignment */
404 *(uint32_t *)info = ntohl(*(uint32_t *)(cu->cu_header +
405 3 * BYTES_PER_XDR_UNIT));
406 break;
407
408 case CLSET_PROG:
409 /* LINTED pointer alignment */
410 *(uint32_t *)(cu->cu_header + 3 * BYTES_PER_XDR_UNIT) =
411 /* LINTED pointer alignment */
412 htonl(*(uint32_t *)info);
413 break;
414
415 default:
416 return (FALSE);
417 }
418 return (TRUE);
419 }
420
421 static void
clnt_door_destroy(CLIENT * cl)422 clnt_door_destroy(CLIENT *cl)
423 {
424 /* LINTED pointer alignment */
425 struct cu_data *cu = (struct cu_data *)cl->cl_private;
426 int cu_fd = cu->cu_fd;
427
428 if (cu->cu_closeit)
429 (void) close(cu_fd);
430 free(cu);
431 if (cl->cl_netid && cl->cl_netid[0])
432 free(cl->cl_netid);
433 if (cl->cl_tp && cl->cl_tp[0])
434 free(cl->cl_tp);
435 free(cl);
436 }
437
438 static struct clnt_ops *
clnt_door_ops(void)439 clnt_door_ops(void)
440 {
441 static struct clnt_ops ops;
442 extern mutex_t ops_lock;
443
444 sig_mutex_lock(&ops_lock);
445 if (ops.cl_call == NULL) {
446 ops.cl_call = clnt_door_call;
447 ops.cl_send = clnt_door_send;
448 ops.cl_abort = clnt_door_abort;
449 ops.cl_geterr = clnt_door_geterr;
450 ops.cl_freeres = clnt_door_freeres;
451 ops.cl_destroy = clnt_door_destroy;
452 ops.cl_control = clnt_door_control;
453 }
454 sig_mutex_unlock(&ops_lock);
455 return (&ops);
456 }
457
458 int
_update_did(CLIENT * cl,int vers)459 _update_did(CLIENT *cl, int vers)
460 {
461 /* LINTED pointer alignment */
462 struct cu_data *cu = (struct cu_data *)cl->cl_private;
463 rpcprog_t prog;
464 char rendezvous[64];
465
466 if (cu->cu_fd >= 0)
467 (void) close(cu->cu_fd);
468 /* Make sure that the right door id is used in door_call. */
469 clnt_control(cl, CLGET_PROG, (void *)&prog);
470 (void) sprintf(rendezvous, RPC_DOOR_RENDEZVOUS, (int)prog, vers);
471 if ((cu->cu_fd = open(rendezvous, O_RDONLY, 0)) < 0) {
472 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
473 rpc_createerr.cf_error.re_errno = errno;
474 rpc_createerr.cf_error.re_terrno = 0;
475 return (0);
476 }
477 free(cl->cl_tp);
478 cl->cl_tp = strdup(rendezvous);
479 if (cl->cl_tp == NULL) {
480 syslog(LOG_ERR, "_update_did: strdup failed");
481 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
482 rpc_createerr.cf_error.re_errno = errno;
483 rpc_createerr.cf_error.re_terrno = 0;
484 return (0);
485 }
486 return (1);
487 }
488