1 /* vi: set sw=4 ts=4: */
3 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
15 #include <sys/resource.h>
17 #include <sys/types.h>
23 #include <selinux/selinux.h> /* for is_selinux_enabled() */
24 #include <selinux/get_context_list.h> /* for get_default_context() */
25 #include <selinux/flask.h> /* for security class definitions */
29 #ifdef CONFIG_FEATURE_UTMP
31 static void checkutmp(int picky);
32 static void setutmp(const char *name, const char *line);
33 /* Stuff global to this file */
34 static struct utmp utent;
39 #define EMPTY_USERNAME_COUNT 10
40 #define USERNAME_SIZE 32
43 static int check_nologin ( int amroot );
45 #if defined CONFIG_FEATURE_SECURETTY
46 static int check_tty ( const char *tty );
49 static inline int check_tty ( const char *tty ) { return 1; }
53 static int is_my_tty ( const char *tty );
54 static int login_prompt ( char *buf_name );
55 static void motd ( void );
58 static void alarm_handler ( int sig ATTRIBUTE_UNUSED)
60 fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT );
61 exit ( EXIT_SUCCESS );
65 int login_main(int argc, char **argv)
70 char username[USERNAME_SIZE];
76 struct passwd *pw, pw_copy;
77 #ifdef CONFIG_WHEEL_GROUP
85 security_context_t user_sid = NULL;
89 amroot = ( getuid ( ) == 0 );
90 signal ( SIGALRM, alarm_handler );
94 while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
101 * username must be a separate token
102 * (-f root, *NOT* -froot). --marekm
104 if ( optarg != argv[optind-1] )
107 if ( !amroot ) /* Auth bypass only if real UID is zero */
108 bb_error_msg_and_die ( "-f permission denied" );
110 safe_strncpy(username, optarg, USERNAME_SIZE);
121 if (optind < argc) // user from command line (getty)
122 safe_strncpy(username, argv[optind], USERNAME_SIZE);
124 if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 ))
125 return EXIT_FAILURE; /* Must be a terminal */
127 #ifdef CONFIG_FEATURE_UTMP
128 checkutmp ( !amroot );
132 if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 ))
133 safe_strncpy ( tty, tmp + 5, sizeof( tty ));
134 else if ( tmp && *tmp == '/' )
135 safe_strncpy ( tty, tmp, sizeof( tty ));
137 safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
139 #ifdef CONFIG_FEATURE_UTMP
141 memset ( utent.ut_host, 0, sizeof utent.ut_host );
145 #ifdef CONFIG_FEATURE_UTMP
146 safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
148 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host );
151 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty );
155 openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH );
161 if(!login_prompt ( username ))
164 if ( !alarmstarted && ( TIMEOUT > 0 )) {
169 if (!( pw = getpwnam ( username ))) {
170 pw_copy.pw_name = "UNKNOWN";
171 pw_copy.pw_passwd = "!";
179 if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' ))
187 if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty )))
190 /* Don't check the password if password entry is empty (!) */
191 if ( !pw-> pw_passwd[0] )
194 /* authorization takes place here */
195 if ( correct_password ( pw ))
204 bb_do_delay(FAIL_DELAY);
205 puts("Login incorrect");
207 if ( ++count == 3 ) {
208 syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
214 if ( check_nologin ( pw-> pw_uid == 0 ))
217 #ifdef CONFIG_FEATURE_UTMP
218 setutmp ( username, tty );
222 snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
224 safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
226 #ifdef CONFIG_SELINUX
227 if (is_selinux_enabled())
229 security_context_t old_tty_sid, new_tty_sid;
231 if (get_default_context(username, NULL, &user_sid))
233 fprintf(stderr, "Unable to get SID for %s\n", username);
236 if (getfilecon(full_tty, &old_tty_sid) < 0)
238 fprintf(stderr, "getfilecon(%.100s) failed: %.100s\n", full_tty, strerror(errno));
241 if (security_compute_relabel(user_sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
243 fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", full_tty, strerror(errno));
246 if(setfilecon(full_tty, new_tty_sid) != 0)
248 fprintf(stderr, "chsid(%.100s, %s) failed: %.100s\n", full_tty, new_tty_sid, strerror(errno));
253 if ( !is_my_tty ( full_tty ))
254 syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
256 /* Try these, but don't complain if they fail
257 * (for example when the root fs is read only) */
258 chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
259 chmod ( full_tty, 0600 );
261 change_identity ( pw );
265 setup_environment ( tmp, 1, !opt_preserve, pw );
268 signal ( SIGALRM, SIG_DFL ); /* default alarm signal */
270 if ( pw-> pw_uid == 0 )
271 syslog ( LOG_INFO, "root login %s\n", fromhost );
272 #ifdef CONFIG_SELINUX
273 /* well, a simple setexeccon() here would do the job as well,
274 * but let's play the game for now */
275 set_current_security_context(user_sid);
277 run_shell ( tmp, 1, 0, 0); /* exec the shell finally. */
284 static int login_prompt ( char *buf_name )
290 for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
291 print_login_prompt();
293 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
296 if ( !strchr ( buf, '\n' ))
299 for ( sp = buf; isspace ( *sp ); sp++ ) { }
300 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
303 safe_strncpy(buf_name, sp, USERNAME_SIZE);
311 static int check_nologin ( int amroot )
313 if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
317 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
318 while (( c = getc ( fp )) != EOF )
319 putchar (( c == '\n' ) ? '\r' : c );
324 puts ( "\r\nSystem closed for routine maintenance.\r" );
329 puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
334 #ifdef CONFIG_FEATURE_SECURETTY
336 static int check_tty ( const char *tty )
342 if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
343 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
344 for ( i = strlen( buf ) - 1; i >= 0; --i ) {
345 if ( !isspace ( buf[i] ))
349 if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
352 if ( strcmp ( buf, tty ) == 0 ) {
360 /* A missing securetty file is not an error. */
366 /* returns 1 if true */
367 static int is_my_tty ( const char *tty )
369 struct stat by_name, by_fd;
371 if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
374 if ( by_name. st_rdev != by_fd. st_rdev )
381 static void motd (void)
386 if (( fp = fopen ( bb_path_motd_file, "r" ))) {
387 while (( c = getc ( fp )) != EOF )
394 #ifdef CONFIG_FEATURE_UTMP
395 // vv Taken from tinylogin utmp.c vv
398 "No utmp entry. You must exec \"login\" from the lowest level \"sh\""
400 "Unable to determine your tty name."
403 * checkutmp - see if utmp file is correct for this process
405 * System V is very picky about the contents of the utmp file
406 * and requires that a slot for the current process exist.
407 * The utmp file is scanned for an entry with the same process
408 * ID. If no entry exists the process exits with a message.
410 * The "picky" flag is for network and other logins that may
411 * use special flags. It allows the pid checks to be overridden.
412 * This means that getty should never invoke login with any
413 * command line flags.
416 static void checkutmp(int picky)
420 pid_t pid = getpid();
424 /* First, try to find a valid utmp entry for this process. */
425 while ((ut = getutent()))
426 if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
427 (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
430 /* If there is one, just use it, otherwise create a new one. */
445 if (strncmp(line, "/dev/", 5) == 0)
447 memset((void *) &utent, 0, sizeof utent);
448 utent.ut_type = LOGIN_PROCESS;
450 strncpy(utent.ut_line, line, sizeof utent.ut_line);
451 /* XXX - assumes /dev/tty?? */
452 strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
453 strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
454 t_tmp = (time_t)utent.ut_time;
460 * setutmp - put a USER_PROCESS entry in the utmp file
462 * setutmp changes the type of the current utmp entry to
463 * USER_PROCESS. the wtmp file will be updated as well.
466 static void setutmp(const char *name, const char *line ATTRIBUTE_UNUSED)
468 time_t t_tmp = (time_t)utent.ut_time;
470 utent.ut_type = USER_PROCESS;
471 strncpy(utent.ut_user, name, sizeof utent.ut_user);
473 /* other fields already filled in by checkutmp above */
477 #ifdef CONFIG_FEATURE_WTMP
478 if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
479 close(creat(bb_path_wtmp_file, 0664));
481 updwtmp(bb_path_wtmp_file, &utent);
484 #endif /* CONFIG_FEATURE_UTMP */