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