xref: /illumos-gate/usr/src/lib/libnsl/rpc/netname.c (revision 4eaa471005973e11a6110b69fe990530b3b95a38)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28 /*
29  * Portions of this source code were derived from Berkeley
30  * 4.3 BSD under license from the Regents of the University of
31  * California.
32  */
33 /*
34  * ==== hack-attack:  possibly MT-safe but definitely not MT-hot.
35  * ==== turn this into a real switch frontend and backends
36  *
37  * Well, at least the API doesn't involve pointers-to-static.
38  */
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 /*
43  * netname utility routines (getnetname, user2netname, host2netname).
44  *
45  * Convert from unix names (uid, gid) to network wide names.
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.
49  */
50 
51 #undef NIS
52 
53 #include "mt.h"
54 #include "rpc_mt.h"
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <sys/types.h>
59 #include <ctype.h>
60 #include <string.h>
61 #include <syslog.h>
62 #include <sys/param.h>
63 #include <rpc/rpc.h>
64 #include <rpcsvc/nis.h>
65 #include <rpcsvc/nis_dhext.h>
66 #include <nsswitch.h>
67 #include <syslog.h>
68 #include <errno.h>
69 
70 #ifndef MAXHOSTNAMELEN
71 #define	MAXHOSTNAMELEN 256
72 #endif
73 #ifndef NGROUPS
74 #define	NGROUPS 16
75 #endif
76 
77 /*
78  * the value for NOBODY_UID is set by the SVID. The following define also
79  * appears in netnamer.c
80  */
81 
82 #define	NOBODY_UID 60001
83 
84 extern int __nis_principal();
85 extern int getdomainname();
86 extern int key_call();
87 #define	OPSYS_LEN 4
88 #define	PKTABLE_LEN 12
89 static const char *OPSYS = "unix";
90 static const char *PKTABLE  = "cred.org_dir";
91 
92 
93 /*
94  * default publickey policy:
95  *	publickey: nis [NOTFOUND = return] files
96  */
97 
98 
99 /*	NSW_NOTSUCCESS  NSW_NOTFOUND   NSW_UNAVAIL    NSW_TRYAGAIN */
100 #define	DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
101 
102 static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL},
103 		lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files};
104 static struct __nsw_switchconfig publickey_default =
105 			{0, "publickey", 2, &lookup_nis};
106 
107 static mutex_t serialize_netname = ERRORCHECKMUTEX;
108 
109 /*
110  * Convert unix cred to network-name using nisplus
111  * nisplus cred table has the following format:
112  *
113  * cname	auth_type auth_name	public  private
114  * ----------------------------------------------------------
115  * nisname	DES	  netname	pubkey  private_key
116  * nisname	LOCAL	  uid		gidlist
117  *
118  * Obtain netname given <uid,domain>.
119  * 0.  If domain is NULL (indicating local domain), first try to get
120  *	netname from keyserv (keylogin sets this).
121  * 1.  Get the nisplus principal name from the LOCAL entry of the cred
122  *	table in the specified domain (the local domain if domain is NULL).
123  * 2.  Using the principal name, lookup the DES entry and extract netname.
124  */
125 
126 static int
127 user2netname_nisplus(int *err, char netname[MAXNETNAMELEN + 1], uid_t uid,
128 								char *domain)
129 {
130 	key_netstres kres;
131 	nis_result *nres;
132 	int len;
133 	uid_t my_uid;
134 	char principal[NIS_MAXNAMELEN+1];
135 	char buf[NIS_MAXNAMELEN+1];
136 	int status;
137 	mechanism_t **mechs;
138 	char auth_type[MECH_MAXATNAME+1];
139 
140 	my_uid = geteuid();
141 
142 	if (my_uid == uid && domain == NULL) {
143 		/*
144 		 * Look up the keyserv interface routines to see if
145 		 * netname is stored there.
146 		 */
147 		kres.key_netstres_u.knet.st_netname = NULL;
148 		if (key_call((rpcproc_t)KEY_NET_GET, xdr_void, NULL,
149 				xdr_key_netstres, (char *)&kres) &&
150 		    kres.status == KEY_SUCCESS) {
151 			len = strlen(kres.key_netstres_u.knet.st_netname);
152 			(void) strncpy(netname,
153 				kres.key_netstres_u.knet.st_netname,
154 				len +1);
155 			free(kres.key_netstres_u.knet.st_netname);
156 			*err = __NSW_SUCCESS;
157 			return (1);
158 		}
159 	}
160 
161 
162 	/*
163 	 * 1.  Determine user's nis+ principal name.
164 	 *
165 	 * If domain is specified, we want to look up the uid in the
166 	 * specified domain to determine the user's principal name.
167 	 * Otherwise, get principal name from local directory.
168 	 */
169 	if (domain == NULL)
170 		domain = nis_local_directory();
171 	/*
172 	 * Don't use nis_local_principal here because we want to
173 	 * catch the TRYAGAIN case so that we handle it properly.
174 	 */
175 	status = __nis_principal(principal, uid, domain);
176 
177 	if (status != NIS_SUCCESS && status != NIS_S_SUCCESS) {
178 		switch (status) {
179 		case NIS_NOTFOUND:
180 		case NIS_PARTIAL:
181 		case NIS_NOSUCHNAME:
182 		case NIS_NOSUCHTABLE:
183 			*err = __NSW_NOTFOUND;
184 			break;
185 		case NIS_S_NOTFOUND:
186 		case NIS_TRYAGAIN:
187 			*err = __NSW_TRYAGAIN;
188 			syslog(LOG_ERR,
189 				"user2netname: (nis+ lookup): %s\n",
190 				nis_sperrno(status));
191 			break;
192 		default:
193 			*err = __NSW_UNAVAIL;
194 			syslog(LOG_ERR,
195 				"user2netname: (nis+ lookup): %s\n",
196 				nis_sperrno(status));
197 		}
198 
199 		return (0);
200 	}
201 
202 	/*
203 	 * 2.  use nis+ principal name to get netname by getting a PK entry.
204 	 *
205 	 * (Use NOAUTH to prevent recursion.)
206 	 */
207 	domain = nis_domain_of(principal);
208 	if ((strlen(principal)+strlen(domain)+PKTABLE_LEN+ 28) >
209 		    (size_t)NIS_MAXNAMELEN) {
210 		*err = __NSW_UNAVAIL;
211 		return (0);
212 	}
213 
214 	if (mechs = __nis_get_mechanisms(FALSE)) {
215 		mechanism_t **mpp;
216 
217 		/*
218 		 * Loop thru mechanism types till we find one in the
219 		 * cred table for this user.
220 		 */
221 		for (mpp = mechs; *mpp; mpp++) {
222 			mechanism_t *mp = *mpp;
223 
224 			if (AUTH_DES_COMPAT_CHK(mp)) {
225 				__nis_release_mechanisms(mechs);
226 				goto try_auth_des;
227 			}
228 			if (!VALID_MECH_ENTRY(mp))
229 				continue;
230 
231 			if (!__nis_mechalias2authtype(mp->alias, auth_type,
232 							sizeof (auth_type)))
233 				continue;
234 
235 			(void) snprintf(buf, sizeof (buf),
236 				"[cname=\"%s\",auth_type=\"%s\"],%s.%s",
237 				principal, auth_type, PKTABLE, domain);
238 			if (buf[strlen(buf)-1] != '.')
239 				(void) strcat(buf, ".");
240 
241 			nres = nis_list(buf,
242 				USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
243 				NULL, NULL);
244 
245 			/*
246 			 * If the entry is not found, let's try the next one,
247 			 * else it's success or a serious enough NIS+ err
248 			 * to bail on.
249 			 */
250 			if (nres->status != NIS_NOTFOUND)
251 				break;
252 		}
253 	} else {
254 	try_auth_des:
255 		/*
256 		 * No valid mechs exist or the AUTH_DES compat entry was
257 		 * found in the security cf.
258 		 */
259 		(void) snprintf(buf, sizeof (buf),
260 					"[cname=\"%s\",auth_type=DES],%s.%s",
261 			principal, PKTABLE, domain);
262 		if (buf[strlen(buf)-1] != '.')
263 			(void) strcat(buf, ".");
264 
265 		nres = nis_list(buf,
266 				USE_DGRAM+NO_AUTHINFO+FOLLOW_LINKS+FOLLOW_PATH,
267 				NULL, NULL);
268 	}
269 
270 	switch (nres->status) {
271 	case NIS_SUCCESS:
272 	case NIS_S_SUCCESS:
273 		break;   /* go and do something useful */
274 	case NIS_NOTFOUND:
275 	case NIS_PARTIAL:
276 	case NIS_NOSUCHNAME:
277 	case NIS_NOSUCHTABLE:
278 		*err = __NSW_NOTFOUND;
279 		nis_freeresult(nres);
280 		return (0);
281 	case NIS_S_NOTFOUND:
282 	case NIS_TRYAGAIN:
283 		*err = __NSW_TRYAGAIN;
284 		syslog(LOG_ERR,
285 			"user2netname: (nis+ lookup): %s\n",
286 			nis_sperrno(nres->status));
287 		nis_freeresult(nres);
288 		return (0);
289 	default:
290 		*err = __NSW_UNAVAIL;
291 		syslog(LOG_ERR, "user2netname: (nis+ lookup): %s\n",
292 			nis_sperrno(nres->status));
293 		nis_freeresult(nres);
294 		return (0);
295 	}
296 
297 	if (nres->objects.objects_len > 1) {
298 		/*
299 		 * Principal with more than one entry for this mech type?
300 		 * Something wrong with cred table. Should be unique.
301 		 * Warn user and continue.
302 		 */
303 		syslog(LOG_ALERT,
304 			"user2netname: %s entry for %s not unique",
305 			auth_type, principal);
306 	}
307 
308 	len = ENTRY_LEN(nres->objects.objects_val, 2);
309 	if (len > MAXNETNAMELEN) {
310 		*err = __NSW_UNAVAIL;
311 		syslog(LOG_ERR, "user2netname: netname of '%s' too long",
312 			principal);
313 		nis_freeresult(nres);
314 		return (0);
315 	}
316 	(void) strncpy(netname, ENTRY_VAL(nres->objects.objects_val, 2), len);
317 	netname[len] = '\0';
318 	nis_freeresult(nres);
319 	*err = __NSW_SUCCESS;
320 	return (1);
321 }
322 
323 #define	MAXIPRINT	(11)	/* max length of printed integer */
324 
325 /*
326  * Convert unix cred to network-name by concatenating the
327  * 3 pieces of information <opsys type> <uid> <domain>.
328  */
329 
330 static int
331 user2netname_nis(int *err, char netname[MAXNETNAMELEN + 1], uid_t uid,
332 								char *domain)
333 {
334 	int i;
335 	char *dfltdom;
336 	if (domain == NULL) {
337 		if (__rpc_get_default_domain(&dfltdom) != 0) {
338 			*err = __NSW_UNAVAIL;
339 			return (0);
340 		}
341 		domain = dfltdom;
342 	}
343 	if ((strlen(domain) + OPSYS_LEN + 3 + MAXIPRINT) >
344 						(size_t)MAXNETNAMELEN) {
345 		*err = __NSW_UNAVAIL;
346 		return (0);
347 	}
348 	(void) snprintf(netname, MAXNETNAMELEN + 1,
349 					"%s.%d@%s", OPSYS, (int)uid, domain);
350 	i = strlen(netname);
351 	if (netname[i-1] == '.')
352 		netname[i-1] = '\0';
353 	*err = __NSW_SUCCESS;
354 	return (1);
355 }
356 
357 /*
358  * Figure out my fully qualified network name
359  */
360 int
361 getnetname(char name[MAXNETNAMELEN + 1])
362 {
363 	uid_t uid;
364 
365 	uid = geteuid();
366 	if (uid == 0)
367 		return (host2netname(name, NULL, NULL));
368 	return (user2netname(name, uid, NULL));
369 }
370 
371 
372 /*
373  * Figure out the fully qualified network name for the given uid.
374  * This is a private interface.
375  */
376 int
377 __getnetnamebyuid(char name[MAXNETNAMELEN + 1], uid_t uid)
378 {
379 	if (uid == 0)
380 		return (host2netname(name, NULL, NULL));
381 	return (user2netname(name, uid, NULL));
382 }
383 
384 /*
385  * Convert unix cred to network-name
386  *
387  * It uses the publickey policy in the /etc/nsswitch.conf file
388  * (Unless the netname is "nobody", which is special cased).
389  * If there is no publickey policy in /etc/nsswitch.conf,
390  * the default publickey policy is used, which is
391  *	publickey: nis [NOTFOUND=return] files
392  * Note that for the non-nisplus case, there is no failover
393  * so only the first entry would be relevant for those cases.
394  */
395 int
396 user2netname(char netname[MAXNETNAMELEN + 1], const uid_t uid,
397 							const char *domain)
398 {
399 	struct __nsw_switchconfig *conf;
400 	struct __nsw_lookup *look;
401 	int needfree = 1, res = 0;
402 	enum __nsw_parse_err perr;
403 	int err;
404 
405 	/*
406 	 * Take care of the special case of "nobody". If the uid is
407 	 * the value assigned by the SVID for nobody, return the string
408 	 * "nobody".
409 	 */
410 
411 	if (uid == NOBODY_UID) {
412 		(void) strlcpy(netname, "nobody", sizeof (netname));
413 		return (1);
414 	}
415 
416 	netname[0] = '\0';  /* make null first (no need for memset) */
417 
418 	if (mutex_lock(&serialize_netname) == EDEADLK) {
419 		/*
420 		 * This thread already holds this lock. This scenario
421 		 * occurs when a process requires a netname which
422 		 * itself requires a netname to look up. As we clearly
423 		 * can't continue like this we return 'nobody'.
424 		 */
425 		(void) strlcpy(netname, "nobody", sizeof (netname));
426 		return (1);
427 	}
428 
429 	conf = __nsw_getconfig("publickey", &perr);
430 	if (!conf) {
431 		conf = &publickey_default;
432 		needfree = 0;
433 	}
434 
435 	for (look = conf->lookups; look; look = look->next) {
436 		if (strcmp(look->service_name, "nisplus") == 0)
437 			res = user2netname_nisplus(&err,
438 						netname, uid, (char *)domain);
439 		/* ldap, nis, and files all do the same thing. */
440 		else if (strcmp(look->service_name, "ldap") == 0 ||
441 			strcmp(look->service_name, "nis") == 0 ||
442 			strcmp(look->service_name, "files") == 0)
443 			res = user2netname_nis(&err,
444 				netname, uid, (char *)domain);
445 		else {
446 			syslog(LOG_INFO,
447 				"user2netname: unknown nameservice \
448 					for publickey info '%s'\n",
449 				look->service_name);
450 			err = __NSW_UNAVAIL;
451 		}
452 		switch (look->actions[err]) {
453 			case __NSW_CONTINUE :
454 				break;
455 			case __NSW_RETURN :
456 				if (needfree)
457 					__nsw_freeconfig(conf);
458 				(void) mutex_unlock(&serialize_netname);
459 				return (res);
460 			default :
461 				syslog(LOG_ERR,
462 			"user2netname: Unknown action for nameservice '%s'",
463 			look->service_name);
464 			}
465 	}
466 	if (needfree)
467 		__nsw_freeconfig(conf);
468 	(void) mutex_unlock(&serialize_netname);
469 	return (0);
470 }
471 
472 
473 /*
474  * Convert host to network-name
475  * This routine returns following netnames given the host and domain
476  * arguments defined below: (domainname=y.z)
477  *	  Arguments
478  *	host	domain		netname
479  *	----	------		-------
480  *	-	-		unix.m@y.z (hostname=m)
481  *	-	a.b		unix.m@a.b (hostname=m)
482  *	-	-		unix.m@y.z (hostname=m.w.x)
483  *	-	a.b		unix.m@a.b (hostname=m.w.x)
484  *	h	-		unix.h@y.z
485  *	h	a.b		unix.h@a.b
486  *	h.w.x	-		unix.h@w.x
487  *	h.w.x	a.b		unix.h@a.b
488  */
489 int
490 host2netname(char netname[MAXNETNAMELEN + 1], const char *host,
491 							const char *domain)
492 {
493 	char *p;
494 	char hostname[MAXHOSTNAMELEN + 1];
495 	char domainname[MAXHOSTNAMELEN + 1];
496 	char *dot_in_host;
497 	int i;
498 	size_t len;
499 
500 	netname[0] = '\0';  /* make null first (no need for memset) */
501 
502 	if (host == NULL) {
503 		(void) strncpy(hostname, nis_local_host(), sizeof (hostname));
504 		p = (char *)strchr(hostname, '.');
505 		if (p) {
506 			*p++ = '\0';
507 			/* if no domain passed, use tail of nis_local_host() */
508 			if (domain == NULL) {
509 				domain = p;
510 			}
511 		}
512 	} else {
513 		len = strlen(host);
514 		if (len >= sizeof (hostname)) {
515 			return (0);
516 		}
517 		(void) strcpy(hostname, host);
518 	}
519 
520 	dot_in_host = (char *)strchr(hostname, '.');
521 	if (domain == NULL) {
522 		p = dot_in_host;
523 		if (p) {
524 			p = (char *)nis_domain_of(hostname);
525 			len = strlen(p);
526 			if (len >= sizeof (domainname)) {
527 				return (0);
528 			}
529 			(void) strcpy(domainname, p);
530 		} else {
531 			domainname[0] = NULL;
532 			if (getdomainname(domainname, MAXHOSTNAMELEN) < 0)
533 				return (0);
534 		}
535 	} else {
536 		len = strlen(domain);
537 		if (len >= sizeof (domainname)) {
538 			return (0);
539 		}
540 		(void) strcpy(domainname, domain);
541 	}
542 
543 	i = strlen(domainname);
544 	if (i == 0)
545 		/* No domainname */
546 		return (0);
547 	if (domainname[i - 1] == '.')
548 		domainname[i - 1] = 0;
549 
550 	if (dot_in_host) {  /* strip off rest of name */
551 		*dot_in_host = '\0';
552 	}
553 
554 	if ((strlen(domainname) + strlen(hostname) + OPSYS_LEN + 3)
555 	    > (size_t)MAXNETNAMELEN) {
556 		return (0);
557 	}
558 
559 	(void) snprintf(netname, MAXNETNAMELEN + 1,
560 				"%s.%s@%s", OPSYS, hostname, domainname);
561 	return (1);
562 }
563