diff options
Diffstat (limited to 'nss/nss_files')
-rw-r--r-- | nss/nss_files/files-initgroups.c | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/nss/nss_files/files-initgroups.c b/nss/nss_files/files-initgroups.c new file mode 100644 index 0000000..2d84cd8 --- /dev/null +++ b/nss/nss_files/files-initgroups.c @@ -0,0 +1,132 @@ +/* Initgroups handling in nss_files module. + Copyright (C) 2011 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#include <alloca.h> +#include <errno.h> +#include <grp.h> +#include <nss.h> +#include <stdio_ext.h> +#include <string.h> +#include <sys/param.h> + +enum nss_status +_nss_files_initgroups_dyn (const char *user, gid_t group, long int *start, + long int *size, gid_t **groupsp, long int limit, + int *errnop) +{ + FILE *stream = fopen ("/etc/group", "re"); + if (stream == NULL) + { + *errnop = errno; + return *errnop == ENOMEM ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; + } + + /* No other thread using this stream. */ + __fsetlocking (stream, FSETLOCKING_BYCALLER); + + char *line = NULL; + size_t linelen = 0; + enum nss_status status = NSS_STATUS_SUCCESS; + + size_t buflen = 1024; + void *buffer = alloca (buflen); + bool buffer_use_malloc = false; + + gid_t *groups = *groupsp; + + /* We have to iterate over the entire file. */ + while (!feof_unlocked (stream)) + { + ssize_t n = getline (&line, &linelen, stream); + if (n < 0) + { + if (! feof_unlocked (stream)) + status = ((*errnop = errno) == ENOMEM + ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL); + break; + } + + struct group grp; + while (_nss_files_parse_grent (line, &grp, buffer, buflen, errnop) == -1) + { + size_t newbuflen = 2 * buflen; + if (buffer_use_malloc || ! __libc_use_alloca (buflen + newbuflen)) + { + char *newbuf = realloc (buffer, buflen); + if (newbuf == NULL) + { + *errnop = ENOMEM; + status = NSS_STATUS_TRYAGAIN; + goto out; + } + buffer = newbuf; + buflen = newbuflen; + buffer_use_malloc = true; + } + else + buffer = extend_alloca (buffer, buflen, newbuflen); + } + + if (grp.gr_gid != group) + for (char **m = grp.gr_mem; *m != NULL; ++m) + if (strcmp (*m, user) == 0) + { + /* Matches user. Insert this group. */ + if (*start == *size) + { + /* Need a bigger buffer. */ + if (limit > 0 && *size == limit) + /* We reached the maximum. */ + goto out; + + long int newsize; + if (limit <= 0) + newsize = 2 * *size; + else + newsize = MIN (limit, 2 * *size); + + gid_t *newgroups = realloc (groups, + newsize * sizeof (*groups)); + if (newgroups == NULL) + { + *errnop = ENOMEM; + status = NSS_STATUS_TRYAGAIN; + goto out; + } + *groupsp = groups = newgroups; + *size = newsize; + } + + groups[*start] = grp.gr_gid; + *start += 1; + + break; + } + } + + out: + /* Free memory. */ + if (buffer_use_malloc) + free (buffer); + free (line); + + fclose (stream); + + return status; +} |