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
user2netname_nis(int * err,char netname[MAXNETNAMELEN+1],uid_t uid,char * domain)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
getnetname(char name[MAXNETNAMELEN+1])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
__getnetnamebyuid(char name[MAXNETNAMELEN+1],uid_t uid)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
user2netname(char netname[MAXNETNAMELEN+1],const uid_t uid,const char * domain)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", sizeof (netname));
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", sizeof (netname));
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
host2netname(char netname[MAXNETNAMELEN+1],const char * host,const char * domain)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