123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737 |
- /*++
- Copyright (c) 2015 Minoca Corp. All Rights Reserved
- Module Name:
- login.c
- Abstract:
- This module implements the login utility, which reads a user's password and
- sets up the login.
- Author:
- Evan Green 16-Mar-2015
- Environment:
- POSIX
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/lib/types.h>
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <getopt.h>
- #include <signal.h>
- #include <stdlib.h>
- #include <string.h>
- #include <syslog.h>
- #include <termios.h>
- #include <unistd.h>
- #include <utmpx.h>
- #include "../swlib.h"
- #include "lutil.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define LOGIN_VERSION_MAJOR 1
- #define LOGIN_VERSION_MINOR 0
- #define LOGIN_USAGE \
- "usage: login [options] [username] [ENV=var]\n" \
- "The login utility authenticates a user and establishes a new session. \n" \
- "Options are:\n" \
- " -f -- Do not perform authentication, user is preauthenticated." \
- " -h host -- Name of the remote host for this login.\n" \
- " -p -- Preserve the environment.\n" \
- " --help -- Displays this help text and exits.\n" \
- " --version -- Displays the application version and exits.\n"
- #define LOGIN_OPTIONS_STRING "fh:pHV"
- //
- // Define the number of seconds before login times out.
- //
- #define LOGIN_TIMEOUT 60
- #define LOGIN_ATTEMPT_COUNT 3
- #define LOGIN_MAX_EMPTY_USER_NAME_TRIES 5
- #define LOGIN_SECURE_TERMINALS_PATH "/etc/securetty"
- #define LOGIN_NOLOGIN_PATH "/etc/nologin"
- #define LOGIN_MOTD_PATH "/etc/motd"
- //
- // Define application options.
- //
- //
- // Set this option to skip authentication.
- //
- #define LOGIN_OPTION_NO_AUTHENTICATION 0x00000001
- //
- // Set this option to preserve the environment.
- //
- #define LOGIN_OPTION_PRESERVE_ENVIRONMENT 0x00000002
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- INT
- LoginCheckSecureTerminal (
- PSTR Terminal
- );
- INT
- LoginCheckNologin (
- VOID
- );
- VOID
- LoginPrintMessageOfTheDay (
- VOID
- );
- void
- LoginAlarmSignalHandler (
- int Signal
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- struct option LoginLongOptions[] = {
- {"help", no_argument, 0, 'H'},
- {"version", no_argument, 0, 'V'},
- {NULL, 0, 0, 0},
- };
- struct termios *SwLoginTerminalSettings = NULL;
- volatile BOOL SwLoginTimeout;
- //
- // ------------------------------------------------------------------ Functions
- //
- INT
- LoginMain (
- INT ArgumentCount,
- CHAR **Arguments
- )
- /*++
- Routine Description:
- This routine is the main entry point for the login 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 Argument;
- ULONG ArgumentIndex;
- ULONG Attempt;
- uid_t EffectiveUserId;
- ULONG EmptyUserNameCount;
- BOOL Failed;
- PSTR Host;
- PSTR Line;
- size_t LineBufferSize;
- ssize_t LineSize;
- INT Option;
- ULONG Options;
- ULONG SetupFlags;
- int Status;
- struct termios TerminalSettings;
- PSTR TtyName;
- struct passwd *User;
- uid_t UserId;
- PSTR UserName;
- Attempt = 0;
- EmptyUserNameCount = 0;
- Host = NULL;
- Line = NULL;
- LineBufferSize = 0;
- Options = 0;
- TtyName = NULL;
- UserName = NULL;
- //
- // Process the control arguments.
- //
- while (TRUE) {
- Option = getopt_long(ArgumentCount,
- Arguments,
- LOGIN_OPTIONS_STRING,
- LoginLongOptions,
- NULL);
- if (Option == -1) {
- break;
- }
- if ((Option == '?') || (Option == ':')) {
- Status = 1;
- goto MainEnd;
- }
- switch (Option) {
- case 'f':
- Options |= LOGIN_OPTION_NO_AUTHENTICATION;
- break;
- case 'p':
- Options |= LOGIN_OPTION_PRESERVE_ENVIRONMENT;
- break;
- case 'h':
- Host = optarg;
- break;
- case 'V':
- SwPrintVersion(LOGIN_VERSION_MAJOR, LOGIN_VERSION_MINOR);
- return 1;
- case 'H':
- printf(LOGIN_USAGE);
- return 1;
- default:
- assert(FALSE);
- Status = 1;
- goto MainEnd;
- }
- }
- ArgumentIndex = optind;
- if (ArgumentIndex > ArgumentCount) {
- ArgumentIndex = ArgumentCount;
- }
- EffectiveUserId = geteuid();
- UserId = getuid();
- if (EffectiveUserId != UserId) {
- SwSanitizeEnvironment();
- }
- //
- // Only root can skip authentication and set a host.
- //
- if (EffectiveUserId != 0) {
- Options &= ~LOGIN_OPTION_NO_AUTHENTICATION;
- Host = NULL;
- }
- //
- // Grab the username argument if it's there.
- //
- if (ArgumentIndex < ArgumentCount) {
- UserName = Arguments[ArgumentIndex];
- ArgumentIndex += 1;
- }
- if ((UserName == NULL) &&
- ((Options & LOGIN_OPTION_NO_AUTHENTICATION) != 0)) {
- SwPrintError(0, NULL, "Username required with -f");
- Status = 1;
- goto MainEnd;
- }
- //
- // Set environment values for the remainder of the arguments.
- //
- while (ArgumentIndex < ArgumentCount) {
- Argument = Arguments[ArgumentIndex];
- if (strchr(Argument, '=') == NULL) {
- SwPrintError(0, Argument, "Unexpected argument");
- Status = 1;
- goto MainEnd;
- }
- putenv(Argument);
- }
- if ((tcgetattr(STDIN_FILENO, &TerminalSettings) < 0) ||
- (isatty(STDOUT_FILENO) == 0)) {
- SwPrintError(0, NULL, "Not a terminal");
- Status = 1;
- goto MainEnd;
- }
- SwLoginTerminalSettings = &TerminalSettings;
- SwLoginTimeout = FALSE;
- signal(SIGINT, SIG_DFL);
- signal(SIGALRM, LoginAlarmSignalHandler);
- alarm(LOGIN_TIMEOUT);
- //
- // Get the current terminal name.
- //
- TtyName = ttyname(STDIN_FILENO);
- if (TtyName == NULL) {
- TtyName = "(unknown terminal)";
- }
- TtyName = strdup(TtyName);
- openlog("login", LOG_PID, LOG_AUTH);
- while (TRUE) {
- if (SwLoginTimeout != FALSE) {
- Status = 1;
- goto MainEnd;
- }
- //
- // Get rid of anything built up.
- //
- tcflush(0, TCIFLUSH);
- //
- // Read in the username if not already supplied.
- //
- if (UserName == NULL) {
- SwPrintLoginPrompt();
- LineSize = getline(&Line, &LineBufferSize, stdin);
- if (LineSize <= 0) {
- Status = 1;
- goto MainEnd;
- }
- while ((LineSize != 0) && (isspace(Line[LineSize - 1]))) {
- LineSize -= 1;
- }
- UserName = Line;
- UserName[LineSize] = '\0';
- //
- // Get past whitespace.
- //
- while (isspace(*UserName)) {
- UserName += 1;
- }
- if (*UserName == '\0') {
- EmptyUserNameCount += 1;
- if (EmptyUserNameCount >= LOGIN_MAX_EMPTY_USER_NAME_TRIES) {
- Status = 1;
- goto MainEnd;
- }
- UserName = NULL;
- continue;
- }
- }
- Failed = FALSE;
- User = getpwnam(UserName);
- if (User != NULL) {
- if ((Options & LOGIN_OPTION_NO_AUTHENTICATION) != 0) {
- break;
- }
- //
- // Root login may be restricted to a few terminals.
- //
- if ((User->pw_uid == 0) &&
- (LoginCheckSecureTerminal(TtyName) != 0)) {
- Failed = TRUE;
- }
- }
- if (Failed != FALSE) {
- Status = EPERM;
- } else {
- Status = SwGetAndCheckPassword(User, NULL);
- }
- if (Status == 0) {
- break;
- }
- //
- // Handle an authentication failure.
- //
- sleep(LOGIN_FAIL_DELAY);
- printf("Login incorrect\n");
- Attempt += 1;
- if (Attempt >= LOGIN_ATTEMPT_COUNT) {
- if (Host != NULL) {
- syslog(LOG_WARNING,
- "invalid password for %s on %s from %s",
- UserName,
- TtyName,
- Host);
- } else {
- syslog(LOG_WARNING,
- "invalid password for %s on %s",
- UserName,
- TtyName);
- }
- syslog(LOG_WARNING,
- "Authentication failure: uid=%ld, euid=%ld, tty=%s "
- "user=%s rhost=%s",
- (long int)UserId,
- (long int)EffectiveUserId,
- TtyName,
- UserName,
- Host);
- SwPrintError(0,
- NULL,
- "Maximum number of tries exceeded (%d)",
- LOGIN_ATTEMPT_COUNT);
- Status = 1;
- goto MainEnd;
- }
- }
- //
- // Authentication was successful.
- //
- alarm(0);
- if (User->pw_uid != 0) {
- if (LoginCheckNologin() != 0) {
- Status = 1;
- goto MainEnd;
- }
- }
- Status = fchown(STDIN_FILENO, User->pw_uid, User->pw_gid);
- if (Status != 0) {
- Status = errno;
- goto MainEnd;
- }
- fchmod(STDIN_FILENO, S_IRUSR | S_IWUSR);
- SwUpdateUtmp(getpid(), USER_PROCESS, TtyName, UserName, Host);
- SwBecomeUser(User);
- SetupFlags = SETUP_USER_ENVIRONMENT_CHANGE_ENVIRONMENT;
- if ((Options & LOGIN_OPTION_PRESERVE_ENVIRONMENT) == 0) {
- SetupFlags |= SETUP_USER_ENVIRONMENT_CLEAR_ENVIRONMENT;
- }
- SwSetupUserEnvironment(User, User->pw_shell, SetupFlags);
- LoginPrintMessageOfTheDay();
- if (Host != NULL) {
- syslog(LOG_INFO, "login as %s on %s from %s", UserName, TtyName, Host);
- if (User->pw_uid == 0) {
- syslog(LOG_INFO, "root login %s from %s", TtyName, Host);
- }
- } else {
- syslog(LOG_INFO, "login as %s on %s", UserName, TtyName);
- if (User->pw_uid == 0) {
- syslog(LOG_INFO, "root login on %s", TtyName);
- }
- }
- signal(SIGINT, SIG_DFL);
- closelog();
- SwCloseFrom(STDERR_FILENO + 1);
- SwExecuteShell(User->pw_shell, TRUE, NULL, NULL);
- Status = 1;
- MainEnd:
- closelog();
- if (Line != NULL) {
- free(Line);
- }
- SwLoginTerminalSettings = NULL;
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- INT
- LoginCheckSecureTerminal (
- PSTR Terminal
- )
- /*++
- Routine Description:
- This routine checks the secure terminals list. If the list exists
- (/etc/securetty) and this terminal is not on it, then root cannot log in.
- Arguments:
- Terminal - Supplies a pointer to the name of this terminal.
- Return Value:
- Returns an integer exit code. 0 for success, nonzero otherwise.
- --*/
- {
- PSTR Line;
- size_t LineBufferSize;
- ssize_t LineSize;
- PSTR SecureTerminal;
- FILE *SecureTerminals;
- INT Status;
- Line = NULL;
- LineBufferSize = 0;
- //
- // Open the file. If it does not exist, root can log in anywhere.
- //
- SecureTerminals = fopen(LOGIN_SECURE_TERMINALS_PATH, "r");
- if (SecureTerminals == NULL) {
- if (errno != ENOENT) {
- return EPERM;
- }
- return 0;
- }
- Status = EPERM;
- while (TRUE) {
- LineSize = getline(&Line, &LineBufferSize, stdin);
- if (LineSize < 0) {
- break;
- }
- while ((LineSize != 0) && (isspace(Line[LineSize - 1]))) {
- LineSize -= 1;
- }
- SecureTerminal = Line;
- SecureTerminal[LineSize] = '\0';
- //
- // Get past whitespace.
- //
- while (isspace(*SecureTerminal)) {
- SecureTerminal += 1;
- }
- //
- // Skip any commented lines.
- //
- if ((*SecureTerminal == '\0') || (*SecureTerminal == '#')) {
- continue;
- }
- if (strcmp(SecureTerminal, Terminal) == 0) {
- Status = 0;
- break;
- }
- }
- if (Line != NULL) {
- free(Line);
- }
- fclose(SecureTerminals);
- return Status;
- }
- INT
- LoginCheckNologin (
- VOID
- )
- /*++
- Routine Description:
- This routine checks /etc/nologin, and prevents logging anyone but root in
- if the file exists.
- Arguments:
- None.
- Return Value:
- 0 if login should proceed.
- Non-zero if login is prevented.
- --*/
- {
- INT Character;
- BOOL Empty;
- FILE *Nologin;
- Nologin = fopen(LOGIN_NOLOGIN_PATH, "r");
- if (Nologin == NULL) {
- return 0;
- }
- //
- // Spit out the contents of the nologin file.
- //
- Empty = TRUE;
- while (TRUE) {
- Character = fgetc(Nologin);
- if (Character == EOF) {
- break;
- }
- if (Character == '\n') {
- fputc('\r', stdout);
- }
- fputc(Character, stdout);
- Empty = FALSE;
- }
- if (Empty != FALSE) {
- printf("\r\nSystem temporarily closed.\r\n");
- }
- fclose(Nologin);
- fflush(NULL);
- tcdrain(STDOUT_FILENO);
- return 1;
- }
- VOID
- LoginPrintMessageOfTheDay (
- VOID
- )
- /*++
- Routine Description:
- This routine is the timeout alarm signal handler.
- Arguments:
- Signal - Supplies the signal number that fired.
- Return Value:
- None.
- --*/
- {
- int Character;
- FILE *File;
- File = fopen(LOGIN_MOTD_PATH, "r");
- if (File == NULL) {
- return;
- }
- while (TRUE) {
- Character = fgetc(File);
- if (Character == EOF) {
- break;
- }
- fputc(Character, stdout);
- }
- fclose(File);
- return;
- }
- void
- LoginAlarmSignalHandler (
- int Signal
- )
- /*++
- Routine Description:
- This routine is the timeout alarm signal handler.
- Arguments:
- Signal - Supplies the signal number that fired.
- Return Value:
- None.
- --*/
- {
- int Flags;
- Flags = fcntl(STDOUT_FILENO, F_GETFL);
- fcntl(STDOUT_FILENO, F_SETFL, Flags | O_NONBLOCK);
- tcsetattr(STDOUT_FILENO, TCSANOW, SwLoginTerminalSettings);
- printf("\r\nLogin timed out after %u seconds.\r\n", LOGIN_TIMEOUT);
- fflush(NULL);
- fcntl(STDOUT_FILENO, F_SETFL, Flags);
- _Exit(1);
- return;
- }
|