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
20 #include <sys/fcntl.h>
21 #include <sys/cygwin.h>
30 #define print_win_error(x) _print_win_error(x, __LINE__)
32 #define MAX_SID_LEN 40
34 static const char version
[] = "$Revision$";
36 extern char *__progname
;
38 SID_IDENTIFIER_AUTHORITY sid_world_auth
= {SECURITY_WORLD_SID_AUTHORITY
};
39 SID_IDENTIFIER_AUTHORITY sid_nt_auth
= {SECURITY_NT_AUTHORITY
};
41 NET_API_STATUS
WINAPI (*dsgetdcname
)(LPWSTR
,LPWSTR
,GUID
*,LPWSTR
,ULONG
,PDOMAIN_CONTROLLER_INFOW
*);
44 #define min(a,b) (((a)<(b))?(a):(b))
54 _print_win_error (DWORD code
, int line
)
58 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
59 | FORMAT_MESSAGE_IGNORE_INSERTS
,
62 MAKELANGID (LANG_NEUTRAL
, SUBLANG_DEFAULT
),
63 (LPTSTR
) buf
, sizeof (buf
), NULL
))
64 fprintf (stderr
, "mkgroup (%d): [%lu] %s", line
, code
, buf
);
66 fprintf (stderr
, "mkgroup (%d): error %lu", line
, code
);
72 HANDLE h
= LoadLibrary ("netapi32.dll");
74 dsgetdcname
= (void *) GetProcAddress (h
, "DsGetDcNameW");
78 get_dcname (char *domain
)
80 static WCHAR server
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
83 WCHAR domain_name
[MAX_DOMAIN_NAME_LEN
+ 1];
84 PDOMAIN_CONTROLLER_INFOW pdci
= NULL
;
90 mbstowcs (domain_name
, domain
, strlen (domain
) + 1);
91 rc
= dsgetdcname (NULL
, domain_name
, NULL
, NULL
, 0, &pdci
);
94 rc
= dsgetdcname (NULL
, NULL
, NULL
, NULL
, 0, &pdci
);
95 if (rc
!= ERROR_SUCCESS
)
100 wcscpy (server
, pdci
->DomainControllerName
);
101 NetApiBufferFree (pdci
);
105 rc
= NetGetDCName (NULL
, NULL
, (void *) &servername
);
106 if (rc
== ERROR_SUCCESS
&& domain
)
108 LPWSTR server
= servername
;
109 mbstowcs (domain_name
, domain
, strlen (domain
) + 1);
110 rc
= NetGetDCName (server
, domain_name
, (void *) &servername
);
111 NetApiBufferFree (server
);
113 if (rc
!= ERROR_SUCCESS
)
118 wcscpy (server
, servername
);
119 NetApiBufferFree ((PVOID
) servername
);
132 sprintf(t
, "%u", GetSidIdentifierAuthority (sid
)->Value
[5]);
134 for (i
= 0; i
< *GetSidSubAuthorityCount (sid
); ++i
)
136 sprintf(t
, "-%lu", *GetSidSubAuthority (sid
, i
));
144 BYTE SubAuthorityCount
;
145 SID_IDENTIFIER_AUTHORITY IdentifierAuthority
;
146 DWORD SubAuthority
[8];
149 #define MAX_BUILTIN_SIDS 100 /* Should be enough for the forseable future. */
150 DBGSID builtin_sid_list
[MAX_BUILTIN_SIDS
];
151 DWORD builtin_sid_cnt
;
154 enum_local_groups (BOOL domain
, domlist_t
*dom_or_machine
, const char *sep
,
155 int id_offset
, char *disp_groupname
)
157 WCHAR machine
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
158 PWCHAR servername
= NULL
;
159 char *d_or_m
= dom_or_machine
? dom_or_machine
->str
: NULL
;
160 BOOL with_dom
= dom_or_machine
? dom_or_machine
->with_dom
: FALSE
;
161 LOCALGROUP_INFO_0
*buffer
;
162 DWORD entriesread
= 0;
163 DWORD totalentries
= 0;
164 DWORD resume_handle
= 0;
165 WCHAR gname
[GNLEN
+ 1];
170 servername
= get_dcname (d_or_m
);
171 if (servername
== (PWCHAR
) -1)
176 int ret
= mbstowcs (machine
, d_or_m
, INTERNET_MAX_HOST_NAME_LENGTH
+ 1);
177 if (ret
< 1 || ret
>= INTERNET_MAX_HOST_NAME_LENGTH
+ 1)
179 fprintf (stderr
, "%s: Invalid machine name '%s'. Skipping...\n",
183 servername
= machine
;
190 if (disp_groupname
!= NULL
)
192 mbstowcs (gname
, disp_groupname
, GNLEN
+ 1);
193 rc
= NetApiBufferAllocate (sizeof (LOCALGROUP_INFO_0
),
195 buffer
[0].lgrpi0_name
= gname
;
199 rc
= NetLocalGroupEnum (servername
, 0, (void *) &buffer
,
200 MAX_PREFERRED_LENGTH
, &entriesread
,
201 &totalentries
, &resume_handle
);
204 case ERROR_ACCESS_DENIED
:
205 print_win_error (rc
);
208 case ERROR_MORE_DATA
:
213 print_win_error (rc
);
217 for (i
= 0; i
< entriesread
; i
++)
219 WCHAR domain_name
[MAX_DOMAIN_NAME_LEN
+ 1];
220 DWORD domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
221 char psid_buffer
[MAX_SID_LEN
];
222 PSID psid
= (PSID
) psid_buffer
;
223 DWORD sid_length
= MAX_SID_LEN
;
225 SID_NAME_USE acc_type
;
227 BOOL is_builtin
= FALSE
;
229 if (!LookupAccountNameW (servername
, buffer
[i
].lgrpi0_name
, psid
,
230 &sid_length
, domain_name
, &domname_len
,
233 print_win_error (rc
);
234 fprintf (stderr
, " (%ls)\n", buffer
[i
].lgrpi0_name
);
237 else if (acc_type
== SidTypeDomain
)
239 WCHAR domname
[MAX_DOMAIN_NAME_LEN
+ GNLEN
+ 2];
241 wcscpy (domname
, domain_name
);
242 wcscat (domname
, L
"\\");
243 wcscat (domname
, buffer
[i
].lgrpi0_name
);
244 sid_length
= MAX_SID_LEN
;
245 domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
246 if (!LookupAccountNameW (servername
, domname
,
248 domain_name
, &domname_len
,
251 print_win_error (rc
);
252 fprintf(stderr
, " (%ls)\n", domname
);
257 /* Store all local SIDs with prefix "S-1-5-32-" and check if it
258 has been printed already. This allows to get all builtin
259 groups exactly once and not once per domain. */
260 pdsid
= (PDBGSID
) psid
;
261 if (pdsid
->IdentifierAuthority
.Value
[5] == sid_nt_auth
.Value
[5]
262 && pdsid
->SubAuthority
[0] == SECURITY_BUILTIN_DOMAIN_RID
)
267 if (servername
&& builtin_sid_cnt
)
268 for (b
= 0; b
< builtin_sid_cnt
; b
++)
269 if (EqualSid (&builtin_sid_list
[b
], psid
))
271 if (builtin_sid_cnt
< MAX_BUILTIN_SIDS
)
272 CopySid (sizeof (DBGSID
), &builtin_sid_list
[builtin_sid_cnt
++],
276 gid
= *GetSidSubAuthority (psid
, *GetSidSubAuthorityCount(psid
) - 1);
278 printf ("%ls%s%ls:%s:%ld:\n",
279 with_dom
? domain_name
: L
"",
281 buffer
[i
].lgrpi0_name
,
283 gid
+ (is_builtin
? 0 : id_offset
));
288 NetApiBufferFree (buffer
);
291 while (rc
== ERROR_MORE_DATA
);
297 enum_groups (BOOL domain
, domlist_t
*dom_or_machine
, const char *sep
,
298 int id_offset
, char *disp_groupname
)
300 WCHAR machine
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
301 PWCHAR servername
= NULL
;
302 char *d_or_m
= dom_or_machine
? dom_or_machine
->str
: NULL
;
303 BOOL with_dom
= dom_or_machine
? dom_or_machine
->with_dom
: FALSE
;
304 GROUP_INFO_2
*buffer
;
305 DWORD entriesread
= 0;
306 DWORD totalentries
= 0;
307 DWORD resume_handle
= 0;
308 WCHAR gname
[GNLEN
+ 1];
313 servername
= get_dcname (d_or_m
);
314 if (servername
== (PWCHAR
) -1)
319 int ret
= mbstowcs (machine
, d_or_m
, INTERNET_MAX_HOST_NAME_LENGTH
+ 1);
320 if (ret
< 1 || ret
>= INTERNET_MAX_HOST_NAME_LENGTH
+ 1)
322 fprintf (stderr
, "%s: Invalid machine name '%s'. Skipping...\n",
326 servername
= machine
;
333 if (disp_groupname
!= NULL
)
335 mbstowcs (gname
, disp_groupname
, GNLEN
+ 1);
336 rc
= NetGroupGetInfo (servername
, (LPWSTR
) & gname
, 2,
341 rc
= NetGroupEnum (servername
, 2, (void *) & buffer
,
342 MAX_PREFERRED_LENGTH
, &entriesread
, &totalentries
,
346 case ERROR_ACCESS_DENIED
:
347 print_win_error (rc
);
350 case ERROR_MORE_DATA
:
355 print_win_error (rc
);
359 for (i
= 0; i
< entriesread
; i
++)
361 WCHAR domain_name
[MAX_DOMAIN_NAME_LEN
+ 1];
362 DWORD domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
363 char psid_buffer
[MAX_SID_LEN
];
364 PSID psid
= (PSID
) psid_buffer
;
365 DWORD sid_length
= MAX_SID_LEN
;
366 SID_NAME_USE acc_type
;
368 int gid
= buffer
[i
].grpi2_group_id
;
369 if (!LookupAccountNameW (servername
, buffer
[i
].grpi2_name
,
371 domain_name
, &domname_len
,
374 print_win_error (rc
);
375 fprintf(stderr
, " (%ls)\n", buffer
[i
].grpi2_name
);
378 else if (acc_type
== SidTypeDomain
)
380 WCHAR domname
[MAX_DOMAIN_NAME_LEN
+ GNLEN
+ 2];
382 wcscpy (domname
, domain_name
);
383 wcscat (domname
, L
"\\");
384 wcscat (domname
, buffer
[i
].grpi2_name
);
385 sid_length
= MAX_SID_LEN
;
386 domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
387 if (!LookupAccountNameW (servername
, domname
,
389 domain_name
, &domname_len
,
392 print_win_error (rc
);
393 fprintf(stderr
, " (%ls)\n", domname
);
397 printf ("%ls%s%ls:%s:%u:\n",
398 with_dom
? domain_name
: L
"",
400 buffer
[i
].grpi2_name
,
405 NetApiBufferFree (buffer
);
408 while (rc
== ERROR_MORE_DATA
);
412 print_special (PSID_IDENTIFIER_AUTHORITY auth
, BYTE cnt
,
413 DWORD sub1
, DWORD sub2
, DWORD sub3
, DWORD sub4
,
414 DWORD sub5
, DWORD sub6
, DWORD sub7
, DWORD sub8
)
416 char name
[UNLEN
+ 1], dom
[MAX_DOMAIN_NAME_LEN
+ 1];
417 DWORD len
, len2
, rid
;
421 if (AllocateAndInitializeSid (auth
, cnt
, sub1
, sub2
, sub3
, sub4
,
422 sub5
, sub6
, sub7
, sub8
, &sid
))
424 if (LookupAccountSid (NULL
, sid
,
425 name
, (len
= UNLEN
+ 1, &len
),
426 dom
, (len2
= MAX_DOMAIN_NAME_LEN
+ 1, &len
),
445 printf ("%s:%s:%lu:\n", name
, put_sid (sid
), rid
);
452 current_group (const char *sep
, int id_offset
)
458 char buffer
[MAX_SID_LEN
];
461 char dom
[MAX_DOMAIN_NAME_LEN
+ 1];
462 DWORD glen
= GNLEN
+ 1;
463 DWORD dlen
= MAX_DOMAIN_NAME_LEN
+ 1;
465 SID_NAME_USE acc_type
;
467 if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY
, &ptok
)
468 || !GetTokenInformation (ptok
, TokenPrimaryGroup
, &tg
, sizeof tg
, &len
)
469 || !CloseHandle (ptok
)
470 || !LookupAccountSidA (NULL
, tg
.psid
, grp
, &glen
, dom
, &dlen
, &acc_type
))
472 print_win_error (GetLastError ());
475 gid
= *GetSidSubAuthority (tg
.psid
, *GetSidSubAuthorityCount(tg
.psid
) - 1);
476 printf ("%s%s%s:%s:%u:\n",
485 usage (FILE * stream
)
488 "Usage: mkgroup [OPTION]...\n"
489 "Print /etc/group file to stdout\n"
492 " -l,--local [machine] print local groups (from local machine if no\n"
493 " machine specified)\n"
494 " -L,--Local [machine] ditto, but generate groupname with machine prefix\n"
495 " -d,--domain [domain] print domain groups (from current domain if no\n"
496 " domain specified)\n"
497 " -D,--Domain [domain] ditto, but generate groupname with machine prefix\n"
498 " -c,--current print current group\n"
499 " -C,--Current ditto, but generate groupname with machine or\n"
501 " -S,--separator char for -L, -D, -C use character char as domain\\group\n"
502 " separator in groupname instead of the default '\\'\n"
503 " -o,--id-offset offset change the default offset (10000) added to gids\n"
504 " in domain or foreign server accounts.\n"
505 " -g,--group groupname only return information for the specified group\n"
506 " one of -l, -L, -d, -D must be specified, too\n"
507 " -s,--no-sids (ignored)\n"
508 " -u,--users (ignored)\n"
509 " -h,--help print this message\n"
510 " -v,--version print version information and exit\n"
512 "Default is to print local groups on stand-alone machines, plus domain\n"
513 "groups on domain controllers and domain member machines.\n");
517 struct option longopts
[] = {
518 {"current", no_argument
, NULL
, 'c'},
519 {"Current", no_argument
, NULL
, 'C'},
520 {"domain", optional_argument
, NULL
, 'd'},
521 {"Domain", optional_argument
, NULL
, 'D'},
522 {"group", required_argument
, NULL
, 'g'},
523 {"help", no_argument
, NULL
, 'h'},
524 {"local", optional_argument
, NULL
, 'l'},
525 {"Local", optional_argument
, NULL
, 'L'},
526 {"id-offset", required_argument
, NULL
, 'o'},
527 {"no-sids", no_argument
, NULL
, 's'},
528 {"separator", required_argument
, NULL
, 'S'},
529 {"users", no_argument
, NULL
, 'u'},
530 {"version", no_argument
, NULL
, 'v'},
531 {0, no_argument
, NULL
, 0}
534 char opts
[] = "cCd::D::g:hl::L::o:sS:uv";
539 const char *v
= strchr (version
, ':');
549 len
= strchr (v
, ' ') - v
;
552 mkgroup (cygwin) %.*s\n\
553 group File Generator\n\
554 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Red Hat, Inc.\n\
556 ", len
, v
, __DATE__
);
559 static PPOLICY_PRIMARY_DOMAIN_INFO p_dom
;
562 fetch_primary_domain ()
565 LSA_OBJECT_ATTRIBUTES oa
= { 0, 0, 0, 0, 0, 0 };
570 status
= LsaOpenPolicy (NULL
, &oa
, POLICY_EXECUTE
, &lsa
);
571 if (!NT_SUCCESS (status
))
573 status
= LsaQueryInformationPolicy (lsa
, PolicyPrimaryDomainInformation
,
576 if (!NT_SUCCESS (status
))
583 main (int argc
, char **argv
)
586 domlist_t locals
[16];
587 int print_domain
= 0;
588 domlist_t domains
[16];
590 int print_current
= 0;
591 const char *sep_char
= "\\";
592 int id_offset
= 10000;
594 char *disp_groupname
= NULL
;
599 setmode (1, O_BINARY
);
602 in_domain
= fetch_primary_domain ();
605 print_special (&sid_nt_auth
, 1, SECURITY_LOCAL_SYSTEM_RID
,
606 0, 0, 0, 0, 0, 0, 0);
609 if (!enum_local_groups (TRUE
, NULL
, sep_char
, id_offset
,
611 enum_groups (TRUE
, NULL
, sep_char
, id_offset
, disp_groupname
);
613 else if (!enum_local_groups (FALSE
, NULL
, sep_char
, 0, disp_groupname
))
614 enum_groups (FALSE
, NULL
, sep_char
, 0, disp_groupname
);
618 while ((c
= getopt_long (argc
, argv
, opts
, longopts
, NULL
)) != EOF
)
623 if (print_local
>= 16)
625 fprintf (stderr
, "%s: Can not enumerate from more than 16 "
626 "servers.\n", __progname
);
630 argv
[optind
] && argv
[optind
][0] != '-' ? argv
[optind
] : NULL
;
631 for (i
= 0; i
< print_local
; ++i
)
632 if ((!locals
[i
].str
&& !opt
)
633 || (locals
[i
].str
&& opt
&& !strcmp (locals
[i
].str
, opt
)))
635 locals
[print_local
].str
= opt
;
636 locals
[print_local
++].with_dom
= c
== 'L';
641 if (print_domain
>= 16)
643 fprintf (stderr
, "%s: Can not enumerate from more than 16 "
644 "domains.\n", __progname
);
648 argv
[optind
] && argv
[optind
][0] != '-' ? argv
[optind
] : NULL
;
649 for (i
= 0; i
< print_domain
; ++i
)
650 if ((!domains
[i
].str
&& !opt
)
651 || (domains
[i
].str
&& opt
&& !strcmp (domains
[i
].str
, opt
)))
653 domains
[print_domain
].str
= opt
;
654 domains
[print_domain
++].with_dom
= c
== 'D';
659 if (strlen (sep_char
) > 1)
661 fprintf (stderr
, "%s: Only one character allowed as domain\\user "
662 "separator character.\n", __progname
);
665 if (*sep_char
== ':')
667 fprintf (stderr
, "%s: Colon not allowed as domain\\user separator "
668 "character.\n", __progname
);
679 id_offset
= strtol (optarg
, NULL
, 10);
686 disp_groupname
= optarg
;
687 isRoot
= !strcmp(disp_groupname
, "root");
696 fprintf (stderr
, "Try '%s --help' for more information.\n", argv
[0]);
700 if (optind
< argc
- 1)
703 /* Get 'system' group */
704 if (!disp_groupname
&& (print_local
> 0 || print_domain
> 0))
705 print_special (&sid_nt_auth
, 1, SECURITY_LOCAL_SYSTEM_RID
,
706 0, 0, 0, 0, 0, 0, 0);
711 /* Very special feature for the oncoming future:
712 Create a "root" group being actually the local Administrators group.
713 Printing root disables printing any other "real" local group. */
714 printf ("root:S-1-5-32-544:0:\n");
717 for (i
= 0; i
< print_local
; ++i
)
721 if (!enum_local_groups (FALSE
, locals
+ i
, sep_char
,
722 id_offset
* off
, disp_groupname
))
723 enum_groups (FALSE
, locals
+ i
, sep_char
, id_offset
* off
++,
726 else if (!enum_local_groups (FALSE
, locals
+ i
, sep_char
, 0,
728 enum_groups (FALSE
, locals
+ i
, sep_char
, 0, disp_groupname
);
731 for (i
= 0; i
< print_domain
; ++i
)
733 if (!enum_local_groups (TRUE
, domains
+ i
, sep_char
, id_offset
* off
,
735 enum_groups (TRUE
, domains
+ i
, sep_char
, id_offset
* off
++,
740 current_group (sep_char
, id_offset
);