1/* Group merging implementation.
2 Copyright (C) 2016-2018 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 <http://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
38int
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}
114libc_hidden_def (__copy_grp)
115
116/* Check that the name, GID and passwd fields match, then
117 copy in the gr_mem array. */
118int
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}
200libc_hidden_def (__merge_grp)
201