xref: /titanic_50/usr/src/lib/libnsl/rpc/netnamer.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  *
22  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
27 /*
28  * Portions of this source code were derived from Berkeley
29  * 4.3 BSD under license from the Regents of the University of
30  * California.
31  */
32 /*
33  * ==== hack-attack:  possibly MT-safe but definitely not MT-hot.
34  * ==== turn this into a real switch frontend and backends
35  *
36  * Well, at least the API doesn't involve pointers-to-static.
37  */
38 
39 #pragma ident	"%Z%%M%	%I%	%E% SMI"
40 
41 /*
42  * netname utility routines convert from netnames to unix names (uid, gid)
43  *
44  * This module is operating system dependent!
45  * What we define here will work with any unix system that has adopted
46  * the Sun NIS domain architecture.
47  */
48 
49 #undef NIS
50 #include "mt.h"
51 #include "rpc_mt.h"
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <sys/types.h>
55 #include <rpc/trace.h>
56 #include <ctype.h>
57 #include <grp.h>
58 #include <pwd.h>
59 #include <string.h>
60 #include <syslog.h>
61 #include <sys/param.h>
62 #include <nsswitch.h>
63 #include <rpc/rpc.h>
64 #include <rpcsvc/nis.h>
65 #include <rpcsvc/ypclnt.h>
66 #include "nsl_stdio_prv.h"
67 #include <nss_dbdefs.h>
68 
69 static const char    OPSYS[]	= "unix";
70 static const char    NETIDFILE[] = "/etc/netid";
71 static const char    NETID[]	= "netid.byname";
72 static const char    PKTABLE[]  = "cred.org_dir";
73 #define	PKTABLE_LEN 12
74 #define	OPSYS_LEN 4
75 
76 #ifndef NGROUPS
77 #define	NGROUPS 16
78 #endif
79 
80 extern int _getgroupsbymember(const char *, gid_t[], int, int);
81 
82 /*
83  * the value for NOBODY_UID is set by the SVID. The following define also
84  * appears in netname.c
85  */
86 
87 #define	NOBODY_UID 60001
88 
89 /*
90  *	default publickey policy:
91  *		publickey: nis [NOTFOUND = return] files
92  */
93 
94 
95 /*		NSW_NOTSUCCESS  NSW_NOTFOUND   NSW_UNAVAIL    NSW_TRYAGAIN */
96 #define	DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
97 
98 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL},
99 		lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files};
100 static struct __nsw_switchconfig publickey_default =
101 			{0, "publickey", 2, &lookup_nis};
102 
103 static mutex_t serialize_netname_r = DEFAULTMUTEX;
104 
105 struct netid_userdata {
106 	uid_t	*uidp;
107 	gid_t	*gidp;
108 	int	*gidlenp;
109 	gid_t	*gidlist;
110 };
111 
112 static int
113 parse_uid(s, argp)
114 	char *s;
115 	struct netid_userdata *argp;
116 {
117 	uid_t	u;
118 
119 	trace1(TR_parse_uid, 0);
120 
121 	if (!s || !isdigit(*s)) {
122 		syslog(LOG_ERR,
123 			"netname2user: expecting uid '%s'", s);
124 		trace1(TR_parse_uid, 1);
125 		return (__NSW_NOTFOUND); /* xxx need a better error */
126 	}
127 
128 	/* Fetch the uid */
129 	u = (uid_t)(atoi(s));
130 
131 	if (u == 0) {
132 		syslog(LOG_ERR, "netname2user: should not have uid 0");
133 		trace1(TR_parse_uid, 1);
134 		return (__NSW_NOTFOUND);
135 	}
136 	*(argp->uidp) = u;
137 	trace1(TR_parse_uid, 1);
138 	return (__NSW_SUCCESS);
139 }
140 
141 
142 /* parse a comma separated gid list */
143 static int
144 parse_gidlist(p, argp)
145 	char *p;
146 	struct netid_userdata *argp;
147 {
148 	int len;
149 	gid_t	g;
150 
151 	trace1(TR_parse_gidlist, 0);
152 	if (! p || (! isdigit(*p))) {
153 		syslog(LOG_ERR,
154 			"netname2user: missing group id list in '%s'.",
155 			p);
156 		trace1(TR_parse_gidlist, 1);
157 		return (__NSW_NOTFOUND);
158 	}
159 
160 	g = (gid_t)(atoi(p));
161 	*(argp->gidp) = g;
162 
163 	len = 0;
164 	while (p = strchr(p, ','))
165 		argp->gidlist[len++] = (gid_t)atoi(++p);
166 	*(argp->gidlenp) = len;
167 	trace1(TR_parse_gidlist, 1);
168 	return (__NSW_SUCCESS);
169 }
170 
171 
172 /*
173  * parse_netid_str()
174  *
175  * Parse uid and group information from the passed string.
176  *
177  * The format of the string passed is
178  * 	uid:gid,grp,grp, ...
179  *
180  */
181 static int
182 parse_netid_str(s, argp)
183 	char	*s;
184 	struct netid_userdata *argp;
185 {
186 	char	*p;
187 	int	err;
188 
189 	trace1(TR_parse_netid_str, 0);
190 
191 	/* get uid */
192 	err = parse_uid(s, argp);
193 	if (err != __NSW_SUCCESS) {
194 		trace1(TR_parse_netid_str, 1);
195 		return (err);
196 	}
197 
198 
199 	/* Now get the group list */
200 	p = strchr(s, ':');
201 	if (!p) {
202 		syslog(LOG_ERR,
203 			"netname2user: missing group id list in '%s'",
204 			s);
205 		trace1(TR_parse_netid_str, 1);
206 		return (__NSW_NOTFOUND);
207 	}
208 	++p;			/* skip ':' */
209 	err = parse_gidlist(p, argp);
210 	trace1(TR_parse_netid_str, 1);
211 	return (err);
212 }
213 
214 static int
215 parse_uid_gidlist(ustr, gstr, argp)
216 	char *ustr;
217 	char *gstr;
218 	struct netid_userdata *argp;
219 {
220 	int	err;
221 
222 	trace1(TR_parse_uid_gidlist, 0);
223 
224 	/* get uid */
225 	err = parse_uid(ustr, argp);
226 	if (err != __NSW_SUCCESS) {
227 		trace1(TR_parse_uid_gidlist, 1);
228 		return (err);
229 	}
230 
231 	/* Now get the group list */
232 	err = parse_gidlist(gstr, argp);
233 	trace1(TR_parse_uid_gidlist, 1);
234 	return (err);
235 }
236 
237 
238 /*
239  * netname2user_files()
240  *
241  * This routine fetches the netid information from the "files" nameservice.
242  * ie /etc/netid.
243  */
244 static int
245 netname2user_files(err, netname, argp)
246 int *err;
247 char *netname;
248 struct netid_userdata *argp;
249 {
250 	char 	buf[512];	/* one line from the file */
251 	char	*name;
252 	char	*value;
253 	char 	*res;
254 	__NSL_FILE *fd;
255 
256 	trace1(TR_netname2user_files, 0);
257 	fd = __nsl_fopen(NETIDFILE, "r");
258 	if (fd == (__NSL_FILE *)0) {
259 		*err = __NSW_UNAVAIL;
260 		trace1(TR_netname2user_files, 1);
261 		return (0);
262 	}
263 	/*
264 	 * for each line in the file parse it appropriately
265 	 * file format is :
266 	 *	netid	uid:grp,grp,grp # for users
267 	 *	netid	0:hostname	# for hosts
268 	 */
269 	while (! __nsl_feof(fd)) {
270 		res = __nsl_fgets(buf, 512, fd);
271 		if (res == NULL)
272 			break;
273 
274 		/* Skip comments and blank lines */
275 		if ((*res == '#') || (*res == '\n'))
276 			continue;
277 
278 		name = &(buf[0]);
279 		while (isspace(*name))
280 			name++;
281 		if (*name == '\0')	/* blank line continue */
282 			continue;
283 		value = name;		/* will contain the value eventually */
284 		while (! isspace(*value))
285 			value++;
286 		if (*value == '\0') {
287 			syslog(LOG_WARNING,
288 				"netname2user: badly formatted line in %s.",
289 				NETIDFILE);
290 			continue;
291 		}
292 		*value++ = '\0'; /* nul terminate the name */
293 
294 		if (strcasecmp(name, netname) == 0) {
295 			__nsl_fclose(fd);
296 			while (isspace(*value))
297 				value++;
298 			*err = parse_netid_str(value, argp);
299 			trace1(TR_netname2user_files, 1);
300 			return (*err == __NSW_SUCCESS);
301 		}
302 	}
303 	__nsl_fclose(fd);
304 	*err = __NSW_NOTFOUND;
305 	trace1(TR_netname2user_files, 1);
306 	return (0);
307 }
308 
309 /*
310  * netname2user_nis()
311  *
312  * This function reads the netid from the NIS (YP) nameservice.
313  */
314 static int
315 netname2user_nis(err, netname, argp)
316 	int *err;
317 	char *netname;
318 	struct netid_userdata *argp;
319 {
320 	char *domain;
321 	int yperr;
322 	char *lookup;
323 	int len;
324 
325 	trace1(TR_netname2user_nis, 0);
326 	domain = strchr(netname, '@');
327 	if (! domain) {
328 		*err = __NSW_UNAVAIL;
329 		trace1(TR_netname2user_nis, 1);
330 		return (0);
331 	}
332 
333 	/* Point past the '@' character */
334 	domain++;
335 	lookup = NULL;
336 	yperr = yp_match(domain, (char *)NETID, netname, strlen(netname),
337 			&lookup, &len);
338 	switch (yperr) {
339 		case 0:
340 			break; /* the successful case */
341 
342 		default :
343 			/*
344 			 *  XXX not sure about yp_match semantics.
345 			 * should err be set to NOTFOUND here?
346 			 */
347 			*err = __NSW_UNAVAIL;
348 			trace1(TR_netname2user_nis, 1);
349 			return (0);
350 	}
351 	if (lookup) {
352 		lookup[len] = '\0';
353 		*err = parse_netid_str(lookup, argp);
354 		free(lookup);
355 		trace1(TR_netname2user_nis, 1);
356 		return (*err == __NSW_SUCCESS);
357 	} else {
358 		trace1(TR_netname2user_nis, 1);
359 		*err = __NSW_NOTFOUND;
360 		return (0);
361 	}
362 }
363 
364 /*
365  * Obtain user information (uid, gidlist) from nisplus.
366  * What we're trying to do here is to map a netname into
367  * local unix information (uid, gids), relevant in
368  * the *local* domain.
369  *
370  *	 cname   auth_type auth_name public  private
371  * ----------------------------------------------------------
372  *	nisname   DES     netname   pubkey  prikey
373  *	nisname   LOCAL   uid       gidlist
374  *
375  * 1.  Find out which 'home' domain to look for user's DES entry.
376  *	This is gotten from the domain part of the netname.
377  * 2.  Get the nisplus principal name from the DES entry in the cred
378  *	table of user's home domain.
379  * 3.  Use the nisplus principal name and search in the cred table of
380  *	the *local* directory for the LOCAL entry.
381  *
382  * Note that we need this translation of netname to <uid,gidlist> to be
383  * secure, so we *must* use authenticated connections.
384  */
385 static int
386 netname2user_nisplus(err, netname, argp)
387 	int *err;
388 	char *netname;
389 	struct netid_userdata *argp;
390 {
391 	char *domain;
392 	nis_result *res;
393 	char	sname[NIS_MAXNAMELEN+1]; /*  search criteria + table name */
394 	char	principal[NIS_MAXNAMELEN+1];
395 	int len;
396 
397 	trace1(TR_netname2user_nisplus, 0);
398 
399 
400 	/* 1.  Get home domain of user. */
401 	domain = strchr(netname, '@');
402 	if (! domain) {
403 		*err = __NSW_UNAVAIL;
404 		trace1(TR_netname2user_nisplus, 1);
405 		return (0);
406 	}
407 	domain++;  /* skip '@' */
408 
409 
410 	/* 2.  Get user's nisplus principal name.  */
411 	if ((strlen(netname)+strlen(domain)+PKTABLE_LEN+32) >
412 		(size_t)NIS_MAXNAMELEN) {
413 		*err = __NSW_UNAVAIL;
414 		trace1(TR_netname2user_nisplus, 1);
415 		return (0);
416 	}
417 	sprintf(sname, "[auth_name=\"%s\",auth_type=DES],%s.%s",
418 		netname, PKTABLE, domain);
419 	if (sname[strlen(sname) - 1] != '.')
420 		strcat(sname, ".");
421 
422 	/* must use authenticated call here */
423 	/* XXX but we cant, for now. XXX */
424 	res = nis_list(sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
425 	    NULL, NULL);
426 	switch (res->status) {
427 	case NIS_SUCCESS:
428 	case NIS_S_SUCCESS:
429 		break;   /* go and do something useful */
430 	case NIS_NOTFOUND:
431 	case NIS_PARTIAL:
432 	case NIS_NOSUCHNAME:
433 	case NIS_NOSUCHTABLE:
434 		*err = __NSW_NOTFOUND;
435 		nis_freeresult(res);
436 		trace1(TR_netname2user_nisplus, 1);
437 		return (0);
438 	case NIS_S_NOTFOUND:
439 	case NIS_TRYAGAIN:
440 		*err = __NSW_TRYAGAIN;
441 		syslog(LOG_ERR,
442 			"netname2user: (nis+ lookup): %s\n",
443 			nis_sperrno(res->status));
444 		nis_freeresult(res);
445 		trace1(TR_netname2user_nisplus, 1);
446 		return (0);
447 	default:
448 		*err = __NSW_UNAVAIL;
449 		syslog(LOG_ERR, "netname2user: (nis+ lookup): %s\n",
450 			nis_sperrno(res->status));
451 		nis_freeresult(res);
452 		trace1(TR_netname2user_nisplus, 1);
453 		return (0);
454 	}
455 
456 	if (res->objects.objects_len > 1) {
457 		/*
458 		 * A netname belonging to more than one principal?
459 		 * Something wrong with cred table. should be unique.
460 		 * Warn user and continue.
461 		 */
462 		syslog(LOG_ALERT,
463 			"netname2user: DES entry for %s in \
464 			directory %s not unique",
465 			netname, domain);
466 	}
467 
468 	len = ENTRY_LEN(res->objects.objects_val, 0);
469 	strncpy(principal, ENTRY_VAL(res->objects.objects_val, 0), len);
470 	principal[len] = '\0';
471 	nis_freeresult(res);
472 
473 	if (principal[0] == '\0') {
474 		*err = __NSW_UNAVAIL;
475 		trace1(TR_netname2user_nisplus, 1);
476 		return (0);
477 	}
478 
479 	/*
480 	 *	3.  Use principal name to look up uid/gid information in
481 	 *	LOCAL entry in **local** cred table.
482 	 */
483 	domain = nis_local_directory();
484 	if ((strlen(principal)+strlen(domain)+PKTABLE_LEN+30) >
485 		(size_t)NIS_MAXNAMELEN) {
486 		*err = __NSW_UNAVAIL;
487 		syslog(LOG_ERR, "netname2user: principal name '%s' too long",
488 			principal);
489 		trace1(TR_netname2user_nisplus, 1);
490 		return (0);
491 	}
492 	sprintf(sname, "[cname=\"%s\",auth_type=LOCAL],%s.%s",
493 		principal, PKTABLE, domain);
494 	if (sname[strlen(sname) - 1] != '.')
495 		strcat(sname, ".");
496 
497 	/* must use authenticated call here */
498 	/* XXX but we cant, for now. XXX */
499 	res = nis_list(sname, USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
500 	    NULL, NULL);
501 	switch (res->status) {
502 	case NIS_NOTFOUND:
503 	case NIS_PARTIAL:
504 	case NIS_NOSUCHNAME:
505 	case NIS_NOSUCHTABLE:
506 		*err = __NSW_NOTFOUND;
507 		nis_freeresult(res);
508 		trace1(TR_netname2user_nisplus, 1);
509 		return (0);
510 	case NIS_S_NOTFOUND:
511 	case NIS_TRYAGAIN:
512 		*err = __NSW_TRYAGAIN;
513 		syslog(LOG_ERR,
514 			"netname2user: (nis+ lookup): %s\n",
515 			nis_sperrno(res->status));
516 		nis_freeresult(res);
517 		trace1(TR_netname2user_nisplus, 1);
518 		return (0);
519 	case NIS_SUCCESS:
520 	case NIS_S_SUCCESS:
521 		break;   /* go and do something useful */
522 	default:
523 		*err = __NSW_UNAVAIL;
524 		syslog(LOG_ERR, "netname2user: (nis+ lookup): %s\n",
525 			nis_sperrno(res->status));
526 		nis_freeresult(res);
527 		trace1(TR_netname2user_nisplus, 1);
528 		return (0);
529 	}
530 
531 	if (res->objects.objects_len > 1) {
532 		/*
533 		 * A principal can have more than one LOCAL entry?
534 		 * Something wrong with cred table.
535 		 * Warn user and continue.
536 		 */
537 		syslog(LOG_ALERT,
538 			"netname2user: LOCAL entry for %s in\
539 				directory %s not unique",
540 			netname, domain);
541 	}
542 	/* nisname	LOCAL	uid 	grp,grp,grp */
543 	*err = parse_uid_gidlist(ENTRY_VAL(res->objects.objects_val, 2),
544 					/* uid */
545 			ENTRY_VAL(res->objects.objects_val, 3), /* gids */
546 			argp);
547 	nis_freeresult(res);
548 	trace1(TR_netname2user_nisplus, 1);
549 	return (*err == __NSW_SUCCESS);
550 }
551 
552 /*
553  * Build the uid and gid from the netname for users in LDAP.
554  * There is no netid container in LDAP. For this we build
555  * the netname to user data dynamically from the passwd and
556  * group data. This works only for users in a single domain.
557  * This function is an interim solution until we support a
558  * netid container in LDAP which enables us to do netname2user
559  * resolution for multiple domains.
560  */
561 static int
562 netname2user_ldap(err, netname, argp)
563 	int *err;
564 	char *netname;
565 	struct netid_userdata *argp;
566 {
567 
568 	char buf[NSS_LINELEN_PASSWD];
569 	char *p1, *p2, *lasts;
570 	struct passwd pw;
571 	uid_t uidnu;
572 	int ngroups = 0;
573 	int count;
574 	char pwbuf[NSS_LINELEN_PASSWD];
575 	gid_t groups[NGROUPS_MAX];
576 
577 	if (strlcpy(buf, netname, NSS_LINELEN_PASSWD) >= NSS_LINELEN_PASSWD) {
578 		*err = __NSW_UNAVAIL;
579 		return (0);
580 	}
581 
582 	/* get the uid from the netname */
583 	if ((p1 = strtok_r(buf, ".", &lasts)) == NULL) {
584 		*err = __NSW_UNAVAIL;
585 		return (0);
586 	}
587 	if ((p2 = strtok_r(NULL, "@", &lasts)) == NULL) {
588 		*err = __NSW_UNAVAIL;
589 		return (0);
590 	}
591 	uidnu = atoi(p2);
592 
593 	/*
594 	 * check out the primary group and crosscheck the uid
595 	 * with the passwd data
596 	 */
597 	if ((getpwuid_r(uidnu, &pw, pwbuf, sizeof (pwbuf))) == NULL) {
598 		*err = __NSW_UNAVAIL;
599 		return (0);
600 	}
601 
602 	*(argp->uidp) = pw.pw_uid;
603 	*(argp->gidp) = pw.pw_gid;
604 
605 	/* search through all groups for membership */
606 
607 	groups[0] = pw.pw_gid;
608 
609 	ngroups = _getgroupsbymember(pw.pw_name, groups, NGROUPS_MAX,
610 				(pw.pw_gid >= 0) ? 1 : 0);
611 
612 	if (ngroups < 0) {
613 		*err = __NSW_UNAVAIL;
614 		return (0);
615 	}
616 
617 	*(argp->gidlenp) = ngroups;
618 
619 	for (count = 0; count < ngroups; count++) {
620 		(argp->gidlist[count]) = groups[count];
621 	}
622 
623 	*err = __NSW_SUCCESS;
624 	return (1);
625 
626 }
627 
628 /*
629  * Convert network-name into unix credential
630  */
631 int
632 netname2user(netname, uidp, gidp, gidlenp, gidlist)
633 	const char netname[MAXNETNAMELEN + 1];
634 	uid_t *uidp;
635 	gid_t *gidp;
636 	int *gidlenp;
637 	gid_t *gidlist;
638 {
639 	struct __nsw_switchconfig *conf;
640 	struct __nsw_lookup *look;
641 	enum __nsw_parse_err perr;
642 	int needfree = 1, res;
643 	struct netid_userdata argp;
644 	int err;
645 
646 	trace1(TR_netname2user, 0);
647 
648 	/*
649 	 * Take care of the special case of nobody. Compare the netname
650 	 * to the string "nobody". If they are equal, return the SVID
651 	 * standard value for nobody.
652 	 */
653 
654 	if (strcmp(netname, "nobody") == 0) {
655 		*uidp = NOBODY_UID;
656 		*gidp = NOBODY_UID;
657 		*gidlenp = 0;
658 		return (1);
659 	}
660 
661 	/*
662 	 * First we do some generic sanity checks on the name we were
663 	 * passed. This lets us assume they are correct in the backends.
664 	 *
665 	 * NOTE: this code only recognizes names of the form :
666 	 *		unix.UID@domainname
667 	 */
668 	if (strncmp(netname, OPSYS, OPSYS_LEN) != 0) {
669 		trace1(TR_netname2user, 1);
670 		return (0);
671 	}
672 	if (! isdigit(netname[OPSYS_LEN+1])) { /* check for uid string */
673 		trace1(TR_netname2user, 1);
674 		return (0);
675 	}
676 
677 	argp.uidp = uidp;
678 	argp.gidp = gidp;
679 	argp.gidlenp = gidlenp;
680 	argp.gidlist = gidlist;
681 	mutex_lock(&serialize_netname_r);
682 
683 	conf = __nsw_getconfig("publickey", &perr);
684 	if (! conf) {
685 		conf = &publickey_default;
686 		needfree = 0;
687 	} else
688 		needfree = 1; /* free the config structure */
689 
690 	for (look = conf->lookups; look; look = look->next) {
691 		if (strcmp(look->service_name, "nisplus") == 0)
692 			res = netname2user_nisplus(&err,
693 						(char *)netname, &argp);
694 		else if (strcmp(look->service_name, "nis") == 0)
695 			res = netname2user_nis(&err, (char *)netname, &argp);
696 		else if (strcmp(look->service_name, "files") == 0)
697 			res = netname2user_files(&err, (char *)netname, &argp);
698 		else if (strcmp(look->service_name, "ldap") == 0)
699 			res = netname2user_ldap(&err, (char *)netname, &argp);
700 		else {
701 			syslog(LOG_INFO,
702 		"netname2user: unknown nameservice for publickey info '%s'\n",
703 						look->service_name);
704 			err = __NSW_UNAVAIL;
705 		}
706 		switch (look->actions[err]) {
707 			case __NSW_CONTINUE :
708 				break;
709 			case __NSW_RETURN :
710 				if (needfree)
711 					__nsw_freeconfig(conf);
712 				mutex_unlock(&serialize_netname_r);
713 				trace1(TR_netname2user, 1);
714 				return (res);
715 			default :
716 				syslog(LOG_ERR,
717 			"netname2user: Unknown action for nameservice '%s'",
718 							look->service_name);
719 		}
720 	}
721 	if (needfree)
722 		__nsw_freeconfig(conf);
723 	mutex_unlock(&serialize_netname_r);
724 	trace1(TR_netname2user, 1);
725 	return (0);
726 }
727 
728 /*
729  * Convert network-name to hostname (fully qualified)
730  * NOTE: this code only recognizes names of the form :
731  *		unix.HOST@domainname
732  *
733  * This is very simple.  Since the netname is of the form:
734  *	unix.host@domainname
735  * We just construct the hostname using information from the domainname.
736  */
737 int
738 netname2host(netname, hostname, hostlen)
739 	const char netname[MAXNETNAMELEN + 1];
740 	char *hostname;
741 	int hostlen;
742 {
743 	char *p, *domainname;
744 	int len, dlen;
745 
746 	trace1(TR_netname2host, 0);
747 
748 	if (!netname) {
749 		syslog(LOG_ERR, "netname2host: null netname");
750 		goto bad_exit;
751 	}
752 
753 	if (strncmp(netname, OPSYS, OPSYS_LEN) != 0)
754 		goto bad_netname;
755 	p = (char *)netname + OPSYS_LEN;	/* skip OPSYS part */
756 	if (*p != '.')
757 		goto bad_netname;
758 	++p;				/* skip '.' */
759 
760 	domainname = strchr(p, '@');	/* get domain name */
761 	if (domainname == 0)
762 		goto bad_netname;
763 
764 	len = domainname - p;		/* host sits between '.' and '@' */
765 	domainname++;			/* skip '@' sign */
766 
767 	if (len <= 0)
768 		goto bad_netname;
769 
770 	if (hostlen < len) {
771 		syslog(LOG_ERR,
772 			"netname2host: insufficient space for hostname");
773 		goto bad_exit;
774 	}
775 
776 	if (isdigit(*p))		/* don't want uid here */
777 		goto bad_netname;
778 
779 	if (*p == '\0')			/* check for null hostname */
780 		goto bad_netname;
781 
782 	strncpy(hostname, p, len);
783 
784 	/* make into fully qualified hostname by concatenating domain part */
785 	dlen = strlen(domainname);
786 	if (hostlen < (len + dlen + 2)) {
787 		syslog(LOG_ERR,
788 			"netname2host: insufficient space for hostname");
789 		goto bad_exit;
790 	}
791 
792 	hostname[len] = '.';
793 	strncpy(hostname+len+1, domainname, dlen);
794 	hostname[len+dlen+1] = '\0';
795 
796 	trace1(TR_netname2host, 1);
797 	return (1);
798 
799 
800 bad_netname:
801 	syslog(LOG_ERR, "netname2host: invalid host netname %s", netname);
802 
803 bad_exit:
804 	hostname[0] = '\0';
805 	trace1(TR_netname2host, 1);
806 	return (0);
807 }
808