]> sourceware.org Git - newlib-cygwin.git/blob - winsup/cygwin/grp.cc
b70db13f88ec6a6a1b3a40075216a03bc21875d0
[newlib-cygwin.git] / winsup / cygwin / grp.cc
1 /* grp.cc
2
3 Copyright 1996, 1997, 1998, 2000, 2001 Cygnus Solutions.
4
5 Original stubs by Jason Molenda of Cygnus Support, crash@cygnus.com
6 First implementation by Gunther Ebert, gunther.ebert@ixos-leipzig.de
7
8 This file is part of Cygwin.
9
10 This software is a copyrighted work licensed under the terms of the
11 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
12 details. */
13
14 #include "winsup.h"
15 #include <grp.h>
16 #include <wininet.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <errno.h>
20 #include "sync.h"
21 #include "sigproc.h"
22 #include "pinfo.h"
23 #include "security.h"
24 #include "fhandler.h"
25 #include "dtable.h"
26 #include "path.h"
27 #include "cygheap.h"
28 #include "cygerrno.h"
29
30 /* Read /etc/group only once for better performance. This is done
31 on the first call that needs information from it. */
32
33 static NO_COPY const char *etc_group = "/etc/group";
34 static struct group *group_buf; /* group contents in memory */
35 static int curr_lines;
36 static int max_lines;
37
38 /* Position in the group cache */
39 #ifdef _MT_SAFE
40 #define grp_pos _reent_winsup()->_grp_pos
41 #else
42 static int grp_pos = 0;
43 #endif
44
45 /* Set to loaded when /etc/passwd has been read in by read_etc_passwd ().
46 Set to emulated if passwd is emulated. */
47 /* Functions in this file need to check the value of passwd_state
48 and read in the password file if it isn't set. */
49 enum grp_state {
50 uninitialized = 0,
51 initializing,
52 emulated,
53 loaded
54 };
55 class grp_check {
56 grp_state state;
57 FILETIME last_modified;
58 char grp_w32[MAX_PATH];
59
60 public:
61 grp_check () : state (uninitialized)
62 {
63 last_modified.dwLowDateTime = last_modified.dwHighDateTime = 0;
64 grp_w32[0] = '\0';
65 }
66 operator grp_state ()
67 {
68 HANDLE h;
69 WIN32_FIND_DATA data;
70
71 if (!grp_w32[0]) /* First call. */
72 {
73 path_conv g ("/etc/group", PC_SYM_FOLLOW | PC_FULL);
74 if (!g.error)
75 strcpy (grp_w32, g.get_win32 ());
76 }
77
78 if ((h = FindFirstFile (grp_w32, &data)) != INVALID_HANDLE_VALUE)
79 {
80 if (CompareFileTime (&data.ftLastWriteTime, &last_modified) > 0)
81 {
82 state = uninitialized;
83 last_modified = data.ftLastWriteTime;
84 }
85 FindClose (h);
86 }
87 return state;
88 }
89 void operator = (grp_state nstate)
90 {
91 state = nstate;
92 }
93 };
94
95 static grp_check group_state;
96
97 static int
98 parse_grp (struct group &grp, const char *line)
99 {
100 int len = strlen(line);
101 char *newline = (char *) malloc (len + 1);
102 (void) memcpy (newline, line, len + 1);
103
104 if (newline[--len] == '\n')
105 newline[len] = '\0';
106
107 char *dp = strchr (newline, ':');
108
109 if (!dp)
110 return 0;
111
112 *dp++ = '\0';
113 grp.gr_name = newline;
114
115 grp.gr_passwd = dp;
116 dp = strchr (grp.gr_passwd, ':');
117 if (dp)
118 {
119 *dp++ = '\0';
120 if (!strlen (grp.gr_passwd))
121 grp.gr_passwd = NULL;
122
123 grp.gr_gid = strtol (dp, NULL, 10);
124 dp = strchr (dp, ':');
125 if (dp)
126 {
127 if (*++dp)
128 {
129 int i = 0;
130 char *cp;
131
132 for (cp = dp; (cp = strchr (cp, ',')) != NULL; ++cp)
133 ++i;
134 char **namearray = (char **) calloc (i + 2, sizeof (char *));
135 if (namearray)
136 {
137 i = 0;
138 for (cp = dp; (cp = strchr (dp, ',')) != NULL; dp = cp + 1)
139 {
140 *cp = '\0';
141 namearray[i++] = dp;
142 }
143 namearray[i++] = dp;
144 namearray[i] = NULL;
145 }
146 grp.gr_mem = namearray;
147 }
148 else
149 grp.gr_mem = (char **) calloc (1, sizeof (char *));
150 return 1;
151 }
152 }
153 return 0;
154 }
155
156 /* Read one line from /etc/group into the group cache */
157 static void
158 add_grp_line (const char *line)
159 {
160 if (curr_lines == max_lines)
161 {
162 max_lines += 10;
163 group_buf = (struct group *) realloc (group_buf, max_lines * sizeof (struct group));
164 }
165 if (parse_grp (group_buf[curr_lines], line))
166 curr_lines++;
167 }
168
169 /* Cygwin internal */
170 /* Read in /etc/group and save contents in the group cache */
171 /* This sets group_in_memory_p to 1 so functions in this file can
172 tell that /etc/group has been read in */
173 /* FIXME: should be static but this is called in uinfo_init outside this
174 file */
175 void
176 read_etc_group ()
177 {
178 char linebuf [200];
179 char group_name [UNLEN + 1];
180 DWORD group_name_len = UNLEN + 1;
181
182 strncpy (group_name, "Administrators", sizeof (group_name));
183
184 static NO_COPY pthread_mutex_t etc_group_mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
185 pthread_mutex_lock (&etc_group_mutex);
186
187 /* if we got blocked by the mutex, then etc_group may have been processed */
188 if (group_state != uninitialized)
189 {
190 pthread_mutex_unlock(&etc_group_mutex);
191 return;
192 }
193
194 if (group_state != initializing)
195 {
196 group_state = initializing;
197 if (max_lines) /* When rereading, free allocated memory first. */
198 {
199 for (int i = 0; i < curr_lines; ++i)
200 {
201 free (group_buf[i].gr_name);
202 free (group_buf[i].gr_mem);
203 }
204 free (group_buf);
205 curr_lines = max_lines = 0;
206 }
207
208 FILE *f = fopen (etc_group, "rt");
209
210 if (f)
211 {
212 while (fgets (linebuf, sizeof (linebuf), f) != NULL)
213 {
214 if (strlen (linebuf))
215 add_grp_line (linebuf);
216 }
217
218 fclose (f);
219 group_state = loaded;
220 }
221 else /* /etc/group doesn't exist -- create default one in memory */
222 {
223 char domain_name [INTERNET_MAX_HOST_NAME_LENGTH + 1];
224 DWORD domain_name_len = INTERNET_MAX_HOST_NAME_LENGTH + 1;
225 SID_NAME_USE acType;
226 debug_printf ("Emulating /etc/group");
227 if (! LookupAccountSidA (NULL ,
228 well_known_admins_sid,
229 group_name,
230 &group_name_len,
231 domain_name,
232 &domain_name_len,
233 &acType))
234 {
235 strcpy (group_name, "unknown");
236 debug_printf ("Failed to get local admins group name. %E");
237 }
238
239 snprintf (linebuf, sizeof (linebuf), "%s::%u:\n", group_name, DEFAULT_GID);
240 add_grp_line (linebuf);
241 group_state = emulated;
242 }
243 }
244
245 pthread_mutex_unlock(&etc_group_mutex);
246 }
247
248 extern "C"
249 struct group *
250 getgrgid (gid_t gid)
251 {
252 struct group * default_grp = NULL;
253 if (group_state <= initializing)
254 read_etc_group();
255
256 for (int i = 0; i < curr_lines; i++)
257 {
258 if (group_buf[i].gr_gid == DEFAULT_GID)
259 default_grp = group_buf + i;
260 if (group_buf[i].gr_gid == gid)
261 return group_buf + i;
262 }
263
264 return default_grp;
265 }
266
267 extern "C"
268 struct group *
269 getgrnam (const char *name)
270 {
271 if (group_state <= initializing)
272 read_etc_group();
273
274 for (int i = 0; i < curr_lines; i++)
275 if (strcasematch (group_buf[i].gr_name, name))
276 return group_buf + i;
277
278 /* Didn't find requested group */
279 return NULL;
280 }
281
282 extern "C"
283 void
284 endgrent()
285 {
286 grp_pos = 0;
287 }
288
289 extern "C"
290 struct group *
291 getgrent()
292 {
293 if (group_state <= initializing)
294 read_etc_group();
295
296 if (grp_pos < curr_lines)
297 return group_buf + grp_pos++;
298
299 return NULL;
300 }
301
302 extern "C"
303 void
304 setgrent ()
305 {
306 grp_pos = 0;
307 }
308
309 /* Internal function. ONLY USE THIS INTERNALLY, NEVER `getgrent'!!! */
310 struct group *
311 internal_getgrent (int pos)
312 {
313 if (group_state <= initializing)
314 read_etc_group();
315
316 if (pos < curr_lines)
317 return group_buf + pos;
318 return NULL;
319 }
320
321 int
322 getgroups (int gidsetsize, gid_t *grouplist, gid_t gid, const char *username)
323 {
324 HANDLE hToken = NULL;
325 DWORD size;
326 int cnt = 0;
327 struct group *gr;
328
329 if (group_state <= initializing)
330 read_etc_group();
331
332 if (allow_ntsec &&
333 OpenProcessToken (hMainProc, TOKEN_QUERY, &hToken))
334 {
335 if (GetTokenInformation (hToken, TokenGroups, NULL, 0, &size)
336 || GetLastError () == ERROR_INSUFFICIENT_BUFFER)
337 {
338 char buf[size];
339 TOKEN_GROUPS *groups = (TOKEN_GROUPS *) buf;
340
341 if (GetTokenInformation (hToken, TokenGroups, buf, size, &size))
342 {
343 cygsid sid;
344
345 for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx)
346 if (sid.getfromgr (gr))
347 for (DWORD pg = 0; pg < groups->GroupCount; ++pg)
348 if (sid == groups->Groups[pg].Sid)
349 {
350 if (cnt < gidsetsize)
351 grouplist[cnt] = gr->gr_gid;
352 ++cnt;
353 if (gidsetsize && cnt > gidsetsize)
354 {
355 CloseHandle (hToken);
356 goto error;
357 }
358 break;
359 }
360 }
361 }
362 else
363 debug_printf ("%d = GetTokenInformation(NULL) %E", size);
364 CloseHandle (hToken);
365 if (cnt)
366 return cnt;
367 }
368
369 for (int gidx = 0; (gr = internal_getgrent (gidx)); ++gidx)
370 if (gid == gr->gr_gid)
371 {
372 if (cnt < gidsetsize)
373 grouplist[cnt] = gr->gr_gid;
374 ++cnt;
375 if (gidsetsize && cnt > gidsetsize)
376 goto error;
377 }
378 else if (gr->gr_mem)
379 for (int gi = 0; gr->gr_mem[gi]; ++gi)
380 if (strcasematch (username, gr->gr_mem[gi]))
381 {
382 if (cnt < gidsetsize)
383 grouplist[cnt] = gr->gr_gid;
384 ++cnt;
385 if (gidsetsize && cnt > gidsetsize)
386 goto error;
387 }
388 return cnt;
389
390 error:
391 set_errno (EINVAL);
392 return -1;
393 }
394
395 extern "C"
396 int
397 getgroups (int gidsetsize, gid_t *grouplist)
398 {
399 return getgroups (gidsetsize, grouplist, myself->gid, cygheap->user.name ());
400 }
401
402 extern "C"
403 int
404 initgroups (const char *, gid_t)
405 {
406 return 0;
407 }
This page took 0.051727 seconds and 4 git commands to generate.