1 /* Group merging implementation.
2    Copyright (C) 2016-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <grp.h>
23 #include <grp-merge.h>
24 
25 #define BUFCHECK(size)			\
26   ({					\
27     do					\
28       {					\
29 	if (c + (size) > buflen)	\
30           {				\
31 	    free (members);		\
32 	    return ERANGE;		\
33 	  }				\
34       }					\
35     while (0);				\
36   })
37 
38 int
__copy_grp(const struct group srcgrp,const size_t buflen,struct group * destgrp,char * destbuf,char ** endptr)39 __copy_grp (const struct group srcgrp, const size_t buflen,
40 	    struct group *destgrp, char *destbuf, char **endptr)
41 {
42   size_t i;
43   size_t c = 0;
44   size_t len;
45   size_t memcount;
46   char **members = NULL;
47 
48   /* Copy the GID.  */
49   destgrp->gr_gid = srcgrp.gr_gid;
50 
51   /* Copy the name.  */
52   len = strlen (srcgrp.gr_name) + 1;
53   BUFCHECK (len);
54   memcpy (&destbuf[c], srcgrp.gr_name, len);
55   destgrp->gr_name = &destbuf[c];
56   c += len;
57 
58   /* Copy the password.  */
59   len = strlen (srcgrp.gr_passwd) + 1;
60   BUFCHECK (len);
61   memcpy (&destbuf[c], srcgrp.gr_passwd, len);
62   destgrp->gr_passwd = &destbuf[c];
63   c += len;
64 
65   /* Count all of the members.  */
66   for (memcount = 0; srcgrp.gr_mem[memcount]; memcount++)
67     ;
68 
69   /* Allocate a temporary holding area for the pointers to the member
70      contents, including space for a NULL-terminator.  */
71   members = malloc (sizeof (char *) * (memcount + 1));
72   if (members == NULL)
73     return ENOMEM;
74 
75   /* Copy all of the group members to destbuf and add a pointer to each of
76      them into the 'members' array.  */
77   for (i = 0; srcgrp.gr_mem[i]; i++)
78     {
79       len = strlen (srcgrp.gr_mem[i]) + 1;
80       BUFCHECK (len);
81       memcpy (&destbuf[c], srcgrp.gr_mem[i], len);
82       members[i] = &destbuf[c];
83       c += len;
84     }
85   members[i] = NULL;
86 
87   /* Align for pointers.  We can't simply align C because we need to
88      align destbuf[c].  */
89   if ((((uintptr_t)destbuf + c) & (__alignof__(char **) - 1)) != 0)
90     {
91       uintptr_t mis_align = ((uintptr_t)destbuf + c) & (__alignof__(char **) - 1);
92       c += __alignof__(char **) - mis_align;
93     }
94 
95   /* Copy the pointers from the members array into the buffer and assign them
96      to the gr_mem member of destgrp.  */
97   destgrp->gr_mem = (char **) &destbuf[c];
98   len = sizeof (char *) * (memcount + 1);
99   BUFCHECK (len);
100   memcpy (&destbuf[c], members, len);
101   c += len;
102   free (members);
103   members = NULL;
104 
105   /* Save the count of members at the end.  */
106   BUFCHECK (sizeof (size_t));
107   memcpy (&destbuf[c], &memcount, sizeof (size_t));
108   c += sizeof (size_t);
109 
110   if (endptr)
111     *endptr = destbuf + c;
112   return 0;
113 }
libc_hidden_def(__copy_grp)114 libc_hidden_def (__copy_grp)
115 
116 /* Check that the name, GID and passwd fields match, then
117    copy in the gr_mem array.  */
118 int
119 __merge_grp (struct group *savedgrp, char *savedbuf, char *savedend,
120 	     size_t buflen, struct group *mergegrp, char *mergebuf)
121 {
122   size_t c, i, len;
123   size_t savedmemcount;
124   size_t memcount;
125   size_t membersize;
126   char **members = NULL;
127 
128   /* We only support merging members of groups with identical names and
129      GID values. If we hit this case, we need to overwrite the current
130      buffer with the saved one (which is functionally equivalent to
131      treating the new lookup as NSS_STATUS_NOTFOUND).  */
132   if (mergegrp->gr_gid != savedgrp->gr_gid
133       || strcmp (mergegrp->gr_name, savedgrp->gr_name))
134     return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL);
135 
136   /* Get the count of group members from the last sizeof (size_t) bytes in the
137      mergegrp buffer.  */
138   savedmemcount = *(size_t *) (savedend - sizeof (size_t));
139 
140   /* Get the count of new members to add.  */
141   for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++)
142     ;
143 
144   /* Create a temporary array to hold the pointers to the member values from
145      both the saved and merge groups.  */
146   membersize = savedmemcount + memcount + 1;
147   members = malloc (sizeof (char *) * membersize);
148   if (members == NULL)
149     return ENOMEM;
150 
151   /* Copy in the existing member pointers from the saved group
152      Note: this is not NULL-terminated yet.  */
153   memcpy (members, savedgrp->gr_mem, sizeof (char *) * savedmemcount);
154 
155   /* Back up into the savedbuf until we get back to the NULL-terminator of the
156      group member list. (This means walking back savedmemcount + 1 (char *) pointers
157      and the member count value.
158      The value of c is going to be the used length of the buffer backed up by
159      the member count and further backed up by the size of the pointers.  */
160   c = savedend - savedbuf
161       - sizeof (size_t)
162       - sizeof (char *) * (savedmemcount + 1);
163 
164   /* Add all the new group members, overwriting the old NULL-terminator while
165      adding the new pointers to the temporary array.  */
166   for (i = 0; mergegrp->gr_mem[i]; i++)
167     {
168       len = strlen (mergegrp->gr_mem[i]) + 1;
169       BUFCHECK (len);
170       memcpy (&savedbuf[c], mergegrp->gr_mem[i], len);
171       members[savedmemcount + i] = &savedbuf[c];
172       c += len;
173     }
174   /* Add the NULL-terminator.  */
175   members[savedmemcount + memcount] = NULL;
176 
177   /* Align for pointers.  We can't simply align C because we need to
178      align savedbuf[c].  */
179   if ((((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1)) != 0)
180     {
181       uintptr_t mis_align = ((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1);
182       c += __alignof__(char **) - mis_align;
183     }
184 
185   /* Copy the member array back into the buffer after the member list and free
186      the member array.  */
187   savedgrp->gr_mem = (char **) &savedbuf[c];
188   len = sizeof (char *) * membersize;
189   BUFCHECK (len);
190   memcpy (&savedbuf[c], members, len);
191   c += len;
192 
193   free (members);
194   members = NULL;
195 
196   /* Finally, copy the results back into mergebuf, since that's the buffer
197      that we were provided by the caller.  */
198   return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL);
199 }
200 libc_hidden_def (__merge_grp)
201