]>
Commit | Line | Data |
---|---|---|
1fd5e000 CF |
1 | /* mkpasswd.c: |
2 | ||
df0f949c | 3 | Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008, 2009, |
61522196 | 4 | 2010, 2011, 2012, 2013 Red Hat, Inc. |
1fd5e000 CF |
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 | ||
a1e19903 | 12 | #define _WIN32_WINNT 0x0600 |
92b499ac | 13 | #include <errno.h> |
1fd5e000 CF |
14 | #include <ctype.h> |
15 | #include <stdlib.h> | |
16 | #include <wchar.h> | |
0bdab5c8 | 17 | #include <wctype.h> |
73535010 | 18 | #include <locale.h> |
1fd5e000 | 19 | #include <stdio.h> |
0fa64fa3 | 20 | #include <unistd.h> |
61522196 | 21 | #include <inttypes.h> |
315f8fd3 | 22 | #include <getopt.h> |
a1e19903 | 23 | #include <io.h> |
12a9c874 | 24 | #include <sys/fcntl.h> |
a1e19903 | 25 | #include <sys/cygwin.h> |
92b499ac | 26 | #include <cygwin/version.h> |
a1e19903 CV |
27 | #include <windows.h> |
28 | #include <lm.h> | |
375a780e | 29 | #include <iptypes.h> |
a1e19903 CV |
30 | #include <wininet.h> |
31 | #include <ntsecapi.h> | |
32 | #include <dsgetdc.h> | |
33 | #include <ntdef.h> | |
71d8f118 | 34 | #include "loadlib.h" |
1fd5e000 | 35 | |
114b7248 PH |
36 | #define print_win_error(x) _print_win_error(x, __LINE__) |
37 | ||
375a780e CV |
38 | #define MAX_SID_LEN 40 |
39 | ||
1fd5e000 CF |
40 | SID_IDENTIFIER_AUTHORITY sid_world_auth = {SECURITY_WORLD_SID_AUTHORITY}; |
41 | SID_IDENTIFIER_AUTHORITY sid_nt_auth = {SECURITY_NT_AUTHORITY}; | |
42 | ||
43 | #ifndef min | |
44 | #define min(a,b) (((a)<(b))?(a):(b)) | |
45 | #endif | |
46 | ||
6510edf4 | 47 | typedef struct |
a1e19903 CV |
48 | { |
49 | char *str; | |
f9519bcd CV |
50 | DWORD id_offset; |
51 | BOOL domain; | |
a1e19903 CV |
52 | BOOL with_dom; |
53 | } domlist_t; | |
54 | ||
6510edf4 | 55 | static void |
a1e19903 CV |
56 | _print_win_error(DWORD code, int line) |
57 | { | |
58 | char buf[4096]; | |
59 | ||
60 | if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | |
61 | | FORMAT_MESSAGE_IGNORE_INSERTS, | |
62 | NULL, | |
63 | code, | |
64 | MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), | |
65 | (LPTSTR) buf, sizeof (buf), NULL)) | |
61522196 CV |
66 | fprintf (stderr, "mkpasswd (%d): [%" PRIu32 "] %s", |
67 | line, (unsigned int) code, buf); | |
a1e19903 | 68 | else |
61522196 CV |
69 | fprintf (stderr, "mkpasswd (%d): error %" PRIu32, |
70 | line, (unsigned int) code); | |
a1e19903 CV |
71 | } |
72 | ||
a1e19903 CV |
73 | static PWCHAR |
74 | get_dcname (char *domain) | |
75 | { | |
76 | static WCHAR server[INTERNET_MAX_HOST_NAME_LENGTH + 1]; | |
77 | DWORD rc; | |
a1e19903 CV |
78 | WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1]; |
79 | PDOMAIN_CONTROLLER_INFOW pdci = NULL; | |
80 | ||
13a20f95 | 81 | if (domain) |
a1e19903 | 82 | { |
13a20f95 CV |
83 | mbstowcs (domain_name, domain, strlen (domain) + 1); |
84 | rc = DsGetDcNameW (NULL, domain_name, NULL, NULL, 0, &pdci); | |
a1e19903 CV |
85 | } |
86 | else | |
13a20f95 CV |
87 | rc = DsGetDcNameW (NULL, NULL, NULL, NULL, 0, &pdci); |
88 | if (rc != ERROR_SUCCESS) | |
a1e19903 | 89 | { |
13a20f95 CV |
90 | print_win_error (rc); |
91 | return (PWCHAR) -1; | |
a1e19903 | 92 | } |
13a20f95 CV |
93 | wcscpy (server, pdci->DomainControllerName); |
94 | NetApiBufferFree (pdci); | |
a1e19903 CV |
95 | return server; |
96 | } | |
97 | ||
6510edf4 | 98 | static char * |
1fd5e000 CF |
99 | put_sid (PSID sid) |
100 | { | |
101 | static char s[512]; | |
102 | char t[32]; | |
103 | DWORD i; | |
104 | ||
105 | strcpy (s, "S-1-"); | |
106 | sprintf(t, "%u", GetSidIdentifierAuthority (sid)->Value[5]); | |
107 | strcat (s, t); | |
108 | for (i = 0; i < *GetSidSubAuthorityCount (sid); ++i) | |
109 | { | |
61522196 | 110 | sprintf(t, "-%" PRIu32, (unsigned int) *GetSidSubAuthority (sid, i)); |
1fd5e000 CF |
111 | strcat (s, t); |
112 | } | |
113 | return s; | |
114 | } | |
115 | ||
6510edf4 | 116 | static void |
dd9752e8 | 117 | uni2ansi (LPWSTR wcs, char *mbs, int size) |
1fd5e000 CF |
118 | { |
119 | if (wcs) | |
375a780e | 120 | wcstombs (mbs, wcs, size); |
1fd5e000 CF |
121 | else |
122 | *mbs = '\0'; | |
123 | } | |
124 | ||
9258eca9 CV |
125 | typedef struct { |
126 | PSID psid; | |
127 | int buffer[10]; | |
128 | } sidbuf; | |
129 | ||
6510edf4 CF |
130 | static sidbuf curr_user; |
131 | static sidbuf curr_pgrp; | |
132 | static BOOL got_curr_user = FALSE; | |
9258eca9 | 133 | |
6510edf4 | 134 | static void |
9258eca9 | 135 | fetch_current_user_sid () |
7041c7ab | 136 | { |
f1c9046a CV |
137 | DWORD len; |
138 | HANDLE ptok; | |
9258eca9 CV |
139 | |
140 | if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &ptok) | |
141 | || !GetTokenInformation (ptok, TokenUser, &curr_user, sizeof curr_user, | |
142 | &len) | |
143 | || !GetTokenInformation (ptok, TokenPrimaryGroup, &curr_pgrp, | |
144 | sizeof curr_pgrp, &len) | |
145 | || !CloseHandle (ptok)) | |
146 | { | |
147 | print_win_error (GetLastError ()); | |
148 | return; | |
149 | } | |
150 | } | |
151 | ||
6510edf4 | 152 | static void |
13a20f95 CV |
153 | current_user (const char *sep, const char *passed_home_path, DWORD id_offset, |
154 | const char *disp_username) | |
9258eca9 | 155 | { |
0bdab5c8 CV |
156 | WCHAR user[UNLEN + 1]; |
157 | WCHAR dom[MAX_DOMAIN_NAME_LEN + 1]; | |
a1e19903 CV |
158 | DWORD ulen = UNLEN + 1; |
159 | DWORD dlen = MAX_DOMAIN_NAME_LEN + 1; | |
160 | SID_NAME_USE acc_type; | |
161 | int uid, gid; | |
05e6f7b2 | 162 | char homedir_psx[PATH_MAX] = {0}; |
a1e19903 | 163 | |
9258eca9 CV |
164 | if (!curr_user.psid || !curr_pgrp.psid |
165 | || !LookupAccountSidW (NULL, curr_user.psid, user, &ulen, dom, &dlen, | |
166 | &acc_type)) | |
f1c9046a | 167 | { |
a1e19903 | 168 | print_win_error (GetLastError ()); |
f1c9046a CV |
169 | return; |
170 | } | |
171 | ||
9258eca9 CV |
172 | uid = *GetSidSubAuthority (curr_user.psid, |
173 | *GetSidSubAuthorityCount(curr_user.psid) - 1); | |
174 | gid = *GetSidSubAuthority (curr_pgrp.psid, | |
175 | *GetSidSubAuthorityCount(curr_pgrp.psid) - 1); | |
f1c9046a CV |
176 | if (passed_home_path[0] == '\0') |
177 | { | |
6428476b | 178 | char *envhome = getenv ("HOME"); |
a1e19903 | 179 | |
6428476b CV |
180 | /* If $HOME exists and is non-empty, just copy it over to homedir_psx. |
181 | Otherwise, generate a new path of the form "/home/$USER". */ | |
182 | if (envhome && envhome[0] != '\0') | |
183 | strncat (homedir_psx, envhome, sizeof (homedir_psx) - 1); | |
184 | else | |
6510edf4 | 185 | { |
0bdab5c8 CV |
186 | wcstombs (stpncpy (homedir_psx, "/home/", sizeof (homedir_psx)), |
187 | user, sizeof (homedir_psx) - 6); | |
188 | homedir_psx[PATH_MAX - 1] = '\0'; | |
f1c9046a CV |
189 | } |
190 | } | |
191 | else | |
192 | { | |
0bdab5c8 CV |
193 | char *p = stpncpy (homedir_psx, passed_home_path, sizeof (homedir_psx)); |
194 | wcstombs (p, user, sizeof (homedir_psx) - (p - homedir_psx)); | |
195 | homedir_psx[PATH_MAX - 1] = '\0'; | |
f1c9046a CV |
196 | } |
197 | ||
61522196 CV |
198 | printf ("%ls%s%ls:unused:%" PRIu32 ":%" PRIu32 |
199 | ":U-%ls\\%ls,%s:%s:/bin/bash\n", | |
0bdab5c8 | 200 | sep ? dom : L"", |
a1e19903 CV |
201 | sep ?: "", |
202 | user, | |
61522196 CV |
203 | (unsigned int) (id_offset + uid), |
204 | (unsigned int) (id_offset + gid), | |
a1e19903 CV |
205 | dom, |
206 | user, | |
9258eca9 | 207 | put_sid (curr_user.psid), |
f1c9046a CV |
208 | homedir_psx); |
209 | } | |
210 | ||
6510edf4 | 211 | static void |
f9519bcd | 212 | enum_unix_users (domlist_t *dom_or_machine, const char *sep, DWORD id_offset, |
0bdab5c8 CV |
213 | char *unix_user_list) |
214 | { | |
215 | WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; | |
216 | PWCHAR servername = NULL; | |
217 | char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL; | |
218 | BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE; | |
219 | SID_IDENTIFIER_AUTHORITY auth = { { 0, 0, 0, 0, 0, 22 } }; | |
220 | char *ustr, *user_list; | |
221 | WCHAR user[UNLEN + sizeof ("Unix User\\") + 1]; | |
222 | WCHAR dom[MAX_DOMAIN_NAME_LEN + 1]; | |
223 | DWORD ulen, dlen, sidlen; | |
224 | PSID psid; | |
225 | char psid_buffer[MAX_SID_LEN]; | |
226 | SID_NAME_USE acc_type; | |
227 | ||
228 | if (!d_or_m) | |
229 | return; | |
230 | ||
231 | int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1); | |
232 | if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1) | |
233 | { | |
234 | fprintf (stderr, "%s: Invalid machine name '%s'. Skipping...\n", | |
92b499ac | 235 | program_invocation_short_name, d_or_m); |
0bdab5c8 CV |
236 | return; |
237 | } | |
238 | servername = machine; | |
239 | ||
240 | if (!AllocateAndInitializeSid (&auth, 2, 1, 0, 0, 0, 0, 0, 0, 0, &psid)) | |
241 | return; | |
242 | ||
243 | if (!(user_list = strdup (unix_user_list))) | |
244 | { | |
245 | FreeSid (psid); | |
246 | return; | |
247 | } | |
248 | ||
249 | for (ustr = strtok (user_list, ","); ustr; ustr = strtok (NULL, ",")) | |
250 | { | |
05e6f7b2 | 251 | if (!isdigit ((unsigned char) ustr[0]) && ustr[0] != '-') |
6510edf4 | 252 | { |
0bdab5c8 CV |
253 | PWCHAR p = wcpcpy (user, L"Unix User\\"); |
254 | ret = mbstowcs (p, ustr, UNLEN + 1); | |
255 | if (ret < 1 || ret >= UNLEN + 1) | |
256 | fprintf (stderr, "%s: Invalid user name '%s'. Skipping...\n", | |
92b499ac | 257 | program_invocation_short_name, ustr); |
0bdab5c8 CV |
258 | else if (LookupAccountNameW (servername, user, |
259 | psid = (PSID) psid_buffer, | |
260 | (sidlen = MAX_SID_LEN, &sidlen), | |
261 | dom, | |
262 | (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen), | |
263 | &acc_type)) | |
61522196 | 264 | printf ("%s%s%ls:unused:%" PRIu32 ":99999:,%s::\n", |
6510edf4 CF |
265 | with_dom ? "Unix User" : "", |
266 | with_dom ? sep : "", | |
267 | user + 10, | |
61522196 | 268 | (unsigned int) (id_offset + |
0bdab5c8 | 269 | *GetSidSubAuthority (psid, |
61522196 | 270 | *GetSidSubAuthorityCount(psid) - 1)), |
6510edf4 | 271 | put_sid (psid)); |
0bdab5c8 CV |
272 | } |
273 | else | |
274 | { | |
275 | DWORD start, stop; | |
276 | char *p = ustr; | |
277 | if (*p == '-') | |
278 | start = 0; | |
279 | else | |
280 | start = strtol (p, &p, 10); | |
281 | if (!*p) | |
282 | stop = start; | |
05e6f7b2 | 283 | else if (*p++ != '-' || !isdigit ((unsigned char) *p) |
0bdab5c8 CV |
284 | || (stop = strtol (p, &p, 10)) < start || *p) |
285 | { | |
286 | fprintf (stderr, "%s: Malformed unix user list entry '%s'. " | |
92b499ac CV |
287 | "Skipping...\n", |
288 | program_invocation_short_name, ustr); | |
0bdab5c8 CV |
289 | continue; |
290 | } | |
291 | for (; start <= stop; ++ start) | |
292 | { | |
293 | *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1) | |
294 | = start; | |
295 | if (LookupAccountSidW (servername, psid, | |
296 | user, (ulen = GNLEN + 1, &ulen), | |
297 | dom, | |
298 | (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen), | |
299 | &acc_type) | |
300 | && !iswdigit (user[0])) | |
61522196 | 301 | printf ("%s%s%ls:unused:%" PRIu32 ":99999:,%s::\n", |
0bdab5c8 CV |
302 | with_dom ? "Unix User" : "", |
303 | with_dom ? sep : "", | |
304 | user, | |
61522196 | 305 | (unsigned int) (id_offset + start), |
0bdab5c8 CV |
306 | put_sid (psid)); |
307 | } | |
308 | } | |
309 | } | |
310 | ||
311 | free (user_list); | |
312 | FreeSid (psid); | |
313 | } | |
314 | ||
6510edf4 | 315 | static int |
a1e19903 | 316 | enum_users (BOOL domain, domlist_t *dom_or_machine, const char *sep, |
13a20f95 CV |
317 | const char *passed_home_path, DWORD id_offset, char *disp_username, |
318 | int print_current) | |
1fd5e000 | 319 | { |
a1e19903 CV |
320 | WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1]; |
321 | PWCHAR servername = NULL; | |
322 | char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL; | |
323 | BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE; | |
1fd5e000 CF |
324 | USER_INFO_3 *buffer; |
325 | DWORD entriesread = 0; | |
326 | DWORD totalentries = 0; | |
327 | DWORD resume_handle = 0; | |
0ac91154 | 328 | DWORD rc; |
375a780e | 329 | WCHAR uni_name[UNLEN + 1]; |
a1e19903 CV |
330 | if (domain) |
331 | { | |
332 | servername = get_dcname (d_or_m); | |
333 | if (servername == (PWCHAR) -1) | |
6510edf4 | 334 | return 1; |
a1e19903 CV |
335 | } |
336 | else if (d_or_m) | |
337 | { | |
338 | int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1); | |
339 | if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1) | |
340 | { | |
341 | fprintf (stderr, "%s: Invalid machine name '%s'. Skipping...\n", | |
92b499ac | 342 | program_invocation_short_name, d_or_m); |
a1e19903 CV |
343 | return 1; |
344 | } | |
345 | servername = machine; | |
346 | } | |
1fd5e000 CF |
347 | |
348 | do | |
349 | { | |
350 | DWORD i; | |
1fd5e000 | 351 | |
375a780e CV |
352 | if (disp_username != NULL) |
353 | { | |
354 | mbstowcs (uni_name, disp_username, UNLEN + 1); | |
355 | rc = NetUserGetInfo (servername, (LPWSTR) &uni_name, 3, | |
356 | (void *) &buffer); | |
357 | entriesread = 1; | |
91dd009e CV |
358 | /* Avoid annoying error messages just because the user hasn't been |
359 | found. */ | |
360 | if (rc == NERR_UserNotFound) | |
361 | return 0; | |
375a780e | 362 | } |
6510edf4 | 363 | else |
375a780e CV |
364 | rc = NetUserEnum (servername, 3, FILTER_NORMAL_ACCOUNT, |
365 | (void *) &buffer, MAX_PREFERRED_LENGTH, | |
366 | &entriesread, &totalentries, &resume_handle); | |
1fd5e000 CF |
367 | switch (rc) |
368 | { | |
369 | case ERROR_ACCESS_DENIED: | |
7041c7ab | 370 | print_win_error(rc); |
a1e19903 | 371 | return 1; |
1fd5e000 CF |
372 | |
373 | case ERROR_MORE_DATA: | |
374 | case ERROR_SUCCESS: | |
375 | break; | |
376 | ||
377 | default: | |
7041c7ab | 378 | print_win_error(rc); |
a1e19903 | 379 | return 1; |
1fd5e000 CF |
380 | } |
381 | ||
382 | for (i = 0; i < entriesread; i++) | |
383 | { | |
375a780e | 384 | char homedir_psx[PATH_MAX]; |
375a780e CV |
385 | WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1]; |
386 | DWORD domname_len = MAX_DOMAIN_NAME_LEN + 1; | |
387 | char psid_buffer[MAX_SID_LEN]; | |
1fd5e000 | 388 | PSID psid = (PSID) psid_buffer; |
375a780e | 389 | DWORD sid_length = MAX_SID_LEN; |
1fd5e000 CF |
390 | SID_NAME_USE acc_type; |
391 | ||
392 | int uid = buffer[i].usri3_user_id; | |
393 | int gid = buffer[i].usri3_primary_group_id; | |
13a20f95 | 394 | homedir_psx[0] = '\0'; |
8606f005 CV |
395 | if (passed_home_path[0] == '\0') |
396 | { | |
13a20f95 CV |
397 | if (buffer[i].usri3_home_dir[0] != L'\0') |
398 | cygwin_conv_path (CCP_WIN_W_TO_POSIX | CCP_ABSOLUTE, | |
399 | buffer[i].usri3_home_dir, homedir_psx, | |
400 | PATH_MAX); | |
8606f005 | 401 | else |
375a780e CV |
402 | uni2ansi (buffer[i].usri3_name, |
403 | stpcpy (homedir_psx, "/home/"), PATH_MAX - 6); | |
8606f005 | 404 | } |
37c49e19 | 405 | else |
375a780e CV |
406 | uni2ansi (buffer[i].usri3_name, |
407 | stpcpy (homedir_psx, passed_home_path), | |
408 | PATH_MAX - strlen (passed_home_path)); | |
9ae2974f | 409 | |
a1e19903 CV |
410 | if (!LookupAccountNameW (servername, buffer[i].usri3_name, |
411 | psid, &sid_length, domain_name, | |
412 | &domname_len, &acc_type)) | |
1fd5e000 | 413 | { |
7041c7ab | 414 | print_win_error(GetLastError ()); |
a1e19903 | 415 | fprintf(stderr, " (%ls)\n", buffer[i].usri3_name); |
1fd5e000 CF |
416 | continue; |
417 | } | |
37c49e19 CF |
418 | else if (acc_type == SidTypeDomain) |
419 | { | |
a1e19903 | 420 | WCHAR domname[MAX_DOMAIN_NAME_LEN + UNLEN + 2]; |
375a780e | 421 | |
7e1fdc9e CV |
422 | wcscpy (domname, domain || !servername |
423 | ? domain_name : servername); | |
375a780e | 424 | wcscat (domname, L"\\"); |
a1e19903 | 425 | wcscat (domname, buffer[i].usri3_name); |
375a780e | 426 | sid_length = MAX_SID_LEN; |
a1e19903 CV |
427 | domname_len = sizeof (domname); |
428 | if (!LookupAccountNameW (servername, domname, psid, | |
429 | &sid_length, domain_name, | |
430 | &domname_len, &acc_type)) | |
37c49e19 | 431 | { |
7041c7ab | 432 | print_win_error(GetLastError ()); |
375a780e | 433 | fprintf(stderr, " (%ls)\n", domname); |
37c49e19 CF |
434 | continue; |
435 | } | |
436 | } | |
6510edf4 CF |
437 | if (!print_current) |
438 | /* fall through */; | |
439 | else if (EqualSid (curr_user.psid, psid)) | |
9258eca9 | 440 | got_curr_user = TRUE; |
6510edf4 | 441 | |
61522196 CV |
442 | printf ("%ls%s%ls:unused:%" PRIu32 ":%" PRIu32 |
443 | ":%ls%sU-%ls\\%ls,%s:%s:/bin/bash\n", | |
a1e19903 CV |
444 | with_dom ? domain_name : L"", |
445 | with_dom ? sep : "", | |
6510edf4 | 446 | buffer[i].usri3_name, |
61522196 CV |
447 | (unsigned int) (id_offset + uid), |
448 | (unsigned int) (id_offset + gid), | |
a1e19903 | 449 | buffer[i].usri3_full_name ?: L"", |
6510edf4 | 450 | buffer[i].usri3_full_name |
a1e19903 CV |
451 | && buffer[i].usri3_full_name[0] ? "," : "", |
452 | domain_name, | |
453 | buffer[i].usri3_name, | |
454 | put_sid (psid), | |
455 | homedir_psx); | |
1fd5e000 CF |
456 | } |
457 | ||
375a780e | 458 | NetApiBufferFree (buffer); |
1fd5e000 CF |
459 | |
460 | } | |
0ac91154 | 461 | while (rc == ERROR_MORE_DATA); |
1fd5e000 CF |
462 | |
463 | return 0; | |
464 | } | |
465 | ||
6510edf4 | 466 | static void |
036fb477 CV |
467 | print_special_by_sid (PSID_IDENTIFIER_AUTHORITY auth, BYTE cnt, |
468 | DWORD sub1, DWORD sub2, DWORD sub3, DWORD sub4, | |
469 | DWORD sub5, DWORD sub6, DWORD sub7, DWORD sub8) | |
011ec894 | 470 | { |
0bdab5c8 | 471 | WCHAR user[UNLEN + 1], dom[MAX_DOMAIN_NAME_LEN + 1]; |
011ec894 CV |
472 | DWORD len, len2, rid; |
473 | PSID sid; | |
0bdab5c8 | 474 | SID_NAME_USE acc_type; |
011ec894 CV |
475 | |
476 | if (AllocateAndInitializeSid (auth, cnt, sub1, sub2, sub3, sub4, | |
6510edf4 | 477 | sub5, sub6, sub7, sub8, &sid)) |
011ec894 | 478 | { |
0bdab5c8 CV |
479 | if (LookupAccountSidW (NULL, sid, |
480 | user, (len = UNLEN + 1, &len), | |
61522196 | 481 | dom, (len2 = MAX_DOMAIN_NAME_LEN + 1, &len2), |
0bdab5c8 | 482 | &acc_type)) |
011ec894 CV |
483 | { |
484 | if (sub8) | |
485 | rid = sub8; | |
486 | else if (sub7) | |
487 | rid = sub7; | |
488 | else if (sub6) | |
489 | rid = sub6; | |
490 | else if (sub5) | |
491 | rid = sub5; | |
492 | else if (sub4) | |
493 | rid = sub4; | |
494 | else if (sub3) | |
495 | rid = sub3; | |
496 | else if (sub2) | |
497 | rid = sub2; | |
498 | else | |
499 | rid = sub1; | |
61522196 CV |
500 | printf ("%ls:*:%" PRIu32 ":%" PRIu32 ":,%s::\n", |
501 | user, (unsigned int) rid, | |
502 | (unsigned int) (rid == 18 ? 544 : rid), /* SYSTEM hack */ | |
a1e19903 | 503 | put_sid (sid)); |
6510edf4 | 504 | } |
011ec894 CV |
505 | FreeSid (sid); |
506 | } | |
507 | } | |
508 | ||
6510edf4 | 509 | static int |
a1e19903 | 510 | usage (FILE * stream) |
1fd5e000 | 511 | { |
a1e19903 | 512 | fprintf (stream, |
92b499ac CV |
513 | "Usage: %s [OPTIONS]...\n" |
514 | "\n" | |
a1e19903 CV |
515 | "Print /etc/passwd file to stdout\n" |
516 | "\n" | |
517 | "Options:\n" | |
92b499ac | 518 | "\n" |
f9519bcd CV |
519 | " -l,--local [machine[,offset]]\n" |
520 | " print local user accounts with uid offset offset\n" | |
521 | " (from local machine if no machine specified)\n" | |
522 | " -L,--Local [machine[,offset]]\n" | |
523 | " ditto, but generate username with machine prefix\n" | |
524 | " -d,--domain [domain[,offset]]\n" | |
525 | " print domain accounts with uid offset offset\n" | |
526 | " (from current domain if no domain specified)\n" | |
527 | " -D,--Domain [domain[,offset]]\n" | |
528 | " ditto, but generate username with domain prefix\n" | |
a1e19903 CV |
529 | " -c,--current print current user\n" |
530 | " -C,--Current ditto, but generate username with machine or\n" | |
531 | " domain prefix\n" | |
532 | " -S,--separator char for -L, -D, -C use character char as domain\\user\n" | |
533 | " separator in username instead of the default '\\'\n" | |
534 | " -o,--id-offset offset change the default offset (10000) added to uids\n" | |
535 | " in domain or foreign server accounts.\n" | |
536 | " -u,--username username only return information for the specified user\n" | |
537 | " one of -l, -L, -d, -D must be specified, too\n" | |
538 | " -p,--path-to-home path use specified path instead of user account home dir\n" | |
539 | " or /home prefix\n" | |
6510edf4 | 540 | " -U,--unix userlist additionally print UNIX users when using -l or -L\n" |
0bdab5c8 CV |
541 | " on a UNIX Samba server\n" |
542 | " userlist is a comma-separated list of usernames\n" | |
543 | " or uid ranges (root,-25,50-100).\n" | |
544 | " (enumerating large ranges can take a long time!)\n" | |
a1e19903 | 545 | " -s,--no-sids (ignored)\n" |
13a20f95 | 546 | " -m,--no-mount (ignored)\n" |
a1e19903 CV |
547 | " -g,--local-groups (ignored)\n" |
548 | " -h,--help displays this message\n" | |
92b499ac | 549 | " -V,--version version information and exit\n" |
a1e19903 CV |
550 | "\n" |
551 | "Default is to print local accounts on stand-alone machines, domain accounts\n" | |
92b499ac CV |
552 | "on domain controllers and domain member machines.\n" |
553 | "\n", program_invocation_short_name); | |
f1c9046a | 554 | return 1; |
1fd5e000 CF |
555 | } |
556 | ||
6510edf4 | 557 | static struct option longopts[] = { |
f1c9046a | 558 | {"current", no_argument, NULL, 'c'}, |
a1e19903 CV |
559 | {"Current", no_argument, NULL, 'C'}, |
560 | {"domain", optional_argument, NULL, 'd'}, | |
561 | {"Domain", optional_argument, NULL, 'D'}, | |
9ae2974f | 562 | {"local-groups", no_argument, NULL, 'g'}, |
a1e19903 CV |
563 | {"help", no_argument, NULL, 'h'}, |
564 | {"local", optional_argument, NULL, 'l'}, | |
565 | {"Local", optional_argument, NULL, 'L'}, | |
315f8fd3 | 566 | {"no-mount", no_argument, NULL, 'm'}, |
a1e19903 | 567 | {"id-offset", required_argument, NULL, 'o'}, |
1d3dc113 | 568 | {"path-to-home", required_argument, NULL, 'p'}, |
a1e19903 CV |
569 | {"no-sids", no_argument, NULL, 's'}, |
570 | {"separator", required_argument, NULL, 'S'}, | |
1d3dc113 | 571 | {"username", required_argument, NULL, 'u'}, |
0bdab5c8 | 572 | {"unix", required_argument, NULL, 'U'}, |
92b499ac | 573 | {"version", no_argument, NULL, 'V'}, |
315f8fd3 CV |
574 | {0, no_argument, NULL, 0} |
575 | }; | |
576 | ||
92b499ac | 577 | static char opts[] = "cCd::D::ghl::L::mo:sS:p:u:U:V"; |
eccebec0 CV |
578 | |
579 | static void | |
580 | print_version () | |
581 | { | |
92b499ac | 582 | printf ("mkpasswd (cygwin) %d.%d.%d\n" |
1b23b30b CF |
583 | "Passwd File Generator\n" |
584 | "Copyright (C) 1997 - %s Red Hat, Inc.\n" | |
585 | "This is free software; see the source for copying conditions. There is NO\n" | |
92b499ac | 586 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", |
1b23b30b CF |
587 | CYGWIN_VERSION_DLL_MAJOR / 1000, |
588 | CYGWIN_VERSION_DLL_MAJOR % 1000, | |
589 | CYGWIN_VERSION_DLL_MINOR, | |
590 | strrchr (__DATE__, ' ') + 1); | |
eccebec0 | 591 | } |
315f8fd3 | 592 | |
036fb477 CV |
593 | static void |
594 | print_special_by_name (PCWSTR name, uid_t uid, gid_t gid) | |
595 | { | |
596 | DWORD size = 256, dom_size = 256; | |
597 | PSID sid = (PSID) alloca (size); | |
598 | WCHAR dom[dom_size]; | |
599 | SID_NAME_USE use; | |
600 | ||
601 | PWCHAR name_only = wcschr (name, L'\\'); | |
602 | if (name_only) | |
603 | ++name_only; | |
604 | ||
605 | if (LookupAccountNameW (NULL, name, sid, &size, dom, &dom_size, &use)) | |
606 | printf ("%ls:*:%lu:%lu:U-%ls%s%ls,%s::\n", | |
607 | name_only ?: name, | |
608 | (unsigned long) uid, | |
609 | (unsigned long) gid, | |
610 | name_only ? dom : L"", | |
611 | name_only ? "\\" : "", | |
612 | name_only ?: name, | |
613 | put_sid (sid)); | |
614 | } | |
615 | ||
a1e19903 CV |
616 | static void |
617 | enum_std_accounts () | |
618 | { | |
619 | /* Generate service starter account entries. */ | |
620 | printf ("SYSTEM:*:18:544:,S-1-5-18::\n"); | |
621 | printf ("LocalService:*:19:544:U-NT AUTHORITY\\LocalService,S-1-5-19::\n"); | |
622 | printf ("NetworkService:*:20:544:U-NT AUTHORITY\\NetworkService,S-1-5-20::\n"); | |
623 | /* Get 'administrators' group (has localized name). */ | |
036fb477 CV |
624 | print_special_by_sid (&sid_nt_auth, 2, SECURITY_BUILTIN_DOMAIN_RID, |
625 | DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0); | |
626 | /* Fetch "TrustedInstaller" account starting with Vista. */ | |
627 | print_special_by_name (L"NT SERVICE\\TrustedInstaller", -2, -2); | |
a1e19903 CV |
628 | } |
629 | ||
630 | static PPOLICY_PRIMARY_DOMAIN_INFO p_dom; | |
631 | ||
632 | static BOOL | |
633 | fetch_primary_domain () | |
634 | { | |
635 | NTSTATUS status; | |
636 | LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 }; | |
637 | LSA_HANDLE lsa; | |
638 | ||
639 | if (!p_dom) | |
640 | { | |
641 | status = LsaOpenPolicy (NULL, &oa, POLICY_VIEW_LOCAL_INFORMATION, &lsa); | |
642 | if (!NT_SUCCESS (status)) | |
643 | return FALSE; | |
644 | status = LsaQueryInformationPolicy (lsa, PolicyPrimaryDomainInformation, | |
6510edf4 | 645 | (PVOID *) ((void *) &p_dom)); |
a1e19903 CV |
646 | LsaClose (lsa); |
647 | if (!NT_SUCCESS (status)) | |
648 | return FALSE; | |
649 | } | |
650 | return !!p_dom->Sid; | |
651 | } | |
652 | ||
eedc36cb | 653 | int |
1fd5e000 CF |
654 | main (int argc, char **argv) |
655 | { | |
f9519bcd CV |
656 | int print_domlist = 0; |
657 | domlist_t domlist[32]; | |
658 | char *opt, *p, *ep; | |
a1e19903 | 659 | int print_current = 0; |
0bdab5c8 | 660 | char *print_unix = NULL; |
a1e19903 | 661 | const char *sep_char = "\\"; |
f9519bcd CV |
662 | DWORD id_offset = 10000, off; |
663 | int c, i; | |
1d3dc113 | 664 | char *disp_username = NULL; |
a1e19903 CV |
665 | char passed_home_path[PATH_MAX]; |
666 | BOOL in_domain; | |
01dd3162 | 667 | int optional_args = 0; |
1fd5e000 | 668 | |
9ae2974f | 669 | passed_home_path[0] = '\0'; |
dfe56933 CF |
670 | if (!isatty (1)) |
671 | setmode (1, O_BINARY); | |
9ae2974f | 672 | |
73535010 CV |
673 | /* Use locale from environment. If not set or set to "C", use UTF-8. */ |
674 | setlocale (LC_CTYPE, ""); | |
675 | if (!strcmp (setlocale (LC_CTYPE, NULL), "C")) | |
676 | setlocale (LC_CTYPE, "en_US.UTF-8"); | |
a1e19903 | 677 | in_domain = fetch_primary_domain (); |
6510edf4 CF |
678 | fetch_current_user_sid (); |
679 | ||
a1e19903 | 680 | if (argc == 1) |
011ec894 | 681 | { |
a1e19903 CV |
682 | enum_std_accounts (); |
683 | if (in_domain) | |
13a20f95 CV |
684 | enum_users (TRUE, NULL, sep_char, passed_home_path, 10000, |
685 | disp_username, 0); | |
a1e19903 | 686 | else |
13a20f95 | 687 | enum_users (FALSE, NULL, sep_char, passed_home_path, 0, |
6510edf4 | 688 | disp_username, 0); |
a1e19903 CV |
689 | return 0; |
690 | } | |
691 | ||
01dd3162 | 692 | unsetenv ("POSIXLY_CORRECT"); /* To get optional arg processing right. */ |
a1e19903 CV |
693 | while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF) |
694 | switch (c) | |
695 | { | |
f9519bcd CV |
696 | case 'd': |
697 | case 'D': | |
a1e19903 CV |
698 | case 'l': |
699 | case 'L': | |
f9519bcd | 700 | if (print_domlist >= 32) |
f1c9046a | 701 | { |
f9519bcd | 702 | fprintf (stderr, "%s: Can not enumerate from more than 32 " |
92b499ac CV |
703 | "domains and machines.\n", |
704 | program_invocation_short_name); | |
f1c9046a CV |
705 | return 1; |
706 | } | |
f36c78a4 | 707 | domlist[print_domlist].domain = (c == 'd' || c == 'D'); |
a1e19903 CV |
708 | opt = optarg ?: |
709 | argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL; | |
096df177 | 710 | if (argv[optind] && opt == argv[optind]) |
01dd3162 | 711 | ++optional_args; |
f9519bcd | 712 | for (i = 0; i < print_domlist; ++i) |
f36c78a4 CV |
713 | if (domlist[i].domain == domlist[print_domlist].domain |
714 | && ((!domlist[i].str && !opt) | |
715 | || (domlist[i].str && opt | |
716 | && (off = strlen (domlist[i].str)) | |
717 | && !strncmp (domlist[i].str, opt, off) | |
718 | && (!opt[off] || opt[off] == ',')))) | |
719 | { | |
720 | fprintf (stderr, "%s: Duplicate %s '%s'. Skipping...\n", | |
92b499ac CV |
721 | program_invocation_short_name, |
722 | domlist[i].domain ? "domain" : "machine", | |
f36c78a4 CV |
723 | domlist[i].str); |
724 | goto skip; | |
725 | } | |
f9519bcd | 726 | domlist[print_domlist].str = opt; |
61522196 | 727 | domlist[print_domlist].id_offset = UINT32_MAX; |
f9519bcd | 728 | if (opt && (p = strchr (opt, ','))) |
a1e19903 | 729 | { |
f9519bcd | 730 | if (p == opt |
05e6f7b2 | 731 | || !isdigit ((unsigned char) p[1]) |
6510edf4 | 732 | || (domlist[print_domlist].id_offset = strtol (p + 1, &ep, 10) |
f9519bcd CV |
733 | , *ep)) |
734 | { | |
735 | fprintf (stderr, "%s: Malformed domain,offset string '%s'. " | |
92b499ac | 736 | "Skipping...\n", program_invocation_short_name, opt); |
6510edf4 | 737 | break; |
f9519bcd CV |
738 | } |
739 | *p = '\0'; | |
a1e19903 | 740 | } |
f9519bcd CV |
741 | domlist[print_domlist++].with_dom = (c == 'D' || c == 'L'); |
742 | skip: | |
a1e19903 CV |
743 | break; |
744 | case 'S': | |
745 | sep_char = optarg; | |
746 | if (strlen (sep_char) > 1) | |
747 | { | |
748 | fprintf (stderr, "%s: Only one character allowed as domain\\user " | |
92b499ac CV |
749 | "separator character.\n", |
750 | program_invocation_short_name); | |
a1e19903 CV |
751 | return 1; |
752 | } | |
753 | if (*sep_char == ':') | |
754 | { | |
755 | fprintf (stderr, "%s: Colon not allowed as domain\\user separator " | |
92b499ac | 756 | "character.\n", program_invocation_short_name); |
a1e19903 CV |
757 | return 1; |
758 | } | |
6510edf4 | 759 | break; |
0bdab5c8 | 760 | case 'U': |
6510edf4 | 761 | print_unix = optarg; |
0bdab5c8 | 762 | break; |
a1e19903 CV |
763 | case 'c': |
764 | sep_char = NULL; | |
765 | /*FALLTHRU*/ | |
766 | case 'C': | |
767 | print_current = 1; | |
768 | break; | |
769 | case 'o': | |
f9519bcd CV |
770 | id_offset = strtoul (optarg, &ep, 10); |
771 | if (*ep) | |
772 | { | |
773 | fprintf (stderr, "%s: Malformed offset '%s'. " | |
92b499ac | 774 | "Skipping...\n", program_invocation_short_name, optarg); |
f9519bcd CV |
775 | return 1; |
776 | } | |
a1e19903 | 777 | break; |
a1e19903 CV |
778 | case 'p': |
779 | if (optarg[0] != '/') | |
780 | { | |
781 | fprintf (stderr, "%s: '%s' is not a fully qualified path.\n", | |
92b499ac | 782 | program_invocation_short_name, optarg); |
f1c9046a CV |
783 | return 1; |
784 | } | |
a1e19903 CV |
785 | strcpy (passed_home_path, optarg); |
786 | if (optarg[strlen (optarg)-1] != '/') | |
787 | strcat (passed_home_path, "/"); | |
788 | break; | |
789 | case 'u': | |
790 | disp_username = optarg; | |
791 | break; | |
792 | case 'h': | |
793 | usage (stdout); | |
794 | return 0; | |
92b499ac | 795 | case 'V': |
a1e19903 CV |
796 | print_version (); |
797 | return 0; | |
13a20f95 CV |
798 | case 'g': /* deprecated */ |
799 | case 's': /* deprecated */ | |
800 | case 'm': /* deprecated */ | |
801 | break; | |
a1e19903 | 802 | default: |
92b499ac CV |
803 | fprintf (stderr, "Try `%s --help' for more information.\n", |
804 | program_invocation_short_name); | |
a1e19903 CV |
805 | return 1; |
806 | } | |
1fd5e000 | 807 | |
01dd3162 | 808 | optind += optional_args; |
6510edf4 CF |
809 | if (argv[optind]) |
810 | { | |
811 | fprintf (stderr, | |
812 | "mkpasswd: non-option command line argument `%s' is not allowed.\n" | |
813 | "Try `mkpasswd --help' for more information.\n", argv[optind]); | |
814 | exit (1); | |
815 | } | |
9258eca9 | 816 | |
f9519bcd CV |
817 | off = id_offset; |
818 | for (i = 0; i < print_domlist; ++i) | |
1d3dc113 | 819 | { |
f9519bcd | 820 | DWORD my_off = (domlist[i].domain || domlist[i].str) |
61522196 | 821 | ? domlist[i].id_offset != UINT_MAX |
f9519bcd CV |
822 | ? domlist[i].id_offset : off : 0; |
823 | if (!domlist[i].domain && domlist[i].str && print_unix) | |
824 | enum_unix_users (domlist + i, sep_char, my_off, print_unix); | |
328b090f | 825 | if (!my_off && !disp_username) |
6510edf4 | 826 | enum_std_accounts (); |
13a20f95 CV |
827 | enum_users (domlist[i].domain, domlist + i, sep_char, passed_home_path, |
828 | my_off, disp_username, print_current); | |
f9519bcd | 829 | if (my_off) |
6510edf4 | 830 | off += id_offset; |
1d3dc113 | 831 | } |
1fd5e000 | 832 | |
9258eca9 | 833 | if (print_current && !got_curr_user) |
13a20f95 | 834 | current_user (sep_char, passed_home_path, off, disp_username); |
f1c9046a | 835 | |
1fd5e000 CF |
836 | return 0; |
837 | } |