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