1 /*****************************************************************************
2 * Written by Chris Dunlap <cdunlap@llnl.gov>.
3 * Copyright (C) 2007-2020 Lawrence Livermore National Security, LLC.
4 * Copyright (C) 2002-2007 The Regents of the University of California.
5 * UCRL-CODE-155910.
6 *
7 * This file is part of the MUNGE Uid 'N' Gid Emporium (MUNGE).
8 * For details, see <https://dun.github.io/munge/>.
9 *
10 * MUNGE is free software: you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License as published by the Free
12 * Software Foundation, either version 3 of the License, or (at your option)
13 * any later version. Additionally for the MUNGE library (libmunge), you
14 * can redistribute it and/or modify it under the terms of the GNU Lesser
15 * General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * MUNGE is distributed in the hope that it will be useful, but WITHOUT
19 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * and GNU Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * and GNU Lesser General Public License along with MUNGE. If not, see
25 * <http://www.gnu.org/licenses/>.
26 *****************************************************************************/
27
28
29 #if HAVE_CONFIG_H
30 # include "config.h"
31 #endif /* HAVE_CONFIG_H */
32
33 #if HAVE_GETPWNAM_R_POSIX
34 #define _POSIX_PTHREAD_SEMANTICS 1 /* for SunOS */
35 #elif HAVE_GETPWNAM_R_AIX
36 #define _THREAD_SAFE 1
37 #define _UNIX95 1
38 #define _XOPEN_SOURCE_EXTENDED 1
39 #elif HAVE_GETPWNAM_R_SUN
40 #undef _POSIX_PTHREAD_SEMANTICS
41 #elif HAVE_GETPWNAM
42 #ifdef WITH_PTHREADS
43 #include <pthread.h>
44 #endif /* WITH_PTHREADS */
45 #else
46 #error "getpwnam() not supported"
47 #endif
48
49 #include <assert.h>
50 #include <errno.h>
51 #include <pwd.h>
52 #include <stddef.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <sys/types.h>
56 #include <unistd.h>
57 #include <munge.h>
58 #include "log.h"
59 #include "xgetpw.h"
60
61
62 /*****************************************************************************
63 * Compiler Fu
64 *****************************************************************************/
65
66 #ifdef __GNUC__
67 #define _UNUSED_ __attribute__ ((unused))
68 #else
69 #define _UNUSED_
70 #endif
71
72
73 /*****************************************************************************
74 * Constants
75 *****************************************************************************/
76
77 #define MINIMUM_PW_BUF_SIZE 1024
78
79
80 /*****************************************************************************
81 * Data Types
82 *****************************************************************************/
83
84 struct xpwbuf_t {
85 char *buf;
86 size_t len;
87 };
88
89
90 /*****************************************************************************
91 * Private Prototypes
92 *****************************************************************************/
93
94 static size_t _xgetpwbuf_get_sys_size (void);
95
96 static int _xgetpwbuf_grow (xpwbuf_p pwbufp, size_t minlen);
97
98 static int _xgetpwbuf_copy_struct (const struct passwd *src,
99 struct passwd *dst, xpwbuf_p pwbufp) _UNUSED_;
100
101 static int _xgetpwbuf_copy_string (const char *src, char **dstp,
102 char **bufp, size_t *buflenp) _UNUSED_;
103
104
105 /*****************************************************************************
106 * Public Functions
107 *****************************************************************************/
108
109 xpwbuf_p
xgetpwbuf_create(size_t len)110 xgetpwbuf_create (size_t len)
111 {
112 /* Allocates a buffer for xgetpwnam(). [len] specifies a suggested size
113 * for the buffer; if 0, the system recommended size will be used.
114 * Returns the buffer on success, or NULL on error (with errno).
115 */
116 xpwbuf_p pwbufp;
117
118 if (len == 0) {
119 len = _xgetpwbuf_get_sys_size ();
120 }
121 pwbufp = malloc (sizeof (struct xpwbuf_t));
122 if (pwbufp == NULL) {
123 return (NULL);
124 }
125 pwbufp->buf = malloc (len);
126 if (pwbufp->buf == NULL) {
127 free (pwbufp);
128 return (NULL);
129 }
130 pwbufp->len = len;
131 log_msg (LOG_DEBUG, "Created password entry buffer of size %u", len);
132 return (pwbufp);
133 }
134
135
136 void
xgetpwbuf_destroy(xpwbuf_p pwbufp)137 xgetpwbuf_destroy (xpwbuf_p pwbufp)
138 {
139 /* Destroys the buffer [pwbufp].
140 */
141 if (pwbufp != NULL) {
142 if (pwbufp->buf != NULL) {
143 free (pwbufp->buf);
144 }
145 free (pwbufp);
146 }
147 return;
148 }
149
150
151 size_t
xgetpwbuf_get_len(xpwbuf_p pwbufp)152 xgetpwbuf_get_len (xpwbuf_p pwbufp)
153 {
154 /* Returns the current size of the allocated buffer within [pwbufp],
155 * or 0 on error (with errno).
156 */
157 if (pwbufp == NULL) {
158 errno = EINVAL;
159 return (0);
160 }
161 return (pwbufp->len);
162 }
163
164
165 int
xgetpwnam(const char * name,struct passwd * pwp,xpwbuf_p pwbufp)166 xgetpwnam (const char *name, struct passwd *pwp, xpwbuf_p pwbufp)
167 {
168 /* Portable encapsulation of getpwnam_r().
169 * Queries the password database for [name], storing the struct passwd result
170 * in [pwp] and additional strings in the buffer [pwbufp].
171 * Returns 0 on success, or -1 on error (with errno).
172 * Returns -1 with ENOENT when [name] is not found.
173 */
174 #if HAVE_GETPWNAM_R_POSIX
175 struct passwd *rv_pwp;
176 #elif HAVE_GETPWNAM_R_AIX
177 #elif HAVE_GETPWNAM_R_SUN
178 struct passwd *rv_pwp;
179 #elif HAVE_GETPWNAM
180 #ifdef WITH_PTHREADS
181 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
182 int rv_mutex;
183 #endif /* WITH_PTHREADS */
184 int rv_copy;
185 struct passwd *rv_pwp;
186 #endif /* HAVE_GETPWNAM */
187 int rv;
188 int got_err;
189 int got_none;
190
191 if ((name == NULL) ||
192 (name[0] == '\0') ||
193 (pwp == NULL) ||
194 (pwbufp == NULL))
195 {
196 errno = EINVAL;
197 return (-1);
198 }
199 assert (pwbufp->buf != NULL);
200 assert (pwbufp->len > 0);
201
202 restart:
203 errno = 0;
204 got_err = 0;
205 got_none = 0;
206
207 #if HAVE_GETPWNAM_R_POSIX
208 rv_pwp = NULL;
209 rv = getpwnam_r (name, pwp, pwbufp->buf, pwbufp->len, &rv_pwp);
210 /*
211 * POSIX.1-2001 does not call "name not found" an error, so the return
212 * value of getpwnam_r() is of limited value. When errors do occur,
213 * some systems return them via the retval, some via errno, and some
214 * return no indication whatsoever.
215 */
216 if (rv_pwp == NULL) {
217 /*
218 * Coalesce the error number onto rv if needed.
219 */
220 if ((rv < 0) && (errno != 0)) {
221 rv = errno;
222 }
223 /* Likely that the name was not found.
224 */
225 if ((rv == 0) ||
226 (rv == ENOENT) ||
227 (rv == ESRCH))
228 {
229 got_none = 1;
230 }
231 /* Likely that an error occurred.
232 */
233 else if (
234 (rv == EINTR) ||
235 (rv == ERANGE) ||
236 (rv == EIO) ||
237 (rv == EMFILE) ||
238 (rv == ENFILE))
239 {
240 got_err = 1;
241 errno = rv;
242 }
243 /* Unable to distinguish "name not found" from error.
244 */
245 else {
246 got_none = 1;
247 }
248 }
249 #elif HAVE_GETPWNAM_R_AIX
250 rv = getpwnam_r (name, pwp, pwbufp->buf, pwbufp->len);
251 if (rv != 0) {
252 if (errno == ESRCH) {
253 got_none = 1;
254 }
255 else {
256 got_err = 1;
257 }
258 }
259 #elif HAVE_GETPWNAM_R_SUN
260 rv_pwp = getpwnam_r (name, pwp, pwbufp->buf, pwbufp->len);
261 if (rv_pwp == NULL) {
262 if (errno == 0) {
263 got_none = 1;
264 }
265 else {
266 got_err = 1;
267 }
268 }
269 #elif HAVE_GETPWNAM
270 #ifdef WITH_PTHREADS
271 if ((rv_mutex = pthread_mutex_lock (&mutex)) != 0) {
272 errno = rv_mutex;
273 log_errno (EMUNGE_SNAFU, LOG_ERR, "Failed to lock xgetpwnam mutex");
274 }
275 #endif /* WITH_PTHREADS */
276 rv_pwp = getpwnam (name);
277 /*
278 * The initial test for (errno != 0), while redundant, allows for the
279 * "name not found" case to short-circuit the rest of the if-condition
280 * on many systems.
281 */
282 if (rv_pwp == NULL) {
283 if ((errno != 0) &&
284 ((errno == EINTR) ||
285 (errno == ERANGE) ||
286 (errno == EIO) ||
287 (errno == EMFILE) ||
288 (errno == ENFILE) ||
289 (errno == ENOMEM)))
290 {
291 got_err = 1;
292 }
293 else {
294 got_none = 1;
295 }
296 rv_copy = 0;
297 }
298 else {
299 rv_copy = _xgetpwbuf_copy_struct (rv_pwp, pwp, pwbufp);
300 }
301 #ifdef WITH_PTHREADS
302 if ((rv_mutex = pthread_mutex_unlock (&mutex)) != 0) {
303 errno = rv_mutex;
304 log_errno (EMUNGE_SNAFU, LOG_ERR, "Failed to unlock xgetpwnam mutex");
305 }
306 #endif /* WITH_PTHREADS */
307 if (rv_copy < 0) {
308 return (-1);
309 }
310 #endif /* HAVE_GETPWNAM */
311
312 if (got_none) {
313 errno = ENOENT;
314 return (-1);
315 }
316 if (got_err) {
317 if (errno == EINTR) {
318 goto restart;
319 }
320 if (errno == ERANGE) {
321 rv = _xgetpwbuf_grow (pwbufp, 0);
322 if (rv == 0) {
323 goto restart;
324 }
325 }
326 return (-1);
327 }
328 /* Some systems set errno even on success. Go figure.
329 */
330 errno = 0;
331 return (0);
332 }
333
334
335 /*****************************************************************************
336 * Private Functions
337 *****************************************************************************/
338
339 static size_t
_xgetpwbuf_get_sys_size(void)340 _xgetpwbuf_get_sys_size (void)
341 {
342 /* Returns the system recommended size for the xgetpw buffer.
343 */
344 long n = -1;
345 size_t len;
346
347 #if HAVE_SYSCONF
348 #ifdef _SC_GETPW_R_SIZE_MAX
349 n = sysconf (_SC_GETPW_R_SIZE_MAX);
350 #endif /* _SC_GETPW_R_SIZE_MAX */
351 #endif /* HAVE_SYSCONF */
352
353 len = (n <= MINIMUM_PW_BUF_SIZE) ? MINIMUM_PW_BUF_SIZE : (size_t) n;
354 return (len);
355 }
356
357
358 static int
_xgetpwbuf_grow(xpwbuf_p pwbufp,size_t minlen)359 _xgetpwbuf_grow (xpwbuf_p pwbufp, size_t minlen)
360 {
361 /* Grows the buffer [pwbufp] to be at least as large as the length [minlen].
362 * Returns 0 on success, or -1 on error (with errno).
363 */
364 size_t newlen;
365 char *newbuf;
366
367 assert (pwbufp != NULL);
368 assert (pwbufp->buf != NULL);
369 assert (pwbufp->len > 0);
370
371 newlen = pwbufp->len;
372 do {
373 newlen *= 2;
374 if (newlen < pwbufp->len) { /* newlen overflowed */
375 errno = ENOMEM;
376 return (-1);
377 }
378 } while (newlen < minlen);
379
380 newbuf = realloc (pwbufp->buf, newlen);
381 if (newbuf == NULL) {
382 errno = ENOMEM;
383 return (-1);
384 }
385 pwbufp->buf = newbuf;
386 pwbufp->len = newlen;
387
388 log_msg (LOG_INFO, "Increased password entry buffer size to %u", newlen);
389 return (0);
390 }
391
392
393 static int
_xgetpwbuf_copy_struct(const struct passwd * src,struct passwd * dst,xpwbuf_p pwbufp)394 _xgetpwbuf_copy_struct (const struct passwd *src, struct passwd *dst,
395 xpwbuf_p pwbufp)
396 {
397 /* Copies the struct passwd [src] into [dst], placing additional strings
398 * and whatnot into buffer [pwbuf].
399 * Returns 0 on success, or -1 on error (with errno).
400 */
401 size_t num_bytes;
402 char *p;
403
404 assert (src != NULL);
405 assert (dst != NULL);
406 assert (pwbufp != NULL);
407 assert (pwbufp->buf != NULL);
408 assert (pwbufp->len > 0);
409
410 /* Compute requisite buffer space.
411 */
412 num_bytes = 0;
413 if (src->pw_name) {
414 num_bytes += strlen (src->pw_name) + 1;
415 }
416 if (src->pw_passwd) {
417 num_bytes += strlen (src->pw_passwd) + 1;
418 }
419 if (src->pw_gecos) {
420 num_bytes += strlen (src->pw_gecos) + 1;
421 }
422 if (src->pw_dir) {
423 num_bytes += strlen (src->pw_dir) + 1;
424 }
425 if (src->pw_shell) {
426 num_bytes += strlen (src->pw_shell) + 1;
427 }
428 /* Ensure requisite buffer space.
429 */
430 if (pwbufp->len < num_bytes) {
431 if (_xgetpwbuf_grow (pwbufp, num_bytes) < 0) {
432 return (-1);
433 }
434 }
435 /* Copy password entry.
436 */
437 assert (pwbufp->len >= num_bytes);
438 memset (dst, 0, sizeof (*dst));
439 p = pwbufp->buf;
440
441 if (_xgetpwbuf_copy_string
442 (src->pw_name, &(dst->pw_name), &p, &num_bytes) < 0) {
443 goto err;
444 }
445 if (_xgetpwbuf_copy_string
446 (src->pw_passwd, &(dst->pw_passwd), &p, &num_bytes) < 0) {
447 goto err;
448 }
449 if (_xgetpwbuf_copy_string
450 (src->pw_gecos, &(dst->pw_gecos), &p, &num_bytes) < 0) {
451 goto err;
452 }
453 if (_xgetpwbuf_copy_string
454 (src->pw_dir, &(dst->pw_dir), &p, &num_bytes) < 0) {
455 goto err;
456 }
457 if (_xgetpwbuf_copy_string
458 (src->pw_shell, &(dst->pw_shell), &p, &num_bytes) < 0) {
459 goto err;
460 }
461 dst->pw_uid = src->pw_uid;
462 dst->pw_gid = src->pw_gid;
463
464 assert (p <= pwbufp->buf + pwbufp->len);
465 return (0);
466
467 err:
468 errno = ERANGE;
469 return (-1);
470 }
471
472
473 static int
_xgetpwbuf_copy_string(const char * src,char ** dstp,char ** bufp,size_t * buflenp)474 _xgetpwbuf_copy_string (const char *src, char **dstp,
475 char **bufp, size_t *buflenp)
476 {
477 /* Copies the string [src] into the buffer [bufp] of size [buflenp],
478 * setting the pointer [dstp] to the newly-copied string. The values
479 * for [bufp] and [buflenp] are adjusted for the remaining buffer space.
480 * Note that [dstp], [bufp], and [buflenp] are all passed by reference.
481 * Returns the number of bytes copied, or -1 on error.
482 */
483 size_t n;
484
485 assert (dstp != NULL);
486 assert (bufp != NULL);
487 assert (*bufp != NULL);
488 assert (buflenp != NULL);
489
490 if (src == NULL) {
491 *dstp = NULL;
492 return (0);
493 }
494 n = strlen (src) + 1;
495 if (*buflenp < n) {
496 return (-1);
497 }
498 *dstp = strcpy (*bufp, src);
499 *bufp += n;
500 *buflenp -= n;
501 return (n);
502 }
503