xref: /illumos-gate/usr/src/lib/libnsl/rpc/clnt_door.c (revision 0616c1c344750b61fbfd80b1185254b28a9fe60d)
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 *
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
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, &params) < 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
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
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
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
329 clnt_door_abort(CLIENT *cl)
330 {
331 	cl = cl;
332 }
333 
334 static bool_t
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
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 *
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
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