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