OSDN Git Service

19b679ee115f85c1fe4e5a1417ef2cf8f91a9739
[pf3gnuchains/pf3gnuchains4x.git] / winsup / utils / mkgroup.c
1 /* mkgroup.c:
2
3    Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
4    2007, 2008, 2009 Red Hat, Inc.
5
6    This file is part of Cygwin.
7
8    This software is a copyrighted work licensed under the terms of the
9    Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10    details. */
11
12 #define _WIN32_WINNT 0x0600
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include <wchar.h>
16 #include <wctype.h>
17 #include <locale.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <getopt.h>
21 #include <io.h>
22 #include <sys/fcntl.h>
23 #include <sys/cygwin.h>
24 #include <windows.h>
25 #include <lm.h>
26 #include <wininet.h>
27 #include <iptypes.h>
28 #include <ntsecapi.h>
29 #include <dsgetdc.h>
30 #include <ntdef.h>
31
32 #define print_win_error(x) _print_win_error(x, __LINE__)
33
34 #define MAX_SID_LEN 40
35
36 static const char version[] = "$Revision$";
37
38 extern char *__progname __declspec(dllimport);
39
40 SID_IDENTIFIER_AUTHORITY sid_world_auth = {SECURITY_WORLD_SID_AUTHORITY};
41 SID_IDENTIFIER_AUTHORITY sid_nt_auth = {SECURITY_NT_AUTHORITY};
42
43 NET_API_STATUS WINAPI (*dsgetdcname)(LPWSTR,LPWSTR,GUID*,LPWSTR,ULONG,PDOMAIN_CONTROLLER_INFOW*);
44
45 #ifndef min
46 #define min(a,b) (((a)<(b))?(a):(b))
47 #endif
48
49 typedef struct
50 {
51   char *str;
52   DWORD id_offset;
53   BOOL domain;
54   BOOL with_dom;
55 } domlist_t;
56
57 static void
58 _print_win_error (DWORD code, int line)
59 {
60   char buf[4096];
61
62   if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
63       | FORMAT_MESSAGE_IGNORE_INSERTS,
64       NULL,
65       code,
66       MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
67       (LPTSTR) buf, sizeof (buf), NULL))
68     fprintf (stderr, "mkgroup (%d): [%lu] %s", line, code, buf);
69   else
70     fprintf (stderr, "mkgroup (%d): error %lu", line, code);
71 }
72
73 static void
74 load_dsgetdcname ()
75 {
76   HANDLE h = LoadLibrary ("netapi32.dll");
77   if (h)
78     dsgetdcname = (void *) GetProcAddress (h, "DsGetDcNameW");
79 }
80
81 static PWCHAR
82 get_dcname (char *domain)
83 {
84   static WCHAR server[INTERNET_MAX_HOST_NAME_LENGTH + 1];
85   DWORD rc;
86   PWCHAR servername;
87   WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1];
88   PDOMAIN_CONTROLLER_INFOW pdci = NULL;
89
90   if (dsgetdcname)
91     {
92       if (domain)
93         {
94           mbstowcs (domain_name, domain, strlen (domain) + 1);
95           rc = dsgetdcname (NULL, domain_name, NULL, NULL, 0, &pdci);
96         }
97       else
98         rc = dsgetdcname (NULL, NULL, NULL, NULL, 0, &pdci);
99       if (rc != ERROR_SUCCESS)
100         {
101           print_win_error (rc);
102           return (PWCHAR) -1;
103         }
104       wcscpy (server, pdci->DomainControllerName);
105       NetApiBufferFree (pdci);
106     }
107   else
108     {
109       rc = NetGetDCName (NULL, NULL, (void *) &servername);
110       if (rc == ERROR_SUCCESS && domain)
111         {
112           LPWSTR server = servername;
113           mbstowcs (domain_name, domain, strlen (domain) + 1);
114           rc = NetGetDCName (server, domain_name, (void *) &servername);
115           NetApiBufferFree (server);
116         }
117       if (rc != ERROR_SUCCESS)
118         {
119           print_win_error(rc);
120           return (PWCHAR) -1;
121         }
122       wcscpy (server, servername);
123       NetApiBufferFree ((PVOID) servername);
124     }
125   return server;
126 }
127
128 static char *
129 put_sid (PSID psid)
130 {
131   static char s[512];
132   char t[32];
133   DWORD i;
134
135   strcpy (s, "S-1-");
136   sprintf(t, "%u", GetSidIdentifierAuthority (psid)->Value[5]);
137   strcat (s, t);
138   for (i = 0; i < *GetSidSubAuthorityCount (psid); ++i)
139     {
140       sprintf(t, "-%lu", *GetSidSubAuthority (psid, i));
141       strcat (s, t);
142     }
143   return s;
144 }
145
146 typedef struct {
147   BYTE  Revision;
148   BYTE  SubAuthorityCount;
149   SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
150   DWORD SubAuthority[8];
151 } DBGSID, *PDBGSID;
152
153 #define MAX_BUILTIN_SIDS 100    /* Should be enough for the forseable future. */
154 DBGSID builtin_sid_list[MAX_BUILTIN_SIDS];
155 DWORD builtin_sid_cnt;
156
157 typedef struct {
158   PSID psid;
159   int buffer[10];
160 } sidbuf;
161
162 static sidbuf curr_pgrp;
163 static BOOL got_curr_pgrp = FALSE;
164
165 static void
166 fetch_current_pgrp_sid ()
167 {
168   DWORD len;
169   HANDLE ptok;
170
171   if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &ptok)
172       || !GetTokenInformation (ptok, TokenPrimaryGroup, &curr_pgrp,
173                                sizeof curr_pgrp, &len)
174       || !CloseHandle (ptok))
175     {
176       print_win_error (GetLastError ());
177       return;
178     }
179 }
180
181 static void
182 current_group (const char *sep, DWORD id_offset)
183 {
184   WCHAR grp[GNLEN + 1];
185   WCHAR dom[MAX_DOMAIN_NAME_LEN + 1];
186   DWORD glen = GNLEN + 1;
187   DWORD dlen = MAX_DOMAIN_NAME_LEN + 1;
188   int gid;
189   SID_NAME_USE acc_type;
190
191   if (!curr_pgrp.psid
192       || !LookupAccountSidW (NULL, curr_pgrp.psid, grp, &glen, dom, &dlen,
193                              &acc_type))
194     {
195       print_win_error (GetLastError ());
196       return;
197     }
198   gid = *GetSidSubAuthority (curr_pgrp.psid,
199                              *GetSidSubAuthorityCount(curr_pgrp.psid) - 1);
200   printf ("%ls%s%ls:%s:%lu:\n",
201           sep ? dom : L"",
202           sep ?: "",
203           grp,
204           put_sid (curr_pgrp.psid),
205           id_offset + gid);
206 }
207
208 static void
209 enum_unix_groups (domlist_t *dom_or_machine, const char *sep, DWORD id_offset,
210                   char *unix_grp_list)
211 {
212   WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1];
213   PWCHAR servername = NULL;
214   char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL;
215   BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE;
216   SID_IDENTIFIER_AUTHORITY auth = { { 0, 0, 0, 0, 0, 22 } };
217   char *gstr, *grp_list;
218   WCHAR grp[GNLEN + sizeof ("Unix Group\\") + 1];
219   WCHAR dom[MAX_DOMAIN_NAME_LEN + 1];
220   DWORD glen, dlen, sidlen;
221   PSID psid;
222   char psid_buffer[MAX_SID_LEN];
223   SID_NAME_USE acc_type;
224
225   if (!d_or_m)
226     return;
227
228   int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1);
229   if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1)
230     {
231       fprintf (stderr, "%s: Invalid machine name '%s'.  Skipping...\n",
232                __progname, d_or_m);
233       return;
234     }
235   servername = machine;
236
237   if (!AllocateAndInitializeSid (&auth, 2, 2, 0, 0, 0, 0, 0, 0, 0, &psid))
238     return;
239
240   if (!(grp_list = strdup (unix_grp_list)))
241     {
242       FreeSid (psid);
243       return;
244     }
245
246   for (gstr = strtok (grp_list, ","); gstr; gstr = strtok (NULL, ","))
247     {
248       if (!isdigit ((unsigned char) gstr[0]) && gstr[0] != '-')
249         {
250           PWCHAR p = wcpcpy (grp, L"Unix Group\\");
251           ret = mbstowcs (p, gstr, GNLEN + 1);
252           if (ret < 1 || ret >= GNLEN + 1)
253             fprintf (stderr, "%s: Invalid group name '%s'.  Skipping...\n",
254                      __progname, gstr);
255           else if (LookupAccountNameW (servername, grp,
256                                        psid = (PSID) psid_buffer,
257                                        (sidlen = MAX_SID_LEN, &sidlen),
258                                        dom,
259                                        (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen),
260                                        &acc_type))
261             printf ("%s%s%ls:%s:%lu:\n",
262                     with_dom ? "Unix Group" : "",
263                     with_dom ? sep : "",
264                     p,
265                     put_sid (psid),
266                     id_offset +
267                     *GetSidSubAuthority (psid,
268                                          *GetSidSubAuthorityCount(psid) - 1));
269         }
270       else
271         {
272           DWORD start, stop;
273           char *p = gstr;
274           if (*p == '-')
275             start = 0;
276           else
277             start = strtol (p, &p, 10);
278           if (!*p)
279             stop = start;
280           else if (*p++ != '-' || !isdigit ((unsigned char) *p)
281                    || (stop = strtol (p, &p, 10)) < start || *p)
282             {
283               fprintf (stderr, "%s: Malformed unix group list entry '%s'.  "
284                                "Skipping...\n", __progname, gstr);
285               continue;
286             }
287           for (; start <= stop; ++ start)
288             {
289               *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1)
290               = start;
291               if (LookupAccountSidW (servername, psid,
292                                      grp, (glen = GNLEN + 1, &glen),
293                                      dom,
294                                      (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen),
295                                      &acc_type)
296                   && !iswdigit (grp[0]))
297                 printf ("%s%s%ls:%s:%lu:\n",
298                         with_dom ? "Unix Group" : "",
299                         with_dom ? sep : "",
300                         grp,
301                         put_sid (psid),
302                         id_offset + start);
303             }
304         }
305     }
306
307   free (grp_list);
308   FreeSid (psid);
309 }
310
311 static int
312 enum_local_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep,
313                    DWORD id_offset, char *disp_groupname, int print_builtin,
314                    int print_current)
315 {
316   WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1];
317   PWCHAR servername = NULL;
318   char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL;
319   BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE;
320   LOCALGROUP_INFO_0 *buffer;
321   DWORD entriesread = 0;
322   DWORD totalentries = 0;
323   DWORD resume_handle = 0;
324   WCHAR gname[GNLEN + 1];
325   DWORD rc;
326
327   if (domain)
328     {
329       servername = get_dcname (d_or_m);
330       if (servername == (PWCHAR) -1)
331         return 1;
332     }
333   else if (d_or_m)
334     {
335       int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1);
336       if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1)
337         {
338           fprintf (stderr, "%s: Invalid machine name '%s'.  Skipping...\n",
339                    __progname, d_or_m);
340           return 1;
341         }
342       servername = machine;
343     }
344
345   do
346     {
347       DWORD i;
348
349       if (disp_groupname)
350         {
351           mbstowcs (gname, disp_groupname, GNLEN + 1);
352           rc = NetLocalGroupGetInfo (servername, gname, 0, (void *) &buffer);
353           if (rc == ERROR_SUCCESS)
354             entriesread = 1;
355           /* Allow further searching for the group and avoid annoying
356              error messages just because the group is not a local group or
357              the group hasn't been found. */
358           else if (rc == ERROR_NO_SUCH_ALIAS || rc == NERR_GroupNotFound)
359             return 0;
360         }
361       else
362         rc = NetLocalGroupEnum (servername, 0, (void *) &buffer,
363                                 MAX_PREFERRED_LENGTH, &entriesread,
364                                 &totalentries, &resume_handle);
365       switch (rc)
366         {
367         case ERROR_ACCESS_DENIED:
368           print_win_error (rc);
369           return 1;
370
371         case ERROR_MORE_DATA:
372         case ERROR_SUCCESS:
373           break;
374
375         default:
376           print_win_error (rc);
377           return 1;
378         }
379
380       for (i = 0; i < entriesread; i++)
381         {
382           WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1];
383           DWORD domname_len = MAX_DOMAIN_NAME_LEN + 1;
384           char psid_buffer[MAX_SID_LEN];
385           PSID psid = (PSID) psid_buffer;
386           DWORD sid_length = MAX_SID_LEN;
387           DWORD gid;
388           SID_NAME_USE acc_type;
389           PDBGSID pdsid;
390           BOOL is_builtin = FALSE;
391
392           if (!LookupAccountNameW (servername, buffer[i].lgrpi0_name, psid,
393                                    &sid_length, domain_name, &domname_len,
394                                    &acc_type))
395             {
396               print_win_error (GetLastError ());
397               fprintf (stderr, " (%ls)\n", buffer[i].lgrpi0_name);
398               continue;
399             }
400           else if (acc_type == SidTypeDomain)
401             {
402               WCHAR domname[MAX_DOMAIN_NAME_LEN + GNLEN + 2];
403
404               wcscpy (domname, domain_name);
405               wcscat (domname, L"\\");
406               wcscat (domname, buffer[i].lgrpi0_name);
407               sid_length = MAX_SID_LEN;
408               domname_len = MAX_DOMAIN_NAME_LEN + 1;
409               if (!LookupAccountNameW (servername, domname,
410                                        psid, &sid_length,
411                                        domain_name, &domname_len,
412                                        &acc_type))
413                 {
414                   print_win_error (GetLastError ());
415                   fprintf(stderr, " (%ls)\n", domname);
416                   continue;
417                 }
418             }
419
420           /* Store all local SIDs with prefix "S-1-5-32-" and check if it
421              has been printed already.  This allows to get all builtin
422              groups exactly once and not once per domain. */
423           pdsid = (PDBGSID) psid;
424           if (pdsid->IdentifierAuthority.Value[5] == sid_nt_auth.Value[5]
425               && pdsid->SubAuthority[0] == SECURITY_BUILTIN_DOMAIN_RID)
426             {
427               int b;
428
429               if (!print_builtin)
430                 goto skip_group;
431               is_builtin = TRUE;
432               if (builtin_sid_cnt)
433                 for (b = 0; b < builtin_sid_cnt; b++)
434                   if (EqualSid (&builtin_sid_list[b], psid))
435                     goto skip_group;
436               if (builtin_sid_cnt < MAX_BUILTIN_SIDS)
437                 CopySid (sizeof (DBGSID), &builtin_sid_list[builtin_sid_cnt++],
438                          psid);
439             }
440           if (!print_current)
441             /* fall through */;
442           else if (EqualSid (curr_pgrp.psid, psid))
443             got_curr_pgrp = TRUE;
444
445           gid = *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1);
446           printf ("%ls%s%ls:%s:%ld:\n",
447                   with_dom && !is_builtin ? domain_name : L"",
448                   with_dom && !is_builtin ? sep : "",
449                   buffer[i].lgrpi0_name,
450                   put_sid (psid),
451                   gid + (is_builtin ? 0 : id_offset));
452 skip_group:
453           ;
454         }
455
456       NetApiBufferFree (buffer);
457
458     }
459   while (rc == ERROR_MORE_DATA);
460
461   /* Return 1 if the single group we're looking for has been found here to
462      avoid calling enum_groups for the same group, thus avoiding a spurious
463      error message "group name could not be found" in enum_groups. */
464   return disp_groupname && entriesread ? 1 : 0;
465 }
466
467 static void
468 enum_groups (BOOL domain, domlist_t *dom_or_machine, const char *sep,
469              DWORD id_offset, char *disp_groupname, int print_current)
470 {
471   WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1];
472   PWCHAR servername = NULL;
473   char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL;
474   BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE;
475   GROUP_INFO_2 *buffer;
476   DWORD entriesread = 0;
477   DWORD totalentries = 0;
478   DWORD resume_handle = 0;
479   WCHAR gname[GNLEN + 1];
480   DWORD rc;
481
482   if (domain)
483     {
484       servername = get_dcname (d_or_m);
485       if (servername == (PWCHAR) -1)
486         return;
487     }
488   else if (d_or_m)
489     {
490       int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1);
491       if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1)
492         {
493           fprintf (stderr, "%s: Invalid machine name '%s'.  Skipping...\n",
494                    __progname, d_or_m);
495           return;
496         }
497       servername = machine;
498     }
499
500   do
501     {
502       DWORD i;
503
504       if (disp_groupname != NULL)
505         {
506           mbstowcs (gname, disp_groupname, GNLEN + 1);
507           rc = NetGroupGetInfo (servername, (LPWSTR) & gname, 2,
508                                 (void *) &buffer);
509           entriesread=1;
510           /* Avoid annoying error messages just because the group hasn't been
511              found. */
512           if (rc == NERR_GroupNotFound)
513             return;
514         }
515       else
516         rc = NetGroupEnum (servername, 2, (void *) & buffer,
517                            MAX_PREFERRED_LENGTH, &entriesread, &totalentries,
518                            &resume_handle);
519       switch (rc)
520         {
521         case ERROR_ACCESS_DENIED:
522           print_win_error (rc);
523           return;
524
525         case ERROR_MORE_DATA:
526         case ERROR_SUCCESS:
527           break;
528
529         default:
530           print_win_error (rc);
531           return;
532         }
533
534       for (i = 0; i < entriesread; i++)
535         {
536           WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1];
537           DWORD domname_len = MAX_DOMAIN_NAME_LEN + 1;
538           char psid_buffer[MAX_SID_LEN];
539           PSID psid = (PSID) psid_buffer;
540           DWORD sid_length = MAX_SID_LEN;
541           SID_NAME_USE acc_type;
542
543           int gid = buffer[i].grpi2_group_id;
544           if (!LookupAccountNameW (servername, buffer[i].grpi2_name,
545                                    psid, &sid_length,
546                                    domain_name, &domname_len,
547                                    &acc_type))
548             {
549               print_win_error (GetLastError ());
550               fprintf(stderr, " (%ls)\n", buffer[i].grpi2_name);
551               continue;
552             }
553           else if (acc_type == SidTypeDomain)
554             {
555               WCHAR domname[MAX_DOMAIN_NAME_LEN + GNLEN + 2];
556
557               wcscpy (domname, domain || !servername
558                                ? domain_name : servername);
559               wcscat (domname, L"\\");
560               wcscat (domname, buffer[i].grpi2_name);
561               sid_length = MAX_SID_LEN;
562               domname_len = MAX_DOMAIN_NAME_LEN + 1;
563               if (!LookupAccountNameW (servername, domname,
564                                        psid, &sid_length,
565                                        domain_name, &domname_len,
566                                        &acc_type))
567                 {
568                   print_win_error (GetLastError ());
569                   fprintf(stderr, " (%ls)\n", domname);
570                   continue;
571                 }
572             }
573           if (!print_current)
574             /* fall through */;
575           else if (EqualSid (curr_pgrp.psid, psid))
576             got_curr_pgrp = TRUE;
577
578           printf ("%ls%s%ls:%s:%lu:\n",
579                   with_dom ? domain_name : L"",
580                   with_dom ? sep : "",
581                   buffer[i].grpi2_name,
582                   put_sid (psid),
583                   id_offset + gid);
584         }
585
586       NetApiBufferFree (buffer);
587
588     }
589   while (rc == ERROR_MORE_DATA);
590 }
591
592 static void
593 print_special (PSID_IDENTIFIER_AUTHORITY auth, BYTE cnt,
594                DWORD sub1, DWORD sub2, DWORD sub3, DWORD sub4,
595                DWORD sub5, DWORD sub6, DWORD sub7, DWORD sub8)
596 {
597   WCHAR grp[GNLEN + 1], dom[MAX_DOMAIN_NAME_LEN + 1];
598   DWORD glen, dlen, rid;
599   PSID psid;
600   SID_NAME_USE acc_type;
601
602   if (AllocateAndInitializeSid (auth, cnt, sub1, sub2, sub3, sub4,
603                                 sub5, sub6, sub7, sub8, &psid))
604     {
605       if (LookupAccountSidW (NULL, psid,
606                             grp, (glen = GNLEN + 1, &glen),
607                             dom, (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen),
608                             &acc_type))
609         {
610           if (sub8)
611             rid = sub8;
612           else if (sub7)
613             rid = sub7;
614           else if (sub6)
615             rid = sub6;
616           else if (sub5)
617             rid = sub5;
618           else if (sub4)
619             rid = sub4;
620           else if (sub3)
621             rid = sub3;
622           else if (sub2)
623             rid = sub2;
624           else
625             rid = sub1;
626           printf ("%ls:%s:%lu:\n", grp, put_sid (psid), rid);
627         }
628       FreeSid (psid);
629     }
630 }
631
632 static int
633 usage (FILE * stream)
634 {
635   fprintf (stream,
636 "Usage: mkgroup [OPTION]...\n"
637 "Print /etc/group file to stdout\n"
638 "\n"
639 "Options:\n"
640 "   -l,--local [machine[,offset]]\n"
641 "                           print local groups with gid offset offset\n"
642 "                           (from local machine if no machine specified)\n"
643 "   -L,--Local [machine[,offset]]\n"
644 "                           ditto, but generate groupname with machine prefix\n"
645 "   -d,--domain [domain[,offset]]\n"
646 "                           print domain groups with gid offset offset\n"
647 "                           (from current domain if no domain specified)\n"
648 "   -D,--Domain [domain[,offset]]\n"
649 "                           ditto, but generate groupname with machine prefix\n"
650 "   -c,--current            print current group\n"
651 "   -C,--Current            ditto, but generate groupname with machine or\n"
652 "                           domain prefix\n"
653 "   -S,--separator char     for -L, -D, -C use character char as domain\\group\n"
654 "                           separator in groupname instead of the default '\\'\n"
655 "   -o,--id-offset offset   change the default offset (10000) added to gids\n"
656 "                           in domain or foreign server accounts.\n"
657 "   -g,--group groupname    only return information for the specified group\n"
658 "                           one of -l, -L, -d, -D must be specified, too\n"
659 "   -b,--no-builtin         don't print BUILTIN groups\n"
660 "   -U,--unix grouplist     additionally print UNIX groups when using -l or -L\n"
661 "                           on a UNIX Samba server\n"
662 "                           grouplist is a comma-separated list of groupnames\n"
663 "                           or gid ranges (root,-25,50-100).\n"
664 "                           (enumerating large ranges can take a long time!)\n"
665 "   -s,--no-sids            (ignored)\n"
666 "   -u,--users              (ignored)\n"
667 "   -h,--help               print this message\n"
668 "   -v,--version            print version information and exit\n"
669 "\n"
670 "Default is to print local groups on stand-alone machines, plus domain\n"
671 "groups on domain controllers and domain member machines.\n");
672   return 1;
673 }
674
675 struct option longopts[] = {
676   {"no-builtin", no_argument, NULL, 'b'},
677   {"current", no_argument, NULL, 'c'},
678   {"Current", no_argument, NULL, 'C'},
679   {"domain", optional_argument, NULL, 'd'},
680   {"Domain", optional_argument, NULL, 'D'},
681   {"group", required_argument, NULL, 'g'},
682   {"help", no_argument, NULL, 'h'},
683   {"local", optional_argument, NULL, 'l'},
684   {"Local", optional_argument, NULL, 'L'},
685   {"id-offset", required_argument, NULL, 'o'},
686   {"no-sids", no_argument, NULL, 's'},
687   {"separator", required_argument, NULL, 'S'},
688   {"users", no_argument, NULL, 'u'},
689   {"unix", required_argument, NULL, 'U'},
690   {"version", no_argument, NULL, 'v'},
691   {0, no_argument, NULL, 0}
692 };
693
694 static char opts[] = "bcCd::D::g:hl::L::o:sS:uU:v";
695
696 static void
697 print_version ()
698 {
699   const char *v = strchr (version, ':');
700   int len;
701   if (!v)
702     {
703       v = "?";
704       len = 1;
705     }
706   else
707     {
708       v += 2;
709       len = strchr (v, ' ') - v;
710     }
711   printf ("\
712 mkgroup (cygwin) %.*s\n\
713 group File Generator\n\
714 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.\n\
715 Compiled on %s\n\
716 ", len, v, __DATE__);
717 }
718
719 static PPOLICY_PRIMARY_DOMAIN_INFO p_dom;
720
721 static BOOL
722 fetch_primary_domain ()
723 {
724   NTSTATUS status;
725   LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 };
726   LSA_HANDLE lsa;
727
728   if (!p_dom)
729     {
730       status = LsaOpenPolicy (NULL, &oa, POLICY_EXECUTE, &lsa);
731       if (!NT_SUCCESS (status))
732         return FALSE;
733       status = LsaQueryInformationPolicy (lsa, PolicyPrimaryDomainInformation,
734                                           (PVOID *) ((void *) &p_dom));
735       LsaClose (lsa);
736       if (!NT_SUCCESS (status))
737         return FALSE;
738     }
739   return !!p_dom->Sid;
740 }
741
742 int
743 main (int argc, char **argv)
744 {
745   int print_domlist = 0;
746   domlist_t domlist[32];
747   char *opt, *p, *ep;
748   int print_current = 0;
749   int print_system = 0;
750   int print_builtin = 1;
751   char *print_unix = NULL;
752   const char *sep_char = "\\";
753   DWORD id_offset = 10000, off;
754   int c, i;
755   char *disp_groupname = NULL;
756   BOOL in_domain;
757   int optional_args = 0;
758
759   if (!isatty (1))
760     setmode (1, O_BINARY);
761
762   /* Use locale from environment.  If not set or set to "C", use UTF-8. */
763   setlocale (LC_CTYPE, "");
764   if (!strcmp (setlocale (LC_CTYPE, NULL), "C"))
765     setlocale (LC_CTYPE, "en_US.UTF-8");
766   load_dsgetdcname ();
767   in_domain = fetch_primary_domain ();
768   fetch_current_pgrp_sid ();
769
770   if (argc == 1)
771     {
772       print_special (&sid_nt_auth, 1, SECURITY_LOCAL_SYSTEM_RID,
773                      0, 0, 0, 0, 0, 0, 0);
774       if (in_domain)
775         {
776           if (!enum_local_groups (TRUE, NULL, sep_char, id_offset,
777                                   disp_groupname, print_builtin, 0))
778             enum_groups (TRUE, NULL, sep_char, id_offset, disp_groupname, 0);
779         }
780       else if (!enum_local_groups (FALSE, NULL, sep_char, 0, disp_groupname,
781                                    print_builtin, 0))
782         enum_groups (FALSE, NULL, sep_char, 0, disp_groupname, 0);
783       return 0;
784     }
785
786   unsetenv ("POSIXLY_CORRECT"); /* To get optional arg processing right. */
787   while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
788     switch (c)
789       {
790       case 'd':
791       case 'D':
792       case 'l':
793       case 'L':
794         if (print_domlist >= 32)
795           {
796             fprintf (stderr, "%s: Can not enumerate from more than 32 "
797                              "domains and machines.\n", __progname);
798             return 1;
799           }
800         domlist[print_domlist].domain = (c == 'd' || c == 'D');
801         opt = optarg ?:
802               argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL;
803         if (argv[optind] && opt == argv[optind])
804           ++optional_args;
805         for (i = 0; i < print_domlist; ++i)
806           if (domlist[i].domain == domlist[print_domlist].domain
807               && ((!domlist[i].str && !opt)
808                   || (domlist[i].str && opt
809                       && (off = strlen (domlist[i].str))
810                       && !strncmp (domlist[i].str, opt, off)
811                       && (!opt[off] || opt[off] == ','))))
812             {
813               fprintf (stderr, "%s: Duplicate %s '%s'.  Skipping...\n",
814                        __progname, domlist[i].domain ? "domain" : "machine",
815                        domlist[i].str);
816               goto skip;
817             }
818         if (!(domlist[print_domlist].str = opt))
819           print_system = 1;
820         domlist[print_domlist].id_offset = ULONG_MAX;
821         if (opt && (p = strchr (opt, ',')))
822           {
823             if (p == opt
824                 || !isdigit ((unsigned char) p[1])
825                 || (domlist[print_domlist].id_offset = strtol (p + 1, &ep, 10)
826                     , *ep))
827               {
828                 fprintf (stderr, "%s: Malformed machine,offset string '%s'.  "
829                          "Skipping...\n", __progname, opt);
830                 break;
831               }
832             *p = '\0';
833           }
834         domlist[print_domlist++].with_dom = (c == 'D' || c == 'L');
835 skip:
836         break;
837       case 'S':
838         sep_char = optarg;
839         if (strlen (sep_char) > 1)
840           {
841             fprintf (stderr, "%s: Only one character allowed as domain\\user "
842                              "separator character.\n", __progname);
843             return 1;
844           }
845         if (*sep_char == ':')
846           {
847             fprintf (stderr, "%s: Colon not allowed as domain\\user separator "
848                              "character.\n", __progname);
849             return 1;
850           }
851         break;
852       case 'U':
853         print_unix = optarg;
854         break;
855       case 'c':
856         sep_char = NULL;
857         /*FALLTHRU*/
858       case 'C':
859         print_current = 1;
860         break;
861       case 'o':
862         id_offset = strtol (optarg, NULL, 10);
863         break;
864       case 'b':
865         print_builtin = 0;
866         break;
867       case 's':
868         break;
869       case 'u':
870         break;
871       case 'g':
872         disp_groupname = optarg;
873         break;
874       case 'h':
875         usage (stdout);
876         return 0;
877       case 'v':
878         print_version ();
879         return 0;
880       default:
881         fprintf (stderr, "Try '%s --help' for more information.\n", argv[0]);
882         return 1;
883       }
884
885   optind += optional_args;
886   if (argv[optind])
887     {
888       fprintf (stderr,
889                "mkgroup: non-option command line argument `%s' is not allowed.\n"
890                "Try `mkgroup --help' for more information.\n", argv[optind]);
891       exit (1);
892     }
893
894   /* Get 'system' group */
895   if (!disp_groupname && print_system && print_builtin && print_domlist)
896     print_special (&sid_nt_auth, 1, SECURITY_LOCAL_SYSTEM_RID,
897                    0, 0, 0, 0, 0, 0, 0);
898
899   off = id_offset;
900   for (i = 0; i < print_domlist; ++i)
901     {
902       DWORD my_off = (domlist[i].domain || domlist[i].str)
903                      ? domlist[i].id_offset != ULONG_MAX
904                        ? domlist[i].id_offset : off : 0;
905       if (!enum_local_groups (domlist[i].domain, domlist + i, sep_char,
906                               my_off, disp_groupname, print_builtin, print_current))
907         {
908           if (!domlist[i].domain && domlist[i].str && print_unix)
909             enum_unix_groups (domlist + i, sep_char, my_off, print_unix);
910           enum_groups (domlist[i].domain, domlist + i, sep_char, my_off,
911                        disp_groupname, print_current);
912           if (my_off)
913             off += id_offset;
914         }
915     }
916
917   if (print_current && !got_curr_pgrp)
918     current_group (sep_char, off);
919
920   return 0;
921 }