Line data Source code
1 : /* mgetgroups.c -- return a list of the groups a user is in
2 :
3 : Copyright (C) 2007-2008 Free Software Foundation, Inc.
4 :
5 : This program is free software: you can redistribute it and/or modify
6 : it under the terms of the GNU General Public License as published by
7 : the Free Software Foundation, either version 3 of the License, or
8 : (at your option) any later version.
9 :
10 : This program 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
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public License
16 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 :
18 : /* Extracted from coreutils' src/id.c. */
19 :
20 : #include <config.h>
21 :
22 : #include "mgetgroups.h"
23 :
24 : #include <stdlib.h>
25 : #include <unistd.h>
26 : #include <stdint.h>
27 : #include <string.h>
28 : #include <errno.h>
29 : #if HAVE_GETGROUPLIST
30 : # include <grp.h>
31 : #endif
32 : #include "getugroups.h"
33 : #include "xalloc.h"
34 :
35 :
36 : static void *
37 12 : allocate_groupbuf (int size)
38 : {
39 12 : if (xalloc_oversized (size, sizeof (GETGROUPS_T)))
40 : {
41 0 : errno = ENOMEM;
42 0 : return NULL;
43 : }
44 :
45 12 : return malloc (size * sizeof (GETGROUPS_T));
46 : }
47 :
48 : /* Like getugroups, but store the result in malloc'd storage.
49 : Set *GROUPS to the malloc'd list of all group IDs of which USERNAME
50 : is a member. If GID is not -1, store it first. GID should be the
51 : group ID (pw_gid) obtained from getpwuid, in case USERNAME is not
52 : listed in the groups database (e.g., /etc/groups). Upon failure,
53 : don't modify *GROUPS, set errno, and return -1. Otherwise, return
54 : the number of groups. */
55 :
56 : int
57 12 : mgetgroups (char const *username, gid_t gid, GETGROUPS_T **groups)
58 : {
59 : int max_n_groups;
60 : int ng;
61 : GETGROUPS_T *g;
62 :
63 : #if HAVE_GETGROUPLIST
64 : /* We prefer to use getgrouplist if available, because it has better
65 : performance characteristics.
66 :
67 : In glibc 2.3.2, getgrouplist is buggy. If you pass a zero as the
68 : size of the output buffer, getgrouplist will still write to the
69 : buffer. Contrary to what some versions of the getgrouplist
70 : manpage say, this doesn't happen with nonzero buffer sizes.
71 : Therefore our usage here just avoids a zero sized buffer. */
72 12 : if (username)
73 : {
74 : enum { N_GROUPS_INIT = 10 };
75 : GETGROUPS_T smallbuf[N_GROUPS_INIT];
76 :
77 12 : max_n_groups = N_GROUPS_INIT;
78 12 : ng = getgrouplist (username, gid, smallbuf, &max_n_groups);
79 :
80 12 : g = allocate_groupbuf (max_n_groups);
81 12 : if (g == NULL)
82 0 : return -1;
83 :
84 12 : if (max_n_groups <= N_GROUPS_INIT)
85 : {
86 : /* smallbuf was big enough, so we already have our data */
87 12 : memcpy (g, smallbuf, max_n_groups * sizeof *g);
88 12 : *groups = g;
89 12 : return max_n_groups;
90 : }
91 :
92 : while (1)
93 0 : {
94 : GETGROUPS_T *h;
95 0 : ng = getgrouplist (username, gid, g, &max_n_groups);
96 0 : if (0 <= ng)
97 : {
98 0 : *groups = g;
99 0 : return ng;
100 : }
101 :
102 : /* When getgrouplist fails, it guarantees that
103 : max_n_groups reflects the new number of groups. */
104 :
105 0 : if (xalloc_oversized (max_n_groups, sizeof *h)
106 0 : || (h = realloc (g, max_n_groups * sizeof *h)) == NULL)
107 : {
108 0 : int saved_errno = errno;
109 0 : free (g);
110 0 : errno = saved_errno;
111 0 : return -1;
112 : }
113 0 : g = h;
114 : }
115 : }
116 : /* else no username, so fall through and use getgroups. */
117 : #endif
118 :
119 0 : max_n_groups = (username
120 : ? getugroups (0, NULL, username, gid)
121 0 : : getgroups (0, NULL));
122 :
123 : /* If we failed to count groups with NULL for a buffer,
124 : try again with a non-NULL one, just in case. */
125 0 : if (max_n_groups < 0)
126 0 : max_n_groups = 5;
127 :
128 0 : g = allocate_groupbuf (max_n_groups);
129 0 : if (g == NULL)
130 0 : return -1;
131 :
132 0 : ng = (username
133 0 : ? getugroups (max_n_groups, g, username, gid)
134 0 : : getgroups (max_n_groups, g));
135 :
136 0 : if (ng < 0)
137 : {
138 0 : int saved_errno = errno;
139 0 : free (g);
140 0 : errno = saved_errno;
141 0 : return -1;
142 : }
143 :
144 0 : *groups = g;
145 0 : return ng;
146 : }
|