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