3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
9 #define _WIN32_WINNT 0x0a00
22 #include <sys/fcntl.h>
23 #include <sys/cygwin.h>
24 #include <cygwin/version.h>
33 #define print_win_error(x) _print_win_error(x, __LINE__)
35 SID_IDENTIFIER_AUTHORITY sid_world_auth
= {SECURITY_WORLD_SID_AUTHORITY
};
36 SID_IDENTIFIER_AUTHORITY sid_nt_auth
= {SECURITY_NT_AUTHORITY
};
39 #define min(a,b) (((a)<(b))?(a):(b))
50 _print_win_error(DWORD code
, int line
)
54 if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
55 | FORMAT_MESSAGE_IGNORE_INSERTS
,
58 MAKELANGID (LANG_NEUTRAL
, SUBLANG_DEFAULT
),
59 (LPTSTR
) buf
, sizeof (buf
), NULL
))
60 fprintf (stderr
, "mkpasswd (%d): [%" PRIu32
"] %s",
61 line
, (unsigned int) code
, buf
);
63 fprintf (stderr
, "mkpasswd (%d): error %" PRIu32
,
64 line
, (unsigned int) code
);
75 sprintf(t
, "%u", GetSidIdentifierAuthority (sid
)->Value
[5]);
77 for (i
= 0; i
< *GetSidSubAuthorityCount (sid
); ++i
)
79 sprintf(t
, "-%" PRIu32
, (unsigned int) *GetSidSubAuthority (sid
, i
));
86 uni2ansi (LPWSTR wcs
, char *mbs
, int size
)
89 wcstombs (mbs
, wcs
, size
);
99 static sidbuf curr_user
;
100 static sidbuf curr_pgrp
;
101 static BOOL got_curr_user
= FALSE
;
104 fetch_current_user_sid ()
109 if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY
, &ptok
)
110 || !GetTokenInformation (ptok
, TokenUser
, &curr_user
, sizeof curr_user
,
112 || !GetTokenInformation (ptok
, TokenPrimaryGroup
, &curr_pgrp
,
113 sizeof curr_pgrp
, &len
)
114 || !CloseHandle (ptok
))
116 print_win_error (GetLastError ());
122 enum_unix_users (domlist_t
*mach
, const char *sep
, DWORD id_offset
,
123 char *unix_user_list
)
125 WCHAR machine
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
126 SID_IDENTIFIER_AUTHORITY auth
= { { 0, 0, 0, 0, 0, 22 } };
127 char *ustr
, *user_list
;
128 WCHAR user
[UNLEN
+ sizeof ("Unix User\\") + 1];
129 WCHAR dom
[MAX_DOMAIN_NAME_LEN
+ 1];
130 DWORD ulen
, dlen
, sidlen
;
133 char psid_buffer
[SECURITY_MAX_SID_SIZE
];
134 SID_NAME_USE acc_type
;
136 int ret
= mbstowcs (machine
, mach
->str
, INTERNET_MAX_HOST_NAME_LENGTH
+ 1);
137 if (ret
< 1 || ret
>= INTERNET_MAX_HOST_NAME_LENGTH
+ 1)
139 fprintf (stderr
, "%s: Invalid machine name '%s'. Skipping...\n",
140 program_invocation_short_name
, mach
->str
);
144 if (!AllocateAndInitializeSid (&auth
, 2, 1, 0, 0, 0, 0, 0, 0, 0,
148 if (!(user_list
= strdup (unix_user_list
)))
150 FreeSid (numeric_psid
);
154 for (ustr
= strtok (user_list
, ","); ustr
; ustr
= strtok (NULL
, ","))
156 if (!isdigit ((unsigned char) ustr
[0]) && ustr
[0] != '-')
158 PWCHAR p
= wcpcpy (user
, L
"Unix User\\");
159 ret
= mbstowcs (p
, ustr
, UNLEN
+ 1);
160 if (ret
< 1 || ret
>= UNLEN
+ 1)
162 fprintf (stderr
, "%s: Invalid user name '%s'. Skipping...\n",
163 program_invocation_short_name
, ustr
);
166 psid
= (PSID
) psid_buffer
;
167 sidlen
= SECURITY_MAX_SID_SIZE
;
168 dlen
= MAX_DOMAIN_NAME_LEN
+ 1;
169 if (LookupAccountNameW (machine
, user
, psid
, &sidlen
,
170 dom
, &dlen
, &acc_type
))
171 printf ("%s%s%ls:*:%" PRIu32
":99999:,%s::\n",
175 (unsigned int) (id_offset
+
176 *GetSidSubAuthority (psid
,
177 *GetSidSubAuthorityCount(psid
) - 1)),
187 start
= strtol (p
, &p
, 10);
190 else if (*p
++ != '-' || !isdigit ((unsigned char) *p
)
191 || (stop
= strtol (p
, &p
, 10)) < start
|| *p
)
193 fprintf (stderr
, "%s: Malformed unix user list entry '%s'. "
195 program_invocation_short_name
, ustr
);
198 for (; start
<= stop
; ++ start
)
201 *GetSidSubAuthority (psid
, *GetSidSubAuthorityCount(psid
) - 1)
204 dlen
= MAX_DOMAIN_NAME_LEN
+ 1;
205 if (LookupAccountSidW (machine
, psid
, user
, &ulen
,
206 dom
, &dlen
, &acc_type
)
207 && !iswdigit (user
[0]))
208 printf ("%s%s%ls:*:%" PRIu32
":99999:,%s::\n",
212 (unsigned int) (id_offset
+ start
),
219 FreeSid (numeric_psid
);
223 enum_users (domlist_t
*mach
, const char *sep
, const char *passed_home_path
,
224 DWORD id_offset
, char *disp_username
, int print_current
)
226 WCHAR machine
[INTERNET_MAX_HOST_NAME_LENGTH
+ 1];
228 DWORD entriesread
= 0;
229 DWORD totalentries
= 0;
230 DWORD resume_handle
= 0;
232 WCHAR uni_name
[UNLEN
+ 1];
234 int ret
= mbstowcs (machine
, mach
->str
, INTERNET_MAX_HOST_NAME_LENGTH
+ 1);
235 if (ret
< 1 || ret
>= INTERNET_MAX_HOST_NAME_LENGTH
+ 1)
237 fprintf (stderr
, "%s: Invalid machine name '%s'. Skipping...\n",
238 program_invocation_short_name
, mach
->str
);
246 if (disp_username
!= NULL
)
248 mbstowcs (uni_name
, disp_username
, UNLEN
+ 1);
249 rc
= NetUserGetInfo (machine
, (LPWSTR
) &uni_name
, 3,
252 /* Avoid annoying error messages just because the user hasn't been
254 if (rc
== NERR_UserNotFound
)
258 rc
= NetUserEnum (machine
, 3, FILTER_NORMAL_ACCOUNT
,
259 (void *) &buffer
, MAX_PREFERRED_LENGTH
,
260 &entriesread
, &totalentries
, &resume_handle
);
263 case ERROR_ACCESS_DENIED
:
267 case ERROR_MORE_DATA
:
276 for (i
= 0; i
< entriesread
; i
++)
278 char homedir_psx
[PATH_MAX
];
279 WCHAR domain_name
[MAX_DOMAIN_NAME_LEN
+ 1];
280 DWORD domname_len
= MAX_DOMAIN_NAME_LEN
+ 1;
281 char psid_buffer
[SECURITY_MAX_SID_SIZE
];
282 PSID psid
= (PSID
) psid_buffer
;
283 DWORD sid_length
= SECURITY_MAX_SID_SIZE
;
284 SID_NAME_USE acc_type
;
286 int uid
= buffer
[i
].usri3_user_id
;
287 int gid
= buffer
[i
].usri3_primary_group_id
;
288 homedir_psx
[0] = '\0';
289 if (passed_home_path
[0] == '\0')
291 if (buffer
[i
].usri3_home_dir
[0] != L
'\0')
292 cygwin_conv_path (CCP_WIN_W_TO_POSIX
| CCP_ABSOLUTE
,
293 buffer
[i
].usri3_home_dir
, homedir_psx
,
296 uni2ansi (buffer
[i
].usri3_name
,
297 stpcpy (homedir_psx
, "/home/"), PATH_MAX
- 6);
300 uni2ansi (buffer
[i
].usri3_name
,
301 stpcpy (homedir_psx
, passed_home_path
),
302 PATH_MAX
- strlen (passed_home_path
));
304 if (!LookupAccountNameW (machine
, buffer
[i
].usri3_name
,
305 psid
, &sid_length
, domain_name
,
306 &domname_len
, &acc_type
))
308 print_win_error(GetLastError ());
309 fprintf(stderr
, " (%ls)\n", buffer
[i
].usri3_name
);
312 else if (acc_type
== SidTypeDomain
)
314 WCHAR domname
[MAX_DOMAIN_NAME_LEN
+ UNLEN
+ 2];
317 p
= wcpcpy (domname
, machine
);
318 p
= wcpcpy (p
, L
"\\");
319 p
= wcpncpy (p
, buffer
[i
].usri3_name
, UNLEN
);
321 sid_length
= SECURITY_MAX_SID_SIZE
;
322 domname_len
= sizeof (domname
);
323 if (!LookupAccountNameW (machine
, domname
, psid
,
324 &sid_length
, domain_name
,
325 &domname_len
, &acc_type
))
327 print_win_error(GetLastError ());
328 fprintf(stderr
, " (%ls)\n", domname
);
334 else if (EqualSid (curr_user
.psid
, psid
))
335 got_curr_user
= TRUE
;
337 printf ("%ls%s%ls:*:%" PRIu32
":%" PRIu32
338 ":%ls%sU-%ls\\%ls,%s:%s:/bin/bash\n",
339 mach
->with_dom
? domain_name
: L
"",
340 mach
->with_dom
? sep
: "",
341 buffer
[i
].usri3_name
,
342 (unsigned int) (id_offset
+ uid
),
343 (unsigned int) (id_offset
+ gid
),
344 buffer
[i
].usri3_full_name
?: L
"",
345 buffer
[i
].usri3_full_name
346 && buffer
[i
].usri3_full_name
[0] ? "," : "",
348 buffer
[i
].usri3_name
,
353 NetApiBufferFree (buffer
);
356 while (rc
== ERROR_MORE_DATA
);
361 static int __attribute__ ((__noreturn__
))
362 usage (FILE * stream
)
365 "Usage: %s [OPTIONS]...\n"
367 "Write /etc/passwd-like output to stdout\n"
369 "Don't use this command to generate a local /etc/passwd file, unless you\n"
370 "really need one. See the Cygwin User's Guide for more information.\n"
374 " -l,--local [machine] Print local user accounts of \"machine\",\n"
375 " from local machine if no machine specified.\n"
376 " Automatically adding machine prefix for local\n"
377 " machine depends on settings in /etc/nsswitch.conf.\n"
378 " -L,--Local machine Ditto, but generate username with machine prefix.\n"
379 " -d,--domain [domain] Print domain accounts,\n"
380 " from current domain if no domain specified.\n"
381 " -c,--current Print current user.\n"
382 " -S,--separator char For -L use character char as domain\\user\n"
383 " separator in username instead of the default '%s'.\n"
384 " -o,--id-offset offset Change the default offset (0x10000) added to uids\n"
385 " of foreign local machine accounts. Use with -l/-L.\n"
386 " -u,--username username Only return information for the specified user.\n"
387 " One of -l, -d must be specified, too\n"
388 " -b,--no-builtin Don't print BUILTIN users.\n"
389 " -p,--path-to-home path Use specified path instead of user account home dir\n"
390 " or /home prefix.\n"
391 " -U,--unix userlist Print UNIX users when using -l on a UNIX Samba\n"
392 " server. Userlist is a comma-separated list of\n"
393 " usernames or uid ranges (root,-25,50-100).\n"
394 " Enumerating large ranges can take a long time!\n"
395 " -h,--help Displays this message.\n"
396 " -V,--version Version information and exit.\n"
398 "Default is to print local accounts on stand-alone machines, domain accounts\n"
399 "on domain controllers and domain member machines.\n"
400 "\n", program_invocation_short_name
,
401 (const char *) cygwin_internal (CW_GETNSSSEP
));
402 exit (stream
== stdout
? 0 : 1);
405 static struct option longopts
[] = {
406 {"no-builtin", no_argument
, NULL
, 'b'},
407 {"current", no_argument
, NULL
, 'c'},
408 {"Current", no_argument
, NULL
, 'C'},
409 {"domain", optional_argument
, NULL
, 'd'},
410 {"Domain", optional_argument
, NULL
, 'D'},
411 {"local-groups", no_argument
, NULL
, 'g'},
412 {"help", no_argument
, NULL
, 'h'},
413 {"local", optional_argument
, NULL
, 'l'},
414 {"Local", optional_argument
, NULL
, 'L'},
415 {"no-mount", no_argument
, NULL
, 'm'},
416 {"id-offset", required_argument
, NULL
, 'o'},
417 {"path-to-home", required_argument
, NULL
, 'p'},
418 {"no-sids", no_argument
, NULL
, 's'},
419 {"separator", required_argument
, NULL
, 'S'},
420 {"username", required_argument
, NULL
, 'u'},
421 {"unix", required_argument
, NULL
, 'U'},
422 {"version", no_argument
, NULL
, 'V'},
423 {0, no_argument
, NULL
, 0}
426 static char opts
[] = "bcCd::D::ghl::L::mo:sS:p:u:U:V";
431 printf ("mkpasswd (cygwin) %d.%d.%d\n"
432 "Passwd File Generator\n"
433 "Copyright (C) 1997 - %s Cygwin Authors\n"
434 "This is free software; see the source for copying conditions. There is NO\n"
435 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
436 CYGWIN_VERSION_DLL_MAJOR
/ 1000,
437 CYGWIN_VERSION_DLL_MAJOR
% 1000,
438 CYGWIN_VERSION_DLL_MINOR
,
439 strrchr (__DATE__
, ' ') + 1);
443 main (int argc
, char **argv
)
445 int print_domlist
= 0;
446 domlist_t domlist
[32];
449 int print_current
= 0;
450 int print_builtin
= 1;
451 char *print_unix
= NULL
;
452 const char *nss_sep
= (const char *) cygwin_internal (CW_GETNSSSEP
);
453 const char *sep_char
= nss_sep
;
454 DWORD id_offset
= 0x10000, off
;
456 char *disp_username
= NULL
;
457 char passed_home_path
[PATH_MAX
];
458 int optional_args
= 0;
459 uintptr_t nss_src
= cygwin_internal (CW_GETNSS_PWD_SRC
);
461 passed_home_path
[0] = '\0';
463 setmode (1, O_BINARY
);
465 /* Use locale from environment. If not set or set to "C", use UTF-8. */
466 setlocale (LC_CTYPE
, "");
467 if (!strcmp (setlocale (LC_CTYPE
, NULL
), "C"))
468 setlocale (LC_CTYPE
, "en_US.UTF-8");
469 fetch_current_user_sid ();
473 int enums
= ENUM_PRIMARY
| ENUM_LOCAL
| ENUM_BUILTIN
;
474 uintptr_t ticket
= cygwin_internal (CW_SETENT
, FALSE
, enums
, NULL
);
479 while ((pwd
= (struct passwd
*) cygwin_internal (CW_GETENT
, FALSE
,
481 printf ("%s:%s:%u:%u:%s:%s:%s\n", pwd
->pw_name
, pwd
->pw_passwd
,
482 pwd
->pw_uid
, pwd
->pw_gid
, pwd
->pw_gecos
, pwd
->pw_dir
,
484 cygwin_internal (CW_ENDENT
, FALSE
, ticket
);
489 unsetenv ("POSIXLY_CORRECT"); /* To get optional arg processing right. */
490 while ((c
= getopt_long (argc
, argv
, opts
, longopts
, NULL
)) != EOF
)
497 if (print_domlist
>= 32)
499 fprintf (stderr
, "%s: Can not enumerate from more than 32 "
500 "domains and machines.\n",
501 program_invocation_short_name
);
504 domlist
[print_domlist
].domain
= (c
== 'd' || c
== 'D');
506 argv
[optind
] && argv
[optind
][0] != '-' ? argv
[optind
] : NULL
;
507 if (argv
[optind
] && opt
== argv
[optind
])
509 for (i
= 0; i
< print_domlist
; ++i
)
510 if (domlist
[i
].domain
== domlist
[print_domlist
].domain
511 && ((!domlist
[i
].str
&& !opt
)
512 || (domlist
[i
].str
&& opt
513 && (off
= strlen (domlist
[i
].str
))
514 && !strncmp (domlist
[i
].str
, opt
, off
)
515 && (!opt
[off
] || opt
[off
] == ','))))
517 fprintf (stderr
, "%s: Duplicate %s '%s'. Skipping...\n",
518 program_invocation_short_name
,
519 domlist
[i
].domain
? "domain" : "machine",
523 domlist
[print_domlist
].str
= opt
;
524 if (opt
&& (p
= strchr (opt
, ',')))
528 fprintf (stderr
, "%s: Malformed domain string '%s'. "
529 "Skipping...\n", program_invocation_short_name
, opt
);
534 if (c
== 'l' || c
== 'L')
536 DWORD csize
= sizeof cname
;
538 domlist
[print_domlist
].with_dom
= (c
== 'L');
541 /* If the system uses /etc/passwd exclusively as account DB,
542 create local group names the old fashioned way. */
543 if (nss_src
== NSS_SRC_FILES
)
545 GetComputerNameExA (ComputerNameNetBIOS
, cname
, &csize
);
546 domlist
[print_domlist
].str
= cname
;
549 else if (nss_src
!= NSS_SRC_FILES
)
551 /* If the system uses Windows account DBs, check if machine
552 name is local machine. If so, remove the domain name to
553 enforce system naming convention. */
554 if (GetComputerNameExA (strchr (opt
, '.')
555 ? ComputerNameDnsFullyQualified
556 : ComputerNameNetBIOS
,
558 && strcasecmp (opt
, cname
) == 0)
559 domlist
[print_domlist
].str
= NULL
;
566 if (strlen (sep_char
) > 1)
568 fprintf (stderr
, "%s: Only one ASCII character allowed as "
569 "domain\\user separator character.\n",
570 program_invocation_short_name
);
573 if (*sep_char
== ':')
575 fprintf (stderr
, "%s: Colon not allowed as domain\\user separator "
576 "character.\n", program_invocation_short_name
);
588 id_offset
= strtoul (optarg
, &ep
, 10);
594 if (optarg
[0] != '/')
596 fprintf (stderr
, "%s: '%s' is not a fully qualified path.\n",
597 program_invocation_short_name
, optarg
);
600 strcpy (passed_home_path
, optarg
);
601 if (optarg
[strlen (optarg
)-1] != '/')
602 strcat (passed_home_path
, "/");
605 disp_username
= optarg
;
612 case 'g': /* deprecated */
613 case 's': /* deprecated */
614 case 'm': /* deprecated */
617 fprintf (stderr
, "Try `%s --help' for more information.\n",
618 program_invocation_short_name
);
622 optind
+= optional_args
;
626 "mkpasswd: non-option command line argument `%s' is not allowed.\n"
627 "Try `mkpasswd --help' for more information.\n", argv
[optind
]);
631 struct passwd
*ppwd
= NULL
;
632 const char *ppwd_sid
= NULL
;
635 ppwd
= (struct passwd
*) cygwin_internal (CW_GETPWSID
, TRUE
,
638 ppwd_sid
= strrchr (ppwd
->pw_gecos
, ',');
641 int enums
= ENUM_NONE
;
642 WCHAR tdoms
[print_domlist
* 258];
644 if (!disp_username
&& print_builtin
&& print_domlist
)
645 enums
|= ENUM_BUILTIN
;
646 for (i
= 0; i
< print_domlist
; ++i
)
648 if (domlist
[i
].domain
)
653 t
+= mbstowcs (t
, domlist
[i
].str
, 257);
657 enums
|= ENUM_PRIMARY
;
659 else if (!domlist
[i
].str
)
666 uintptr_t ticket
= cygwin_internal (CW_SETENT
, FALSE
, enums
,
667 t
> tdoms
? tdoms
: NULL
);
672 while ((pwd
= (struct passwd
*)
673 cygwin_internal (CW_GETENT
, FALSE
, ticket
)))
677 && strcasecmp (disp_username
, pwd
->pw_name
) != 0
678 && (!(p
= strchr (pwd
->pw_name
, nss_sep
[0]))
679 || strcasecmp (disp_username
, p
+ 1) != 0))
681 printf ("%s:%s:%u:%u:%s:%s%s:%s\n", pwd
->pw_name
, pwd
->pw_passwd
,
682 pwd
->pw_uid
, pwd
->pw_gid
, pwd
->pw_gecos
,
683 passed_home_path
[0] ? passed_home_path
: "",
684 passed_home_path
[0] ? (p
? p
+ 1 : pwd
->pw_name
)
687 const char *pwd_sid
= strrchr (pwd
->pw_gecos
, ',');
688 if (ppwd
&& ppwd_sid
&& pwd_sid
&& !strcmp (pwd_sid
, ppwd_sid
))
689 got_curr_user
= TRUE
;
691 cygwin_internal (CW_ENDENT
, FALSE
, ticket
);
695 if (print_current
&& !got_curr_user
)
697 p
= strchr (ppwd
->pw_name
, nss_sep
[0]);
698 printf ("%s:%s:%u:%u:%s:%s%s:%s\n", ppwd
->pw_name
, ppwd
->pw_passwd
,
699 ppwd
->pw_uid
, ppwd
->pw_gid
, ppwd
->pw_gecos
,
700 passed_home_path
[0] ? passed_home_path
: "",
701 passed_home_path
[0] ? (p
? p
+ 1 : ppwd
->pw_name
) : ppwd
->pw_dir
,
706 for (i
= 0; i
< print_domlist
; ++i
)
708 if (domlist
[i
].domain
|| !domlist
[i
].str
)
710 enum_users (domlist
+ i
, sep_char
, passed_home_path
,
711 (nss_src
== NSS_SRC_FILES
) ? 0x30000 : off
,
712 disp_username
, print_current
);
713 if (!domlist
[i
].domain
&& domlist
[i
].str
&& print_unix
)
714 enum_unix_users (domlist
+ i
, sep_char
, 0xff000000, print_unix
);