123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714 |
- /*++
- Copyright (c) 2015 Minoca Corp. All Rights Reserved
- Module Name:
- passwd.c
- Abstract:
- This module implements the passwd utility, which allows a user to change
- his or her password (or the superuser to change any password).
- Author:
- Evan Green 11-Mar-2015
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <dirent.h>
- #include <errno.h>
- #include <getopt.h>
- #include <stdlib.h>
- #include <string.h>
- #include <syslog.h>
- #include <unistd.h>
- #include "../swlib.h"
- #include "lutil.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define PASSWD_VERSION_MAJOR 1
- #define PASSWD_VERSION_MINOR 0
- #define PASSWD_USAGE \
- "usage: passwd [options] username\n" \
- "The passwd utility allows a user to change his or her password, or \n" \
- "allows the superuser to change any password. Options are:\n" \
- " -A, --algorithm -- Specifies the password algorighm to use.\n" \
- " The default is SHA512.\n" \
- " -d, --delete -- Delete a user's password (make it empty). This means\n" \
- " no password is necessary to log in to the account.\n" \
- " -l, --lock -- Lock the password, disabling password-based\n " \
- " authentication to this account.\n" \
- " -R, --root=dir -- Chroot into the given directory before operation.\n" \
- " -u, --unlock -- Unlock the password.\n" \
- " --help -- Displays this help text and exits.\n" \
- " --version -- Displays the application version and exits.\n"
- #define PASSWD_OPTIONS_STRING "A:dlR:uHV"
- //
- // Define application options.
- //
- //
- // Set this option to delete the password.
- //
- #define PASSWD_OPTION_DELETE 0x00000001
- //
- // Set this option to lock the password.
- //
- #define PASSWD_OPTION_LOCK 0x00000002
- //
- // Set this option to unlock the password.
- //
- #define PASSWD_OPTION_UNLOCK 0x00000004
- //
- // Define the set of password options that need root.
- //
- #define PASSWD_OPTIONS_ROOT \
- (PASSWD_OPTION_DELETE | PASSWD_OPTION_LOCK | PASSWD_OPTION_UNLOCK)
- //
- // Define the number of times to attempt to get a matching and different new
- // password.
- //
- #define PASSWD_NEW_ATTEMPTS 3
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- PSTR
- PasswdGetNewPassword (
- struct passwd *User,
- struct spwd *Shadow,
- uid_t CurrentUid,
- PSTR Algorithm
- );
- VOID
- PasswdLogMessage (
- int Priority,
- PSTR Format,
- ...
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option PasswdLongOptions[] = {
- {"algorithm", required_argument, 0, 'A'},
- {"delete", no_argument, 0, 'd'},
- {"lock", no_argument, 0, 'l'},
- {"root", required_argument, 0, 'R'},
- {"unlock", no_argument, 0, 'u'},
- {"help", no_argument, 0, 'H'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- PasswdMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the passwd utility.
- Arguments:
- ArgumentCount - Supplies the number of command line arguments the program
- was invoked with.
- Arguments - Supplies a tokenized array of command line arguments.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- PSTR Algorithm;
- PPASSWD_ALGORITHM AlgorithmEntry;
- ULONG ArgumentIndex;
- size_t Length;
- struct spwd LocalShadow;
- PSTR NewPassword;
- PSTR OldPassword;
- INT Option;
- ULONG Options;
- PSTR RootDirectory;
- struct spwd *Shadow;
- UPDATE_PASSWORD_OPERATION ShadowOperation;
- int Status;
- PSTR ThisUserName;
- int TotalStatus;
- struct passwd *User;
- uid_t UserId;
- PSTR UserName;
- Algorithm = PASSWD_DEFAULT_ALGORITHM;
- NewPassword = NULL;
- Options = 0;
- RootDirectory = NULL;
- ShadowOperation = UpdatePasswordUpdateLine;
- ThisUserName = NULL;
- TotalStatus = 0;
- UserName = NULL;
- //
- // Process the control arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- PASSWD_OPTIONS_STRING,
- PasswdLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- //
- // Select a new password hashing algorithm.
- //
- case 'A':
- if (strcasecmp(optarg, "des") == 0) {
- SwPrintError(0, NULL, "The DES algorithm has been deprecated");
- Status = 1;
- goto MainEnd;
- }
- AlgorithmEntry = &(SwPasswordAlgorithms[0]);
- while (AlgorithmEntry->Name != NULL) {
- if (strcasecmp(optarg, AlgorithmEntry->Name) == 0) {
- Algorithm = AlgorithmEntry->Id;
- break;
- }
- AlgorithmEntry += 1;
- }
- if (AlgorithmEntry->Name == NULL) {
- SwPrintError(0, optarg, "Unknown algorithm");
- Status = 1;
- goto MainEnd;
- }
- break;
- case 'd':
- Options |= PASSWD_OPTION_DELETE;
- break;
- case 'l':
- Options |= PASSWD_OPTION_LOCK;
- Options &= ~PASSWD_OPTION_UNLOCK;
- break;
- case 'R':
- RootDirectory = optarg;
- break;
- case 'u':
- Options |= PASSWD_OPTION_UNLOCK;
- Options &= ~PASSWD_OPTION_LOCK;
- break;
- case 'V':
- SwPrintVersion(PASSWD_VERSION_MAJOR, PASSWD_VERSION_MINOR);
- return 1;
- case 'H':
- printf(PASSWD_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- ArgumentIndex = optind;
- if (ArgumentIndex > ArgumentCount) {
- ArgumentIndex = ArgumentCount;
- }
- //
- // Chroot if requested. Warm up libcrypt in case it's not in the chrooted
- // environment.
- //
- if (RootDirectory != NULL) {
- SwCrypt(NULL, NULL);
- Status = chroot(RootDirectory);
- if (Status != 0) {
- Status = errno;
- SwPrintError(Status, RootDirectory, "Failed to chroot");
- goto MainEnd;
- }
- Status = chdir("/");
- if (Status != 0) {
- Status = errno;
- SwPrintError(Status, RootDirectory, "Failed to chdir");
- goto MainEnd;
- }
- }
- if (ArgumentIndex < ArgumentCount) {
- UserName = Arguments[ArgumentIndex];
- ArgumentIndex += 1;
- }
- if (ArgumentIndex != ArgumentCount) {
- SwPrintError(0, NULL, "Unexpected additional arguments");
- return 1;
- }
- openlog("passwd", 0, LOG_AUTH);
- UserId = getuid();
- //
- // Only root can delete, lock, or unlock a password.
- //
- if (UserId != 0) {
- if ((Options & PASSWD_OPTIONS_ROOT) != 0) {
- SwPrintError(0, NULL, "-l, -u, and -d require root privileges");
- Status = 1;
- goto MainEnd;
- }
- }
- //
- // Get the current user's name.
- //
- User = getpwuid(UserId);
- if (User == NULL) {
- SwPrintError(0, NULL, "User %ld not found", (long int)UserId);
- Status = ENOENT;
- goto MainEnd;
- }
- ThisUserName = strdup(User->pw_name);
- if (ThisUserName == NULL) {
- Status = ENOMEM;
- goto MainEnd;
- }
- //
- // If the user specified a login name on the command line, get that one.
- //
- if (UserName != NULL) {
- User = getpwnam(UserName);
- }
- //
- // The user can only change their own password, except for the superuser.
- //
- if ((UserId != 0) && (User->pw_uid != UserId)) {
- PasswdLogMessage(LOG_WARNING,
- "passwd: User %s cannot change password for "
- "account %s",
- ThisUserName,
- User->pw_name);
- Status = EPERM;
- goto MainEnd;
- }
- //
- // Get the shadow data.
- //
- errno = 0;
- Shadow = getspnam(User->pw_name);
- if ((Shadow == NULL) && (errno != ENOENT)) {
- Status = errno;
- PasswdLogMessage(LOG_WARNING,
- "passwd: warning: No shadow record of user %s, "
- "creating one",
- User->pw_name,
- strerror(Status));
- memcpy(&LocalShadow, &SwShadowTemplate, sizeof(struct spwd));
- LocalShadow.sp_namp = User->pw_name;
- LocalShadow.sp_lstchg = time(NULL) / (3600 * 24);
- Shadow = &LocalShadow;
- ShadowOperation = UpdatePasswordAddLine;
- }
- if (Shadow != NULL) {
- OldPassword = Shadow->sp_pwdp;
- } else {
- OldPassword = User->pw_passwd;
- }
- //
- // Potentially lock the password.
- //
- if ((Options & PASSWD_OPTION_LOCK) != 0) {
- if (OldPassword[0] != '!') {
- Length = strlen(OldPassword) + 2;
- NewPassword = malloc(Length);
- if (NewPassword != NULL) {
- NewPassword[0] = '!';
- strncpy(NewPassword + 1, OldPassword, Length - 1);
- }
- }
- //
- // Potentially unlock the password.
- //
- } else if ((Options & PASSWD_OPTION_UNLOCK) != 0) {
- if (OldPassword[0] == '!') {
- NewPassword = strdup(OldPassword + 1);
- }
- } else if ((Options & PASSWD_OPTION_DELETE) != 0) {
- NewPassword = strdup("");
- //
- // This is not a lock, unlock, or delete, just a regular password change.
- //
- } else {
- if ((UserId != 0) && (OldPassword[0] == '!')) {
- PasswdLogMessage(LOG_WARNING,
- "passwd: Cannot change password for %s: "
- "Account locked",
- User->pw_name);
- Status = EPERM;
- goto MainEnd;
- }
- NewPassword = PasswdGetNewPassword(User, Shadow, UserId, Algorithm);
- }
- if (NewPassword == NULL) {
- SwPrintError(0,
- NULL,
- "passwd: Password for %s is unchanged",
- User->pw_name);
- Status = 1;
- goto MainEnd;
- }
- if (Shadow != NULL) {
- Shadow->sp_pwdp = NewPassword;
- User->pw_passwd = PASSWORD_SHADOWED;
- Shadow->sp_lstchg = time(NULL) / (3600 * 24);
- } else {
- User->pw_passwd = NewPassword;
- }
- Status = SwUpdatePasswordLine(User, Shadow, ShadowOperation);
- if (Status < 0) {
- PasswdLogMessage(LOG_ERR,
- "passwd: Unable to change password for %s: %s",
- User->pw_name,
- strerror(Status));
- } else {
- PasswdLogMessage(LOG_NOTICE,
- "passwd: Password changed for user %s",
- User->pw_name);
- }
- MainEnd:
- closelog();
- if (NewPassword != NULL) {
- memset(NewPassword, 0, strlen(NewPassword));
- free(NewPassword);
- }
- if (ThisUserName != NULL) {
- free(ThisUserName);
- }
- if ((TotalStatus == 0) && (Status != 0)) {
- TotalStatus = Status;
- }
- return TotalStatus;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- PSTR
- PasswdGetNewPassword (
- struct passwd *User,
- struct spwd *Shadow,
- uid_t CurrentUid,
- PSTR Algorithm
- )
- /*++
- Routine Description:
- This routine reads a new password in for the user.
- Arguments:
- User - Supplies a pointer to the user structure.
- Shadow - Supplies an optional pointer to the shadow structure.
- CurrentUid - Supplies the current running user ID.
- Algorithm - Supplies a pointer to the algorithm to use.
- Return Value:
- Returns a pointer to an allocated hashed password on success.
- NULL on failure.
- --*/
- {
- UINTN Attempt;
- BOOL Correct;
- PSTR CurrentPassword;
- int Match;
- PSTR NewHashedPassword;
- PSTR NewHashedPasswordCopy;
- PSTR NewPassword;
- PSTR NewPasswordCopy;
- PSTR OldPasswordHash;
- if (Shadow != NULL) {
- OldPasswordHash = Shadow->sp_pwdp;
- } else {
- OldPasswordHash = User->pw_passwd;
- }
- //
- // Validate the old password first.
- //
- if ((CurrentUid != 0) && (OldPasswordHash != NULL) &&
- (OldPasswordHash[0] != '\0')) {
- CurrentPassword = getpass("Old password: ");
- if (CurrentPassword == NULL) {
- return NULL;
- }
- Correct = SwCheckPassword(CurrentPassword, OldPasswordHash);
- //
- // Zero out the password buffer.
- //
- memset(CurrentPassword, 0, strlen(CurrentPassword));
- if (Correct == FALSE) {
- sleep(LOGIN_FAIL_DELAY);
- PasswdLogMessage(LOG_WARNING,
- "Incorrect password for %s",
- User->pw_name);
- return NULL;
- }
- }
- for (Attempt = 0; Attempt < PASSWD_NEW_ATTEMPTS; Attempt += 1) {
- //
- // Ask for the new password.
- //
- NewPassword = getpass("New password: ");
- if (NewPassword == NULL) {
- return NULL;
- }
- NewPasswordCopy = strdup(NewPassword);
- memset(NewPassword, 0, strlen(NewPassword));
- if (NewPasswordCopy == NULL) {
- return NULL;
- }
- //
- // Ask for it again.
- //
- NewPassword = getpass("Retype new password: ");
- if (NewPassword == NULL) {
- memset(NewPasswordCopy, 0, strlen(NewPasswordCopy));
- free(NewPasswordCopy);
- NewPasswordCopy = NULL;
- return NULL;
- }
- //
- // Complain if they don't match, it's the same as the old password,
- // or it's empty.
- //
- Match = strcmp(NewPassword, NewPasswordCopy);
- memset(NewPassword, 0, strlen(NewPassword));
- Correct = SwCheckPassword(NewPasswordCopy, OldPasswordHash);
- if ((Match != 0) ||
- (Correct != FALSE) ||
- (NewPasswordCopy[0] == '\0')) {
- if (Match != 0) {
- SwPrintError(0, NULL, "Passwords don't match");
- } else if (NewPasswordCopy[0] == '\0') {
- SwPrintError(0,
- NULL,
- "Error: Password is empty, use -d to delete a "
- "password");
- } else {
- SwPrintError(0,
- NULL,
- "New password is the same as the old one");
- }
- memset(NewPasswordCopy, 0, strlen(NewPasswordCopy));
- free(NewPasswordCopy);
- NewPasswordCopy = NULL;
- continue;
- }
- break;
- }
- //
- // Hash up the password.
- //
- NewHashedPassword = NULL;
- NewHashedPasswordCopy = NULL;
- if (NewPasswordCopy != NULL) {
- NewHashedPassword = SwCreateHashedPassword(Algorithm,
- -1,
- 0,
- NewPasswordCopy);
- memset(NewPasswordCopy, 0, strlen(NewPasswordCopy));
- free(NewPasswordCopy);
- //
- // Return a copy of that hash to get it out of the static buffer.
- //
- NewHashedPasswordCopy = strdup(NewHashedPassword);
- memset(NewHashedPassword, 0, strlen(NewHashedPassword));
- }
- return NewHashedPasswordCopy;
- }
- VOID
- PasswdLogMessage (
- int Priority,
- PSTR Format,
- ...
- )
- /*++
- Routine Description:
- This routine logs a message to both stderr and the syslog.
- Arguments:
- Priority - Supplies the priority of the log message. See LOG_* definitions.
- Format - Supplies the printf-style format string.
- ... - Supplies the additional arguments.
- Return Value:
- None.
- --*/
- {
- va_list ArgumentList;
- va_start(ArgumentList, Format);
- vsyslog(Priority, Format, ArgumentList);
- va_end(ArgumentList);
- va_start(ArgumentList, Format);
- vfprintf(stderr, Format, ArgumentList);
- va_end(ArgumentList);
- fputc('\n', stderr);
- return;
- }
|