diff options
Diffstat (limited to 'grp/grp-merge.c')
-rw-r--r-- | grp/grp-merge.c | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/grp/grp-merge.c b/grp/grp-merge.c new file mode 100644 index 0000000000..0a1eb38d2c --- /dev/null +++ b/grp/grp-merge.c @@ -0,0 +1,186 @@ +/* Group merging implementation. + Copyright (C) 2016 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, see + <http://www.gnu.org/licenses/>. */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <grp.h> +#include <grp-merge.h> + +#define BUFCHECK(size) \ + ({ \ + do \ + { \ + if (c + (size) > buflen) \ + { \ + free (members); \ + return ERANGE; \ + } \ + } \ + while (0); \ + }) + +int +internal_function +__copy_grp (const struct group srcgrp, const size_t buflen, + struct group *destgrp, char *destbuf, char **endptr) +{ + size_t i; + size_t c = 0; + size_t len; + size_t memcount; + char **members = NULL; + + /* Copy the GID. */ + destgrp->gr_gid = srcgrp.gr_gid; + + /* Copy the name. */ + len = strlen (srcgrp.gr_name) + 1; + BUFCHECK (len); + memcpy (&destbuf[c], srcgrp.gr_name, len); + destgrp->gr_name = &destbuf[c]; + c += len; + + /* Copy the password. */ + len = strlen (srcgrp.gr_passwd) + 1; + BUFCHECK (len); + memcpy (&destbuf[c], srcgrp.gr_passwd, len); + destgrp->gr_passwd = &destbuf[c]; + c += len; + + /* Count all of the members. */ + for (memcount = 0; srcgrp.gr_mem[memcount]; memcount++) + ; + + /* Allocate a temporary holding area for the pointers to the member + contents, including space for a NULL-terminator. */ + members = malloc (sizeof (char *) * (memcount + 1)); + if (members == NULL) + return ENOMEM; + + /* Copy all of the group members to destbuf and add a pointer to each of + them into the 'members' array. */ + for (i = 0; srcgrp.gr_mem[i]; i++) + { + len = strlen (srcgrp.gr_mem[i]) + 1; + BUFCHECK (len); + memcpy (&destbuf[c], srcgrp.gr_mem[i], len); + members[i] = &destbuf[c]; + c += len; + } + members[i] = NULL; + + /* Copy the pointers from the members array into the buffer and assign them + to the gr_mem member of destgrp. */ + destgrp->gr_mem = (char **) &destbuf[c]; + len = sizeof (char *) * (memcount + 1); + BUFCHECK (len); + memcpy (&destbuf[c], members, len); + c += len; + free (members); + members = NULL; + + /* Save the count of members at the end. */ + BUFCHECK (sizeof (size_t)); + memcpy (&destbuf[c], &memcount, sizeof (size_t)); + c += sizeof (size_t); + + if (endptr) + *endptr = destbuf + c; + return 0; +} +libc_hidden_def (__copy_grp) + +/* Check that the name, GID and passwd fields match, then + copy in the gr_mem array. */ +int +internal_function +__merge_grp (struct group *savedgrp, char *savedbuf, char *savedend, + size_t buflen, struct group *mergegrp, char *mergebuf) +{ + size_t c, i, len; + size_t savedmemcount; + size_t memcount; + size_t membersize; + char **members = NULL; + + /* We only support merging members of groups with identical names and + GID values. If we hit this case, we need to overwrite the current + buffer with the saved one (which is functionally equivalent to + treating the new lookup as NSS_STATUS_NOTFOUND). */ + if (mergegrp->gr_gid != savedgrp->gr_gid + || strcmp (mergegrp->gr_name, savedgrp->gr_name)) + return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL); + + /* Get the count of group members from the last sizeof (size_t) bytes in the + mergegrp buffer. */ + savedmemcount = (size_t) *(savedend - sizeof (size_t)); + + /* Get the count of new members to add. */ + for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++) + ; + + /* Create a temporary array to hold the pointers to the member values from + both the saved and merge groups. */ + membersize = savedmemcount + memcount + 1; + members = malloc (sizeof (char *) * membersize); + if (members == NULL) + return ENOMEM; + + /* Copy in the existing member pointers from the saved group + Note: this is not NULL-terminated yet. */ + memcpy (members, savedgrp->gr_mem, sizeof (char *) * savedmemcount); + + /* Back up into the savedbuf until we get back to the NULL-terminator of the + group member list. (This means walking back savedmemcount + 1 (char *) pointers + and the member count value. + The value of c is going to be the used length of the buffer backed up by + the member count and further backed up by the size of the pointers. */ + c = savedend - savedbuf + - sizeof (size_t) + - sizeof (char *) * (savedmemcount + 1); + + /* Add all the new group members, overwriting the old NULL-terminator while + adding the new pointers to the temporary array. */ + for (i = 0; mergegrp->gr_mem[i]; i++) + { + len = strlen (mergegrp->gr_mem[i]) + 1; + BUFCHECK (len); + memcpy (&savedbuf[c], mergegrp->gr_mem[i], len); + members[savedmemcount + i] = &savedbuf[c]; + c += len; + } + /* Add the NULL-terminator. */ + members[savedmemcount + memcount] = NULL; + + /* Copy the member array back into the buffer after the member list and free + the member array. */ + savedgrp->gr_mem = (char **) &savedbuf[c]; + len = sizeof (char *) * membersize; + BUFCHECK (len); + memcpy (&savedbuf[c], members, len); + c += len; + + free (members); + members = NULL; + + /* Finally, copy the results back into mergebuf, since that's the buffer + that we were provided by the caller. */ + return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL); +} +libc_hidden_def (__merge_grp) |