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
39 int str2passwd(const char *, int, void *,
40 char *, int);
41
42 static DEFINE_NSS_DB_ROOT(db_root);
43 static DEFINE_NSS_GETENT(context);
44
45 void
_nss_initf_passwd(nss_db_params_t * p)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
54 struct passwd *
55 _uncached_getpwuid_r(uid_t uid, struct passwd *result, char *buffer,
56 int buflen);
57
58 struct 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 */
66 struct passwd *
getpwnam_r(const char * name,struct passwd * result,char * buffer,int buflen)67 getpwnam_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 */
86 struct passwd *
getpwuid_r(uid_t uid,struct passwd * result,char * buffer,int buflen)87 getpwuid_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
99 struct passwd *
_uncached_getpwuid_r(uid_t uid,struct passwd * result,char * buffer,int buflen)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 */
117 int
__posix_getpwuid_r(uid_t uid,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)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
133 struct passwd *
_uncached_getpwnam_r(const char * name,struct passwd * result,char * buffer,int buflen)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 */
150 int
__posix_getpwnam_r(const char * name,struct passwd * pwd,char * buffer,size_t bufsize,struct passwd ** result)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
166 void
setpwent(void)167 setpwent(void)
168 {
169 nss_setent(&db_root, _nss_initf_passwd, &context);
170 }
171
172 void
endpwent(void)173 endpwent(void)
174 {
175 nss_endent(&db_root, _nss_initf_passwd, &context);
176 nss_delete(&db_root);
177 }
178
179 struct passwd *
getpwent_r(struct passwd * result,char * buffer,int buflen)180 getpwent_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
198 struct passwd *
fgetpwent_r(FILE * f,struct passwd * result,char * buffer,int buflen)199 fgetpwent_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
212 static char *
gettok(char ** nextpp)213 gettok(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 */
240 int
str2passwd(const char * instr,int lenstr,void * ent,char * buffer,int buflen)241 str2passwd(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