source: MondoRescue/branches/stable/mindi-busybox/loginutils/login.c @ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 10.6 KB
Line 
1/* 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>
10#include <utmp.h>
11#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
19#include <selinux/selinux.h>  /* for is_selinux_enabled()  */
20#include <selinux/get_context_list.h> /* for get_default_context() */
21#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
27static void checkutmp(int picky);
28static void setutmp(const char *name, const char *line);
29/* Stuff global to this file */
30static 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
39static int check_nologin ( int amroot );
40
41#if defined CONFIG_FEATURE_SECURETTY
42static int check_tty ( const char *tty );
43
44#else
45static inline int check_tty ( const char *tty )  { return 1; }
46
47#endif
48
49static int is_my_tty ( const char *tty );
50static int login_prompt ( char *buf_name );
51static void motd ( void );
52
53
54static 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
61int 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
196auth_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
280static 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
307static 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
332static 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 */
363static 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
377static 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
398/*
399 * checkutmp - see if utmp file is correct for this process
400 *
401 *  System V is very picky about the contents of the utmp file
402 *  and requires that a slot for the current process exist.
403 *  The utmp file is scanned for an entry with the same process
404 *  ID.  If no entry exists the process exits with a message.
405 *
406 *  The "picky" flag is for network and other logins that may
407 *  use special flags.  It allows the pid checks to be overridden.
408 *  This means that getty should never invoke login with any
409 *  command line flags.
410 */
411
412static void checkutmp(int picky)
413{
414    char *line;
415    struct utmp *ut;
416    pid_t pid = getpid();
417
418    setutent();
419
420    /* First, try to find a valid utmp entry for this process.  */
421    while ((ut = getutent()))
422        if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
423            (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
424            break;
425
426    /* If there is one, just use it, otherwise create a new one.  */
427    if (ut) {
428        utent = *ut;
429    } 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    }
453}
454
455/*
456 * setutmp - put a USER_PROCESS entry in the utmp file
457 *
458 *  setutmp changes the type of the current utmp entry to
459 *  USER_PROCESS.  the wtmp file will be updated as well.
460 */
461
462static 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 */
470    setutent();
471    pututline(&utent);
472    endutent();
473#ifdef CONFIG_FEATURE_WTMP
474    if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
475        close(creat(bb_path_wtmp_file, 0664));
476    }
477    updwtmp(bb_path_wtmp_file, &utent);
478#endif
479}
480#endif /* CONFIG_FEATURE_UTMP */
Note: See TracBrowser for help on using the repository browser.