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
__rpc_negotiate_uid(int fd)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
svc_fd_negotiate_ucred(int fd)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
find_ucred_opt(const SVCXPRT * trans,ucred_t * uc,bool_t checkzone)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
__rpc_get_local_uid(SVCXPRT * trans,uid_t * uid_out)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
__rpc_get_local_cred(SVCXPRT * xprt,svc_local_cred_t * lcred)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
svc_getcallerucred(const SVCXPRT * trans,ucred_t ** uc)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
__rpc_get_ltaddr(struct netbuf * nbufp,struct netbuf * ltaddr)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
__rpc_tli_set_options(int fd,int optlevel,int optname,int optval)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 SO_EXCLBIND:
498 case TCP_EXCLBIND:
499 case UDP_EXCLBIND:
500 /* LINTED */
501 ip = (int *)(buf + sizeof (struct opthdr));
502 *ip = optval;
503 optsz = sizeof (int);
504 break;
505 default:
506 return (-1);
507 }
508
509 /* LINTED */
510 topt = (struct opthdr *)buf;
511 topt->level = optlevel;
512 topt->name = optname;
513 topt->len = optsz;
514 oreq.flags = T_NEGOTIATE;
515 oreq.opt.len = sizeof (struct opthdr) + optsz;
516 oreq.opt.buf = buf;
517
518 ores.flags = 0;
519 ores.opt.buf = buf;
520 ores.opt.maxlen = __TRANSPORT_INDSZ;
521 if (t_optmgmt(fd, &oreq, &ores) < 0 ||
522 ores.flags != T_SUCCESS) {
523 return (-1);
524 }
525 return (0);
526 }
527
528 /*
529 * Format an error message corresponding to the given TLI and system error
530 * codes.
531 */
532
533 void
__tli_sys_strerror(char * buf,size_t buflen,int tli_err,int sys_err)534 __tli_sys_strerror(char *buf, size_t buflen, int tli_err, int sys_err)
535 {
536 char *errorstr;
537
538 if (tli_err == TSYSERR) {
539 errorstr = strerror(sys_err);
540 if (errorstr == NULL)
541 (void) snprintf(buf, buflen,
542 dgettext(__nsl_dom,
543 "Unknown system error %d"),
544 sys_err);
545 else
546 (void) strlcpy(buf, errorstr, buflen);
547 } else {
548 errorstr = t_strerror(tli_err);
549 (void) strlcpy(buf, errorstr, buflen);
550 }
551 }
552
553 /*
554 * Depending on the specified RPC number, attempt to set mac_exempt
555 * option on the opened socket; these requests need to be able to do MAC
556 * MAC read-down operations. Privilege is needed to set this option.
557 */
558
559 void
__rpc_set_mac_options(int fd,const struct netconfig * nconf,rpcprog_t prognum)560 __rpc_set_mac_options(int fd, const struct netconfig *nconf, rpcprog_t prognum)
561 {
562 int ret = 0;
563
564 if (!is_system_labeled())
565 return;
566
567 if (strcmp(nconf->nc_protofmly, NC_INET) != 0 &&
568 strcmp(nconf->nc_protofmly, NC_INET6) != 0)
569 return;
570
571 if (is_multilevel(prognum)) {
572 ret = __rpc_tli_set_options(fd, SOL_SOCKET, SO_MAC_EXEMPT, 1);
573 if (ret < 0) {
574 char errorstr[100];
575
576 __tli_sys_strerror(errorstr, sizeof (errorstr),
577 t_errno, errno);
578 (void) syslog(LOG_ERR, "rpc_set_mac_options: %s",
579 errorstr);
580 }
581 }
582 }
583