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.
27 * Copyright 2014 Nexenta Systems, Inc. All rights reserved.
29 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
30 /* All Rights Reserved */
32 * Portions of this source code were derived from Berkeley
33 * 4.3 BSD under license from the Regents of the University of
37 * ==== hack-attack: possibly MT-safe but definitely not MT-hot.
38 * ==== turn this into a real switch frontend and backends
40 * Well, at least the API doesn't involve pointers-to-static.
44 * netname utility routines convert from netnames to unix names (uid, gid)
46 * This module is operating system dependent!
47 * What we define here will work with any unix system that has adopted
48 * the Sun NIS domain architecture.
57 #include <sys/types.h>
63 #include <sys/param.h>
66 #include <rpcsvc/nis.h>
67 #include <rpcsvc/ypclnt.h>
68 #include <nss_dbdefs.h>
70 static const char OPSYS
[] = "unix";
71 static const char NETIDFILE
[] = "/etc/netid";
72 static const char NETID
[] = "netid.byname";
75 extern int _getgroupsbymember(const char *, gid_t
[], int, int);
78 * the value for NOBODY_UID is set by the SVID. The following define also
79 * appears in netname.c
82 #define NOBODY_UID 60001
85 * default publickey policy:
86 * publickey: nis [NOTFOUND = return] files
90 /* NSW_NOTSUCCESS NSW_NOTFOUND NSW_UNAVAIL NSW_TRYAGAIN */
91 #define DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
93 static struct __nsw_lookup lookup_files
= {"files", DEF_ACTION
, NULL
, NULL
},
94 lookup_nis
= {"nis", DEF_ACTION
, NULL
, &lookup_files
};
95 static struct __nsw_switchconfig publickey_default
=
96 {0, "publickey", 2, &lookup_nis
};
98 static mutex_t serialize_netname_r
= DEFAULTMUTEX
;
100 struct netid_userdata
{
108 parse_uid(char *s
, struct netid_userdata
*argp
)
112 if (!s
|| !isdigit(*s
)) {
114 "netname2user: expecting uid '%s'", s
);
115 return (__NSW_NOTFOUND
); /* xxx need a better error */
119 u
= (uid_t
)(atoi(s
));
122 syslog(LOG_ERR
, "netname2user: should not have uid 0");
123 return (__NSW_NOTFOUND
);
126 return (__NSW_SUCCESS
);
130 /* parse a comma separated gid list */
132 parse_gidlist(char *p
, struct netid_userdata
*argp
)
137 if (!p
|| (!isdigit(*p
))) {
139 "netname2user: missing group id list in '%s'.",
141 return (__NSW_NOTFOUND
);
144 g
= (gid_t
)(atoi(p
));
148 while (p
= strchr(p
, ','))
149 argp
->gidlist
[len
++] = (gid_t
)atoi(++p
);
150 *(argp
->gidlenp
) = len
;
151 return (__NSW_SUCCESS
);
158 * Parse uid and group information from the passed string.
160 * The format of the string passed is
161 * uid:gid,grp,grp, ...
165 parse_netid_str(char *s
, struct netid_userdata
*argp
)
171 err
= parse_uid(s
, argp
);
172 if (err
!= __NSW_SUCCESS
)
175 /* Now get the group list */
179 "netname2user: missing group id list in '%s'", s
);
180 return (__NSW_NOTFOUND
);
183 err
= parse_gidlist(p
, argp
);
188 * netname2user_files()
190 * This routine fetches the netid information from the "files" nameservice.
194 netname2user_files(int *err
, char *netname
, struct netid_userdata
*argp
)
196 char buf
[512]; /* one line from the file */
202 fd
= fopen(NETIDFILE
, "rF");
204 *err
= __NSW_UNAVAIL
;
208 * for each line in the file parse it appropriately
210 * netid uid:grp,grp,grp # for users
211 * netid 0:hostname # for hosts
214 res
= fgets(buf
, 512, fd
);
218 /* Skip comments and blank lines */
219 if ((*res
== '#') || (*res
== '\n'))
223 while (isspace(*name
))
225 if (*name
== '\0') /* blank line continue */
227 value
= name
; /* will contain the value eventually */
228 while (!isspace(*value
))
230 if (*value
== '\0') {
232 "netname2user: badly formatted line in %s.",
236 *value
++ = '\0'; /* nul terminate the name */
238 if (strcasecmp(name
, netname
) == 0) {
240 while (isspace(*value
))
242 *err
= parse_netid_str(value
, argp
);
243 return (*err
== __NSW_SUCCESS
);
247 *err
= __NSW_NOTFOUND
;
254 * This function reads the netid from the NIS (YP) nameservice.
257 netname2user_nis(int *err
, char *netname
, struct netid_userdata
*argp
)
264 domain
= strchr(netname
, '@');
266 *err
= __NSW_UNAVAIL
;
270 /* Point past the '@' character */
273 yperr
= yp_match(domain
, (char *)NETID
, netname
, strlen(netname
),
277 break; /* the successful case */
281 * XXX not sure about yp_match semantics.
282 * should err be set to NOTFOUND here?
284 *err
= __NSW_UNAVAIL
;
289 *err
= parse_netid_str(lookup
, argp
);
291 return (*err
== __NSW_SUCCESS
);
293 *err
= __NSW_NOTFOUND
;
298 * Build the uid and gid from the netname for users in LDAP.
299 * There is no netid container in LDAP. For this we build
300 * the netname to user data dynamically from the passwd and
301 * group data. This works only for users in a single domain.
302 * This function is an interim solution until we support a
303 * netid container in LDAP which enables us to do netname2user
304 * resolution for multiple domains.
307 netname2user_ldap(int *err
, char *netname
, struct netid_userdata
*argp
)
309 char buf
[NSS_LINELEN_PASSWD
];
315 char pwbuf
[NSS_LINELEN_PASSWD
];
316 struct passwd
*result
;
317 int maxgrp
= sysconf(_SC_NGROUPS_MAX
);
318 gid_t
*groups
= alloca(maxgrp
* sizeof (gid_t
));
320 if (strlcpy(buf
, netname
, NSS_LINELEN_PASSWD
) >= NSS_LINELEN_PASSWD
) {
321 *err
= __NSW_UNAVAIL
;
325 /* get the uid from the netname */
326 if (strtok_r(buf
, ".", &lasts
) == NULL
) {
327 *err
= __NSW_UNAVAIL
;
330 if ((p2
= strtok_r(NULL
, "@", &lasts
)) == NULL
) {
331 *err
= __NSW_UNAVAIL
;
337 * check out the primary group and crosscheck the uid
338 * with the passwd data
340 (void) getpwuid_r(uidnu
, &pw
, pwbuf
, sizeof (pwbuf
), &result
);
341 if (result
== NULL
) {
342 *err
= __NSW_UNAVAIL
;
346 *(argp
->uidp
) = pw
.pw_uid
;
347 *(argp
->gidp
) = pw
.pw_gid
;
349 /* search through all groups for membership */
351 groups
[0] = pw
.pw_gid
;
353 ngroups
= _getgroupsbymember(pw
.pw_name
, groups
, maxgrp
,
354 (pw
.pw_gid
<= MAXUID
) ? 1 : 0);
357 *err
= __NSW_UNAVAIL
;
361 *(argp
->gidlenp
) = ngroups
;
363 for (count
= 0; count
< ngroups
; count
++) {
364 (argp
->gidlist
[count
]) = groups
[count
];
367 *err
= __NSW_SUCCESS
;
373 * Convert network-name into unix credential
376 netname2user(const char netname
[MAXNETNAMELEN
+ 1], uid_t
*uidp
, gid_t
*gidp
,
377 int *gidlenp
, gid_t
*gidlist
)
379 struct __nsw_switchconfig
*conf
;
380 struct __nsw_lookup
*look
;
381 enum __nsw_parse_err perr
;
382 int needfree
= 1, res
;
383 struct netid_userdata argp
;
387 * Take care of the special case of nobody. Compare the netname
388 * to the string "nobody". If they are equal, return the SVID
389 * standard value for nobody.
392 if (strcmp(netname
, "nobody") == 0) {
400 * First we do some generic sanity checks on the name we were
401 * passed. This lets us assume they are correct in the backends.
403 * NOTE: this code only recognizes names of the form :
404 * unix.UID@domainname
406 if (strncmp(netname
, OPSYS
, OPSYS_LEN
) != 0)
408 if (!isdigit(netname
[OPSYS_LEN
+1])) /* check for uid string */
413 argp
.gidlenp
= gidlenp
;
414 argp
.gidlist
= gidlist
;
415 (void) mutex_lock(&serialize_netname_r
);
417 conf
= __nsw_getconfig("publickey", &perr
);
419 conf
= &publickey_default
;
422 needfree
= 1; /* free the config structure */
424 for (look
= conf
->lookups
; look
; look
= look
->next
) {
425 if (strcmp(look
->service_name
, "nis") == 0)
426 res
= netname2user_nis(&err
, (char *)netname
, &argp
);
427 else if (strcmp(look
->service_name
, "files") == 0)
428 res
= netname2user_files(&err
, (char *)netname
, &argp
);
429 else if (strcmp(look
->service_name
, "ldap") == 0)
430 res
= netname2user_ldap(&err
, (char *)netname
, &argp
);
433 "netname2user: unknown nameservice for publickey"
434 "info '%s'\n", look
->service_name
);
438 switch (look
->actions
[err
]) {
439 case __NSW_CONTINUE
:
443 (void) __nsw_freeconfig(conf
);
444 (void) mutex_unlock(&serialize_netname_r
);
448 "netname2user: Unknown action for "
449 "nameservice '%s'", look
->service_name
);
453 (void) __nsw_freeconfig(conf
);
454 (void) mutex_unlock(&serialize_netname_r
);
459 * Convert network-name to hostname (fully qualified)
460 * NOTE: this code only recognizes names of the form :
461 * unix.HOST@domainname
463 * This is very simple. Since the netname is of the form:
464 * unix.host@domainname
465 * We just construct the hostname using information from the domainname.
468 netname2host(const char netname
[MAXNETNAMELEN
+ 1], char *hostname
,
471 char *p
, *domainname
;
475 syslog(LOG_ERR
, "netname2host: null netname");
479 if (strncmp(netname
, OPSYS
, OPSYS_LEN
) != 0)
481 p
= (char *)netname
+ OPSYS_LEN
; /* skip OPSYS part */
486 domainname
= strchr(p
, '@'); /* get domain name */
490 len
= domainname
- p
; /* host sits between '.' and '@' */
491 domainname
++; /* skip '@' sign */
498 "netname2host: insufficient space for hostname");
502 if (isdigit(*p
)) /* don't want uid here */
505 if (*p
== '\0') /* check for null hostname */
508 (void) strncpy(hostname
, p
, len
);
510 /* make into fully qualified hostname by concatenating domain part */
511 dlen
= strlen(domainname
);
512 if (hostlen
< (len
+ dlen
+ 2)) {
514 "netname2host: insufficient space for hostname");
519 (void) strncpy(hostname
+len
+1, domainname
, dlen
);
520 hostname
[len
+dlen
+1] = '\0';
525 syslog(LOG_ERR
, "netname2host: invalid host netname %s", netname
);