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
27#include "lint.h"
28#include <sys/types.h>
29#include <pwd.h>
30#include <nss_dbdefs.h>
31#include <stdio.h>
32#include <synch.h>
33#include <sys/param.h>
34#include <string.h>
35#include <stdlib.h>
36#include <sys/mman.h>
37#include <errno.h>
38
39int str2passwd(const char *, int, void *,
40	char *, int);
41
42static DEFINE_NSS_DB_ROOT(db_root);
43static DEFINE_NSS_GETENT(context);
44
45void
46_nss_initf_passwd(nss_db_params_t *p)
47{
48	p->name	= NSS_DBNAM_PASSWD;
49	p->default_config = NSS_DEFCONF_PASSWD;
50}
51
52#include <getxby_door.h>
53
54struct passwd *
55_uncached_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
56	int buflen);
57
58struct passwd *
59_uncached_getpwnam_r(const char *name, struct passwd *result, char *buffer,
60    int buflen);
61
62/*
63 * POSIX.1c Draft-6 version of the function getpwnam_r.
64 * It was implemented by Solaris 2.3.
65 */
66struct passwd *
67getpwnam_r(const char *name, struct passwd *result, char *buffer, int buflen)
68{
69	nss_XbyY_args_t arg;
70
71	if (name == (const char *)NULL) {
72		errno = ERANGE;
73		return (NULL);
74	}
75	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
76	arg.key.name = name;
77	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYNAME,
78	    &arg);
79	return ((struct passwd *)NSS_XbyY_FINI(&arg));
80}
81
82/*
83 * POSIX.1c Draft-6 version of the function getpwuid_r.
84 * It was implemented by Solaris 2.3.
85 */
86struct passwd *
87getpwuid_r(uid_t uid, struct passwd *result, char *buffer, int buflen)
88{
89	nss_XbyY_args_t arg;
90
91	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
92	arg.key.uid = uid;
93	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYUID,
94	    &arg);
95	return ((struct passwd *)NSS_XbyY_FINI(&arg));
96}
97
98
99struct passwd *
100_uncached_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
101	int buflen)
102{
103	nss_XbyY_args_t arg;
104
105	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
106	arg.key.uid = uid;
107	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYUID,
108	    &arg);
109	return ((struct passwd *)NSS_XbyY_FINI(&arg));
110}
111
112
113/*
114 * POSIX.1c standard version of the function getpwuid_r.
115 * User gets it via static getpwuid_r from the header file.
116 */
117int
118__posix_getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer,
119    size_t bufsize, struct passwd **result)
120{
121	int nerrno = 0;
122	int oerrno = errno;
123
124	errno = 0;
125	if ((*result = getpwuid_r(uid, pwd, buffer, (uintptr_t)bufsize))
126	    == NULL) {
127			nerrno = errno;
128	}
129	errno = oerrno;
130	return (nerrno);
131}
132
133struct passwd *
134_uncached_getpwnam_r(const char *name, struct passwd *result, char *buffer,
135	int buflen)
136{
137	nss_XbyY_args_t arg;
138
139	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
140	arg.key.name = name;
141	(void) nss_search(&db_root, _nss_initf_passwd, NSS_DBOP_PASSWD_BYNAME,
142	    &arg);
143	return ((struct passwd *)NSS_XbyY_FINI(&arg));
144}
145
146/*
147 * POSIX.1c standard version of the function getpwnam_r.
148 * User gets it via static getpwnam_r from the header file.
149 */
150int
151__posix_getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
152    size_t bufsize, struct passwd **result)
153{
154	int nerrno = 0;
155	int oerrno = errno;
156
157	errno = 0;
158	if ((*result = getpwnam_r(name, pwd, buffer, (uintptr_t)bufsize))
159	    == NULL) {
160			nerrno = errno;
161	}
162	errno = oerrno;
163	return (nerrno);
164}
165
166void
167setpwent(void)
168{
169	nss_setent(&db_root, _nss_initf_passwd, &context);
170}
171
172void
173endpwent(void)
174{
175	nss_endent(&db_root, _nss_initf_passwd, &context);
176	nss_delete(&db_root);
177}
178
179struct passwd *
180getpwent_r(struct passwd *result, char *buffer, int buflen)
181{
182	nss_XbyY_args_t arg;
183	char		*nam;
184
185	/* In getXXent_r(), protect the unsuspecting caller from +/- entries */
186
187	do {
188		NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
189		/* No key to fill in */
190		(void) nss_getent(&db_root, _nss_initf_passwd, &context, &arg);
191	} while (arg.returnval != 0 &&
192	    (nam = ((struct passwd *)arg.returnval)->pw_name) != 0 &&
193	    (*nam == '+' || *nam == '-'));
194
195	return ((struct passwd *)NSS_XbyY_FINI(&arg));
196}
197
198struct passwd *
199fgetpwent_r(FILE *f, struct passwd *result, char *buffer, int buflen)
200{
201	extern void	_nss_XbyY_fgets(FILE *, nss_XbyY_args_t *);
202	nss_XbyY_args_t	arg;
203
204	/* ... but in fgetXXent_r, the caller deserves any +/- entry he gets */
205
206	/* No key to fill in */
207	NSS_XbyY_INIT(&arg, result, buffer, buflen, str2passwd);
208	_nss_XbyY_fgets(f, &arg);
209	return ((struct passwd *)NSS_XbyY_FINI(&arg));
210}
211
212static char *
213gettok(char **nextpp)
214{
215	char	*p = *nextpp;
216	char	*q = p;
217	char	c;
218
219	if (p == 0)
220		return (0);
221
222	while ((c = *q) != '\0' && c != ':')
223		q++;
224
225	if (c == '\0')
226		*nextpp = 0;
227	else {
228		*q++ = '\0';
229		*nextpp = q;
230	}
231	return (p);
232}
233
234/*
235 * Return values: 0 = success, 1 = parse error, 2 = erange ...
236 * The structure pointer passed in is a structure in the caller's space
237 * wherein the field pointers would be set to areas in the buffer if
238 * need be. instring and buffer should be separate areas.
239 */
240int
241str2passwd(const char *instr, int lenstr, void *ent, char *buffer, int buflen)
242{
243	struct passwd	*passwd	= (struct passwd *)ent;
244	char		*p, *next;
245	int		black_magic;	/* "+" or "-" entry */
246	ulong_t		tmp;
247
248	if (lenstr + 1 > buflen)
249		return (NSS_STR_PARSE_ERANGE);
250
251	/*
252	 * We copy the input string into the output buffer and
253	 * operate on it in place.
254	 */
255	if (instr != buffer) {
256		/* Overlapping buffer copies are OK */
257		(void) memmove(buffer, instr, lenstr);
258		buffer[lenstr] = '\0';
259	}
260
261	/* quick exit do not entry fill if not needed */
262	if (ent == (void *)NULL)
263		return (NSS_STR_PARSE_SUCCESS);
264
265	next = buffer;
266
267	passwd->pw_name = p = gettok(&next);		/* username */
268	if (*p == '\0') {
269		/* Empty username;  not allowed */
270		return (NSS_STR_PARSE_PARSE);
271	}
272	black_magic = (*p == '+' || *p == '-');
273	if (black_magic) {
274		passwd->pw_uid = UID_NOBODY;
275		passwd->pw_gid = GID_NOBODY;
276		/*
277		 *  pwconv tests pw_passwd and pw_age == NULL
278		 */
279		passwd->pw_passwd  = "";
280		passwd->pw_age	= "";
281		/*
282		 * the rest of the passwd entry is "optional"
283		 */
284		passwd->pw_comment = "";
285		passwd->pw_gecos = "";
286		passwd->pw_dir	= "";
287		passwd->pw_shell = "";
288	}
289
290	passwd->pw_passwd = p = gettok(&next);		/* password */
291	if (p == 0) {
292		if (black_magic)
293			return (NSS_STR_PARSE_SUCCESS);
294		else
295			return (NSS_STR_PARSE_PARSE);
296	}
297	for (; *p != '\0';  p++) {			/* age */
298		if (*p == ',') {
299			*p++ = '\0';
300			break;
301		}
302	}
303	passwd->pw_age = p;
304
305	p = next;					/* uid */
306	if (p == 0 || *p == '\0') {
307		if (black_magic)
308			return (NSS_STR_PARSE_SUCCESS);
309		else
310			return (NSS_STR_PARSE_PARSE);
311	}
312	if (!black_magic) {
313		/*
314		 * strtoul returns unsigned long which is
315		 * 8 bytes on a 64-bit system. We don't want
316		 * to assign it directly to passwd->pw_uid
317		 * which is 4 bytes or else we will end up
318		 * truncating the value.
319		 */
320		errno = 0;
321		tmp = strtoul(p, &next, 10);
322		if (next == p || errno != 0) {
323			/* uid field should be nonempty */
324			/* also check errno from strtoul */
325			return (NSS_STR_PARSE_PARSE);
326		}
327		/*
328		 * The old code (in 2.0 through 2.5) would check
329		 * for the uid being negative, or being greater
330		 * than 60001 (the rfs limit).  If it met either of
331		 * these conditions, the uid was translated to 60001.
332		 *
333		 * Now we just check for -1 (UINT32_MAX); anything else
334		 * is administrative policy
335		 */
336		if (tmp >= UINT32_MAX)
337			passwd->pw_uid = UID_NOBODY;
338		else
339			passwd->pw_uid = (uid_t)tmp;
340	}
341	if (*next++ != ':') {
342		if (black_magic)
343			(void) gettok(&next);
344		else
345			return (NSS_STR_PARSE_PARSE);
346	}
347	p = next;					/* gid */
348	if (p == 0 || *p == '\0') {
349		if (black_magic)
350			return (NSS_STR_PARSE_SUCCESS);
351		else
352			return (NSS_STR_PARSE_PARSE);
353	}
354	if (!black_magic) {
355		errno = 0;
356		tmp = strtoul(p, &next, 10);
357		if (next == p || errno != 0) {
358			/* gid field should be nonempty */
359			/* also check errno from strtoul */
360			return (NSS_STR_PARSE_PARSE);
361		}
362		/*
363		 * gid should not be -1; anything else
364		 * is administrative policy.
365		 */
366		if (tmp >= UINT32_MAX)
367			passwd->pw_gid = GID_NOBODY;
368		else
369			passwd->pw_gid = (gid_t)tmp;
370	}
371	if (*next++ != ':') {
372		if (black_magic)
373			(void) gettok(&next);
374		else
375			return (NSS_STR_PARSE_PARSE);
376	}
377
378	passwd->pw_gecos = passwd->pw_comment = p = gettok(&next);
379	if (p == 0) {
380		if (black_magic)
381			return (NSS_STR_PARSE_SUCCESS);
382		else
383			return (NSS_STR_PARSE_PARSE);
384	}
385
386	passwd->pw_dir = p = gettok(&next);
387	if (p == 0) {
388		if (black_magic)
389			return (NSS_STR_PARSE_SUCCESS);
390		else
391			return (NSS_STR_PARSE_PARSE);
392	}
393
394	passwd->pw_shell = p = gettok(&next);
395	if (p == 0) {
396		if (black_magic)
397			return (NSS_STR_PARSE_SUCCESS);
398		else
399			return (NSS_STR_PARSE_PARSE);
400	}
401
402	/* Better not be any more fields... */
403	if (next == 0) {
404		/* Successfully parsed and stored */
405		return (NSS_STR_PARSE_SUCCESS);
406	}
407	return (NSS_STR_PARSE_PARSE);
408}
409