Ignore:
Timestamp:
Nov 4, 2007, 3:16:40 AM (16 years ago)
Author:
Bruno Cornec
Message:

Update to busybox 1.7.2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.5/mindi-busybox/loginutils/login.c

    r821 r1765  
    11/* vi: set sw=4 ts=4: */
    2 #include <fcntl.h>
    3 #include <signal.h>
    4 #include <stdio.h>
    5 #include <stdlib.h>
    6 #include <string.h>
    7 #include <syslog.h>
    8 #include <termios.h>
    9 #include <unistd.h>
     2/*
     3 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     4 */
     5
     6#include "libbb.h"
    107#include <utmp.h>
    118#include <sys/resource.h>
    12 #include <sys/stat.h>
    13 #include <sys/types.h>
    14 #include <ctype.h>
    15 #include <time.h>
    16 
    17 #include "busybox.h"
    18 #ifdef CONFIG_SELINUX
     9#include <syslog.h>
     10
     11#if ENABLE_SELINUX
    1912#include <selinux/selinux.h>  /* for is_selinux_enabled()  */
    2013#include <selinux/get_context_list.h> /* for get_default_context() */
    2114#include <selinux/flask.h> /* for security class definitions  */
    22 #include <errno.h>
    23 #endif
    24 
    25 #ifdef CONFIG_FEATURE_UTMP
    26 // import from utmp.c
    27 static void checkutmp(int picky);
    28 static void setutmp(const char *name, const char *line);
    29 /* Stuff global to this file */
    30 static struct utmp utent;
    31 #endif
    32 
    33 // login defines
    34 #define TIMEOUT       60
    35 #define EMPTY_USERNAME_COUNT    10
    36 #define USERNAME_SIZE 32
    37 
    38 
    39 static int check_nologin ( int amroot );
    40 
    41 #if defined CONFIG_FEATURE_SECURETTY
    42 static int check_tty ( const char *tty );
    43 
    44 #else
    45 static inline int check_tty ( const char *tty )  { return 1; }
    46 
    47 #endif
    48 
    49 static int is_my_tty ( const char *tty );
    50 static int login_prompt ( char *buf_name );
    51 static void motd ( void );
    52 
    53 
    54 static void alarm_handler ( int sig ATTRIBUTE_UNUSED)
    55 {
    56     fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT );
    57     exit ( EXIT_SUCCESS );
    58 }
    59 
    60 
    61 int login_main(int argc, char **argv)
    62 {
    63     char tty[BUFSIZ];
    64     char full_tty[200];
    65     char fromhost[512];
    66     char username[USERNAME_SIZE];
    67     const char *tmp;
    68     int amroot;
    69     int flag;
    70     int failed;
    71     int count=0;
    72     struct passwd *pw, pw_copy;
    73 #ifdef CONFIG_WHEEL_GROUP
    74     struct group *grp;
    75 #endif
    76     int opt_preserve = 0;
    77     int opt_fflag = 0;
    78     char *opt_host = 0;
    79     int alarmstarted = 0;
    80 #ifdef CONFIG_SELINUX
    81     security_context_t user_sid = NULL;
    82 #endif
    83 
    84     username[0]=0;
    85     amroot = ( getuid ( ) == 0 );
    86     signal ( SIGALRM, alarm_handler );
    87     alarm ( TIMEOUT );
    88     alarmstarted = 1;
    89 
    90     while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
    91         switch ( flag ) {
    92         case 'p':
    93             opt_preserve = 1;
    94             break;
    95         case 'f':
    96             /*
    97              * username must be a separate token
    98              * (-f root, *NOT* -froot). --marekm
    99              */
    100             if ( optarg != argv[optind-1] )
    101                 bb_show_usage( );
    102 
    103             if ( !amroot )      /* Auth bypass only if real UID is zero */
    104                 bb_error_msg_and_die ( "-f permission denied" );
    105 
    106             safe_strncpy(username, optarg, USERNAME_SIZE);
    107             opt_fflag = 1;
    108             break;
    109         case 'h':
    110             opt_host = optarg;
    111             break;
    112         default:
    113             bb_show_usage( );
    114         }
    115     }
    116 
    117     if (optind < argc)             // user from command line (getty)
    118         safe_strncpy(username, argv[optind], USERNAME_SIZE);
    119 
    120     if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 ))
    121         return EXIT_FAILURE;        /* Must be a terminal */
    122 
    123 #ifdef CONFIG_FEATURE_UTMP
    124     checkutmp ( !amroot );
    125 #endif
    126 
    127     tmp = ttyname ( 0 );
    128     if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 ))
    129         safe_strncpy ( tty, tmp + 5, sizeof( tty ));
    130     else if ( tmp && *tmp == '/' )
    131         safe_strncpy ( tty, tmp, sizeof( tty ));
    132     else
    133         safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
    134 
    135 #ifdef CONFIG_FEATURE_UTMP
    136     if ( amroot )
    137         memset ( utent.ut_host, 0, sizeof utent.ut_host );
    138 #endif
    139 
    140     if ( opt_host ) {
    141 #ifdef CONFIG_FEATURE_UTMP
    142         safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
    143 #endif
    144         snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host );
    145     }
    146     else
    147         snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty );
    148 
    149     bb_setpgrp;
    150 
    151     openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH );
    152 
    153     while ( 1 ) {
    154         failed = 0;
    155 
    156         if ( !username[0] )
    157             if(!login_prompt ( username ))
    158                 return EXIT_FAILURE;
    159 
    160         if ( !alarmstarted && ( TIMEOUT > 0 )) {
    161             alarm ( TIMEOUT );
    162             alarmstarted = 1;
    163         }
    164 
    165         if (!( pw = getpwnam ( username ))) {
    166             pw_copy.pw_name   = "UNKNOWN";
    167             pw_copy.pw_passwd = "!";
    168             opt_fflag = 0;
    169             failed = 1;
    170         } else
    171             pw_copy = *pw;
    172 
    173         pw = &pw_copy;
    174 
    175         if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' ))
    176             failed = 1;
    177 
    178         if ( opt_fflag ) {
    179             opt_fflag = 0;
    180             goto auth_ok;
    181         }
    182 
    183         if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty )))
    184             failed = 1;
    185 
    186         /* Don't check the password if password entry is empty (!) */
    187         if ( !pw-> pw_passwd[0] )
    188             goto auth_ok;
    189 
    190         /* authorization takes place here */
    191         if ( correct_password ( pw ))
    192             goto auth_ok;
    193 
    194         failed = 1;
    195 
    196 auth_ok:
    197         if ( !failed)
    198             break;
    199 
    200         bb_do_delay(FAIL_DELAY);
    201         puts("Login incorrect");
    202         username[0] = 0;
    203         if ( ++count == 3 ) {
    204             syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
    205             return EXIT_FAILURE;
    206     }
    207     }
    208 
    209     alarm ( 0 );
    210     if ( check_nologin ( pw-> pw_uid == 0 ))
    211         return EXIT_FAILURE;
    212 
    213 #ifdef CONFIG_FEATURE_UTMP
    214     setutmp ( username, tty );
    215 #endif
    216 
    217     if ( *tty != '/' )
    218         snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
    219     else
    220         safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
    221 
    222 #ifdef CONFIG_SELINUX
    223     if (is_selinux_enabled())
    224     {
    225         security_context_t old_tty_sid, new_tty_sid;
    226 
    227         if (get_default_context(username, NULL, &user_sid))
    228         {
    229             fprintf(stderr, "Unable to get SID for %s\n", username);
    230             exit(1);
    231         }
    232         if (getfilecon(full_tty, &old_tty_sid) < 0)
    233         {
    234             fprintf(stderr, "getfilecon(%.100s) failed: %.100s\n", full_tty, strerror(errno));
    235             return EXIT_FAILURE;
    236         }
    237         if (security_compute_relabel(user_sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
    238         {
    239             fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", full_tty, strerror(errno));
    240             return EXIT_FAILURE;
    241         }
    242         if(setfilecon(full_tty, new_tty_sid) != 0)
    243         {
    244             fprintf(stderr, "chsid(%.100s, %s) failed: %.100s\n", full_tty, new_tty_sid, strerror(errno));
    245             return EXIT_FAILURE;
    246         }
    247     }
    248 #endif
    249     if ( !is_my_tty ( full_tty ))
    250         syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
    251 
    252     /* Try these, but don't complain if they fail
    253      * (for example when the root fs is read only) */
    254     chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
    255     chmod ( full_tty, 0600 );
    256 
    257     change_identity ( pw );
    258     tmp = pw-> pw_shell;
    259     if(!tmp || !*tmp)
    260         tmp = DEFAULT_SHELL;
    261     setup_environment ( tmp, 1, !opt_preserve, pw );
    262 
    263     motd ( );
    264     signal ( SIGALRM, SIG_DFL );    /* default alarm signal */
    265 
    266     if ( pw-> pw_uid == 0 )
    267         syslog ( LOG_INFO, "root login %s\n", fromhost );
    268 #ifdef CONFIG_SELINUX
    269     /* well, a simple setexeccon() here would do the job as well,
    270      * but let's play the game for now */
    271     set_current_security_context(user_sid);
    272 #endif
    273     run_shell ( tmp, 1, 0, 0);  /* exec the shell finally. */
    274 
    275     return EXIT_FAILURE;
    276 }
    277 
    278 
    279 
    280 static int login_prompt ( char *buf_name )
    281 {
    282     char buf [1024];
    283     char *sp, *ep;
    284     int i;
    285 
    286     for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
    287         print_login_prompt();
    288 
    289         if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
    290             return 0;
    291 
    292         if ( !strchr ( buf, '\n' ))
    293             return 0;
    294 
    295         for ( sp = buf; isspace ( *sp ); sp++ ) { }
    296         for ( ep = sp; isgraph ( *ep ); ep++ ) { }
    297 
    298         *ep = 0;
    299         safe_strncpy(buf_name, sp, USERNAME_SIZE);
    300         if(buf_name[0])
    301             return 1;
    302     }
    303     return 0;
    304 }
    305 
    306 
    307 static int check_nologin ( int amroot )
    308 {
    309     if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
    310         FILE *fp;
    311         int c;
    312 
    313         if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
    314             while (( c = getc ( fp )) != EOF )
    315                 putchar (( c == '\n' ) ? '\r' : c );
    316 
    317             fflush ( stdout );
    318             fclose ( fp );
    319         } else {
    320             puts ( "\r\nSystem closed for routine maintenance.\r" );
    321         }
    322         if ( !amroot )
    323             return 1;
    324 
    325         puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
    326     }
    327     return 0;
    328 }
    329 
    330 #ifdef CONFIG_FEATURE_SECURETTY
    331 
    332 static int check_tty ( const char *tty )
    333 {
    334     FILE *fp;
    335     int i;
    336     char buf[BUFSIZ];
    337 
    338     if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
    339         while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
    340             for ( i = strlen( buf ) - 1; i >= 0; --i ) {
    341                 if ( !isspace ( buf[i] ))
    342                     break;
    343             }
    344             buf[++i] = '\0';
    345             if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
    346                 continue;
    347 
    348             if ( strcmp ( buf, tty ) == 0 ) {
    349                 fclose ( fp );
    350                 return 1;
    351             }
    352         }
    353         fclose(fp);
    354         return 0;
    355     }
    356     /* A missing securetty file is not an error. */
    357     return 1;
    358 }
    359 
    360 #endif
    361 
    362 /* returns 1 if true */
    363 static int is_my_tty ( const char *tty )
    364 {
    365     struct stat by_name, by_fd;
    366 
    367     if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
    368         return 0;
    369 
    370     if ( by_name. st_rdev != by_fd. st_rdev )
    371         return 0;
    372     else
    373         return 1;
    374 }
    375 
    376 
    377 static void motd (void)
    378 {
    379     FILE *fp;
    380     register int c;
    381 
    382     if (( fp = fopen ( bb_path_motd_file, "r" ))) {
    383         while (( c = getc ( fp )) != EOF )
    384             putchar ( c );
    385         fclose ( fp );
    386     }
    387 }
    388 
    389 
    390 #ifdef CONFIG_FEATURE_UTMP
    391 // vv  Taken from tinylogin utmp.c  vv
    392 
    393 #define NO_UTENT \
    394     "No utmp entry.  You must exec \"login\" from the lowest level \"sh\""
    395 #define NO_TTY \
    396     "Unable to determine your tty name."
    397 
     15#endif
     16
     17#if ENABLE_PAM
     18/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
     19#undef setlocale
     20/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx.
     21 * Apparently they like to confuse people. */
     22#include <security/pam_appl.h>
     23#include <security/pam_misc.h>
     24static const struct pam_conv conv = {
     25    misc_conv,
     26    NULL
     27};
     28#endif
     29
     30enum {
     31    TIMEOUT = 60,
     32    EMPTY_USERNAME_COUNT = 10,
     33    USERNAME_SIZE = 32,
     34    TTYNAME_SIZE = 32,
     35};
     36
     37static char* short_tty;
     38
     39#if ENABLE_FEATURE_UTMP
     40/* vv  Taken from tinylogin utmp.c  vv */
    39841/*
    399  * checkutmp - see if utmp file is correct for this process
     42 * read_or_build_utent - see if utmp file is correct for this process
    40043 *
    40144 *  System V is very picky about the contents of the utmp file
     
    41053 */
    41154
    412 static void checkutmp(int picky)
    413 {
    414     char *line;
     55static void read_or_build_utent(struct utmp *utptr, int picky)
     56{
    41557    struct utmp *ut;
    41658    pid_t pid = getpid();
     
    42163    while ((ut = getutent()))
    42264        if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
    423             (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
     65        (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
    42466            break;
    42567
    42668    /* If there is one, just use it, otherwise create a new one.  */
    42769    if (ut) {
    428         utent = *ut;
     70        *utptr = *ut;
    42971    } else {
    430         time_t t_tmp;
    431        
    432         if (picky) {
    433             puts(NO_UTENT);
    434             exit(1);
    435         }
    436         line = ttyname(0);
    437         if (!line) {
    438             puts(NO_TTY);
    439             exit(1);
    440         }
    441         if (strncmp(line, "/dev/", 5) == 0)
    442             line += 5;
    443         memset((void *) &utent, 0, sizeof utent);
    444         utent.ut_type = LOGIN_PROCESS;
    445         utent.ut_pid = pid;
    446         strncpy(utent.ut_line, line, sizeof utent.ut_line);
    447         /* XXX - assumes /dev/tty?? */
    448         strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
    449         strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
    450         t_tmp = (time_t)utent.ut_time;
    451         time(&t_tmp);
    452     }
     72        if (picky)
     73            bb_error_msg_and_die("no utmp entry found");
     74
     75        memset(utptr, 0, sizeof(*utptr));
     76        utptr->ut_type = LOGIN_PROCESS;
     77        utptr->ut_pid = pid;
     78        strncpy(utptr->ut_line, short_tty, sizeof(utptr->ut_line));
     79        /* This one is only 4 chars wide. Try to fit something
     80         * remotely meaningful by skipping "tty"... */
     81        strncpy(utptr->ut_id, short_tty + 3, sizeof(utptr->ut_id));
     82        strncpy(utptr->ut_user, "LOGIN", sizeof(utptr->ut_user));
     83        utptr->ut_time = time(NULL);
     84    }
     85    if (!picky) /* root login */
     86        memset(utptr->ut_host, 0, sizeof(utptr->ut_host));
    45387}
    45488
    45589/*
    456  * setutmp - put a USER_PROCESS entry in the utmp file
     90 * write_utent - put a USER_PROCESS entry in the utmp file
    45791 *
    458  *  setutmp changes the type of the current utmp entry to
     92 *  write_utent changes the type of the current utmp entry to
    45993 *  USER_PROCESS.  the wtmp file will be updated as well.
    46094 */
    461 
    462 static void setutmp(const char *name, const char *line ATTRIBUTE_UNUSED)
    463 {
    464     time_t t_tmp = (time_t)utent.ut_time;
    465 
    466     utent.ut_type = USER_PROCESS;
    467     strncpy(utent.ut_user, name, sizeof utent.ut_user);
    468     time(&t_tmp);
    469     /* other fields already filled in by checkutmp above */
     95static void write_utent(struct utmp *utptr, const char *username)
     96{
     97    utptr->ut_type = USER_PROCESS;
     98    strncpy(utptr->ut_user, username, sizeof(utptr->ut_user));
     99    utptr->ut_time = time(NULL);
     100    /* other fields already filled in by read_or_build_utent above */
    470101    setutent();
    471     pututline(&utent);
     102    pututline(utptr);
    472103    endutent();
    473 #ifdef CONFIG_FEATURE_WTMP
     104#if ENABLE_FEATURE_WTMP
    474105    if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
    475106        close(creat(bb_path_wtmp_file, 0664));
    476107    }
    477     updwtmp(bb_path_wtmp_file, &utent);
    478 #endif
    479 }
    480 #endif /* CONFIG_FEATURE_UTMP */
     108    updwtmp(bb_path_wtmp_file, utptr);
     109#endif
     110}
     111#else /* !ENABLE_FEATURE_UTMP */
     112#define read_or_build_utent(utptr, picky) ((void)0)
     113#define write_utent(utptr, username) ((void)0)
     114#endif /* !ENABLE_FEATURE_UTMP */
     115
     116#if ENABLE_FEATURE_NOLOGIN
     117static void die_if_nologin_and_non_root(int amroot)
     118{
     119    FILE *fp;
     120    int c;
     121
     122    if (access("/etc/nologin", F_OK))
     123        return;
     124
     125    fp = fopen("/etc/nologin", "r");
     126    if (fp) {
     127        while ((c = getc(fp)) != EOF)
     128            putchar((c=='\n') ? '\r' : c);
     129        fflush(stdout);
     130        fclose(fp);
     131    } else
     132        puts("\r\nSystem closed for routine maintenance\r");
     133    if (!amroot)
     134        exit(1);
     135    puts("\r\n[Disconnect bypassed -- root login allowed]\r");
     136}
     137#else
     138static ALWAYS_INLINE void die_if_nologin_and_non_root(int amroot) {}
     139#endif
     140
     141#if ENABLE_FEATURE_SECURETTY && !ENABLE_PAM
     142static int check_securetty(void)
     143{
     144    FILE *fp;
     145    int i;
     146    char buf[256];
     147
     148    fp = fopen("/etc/securetty", "r");
     149    if (!fp) {
     150        /* A missing securetty file is not an error. */
     151        return 1;
     152    }
     153    while (fgets(buf, sizeof(buf)-1, fp)) {
     154        for (i = strlen(buf)-1; i >= 0; --i) {
     155            if (!isspace(buf[i]))
     156                break;
     157        }
     158        buf[++i] = '\0';
     159        if (!buf[0] || (buf[0] == '#'))
     160            continue;
     161        if (strcmp(buf, short_tty) == 0) {
     162            fclose(fp);
     163            return 1;
     164        }
     165    }
     166    fclose(fp);
     167    return 0;
     168}
     169#else
     170static ALWAYS_INLINE int check_securetty(void) { return 1; }
     171#endif
     172
     173static void get_username_or_die(char *buf, int size_buf)
     174{
     175    int c, cntdown;
     176
     177    cntdown = EMPTY_USERNAME_COUNT;
     178 prompt:
     179    print_login_prompt();
     180    /* skip whitespace */
     181    do {
     182        c = getchar();
     183        if (c == EOF) exit(1);
     184        if (c == '\n') {
     185            if (!--cntdown) exit(1);
     186            goto prompt;
     187        }
     188    } while (isspace(c));
     189
     190    *buf++ = c;
     191    if (!fgets(buf, size_buf-2, stdin))
     192        exit(1);
     193    if (!strchr(buf, '\n'))
     194        exit(1);
     195    while (isgraph(*buf)) buf++;
     196    *buf = '\0';
     197}
     198
     199static void motd(void)
     200{
     201    int fd;
     202
     203    fd = open(bb_path_motd_file, O_RDONLY);
     204    if (fd) {
     205        fflush(stdout);
     206        bb_copyfd_eof(fd, STDOUT_FILENO);
     207        close(fd);
     208    }
     209}
     210
     211static void alarm_handler(int sig ATTRIBUTE_UNUSED)
     212{
     213    /* This is the escape hatch!  Poor serial line users and the like
     214     * arrive here when their connection is broken.
     215     * We don't want to block here */
     216    ndelay_on(1);
     217    ndelay_on(2);
     218    printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT);
     219    exit(EXIT_SUCCESS);
     220}
     221
     222int login_main(int argc, char **argv);
     223int login_main(int argc, char **argv)
     224{
     225    enum {
     226        LOGIN_OPT_f = (1<<0),
     227        LOGIN_OPT_h = (1<<1),
     228        LOGIN_OPT_p = (1<<2),
     229    };
     230    char *fromhost;
     231    char username[USERNAME_SIZE];
     232    const char *tmp;
     233    int amroot;
     234    unsigned opt;
     235    int count = 0;
     236    struct passwd *pw;
     237    char *opt_host = NULL;
     238    char *opt_user = NULL;
     239    char full_tty[TTYNAME_SIZE];
     240    USE_SELINUX(security_context_t user_sid = NULL;)
     241    USE_FEATURE_UTMP(struct utmp utent;)
     242    USE_PAM(pam_handle_t *pamh;)
     243    USE_PAM(int pamret;)
     244    USE_PAM(const char *failed_msg;)
     245
     246    short_tty = full_tty;
     247    username[0] = '\0';
     248    amroot = (getuid() == 0);
     249    signal(SIGALRM, alarm_handler);
     250    alarm(TIMEOUT);
     251
     252    /* Mandatory paranoia for suid applet:
     253     * ensure that fd# 0,1,2 are opened (at least to /dev/null)
     254     * and any extra open fd's are closed.
     255     * (The name of the function is misleading. Not daemonizing here.) */
     256    bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL);
     257
     258    opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
     259    if (opt & LOGIN_OPT_f) {
     260        if (!amroot)
     261            bb_error_msg_and_die("-f is for root only");
     262        safe_strncpy(username, opt_user, sizeof(username));
     263    }
     264    if (optind < argc) /* user from command line (getty) */
     265        safe_strncpy(username, argv[optind], sizeof(username));
     266
     267    /* Let's find out and memorize our tty */
     268    if (!isatty(0) || !isatty(1) || !isatty(2))
     269        return EXIT_FAILURE;        /* Must be a terminal */
     270    safe_strncpy(full_tty, "UNKNOWN", sizeof(full_tty));
     271    tmp = ttyname(0);
     272    if (tmp) {
     273        safe_strncpy(full_tty, tmp, sizeof(full_tty));
     274        if (strncmp(full_tty, "/dev/", 5) == 0)
     275            short_tty = full_tty + 5;
     276    }
     277
     278    read_or_build_utent(&utent, !amroot);
     279
     280    if (opt_host) {
     281        USE_FEATURE_UTMP(
     282            safe_strncpy(utent.ut_host, opt_host, sizeof(utent.ut_host));
     283        )
     284        fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host);
     285    } else
     286        fromhost = xasprintf(" on '%s'", short_tty);
     287
     288    /* Was breaking "login <username>" from shell command line: */
     289    /*bb_setpgrp();*/
     290
     291    openlog(applet_name, LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
     292
     293    while (1) {
     294        if (!username[0])
     295            get_username_or_die(username, sizeof(username));
     296
     297#if ENABLE_PAM
     298        pamret = pam_start("login", username, &conv, &pamh);
     299        if (pamret != PAM_SUCCESS) {
     300            failed_msg = "pam_start";
     301            goto pam_auth_failed;
     302        }
     303        /* set TTY (so things like securetty work) */
     304        pamret = pam_set_item(pamh, PAM_TTY, short_tty);
     305        if (pamret != PAM_SUCCESS) {
     306            failed_msg = "pam_set_item(TTY)";
     307            goto pam_auth_failed;
     308        }
     309        pamret = pam_authenticate(pamh, 0);
     310        if (pamret != PAM_SUCCESS) {
     311            failed_msg = "pam_authenticate";
     312            goto pam_auth_failed;
     313            /* TODO: or just "goto auth_failed"
     314             * since user seems to enter wrong password
     315             * (in this case pamret == 7)
     316             */
     317        }
     318        /* check that the account is healthy */
     319        pamret = pam_acct_mgmt(pamh, 0);
     320        if (pamret != PAM_SUCCESS) {
     321            failed_msg = "account setup";
     322            goto pam_auth_failed;
     323        }
     324        /* read user back */
     325        {
     326            const char *pamuser;
     327            /* gcc: "dereferencing type-punned pointer breaks aliasing rules..."
     328             * thus we cast to (void*) */
     329            if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) {
     330                failed_msg = "pam_get_item(USER)";
     331                goto pam_auth_failed;
     332            }
     333            safe_strncpy(username, pamuser, sizeof(username));
     334        }
     335        /* If we get here, the user was authenticated, and is
     336         * granted access. */
     337        pw = getpwnam(username);
     338        if (pw)
     339            break;
     340        goto auth_failed;
     341 pam_auth_failed:
     342        bb_error_msg("%s failed: %s (%d)", failed_msg, pam_strerror(pamh, pamret), pamret);
     343        safe_strncpy(username, "UNKNOWN", sizeof(username));
     344#else /* not PAM */
     345        pw = getpwnam(username);
     346        if (!pw) {
     347            strcpy(username, "UNKNOWN");
     348            goto fake_it;
     349        }
     350
     351        if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
     352            goto auth_failed;
     353
     354        if (opt & LOGIN_OPT_f)
     355            break; /* -f USER: success without asking passwd */
     356
     357        if (pw->pw_uid == 0 && !check_securetty())
     358            goto auth_failed;
     359
     360        /* Don't check the password if password entry is empty (!) */
     361        if (!pw->pw_passwd[0])
     362            break;
     363 fake_it:
     364        /* authorization takes place here */
     365        if (correct_password(pw))
     366            break;
     367#endif /* ENABLE_PAM */
     368 auth_failed:
     369        opt &= ~LOGIN_OPT_f;
     370        bb_do_delay(FAIL_DELAY);
     371        /* TODO: doesn't sound like correct English phrase to me */
     372        puts("Login incorrect");
     373        if (++count == 3) {
     374            syslog(LOG_WARNING, "invalid password for '%s'%s",
     375                        username, fromhost);
     376            return EXIT_FAILURE;
     377        }
     378        username[0] = '\0';
     379    }
     380
     381    alarm(0);
     382    die_if_nologin_and_non_root(pw->pw_uid == 0);
     383
     384    write_utent(&utent, username);
     385
     386#if ENABLE_SELINUX
     387    if (is_selinux_enabled()) {
     388        security_context_t old_tty_sid, new_tty_sid;
     389
     390        if (get_default_context(username, NULL, &user_sid)) {
     391            bb_error_msg_and_die("cannot get SID for %s",
     392                    username);
     393        }
     394        if (getfilecon(full_tty, &old_tty_sid) < 0) {
     395            bb_perror_msg_and_die("getfilecon(%s) failed",
     396                    full_tty);
     397        }
     398        if (security_compute_relabel(user_sid, old_tty_sid,
     399                    SECCLASS_CHR_FILE, &new_tty_sid) != 0) {
     400            bb_perror_msg_and_die("security_change_sid(%s) failed",
     401                    full_tty);
     402        }
     403        if (setfilecon(full_tty, new_tty_sid) != 0) {
     404            bb_perror_msg_and_die("chsid(%s, %s) failed",
     405                    full_tty, new_tty_sid);
     406        }
     407    }
     408#endif
     409    /* Try these, but don't complain if they fail.
     410     * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */
     411    fchown(0, pw->pw_uid, pw->pw_gid);
     412    fchmod(0, 0600);
     413
     414    if (ENABLE_LOGIN_SCRIPTS) {
     415        char *t_argv[2];
     416
     417        t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT");
     418        if (t_argv[0]) {
     419            t_argv[1] = NULL;
     420            xsetenv("LOGIN_TTY", full_tty);
     421            xsetenv("LOGIN_USER", pw->pw_name);
     422            xsetenv("LOGIN_UID", utoa(pw->pw_uid));
     423            xsetenv("LOGIN_GID", utoa(pw->pw_gid));
     424            xsetenv("LOGIN_SHELL", pw->pw_shell);
     425            xspawn(t_argv); /* NOMMU-friendly */
     426            /* All variables are unset by setup_environment */
     427            wait(NULL);
     428        }
     429    }
     430
     431    change_identity(pw);
     432    tmp = pw->pw_shell;
     433    if (!tmp || !*tmp)
     434        tmp = DEFAULT_SHELL;
     435    /* setup_environment params: shell, loginshell, changeenv, pw */
     436    setup_environment(tmp, 1, !(opt & LOGIN_OPT_p), pw);
     437    /* FIXME: login shell = 1 -> 3rd parameter is ignored! */
     438
     439    motd();
     440
     441    if (pw->pw_uid == 0)
     442        syslog(LOG_INFO, "root login%s", fromhost);
     443#if ENABLE_SELINUX
     444    /* well, a simple setexeccon() here would do the job as well,
     445     * but let's play the game for now */
     446    set_current_security_context(user_sid);
     447#endif
     448
     449    // util-linux login also does:
     450    // /* start new session */
     451    // setsid();
     452    // /* TIOCSCTTY: steal tty from other process group */
     453    // if (ioctl(0, TIOCSCTTY, 1)) error_msg...
     454    // BBox login used to do this (see above):
     455    // bb_setpgrp();
     456    // If this stuff is really needed, add it and explain why!
     457
     458    /* set signals to defaults */
     459    signal(SIGALRM, SIG_DFL);
     460    /* Is this correct? This way user can ctrl-c out of /etc/profile,
     461     * potentially creating security breach (tested with bash 3.0).
     462     * But without this, bash 3.0 will not enable ctrl-c either.
     463     * Maybe bash is buggy?
     464     * Need to find out what standards say about /bin/login -
     465     * should it leave SIGINT etc enabled or disabled? */
     466    signal(SIGINT, SIG_DFL);
     467
     468    /* Exec login shell with no additional parameters */
     469    run_shell(tmp, 1, NULL, NULL);
     470
     471    /* return EXIT_FAILURE; - not reached */
     472}
Note: See TracChangeset for help on using the changeset viewer.