1 /* passwd.c: Changing passwords and managing account information
3 Copyright 1999, 2000, 2001, 2002, 2003, 2008, 2009 Red Hat, Inc.
5 Written by Corinna Vinschen <corinna.vinschen@cityweb.de>
7 This file is part of Cygwin.
9 This software is a copyrighted work licensed under the terms of the
10 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
26 #include <sys/cygwin.h>
27 #include <sys/types.h>
33 #define USER_PRIV_ADMIN 2
35 static const char version[] = "$Revision$";
36 static char *prog_name;
38 static struct option longopts[] =
40 {"cannot-change", no_argument, NULL, 'c'},
41 {"can-change", no_argument, NULL, 'C'},
42 {"logonserver", required_argument, NULL, 'd'},
43 {"never-expires", no_argument, NULL, 'e'},
44 {"expires", no_argument, NULL, 'E'},
45 {"help", no_argument, NULL, 'h' },
46 {"inactive", required_argument, NULL, 'i'},
47 {"lock", no_argument, NULL, 'l'},
48 {"minage", required_argument, NULL, 'n'},
49 {"pwd-not-required", no_argument, NULL, 'p'},
50 {"pwd-required", no_argument, NULL, 'P'},
51 {"unlock", no_argument, NULL, 'u'},
52 {"version", no_argument, NULL, 'v'},
53 {"maxage", required_argument, NULL, 'x'},
54 {"length", required_argument, NULL, 'L'},
55 {"status", no_argument, NULL, 'S'},
56 { "reg-store-pwd", no_argument, NULL, 'R'},
60 static char opts[] = "cCd:eEhi:ln:pPuvx:L:SR";
63 eprint (int with_name, const char *fmt, ...)
68 fprintf(stderr, "%s: ", prog_name);
70 vfprintf (stderr, fmt, ap);
72 fprintf(stderr, "\n");
77 EvalRet (int ret, const char *user)
84 case ERROR_ACCESS_DENIED:
86 eprint (0, "You may not change password expiry information.");
88 eprint (0, "You may not change the password for %s.", user);
91 eprint (0, "Bad password: Invalid.");
94 case NERR_PasswordTooShort:
95 eprint (0, "Bad password: Too short.");
98 case NERR_UserNotFound:
99 eprint (1, "unknown user %s", user);
102 case ERROR_INVALID_PASSWORD:
103 case NERR_BadPassword:
104 eprint (0, "Incorrect password for %s.", user);
105 eprint (0, "The password for %s is unchanged.", user);
109 eprint (1, "unrecoverable error %d", ret);
116 GetPW (char *user, int print_win_name, LPCWSTR server)
118 char usr_buf[UNLEN + 1];
119 WCHAR name[UNLEN + 1];
123 char *domain = (char *) alloca (INTERNET_MAX_HOST_NAME_LENGTH + 1);
125 /* Try getting a Win32 username in case the user edited /etc/passwd */
126 if ((pw = getpwnam (user)))
128 cygwin_internal (CW_EXTRACT_DOMAIN_AND_USER, pw, domain, usr_buf);
129 if (strcasecmp (pw->pw_name, usr_buf))
131 /* Hack to avoid problem with LookupAccountSid after impersonation */
132 if (strcasecmp (usr_buf, "SYSTEM"))
136 printf ("Windows username : %s\n", user);
140 mbstowcs (name, user, UNLEN + 1);
141 ret = NetUserGetInfo (server, name, 3, (void *) &ui);
142 return EvalRet (ret, user) ? NULL : ui;
146 ChangePW (const char *user, const char *oldpwd, const char *pwd, int justcheck,
149 WCHAR name[UNLEN + 1], oldpass[512], pass[512];
152 mbstowcs (name, user, UNLEN + 1);
153 mbstowcs (pass, pwd, 512);
158 ui.usri1003_password = pass;
159 ret = NetUserSetInfo (server, name, 1003, (LPBYTE) &ui, NULL);
163 mbstowcs (oldpass, oldpwd, 512);
164 ret = NetUserChangePassword (server, name, oldpass, pass);
166 if (justcheck && ret != ERROR_INVALID_PASSWORD)
168 if (! EvalRet (ret, user) && ! justcheck)
170 eprint (0, "Password changed.");
176 PrintPW (PUSER_INFO_3 ui, LPCWSTR server)
178 time_t t = time (NULL) - ui->usri3_password_age;
180 PUSER_MODALS_INFO_0 mi;
182 printf ("Account disabled : %s",
183 (ui->usri3_flags & UF_ACCOUNTDISABLE) ? "yes\n" : "no\n");
184 printf ("Password not required : %s",
185 (ui->usri3_flags & UF_PASSWD_NOTREQD) ? "yes\n" : "no\n");
186 printf ("User can't change password : %s",
187 (ui->usri3_flags & UF_PASSWD_CANT_CHANGE) ? "yes\n" : "no\n");
188 printf ("Password never expires : %s",
189 (ui->usri3_flags & UF_DONT_EXPIRE_PASSWD) ? "yes\n" : "no\n");
190 printf ("Password expired : %s",
191 (ui->usri3_password_expired) ? "yes\n" : "no\n");
192 printf ("Latest password change : %s", ctime(&t));
193 ret = NetUserModalsGet (server, 0, (void *) &mi);
196 if (mi->usrmod0_max_passwd_age == TIMEQ_FOREVER)
197 mi->usrmod0_max_passwd_age = 0;
198 if (mi->usrmod0_min_passwd_age == TIMEQ_FOREVER)
199 mi->usrmod0_min_passwd_age = 0;
200 if (mi->usrmod0_force_logoff == TIMEQ_FOREVER)
201 mi->usrmod0_force_logoff = 0;
202 if (ui->usri3_priv == USER_PRIV_ADMIN)
203 mi->usrmod0_min_passwd_len = 0;
204 printf ("\nSystem password settings:\n");
205 printf ("Max. password age %ld days\n",
206 mi->usrmod0_max_passwd_age / ONE_DAY);
207 printf ("Min. password age %ld days\n",
208 mi->usrmod0_min_passwd_age / ONE_DAY);
209 printf ("Force logout after %ld days\n",
210 mi->usrmod0_force_logoff / ONE_DAY);
211 printf ("Min. password length: %ld\n",
212 mi->usrmod0_min_passwd_len);
217 SetModals (int xarg, int narg, int iarg, int Larg, LPCWSTR server)
220 PUSER_MODALS_INFO_0 mi;
222 ret = NetUserModalsGet (server, 0, (void *) &mi);
226 mi->usrmod0_max_passwd_age = TIMEQ_FOREVER;
228 mi->usrmod0_max_passwd_age = xarg * ONE_DAY;
232 mi->usrmod0_min_passwd_age = TIMEQ_FOREVER;
233 mi->usrmod0_password_hist_len = 0;
236 mi->usrmod0_min_passwd_age = narg * ONE_DAY;
239 mi->usrmod0_force_logoff = TIMEQ_FOREVER;
241 mi->usrmod0_force_logoff = iarg * ONE_DAY;
244 mi->usrmod0_min_passwd_len = Larg;
246 ret = NetUserModalsSet (server, 0, (LPBYTE) mi, NULL);
247 NetApiBufferFree (mi);
249 return EvalRet (ret, NULL);
252 static void usage (FILE * stream, int status) __attribute__ ((noreturn));
254 usage (FILE * stream, int status)
257 "Usage: %s [OPTION] [USER]\n"
258 "Change USER's password or password attributes.\n"
261 " -l, --lock lock USER's account.\n"
262 " -u, --unlock unlock USER's account.\n"
263 " -c, --cannot-change USER can't change password.\n"
264 " -C, --can-change USER can change password.\n"
265 " -e, --never-expires USER's password never expires.\n"
266 " -E, --expires USER's password expires according to system's\n"
267 " password aging rule.\n"
268 " -p, --pwd-not-required no password required for USER.\n"
269 " -P, --pwd-required password is required for USER.\n"
270 " -R, --reg-store-pwd enter password to store it in the registry for\n"
271 " later usage by services to be able to switch\n"
272 " to this user context with network credentials.\n"
274 "System operations:\n"
275 " -i, --inactive NUM set NUM of days before inactive accounts are disabled\n"
276 " (inactive accounts are those with expired passwords).\n"
277 " -n, --minage DAYS set system minimum password age to DAYS days.\n"
278 " -x, --maxage DAYS set system maximum password age to DAYS days.\n"
279 " -L, --length LEN set system minimum password length to LEN.\n"
282 " -d, --logonserver SERVER connect to SERVER (e.g. domain controller).\n"
283 " Default server is the local system, unless\n"
284 " changing the current user, in which case the\n"
285 " default is the content of $LOGONSERVER.\n"
286 " -S, --status display password status for USER (locked, expired,\n"
287 " etc.) plus global system password settings.\n"
288 " -h, --help output usage information and exit.\n"
289 " -v, --version output version information and exit.\n"
291 "If no option is given, change USER's password. If no user name is given,\n"
292 "operate on current user. System operations must not be mixed with user\n"
293 "operations. Don't specify a USER when triggering a system operation.\n"
295 "Don't specify a user or any other option together with the -R option.\n"
296 "Non-Admin users can only store their password if cygserver is running\n"
297 "as service under the SYSTEM account.\n"
298 "Note that storing even obfuscated passwords in the registry is not overly\n"
299 "secure. Use this feature only if the machine is adequately locked down.\n"
300 "Don't use this feature if you don't need network access within a remote\n"
301 "session. You can delete your stored password by using `passwd -R' and\n"
302 "specifying an empty password.\n"
304 "Report bugs to <cygwin@cygwin.com>\n", prog_name);
311 static int is_admin = -1;
315 SID_IDENTIFIER_AUTHORITY nt_auth = {SECURITY_NT_AUTHORITY};
322 if (OpenProcessToken (GetCurrentProcess (), TOKEN_READ, &token))
324 GetTokenInformation (token, TokenGroups, NULL, 0, &size);
325 grps = (PTOKEN_GROUPS) alloca (size);
326 if (!GetTokenInformation(token, TokenGroups, grps, size, &size)
327 || !AllocateAndInitializeSid (&nt_auth, 2,
328 SECURITY_BUILTIN_DOMAIN_RID,
329 DOMAIN_ALIAS_RID_ADMINS,
330 0, 0, 0, 0, 0, 0, &admin_grp))
334 for (i = 0; i < grps->GroupCount; ++i)
335 if (EqualSid (admin_grp, grps->Groups[i].Sid)
336 && (grps->Groups[i].Attributes
337 & (SE_GROUP_ENABLED | SE_GROUP_USE_FOR_DENY_ONLY))
354 const char *v = strchr (version, ':');
364 len = strchr (v, ' ') - v;
369 Copyright 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.\n\
371 ", prog_name, len, v, __DATE__);
375 main (int argc, char **argv)
377 char *c, *logonserver;
378 char user[UNLEN + 1], oldpwd[_PASSWORD_LEN + 1], newpwd[_PASSWORD_LEN + 1];
398 LPWSTR server = NULL;
400 prog_name = strrchr (argv[0], '/');
401 if (prog_name == NULL)
402 prog_name = strrchr (argv[0], '\\');
403 if (prog_name == NULL)
407 c = strrchr (prog_name, '.');
411 /* Use locale from environment. If not set or set to "C", use UTF-8. */
412 setlocale (LC_CTYPE, "");
413 if (!strcmp (setlocale (LC_CTYPE, NULL), "C"))
414 setlocale (LC_CTYPE, "en_US.UTF-8");
415 while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
423 if (lopt || uopt || copt || Copt || eopt || Eopt || popt || Popt || Sopt || Ropt)
425 if ((iarg = atoi (optarg)) < 0 || iarg > 999)
426 return eprint (1, "Force logout time must be between 0 and 999.");
430 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || uopt || Sopt || Ropt)
436 if (lopt || uopt || copt || Copt || eopt || Eopt || popt || Popt || Sopt || Ropt)
438 if ((narg = atoi (optarg)) < 0 || narg > 999)
439 return eprint (1, "Minimum password age must be between 0 and 999.");
440 if (xarg >= 0 && narg > xarg)
441 return eprint (1, "Minimum password age must be less than "
442 "maximum password age.");
446 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || lopt || Sopt || Ropt)
452 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || Sopt || Ropt)
458 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || Sopt || Ropt)
467 char *tmpbuf = alloca (strlen (optarg) + 3);
470 strcpy (tmpbuf, "\\\\");
471 strcat (tmpbuf, optarg);
472 size_t len = mbstowcs (NULL, tmpbuf, 0);
473 if (len > 0 && len != (size_t) -1)
474 mbstowcs (server = alloca ((len + 1) * sizeof (wchar_t)),
480 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || Sopt || Ropt)
486 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || Sopt || Ropt)
492 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || Sopt || Ropt)
498 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || Sopt || Ropt)
509 if (lopt || uopt || copt || Copt || eopt || Eopt || popt || Popt || Sopt || Ropt)
511 if ((xarg = atoi (optarg)) < 0 || xarg > 999)
512 return eprint (1, "Maximum password age must be between 0 and 999.");
513 if (narg >= 0 && xarg < narg)
514 return eprint (1, "Maximum password age must be greater than "
515 "minimum password age.");
519 if (lopt || uopt || copt || Copt || eopt || Eopt || popt || Popt || Sopt || Ropt)
521 if ((Larg = atoi (optarg)) < 0 || Larg > LM20_PWLEN)
522 return eprint (1, "Minimum password length must be between "
523 "0 and %d.", LM20_PWLEN);
527 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || lopt || uopt
528 || copt || Copt || eopt || Eopt || popt || Popt || Ropt)
534 if (xarg >= 0 || narg >= 0 || iarg >= 0 || Larg >= 0 || lopt || uopt
535 || copt || Copt || eopt || Eopt || popt || Popt || Sopt
550 "This functionality stores a password in the registry for usage by services\n"
551 "which need to change the user context and require network access. Typical\n"
552 "applications are interactive remote logons using sshd, cron task, etc.\n"
553 "This password will always tried first when any privileged application is\n"
554 "about to switch the user context.\n\n"
555 "Note that storing even obfuscated passwords in the registry is not overly\n"
556 "secure. Use this feature only if the machine is adequately locked down.\n"
557 "Don't use this feature if you don't need network access within a remote\n"
559 "You can delete your stored password by specifying an empty password.\n\n");
560 strcpy (newpwd, getpass ("Enter your current password: "));
561 if (strcmp (newpwd, getpass ("Re-enter your current password: ")))
562 eprint (0, "Password is not identical.");
563 else if (cygwin_internal (CW_SET_PRIV_KEY, newpwd))
564 return eprint (0, "Storing password failed: %s", strerror (errno));
568 if (Larg >= 0 || xarg >= 0 || narg >= 0 || iarg >= 0)
572 return SetModals (xarg, narg, iarg, Larg, server);
575 strcpy (user, optind >= argc ? getlogin () : argv[optind]);
577 /* Changing password for calling user? Use logonserver for user as well. */
578 if (!server && optind >= argc)
581 if ((logonserver = getenv ("LOGONSERVER")))
583 size_t len = mbstowcs (NULL, logonserver, 0);
584 if (len > 0 && len != (size_t) -1)
585 mbstowcs (server = alloca ((len + 1) * sizeof (wchar_t)),
586 logonserver, len + 1);
590 ui = GetPW (user, 1, server);
594 if (lopt || uopt || copt || Copt || eopt || Eopt || popt || Popt || Sopt)
598 uif.usri1008_flags = ui->usri3_flags;
601 if (ui->usri3_priv == USER_PRIV_ADMIN)
602 return eprint (0, "Locking an admin account is disallowed.");
603 uif.usri1008_flags |= UF_ACCOUNTDISABLE;
606 uif.usri1008_flags &= ~UF_ACCOUNTDISABLE;
608 uif.usri1008_flags |= UF_PASSWD_CANT_CHANGE;
610 uif.usri1008_flags &= ~UF_PASSWD_CANT_CHANGE;
612 uif.usri1008_flags |= UF_DONT_EXPIRE_PASSWD;
614 uif.usri1008_flags &= ~UF_DONT_EXPIRE_PASSWD;
616 uif.usri1008_flags |= UF_PASSWD_NOTREQD;
618 uif.usri1008_flags &= ~UF_PASSWD_NOTREQD;
620 if (lopt || uopt || copt || Copt || eopt || Eopt || popt || Popt)
622 ret = NetUserSetInfo (server, ui->usri3_name, 1008, (LPBYTE) &uif,
624 return EvalRet (ret, NULL);
627 PrintPW (ui, server);
631 if (!caller_is_admin () && !myself)
632 return eprint (0, "You may not change the password for %s.", user);
634 eprint (0, "Enter the new password (minimum of 5, maximum of 8 characters).");
635 eprint (0, "Please use a combination of upper and lower case letters and numbers.");
638 if (!caller_is_admin ())
640 strcpy (oldpwd, getpass ("Old password: "));
641 if (ChangePW (user, oldpwd, oldpwd, 1, server))
647 strcpy (newpwd, getpass ("New password: "));
648 if (strcmp (newpwd, getpass ("Re-enter new password: ")))
649 eprint (0, "Password is not identical.");
650 else if (! ChangePW (user, *oldpwd ? oldpwd : NULL, newpwd, 0, server))
652 if (! ret && cnt < 2)
653 eprint (0, "Try again.");
655 while (! ret && ++cnt < 3);