1/* Group merging implementation.
2 Copyright (C) 2016 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
39internal_function
40__copy_grp (const struct group srcgrp, const size_t buflen,
41 struct group *destgrp, char *destbuf, char **endptr)
42{
43 size_t i;
44 size_t c = 0;
45 size_t len;
46 size_t memcount;
47 char **members = NULL;
48
49 /* Copy the GID. */
50 destgrp->gr_gid = srcgrp.gr_gid;
51
52 /* Copy the name. */
53 len = strlen (srcgrp.gr_name) + 1;
54 BUFCHECK (len);
55 memcpy (&destbuf[c], srcgrp.gr_name, len);
56 destgrp->gr_name = &destbuf[c];
57 c += len;
58
59 /* Copy the password. */
60 len = strlen (srcgrp.gr_passwd) + 1;
61 BUFCHECK (len);
62 memcpy (&destbuf[c], srcgrp.gr_passwd, len);
63 destgrp->gr_passwd = &destbuf[c];
64 c += len;
65
66 /* Count all of the members. */
67 for (memcount = 0; srcgrp.gr_mem[memcount]; memcount++)
68 ;
69
70 /* Allocate a temporary holding area for the pointers to the member
71 contents, including space for a NULL-terminator. */
72 members = malloc (sizeof (char *) * (memcount + 1));
73 if (members == NULL)
74 return ENOMEM;
75
76 /* Copy all of the group members to destbuf and add a pointer to each of
77 them into the 'members' array. */
78 for (i = 0; srcgrp.gr_mem[i]; i++)
79 {
80 len = strlen (srcgrp.gr_mem[i]) + 1;
81 BUFCHECK (len);
82 memcpy (&destbuf[c], srcgrp.gr_mem[i], len);
83 members[i] = &destbuf[c];
84 c += len;
85 }
86 members[i] = NULL;
87
88 /* Align for pointers. We can't simply align C because we need to
89 align destbuf[c]. */
90 if ((((uintptr_t)destbuf + c) & (__alignof__(char **) - 1)) != 0)
91 {
92 uintptr_t mis_align = ((uintptr_t)destbuf + c) & (__alignof__(char **) - 1);
93 c += __alignof__(char **) - mis_align;
94 }
95
96 /* Copy the pointers from the members array into the buffer and assign them
97 to the gr_mem member of destgrp. */
98 destgrp->gr_mem = (char **) &destbuf[c];
99 len = sizeof (char *) * (memcount + 1);
100 BUFCHECK (len);
101 memcpy (&destbuf[c], members, len);
102 c += len;
103 free (members);
104 members = NULL;
105
106 /* Save the count of members at the end. */
107 BUFCHECK (sizeof (size_t));
108 memcpy (&destbuf[c], &memcount, sizeof (size_t));
109 c += sizeof (size_t);
110
111 if (endptr)
112 *endptr = destbuf + c;
113 return 0;
114}
115libc_hidden_def (__copy_grp)
116
117/* Check that the name, GID and passwd fields match, then
118 copy in the gr_mem array. */
119int
120internal_function
121__merge_grp (struct group *savedgrp, char *savedbuf, char *savedend,
122 size_t buflen, struct group *mergegrp, char *mergebuf)
123{
124 size_t c, i, len;
125 size_t savedmemcount;
126 size_t memcount;
127 size_t membersize;
128 char **members = NULL;
129
130 /* We only support merging members of groups with identical names and
131 GID values. If we hit this case, we need to overwrite the current
132 buffer with the saved one (which is functionally equivalent to
133 treating the new lookup as NSS_STATUS_NOTFOUND). */
134 if (mergegrp->gr_gid != savedgrp->gr_gid
135 || strcmp (mergegrp->gr_name, savedgrp->gr_name))
136 return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL);
137
138 /* Get the count of group members from the last sizeof (size_t) bytes in the
139 mergegrp buffer. */
140 savedmemcount = *(size_t *) (savedend - sizeof (size_t));
141
142 /* Get the count of new members to add. */
143 for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++)
144 ;
145
146 /* Create a temporary array to hold the pointers to the member values from
147 both the saved and merge groups. */
148 membersize = savedmemcount + memcount + 1;
149 members = malloc (sizeof (char *) * membersize);
150 if (members == NULL)
151 return ENOMEM;
152
153 /* Copy in the existing member pointers from the saved group
154 Note: this is not NULL-terminated yet. */
155 memcpy (members, savedgrp->gr_mem, sizeof (char *) * savedmemcount);
156
157 /* Back up into the savedbuf until we get back to the NULL-terminator of the
158 group member list. (This means walking back savedmemcount + 1 (char *) pointers
159 and the member count value.
160 The value of c is going to be the used length of the buffer backed up by
161 the member count and further backed up by the size of the pointers. */
162 c = savedend - savedbuf
163 - sizeof (size_t)
164 - sizeof (char *) * (savedmemcount + 1);
165
166 /* Add all the new group members, overwriting the old NULL-terminator while
167 adding the new pointers to the temporary array. */
168 for (i = 0; mergegrp->gr_mem[i]; i++)
169 {
170 len = strlen (mergegrp->gr_mem[i]) + 1;
171 BUFCHECK (len);
172 memcpy (&savedbuf[c], mergegrp->gr_mem[i], len);
173 members[savedmemcount + i] = &savedbuf[c];
174 c += len;
175 }
176 /* Add the NULL-terminator. */
177 members[savedmemcount + memcount] = NULL;
178
179 /* Align for pointers. We can't simply align C because we need to
180 align savedbuf[c]. */
181 if ((((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1)) != 0)
182 {
183 uintptr_t mis_align = ((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1);
184 c += __alignof__(char **) - mis_align;
185 }
186
187 /* Copy the member array back into the buffer after the member list and free
188 the member array. */
189 savedgrp->gr_mem = (char **) &savedbuf[c];
190 len = sizeof (char *) * membersize;
191 BUFCHECK (len);
192 memcpy (&savedbuf[c], members, len);
193 c += len;
194
195 free (members);
196 members = NULL;
197
198 /* Finally, copy the results back into mergebuf, since that's the buffer
199 that we were provided by the caller. */
200 return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL);
201}
202libc_hidden_def (__merge_grp)
203