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