3 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
4 2007, 2008 Red Hat, Inc.
6 This file is part of Cygwin.
8 This software is a copyrighted work licensed under the terms of the
9 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
12 #define _WIN32_WINNT 0x0600
21 #include <sys/fcntl.h>
22 #include <sys/cygwin.h>
31 #define print_win_error(x) _print_win_error(x, __LINE__)
33 #define MAX_SID_LEN 40
35 static const char version
[] = "$Revision$";
37 extern char *__progname
;
39 SID_IDENTIFIER_AUTHORITY sid_world_auth
= {SECURITY_WORLD_SID_AUTHORITY
};
40 SID_IDENTIFIER_AUTHORITY sid_nt_auth
= {SECURITY_NT_AUTHORITY
};
42 NET_API_STATUS
WINAPI (*dsgetdcname
)(LPWSTR
,LPWSTR
,GUID
*,LPWSTR
,ULONG
,PDOMAIN_CONTROLLER_INFOW
*);
45 #define min(a,b) (((a)<(b))?(a):(b))
55 _print_win_error (DWORD code
, int line
)
59 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
60 | FORMAT_MESSAGE_IGNORE_INSERTS
,
63 MAKELANGID (LANG_NEUTRAL
, SUBLANG_DEFAULT
),
64 (LPTSTR
) buf
, sizeof (buf
), NULL
))
65 fprintf (stderr
, "mkgroup (%d): [%lu] %s", line
, code
, buf
);
67 fprintf (stderr
, "mkgroup (%d): error %lu", line
, code
);
73 HANDLE h
= LoadLibrary ("netapi32.dll");
75 dsgetdcname
= (void *) GetProcAddress (h
, "DsGetDcNameW");
79 get_dcname (char *domain
)
81 static WCHAR server
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
84 WCHAR domain_name
[MAX_DOMAIN_NAME_LEN
+ 1];
85 PDOMAIN_CONTROLLER_INFOW pdci
= NULL
;
91 mbstowcs (domain_name
, domain
, strlen (domain
) + 1);
92 rc
= dsgetdcname (NULL
, domain_name
, NULL
, NULL
, 0, &pdci
);
95 rc
= dsgetdcname (NULL
, NULL
, NULL
, NULL
, 0, &pdci
);
96 if (rc
!= ERROR_SUCCESS
)
101 wcscpy (server
, pdci
->DomainControllerName
);
102 NetApiBufferFree (pdci
);
106 rc
= NetGetDCName (NULL
, NULL
, (void *) &servername
);
107 if (rc
== ERROR_SUCCESS
&& domain
)
109 LPWSTR server
= servername
;
110 mbstowcs (domain_name
, domain
, strlen (domain
) + 1);
111 rc
= NetGetDCName (server
, domain_name
, (void *) &servername
);
112 NetApiBufferFree (server
);
114 if (rc
!= ERROR_SUCCESS
)
119 wcscpy (server
, servername
);
120 NetApiBufferFree ((PVOID
) servername
);
133 sprintf(t
, "%u", GetSidIdentifierAuthority (psid
)->Value
[5]);
135 for (i
= 0; i
< *GetSidSubAuthorityCount (psid
); ++i
)
137 sprintf(t
, "-%lu", *GetSidSubAuthority (psid
, i
));
145 BYTE SubAuthorityCount
;
146 SID_IDENTIFIER_AUTHORITY IdentifierAuthority
;
147 DWORD SubAuthority
[8];
150 #define MAX_BUILTIN_SIDS 100 /* Should be enough for the forseable future. */
151 DBGSID builtin_sid_list
[MAX_BUILTIN_SIDS
];
152 DWORD builtin_sid_cnt
;
155 enum_unix_groups (domlist_t
*dom_or_machine
, const char *sep
, int id_offset
,
158 WCHAR machine
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
159 PWCHAR servername
= NULL
;
160 char *d_or_m
= dom_or_machine
? dom_or_machine
->str
: NULL
;
161 BOOL with_dom
= dom_or_machine
? dom_or_machine
->with_dom
: FALSE
;
162 SID_IDENTIFIER_AUTHORITY auth
= { { 0, 0, 0, 0, 0, 22 } };
163 char *gstr
, *grp_list
;
164 WCHAR grp
[GNLEN
+ sizeof ("Unix Group\\") + 1];
165 WCHAR dom
[MAX_DOMAIN_NAME_LEN
+ 1];
166 DWORD glen
, dlen
, sidlen
;
168 char psid_buffer
[MAX_SID_LEN
];
169 SID_NAME_USE acc_type
;
174 int ret
= mbstowcs (machine
, d_or_m
, INTERNET_MAX_HOST_NAME_LENGTH
+ 1);
175 if (ret
< 1 || ret
>= INTERNET_MAX_HOST_NAME_LENGTH
+ 1)
177 fprintf (stderr
, "%s: Invalid machine name '%s'. Skipping...\n",
181 servername
= machine
;
183 if (!AllocateAndInitializeSid (&auth
, 2, 2, 0, 0, 0, 0, 0, 0, 0, &psid
))
186 if (!(grp_list
= strdup (unix_grp_list
)))
192 for (gstr
= strtok (grp_list
, ","); gstr
; gstr
= strtok (NULL
, ","))
194 if (!isdigit (gstr
[0]) && gstr
[0] != '-')
196 PWCHAR p
= wcpcpy (grp
, L
"Unix Group\\");
197 ret
= mbstowcs (p
, gstr
, GNLEN
+ 1);
198 if (ret
< 1 || ret
>= GNLEN
+ 1)
199 fprintf (stderr
, "%s: Invalid group name '%s'. Skipping...\n",
201 else if (LookupAccountNameW (servername
, grp
,
202 psid
= (PSID
) psid_buffer
,
203 (sidlen
= MAX_SID_LEN
, &sidlen
),
205 (dlen
= MAX_DOMAIN_NAME_LEN
+ 1, &dlen
),
207 printf ("%s%s%ls:%s:%lu:\n",
208 with_dom
? "Unix Group" : "",
213 *GetSidSubAuthority (psid
,
214 *GetSidSubAuthorityCount(psid
) - 1));
223 start
= strtol (p
, &p
, 10);
226 else if (*p
++ != '-' || !isdigit (*p
)
227 || (stop
= strtol (p
, &p
, 10)) < start
|| *p
)
229 fprintf (stderr
, "%s: Malformed unix group list entry '%s'. "
230 "Skipping...\n", __progname
, gstr
);
233 for (; start
<= stop
; ++ start
)
235 *GetSidSubAuthority (psid
, *GetSidSubAuthorityCount(psid
) - 1)
237 if (LookupAccountSidW (servername
, psid
,
238 grp
, (glen
= GNLEN
+ 1, &glen
),
240 (dlen
= MAX_DOMAIN_NAME_LEN
+ 1, &dlen
),
242 && !iswdigit (grp
[0]))
243 printf ("%s%s%ls:%s:%lu:\n",
244 with_dom
? "Unix Group" : "",
258 enum_local_groups (BOOL domain
, domlist_t
*dom_or_machine
, const char *sep
,
259 int id_offset
, char *disp_groupname
)
261 WCHAR machine
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
262 PWCHAR servername
= NULL
;
263 char *d_or_m
= dom_or_machine
? dom_or_machine
->str
: NULL
;
264 BOOL with_dom
= dom_or_machine
? dom_or_machine
->with_dom
: FALSE
;
265 LOCALGROUP_INFO_0
*buffer
;
266 DWORD entriesread
= 0;
267 DWORD totalentries
= 0;
268 DWORD resume_handle
= 0;
269 WCHAR gname
[GNLEN
+ 1];
274 servername
= get_dcname (d_or_m
);
275 if (servername
== (PWCHAR
) -1)
280 int ret
= mbstowcs (machine
, d_or_m
, INTERNET_MAX_HOST_NAME_LENGTH
+ 1);
281 if (ret
< 1 || ret
>= INTERNET_MAX_HOST_NAME_LENGTH
+ 1)
283 fprintf (stderr
, "%s: Invalid machine name '%s'. Skipping...\n",
287 servername
= machine
;
294 if (disp_groupname
!= NULL
)
296 mbstowcs (gname
, disp_groupname
, GNLEN
+ 1);
297 rc
= NetApiBufferAllocate (sizeof (LOCALGROUP_INFO_0
),
299 buffer
[0].lgrpi0_name
= gname
;
303 rc
= NetLocalGroupEnum (servername
, 0, (void *) &buffer
,
304 MAX_PREFERRED_LENGTH
, &entriesread
,
305 &totalentries
, &resume_handle
);
308 case ERROR_ACCESS_DENIED
:
309 print_win_error (rc
);
312 case ERROR_MORE_DATA
:
317 print_win_error (rc
);
321 for (i
= 0; i
< entriesread
; i
++)
323 WCHAR domain_name
[MAX_DOMAIN_NAME_LEN
+ 1];
324 DWORD domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
325 char psid_buffer
[MAX_SID_LEN
];
326 PSID psid
= (PSID
) psid_buffer
;
327 DWORD sid_length
= MAX_SID_LEN
;
329 SID_NAME_USE acc_type
;
331 BOOL is_builtin
= FALSE
;
333 if (!LookupAccountNameW (servername
, buffer
[i
].lgrpi0_name
, psid
,
334 &sid_length
, domain_name
, &domname_len
,
337 print_win_error (rc
);
338 fprintf (stderr
, " (%ls)\n", buffer
[i
].lgrpi0_name
);
341 else if (acc_type
== SidTypeDomain
)
343 WCHAR domname
[MAX_DOMAIN_NAME_LEN
+ GNLEN
+ 2];
345 wcscpy (domname
, domain_name
);
346 wcscat (domname
, L
"\\");
347 wcscat (domname
, buffer
[i
].lgrpi0_name
);
348 sid_length
= MAX_SID_LEN
;
349 domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
350 if (!LookupAccountNameW (servername
, domname
,
352 domain_name
, &domname_len
,
355 print_win_error (rc
);
356 fprintf(stderr
, " (%ls)\n", domname
);
361 /* Store all local SIDs with prefix "S-1-5-32-" and check if it
362 has been printed already. This allows to get all builtin
363 groups exactly once and not once per domain. */
364 pdsid
= (PDBGSID
) psid
;
365 if (pdsid
->IdentifierAuthority
.Value
[5] == sid_nt_auth
.Value
[5]
366 && pdsid
->SubAuthority
[0] == SECURITY_BUILTIN_DOMAIN_RID
)
371 if (servername
&& builtin_sid_cnt
)
372 for (b
= 0; b
< builtin_sid_cnt
; b
++)
373 if (EqualSid (&builtin_sid_list
[b
], psid
))
375 if (builtin_sid_cnt
< MAX_BUILTIN_SIDS
)
376 CopySid (sizeof (DBGSID
), &builtin_sid_list
[builtin_sid_cnt
++],
380 gid
= *GetSidSubAuthority (psid
, *GetSidSubAuthorityCount(psid
) - 1);
382 printf ("%ls%s%ls:%s:%ld:\n",
383 with_dom
? domain_name
: L
"",
385 buffer
[i
].lgrpi0_name
,
387 gid
+ (is_builtin
? 0 : id_offset
));
392 NetApiBufferFree (buffer
);
395 while (rc
== ERROR_MORE_DATA
);
401 enum_groups (BOOL domain
, domlist_t
*dom_or_machine
, const char *sep
,
402 int id_offset
, char *disp_groupname
)
404 WCHAR machine
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
405 PWCHAR servername
= NULL
;
406 char *d_or_m
= dom_or_machine
? dom_or_machine
->str
: NULL
;
407 BOOL with_dom
= dom_or_machine
? dom_or_machine
->with_dom
: FALSE
;
408 GROUP_INFO_2
*buffer
;
409 DWORD entriesread
= 0;
410 DWORD totalentries
= 0;
411 DWORD resume_handle
= 0;
412 WCHAR gname
[GNLEN
+ 1];
417 servername
= get_dcname (d_or_m
);
418 if (servername
== (PWCHAR
) -1)
423 int ret
= mbstowcs (machine
, d_or_m
, INTERNET_MAX_HOST_NAME_LENGTH
+ 1);
424 if (ret
< 1 || ret
>= INTERNET_MAX_HOST_NAME_LENGTH
+ 1)
426 fprintf (stderr
, "%s: Invalid machine name '%s'. Skipping...\n",
430 servername
= machine
;
437 if (disp_groupname
!= NULL
)
439 mbstowcs (gname
, disp_groupname
, GNLEN
+ 1);
440 rc
= NetGroupGetInfo (servername
, (LPWSTR
) & gname
, 2,
445 rc
= NetGroupEnum (servername
, 2, (void *) & buffer
,
446 MAX_PREFERRED_LENGTH
, &entriesread
, &totalentries
,
450 case ERROR_ACCESS_DENIED
:
451 print_win_error (rc
);
454 case ERROR_MORE_DATA
:
459 print_win_error (rc
);
463 for (i
= 0; i
< entriesread
; i
++)
465 WCHAR domain_name
[MAX_DOMAIN_NAME_LEN
+ 1];
466 DWORD domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
467 char psid_buffer
[MAX_SID_LEN
];
468 PSID psid
= (PSID
) psid_buffer
;
469 DWORD sid_length
= MAX_SID_LEN
;
470 SID_NAME_USE acc_type
;
472 int gid
= buffer
[i
].grpi2_group_id
;
473 if (!LookupAccountNameW (servername
, buffer
[i
].grpi2_name
,
475 domain_name
, &domname_len
,
478 print_win_error (rc
);
479 fprintf(stderr
, " (%ls)\n", buffer
[i
].grpi2_name
);
482 else if (acc_type
== SidTypeDomain
)
484 WCHAR domname
[MAX_DOMAIN_NAME_LEN
+ GNLEN
+ 2];
486 wcscpy (domname
, domain
|| !servername
487 ? domain_name
: servername
);
488 wcscat (domname
, L
"\\");
489 wcscat (domname
, buffer
[i
].grpi2_name
);
490 sid_length
= MAX_SID_LEN
;
491 domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
492 if (!LookupAccountNameW (servername
, domname
,
494 domain_name
, &domname_len
,
497 print_win_error (rc
);
498 fprintf(stderr
, " (%ls)\n", domname
);
502 printf ("%ls%s%ls:%s:%u:\n",
503 with_dom
? domain_name
: L
"",
505 buffer
[i
].grpi2_name
,
510 NetApiBufferFree (buffer
);
513 while (rc
== ERROR_MORE_DATA
);
517 print_special (PSID_IDENTIFIER_AUTHORITY auth
, BYTE cnt
,
518 DWORD sub1
, DWORD sub2
, DWORD sub3
, DWORD sub4
,
519 DWORD sub5
, DWORD sub6
, DWORD sub7
, DWORD sub8
)
521 WCHAR grp
[GNLEN
+ 1], dom
[MAX_DOMAIN_NAME_LEN
+ 1];
522 DWORD glen
, dlen
, rid
;
524 SID_NAME_USE acc_type
;
526 if (AllocateAndInitializeSid (auth
, cnt
, sub1
, sub2
, sub3
, sub4
,
527 sub5
, sub6
, sub7
, sub8
, &psid
))
529 if (LookupAccountSidW (NULL
, psid
,
530 grp
, (glen
= GNLEN
+ 1, &glen
),
531 dom
, (dlen
= MAX_DOMAIN_NAME_LEN
+ 1, &dlen
),
550 printf ("%ls:%s:%lu:\n", grp
, put_sid (psid
), rid
);
557 current_group (const char *sep
, int id_offset
)
563 char buffer
[MAX_SID_LEN
];
565 WCHAR grp
[GNLEN
+ 1];
566 WCHAR dom
[MAX_DOMAIN_NAME_LEN
+ 1];
567 DWORD glen
= GNLEN
+ 1;
568 DWORD dlen
= MAX_DOMAIN_NAME_LEN
+ 1;
570 SID_NAME_USE acc_type
;
572 if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY
, &ptok
)
573 || !GetTokenInformation (ptok
, TokenPrimaryGroup
, &tg
, sizeof tg
, &len
)
574 || !CloseHandle (ptok
)
575 || !LookupAccountSidW (NULL
, tg
.psid
, grp
, &glen
, dom
, &dlen
, &acc_type
))
577 print_win_error (GetLastError ());
580 gid
= *GetSidSubAuthority (tg
.psid
, *GetSidSubAuthorityCount(tg
.psid
) - 1);
581 printf ("%ls%s%ls:%s:%u:\n",
590 usage (FILE * stream
)
593 "Usage: mkgroup [OPTION]...\n"
594 "Print /etc/group file to stdout\n"
597 " -l,--local [machine] print local groups (from local machine if no\n"
598 " machine specified)\n"
599 " -L,--Local [machine] ditto, but generate groupname with machine prefix\n"
600 " -d,--domain [domain] print domain groups (from current domain if no\n"
601 " domain specified)\n"
602 " -D,--Domain [domain] ditto, but generate groupname with machine prefix\n"
603 " -c,--current print current group\n"
604 " -C,--Current ditto, but generate groupname with machine or\n"
606 " -S,--separator char for -L, -D, -C use character char as domain\\group\n"
607 " separator in groupname instead of the default '\\'\n"
608 " -o,--id-offset offset change the default offset (10000) added to gids\n"
609 " in domain or foreign server accounts.\n"
610 " -g,--group groupname only return information for the specified group\n"
611 " one of -l, -L, -d, -D must be specified, too\n"
612 " -U,--unix grouplist additionally print UNIX groups when using -l or -L\n"
613 " on a UNIX Samba server\n"
614 " grouplist is a comma-separated list of groupnames\n"
615 " or gid ranges (root,-25,50-100).\n"
616 " (enumerating large ranges can take a long time!)\n"
617 " -s,--no-sids (ignored)\n"
618 " -u,--users (ignored)\n"
619 " -h,--help print this message\n"
620 " -v,--version print version information and exit\n"
622 "Default is to print local groups on stand-alone machines, plus domain\n"
623 "groups on domain controllers and domain member machines.\n");
627 struct option longopts
[] = {
628 {"current", no_argument
, NULL
, 'c'},
629 {"Current", no_argument
, NULL
, 'C'},
630 {"domain", optional_argument
, NULL
, 'd'},
631 {"Domain", optional_argument
, NULL
, 'D'},
632 {"group", required_argument
, NULL
, 'g'},
633 {"help", no_argument
, NULL
, 'h'},
634 {"local", optional_argument
, NULL
, 'l'},
635 {"Local", optional_argument
, NULL
, 'L'},
636 {"id-offset", required_argument
, NULL
, 'o'},
637 {"no-sids", no_argument
, NULL
, 's'},
638 {"separator", required_argument
, NULL
, 'S'},
639 {"users", no_argument
, NULL
, 'u'},
640 {"unix", required_argument
, NULL
, 'U'},
641 {"version", no_argument
, NULL
, 'v'},
642 {0, no_argument
, NULL
, 0}
645 char opts
[] = "cCd::D::g:hl::L::o:sS:uU:v";
650 const char *v
= strchr (version
, ':');
660 len
= strchr (v
, ' ') - v
;
663 mkgroup (cygwin) %.*s\n\
664 group File Generator\n\
665 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.\n\
667 ", len
, v
, __DATE__
);
670 static PPOLICY_PRIMARY_DOMAIN_INFO p_dom
;
673 fetch_primary_domain ()
676 LSA_OBJECT_ATTRIBUTES oa
= { 0, 0, 0, 0, 0, 0 };
681 status
= LsaOpenPolicy (NULL
, &oa
, POLICY_EXECUTE
, &lsa
);
682 if (!NT_SUCCESS (status
))
684 status
= LsaQueryInformationPolicy (lsa
, PolicyPrimaryDomainInformation
,
687 if (!NT_SUCCESS (status
))
694 main (int argc
, char **argv
)
697 domlist_t locals
[16];
698 int print_domain
= 0;
699 domlist_t domains
[16];
701 int print_current
= 0;
702 int print_system
= 0;
703 char *print_unix
= NULL
;
704 const char *sep_char
= "\\";
705 int id_offset
= 10000;
707 char *disp_groupname
= NULL
;
711 setmode (1, O_BINARY
);
714 in_domain
= fetch_primary_domain ();
717 print_special (&sid_nt_auth
, 1, SECURITY_LOCAL_SYSTEM_RID
,
718 0, 0, 0, 0, 0, 0, 0);
721 if (!enum_local_groups (TRUE
, NULL
, sep_char
, id_offset
,
723 enum_groups (TRUE
, NULL
, sep_char
, id_offset
, disp_groupname
);
725 else if (!enum_local_groups (FALSE
, NULL
, sep_char
, 0, disp_groupname
))
726 enum_groups (FALSE
, NULL
, sep_char
, 0, disp_groupname
);
730 while ((c
= getopt_long (argc
, argv
, opts
, longopts
, NULL
)) != EOF
)
735 if (print_local
>= 16)
737 fprintf (stderr
, "%s: Can not enumerate from more than 16 "
738 "servers.\n", __progname
);
742 argv
[optind
] && argv
[optind
][0] != '-' ? argv
[optind
] : NULL
;
743 for (i
= 0; i
< print_local
; ++i
)
744 if ((!locals
[i
].str
&& !opt
)
745 || (locals
[i
].str
&& opt
&& !strcmp (locals
[i
].str
, opt
)))
747 if (!(locals
[print_local
].str
= opt
))
749 locals
[print_local
++].with_dom
= c
== 'L';
754 if (print_domain
>= 16)
756 fprintf (stderr
, "%s: Can not enumerate from more than 16 "
757 "domains.\n", __progname
);
761 argv
[optind
] && argv
[optind
][0] != '-' ? argv
[optind
] : NULL
;
762 for (i
= 0; i
< print_domain
; ++i
)
763 if ((!domains
[i
].str
&& !opt
)
764 || (domains
[i
].str
&& opt
&& !strcmp (domains
[i
].str
, opt
)))
766 if (!(domains
[print_domain
].str
= opt
))
768 domains
[print_domain
++].with_dom
= c
== 'D';
773 if (strlen (sep_char
) > 1)
775 fprintf (stderr
, "%s: Only one character allowed as domain\\user "
776 "separator character.\n", __progname
);
779 if (*sep_char
== ':')
781 fprintf (stderr
, "%s: Colon not allowed as domain\\user separator "
782 "character.\n", __progname
);
796 id_offset
= strtol (optarg
, NULL
, 10);
803 disp_groupname
= optarg
;
812 fprintf (stderr
, "Try '%s --help' for more information.\n", argv
[0]);
816 if (optind
< argc
- 1)
819 /* Get 'system' group */
820 if (!disp_groupname
&& print_system
)
821 print_special (&sid_nt_auth
, 1, SECURITY_LOCAL_SYSTEM_RID
,
822 0, 0, 0, 0, 0, 0, 0);
825 for (i
= 0; i
< print_local
; ++i
)
829 if (!enum_local_groups (FALSE
, locals
+ i
, sep_char
,
830 id_offset
* off
, disp_groupname
))
833 enum_unix_groups (locals
+ i
, sep_char
, id_offset
* off
,
835 enum_groups (FALSE
, locals
+ i
, sep_char
, id_offset
* off
++,
839 else if (!enum_local_groups (FALSE
, locals
+ i
, sep_char
, 0,
841 enum_groups (FALSE
, locals
+ i
, sep_char
, 0, disp_groupname
);
844 for (i
= 0; i
< print_domain
; ++i
)
846 if (!enum_local_groups (TRUE
, domains
+ i
, sep_char
, id_offset
* off
,
848 enum_groups (TRUE
, domains
+ i
, sep_char
, id_offset
* off
++,
853 current_group (sep_char
, id_offset
);