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
__rpc_negotiate_uid(int fd)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
svc_fd_negotiate_ucred(int fd)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
find_ucred_opt(const SVCXPRT * trans,ucred_t * uc,bool_t checkzone)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
__rpc_get_local_uid(SVCXPRT * trans,uid_t * uid_out)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
__rpc_get_local_cred(SVCXPRT * xprt,svc_local_cred_t * lcred)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
svc_getcallerucred(const SVCXPRT * trans,ucred_t ** uc)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
__rpc_get_ltaddr(struct netbuf * nbufp,struct netbuf * ltaddr)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
__rpc_tli_set_options(int fd,int optlevel,int optname,int optval)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
__tli_sys_strerror(char * buf,size_t buflen,int tli_err,int sys_err)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
__rpc_set_mac_options(int fd,const struct netconfig * nconf,rpcprog_t prognum)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