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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
29 * This module contains the subroutines used by the server to manipulate
42 #include <sys/fcntl.h>
43 #include <netinet/in.h>
44 #include <rpc/rpc.h> /* Must be ahead of rpcb_clnt.h */
47 #include <netconfig.h>
49 #include <rpc/rpcb_clnt.h>
50 #include <rpc/pmap_clnt.h>
51 #include <rpcsvc/nis.h>
52 #include <rpcsvc/nis_dhext.h>
54 #include <sys/systeminfo.h>
57 #define MAXIPRINT (11) /* max length of printed integer */
59 * send and receive buffer size used for clnt_tli_create if not specified.
60 * This is only used for "UDP" connection.
61 * This limit can be changed from the application if this value is too
62 * small for the application. To use the maximum value for the transport,
63 * set this value to 0.
65 int __nisipbufsize
= 8192;
69 * Static function prototypes.
71 static struct local_names
*__get_local_names(void);
72 static char *__map_addr(struct netconfig
*, char *, rpcprog_t
, rpcvers_t
);
75 * nis_dir_cmp() -- the results can be read as:
76 * "Name 'n1' is a $result than name 'n2'"
81 nis_name n2
) /* See if these are the same domain */
86 if ((n1
== NULL
) || (n2
== NULL
))
92 /* In this routine we're lenient and don't require a trailing '.' */
93 /* so we need to ignore it if it does appear. */
94 /* ==== That's what the previous version did so this one does */
95 /* too, but why? Is this inconsistent with rest of system? */
96 if (l1
!= 0 && n1
[l1
- 1] == '.') {
99 if (l2
!= 0 && n2
[l2
- 1] == '.') {
105 } else if (l1
== l2
) {
107 } else /* (l1 < l2); swap l1/l2 and n1/n2 */ {
110 ntmp
= n1
; n1
= n2
; n2
= ntmp
;
111 ltmp
= l1
; l1
= l2
; l2
= ltmp
;
113 result
= HIGHER_NAME
;
116 /* Now l1 >= l2 in all cases */
118 /* Special case for n2 == "." or "" */
124 return (NOT_SEQUENTIAL
);
127 if (strncasecmp(n1
, n2
, l2
) == 0) {
130 return (NOT_SEQUENTIAL
);
133 #define LN_BUFSIZE (size_t)1024
135 struct principal_list
{
137 char principal
[LN_BUFSIZE
];
138 struct principal_list
*next
;
143 char domain
[LN_BUFSIZE
];
144 char host
[LN_BUFSIZE
];
146 struct principal_list
*principal_map
;
147 char group
[LN_BUFSIZE
];
150 static mutex_t ln_lock
= DEFAULTMUTEX
; /* lock level 2 */
151 static struct local_names
*ln
= NULL
;
152 static struct local_names
*__get_local_names1();
154 static struct local_names
*
155 __get_local_names(void)
157 struct local_names
*names
;
159 sig_mutex_lock(&ln_lock
);
160 names
= __get_local_names1();
161 sig_mutex_unlock(&ln_lock
);
166 static struct local_names
*
167 __get_local_names1(void)
172 /* Second and subsequent calls go this way */
175 /* First call goes this way */
176 ln
= calloc(1, sizeof (*ln
));
178 syslog(LOG_ERR
, "__get_local_names: Out of heap.");
181 ln
->principal_map
= NULL
;
183 if (sysinfo(SI_SRPC_DOMAIN
, ln
->domain
, LN_BUFSIZE
) < 0)
185 /* If no dot exists, add one. */
186 if (ln
->domain
[strlen(ln
->domain
)-1] != '.')
187 (void) strcat(ln
->domain
, ".");
188 if (sysinfo(SI_HOSTNAME
, ln
->host
, LN_BUFSIZE
) < 0)
192 * Check for fully qualified hostname. If it's a fully qualified
193 * hostname, strip off the domain part. We always use the local
194 * domainname for the host principal name.
196 t
= strchr(ln
->host
, '.');
199 if (ln
->domain
[0] != '.')
200 (void) strcat(ln
->host
, ".");
201 ln
->rpcdomain
= strdup(ln
->domain
);
202 (void) strcat(ln
->host
, ln
->domain
);
204 t
= getenv("NIS_GROUP");
208 size_t maxlen
= LN_BUFSIZE
-1; /* max chars to copy */
209 char *temp
; /* temp marker */
212 * Copy <= maximum characters from NIS_GROUP; strncpy()
213 * doesn't terminate, so we do that manually. #1223323
214 * Also check to see if it's "". If it's the null string,
215 * we return because we don't want to add ".domain".
217 (void) strncpy(ln
->group
, t
, maxlen
);
218 if (strcmp(ln
->group
, "") == 0) {
221 ln
->group
[maxlen
] = '\0';
223 /* Is the group name somewhat fully-qualified? */
224 temp
= strrchr(ln
->group
, '.');
226 /* If not, we need to add ".domain" to the group */
227 if ((temp
== NULL
) || (temp
[1] != '\0')) {
229 /* truncate to make room for ".domain" */
230 ln
->group
[maxlen
- (strlen(ln
->domain
)+1)] = '\0';
232 /* concat '.' if domain doesn't already have it */
233 if (ln
->domain
[0] != '.') {
234 (void) strcat(ln
->group
, ".");
236 (void) strcat(ln
->group
, ln
->domain
);
245 * Return's the group name of the current user.
248 nis_local_group(void)
250 struct local_names
*ln
= __get_local_names();
252 /* LOCK NOTE: Warning, after initialization, "ln" is expected */
253 /* to stay constant, So no need to lock here. If this assumption */
254 /* is changed, this code must be protected. */
263 * This internal funtion will accept a pointer to a NIS name string and
264 * return a pointer to the next separator occurring in it (it will point
265 * just past the first label). It allows for labels to be "quoted" to
266 * prevent the the dot character within them to be interpreted as a
267 * separator, also the quote character itself can be quoted by using
268 * it twice. If the the name contains only one label and no trailing
269 * dot character, a pointer to the terminating NULL is returned.
272 __nis_nextsep_of(char *s
)
275 int in_quotes
= FALSE
, quote_quote
= FALSE
;
280 for (d
= s
; (in_quotes
&& (*d
!= '\0')) ||
281 (!in_quotes
&& (*d
!= '.') && (*d
!= '\0')); d
++) {
282 if (quote_quote
&& in_quotes
&& (*d
!= '"')) {
287 } else if (quote_quote
&& in_quotes
&& (*d
== '"')) {
289 } else if (quote_quote
&& (*d
!= '"')) {
292 } else if (quote_quote
&& (*d
== '"')) {
294 } else if (in_quotes
&& (*d
== '"')) {
296 } else if (!in_quotes
&& (*d
== '"')) {
301 if (quote_quote
|| in_quotes
) {
302 syslog(LOG_DEBUG
, "__nis_nextsep_of: "
303 "Mismatched quotes in %s", s
);
312 * This internal funtion will accept a pointer to a NIS name string and
313 * return a pointer to the "domain" part of it.
315 * ==== We don't need nis_domain_of_r(), but should we provide one for
319 nis_domain_of(char *s
)
323 d
= __nis_nextsep_of(s
);
328 if (*d
== '\0') /* Don't return a zero length string */
329 return ("."); /* return root domain instead */
337 * Returns the first label of a name. (other half of __domain_of)
346 const char *d
= __nis_nextsep_of((char *)s
);
352 if (bufsize
< nchars
+ 1) {
355 (void) strncpy(buf
, s
, nchars
);
360 static pthread_key_t buf_key
= PTHREAD_ONCE_KEY_NP
;
361 static char buf_main
[LN_BUFSIZE
];
366 char *buf
= thr_main()? buf_main
:
367 thr_get_storage(&buf_key
, LN_BUFSIZE
, free
);
371 return (nis_leaf_of_r(s
, buf
, LN_BUFSIZE
));
376 * This internal function will remove from the NIS name, the domain
377 * name of the current server, this will leave the unique part in
378 * the name this becomes the "internal" version of the name. If this
379 * function returns NULL then the name we were given to resolve is
381 * NB: Uses static storage and this is a no-no with threads. XXX
386 char *s
, /* string with the name in it. */
391 struct local_names
*ln
= __get_local_names();
396 return (NULL
); /* No string, this can't continue */
398 d
= &(ln
->domain
[0]);
399 dl
= strlen(ln
->domain
); /* _always dot terminated_ */
402 if (sl
>= bufsize
|| (s
[sl
-1] != '.' && sl
>= bufsize
-1))
404 (void) strcpy(buf
, s
); /* Make a private copy of 's' */
405 if (buf
[sl
-1] != '.') { /* Add a dot if necessary. */
406 (void) strcat(buf
, ".");
410 if (dl
== 1) { /* We're the '.' directory */
411 buf
[sl
-1] = '\0'; /* Lose the 'dot' */
415 p
= nis_dir_cmp(buf
, d
);
417 /* 's' is above 'd' in the tree */
418 if ((p
== HIGHER_NAME
) || (p
== NOT_SEQUENTIAL
) || (p
== SAME_NAME
))
421 /* Insert a NUL where the domain name starts in the string */
422 buf
[(sl
- dl
) - 1] = '\0';
424 /* Don't return a zero length name */
433 char *s
) /* string with the name in it. */
435 char *buf
= thr_main()? buf_main
:
436 thr_get_storage(&buf_key
, LN_BUFSIZE
, free
);
440 return (nis_name_of_r(s
, buf
, LN_BUFSIZE
));
446 * nis_local_directory()
448 * Return a pointer to a string with the local directory name in it.
451 nis_local_directory(void)
453 struct local_names
*ln
= __get_local_names();
455 /* LOCK NOTE: Warning, after initialization, "ln" is expected */
456 /* to stay constant, So no need to lock here. If this assumption */
457 /* is changed, this code must be protected. */
466 * Return a pointer to a string with the rpc domain name in it.
471 struct local_names
*ln
= __get_local_names();
473 /* LOCK NOTE: Warning, after initialization, "ln" is expected */
474 /* to stay constant, So no need to lock here. If this assumption */
475 /* is changed, this code must be protected. */
478 return (ln
->rpcdomain
);
484 * Generate the principal name for this host, "hostname"+"domainname"
485 * unless the hostname already has "dots" in its name.
490 struct local_names
*ln
= __get_local_names();
492 /* LOCK NOTE: Warning, after initialization, "ln" is expected */
493 /* to stay constant, So no need to lock here. If this assumption */
494 /* is changed, this code must be protected. */
502 * nis_destroy_object()
503 * This function takes a pointer to a NIS object and deallocates it. This
504 * is the inverse of __clone_object below. It must be able to correctly
505 * deallocate partially allocated objects because __clone_object will call
506 * it if it runs out of memory and has to abort. Everything is freed,
507 * INCLUDING the pointer that is passed.
510 nis_destroy_object(nis_object
*obj
) /* The object to clone */
514 xdr_free(xdr_nis_object
, (char *)obj
);
516 } /* nis_destroy_object */
519 destroy_nis_sdata(void *p
)
521 struct nis_sdata
*ns
= p
;
528 /* XXX Why are these static ? */
529 /* static XDR in_xdrs, out_xdrs; */
534 * This function takes a pointer to a NIS object and clones it. This
535 * duplicate object is now available for use in the local context.
539 nis_object
*obj
, /* The object to clone */
540 nis_object
*dest
, /* Use this pointer if non-null */
541 struct nis_sdata
*clone_buf_ptr
)
543 nis_object
*result
; /* The clone itself */
544 int status
; /* a counter variable */
545 XDR in_xdrs
, out_xdrs
;
547 if (!nis_get_static_storage(clone_buf_ptr
, 1,
548 xdr_sizeof(xdr_nis_object
, obj
)))
551 (void) memset(&in_xdrs
, 0, sizeof (in_xdrs
));
552 (void) memset(&out_xdrs
, 0, sizeof (out_xdrs
));
553 xdrmem_create(&in_xdrs
, clone_buf_ptr
->buf
, clone_buf_ptr
->size
,
555 xdrmem_create(&out_xdrs
, clone_buf_ptr
->buf
, clone_buf_ptr
->size
,
558 /* Allocate a basic NIS object structure */
560 (void) memset(dest
, 0, sizeof (nis_object
));
563 result
= calloc(1, sizeof (nis_object
));
568 /* Encode our object into the clone buffer */
569 (void) xdr_setpos(&in_xdrs
, 0);
570 status
= xdr_nis_object(&in_xdrs
, obj
);
574 /* Now decode the buffer into our result pointer ... */
575 (void) xdr_setpos(&out_xdrs
, 0);
576 status
= xdr_nis_object(&out_xdrs
, result
);
580 /* presto changeo, a new object */
582 } /* __clone_object_r */
587 nis_object
*obj
, /* The object to clone */
588 nis_object
*dest
) /* Use this pointer if non-null */
590 static pthread_key_t clone_buf_key
= PTHREAD_ONCE_KEY_NP
;
591 static struct nis_sdata clone_buf_main
;
592 struct nis_sdata
*clone_buf_ptr
;
594 clone_buf_ptr
= thr_main()? &clone_buf_main
:
595 thr_get_storage(&clone_buf_key
, sizeof (struct nis_sdata
),
597 return (nis_clone_object_r(obj
, dest
, clone_buf_ptr
));
598 } /* __clone_object */
600 /* Various subroutines used by the server code */
602 nis_read_obj(char *f
) /* name of the object to read */
605 int status
; /* Status of the XDR decoding */
606 XDR xdrs
; /* An xdr stream handle */
609 res
= calloc(1, sizeof (nis_object
));
613 rootfile
= fopen(f
, "rF");
614 if (rootfile
== NULL
) {
615 /* This is ok if we are the root of roots. */
619 /* Now read in the object */
620 xdrstdio_create(&xdrs
, rootfile
, XDR_DECODE
);
621 status
= xdr_nis_object(&xdrs
, res
);
623 (void) fclose(rootfile
);
625 syslog(LOG_ERR
, "Object file %s is corrupt!", f
);
626 xdr_free(xdr_nis_object
, (char *)res
);
635 char *f
, /* name of the object to read */
636 nis_object
*o
) /* The object to write */
639 int status
; /* Status of the XDR decoding */
640 XDR xdrs
; /* An xdr stream handle */
642 rootfile
= fopen(f
, "wF");
643 if (rootfile
== NULL
) {
646 /* Now encode the object */
647 xdrstdio_create(&xdrs
, rootfile
, XDR_ENCODE
);
648 status
= xdr_nis_object(&xdrs
, o
);
650 (void) fclose(rootfile
);
655 * Transport INDEPENDENT RPC code. This code assumes you
656 * are using the new RPC/tli code and will build
657 * a ping handle on top of a datagram transport.
663 * This is our internal function that replaces rpcb_getaddr(). We
664 * build our own to prevent calling netdir_getbyname() which could
665 * recurse to the nameservice.
669 struct netconfig
*nc
, /* Our transport */
670 char *uaddr
, /* RPCBIND address */
671 rpcprog_t prog
, /* Name service Prog */
675 RPCB parms
; /* Parameters for RPC binder */
676 enum clnt_stat clnt_st
; /* Result from the rpc call */
677 char *ua
= NULL
; /* Universal address of service */
678 char *res
= NULL
; /* Our result to the parent */
679 struct timeval tv
; /* Timeout for our rpcb call */
680 int ilen
, olen
; /* buffer length for clnt_tli_create */
683 * If using "udp", use __nisipbufsize if inbuf and outbuf are set to 0.
685 if (strcmp(NC_UDP
, nc
->nc_proto
) == 0) {
687 ilen
= olen
= __nisipbufsize
;
691 client
= __nis_clnt_create(RPC_ANYFD
, nc
, uaddr
, 0, 0,
692 RPCBPROG
, RPCBVERS
, ilen
, olen
);
696 (void) clnt_control(client
, CLSET_FD_CLOSE
, NULL
);
699 * Now make the call to get the NIS service address.
700 * We set the retry timeout to 3 seconds so that we
701 * will retry a few times. Retries should be rare
702 * because we are usually only called when we know
703 * a server is available.
707 (void) clnt_control(client
, CLSET_RETRY_TIMEOUT
, (char *)&tv
);
713 parms
.r_netid
= nc
->nc_netid
; /* not needed */
714 parms
.r_addr
= ""; /* not needed; just for xdring */
715 parms
.r_owner
= ""; /* not needed; just for xdring */
716 clnt_st
= clnt_call(client
, RPCBPROC_GETADDR
, xdr_rpcb
, (char *)&parms
,
717 xdr_wrapstring
, (char *)&ua
, tv
);
719 if (clnt_st
== RPC_SUCCESS
) {
720 clnt_destroy(client
);
726 xdr_free(xdr_wrapstring
, (char *)&ua
);
728 } else if (((clnt_st
== RPC_PROGVERSMISMATCH
) ||
729 (clnt_st
== RPC_PROGUNAVAIL
)) &&
730 (strcmp(nc
->nc_protofmly
, NC_INET
) == 0)) {
732 * version 3 not available. Try version 2
733 * The assumption here is that the netbuf
734 * is arranged in the sockaddr_in
735 * style for IP cases.
737 * Note: If the remote host doesn't support version 3,
738 * we assume it doesn't know IPv6 either.
741 struct sockaddr_in
*sa
;
742 struct netbuf remote
;
746 (void) clnt_control(client
, CLGET_SVC_ADDR
, (char *)&remote
);
747 /* LINTED pointer cast */
748 sa
= (struct sockaddr_in
*)(remote
.buf
);
749 protocol
= strcmp(nc
->nc_proto
, NC_TCP
) ?
750 IPPROTO_UDP
: IPPROTO_TCP
;
751 port
= (ushort_t
)pmap_getport(sa
, prog
, ver
, protocol
);
755 (void) sprintf(buf
, "%d.%d.%d.%d.%d.%d",
756 (sa
->sin_addr
.s_addr
>> 24) & 0xff,
757 (sa
->sin_addr
.s_addr
>> 16) & 0xff,
758 (sa
->sin_addr
.s_addr
>> 8) & 0xff,
759 (sa
->sin_addr
.s_addr
) & 0xff,
765 clnt_destroy(client
);
768 if (clnt_st
== RPC_TIMEDOUT
)
769 syslog(LOG_ERR
, "NIS+ server not responding");
771 syslog(LOG_ERR
, "NIS+ server could not be contacted: %s",
772 clnt_sperrno(clnt_st
));
773 clnt_destroy(client
);
780 extern int __can_use_af(sa_family_t af
);
783 __nis_clnt_create(int fd
, struct netconfig
*nc
, char *uaddr
,
784 struct netbuf
*addr
, int domapaddr
,
785 int prog
, int ver
, int inbuf
, int outbuf
) {
792 if (nc
== 0 || (addr
== 0 && uaddr
== 0)) {
797 * Check if we have a useable interface for this address family.
798 * This check properly belongs in RPC (or even further down),
799 * but until they provide it, we roll our own.
801 if (__can_use_af((strcmp(nc
->nc_protofmly
, NC_INET6
) == 0) ?
802 AF_INET6
: AF_INET
) == 0) {
807 svc_addr
= __map_addr(nc
, uaddr
, prog
, ver
);
810 addr
= uaddr2taddr(nc
, svc_addr
);
813 } else if (addr
== 0) {
814 addr
= uaddr2taddr(nc
, uaddr
);
822 clnt
= clnt_tli_create(fd
, nc
, addr
, prog
, ver
, outbuf
, inbuf
);
825 if (clnt_control(clnt
, CLGET_FD
, (char *)&fd
))
826 /* make it "close on exec" */
827 (void) fcntl(fd
, F_SETFD
, FD_CLOEXEC
);
828 (void) clnt_control(clnt
, CLSET_FD_CLOSE
, NULL
);
832 netdir_free(addr
, ND_ADDR
);
837 static mutex_t __nis_ss_used_lock
= DEFAULTMUTEX
; /* lock level 3 */
838 int __nis_ss_used
= 0;
841 * nis_get_static_storage()
843 * This function is used by various functions in their effort to minimize the
844 * hassles of memory management in an RPC daemon. Because the service doesn't
845 * implement any hard limits, this function allows people to get automatically
846 * growing buffers that meet their storage requirements. It returns the
847 * pointer in the nis_sdata structure.
851 nis_get_static_storage(
852 struct nis_sdata
*bs
, /* User buffer structure */
853 uint_t el
, /* Sizeof elements */
854 uint_t nel
) /* Number of elements */
863 bs
->buf
= malloc(sz
);
867 sig_mutex_lock(&__nis_ss_used_lock
);
869 sig_mutex_unlock(&__nis_ss_used_lock
);
870 } else if (bs
->size
< sz
) {
874 size_delta
= - (bs
->size
);
875 bs
->buf
= malloc(sz
);
877 /* check the result of malloc() first */
878 /* then update the statistic. */
883 sig_mutex_lock(&__nis_ss_used_lock
);
884 __nis_ss_used
+= size_delta
;
885 sig_mutex_unlock(&__nis_ss_used_lock
);
888 (void) memset(bs
->buf
, 0, sz
); /* SYSV version of bzero() */