]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* mkgroup.c: |
2 | ||
1fd5e000 CF |
3 | This file is part of Cygwin. |
4 | ||
5 | This software is a copyrighted work licensed under the terms of the | |
6 | Cygwin license. Please consult the file "CYGWIN_LICENSE" for | |
7 | details. */ | |
8 | ||
92b499ac | 9 | #include <errno.h> |
1fd5e000 CF |
10 | #include <ctype.h> |
11 | #include <stdlib.h> | |
12 | #include <wchar.h> | |
0bdab5c8 | 13 | #include <wctype.h> |
73535010 | 14 | #include <locale.h> |
1fd5e000 | 15 | #include <stdio.h> |
a1e19903 | 16 | #include <unistd.h> |
61522196 | 17 | #include <inttypes.h> |
315f8fd3 | 18 | #include <getopt.h> |
a1e19903 | 19 | #include <io.h> |
16a976cf | 20 | #include <grp.h> |
a1e19903 CV |
21 | #include <sys/fcntl.h> |
22 | #include <sys/cygwin.h> | |
92b499ac | 23 | #include <cygwin/version.h> |
a1e19903 CV |
24 | #include <windows.h> |
25 | #include <lm.h> | |
375a780e CV |
26 | #include <wininet.h> |
27 | #include <iptypes.h> | |
eeec2a48 CV |
28 | #include <ntsecapi.h> |
29 | #include <ntdef.h> | |
1fd5e000 | 30 | |
d829da14 CV |
31 | #define print_win_error(x) _print_win_error(x, __LINE__) |
32 | ||
1fd5e000 CF |
33 | SID_IDENTIFIER_AUTHORITY sid_world_auth = {SECURITY_WORLD_SID_AUTHORITY}; |
34 | SID_IDENTIFIER_AUTHORITY sid_nt_auth = {SECURITY_NT_AUTHORITY}; | |
35 | ||
36 | #ifndef min | |
37 | #define min(a,b) (((a)<(b))?(a):(b)) | |
38 | #endif | |
39 | ||
a1e19903 CV |
40 | typedef struct |
41 | { | |
42 | char *str; | |
f9519bcd | 43 | BOOL domain; |
4acb3408 | 44 | BOOL with_dom; |
a1e19903 CV |
45 | } domlist_t; |
46 | ||
6510edf4 | 47 | static void |
a1e19903 CV |
48 | _print_win_error (DWORD code, int line) |
49 | { | |
50 | char buf[4096]; | |
51 | ||
52 | if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | |
53 | | FORMAT_MESSAGE_IGNORE_INSERTS, | |
54 | NULL, | |
55 | code, | |
56 | MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), | |
57 | (LPTSTR) buf, sizeof (buf), NULL)) | |
61522196 CV |
58 | fprintf (stderr, "mkgroup (%d): [%" PRIu32 "] %s", |
59 | line, (unsigned int) code, buf); | |
a1e19903 | 60 | else |
61522196 CV |
61 | fprintf (stderr, "mkgroup (%d): error %" PRIu32 , |
62 | line, (unsigned int) code); | |
a1e19903 CV |
63 | } |
64 | ||
6510edf4 | 65 | static char * |
0bdab5c8 | 66 | put_sid (PSID psid) |
1fd5e000 CF |
67 | { |
68 | static char s[512]; | |
69 | char t[32]; | |
70 | DWORD i; | |
71 | ||
72 | strcpy (s, "S-1-"); | |
0bdab5c8 | 73 | sprintf(t, "%u", GetSidIdentifierAuthority (psid)->Value[5]); |
1fd5e000 | 74 | strcat (s, t); |
0bdab5c8 | 75 | for (i = 0; i < *GetSidSubAuthorityCount (psid); ++i) |
1fd5e000 | 76 | { |
61522196 | 77 | sprintf(t, "-%" PRIu32 , (unsigned int) *GetSidSubAuthority (psid, i)); |
1fd5e000 CF |
78 | strcat (s, t); |
79 | } | |
80 | return s; | |
81 | } | |
82 | ||
375a780e CV |
83 | typedef struct { |
84 | BYTE Revision; | |
85 | BYTE SubAuthorityCount; | |
86 | SID_IDENTIFIER_AUTHORITY IdentifierAuthority; | |
87 | DWORD SubAuthority[8]; | |
88 | } DBGSID, *PDBGSID; | |
89 | ||
90 | #define MAX_BUILTIN_SIDS 100 /* Should be enough for the forseable future. */ | |
91 | DBGSID builtin_sid_list[MAX_BUILTIN_SIDS]; | |
92 | DWORD builtin_sid_cnt; | |
93 | ||
9258eca9 CV |
94 | typedef struct { |
95 | PSID psid; | |
96 | int buffer[10]; | |
97 | } sidbuf; | |
98 | ||
6510edf4 CF |
99 | static sidbuf curr_pgrp; |
100 | static BOOL got_curr_pgrp = FALSE; | |
9258eca9 | 101 | |
6510edf4 | 102 | static void |
9258eca9 CV |
103 | fetch_current_pgrp_sid () |
104 | { | |
105 | DWORD len; | |
106 | HANDLE ptok; | |
107 | ||
108 | if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &ptok) | |
109 | || !GetTokenInformation (ptok, TokenPrimaryGroup, &curr_pgrp, | |
110 | sizeof curr_pgrp, &len) | |
111 | || !CloseHandle (ptok)) | |
112 | { | |
113 | print_win_error (GetLastError ()); | |
114 | return; | |
115 | } | |
116 | } | |
117 | ||
6510edf4 | 118 | static void |
16a976cf | 119 | enum_unix_groups (domlist_t *mach, const char *sep, DWORD id_offset, |
0bdab5c8 CV |
120 | char *unix_grp_list) |
121 | { | |
122 | WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; | |
0bdab5c8 CV |
123 | SID_IDENTIFIER_AUTHORITY auth = { { 0, 0, 0, 0, 0, 22 } }; |
124 | char *gstr, *grp_list; | |
125 | WCHAR grp[GNLEN + sizeof ("Unix Group\\") + 1]; | |
126 | WCHAR dom[MAX_DOMAIN_NAME_LEN + 1]; | |
127 | DWORD glen, dlen, sidlen; | |
128 | PSID psid; | |
773f4fa5 CV |
129 | PSID numeric_psid; |
130 | char psid_buffer[SECURITY_MAX_SID_SIZE]; | |
0bdab5c8 CV |
131 | SID_NAME_USE acc_type; |
132 | ||
16a976cf | 133 | int ret = mbstowcs (machine, mach->str, INTERNET_MAX_HOST_NAME_LENGTH + 1); |
0bdab5c8 CV |
134 | if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1) |
135 | { | |
136 | fprintf (stderr, "%s: Invalid machine name '%s'. Skipping...\n", | |
16a976cf | 137 | program_invocation_short_name, mach->str); |
0bdab5c8 CV |
138 | return; |
139 | } | |
0bdab5c8 | 140 | |
773f4fa5 CV |
141 | if (!AllocateAndInitializeSid (&auth, 2, 2, 0, 0, 0, 0, 0, 0, 0, |
142 | &numeric_psid)) | |
0bdab5c8 CV |
143 | return; |
144 | ||
145 | if (!(grp_list = strdup (unix_grp_list))) | |
146 | { | |
773f4fa5 | 147 | FreeSid (numeric_psid); |
0bdab5c8 CV |
148 | return; |
149 | } | |
150 | ||
151 | for (gstr = strtok (grp_list, ","); gstr; gstr = strtok (NULL, ",")) | |
152 | { | |
05e6f7b2 | 153 | if (!isdigit ((unsigned char) gstr[0]) && gstr[0] != '-') |
0bdab5c8 CV |
154 | { |
155 | PWCHAR p = wcpcpy (grp, L"Unix Group\\"); | |
156 | ret = mbstowcs (p, gstr, GNLEN + 1); | |
157 | if (ret < 1 || ret >= GNLEN + 1) | |
773f4fa5 CV |
158 | { |
159 | fprintf (stderr, "%s: Invalid group name '%s'. Skipping...\n", | |
160 | program_invocation_short_name, gstr); | |
161 | continue; | |
162 | } | |
163 | psid = (PSID) psid_buffer; | |
164 | sidlen = SECURITY_MAX_SID_SIZE; | |
165 | dlen = MAX_DOMAIN_NAME_LEN + 1; | |
166 | if (LookupAccountNameW (machine, grp, psid, &sidlen, | |
167 | dom, &dlen, &acc_type)) | |
61522196 | 168 | printf ("%s%s%ls:%s:%" PRIu32 ":\n", |
df59ab7e CV |
169 | "Unix_Group", |
170 | sep, | |
0bdab5c8 CV |
171 | p, |
172 | put_sid (psid), | |
61522196 | 173 | (unsigned int) (id_offset + |
0bdab5c8 | 174 | *GetSidSubAuthority (psid, |
61522196 | 175 | *GetSidSubAuthorityCount(psid) - 1))); |
0bdab5c8 CV |
176 | } |
177 | else | |
178 | { | |
179 | DWORD start, stop; | |
180 | char *p = gstr; | |
181 | if (*p == '-') | |
182 | start = 0; | |
183 | else | |
184 | start = strtol (p, &p, 10); | |
185 | if (!*p) | |
186 | stop = start; | |
05e6f7b2 | 187 | else if (*p++ != '-' || !isdigit ((unsigned char) *p) |
0bdab5c8 CV |
188 | || (stop = strtol (p, &p, 10)) < start || *p) |
189 | { | |
190 | fprintf (stderr, "%s: Malformed unix group list entry '%s'. " | |
92b499ac CV |
191 | "Skipping...\n", |
192 | program_invocation_short_name, gstr); | |
0bdab5c8 CV |
193 | continue; |
194 | } | |
195 | for (; start <= stop; ++ start) | |
196 | { | |
773f4fa5 | 197 | psid = numeric_psid; |
0bdab5c8 CV |
198 | *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1) |
199 | = start; | |
773f4fa5 CV |
200 | glen = GNLEN + 1; |
201 | dlen = MAX_DOMAIN_NAME_LEN + 1; | |
202 | if (LookupAccountSidW (machine, psid, grp, &glen, | |
203 | dom, &dlen, &acc_type) | |
0bdab5c8 | 204 | && !iswdigit (grp[0])) |
61522196 | 205 | printf ("%s%s%ls:%s:%" PRIu32 ":\n", |
df59ab7e CV |
206 | "Unix_Group", |
207 | sep, | |
0bdab5c8 CV |
208 | grp, |
209 | put_sid (psid), | |
61522196 | 210 | (unsigned int) (id_offset + start)); |
0bdab5c8 CV |
211 | } |
212 | } | |
213 | } | |
214 | ||
215 | free (grp_list); | |
773f4fa5 | 216 | FreeSid (numeric_psid); |
0bdab5c8 CV |
217 | } |
218 | ||
6510edf4 | 219 | static int |
16a976cf | 220 | enum_local_groups (domlist_t *mach, const char *sep, |
6510edf4 CF |
221 | DWORD id_offset, char *disp_groupname, int print_builtin, |
222 | int print_current) | |
1fd5e000 | 223 | { |
a1e19903 | 224 | WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; |
1fd5e000 CF |
225 | LOCALGROUP_INFO_0 *buffer; |
226 | DWORD entriesread = 0; | |
227 | DWORD totalentries = 0; | |
61522196 | 228 | DWORD_PTR resume_handle = 0; |
a1e19903 | 229 | WCHAR gname[GNLEN + 1]; |
0ac91154 | 230 | DWORD rc; |
1fd5e000 | 231 | |
16a976cf CV |
232 | int ret = mbstowcs (machine, mach->str, INTERNET_MAX_HOST_NAME_LENGTH + 1); |
233 | if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1) | |
a1e19903 | 234 | { |
16a976cf CV |
235 | fprintf (stderr, "%s: Invalid machine name '%s'. Skipping...\n", |
236 | program_invocation_short_name, mach->str); | |
237 | return 1; | |
a1e19903 CV |
238 | } |
239 | ||
1fd5e000 CF |
240 | do |
241 | { | |
242 | DWORD i; | |
1fd5e000 | 243 | |
91dd009e | 244 | if (disp_groupname) |
e3118d88 | 245 | { |
a1e19903 | 246 | mbstowcs (gname, disp_groupname, GNLEN + 1); |
16a976cf | 247 | rc = NetLocalGroupGetInfo (machine, gname, 0, (void *) &buffer); |
bba48953 CV |
248 | if (rc == ERROR_SUCCESS) |
249 | entriesread = 1; | |
91dd009e CV |
250 | /* Allow further searching for the group and avoid annoying |
251 | error messages just because the group is not a local group or | |
252 | the group hasn't been found. */ | |
253 | else if (rc == ERROR_NO_SUCH_ALIAS || rc == NERR_GroupNotFound) | |
254 | return 0; | |
e3118d88 | 255 | } |
6510edf4 | 256 | else |
16a976cf | 257 | rc = NetLocalGroupEnum (machine, 0, (void *) &buffer, |
375a780e CV |
258 | MAX_PREFERRED_LENGTH, &entriesread, |
259 | &totalentries, &resume_handle); | |
1fd5e000 CF |
260 | switch (rc) |
261 | { | |
262 | case ERROR_ACCESS_DENIED: | |
a1e19903 CV |
263 | print_win_error (rc); |
264 | return 1; | |
1fd5e000 CF |
265 | |
266 | case ERROR_MORE_DATA: | |
267 | case ERROR_SUCCESS: | |
268 | break; | |
269 | ||
270 | default: | |
a1e19903 CV |
271 | print_win_error (rc); |
272 | return 1; | |
1fd5e000 CF |
273 | } |
274 | ||
275 | for (i = 0; i < entriesread; i++) | |
276 | { | |
375a780e CV |
277 | WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1]; |
278 | DWORD domname_len = MAX_DOMAIN_NAME_LEN + 1; | |
773f4fa5 | 279 | char psid_buffer[SECURITY_MAX_SID_SIZE]; |
1fd5e000 | 280 | PSID psid = (PSID) psid_buffer; |
773f4fa5 | 281 | DWORD sid_length = SECURITY_MAX_SID_SIZE; |
1fd5e000 CF |
282 | DWORD gid; |
283 | SID_NAME_USE acc_type; | |
375a780e CV |
284 | PDBGSID pdsid; |
285 | BOOL is_builtin = FALSE; | |
1fd5e000 | 286 | |
16a976cf | 287 | if (!LookupAccountNameW (machine, buffer[i].lgrpi0_name, psid, |
375a780e CV |
288 | &sid_length, domain_name, &domname_len, |
289 | &acc_type)) | |
1fd5e000 | 290 | { |
2d1bfd52 | 291 | print_win_error (GetLastError ()); |
375a780e | 292 | fprintf (stderr, " (%ls)\n", buffer[i].lgrpi0_name); |
1fd5e000 CF |
293 | continue; |
294 | } | |
6510edf4 CF |
295 | else if (acc_type == SidTypeDomain) |
296 | { | |
297 | WCHAR domname[MAX_DOMAIN_NAME_LEN + GNLEN + 2]; | |
526107a7 | 298 | PWCHAR p; |
6510edf4 | 299 | |
526107a7 CV |
300 | p = wcpcpy (domname, domain_name); |
301 | p = wcpcpy (p, L"\\"); | |
302 | p = wcpncpy (p, buffer[i].lgrpi0_name, GNLEN); | |
303 | *p = L'\0'; | |
773f4fa5 | 304 | sid_length = SECURITY_MAX_SID_SIZE; |
6510edf4 | 305 | domname_len = MAX_DOMAIN_NAME_LEN + 1; |
16a976cf | 306 | if (!LookupAccountNameW (machine, domname, |
6510edf4 CF |
307 | psid, &sid_length, |
308 | domain_name, &domname_len, | |
309 | &acc_type)) | |
310 | { | |
2d1bfd52 | 311 | print_win_error (GetLastError ()); |
375a780e | 312 | fprintf(stderr, " (%ls)\n", domname); |
6510edf4 CF |
313 | continue; |
314 | } | |
315 | } | |
1fd5e000 | 316 | |
375a780e CV |
317 | /* Store all local SIDs with prefix "S-1-5-32-" and check if it |
318 | has been printed already. This allows to get all builtin | |
319 | groups exactly once and not once per domain. */ | |
320 | pdsid = (PDBGSID) psid; | |
321 | if (pdsid->IdentifierAuthority.Value[5] == sid_nt_auth.Value[5] | |
322 | && pdsid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID) | |
323 | { | |
324 | int b; | |
325 | ||
f9519bcd | 326 | if (!print_builtin) |
6510edf4 | 327 | goto skip_group; |
375a780e | 328 | is_builtin = TRUE; |
f9519bcd | 329 | if (builtin_sid_cnt) |
375a780e CV |
330 | for (b = 0; b < builtin_sid_cnt; b++) |
331 | if (EqualSid (&builtin_sid_list[b], psid)) | |
332 | goto skip_group; | |
333 | if (builtin_sid_cnt < MAX_BUILTIN_SIDS) | |
334 | CopySid (sizeof (DBGSID), &builtin_sid_list[builtin_sid_cnt++], | |
335 | psid); | |
336 | } | |
6510edf4 CF |
337 | if (!print_current) |
338 | /* fall through */; | |
339 | else if (EqualSid (curr_pgrp.psid, psid)) | |
9258eca9 | 340 | got_curr_pgrp = TRUE; |
328b090f | 341 | |
1fd5e000 | 342 | gid = *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1); |
61522196 | 343 | printf ("%ls%s%ls:%s:%" PRIu32 ":\n", |
4acb3408 CV |
344 | mach->with_dom && !is_builtin ? domain_name : L"", |
345 | mach->with_dom && !is_builtin ? sep : "", | |
a1e19903 CV |
346 | buffer[i].lgrpi0_name, |
347 | put_sid (psid), | |
61522196 | 348 | (unsigned int) (gid + (is_builtin ? 0 : id_offset))); |
375a780e CV |
349 | skip_group: |
350 | ; | |
1fd5e000 CF |
351 | } |
352 | ||
375a780e | 353 | NetApiBufferFree (buffer); |
1fd5e000 CF |
354 | |
355 | } | |
0ac91154 | 356 | while (rc == ERROR_MORE_DATA); |
1fd5e000 | 357 | |
91dd009e | 358 | /* Return 1 if the single group we're looking for has been found here to |
5a625821 CV |
359 | avoid calling enum_groups for the same group, thus avoiding a spurious |
360 | error message "group name could not be found" in enum_groups. */ | |
91dd009e | 361 | return disp_groupname && entriesread ? 1 : 0; |
1fd5e000 CF |
362 | } |
363 | ||
6510edf4 | 364 | static void |
16a976cf CV |
365 | enum_groups (domlist_t *mach, const char *sep, DWORD id_offset, |
366 | char *disp_groupname, int print_current) | |
1fd5e000 | 367 | { |
a1e19903 | 368 | WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; |
1fd5e000 CF |
369 | GROUP_INFO_2 *buffer; |
370 | DWORD entriesread = 0; | |
371 | DWORD totalentries = 0; | |
61522196 | 372 | DWORD_PTR resume_handle = 0; |
a1e19903 | 373 | WCHAR gname[GNLEN + 1]; |
0ac91154 | 374 | DWORD rc; |
1fd5e000 | 375 | |
16a976cf CV |
376 | int ret = mbstowcs (machine, mach->str, INTERNET_MAX_HOST_NAME_LENGTH + 1); |
377 | if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1) | |
a1e19903 | 378 | { |
16a976cf CV |
379 | fprintf (stderr, "%s: Invalid machine name '%s'. Skipping...\n", |
380 | program_invocation_short_name, mach->str); | |
381 | return; | |
a1e19903 CV |
382 | } |
383 | ||
1fd5e000 CF |
384 | do |
385 | { | |
386 | DWORD i; | |
1fd5e000 | 387 | |
e3118d88 CV |
388 | if (disp_groupname != NULL) |
389 | { | |
a1e19903 | 390 | mbstowcs (gname, disp_groupname, GNLEN + 1); |
16a976cf | 391 | rc = NetGroupGetInfo (machine, (LPWSTR) & gname, 2, (void *) &buffer); |
e3118d88 | 392 | entriesread=1; |
91dd009e CV |
393 | /* Avoid annoying error messages just because the group hasn't been |
394 | found. */ | |
395 | if (rc == NERR_GroupNotFound) | |
396 | return; | |
e3118d88 | 397 | } |
6510edf4 | 398 | else |
16a976cf CV |
399 | rc = NetGroupEnum (machine, 2, (void *) & buffer, MAX_PREFERRED_LENGTH, |
400 | &entriesread, &totalentries, &resume_handle); | |
1fd5e000 CF |
401 | switch (rc) |
402 | { | |
403 | case ERROR_ACCESS_DENIED: | |
a1e19903 CV |
404 | print_win_error (rc); |
405 | return; | |
1fd5e000 CF |
406 | |
407 | case ERROR_MORE_DATA: | |
408 | case ERROR_SUCCESS: | |
409 | break; | |
410 | ||
411 | default: | |
a1e19903 CV |
412 | print_win_error (rc); |
413 | return; | |
1fd5e000 CF |
414 | } |
415 | ||
416 | for (i = 0; i < entriesread; i++) | |
417 | { | |
375a780e CV |
418 | WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1]; |
419 | DWORD domname_len = MAX_DOMAIN_NAME_LEN + 1; | |
773f4fa5 | 420 | char psid_buffer[SECURITY_MAX_SID_SIZE]; |
1fd5e000 | 421 | PSID psid = (PSID) psid_buffer; |
773f4fa5 | 422 | DWORD sid_length = SECURITY_MAX_SID_SIZE; |
1fd5e000 CF |
423 | SID_NAME_USE acc_type; |
424 | ||
425 | int gid = buffer[i].grpi2_group_id; | |
16a976cf | 426 | if (!LookupAccountNameW (machine, buffer[i].grpi2_name, |
a1e19903 CV |
427 | psid, &sid_length, |
428 | domain_name, &domname_len, | |
429 | &acc_type)) | |
430 | { | |
2d1bfd52 | 431 | print_win_error (GetLastError ()); |
a1e19903 CV |
432 | fprintf(stderr, " (%ls)\n", buffer[i].grpi2_name); |
433 | continue; | |
434 | } | |
435 | else if (acc_type == SidTypeDomain) | |
436 | { | |
437 | WCHAR domname[MAX_DOMAIN_NAME_LEN + GNLEN + 2]; | |
526107a7 | 438 | PWCHAR p; |
a1e19903 | 439 | |
526107a7 CV |
440 | p = wcpcpy (domname, machine); |
441 | p = wcpcpy (p, L"\\"); | |
442 | p = wcpncpy (p, buffer[i].grpi2_name, GNLEN); | |
443 | *p = L'\0'; | |
773f4fa5 | 444 | sid_length = SECURITY_MAX_SID_SIZE; |
a1e19903 | 445 | domname_len = MAX_DOMAIN_NAME_LEN + 1; |
16a976cf CV |
446 | if (!LookupAccountNameW (machine, domname, psid, &sid_length, |
447 | domain_name, &domname_len, &acc_type)) | |
a1e19903 | 448 | { |
2d1bfd52 | 449 | print_win_error (GetLastError ()); |
a1e19903 CV |
450 | fprintf(stderr, " (%ls)\n", domname); |
451 | continue; | |
452 | } | |
453 | } | |
6510edf4 CF |
454 | if (!print_current) |
455 | /* fall through */; | |
456 | else if (EqualSid (curr_pgrp.psid, psid)) | |
9258eca9 | 457 | got_curr_pgrp = TRUE; |
328b090f | 458 | |
61522196 | 459 | printf ("%ls%s%ls:%s:%" PRIu32 ":\n", |
4acb3408 CV |
460 | mach->with_dom ? domain_name : L"", |
461 | mach->with_dom ? sep : "", | |
a1e19903 CV |
462 | buffer[i].grpi2_name, |
463 | put_sid (psid), | |
61522196 | 464 | (unsigned int) (id_offset + gid)); |
1fd5e000 CF |
465 | } |
466 | ||
375a780e | 467 | NetApiBufferFree (buffer); |
1fd5e000 CF |
468 | |
469 | } | |
0ac91154 | 470 | while (rc == ERROR_MORE_DATA); |
1fd5e000 CF |
471 | } |
472 | ||
e7fca6f8 | 473 | static int __attribute__ ((__noreturn__)) |
a1e19903 | 474 | usage (FILE * stream) |
1fd5e000 | 475 | { |
a1e19903 | 476 | fprintf (stream, |
92b499ac CV |
477 | "Usage: %s [OPTION]...\n" |
478 | "\n" | |
16a976cf | 479 | "Write /etc/group-like output to stdout\n" |
a1e19903 | 480 | "\n" |
c72a0d36 CV |
481 | "Don't use this command to generate a local /etc/group file, unless you\n" |
482 | "really need one. See the Cygwin User's Guide for more information.\n" | |
483 | "\n" | |
a1e19903 | 484 | "Options:\n" |
92b499ac | 485 | "\n" |
35983199 | 486 | " -l,--local [machine] Print local group accounts of \"machine\",\n" |
4acb3408 | 487 | " from local machine if no machine specified.\n" |
35983199 CV |
488 | " Automatically adding machine prefix for local\n" |
489 | " machine depends on settings in /etc/nsswitch.conf.\n" | |
490 | " -L,--Local machine Ditto, but generate groupname with machine prefix.\n" | |
491 | " -d,--domain [domain] Print domain groups,\n" | |
492 | " from current domain if no domain specified.\n" | |
493 | " -c,--current Print current group.\n" | |
494 | " -S,--separator char For -L use character char as domain\\group\n" | |
495 | " separator in groupname instead of default '%s'.\n" | |
496 | " -o,--id-offset offset Change the default offset (0x10000) added to gids\n" | |
497 | " of foreign local machine accounts. Use with -l/-L.\n" | |
498 | " -g,--group groupname Only return information for the specified group.\n" | |
499 | " One of -l, -d must be specified, too.\n" | |
500 | " -b,--no-builtin Don't print BUILTIN groups.\n" | |
501 | " -U,--unix grouplist Print UNIX groups when using -l on a UNIX Samba\n" | |
502 | " server. Grouplist is a comma-separated list of\n" | |
16a976cf | 503 | " groupnames or gid ranges (root,-25,50-100).\n" |
35983199 CV |
504 | " Enumerating large ranges can take a long time!\n" |
505 | " -h,--help Print this message.\n" | |
506 | " -v,--version Print version information and exit.\n" | |
a1e19903 CV |
507 | "\n" |
508 | "Default is to print local groups on stand-alone machines, plus domain\n" | |
c72a0d36 | 509 | "groups on domain controllers and domain member machines.\n" |
16a976cf CV |
510 | "\n", program_invocation_short_name, |
511 | (const char *) cygwin_internal (CW_GETNSSSEP)); | |
e7fca6f8 | 512 | exit (0); |
1fd5e000 CF |
513 | } |
514 | ||
315f8fd3 | 515 | struct option longopts[] = { |
f9519bcd | 516 | {"no-builtin", no_argument, NULL, 'b'}, |
f1c9046a | 517 | {"current", no_argument, NULL, 'c'}, |
a1e19903 CV |
518 | {"Current", no_argument, NULL, 'C'}, |
519 | {"domain", optional_argument, NULL, 'd'}, | |
520 | {"Domain", optional_argument, NULL, 'D'}, | |
521 | {"group", required_argument, NULL, 'g'}, | |
522 | {"help", no_argument, NULL, 'h'}, | |
523 | {"local", optional_argument, NULL, 'l'}, | |
524 | {"Local", optional_argument, NULL, 'L'}, | |
9f2cad57 | 525 | {"id-offset", required_argument, NULL, 'o'}, |
315f8fd3 | 526 | {"no-sids", no_argument, NULL, 's'}, |
a1e19903 | 527 | {"separator", required_argument, NULL, 'S'}, |
8a3adc99 | 528 | {"users", no_argument, NULL, 'u'}, |
0bdab5c8 | 529 | {"unix", required_argument, NULL, 'U'}, |
92b499ac | 530 | {"version", no_argument, NULL, 'V'}, |
315f8fd3 CV |
531 | {0, no_argument, NULL, 0} |
532 | }; | |
533 | ||
92b499ac | 534 | static char opts[] = "bcCd::D::g:hl::L::o:sS:uU:V"; |
22a9157c | 535 | |
6510edf4 | 536 | static void |
22a9157c CV |
537 | print_version () |
538 | { | |
92b499ac | 539 | printf ("mkgroup (cygwin) %d.%d.%d\n" |
1b23b30b | 540 | "Group File Generator\n" |
6e623e93 | 541 | "Copyright (C) 1997 - %s Cygwin Authors\n" |
1b23b30b | 542 | "This is free software; see the source for copying conditions. There is NO\n" |
92b499ac | 543 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", |
1b23b30b CF |
544 | CYGWIN_VERSION_DLL_MAJOR / 1000, |
545 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
546 | CYGWIN_VERSION_DLL_MINOR, | |
547 | strrchr (__DATE__, ' ') + 1); | |
22a9157c | 548 | } |
315f8fd3 | 549 | |
1fd5e000 CF |
550 | int |
551 | main (int argc, char **argv) | |
552 | { | |
f9519bcd CV |
553 | int print_domlist = 0; |
554 | domlist_t domlist[32]; | |
4acb3408 | 555 | char cname[1024]; |
16a976cf | 556 | char *opt, *p; |
a1e19903 | 557 | int print_current = 0; |
f9519bcd | 558 | int print_builtin = 1; |
0bdab5c8 | 559 | char *print_unix = NULL; |
16a976cf CV |
560 | const char *sep_char = (const char *) cygwin_internal (CW_GETNSSSEP); |
561 | DWORD id_offset = 0x10000, off; | |
f9519bcd | 562 | int c, i; |
e3118d88 | 563 | char *disp_groupname = NULL; |
01dd3162 | 564 | int optional_args = 0; |
f3939c05 | 565 | uintptr_t nss_src = cygwin_internal (CW_GETNSS_GRP_SRC); |
eeec2a48 | 566 | |
a1e19903 CV |
567 | if (!isatty (1)) |
568 | setmode (1, O_BINARY); | |
f1c9046a | 569 | |
73535010 CV |
570 | /* Use locale from environment. If not set or set to "C", use UTF-8. */ |
571 | setlocale (LC_CTYPE, ""); | |
572 | if (!strcmp (setlocale (LC_CTYPE, NULL), "C")) | |
573 | setlocale (LC_CTYPE, "en_US.UTF-8"); | |
6510edf4 CF |
574 | fetch_current_pgrp_sid (); |
575 | ||
a1e19903 | 576 | if (argc == 1) |
011ec894 | 577 | { |
16a976cf CV |
578 | int enums = ENUM_PRIMARY | ENUM_LOCAL | ENUM_BUILTIN; |
579 | uintptr_t ticket = cygwin_internal (CW_SETENT, TRUE, enums, NULL); | |
580 | if (ticket) | |
011ec894 | 581 | { |
16a976cf CV |
582 | struct group *grp; |
583 | ||
584 | while ((grp = (struct group *) cygwin_internal (CW_GETENT, TRUE, | |
585 | ticket))) | |
586 | printf ("%s:%s:%u:\n", grp->gr_name, grp->gr_passwd, grp->gr_gid); | |
587 | cygwin_internal (CW_ENDENT, TRUE, ticket); | |
011ec894 | 588 | } |
315f8fd3 CV |
589 | return 0; |
590 | } | |
f1c9046a | 591 | |
01dd3162 | 592 | unsetenv ("POSIXLY_CORRECT"); /* To get optional arg processing right. */ |
a1e19903 CV |
593 | while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) |
594 | switch (c) | |
595 | { | |
f9519bcd CV |
596 | case 'd': |
597 | case 'D': | |
a1e19903 CV |
598 | case 'l': |
599 | case 'L': | |
f9519bcd | 600 | if (print_domlist >= 32) |
a1e19903 | 601 | { |
f9519bcd | 602 | fprintf (stderr, "%s: Can not enumerate from more than 32 " |
92b499ac CV |
603 | "domains and machines.\n", |
604 | program_invocation_short_name); | |
a1e19903 CV |
605 | return 1; |
606 | } | |
f36c78a4 | 607 | domlist[print_domlist].domain = (c == 'd' || c == 'D'); |
a1e19903 CV |
608 | opt = optarg ?: |
609 | argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL; | |
096df177 | 610 | if (argv[optind] && opt == argv[optind]) |
01dd3162 | 611 | ++optional_args; |
f9519bcd | 612 | for (i = 0; i < print_domlist; ++i) |
f36c78a4 CV |
613 | if (domlist[i].domain == domlist[print_domlist].domain |
614 | && ((!domlist[i].str && !opt) | |
615 | || (domlist[i].str && opt | |
616 | && (off = strlen (domlist[i].str)) | |
617 | && !strncmp (domlist[i].str, opt, off) | |
618 | && (!opt[off] || opt[off] == ',')))) | |
619 | { | |
620 | fprintf (stderr, "%s: Duplicate %s '%s'. Skipping...\n", | |
92b499ac CV |
621 | program_invocation_short_name, |
622 | domlist[i].domain ? "domain" : "machine", | |
f36c78a4 | 623 | domlist[i].str); |
df59ab7e | 624 | break; |
f36c78a4 | 625 | } |
16a976cf | 626 | domlist[print_domlist].str = opt; |
f9519bcd | 627 | if (opt && (p = strchr (opt, ','))) |
a1e19903 | 628 | { |
16a976cf | 629 | if (p == opt) |
f9519bcd | 630 | { |
4acb3408 | 631 | fprintf (stderr, "%s: Malformed machine string '%s'. " |
92b499ac | 632 | "Skipping...\n", program_invocation_short_name, opt); |
6510edf4 | 633 | break; |
f9519bcd CV |
634 | } |
635 | *p = '\0'; | |
a1e19903 | 636 | } |
4acb3408 | 637 | if (c == 'l' || c == 'L') |
df59ab7e | 638 | { |
df59ab7e CV |
639 | DWORD csize = sizeof cname; |
640 | ||
4acb3408 CV |
641 | domlist[print_domlist].with_dom = (c == 'L'); |
642 | if (!opt) | |
643 | { | |
644 | /* If the system uses /etc/group exclusively as account DB, | |
645 | create local group names the old fashioned way. */ | |
f3939c05 | 646 | if (nss_src == NSS_SRC_FILES) |
4acb3408 CV |
647 | { |
648 | GetComputerNameExA (ComputerNameNetBIOS, cname, &csize); | |
649 | domlist[print_domlist].str = cname; | |
650 | } | |
651 | } | |
f3939c05 | 652 | else if (nss_src != NSS_SRC_FILES) |
4acb3408 CV |
653 | { |
654 | /* If the system uses Windows account DBs, check if machine | |
655 | name is local machine. If so, remove the domain name to | |
656 | enforce system naming convention. */ | |
657 | if (GetComputerNameExA (strchr (opt, '.') | |
658 | ? ComputerNameDnsFullyQualified | |
659 | : ComputerNameNetBIOS, | |
660 | cname, &csize) | |
661 | && strcasecmp (opt, cname) == 0) | |
662 | domlist[print_domlist].str = NULL; | |
663 | } | |
df59ab7e CV |
664 | } |
665 | ++print_domlist; | |
a1e19903 CV |
666 | break; |
667 | case 'S': | |
668 | sep_char = optarg; | |
669 | if (strlen (sep_char) > 1) | |
670 | { | |
16a976cf CV |
671 | fprintf (stderr, "%s: Only one ASCII character allowed as " |
672 | "domain\\user separator character.\n", | |
92b499ac | 673 | program_invocation_short_name); |
a1e19903 CV |
674 | return 1; |
675 | } | |
676 | if (*sep_char == ':') | |
677 | { | |
678 | fprintf (stderr, "%s: Colon not allowed as domain\\user separator " | |
92b499ac | 679 | "character.\n", program_invocation_short_name); |
a1e19903 CV |
680 | return 1; |
681 | } | |
6510edf4 | 682 | break; |
0bdab5c8 | 683 | case 'U': |
6510edf4 | 684 | print_unix = optarg; |
0bdab5c8 | 685 | break; |
a1e19903 | 686 | case 'c': |
a1e19903 CV |
687 | case 'C': |
688 | print_current = 1; | |
689 | break; | |
690 | case 'o': | |
691 | id_offset = strtol (optarg, NULL, 10); | |
692 | break; | |
f9519bcd | 693 | case 'b': |
6510edf4 | 694 | print_builtin = 0; |
f9519bcd | 695 | break; |
a1e19903 CV |
696 | case 's': |
697 | break; | |
698 | case 'u': | |
699 | break; | |
700 | case 'g': | |
701 | disp_groupname = optarg; | |
a1e19903 CV |
702 | break; |
703 | case 'h': | |
704 | usage (stdout); | |
92b499ac | 705 | case 'V': |
a1e19903 CV |
706 | print_version (); |
707 | return 0; | |
708 | default: | |
92b499ac | 709 | fprintf (stderr, "Try `%s --help' for more information.\n", argv[0]); |
a1e19903 CV |
710 | return 1; |
711 | } | |
1fd5e000 | 712 | |
01dd3162 | 713 | optind += optional_args; |
6510edf4 CF |
714 | if (argv[optind]) |
715 | { | |
716 | fprintf (stderr, | |
717 | "mkgroup: non-option command line argument `%s' is not allowed.\n" | |
718 | "Try `mkgroup --help' for more information.\n", argv[optind]); | |
719 | exit (1); | |
720 | } | |
721 | ||
16a976cf CV |
722 | struct group *pgrp = NULL; |
723 | if (print_current) | |
724 | pgrp = (struct group *) cygwin_internal (CW_GETGRSID, TRUE, curr_pgrp.psid); | |
725 | ||
726 | int enums = ENUM_NONE; | |
727 | WCHAR tdoms[print_domlist * 258]; | |
728 | PWCHAR t = tdoms; | |
729 | if (!disp_groupname && print_builtin && print_domlist) | |
730 | enums |= ENUM_BUILTIN; | |
731 | for (i = 0; i < print_domlist; ++i) | |
036fb477 | 732 | { |
16a976cf CV |
733 | if (domlist[i].domain) |
734 | { | |
735 | if (domlist[i].str) | |
736 | { | |
737 | enums |= ENUM_TDOMS; | |
738 | t += mbstowcs (t, domlist[i].str, 257); | |
739 | *t++ = L'\0'; | |
740 | } | |
741 | else | |
742 | enums |= ENUM_PRIMARY; | |
743 | } | |
744 | else if (!domlist[i].str) | |
745 | enums |= ENUM_LOCAL; | |
036fb477 | 746 | } |
16a976cf CV |
747 | if (t > tdoms) |
748 | *t++ = L'\0'; | |
749 | if (enums) | |
750 | { | |
751 | uintptr_t ticket = cygwin_internal (CW_SETENT, TRUE, enums, | |
752 | t > tdoms ? tdoms : NULL); | |
753 | if (ticket) | |
754 | { | |
755 | struct group *grp; | |
756 | const char *nss_sep = (const char *) cygwin_internal (CW_GETNSSSEP); | |
757 | ||
758 | while ((grp = (struct group *) | |
759 | cygwin_internal (CW_GETENT, TRUE, ticket))) | |
760 | { | |
761 | if (disp_groupname | |
762 | && strcasecmp (disp_groupname, grp->gr_name) != 0 | |
763 | && (!(p = strchr (grp->gr_name, nss_sep[0])) | |
764 | || strcasecmp (disp_groupname, p + 1) != 0)) | |
765 | continue; | |
766 | printf ("%s:%s:%u:\n", grp->gr_name, grp->gr_passwd, | |
767 | grp->gr_gid); | |
768 | if (pgrp && !strcmp (grp->gr_passwd, pgrp->gr_passwd)) | |
769 | got_curr_pgrp = TRUE; | |
770 | } | |
771 | cygwin_internal (CW_ENDENT, TRUE, ticket); | |
772 | } | |
773 | } | |
774 | ||
775 | if (print_current && !got_curr_pgrp) | |
776 | printf ("%s:%s:%u:\n", pgrp->gr_name, pgrp->gr_passwd, pgrp->gr_gid); | |
1fd5e000 | 777 | |
16a976cf | 778 | off = 0xfd000000; |
f9519bcd | 779 | for (i = 0; i < print_domlist; ++i) |
a1e19903 | 780 | { |
16a976cf CV |
781 | if (domlist[i].domain || !domlist[i].str) |
782 | continue; | |
3bde24b8 CV |
783 | if (!enum_local_groups (domlist + i, sep_char, |
784 | (nss_src == NSS_SRC_FILES) ? 0x30000 : off, | |
785 | disp_groupname, print_builtin, print_current)) | |
4aeb44ee | 786 | { |
f3939c05 CV |
787 | enum_groups (domlist + i, sep_char, |
788 | (nss_src == NSS_SRC_FILES) ? 0x30000 : off, | |
789 | disp_groupname, print_current); | |
f9519bcd | 790 | if (!domlist[i].domain && domlist[i].str && print_unix) |
16a976cf CV |
791 | enum_unix_groups (domlist + i, sep_char, 0xff000000, print_unix); |
792 | off += id_offset; | |
4aeb44ee | 793 | } |
a1e19903 CV |
794 | } |
795 | ||
1fd5e000 CF |
796 | return 0; |
797 | } |