import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / ti_opts.c
blob9648081d85e99f2bb44b29dfb65ffd2ffebc9231
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
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.
35 #pragma ident "%Z%%M% %I% %E% SMI"
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>
62 extern bool_t __svc_get_door_ucred(const SVCXPRT *, ucred_t *);
65 * This routine is typically called on the server side if the server
66 * wants to know the caller ucred. Called typically by rpcbind. It
67 * depends upon the t_optmgmt call to local transport driver so that
68 * return the uid value in options in T_CONN_IND, T_CONN_CON and
69 * T_UNITDATA_IND.
70 * With the advent of the credential in the mblk, this is simply
71 * extended to all transports when the packet travels over the
72 * loopback network; for UDP we use a special socket option and for
73 * tcp we don't need to do any setup, we just call getpeerucred()
74 * later.
78 * Version for Solaris with new local transport code and ucred.
80 int
81 __rpc_negotiate_uid(int fd)
83 struct strioctl strioc;
84 unsigned int set = 1;
86 /* For tcp we use getpeerucred and it needs no initialization. */
87 if (ioctl(fd, I_FIND, "tcp") > 0)
88 return (0);
90 strioc.ic_cmd = TL_IOC_UCREDOPT;
91 strioc.ic_timout = -1;
92 strioc.ic_len = (int)sizeof (unsigned int);
93 strioc.ic_dp = (char *)&set;
95 if (ioctl(fd, I_STR, &strioc) == -1 &&
96 __rpc_tli_set_options(fd, SOL_SOCKET, SO_RECVUCRED, 1) == -1) {
97 syslog(LOG_ERR, "rpc_negotiate_uid (%s): %m",
98 "ioctl:I_STR:TL_IOC_UCREDOPT/SO_RECVUCRED");
99 return (-1);
101 return (0);
104 void
105 svc_fd_negotiate_ucred(int fd)
107 (void) __rpc_negotiate_uid(fd);
112 * This returns the ucred of the caller. It assumes that the optbuf
113 * information is stored at xprt->xp_p2.
114 * There are three distinct cases: the option buffer is headed
115 * with a "struct opthdr" and the credential option is the only
116 * one, or it's a T_opthdr and our option may follow others; or there
117 * are no options and we attempt getpeerucred().
119 static int
120 find_ucred_opt(const SVCXPRT *trans, ucred_t *uc, bool_t checkzone)
122 /* LINTED pointer alignment */
123 struct netbuf *abuf = (struct netbuf *)trans->xp_p2;
124 char *bufp, *maxbufp;
125 struct opthdr *opth;
126 static zoneid_t myzone = MIN_ZONEID - 1; /* invalid */
128 if (abuf == NULL || abuf->buf == NULL) {
129 if (getpeerucred(trans->xp_fd, &uc) == 0)
130 goto verifyzone;
131 return (-1);
134 #ifdef RPC_DEBUG
135 syslog(LOG_INFO, "find_ucred_opt %p %x", abuf->buf, abuf->len);
136 #endif
137 /* LINTED pointer cast */
138 opth = (struct opthdr *)abuf->buf;
139 if (opth->level == TL_PROT_LEVEL &&
140 opth->name == TL_OPT_PEER_UCRED &&
141 opth->len + sizeof (*opth) == abuf->len) {
142 #ifdef RPC_DEBUG
143 syslog(LOG_INFO, "find_ucred_opt (opthdr): OK!");
144 #endif
145 (void) memcpy(uc, &opth[1], opth->len);
147 * Always from inside our zone because zones use a separate name
148 * space for loopback; at this time, the kernel may send a
149 * packet pretending to be from the global zone when it's
150 * really from our zone so we skip the zone check.
152 return (0);
155 bufp = abuf->buf;
156 maxbufp = bufp + abuf->len;
158 while (bufp + sizeof (struct T_opthdr) < maxbufp) {
159 /* LINTED pointer cast */
160 struct T_opthdr *opt = (struct T_opthdr *)bufp;
162 #ifdef RPC_DEBUG
163 syslog(LOG_INFO, "find_ucred_opt opt: %p %x, %d %d", opt,
164 opt->len, opt->name, opt->level);
165 #endif
166 if (opt->len > maxbufp - bufp || (opt->len & 3))
167 return (-1);
168 if (opt->level == SOL_SOCKET && opt->name == SCM_UCRED &&
169 opt->len - sizeof (struct T_opthdr) <= ucred_size()) {
170 #ifdef RPC_DEBUG
171 syslog(LOG_INFO, "find_ucred_opt (T_opthdr): OK!");
172 #endif
173 (void) memcpy(uc, &opt[1],
174 opt->len - sizeof (struct T_opthdr));
175 goto verifyzone;
177 bufp += opt->len;
179 if (getpeerucred(trans->xp_fd, &uc) != 0)
180 return (-1);
181 verifyzone:
182 if (!checkzone)
183 return (0);
185 if (myzone == MIN_ZONEID - 1)
186 myzone = getzoneid();
188 /* Return 0 only for the local zone */
189 return (ucred_getzoneid(uc) == myzone ? 0 : -1);
193 * Version for Solaris with new local transport code
196 __rpc_get_local_uid(SVCXPRT *trans, uid_t *uid_out)
198 ucred_t *uc = alloca(ucred_size());
199 int err;
201 /* LINTED - pointer alignment */
202 if (svc_type(trans) == SVC_DOOR)
203 err = __svc_get_door_ucred(trans, uc) == FALSE;
204 else
205 err = find_ucred_opt(trans, uc, B_TRUE);
207 if (err != 0)
208 return (-1);
209 *uid_out = ucred_geteuid(uc);
210 return (0);
214 * Return local credentials.
216 bool_t
217 __rpc_get_local_cred(SVCXPRT *xprt, svc_local_cred_t *lcred)
219 ucred_t *uc = alloca(ucred_size());
220 int err;
222 /* LINTED - pointer alignment */
223 if (svc_type(xprt) == SVC_DOOR)
224 err = __svc_get_door_ucred(xprt, uc) == FALSE;
225 else
226 err = find_ucred_opt(xprt, uc, B_TRUE);
228 if (err != 0)
229 return (FALSE);
231 lcred->euid = ucred_geteuid(uc);
232 lcred->egid = ucred_getegid(uc);
233 lcred->ruid = ucred_getruid(uc);
234 lcred->rgid = ucred_getrgid(uc);
235 lcred->pid = ucred_getpid(uc);
236 return (TRUE);
240 * Return local ucred.
243 svc_getcallerucred(const SVCXPRT *trans, ucred_t **uc)
245 ucred_t *ucp = *uc;
246 int err;
248 if (ucp == NULL) {
249 ucp = malloc(ucred_size());
250 if (ucp == NULL)
251 return (-1);
254 /* LINTED - pointer alignment */
255 if (svc_type(trans) == SVC_DOOR)
256 err = __svc_get_door_ucred(trans, ucp) == FALSE;
257 else
258 err = find_ucred_opt(trans, ucp, B_FALSE);
260 if (err != 0) {
261 if (*uc == NULL)
262 free(ucp);
263 return (-1);
266 if (*uc == NULL)
267 *uc = ucp;
269 return (0);
274 * get local ip address
277 __rpc_get_ltaddr(struct netbuf *nbufp, struct netbuf *ltaddr)
279 unsigned int total_optlen;
280 struct T_opthdr *opt, *opt_start = NULL, *opt_end;
281 struct sockaddr_in *ipv4sa;
282 struct sockaddr_in6 *ipv6sa;
283 int s;
284 struct sioc_addrreq areq;
286 if (nbufp == NULL || ltaddr == NULL) {
287 t_errno = TBADOPT;
288 return (-1);
291 total_optlen = nbufp->len;
292 if (total_optlen == 0)
293 return (1);
295 /* LINTED pointer alignment */
296 opt_start = (struct T_opthdr *)nbufp->buf;
297 if (opt_start == NULL) {
298 t_errno = TBADOPT;
299 return (-1);
302 /* Make sure the start of the buffer is aligned */
303 if (!(__TPI_TOPT_ISALIGNED(opt_start))) {
304 t_errno = TBADOPT;
305 return (-1);
308 /* LINTED pointer alignment */
309 opt_end = (struct T_opthdr *)((uchar_t *)opt_start + total_optlen);
310 opt = opt_start;
313 * Look for the desired option header
315 do {
316 if (((uchar_t *)opt + sizeof (struct T_opthdr)) >
317 (uchar_t *)opt_end) {
318 t_errno = TBADOPT;
319 return (-1);
321 if (opt->len < sizeof (struct T_opthdr)) {
322 t_errno = TBADOPT;
323 return (-1);
325 if (((uchar_t *)opt + opt->len) > (uchar_t *)opt_end) {
326 t_errno = TBADOPT;
327 return (-1);
329 switch (opt->level) {
330 case IPPROTO_IP:
331 if (opt->name == IP_RECVDSTADDR) {
332 struct sockaddr_in v4tmp;
334 opt++;
335 if (((uchar_t *)opt + sizeof (struct in_addr)) >
336 (uchar_t *)opt_end) {
337 t_errno = TBADOPT;
338 return (-1);
340 bzero(&v4tmp, sizeof (v4tmp));
341 v4tmp.sin_family = AF_INET;
342 v4tmp.sin_addr = *(struct in_addr *)opt;
343 #ifdef RPC_DEBUG
345 struct in_addr ia;
346 char str[INET_ADDRSTRLEN];
348 ia = *(struct in_addr *)opt;
349 (void) inet_ntop(AF_INET, &ia,
350 str, sizeof (str));
351 syslog(LOG_INFO,
352 "__rpc_get_ltaddr for IP_RECVDSTADDR: %s",
353 str);
355 #endif
356 if ((s = open("/dev/udp", O_RDONLY)) < 0) {
357 #ifdef RPC_DEBUG
358 syslog(LOG_ERR, "__rpc_get_ltaddr: "
359 "dev udp open failed");
360 #endif
361 return (1);
364 (void) memcpy(&areq.sa_addr, &v4tmp,
365 sizeof (v4tmp));
366 areq.sa_res = -1;
367 if (ioctl(s, SIOCTMYADDR, (caddr_t)&areq) < 0) {
368 syslog(LOG_ERR,
369 "get_ltaddr:ioctl for udp failed");
370 (void) close(s);
371 return (1);
373 (void) close(s);
374 if (areq.sa_res == 1) {
375 /* LINTED pointer cast */
376 ipv4sa = (struct sockaddr_in *)ltaddr->buf;
377 ipv4sa->sin_family = AF_INET;
378 ipv4sa->sin_addr = *(struct in_addr *)opt;
379 return (0);
380 } else
381 return (1);
384 break;
385 case IPPROTO_IPV6:
386 if (opt->name == IPV6_PKTINFO) {
387 struct sockaddr_in6 v6tmp;
388 opt++;
389 if (((uchar_t *)opt +
390 sizeof (struct in6_pktinfo)) >
391 (uchar_t *)opt_end) {
392 t_errno = TBADOPT;
393 return (-1);
395 bzero(&v6tmp, sizeof (v6tmp));
396 v6tmp.sin6_family = AF_INET6;
397 v6tmp.sin6_addr =
398 ((struct in6_pktinfo *)opt)->ipi6_addr;
399 #ifdef RPC_DEBUG
401 struct in6_pktinfo *in6_pkt;
402 char str[INET6_ADDRSTRLEN];
404 in6_pkt = (struct in6_pktinfo *)opt;
405 (void) inet_ntop(AF_INET6, &in6_pkt->ipi6_addr,
406 str, sizeof (str));
407 syslog(LOG_INFO,
408 "__rpc_get_ltaddr for IPV6_PKTINFO: %s",
409 str);
411 #endif
412 if ((s = open("/dev/udp6", O_RDONLY)) < 0) {
413 #ifdef RPC_DEBUG
414 syslog(LOG_ERR, "__rpc_get_ltaddr: "
415 "dev udp6 open failed");
416 #endif
417 return (1);
420 (void) memcpy(&areq.sa_addr, &v6tmp,
421 sizeof (v6tmp));
422 areq.sa_res = -1;
423 if (ioctl(s, SIOCTMYADDR, (caddr_t)&areq) < 0) {
424 syslog(LOG_ERR,
425 "get_ltaddr:ioctl for udp6 failed");
426 (void) close(s);
427 return (1);
429 (void) close(s);
430 if (areq.sa_res == 1) {
431 /* LINTED pointer cast */
432 ipv6sa = (struct sockaddr_in6 *)ltaddr->buf;
433 ipv6sa->sin6_family = AF_INET6;
434 ipv6sa->sin6_addr =
435 ((struct in6_pktinfo *)opt)->ipi6_addr;
437 return (0);
438 } else
439 return (1);
441 break;
442 default:
443 break;
445 /* LINTED improper alignment */
446 opt = (struct T_opthdr *)((uchar_t *)opt +
447 __TPI_ALIGN(opt->len));
448 } while (opt < opt_end);
449 return (1);
452 #define __TRANSPORT_INDSZ 128
455 __rpc_tli_set_options(int fd, int optlevel, int optname, int optval)
457 struct t_optmgmt oreq, ores;
458 struct opthdr *topt;
459 int *ip;
460 int optsz;
461 char buf[__TRANSPORT_INDSZ];
464 switch (optname) {
465 case SO_LINGER: {
466 struct linger *ling;
467 /* LINTED */
468 ling = (struct linger *)
469 (buf + sizeof (struct opthdr));
470 ling->l_onoff = 1;
471 ling->l_linger = (int)optval;
472 optsz = sizeof (struct linger);
473 break;
475 case IP_RECVDSTADDR:
476 case IPV6_RECVPKTINFO:
477 case SO_DEBUG:
478 case SO_KEEPALIVE:
479 case SO_DONTROUTE:
480 case SO_USELOOPBACK:
481 case SO_REUSEADDR:
482 case SO_DGRAM_ERRIND:
483 case SO_RECVUCRED:
484 case SO_EXCLBIND:
485 case TCP_EXCLBIND:
486 case UDP_EXCLBIND:
487 /* LINTED */
488 ip = (int *)(buf + sizeof (struct opthdr));
489 *ip = optval;
490 optsz = sizeof (int);
491 break;
492 default:
493 return (-1);
496 /* LINTED */
497 topt = (struct opthdr *)buf;
498 topt->level = optlevel;
499 topt->name = optname;
500 topt->len = optsz;
501 oreq.flags = T_NEGOTIATE;
502 oreq.opt.len = sizeof (struct opthdr) + optsz;
503 oreq.opt.buf = buf;
505 ores.flags = 0;
506 ores.opt.buf = buf;
507 ores.opt.maxlen = __TRANSPORT_INDSZ;
508 if (t_optmgmt(fd, &oreq, &ores) < 0 ||
509 ores.flags != T_SUCCESS) {
510 return (-1);
512 return (0);
516 * Format an error message corresponding to the given TLI and system error
517 * codes.
520 void
521 __tli_sys_strerror(char *buf, size_t buflen, int tli_err, int sys_err)
523 char *errorstr;
525 if (tli_err == TSYSERR) {
526 errorstr = strerror(sys_err);
527 if (errorstr == NULL)
528 (void) snprintf(buf, buflen,
529 dgettext(__nsl_dom,
530 "Unknown system error %d"),
531 sys_err);
532 else
533 (void) strlcpy(buf, errorstr, buflen);
534 } else {
535 errorstr = t_strerror(tli_err);
536 (void) strlcpy(buf, errorstr, buflen);