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, ¶ms) < 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