import less(1)
[unleashed/tickless.git] / usr / src / lib / libc / port / nsl / netnamer.c
blobbacadb2bed69adee6c835794d425d22977ac92dc
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 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
34 * California.
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.
51 #include "mt.h"
52 #include "rpc_mt.h"
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <unistd.h>
56 #include <alloca.h>
57 #include <sys/types.h>
58 #include <ctype.h>
59 #include <grp.h>
60 #include <pwd.h>
61 #include <string.h>
62 #include <syslog.h>
63 #include <sys/param.h>
64 #include <nsswitch.h>
65 #include <rpc/rpc.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";
73 #define OPSYS_LEN 4
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 {
101 uid_t *uidp;
102 gid_t *gidp;
103 int *gidlenp;
104 gid_t *gidlist;
107 static int
108 parse_uid(char *s, struct netid_userdata *argp)
110 uid_t u;
112 if (!s || !isdigit(*s)) {
113 syslog(LOG_ERR,
114 "netname2user: expecting uid '%s'", s);
115 return (__NSW_NOTFOUND); /* xxx need a better error */
118 /* Fetch the uid */
119 u = (uid_t)(atoi(s));
121 if (u == 0) {
122 syslog(LOG_ERR, "netname2user: should not have uid 0");
123 return (__NSW_NOTFOUND);
125 *(argp->uidp) = u;
126 return (__NSW_SUCCESS);
130 /* parse a comma separated gid list */
131 static int
132 parse_gidlist(char *p, struct netid_userdata *argp)
134 int len;
135 gid_t g;
137 if (!p || (!isdigit(*p))) {
138 syslog(LOG_ERR,
139 "netname2user: missing group id list in '%s'.",
141 return (__NSW_NOTFOUND);
144 g = (gid_t)(atoi(p));
145 *(argp->gidp) = g;
147 len = 0;
148 while (p = strchr(p, ','))
149 argp->gidlist[len++] = (gid_t)atoi(++p);
150 *(argp->gidlenp) = len;
151 return (__NSW_SUCCESS);
156 * parse_netid_str()
158 * Parse uid and group information from the passed string.
160 * The format of the string passed is
161 * uid:gid,grp,grp, ...
164 static int
165 parse_netid_str(char *s, struct netid_userdata *argp)
167 char *p;
168 int err;
170 /* get uid */
171 err = parse_uid(s, argp);
172 if (err != __NSW_SUCCESS)
173 return (err);
175 /* Now get the group list */
176 p = strchr(s, ':');
177 if (!p) {
178 syslog(LOG_ERR,
179 "netname2user: missing group id list in '%s'", s);
180 return (__NSW_NOTFOUND);
182 ++p; /* skip ':' */
183 err = parse_gidlist(p, argp);
184 return (err);
188 * netname2user_files()
190 * This routine fetches the netid information from the "files" nameservice.
191 * ie /etc/netid.
193 static int
194 netname2user_files(int *err, char *netname, struct netid_userdata *argp)
196 char buf[512]; /* one line from the file */
197 char *name;
198 char *value;
199 char *res;
200 FILE *fd;
202 fd = fopen(NETIDFILE, "rF");
203 if (fd == NULL) {
204 *err = __NSW_UNAVAIL;
205 return (0);
208 * for each line in the file parse it appropriately
209 * file format is :
210 * netid uid:grp,grp,grp # for users
211 * netid 0:hostname # for hosts
213 while (!feof(fd)) {
214 res = fgets(buf, 512, fd);
215 if (res == NULL)
216 break;
218 /* Skip comments and blank lines */
219 if ((*res == '#') || (*res == '\n'))
220 continue;
222 name = &(buf[0]);
223 while (isspace(*name))
224 name++;
225 if (*name == '\0') /* blank line continue */
226 continue;
227 value = name; /* will contain the value eventually */
228 while (!isspace(*value))
229 value++;
230 if (*value == '\0') {
231 syslog(LOG_WARNING,
232 "netname2user: badly formatted line in %s.",
233 NETIDFILE);
234 continue;
236 *value++ = '\0'; /* nul terminate the name */
238 if (strcasecmp(name, netname) == 0) {
239 (void) fclose(fd);
240 while (isspace(*value))
241 value++;
242 *err = parse_netid_str(value, argp);
243 return (*err == __NSW_SUCCESS);
246 (void) fclose(fd);
247 *err = __NSW_NOTFOUND;
248 return (0);
252 * netname2user_nis()
254 * This function reads the netid from the NIS (YP) nameservice.
256 static int
257 netname2user_nis(int *err, char *netname, struct netid_userdata *argp)
259 char *domain;
260 int yperr;
261 char *lookup;
262 int len;
264 domain = strchr(netname, '@');
265 if (!domain) {
266 *err = __NSW_UNAVAIL;
267 return (0);
270 /* Point past the '@' character */
271 domain++;
272 lookup = NULL;
273 yperr = yp_match(domain, (char *)NETID, netname, strlen(netname),
274 &lookup, &len);
275 switch (yperr) {
276 case 0:
277 break; /* the successful case */
279 default :
281 * XXX not sure about yp_match semantics.
282 * should err be set to NOTFOUND here?
284 *err = __NSW_UNAVAIL;
285 return (0);
287 if (lookup) {
288 lookup[len] = '\0';
289 *err = parse_netid_str(lookup, argp);
290 free(lookup);
291 return (*err == __NSW_SUCCESS);
293 *err = __NSW_NOTFOUND;
294 return (0);
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.
306 static int
307 netname2user_ldap(int *err, char *netname, struct netid_userdata *argp)
309 char buf[NSS_LINELEN_PASSWD];
310 char *p2, *lasts;
311 struct passwd pw;
312 uid_t uidnu;
313 int ngroups = 0;
314 int count;
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;
322 return (0);
325 /* get the uid from the netname */
326 if (strtok_r(buf, ".", &lasts) == NULL) {
327 *err = __NSW_UNAVAIL;
328 return (0);
330 if ((p2 = strtok_r(NULL, "@", &lasts)) == NULL) {
331 *err = __NSW_UNAVAIL;
332 return (0);
334 uidnu = atoi(p2);
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;
343 return (0);
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);
356 if (ngroups < 0) {
357 *err = __NSW_UNAVAIL;
358 return (0);
361 *(argp->gidlenp) = ngroups;
363 for (count = 0; count < ngroups; count++) {
364 (argp->gidlist[count]) = groups[count];
367 *err = __NSW_SUCCESS;
368 return (1);
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;
384 int err;
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) {
393 *uidp = NOBODY_UID;
394 *gidp = NOBODY_UID;
395 *gidlenp = 0;
396 return (1);
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)
407 return (0);
408 if (!isdigit(netname[OPSYS_LEN+1])) /* check for uid string */
409 return (0);
411 argp.uidp = uidp;
412 argp.gidp = gidp;
413 argp.gidlenp = gidlenp;
414 argp.gidlist = gidlist;
415 (void) mutex_lock(&serialize_netname_r);
417 conf = __nsw_getconfig("publickey", &perr);
418 if (!conf) {
419 conf = &publickey_default;
420 needfree = 0;
421 } else
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);
431 else {
432 syslog(LOG_INFO,
433 "netname2user: unknown nameservice for publickey"
434 "info '%s'\n", look->service_name);
435 err = __NSW_UNAVAIL;
436 res = 0;
438 switch (look->actions[err]) {
439 case __NSW_CONTINUE :
440 break;
441 case __NSW_RETURN :
442 if (needfree)
443 (void) __nsw_freeconfig(conf);
444 (void) mutex_unlock(&serialize_netname_r);
445 return (res);
446 default :
447 syslog(LOG_ERR,
448 "netname2user: Unknown action for "
449 "nameservice '%s'", look->service_name);
452 if (needfree)
453 (void) __nsw_freeconfig(conf);
454 (void) mutex_unlock(&serialize_netname_r);
455 return (0);
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,
469 const int hostlen)
471 char *p, *domainname;
472 int len, dlen;
474 if (!netname) {
475 syslog(LOG_ERR, "netname2host: null netname");
476 goto bad_exit;
479 if (strncmp(netname, OPSYS, OPSYS_LEN) != 0)
480 goto bad_netname;
481 p = (char *)netname + OPSYS_LEN; /* skip OPSYS part */
482 if (*p != '.')
483 goto bad_netname;
484 ++p; /* skip '.' */
486 domainname = strchr(p, '@'); /* get domain name */
487 if (domainname == 0)
488 goto bad_netname;
490 len = domainname - p; /* host sits between '.' and '@' */
491 domainname++; /* skip '@' sign */
493 if (len <= 0)
494 goto bad_netname;
496 if (hostlen < len) {
497 syslog(LOG_ERR,
498 "netname2host: insufficient space for hostname");
499 goto bad_exit;
502 if (isdigit(*p)) /* don't want uid here */
503 goto bad_netname;
505 if (*p == '\0') /* check for null hostname */
506 goto bad_netname;
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)) {
513 syslog(LOG_ERR,
514 "netname2host: insufficient space for hostname");
515 goto bad_exit;
518 hostname[len] = '.';
519 (void) strncpy(hostname+len+1, domainname, dlen);
520 hostname[len+dlen+1] = '\0';
522 return (1);
524 bad_netname:
525 syslog(LOG_ERR, "netname2host: invalid host netname %s", netname);
527 bad_exit:
528 hostname[0] = '\0';
529 return (0);