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