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 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 /* 29 * Portions of this source code were derived from Berkeley 30 * 4.3 BSD under license from the Regents of the University of 31 * California. 32 */ 33 34 #pragma ident "%Z%%M% %I% %E% SMI" 35 36 #include <stdio.h> 37 #include <netinet/in.h> 38 #include <netinet/tcp.h> 39 #include <netinet/udp.h> 40 #include <inttypes.h> 41 #include <sys/types.h> 42 #include <rpc/trace.h> 43 #include <tiuser.h> 44 #include <sys/socket.h> 45 #include <net/if.h> 46 #include <sys/sockio.h> 47 #include <rpc/rpc.h> 48 #include <sys/tl.h> 49 #include <sys/stropts.h> 50 #include <errno.h> 51 #include <libintl.h> 52 #include <strings.h> 53 #include <syslog.h> 54 #include <unistd.h> 55 #include <ucred.h> 56 #include <alloca.h> 57 #include <stdlib.h> 58 #include <zone.h> 59 60 extern char *inet_ntop(int, const void *, char *, size_t); 61 extern bool_t __svc_get_door_ucred(const SVCXPRT *, ucred_t *); 62 63 64 /* 65 * This routine is typically called on the server side if the server 66 * wants to know the caller ucred. Called typically by rpcbind. It 67 * depends upon the t_optmgmt call to local transport driver so that 68 * return the uid value in options in T_CONN_IND, T_CONN_CON and 69 * T_UNITDATA_IND. 70 * With the advent of the credential in the mblk, this is simply 71 * extended to all transports when the packet travels over the 72 * loopback network; for UDP we use a special socket option and for 73 * tcp we don't need to do any setup, we just call getpeerucred() 74 * later. 75 */ 76 77 /* 78 * Version for Solaris with new local transport code and ucred. 79 */ 80 int 81 __rpc_negotiate_uid(fd) 82 int fd; 83 { 84 struct strioctl strioc; 85 unsigned int set = 1; 86 87 trace2(TR___rpc_negotiate_uid, 0, fd); 88 89 /* For tcp we use getpeerucred and it needs no initialization. */ 90 if (ioctl(fd, I_FIND, "tcp") > 0) 91 return (0); 92 93 strioc.ic_cmd = TL_IOC_UCREDOPT; 94 strioc.ic_timout = -1; 95 strioc.ic_len = (int)sizeof (unsigned int); 96 strioc.ic_dp = (char *)&set; 97 98 if (ioctl(fd, I_STR, &strioc) == -1 && 99 __rpc_tli_set_options(fd, SOL_SOCKET, SO_RECVUCRED, 1) == -1) { 100 syslog(LOG_ERR, "rpc_negotiate_uid (%s): %m", 101 "ioctl:I_STR:TL_IOC_UCREDOPT/SO_RECVUCRED"); 102 trace2(TR___rpc_negotiate_uid, 1, fd); 103 return (-1); 104 } 105 106 trace2(TR___rpc_negotiate_uid, 1, fd); 107 return (0); 108 } 109 110 void 111 svc_fd_negotiate_ucred(int fd) 112 { 113 (void) __rpc_negotiate_uid(fd); 114 } 115 116 117 /* 118 * This returns the ucred of the caller. It assumes that the optbuf 119 * information is stored at xprt->xp_p2. 120 * There are three distinct cases: the option buffer is headed 121 * with a "struct opthdr" and the credential option is the only 122 * one, or it's a T_opthdr and our option may follow others; or there 123 * are no options and we attempt getpeerucred(). 124 */ 125 static int 126 find_ucred_opt(const SVCXPRT *trans, ucred_t *uc, bool_t checkzone) 127 { 128 /* LINTED pointer alignment */ 129 struct netbuf *abuf = (struct netbuf *)trans->xp_p2; 130 char *bufp, *maxbufp; 131 struct opthdr *opth; 132 static zoneid_t myzone = MIN_ZONEID - 1; /* invalid */ 133 134 if (abuf == NULL || abuf->buf == NULL) { 135 if (getpeerucred(trans->xp_fd, &uc) == 0) 136 goto verifyzone; 137 return (-1); 138 } 139 140 #ifdef RPC_DEBUG 141 syslog(LOG_INFO, "find_ucred_opt %p %x", abuf->buf, abuf->len); 142 #endif 143 opth = (struct opthdr *)abuf->buf; 144 if (opth->level == TL_PROT_LEVEL && 145 opth->name == TL_OPT_PEER_UCRED && 146 opth->len + sizeof (*opth) == abuf->len) { 147 #ifdef RPC_DEBUG 148 syslog(LOG_INFO, "find_ucred_opt (opthdr): OK!"); 149 #endif 150 (void) memcpy(uc, &opth[1], opth->len); 151 /* 152 * Always from inside our zone because zones use a separate name 153 * space for loopback; at this time, the kernel may send a 154 * packet pretending to be from the global zone when it's 155 * really from our zone so we skip the zone check. 156 */ 157 return (0); 158 } 159 160 bufp = abuf->buf; 161 maxbufp = bufp + abuf->len; 162 163 while (bufp + sizeof (struct T_opthdr) < maxbufp) { 164 struct T_opthdr *opt = (struct T_opthdr *)bufp; 165 166 #ifdef RPC_DEBUG 167 syslog(LOG_INFO, "find_ucred_opt opt: %p %x, %d %d", opt, 168 opt->len, opt->name, opt->level); 169 #endif 170 if (opt->len > maxbufp - bufp || opt->len < 0 || (opt->len & 3)) 171 return (-1); 172 if (opt->level == SOL_SOCKET && opt->name == SCM_UCRED && 173 opt->len - sizeof (struct T_opthdr) <= ucred_size()) { 174 #ifdef RPC_DEBUG 175 syslog(LOG_INFO, "find_ucred_opt (T_opthdr): OK!"); 176 #endif 177 (void) memcpy(uc, &opt[1], 178 opt->len - sizeof (struct T_opthdr)); 179 goto verifyzone; 180 } 181 bufp += opt->len; 182 } 183 if (getpeerucred(trans->xp_fd, &uc) != 0) 184 return (-1); 185 verifyzone: 186 if (!checkzone) 187 return (0); 188 189 if (myzone == MIN_ZONEID - 1) 190 myzone = getzoneid(); 191 192 /* Return 0 only for the local zone */ 193 return (ucred_getzoneid(uc) == myzone ? 0 : -1); 194 } 195 196 /* 197 * Version for Solaris with new local transport code 198 */ 199 int 200 __rpc_get_local_uid(SVCXPRT *trans, uid_t *uid_out) 201 { 202 ucred_t *uc = alloca(ucred_size()); 203 int err; 204 205 trace1(TR___rpc_get_local_uid, 0); 206 207 /* LINTED - pointer alignment */ 208 if (svc_type(trans) == SVC_DOOR) 209 err = __svc_get_door_ucred(trans, uc) == FALSE; 210 else 211 err = find_ucred_opt(trans, uc, B_TRUE); 212 213 if (err != 0) { 214 trace1(TR___rpc_get_local_uid, 1); 215 return (-1); 216 } 217 *uid_out = ucred_geteuid(uc); 218 219 trace1(TR___rpc_get_local_uid, 1); 220 return (0); 221 } 222 223 /* 224 * Return local credentials. 225 */ 226 bool_t 227 __rpc_get_local_cred(SVCXPRT *xprt, svc_local_cred_t *lcred) 228 { 229 ucred_t *uc = alloca(ucred_size()); 230 int err; 231 232 /* LINTED - pointer alignment */ 233 if (svc_type(xprt) == SVC_DOOR) 234 err = __svc_get_door_ucred(xprt, uc) == FALSE; 235 else 236 err = find_ucred_opt(xprt, uc, B_TRUE); 237 238 if (err != 0) 239 return (FALSE); 240 241 lcred->euid = ucred_geteuid(uc); 242 lcred->egid = ucred_getegid(uc); 243 lcred->ruid = ucred_getruid(uc); 244 lcred->rgid = ucred_getrgid(uc); 245 lcred->pid = ucred_getpid(uc); 246 return (TRUE); 247 } 248 249 /* 250 * Return local ucred. 251 */ 252 int 253 svc_getcallerucred(const SVCXPRT *trans, ucred_t **uc) 254 { 255 ucred_t *ucp = *uc; 256 int err; 257 258 if (ucp == NULL) { 259 ucp = malloc(ucred_size()); 260 if (ucp == NULL) 261 return (-1); 262 } 263 264 /* LINTED - pointer alignment */ 265 if (svc_type(trans) == SVC_DOOR) 266 err = __svc_get_door_ucred(trans, ucp) == FALSE; 267 else 268 err = find_ucred_opt(trans, ucp, B_FALSE); 269 270 if (err != 0) { 271 if (*uc == NULL) 272 free(ucp); 273 return (-1); 274 } 275 276 if (*uc == NULL) 277 *uc = ucp; 278 279 return (0); 280 } 281 282 283 /* 284 * get local ip address 285 */ 286 int 287 __rpc_get_ltaddr(struct netbuf *nbufp, struct netbuf *ltaddr) 288 { 289 unsigned int total_optlen; 290 struct T_opthdr *opt, *opt_start = NULL, *opt_end; 291 struct sockaddr_in *ipv4sa; 292 struct sockaddr_in6 *ipv6sa; 293 int s; 294 struct sioc_addrreq areq; 295 296 if (nbufp == (struct netbuf *)0 || ltaddr == (struct netbuf *)0) { 297 t_errno = TBADOPT; 298 return (-1); 299 } 300 301 total_optlen = nbufp->len; 302 if (total_optlen == 0) 303 return (1); 304 305 /* LINTED pointer alignment */ 306 opt_start = (struct T_opthdr *)nbufp->buf; 307 if (opt_start == NULL) { 308 t_errno = TBADOPT; 309 return (-1); 310 } 311 312 /* Make sure the start of the buffer is aligned */ 313 if (!(__TPI_TOPT_ISALIGNED(opt_start))) { 314 t_errno = TBADOPT; 315 return (-1); 316 } 317 318 /* LINTED pointer alignment */ 319 opt_end = (struct T_opthdr *)((uchar_t *)opt_start + total_optlen); 320 opt = opt_start; 321 322 /* 323 * Look for the desired option header 324 */ 325 do { 326 if (((uchar_t *)opt + sizeof (struct T_opthdr)) > 327 (uchar_t *)opt_end) { 328 t_errno = TBADOPT; 329 return (-1); 330 } 331 if (opt->len < sizeof (struct T_opthdr)) { 332 t_errno = TBADOPT; 333 return (-1); 334 } 335 if (((uchar_t *)opt + opt->len) > (uchar_t *)opt_end) { 336 t_errno = TBADOPT; 337 return (-1); 338 } 339 switch (opt->level) { 340 case IPPROTO_IP: 341 if (opt->name == IP_RECVDSTADDR) { 342 struct sockaddr_in v4tmp; 343 344 opt++; 345 if (((uchar_t *)opt + sizeof (struct in_addr)) > 346 (uchar_t *)opt_end) { 347 t_errno = TBADOPT; 348 return (-1); 349 } 350 /* LINTED pointer alignment */ 351 bzero(&v4tmp, sizeof (v4tmp)); 352 v4tmp.sin_family = AF_INET; 353 v4tmp.sin_addr = *(struct in_addr *)opt; 354 #ifdef RPC_DEBUG 355 { 356 struct in_addr ia; 357 char str[INET_ADDRSTRLEN]; 358 359 ia = *(struct in_addr *)opt; 360 (void) inet_ntop(AF_INET, &ia, 361 str, sizeof (str)); 362 syslog(LOG_INFO, 363 "__rpc_get_ltaddr for IP_RECVDSTADDR: %s", 364 str); 365 } 366 #endif 367 if ((s = open("/dev/udp", O_RDONLY)) < 0) { 368 #ifdef RPC_DEBUG 369 syslog(LOG_ERR, "__rpc_get_ltaddr: " 370 "dev udp open failed"); 371 #endif 372 return (1); 373 } 374 375 memcpy(&areq.sa_addr, (void *)&v4tmp, 376 sizeof (v4tmp)); 377 areq.sa_res = -1; 378 if (ioctl(s, SIOCTMYADDR, (caddr_t)&areq) < 0) { 379 syslog(LOG_ERR, 380 "get_ltaddr:ioctl for udp failed"); 381 close(s); 382 return (1); 383 } 384 close(s); 385 if (areq.sa_res == 1) { 386 ipv4sa = (struct sockaddr_in *)ltaddr->buf; 387 ipv4sa->sin_family = AF_INET; 388 ipv4sa->sin_addr = *(struct in_addr *)opt; 389 return (0); 390 } else 391 return (1); 392 393 } 394 break; 395 case IPPROTO_IPV6: 396 if (opt->name == IPV6_PKTINFO) { 397 struct sockaddr_in6 v6tmp; 398 opt++; 399 if (((uchar_t *)opt + 400 sizeof (struct in6_pktinfo)) > 401 (uchar_t *)opt_end) { 402 t_errno = TBADOPT; 403 return (-1); 404 } 405 bzero(&v6tmp, sizeof (v6tmp)); 406 v6tmp.sin6_family = AF_INET6; 407 v6tmp.sin6_addr = 408 ((struct in6_pktinfo *)opt)->ipi6_addr; 409 #ifdef RPC_DEBUG 410 { 411 struct in6_pktinfo *in6_pkt; 412 char str[INET6_ADDRSTRLEN]; 413 414 in6_pkt = (struct in6_pktinfo *)opt; 415 (void) inet_ntop(AF_INET6, &in6_pkt->ipi6_addr, 416 str, sizeof (str)); 417 syslog(LOG_INFO, 418 "__rpc_get_ltaddr for IPV6_PKTINFO: %s", 419 str); 420 } 421 #endif 422 if ((s = open("/dev/udp6", O_RDONLY)) < 0) { 423 #ifdef RPC_DEBUG 424 syslog(LOG_ERR, "__rpc_get_ltaddr: " 425 "dev udp6 open failed"); 426 #endif 427 return (1); 428 } 429 430 memcpy(&areq.sa_addr, (void *)&v6tmp, 431 sizeof (v6tmp)); 432 areq.sa_res = -1; 433 if (ioctl(s, SIOCTMYADDR, (caddr_t)&areq) < 0) { 434 syslog(LOG_ERR, 435 "get_ltaddr:ioctl for udp6 failed"); 436 close(s); 437 return (1); 438 } 439 close(s); 440 if (areq.sa_res == 1) { 441 ipv6sa = (struct sockaddr_in6 *)ltaddr->buf; 442 ipv6sa->sin6_family = AF_INET6; 443 ipv6sa->sin6_addr = 444 ((struct in6_pktinfo *)opt)->ipi6_addr; 445 446 return (0); 447 } else 448 return (1); 449 } 450 break; 451 default: 452 break; 453 } 454 /* LINTED improper alignment */ 455 opt = (struct T_opthdr *)((uchar_t *)opt + 456 __TPI_ALIGN(opt->len)); 457 } while (opt < opt_end); 458 return (1); 459 } 460 461 #define __TRANSPORT_INDSZ 128 462 463 int 464 __rpc_tli_set_options(int fd, int optlevel, int optname, int optval) 465 { 466 struct t_optmgmt oreq, ores; 467 struct opthdr *topt; 468 int *ip; 469 int optsz; 470 char buf[__TRANSPORT_INDSZ]; 471 472 473 switch (optname) { 474 case SO_DONTLINGER: { 475 struct linger *ling; 476 /* LINTED */ 477 ling = (struct linger *) 478 (buf + sizeof (struct opthdr)); 479 ling->l_onoff = 0; 480 optsz = sizeof (struct linger); 481 break; 482 } 483 484 case SO_LINGER: { 485 struct linger *ling; 486 /* LINTED */ 487 ling = (struct linger *) 488 (buf + sizeof (struct opthdr)); 489 ling->l_onoff = 1; 490 ling->l_linger = (int)optval; 491 optsz = sizeof (struct linger); 492 break; 493 } 494 case IP_RECVDSTADDR: 495 case IPV6_RECVPKTINFO: 496 case SO_DEBUG: 497 case SO_KEEPALIVE: 498 case SO_DONTROUTE: 499 case SO_USELOOPBACK: 500 case SO_REUSEADDR: 501 case SO_DGRAM_ERRIND: 502 case SO_RECVUCRED: 503 case TCP_EXCLBIND: 504 case UDP_EXCLBIND: 505 /* LINTED */ 506 ip = (int *)(buf + sizeof (struct opthdr)); 507 *ip = optval; 508 optsz = sizeof (int); 509 break; 510 default: 511 return (-1); 512 } 513 514 /* LINTED */ 515 topt = (struct opthdr *)buf; 516 topt->level = optlevel; 517 topt->name = optname; 518 topt->len = optsz; 519 oreq.flags = T_NEGOTIATE; 520 oreq.opt.len = sizeof (struct opthdr) + optsz; 521 oreq.opt.buf = buf; 522 523 ores.flags = 0; 524 ores.opt.buf = buf; 525 ores.opt.maxlen = __TRANSPORT_INDSZ; 526 if (t_optmgmt(fd, &oreq, &ores) < 0 || 527 ores.flags != T_SUCCESS) { 528 return (-1); 529 } 530 return (0); 531 } 532 533 /* 534 * Format an error message corresponding to the given TLI and system error 535 * codes. 536 */ 537 538 void 539 __tli_sys_strerror(char *buf, size_t buflen, int tli_err, int sys_err) 540 { 541 char *errorstr; 542 543 if (tli_err == TSYSERR) { 544 errorstr = strerror(sys_err); 545 if (errorstr == NULL) 546 (void) snprintf(buf, buflen, 547 dgettext(__nsl_dom, 548 "Unknown system error %d"), 549 sys_err); 550 else 551 (void) strlcpy(buf, errorstr, buflen); 552 } else { 553 errorstr = t_strerror(tli_err); 554 strlcpy(buf, errorstr, buflen); 555 } 556 } 557