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]
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
35 #pragma ident "%Z%%M% %I% %E% SMI"
39 #include <netinet/in.h>
40 #include <netinet/tcp.h>
41 #include <netinet/udp.h>
43 #include <sys/types.h>
45 #include <sys/socket.h>
47 #include <sys/sockio.h>
50 #include <sys/stropts.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
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()
78 * Version for Solaris with new local transport code and ucred.
81 __rpc_negotiate_uid(int fd
)
83 struct strioctl strioc
;
86 /* For tcp we use getpeerucred and it needs no initialization. */
87 if (ioctl(fd
, I_FIND
, "tcp") > 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");
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().
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
;
126 static zoneid_t myzone
= MIN_ZONEID
- 1; /* invalid */
128 if (abuf
== NULL
|| abuf
->buf
== NULL
) {
129 if (getpeerucred(trans
->xp_fd
, &uc
) == 0)
135 syslog(LOG_INFO
, "find_ucred_opt %p %x", abuf
->buf
, abuf
->len
);
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
) {
143 syslog(LOG_INFO
, "find_ucred_opt (opthdr): OK!");
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.
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
;
163 syslog(LOG_INFO
, "find_ucred_opt opt: %p %x, %d %d", opt
,
164 opt
->len
, opt
->name
, opt
->level
);
166 if (opt
->len
> maxbufp
- bufp
|| (opt
->len
& 3))
168 if (opt
->level
== SOL_SOCKET
&& opt
->name
== SCM_UCRED
&&
169 opt
->len
- sizeof (struct T_opthdr
) <= ucred_size()) {
171 syslog(LOG_INFO
, "find_ucred_opt (T_opthdr): OK!");
173 (void) memcpy(uc
, &opt
[1],
174 opt
->len
- sizeof (struct T_opthdr
));
179 if (getpeerucred(trans
->xp_fd
, &uc
) != 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());
201 /* LINTED - pointer alignment */
202 if (svc_type(trans
) == SVC_DOOR
)
203 err
= __svc_get_door_ucred(trans
, uc
) == FALSE
;
205 err
= find_ucred_opt(trans
, uc
, B_TRUE
);
209 *uid_out
= ucred_geteuid(uc
);
214 * Return local credentials.
217 __rpc_get_local_cred(SVCXPRT
*xprt
, svc_local_cred_t
*lcred
)
219 ucred_t
*uc
= alloca(ucred_size());
222 /* LINTED - pointer alignment */
223 if (svc_type(xprt
) == SVC_DOOR
)
224 err
= __svc_get_door_ucred(xprt
, uc
) == FALSE
;
226 err
= find_ucred_opt(xprt
, uc
, B_TRUE
);
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
);
240 * Return local ucred.
243 svc_getcallerucred(const SVCXPRT
*trans
, ucred_t
**uc
)
249 ucp
= malloc(ucred_size());
254 /* LINTED - pointer alignment */
255 if (svc_type(trans
) == SVC_DOOR
)
256 err
= __svc_get_door_ucred(trans
, ucp
) == FALSE
;
258 err
= find_ucred_opt(trans
, ucp
, B_FALSE
);
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
;
284 struct sioc_addrreq areq
;
286 if (nbufp
== NULL
|| ltaddr
== NULL
) {
291 total_optlen
= nbufp
->len
;
292 if (total_optlen
== 0)
295 /* LINTED pointer alignment */
296 opt_start
= (struct T_opthdr
*)nbufp
->buf
;
297 if (opt_start
== NULL
) {
302 /* Make sure the start of the buffer is aligned */
303 if (!(__TPI_TOPT_ISALIGNED(opt_start
))) {
308 /* LINTED pointer alignment */
309 opt_end
= (struct T_opthdr
*)((uchar_t
*)opt_start
+ total_optlen
);
313 * Look for the desired option header
316 if (((uchar_t
*)opt
+ sizeof (struct T_opthdr
)) >
317 (uchar_t
*)opt_end
) {
321 if (opt
->len
< sizeof (struct T_opthdr
)) {
325 if (((uchar_t
*)opt
+ opt
->len
) > (uchar_t
*)opt_end
) {
329 switch (opt
->level
) {
331 if (opt
->name
== IP_RECVDSTADDR
) {
332 struct sockaddr_in v4tmp
;
335 if (((uchar_t
*)opt
+ sizeof (struct in_addr
)) >
336 (uchar_t
*)opt_end
) {
340 bzero(&v4tmp
, sizeof (v4tmp
));
341 v4tmp
.sin_family
= AF_INET
;
342 v4tmp
.sin_addr
= *(struct in_addr
*)opt
;
346 char str
[INET_ADDRSTRLEN
];
348 ia
= *(struct in_addr
*)opt
;
349 (void) inet_ntop(AF_INET
, &ia
,
352 "__rpc_get_ltaddr for IP_RECVDSTADDR: %s",
356 if ((s
= open("/dev/udp", O_RDONLY
)) < 0) {
358 syslog(LOG_ERR
, "__rpc_get_ltaddr: "
359 "dev udp open failed");
364 (void) memcpy(&areq
.sa_addr
, &v4tmp
,
367 if (ioctl(s
, SIOCTMYADDR
, (caddr_t
)&areq
) < 0) {
369 "get_ltaddr:ioctl for udp failed");
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
;
386 if (opt
->name
== IPV6_PKTINFO
) {
387 struct sockaddr_in6 v6tmp
;
389 if (((uchar_t
*)opt
+
390 sizeof (struct in6_pktinfo
)) >
391 (uchar_t
*)opt_end
) {
395 bzero(&v6tmp
, sizeof (v6tmp
));
396 v6tmp
.sin6_family
= AF_INET6
;
398 ((struct in6_pktinfo
*)opt
)->ipi6_addr
;
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
,
408 "__rpc_get_ltaddr for IPV6_PKTINFO: %s",
412 if ((s
= open("/dev/udp6", O_RDONLY
)) < 0) {
414 syslog(LOG_ERR
, "__rpc_get_ltaddr: "
415 "dev udp6 open failed");
420 (void) memcpy(&areq
.sa_addr
, &v6tmp
,
423 if (ioctl(s
, SIOCTMYADDR
, (caddr_t
)&areq
) < 0) {
425 "get_ltaddr:ioctl for udp6 failed");
430 if (areq
.sa_res
== 1) {
431 /* LINTED pointer cast */
432 ipv6sa
= (struct sockaddr_in6
*)ltaddr
->buf
;
433 ipv6sa
->sin6_family
= AF_INET6
;
435 ((struct in6_pktinfo
*)opt
)->ipi6_addr
;
445 /* LINTED improper alignment */
446 opt
= (struct T_opthdr
*)((uchar_t
*)opt
+
447 __TPI_ALIGN(opt
->len
));
448 } while (opt
< opt_end
);
452 #define __TRANSPORT_INDSZ 128
455 __rpc_tli_set_options(int fd
, int optlevel
, int optname
, int optval
)
457 struct t_optmgmt oreq
, ores
;
461 char buf
[__TRANSPORT_INDSZ
];
468 ling
= (struct linger
*)
469 (buf
+ sizeof (struct opthdr
));
471 ling
->l_linger
= (int)optval
;
472 optsz
= sizeof (struct linger
);
476 case IPV6_RECVPKTINFO
:
482 case SO_DGRAM_ERRIND
:
488 ip
= (int *)(buf
+ sizeof (struct opthdr
));
490 optsz
= sizeof (int);
497 topt
= (struct opthdr
*)buf
;
498 topt
->level
= optlevel
;
499 topt
->name
= optname
;
501 oreq
.flags
= T_NEGOTIATE
;
502 oreq
.opt
.len
= sizeof (struct opthdr
) + optsz
;
507 ores
.opt
.maxlen
= __TRANSPORT_INDSZ
;
508 if (t_optmgmt(fd
, &oreq
, &ores
) < 0 ||
509 ores
.flags
!= T_SUCCESS
) {
516 * Format an error message corresponding to the given TLI and system error
521 __tli_sys_strerror(char *buf
, size_t buflen
, int tli_err
, int sys_err
)
525 if (tli_err
== TSYSERR
) {
526 errorstr
= strerror(sys_err
);
527 if (errorstr
== NULL
)
528 (void) snprintf(buf
, buflen
,
530 "Unknown system error %d"),
533 (void) strlcpy(buf
, errorstr
, buflen
);
535 errorstr
= t_strerror(tli_err
);
536 (void) strlcpy(buf
, errorstr
, buflen
);