Imported Upstream version 1.8.7
[debian/sudo] / plugins / sudoers / pwutil_impl.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2013
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  * Sponsored in part by the Defense Advanced Research Projects
18  * Agency (DARPA) and Air Force Research Laboratory, Air Force
19  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20  */
21
22 #include <config.h>
23
24 #include <sys/types.h>
25 #include <stdio.h>
26 #ifdef STDC_HEADERS
27 # include <stdlib.h>
28 # include <stddef.h>
29 #else
30 # ifdef HAVE_STDLIB_H
31 #  include <stdlib.h>
32 # endif
33 #endif /* STDC_HEADERS */
34 #ifdef HAVE_STRING_H
35 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
36 #  include <memory.h>
37 # endif
38 # include <string.h>
39 #endif /* HAVE_STRING_H */
40 #ifdef HAVE_STRINGS_H
41 # include <strings.h>
42 #endif /* HAVE_STRINGS_H */
43 #ifdef HAVE_UNISTD_H
44 # include <unistd.h>
45 #endif /* HAVE_UNISTD_H */
46 #include <limits.h>
47 #include <pwd.h>
48 #include <grp.h>
49
50 #include "sudoers.h"
51 #include "pwutil.h"
52
53 #ifndef LOGIN_NAME_MAX
54 # ifdef _POSIX_LOGIN_NAME_MAX
55 #  define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
56 # else
57 #  define LOGIN_NAME_MAX 9
58 # endif
59 #endif /* LOGIN_NAME_MAX */
60
61 #define FIELD_SIZE(src, name, size)                     \
62 do {                                                    \
63         if (src->name) {                                \
64                 size = strlen(src->name) + 1;           \
65                 total += size;                          \
66         }                                               \
67 } while (0)
68
69 #define FIELD_COPY(src, dst, name, size)                \
70 do {                                                    \
71         if (src->name) {                                \
72                 memcpy(cp, src->name, size);            \
73                 dst->name = cp;                         \
74                 cp += size;                             \
75         }                                               \
76 } while (0)
77
78 /*
79  * Dynamically allocate space for a struct item plus the key and data
80  * elements.  If name is non-NULL it is used as the key, else the
81  * uid is the key.  Fills in datum from struct password.
82  */
83 struct cache_item *
84 sudo_make_pwitem(uid_t uid, const char *name)
85 {
86     char *cp;
87     const char *pw_shell;
88     size_t nsize, psize, csize, gsize, dsize, ssize, total;
89     struct cache_item_pw *pwitem;
90     struct passwd *pw, *newpw;
91     debug_decl(sudo_make_pwitem, SUDO_DEBUG_NSS)
92
93     /* Look up by name or uid. */
94     pw = name ? getpwnam(name) : getpwuid(uid);
95     if (pw == NULL)
96         debug_return_ptr(NULL);
97
98     /* If shell field is empty, expand to _PATH_BSHELL. */
99     pw_shell = (pw->pw_shell == NULL || pw->pw_shell[0] == '\0')
100         ? _PATH_BSHELL : pw->pw_shell;
101
102     /* Allocate in one big chunk for easy freeing. */
103     nsize = psize = csize = gsize = dsize = ssize = 0;
104     total = sizeof(*pwitem);
105     FIELD_SIZE(pw, pw_name, nsize);
106     FIELD_SIZE(pw, pw_passwd, psize);
107 #ifdef HAVE_LOGIN_CAP_H
108     FIELD_SIZE(pw, pw_class, csize);
109 #endif
110     FIELD_SIZE(pw, pw_gecos, gsize);
111     FIELD_SIZE(pw, pw_dir, dsize);
112     /* Treat shell specially since we expand "" -> _PATH_BSHELL */
113     ssize = strlen(pw_shell) + 1;
114     total += ssize;
115     if (name != NULL)
116         total += strlen(name) + 1;
117
118     /* Allocate space for struct item, struct passwd and the strings. */
119     pwitem = ecalloc(1, total);
120     newpw = &pwitem->pw;
121
122     /*
123      * Copy in passwd contents and make strings relative to space
124      * at the end of the struct.
125      */
126     memcpy(newpw, pw, sizeof(*pw));
127     cp = (char *)(pwitem + 1);
128     FIELD_COPY(pw, newpw, pw_name, nsize);
129     FIELD_COPY(pw, newpw, pw_passwd, psize);
130 #ifdef HAVE_LOGIN_CAP_H
131     FIELD_COPY(pw, newpw, pw_class, csize);
132 #endif
133     FIELD_COPY(pw, newpw, pw_gecos, gsize);
134     FIELD_COPY(pw, newpw, pw_dir, dsize);
135     /* Treat shell specially since we expand "" -> _PATH_BSHELL */
136     memcpy(cp, pw_shell, ssize);
137     newpw->pw_shell = cp;
138     cp += ssize;
139
140     /* Set key and datum. */
141     if (name != NULL) {
142         memcpy(cp, name, strlen(name) + 1);
143         pwitem->cache.k.name = cp;
144     } else {
145         pwitem->cache.k.uid = pw->pw_uid;
146     }
147     pwitem->cache.d.pw = newpw;
148     pwitem->cache.refcnt = 1;
149
150     debug_return_ptr(&pwitem->cache);
151 }
152
153 /*
154  * Dynamically allocate space for a struct item plus the key and data
155  * elements.  If name is non-NULL it is used as the key, else the
156  * gid is the key.  Fills in datum from struct group.
157  */
158 struct cache_item *
159 sudo_make_gritem(gid_t gid, const char *name)
160 {
161     char *cp;
162     size_t nsize, psize, nmem, total, len;
163     struct cache_item_gr *gritem;
164     struct group *gr, *newgr;
165     debug_decl(sudo_make_gritem, SUDO_DEBUG_NSS)
166
167     /* Look up by name or gid. */
168     gr = name ? getgrnam(name) : getgrgid(gid);
169     if (gr == NULL)
170         debug_return_ptr(NULL);
171
172     /* Allocate in one big chunk for easy freeing. */
173     nsize = psize = nmem = 0;
174     total = sizeof(*gritem);
175     FIELD_SIZE(gr, gr_name, nsize);
176     FIELD_SIZE(gr, gr_passwd, psize);
177     if (gr->gr_mem) {
178         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++)
179             total += strlen(gr->gr_mem[nmem]) + 1;
180         nmem++;
181         total += sizeof(char *) * nmem;
182     }
183     if (name != NULL)
184         total += strlen(name) + 1;
185
186     gritem = ecalloc(1, total);
187
188     /*
189      * Copy in group contents and make strings relative to space
190      * at the end of the buffer.  Note that gr_mem must come
191      * immediately after struct group to guarantee proper alignment.
192      */
193     newgr = &gritem->gr;
194     memcpy(newgr, gr, sizeof(*gr));
195     cp = (char *)(gritem + 1);
196     if (gr->gr_mem) {
197         newgr->gr_mem = (char **)cp;
198         cp += sizeof(char *) * nmem;
199         for (nmem = 0; gr->gr_mem[nmem] != NULL; nmem++) {
200             len = strlen(gr->gr_mem[nmem]) + 1;
201             memcpy(cp, gr->gr_mem[nmem], len);
202             newgr->gr_mem[nmem] = cp;
203             cp += len;
204         }
205         newgr->gr_mem[nmem] = NULL;
206     }
207     FIELD_COPY(gr, newgr, gr_passwd, psize);
208     FIELD_COPY(gr, newgr, gr_name, nsize);
209
210     /* Set key and datum. */
211     if (name != NULL) {
212         memcpy(cp, name, strlen(name) + 1);
213         gritem->cache.k.name = cp;
214     } else {
215         gritem->cache.k.gid = gr->gr_gid;
216     }
217     gritem->cache.d.gr = newgr;
218     gritem->cache.refcnt = 1;
219
220     debug_return_ptr(&gritem->cache);
221 }
222
223 /*
224  * Dynamically allocate space for a struct item plus the key and data
225  * elements.  Fills in datum from user_gids or from getgrouplist(3).
226  */
227 struct cache_item *
228 sudo_make_grlist_item(struct passwd *pw, char * const *unused1,
229     char * const *unused2)
230 {
231     char *cp;
232     size_t i, nsize, ngroups, total, len;
233     struct cache_item_grlist *grlitem;
234     struct group_list *grlist;
235     GETGROUPS_T *gids;
236     struct group *grp;
237     int ngids, groupname_len;
238     debug_decl(sudo_make_grlist_item, SUDO_DEBUG_NSS)
239
240     if (pw == sudo_user.pw && sudo_user.gids != NULL) {
241         gids = user_gids;
242         ngids = user_ngids;
243         user_gids = NULL;
244         user_ngids = 0;
245     } else {
246         if (sudo_user.max_groups != -1) {
247             ngids = sudo_user.max_groups;
248             gids = emalloc2(ngids, sizeof(GETGROUPS_T));
249             (void)getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids);
250         } else {
251 #if defined(HAVE_SYSCONF) && defined(_SC_NGROUPS_MAX)
252             ngids = (int)sysconf(_SC_NGROUPS_MAX) * 2;
253             if (ngids < 0)
254 #endif
255                 ngids = NGROUPS_MAX * 2;
256             gids = emalloc2(ngids, sizeof(GETGROUPS_T));
257             if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1) {
258                 efree(gids);
259                 gids = emalloc2(ngids, sizeof(GETGROUPS_T));
260                 if (getgrouplist(pw->pw_name, pw->pw_gid, gids, &ngids) == -1)
261                     ngids = -1;
262             }
263         }
264     }
265     if (ngids <= 0) {
266         efree(gids);
267         debug_return_ptr(NULL);
268     }
269
270 #ifdef HAVE_SETAUTHDB
271     aix_setauthdb((char *) pw->pw_name);
272 #endif
273
274 #if defined(HAVE_SYSCONF) && defined(_SC_LOGIN_NAME_MAX)
275     groupname_len = MAX((int)sysconf(_SC_LOGIN_NAME_MAX), 32);
276 #else
277     groupname_len = MAX(LOGIN_NAME_MAX, 32);
278 #endif
279
280     /* Allocate in one big chunk for easy freeing. */
281     nsize = strlen(pw->pw_name) + 1;
282     total = sizeof(*grlitem) + nsize;
283     total += sizeof(char *) * ngids;
284     total += sizeof(gid_t *) * ngids;
285     total += groupname_len * ngids;
286
287 again:
288     grlitem = ecalloc(1, total);
289
290     /*
291      * Copy in group list and make pointers relative to space
292      * at the end of the buffer.  Note that the groups array must come
293      * immediately after struct group to guarantee proper alignment.
294      */
295     grlist = &grlitem->grlist;
296     cp = (char *)(grlitem + 1);
297     grlist->groups = (char **)cp;
298     cp += sizeof(char *) * ngids;
299     grlist->gids = (gid_t *)cp;
300     cp += sizeof(gid_t) * ngids;
301
302     /* Set key and datum. */
303     memcpy(cp, pw->pw_name, nsize);
304     grlitem->cache.k.name = cp;
305     grlitem->cache.d.grlist = grlist;
306     grlitem->cache.refcnt = 1;
307     cp += nsize;
308
309     /*
310      * Store group IDs.
311      */
312     for (i = 0; i < ngids; i++)
313         grlist->gids[i] = gids[i];
314     grlist->ngids = ngids;
315
316     /*
317      * Resolve and store group names by ID.
318      */
319     ngroups = 0;
320     for (i = 0; i < ngids; i++) {
321         if ((grp = sudo_getgrgid(gids[i])) != NULL) {
322             len = strlen(grp->gr_name) + 1;
323             if (cp - (char *)grlitem + len > total) {
324                 total += len + groupname_len;
325                 efree(grlitem);
326                 sudo_gr_delref(grp);
327                 goto again;
328             }
329             memcpy(cp, grp->gr_name, len);
330             grlist->groups[ngroups++] = cp;
331             cp += len;
332             sudo_gr_delref(grp);
333         }
334     }
335     grlist->ngroups = ngroups;
336     efree(gids);
337
338 #ifdef HAVE_SETAUTHDB
339     aix_restoreauthdb();
340 #endif
341
342     debug_return_ptr(&grlitem->cache);
343 }