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