netname.c revision 11262:b7ebfbf2359e
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 2009 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
41/*
42 * netname utility routines (getnetname, user2netname, host2netname).
43 *
44 * Convert from unix names (uid, gid) to network wide names.
45 * This module is operating system dependent!
46 * What we define here will work with any unix system that has adopted
47 * the Sun NIS domain architecture.
48 */
49
50#undef NIS
51
52#include "mt.h"
53#include "rpc_mt.h"
54#include <stdio.h>
55#include <stdlib.h>
56#include <unistd.h>
57#include <sys/types.h>
58#include <ctype.h>
59#include <string.h>
60#include <syslog.h>
61#include <sys/param.h>
62#include <rpc/rpc.h>
63#include <rpcsvc/nis.h>
64#include <rpcsvc/nis_dhext.h>
65#include <nsswitch.h>
66#include <syslog.h>
67#include <errno.h>
68
69#ifndef MAXHOSTNAMELEN
70#define	MAXHOSTNAMELEN 256
71#endif
72#ifndef NGROUPS
73#define	NGROUPS 16
74#endif
75
76/*
77 * the value for NOBODY_UID is set by the SVID. The following define also
78 * appears in netnamer.c
79 */
80
81#define	NOBODY_UID 60001
82
83extern int getdomainname();
84extern int key_call();
85#define	OPSYS_LEN 4
86static const char *OPSYS = "unix";
87
88/*
89 * default publickey policy:
90 *	publickey: nis [NOTFOUND = return] files
91 */
92
93
94/*	NSW_NOTSUCCESS  NSW_NOTFOUND   NSW_UNAVAIL    NSW_TRYAGAIN */
95#define	DEF_ACTION {__NSW_RETURN, __NSW_RETURN, __NSW_CONTINUE, __NSW_CONTINUE}
96
97static struct __nsw_lookup lookup_files = {"files", DEF_ACTION, NULL, NULL},
98		lookup_nis = {"nis", DEF_ACTION, NULL, &lookup_files};
99static struct __nsw_switchconfig publickey_default =
100			{0, "publickey", 2, &lookup_nis};
101
102static mutex_t serialize_netname = ERRORCHECKMUTEX;
103
104
105#define	MAXIPRINT	(11)	/* max length of printed integer */
106
107/*
108 * Convert unix cred to network-name by concatenating the
109 * 3 pieces of information <opsys type> <uid> <domain>.
110 */
111
112static int
113user2netname_nis(int *err, char netname[MAXNETNAMELEN + 1], uid_t uid,
114								char *domain)
115{
116	int i;
117	char *dfltdom;
118	if (domain == NULL) {
119		if (__rpc_get_default_domain(&dfltdom) != 0) {
120			*err = __NSW_UNAVAIL;
121			return (0);
122		}
123		domain = dfltdom;
124	}
125	if ((strlen(domain) + OPSYS_LEN + 3 + MAXIPRINT) >
126	    (size_t)MAXNETNAMELEN) {
127		*err = __NSW_UNAVAIL;
128		return (0);
129	}
130	(void) snprintf(netname, MAXNETNAMELEN + 1,
131	    "%s.%d@%s", OPSYS, (int)uid, domain);
132	i = strlen(netname);
133	if (netname[i-1] == '.')
134		netname[i-1] = '\0';
135	*err = __NSW_SUCCESS;
136	return (1);
137}
138
139/*
140 * Figure out my fully qualified network name
141 */
142int
143getnetname(char name[MAXNETNAMELEN + 1])
144{
145	uid_t uid;
146
147	uid = geteuid();
148	if (uid == 0)
149		return (host2netname(name, NULL, NULL));
150	return (user2netname(name, uid, NULL));
151}
152
153
154/*
155 * Figure out the fully qualified network name for the given uid.
156 * This is a private interface.
157 */
158int
159__getnetnamebyuid(char name[MAXNETNAMELEN + 1], uid_t uid)
160{
161	if (uid == 0)
162		return (host2netname(name, NULL, NULL));
163	return (user2netname(name, uid, NULL));
164}
165
166/*
167 * Convert unix cred to network-name
168 *
169 * It uses the publickey policy in the /etc/nsswitch.conf file
170 * (Unless the netname is "nobody", which is special cased).
171 * If there is no publickey policy in /etc/nsswitch.conf,
172 * the default publickey policy is used, which is
173 *	publickey: nis [NOTFOUND=return] files
174 * Note that for the non-nisplus case, there is no failover
175 * so only the first entry would be relevant for those cases.
176 */
177int
178user2netname(char netname[MAXNETNAMELEN + 1], const uid_t uid,
179							const char *domain)
180{
181	struct __nsw_switchconfig *conf;
182	struct __nsw_lookup *look;
183	int needfree = 1, res = 0;
184	enum __nsw_parse_err perr;
185	int err;
186
187	/*
188	 * Take care of the special case of "nobody". If the uid is
189	 * the value assigned by the SVID for nobody, return the string
190	 * "nobody".
191	 */
192
193	if (uid == NOBODY_UID) {
194		(void) strlcpy(netname, "nobody", sizeof (netname));
195		return (1);
196	}
197
198	netname[0] = '\0';  /* make null first (no need for memset) */
199
200	if (mutex_lock(&serialize_netname) == EDEADLK) {
201		/*
202		 * This thread already holds this lock. This scenario
203		 * occurs when a process requires a netname which
204		 * itself requires a netname to look up. As we clearly
205		 * can't continue like this we return 'nobody'.
206		 */
207		(void) strlcpy(netname, "nobody", sizeof (netname));
208		return (1);
209	}
210
211	conf = __nsw_getconfig("publickey", &perr);
212	if (!conf) {
213		conf = &publickey_default;
214		needfree = 0;
215	}
216
217	for (look = conf->lookups; look; look = look->next) {
218		/* ldap, nis, and files all do the same thing. */
219		if (strcmp(look->service_name, "ldap") == 0 ||
220		    strcmp(look->service_name, "nis") == 0 ||
221		    strcmp(look->service_name, "files") == 0)
222			res = user2netname_nis(&err,
223			    netname, uid, (char *)domain);
224		else {
225			syslog(LOG_INFO,
226			    "user2netname: unknown nameservice \
227					for publickey info '%s'\n",
228			    look->service_name);
229			err = __NSW_UNAVAIL;
230		}
231		switch (look->actions[err]) {
232			case __NSW_CONTINUE :
233				break;
234			case __NSW_RETURN :
235				if (needfree)
236					__nsw_freeconfig(conf);
237				(void) mutex_unlock(&serialize_netname);
238				return (res);
239			default :
240				syslog(LOG_ERR,
241			"user2netname: Unknown action for nameservice '%s'",
242				    look->service_name);
243			}
244	}
245	if (needfree)
246		__nsw_freeconfig(conf);
247	(void) mutex_unlock(&serialize_netname);
248	return (0);
249}
250
251
252/*
253 * Convert host to network-name
254 * This routine returns following netnames given the host and domain
255 * arguments defined below: (domainname=y.z)
256 *	  Arguments
257 *	host	domain		netname
258 *	----	------		-------
259 *	-	-		unix.m@y.z (hostname=m)
260 *	-	a.b		unix.m@a.b (hostname=m)
261 *	-	-		unix.m@y.z (hostname=m.w.x)
262 *	-	a.b		unix.m@a.b (hostname=m.w.x)
263 *	h	-		unix.h@y.z
264 *	h	a.b		unix.h@a.b
265 *	h.w.x	-		unix.h@w.x
266 *	h.w.x	a.b		unix.h@a.b
267 */
268int
269host2netname(char netname[MAXNETNAMELEN + 1], const char *host,
270							const char *domain)
271{
272	char *p;
273	char hostname[MAXHOSTNAMELEN + 1];
274	char domainname[MAXHOSTNAMELEN + 1];
275	char *dot_in_host;
276	int i;
277	size_t len;
278
279	netname[0] = '\0';  /* make null first (no need for memset) */
280
281	if (host == NULL) {
282		(void) strncpy(hostname, nis_local_host(), sizeof (hostname));
283		p = (char *)strchr(hostname, '.');
284		if (p) {
285			*p++ = '\0';
286			/* if no domain passed, use tail of nis_local_host() */
287			if (domain == NULL) {
288				domain = p;
289			}
290		}
291	} else {
292		len = strlen(host);
293		if (len >= sizeof (hostname)) {
294			return (0);
295		}
296		(void) strcpy(hostname, host);
297	}
298
299	dot_in_host = (char *)strchr(hostname, '.');
300	if (domain == NULL) {
301		p = dot_in_host;
302		if (p) {
303			p = (char *)nis_domain_of(hostname);
304			len = strlen(p);
305			if (len >= sizeof (domainname)) {
306				return (0);
307			}
308			(void) strcpy(domainname, p);
309		} else {
310			domainname[0] = NULL;
311			if (getdomainname(domainname, MAXHOSTNAMELEN) < 0)
312				return (0);
313		}
314	} else {
315		len = strlen(domain);
316		if (len >= sizeof (domainname)) {
317			return (0);
318		}
319		(void) strcpy(domainname, domain);
320	}
321
322	i = strlen(domainname);
323	if (i == 0)
324		/* No domainname */
325		return (0);
326	if (domainname[i - 1] == '.')
327		domainname[i - 1] = 0;
328
329	if (dot_in_host) {  /* strip off rest of name */
330		*dot_in_host = '\0';
331	}
332
333	if ((strlen(domainname) + strlen(hostname) + OPSYS_LEN + 3)
334	    > (size_t)MAXNETNAMELEN) {
335		return (0);
336	}
337
338	(void) snprintf(netname, MAXNETNAMELEN + 1,
339	    "%s.%s@%s", OPSYS, hostname, domainname);
340	return (1);
341}
342