blob: 99f66b53bcfae03ee5bb3e25390f9eb56de9e26b [file] [log] [blame]
Robert Griebl1fca5582002-06-04 20:45:46 +00001/* 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/time.h>
14#include <sys/types.h>
15#include <ctype.h>
16#include <time.h>
Eric Andersen27f64e12002-06-23 04:24:25 +000017
Robert Griebl1fca5582002-06-04 20:45:46 +000018#include "busybox.h"
Eric Andersen9e480452003-07-03 10:07:04 +000019#ifdef CONFIG_SELINUX
20#include <flask_util.h>
21#include <get_sid_list.h>
22#include <proc_secure.h>
23#include <fs_secure.h>
24#endif
Robert Griebl1fca5582002-06-04 20:45:46 +000025
Eric Andersenfdfe2982002-10-10 03:55:09 +000026#ifdef CONFIG_FEATURE_U_W_TMP
Robert Griebl1fca5582002-06-04 20:45:46 +000027// import from utmp.c
28static void checkutmp(int picky);
29static void setutmp(const char *name, const char *line);
Eric Andersen71ae64b2002-10-10 04:20:21 +000030/* Stuff global to this file */
31struct utmp utent;
Eric Andersenfdfe2982002-10-10 03:55:09 +000032#endif
Robert Griebl1fca5582002-06-04 20:45:46 +000033
Robert Griebl1fca5582002-06-04 20:45:46 +000034// login defines
Robert Griebl1fca5582002-06-04 20:45:46 +000035#define TIMEOUT 60
Eric Andersen0fbff132002-06-22 17:49:29 +000036#define EMPTY_USERNAME_COUNT 10
Eric Andersen0fbff132002-06-22 17:49:29 +000037#define USERNAME_SIZE 32
38
Robert Griebl1fca5582002-06-04 20:45:46 +000039
40static int check_nologin ( int amroot );
41
42#if defined CONFIG_FEATURE_SECURETTY
43static int check_tty ( const char *tty );
44
45#else
46static inline int check_tty ( const char *tty ) { return 1; }
47
48#endif
49
50static int is_my_tty ( const char *tty );
Eric Andersen0fbff132002-06-22 17:49:29 +000051static int login_prompt ( char *buf_name );
Robert Griebl1fca5582002-06-04 20:45:46 +000052static void motd ( void );
Robert Griebl1fca5582002-06-04 20:45:46 +000053
54
55static void alarm_handler ( int sig )
56{
Eric Andersen0fbff132002-06-22 17:49:29 +000057 fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT );
Robert Griebl1fca5582002-06-04 20:45:46 +000058 exit ( EXIT_SUCCESS );
59}
60
61
62extern int login_main(int argc, char **argv)
63{
64 char tty[BUFSIZ];
65 char full_tty[200];
66 char fromhost[512];
Eric Andersen0fbff132002-06-22 17:49:29 +000067 char username[USERNAME_SIZE];
Glenn L McGrathdc4e75e2003-09-02 02:36:18 +000068 const char *tmp;
Robert Griebl1fca5582002-06-04 20:45:46 +000069 int amroot;
70 int flag;
71 int failed;
72 int count=0;
73 struct passwd *pw, pw_copy;
Eric Andersen27f64e12002-06-23 04:24:25 +000074#ifdef CONFIG_WHEEL_GROUP
75 struct group *grp;
76#endif
Robert Griebl1fca5582002-06-04 20:45:46 +000077 int opt_preserve = 0;
78 int opt_fflag = 0;
79 char *opt_host = 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000080 int alarmstarted = 0;
Eric Andersen9e480452003-07-03 10:07:04 +000081#ifdef CONFIG_SELINUX
82 int flask_enabled = is_flask_enabled();
83 security_id_t sid = 0, old_tty_sid, new_tty_sid;
84#endif
Robert Griebl1fca5582002-06-04 20:45:46 +000085
Eric Andersen0fbff132002-06-22 17:49:29 +000086 username[0]=0;
Robert Griebl1fca5582002-06-04 20:45:46 +000087 amroot = ( getuid ( ) == 0 );
88 signal ( SIGALRM, alarm_handler );
Eric Andersend8ceba92003-07-30 06:56:07 +000089 alarm ( TIMEOUT );
90 alarmstarted = 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +000091
Robert Griebl1fca5582002-06-04 20:45:46 +000092 while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
93 switch ( flag ) {
94 case 'p':
Robert Griebl1fca5582002-06-04 20:45:46 +000095 opt_preserve = 1;
96 break;
97 case 'f':
98 /*
99 * username must be a seperate token
100 * (-f root, *NOT* -froot). --marekm
101 */
102 if ( optarg != argv[optind-1] )
Manuel Novoa III cad53642003-03-19 09:13:01 +0000103 bb_show_usage( );
Robert Griebl1fca5582002-06-04 20:45:46 +0000104
105 if ( !amroot ) /* Auth bypass only if real UID is zero */
Manuel Novoa III cad53642003-03-19 09:13:01 +0000106 bb_error_msg_and_die ( "-f permission denied" );
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000107
Eric Andersen0fbff132002-06-22 17:49:29 +0000108 safe_strncpy(username, optarg, USERNAME_SIZE);
Robert Griebl1fca5582002-06-04 20:45:46 +0000109 opt_fflag = 1;
110 break;
111 case 'h':
112 opt_host = optarg;
113 break;
114 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000115 bb_show_usage( );
Robert Griebl1fca5582002-06-04 20:45:46 +0000116 }
117 }
118
Eric Andersen0fbff132002-06-22 17:49:29 +0000119 if (optind < argc) // user from command line (getty)
120 safe_strncpy(username, argv[optind], USERNAME_SIZE);
Robert Griebl1fca5582002-06-04 20:45:46 +0000121
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000122 if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 ))
Robert Griebl1fca5582002-06-04 20:45:46 +0000123 return EXIT_FAILURE; /* Must be a terminal */
124
Eric Andersenfdfe2982002-10-10 03:55:09 +0000125#ifdef CONFIG_FEATURE_U_W_TMP
Robert Griebl1fca5582002-06-04 20:45:46 +0000126 checkutmp ( !amroot );
Eric Andersenfdfe2982002-10-10 03:55:09 +0000127#endif
Robert Griebl1fca5582002-06-04 20:45:46 +0000128
129 tmp = ttyname ( 0 );
Eric Andersen21d30862004-03-09 21:27:32 +0000130 if ( tmp )
131 safe_strncpy ( tty, tmp, sizeof( tty ));
Robert Griebl1fca5582002-06-04 20:45:46 +0000132 else
133 safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
134
Eric Andersen71ae64b2002-10-10 04:20:21 +0000135#ifdef CONFIG_FEATURE_U_W_TMP
Robert Griebl1fca5582002-06-04 20:45:46 +0000136 if ( amroot )
137 memset ( utent.ut_host, 0, sizeof utent.ut_host );
Eric Andersen71ae64b2002-10-10 04:20:21 +0000138#endif
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000139
Robert Griebl1fca5582002-06-04 20:45:46 +0000140 if ( opt_host ) {
Eric Andersen71ae64b2002-10-10 04:20:21 +0000141#ifdef CONFIG_FEATURE_U_W_TMP
Robert Griebl1fca5582002-06-04 20:45:46 +0000142 safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
Eric Andersen71ae64b2002-10-10 04:20:21 +0000143#endif
Robert Griebl1fca5582002-06-04 20:45:46 +0000144 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host );
145 }
146 else
147 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty );
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000148
Eric Andersen0fbff132002-06-22 17:49:29 +0000149 setpgrp();
150
Robert Griebl1fca5582002-06-04 20:45:46 +0000151 openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH );
152
153 while ( 1 ) {
154 failed = 0;
155
Eric Andersen0fbff132002-06-22 17:49:29 +0000156 if ( !username[0] )
157 if(!login_prompt ( username ))
158 return EXIT_FAILURE;
Robert Griebl1fca5582002-06-04 20:45:46 +0000159
160 if ( !alarmstarted && ( TIMEOUT > 0 )) {
161 alarm ( TIMEOUT );
162 alarmstarted = 1;
163 }
164
165 if (!( pw = getpwnam ( username ))) {
Eric Andersen0fbff132002-06-22 17:49:29 +0000166 pw_copy.pw_name = "UNKNOWN";
167 pw_copy.pw_passwd = "!";
Robert Griebl1fca5582002-06-04 20:45:46 +0000168 opt_fflag = 0;
169 failed = 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000170 } else
Robert Griebl1fca5582002-06-04 20:45:46 +0000171 pw_copy = *pw;
172
173 pw = &pw_copy;
174
175 if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' ))
176 failed = 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000177
Robert Griebl1fca5582002-06-04 20:45:46 +0000178 if ( opt_fflag ) {
179 opt_fflag = 0;
180 goto auth_ok;
181 }
182
Eric Andersen0fbff132002-06-22 17:49:29 +0000183 if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty )))
Robert Griebl1fca5582002-06-04 20:45:46 +0000184 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
Robert Griebl1fca5582002-06-04 20:45:46 +0000194 failed = 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000195
Robert Griebl1fca5582002-06-04 20:45:46 +0000196auth_ok:
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000197 if ( !failed)
Robert Griebl1fca5582002-06-04 20:45:46 +0000198 break;
199
200 { // delay next try
201 time_t start, now;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000202
Robert Griebl1fca5582002-06-04 20:45:46 +0000203 time ( &start );
204 now = start;
205 while ( difftime ( now, start ) < FAIL_DELAY) {
206 sleep ( FAIL_DELAY );
207 time ( &now );
208 }
209 }
210
211 puts("Login incorrect");
Eric Andersen0fbff132002-06-22 17:49:29 +0000212 username[0] = 0;
213 if ( ++count == 3 ) {
214 syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
Robert Griebl1fca5582002-06-04 20:45:46 +0000215 return EXIT_FAILURE;
216 }
Eric Andersen0fbff132002-06-22 17:49:29 +0000217 }
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000218
Robert Griebl1fca5582002-06-04 20:45:46 +0000219 alarm ( 0 );
220 if ( check_nologin ( pw-> pw_uid == 0 ))
221 return EXIT_FAILURE;
222
Eric Andersenfdfe2982002-10-10 03:55:09 +0000223#ifdef CONFIG_FEATURE_U_W_TMP
Robert Griebl1fca5582002-06-04 20:45:46 +0000224 setutmp ( username, tty );
Eric Andersenfdfe2982002-10-10 03:55:09 +0000225#endif
Eric Andersen9e480452003-07-03 10:07:04 +0000226#ifdef CONFIG_SELINUX
227 if (flask_enabled)
228 {
229 struct stat st;
230
231 if (get_default_sid(username, 0, &sid))
232 {
233 fprintf(stderr, "Unable to get SID for %s\n", username);
234 exit(1);
235 }
236 if (stat_secure(tty, &st, &old_tty_sid))
237 {
238 fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno));
239 return EXIT_FAILURE;
240 }
241 if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
242 {
243 fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno));
244 return EXIT_FAILURE;
245 }
246 if(chsid(tty, new_tty_sid) != 0)
247 {
248 fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno));
249 return EXIT_FAILURE;
250 }
251 }
252 else
253 sid = 0;
254#endif
255
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000256 if ( *tty != '/' )
Robert Griebl1fca5582002-06-04 20:45:46 +0000257 snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
258 else
259 safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000260
261 if ( !is_my_tty ( full_tty ))
Robert Griebl1fca5582002-06-04 20:45:46 +0000262 syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000263
264 /* Try these, but don't complain if they fail
Robert Griebl1fca5582002-06-04 20:45:46 +0000265 * (for example when the root fs is read only) */
266 chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
267 chmod ( full_tty, 0600 );
268
269 change_identity ( pw );
Glenn L McGrathdc4e75e2003-09-02 02:36:18 +0000270 tmp = pw-> pw_shell;
271 if(!tmp || !*tmp)
272 tmp = DEFAULT_SHELL;
273 setup_environment ( tmp, 1, !opt_preserve, pw );
Robert Griebl1fca5582002-06-04 20:45:46 +0000274
275 motd ( );
276 signal ( SIGALRM, SIG_DFL ); /* default alarm signal */
277
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000278 if ( pw-> pw_uid == 0 )
Robert Griebl1fca5582002-06-04 20:45:46 +0000279 syslog ( LOG_INFO, "root login %s\n", fromhost );
Glenn L McGrathdc4e75e2003-09-02 02:36:18 +0000280 run_shell ( tmp, 1, 0, 0
Eric Andersen9e480452003-07-03 10:07:04 +0000281#ifdef CONFIG_SELINUX
282 , sid
283#endif
284 ); /* exec the shell finally. */
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000285
Robert Griebl1fca5582002-06-04 20:45:46 +0000286 return EXIT_FAILURE;
287}
288
289
290
Eric Andersen0fbff132002-06-22 17:49:29 +0000291static int login_prompt ( char *buf_name )
Robert Griebl1fca5582002-06-04 20:45:46 +0000292{
293 char buf [1024];
294 char *sp, *ep;
Eric Andersen0fbff132002-06-22 17:49:29 +0000295 int i;
Robert Griebl1fca5582002-06-04 20:45:46 +0000296
Eric Andersen0fbff132002-06-22 17:49:29 +0000297 for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000298 print_login_prompt();
Robert Griebl1fca5582002-06-04 20:45:46 +0000299
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000300 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
301 return 0;
302
Eric Andersen0fbff132002-06-22 17:49:29 +0000303 if ( !strchr ( buf, '\n' ))
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000304 return 0;
Robert Griebl1fca5582002-06-04 20:45:46 +0000305
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000306 for ( sp = buf; isspace ( *sp ); sp++ ) { }
307 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
308
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000309 *ep = 0;
Eric Andersen0fbff132002-06-22 17:49:29 +0000310 safe_strncpy(buf_name, sp, USERNAME_SIZE);
311 if(buf_name[0])
312 return 1;
313 }
314 return 0;
Robert Griebl1fca5582002-06-04 20:45:46 +0000315}
316
317
318static int check_nologin ( int amroot )
319{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000320 if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000321 FILE *fp;
322 int c;
323
Manuel Novoa III cad53642003-03-19 09:13:01 +0000324 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000325 while (( c = getc ( fp )) != EOF )
326 putchar (( c == '\n' ) ? '\r' : c );
327
328 fflush ( stdout );
329 fclose ( fp );
330 } else {
331 puts ( "\r\nSystem closed for routine maintenance.\r" );
332 }
333 if ( !amroot )
334 return 1;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000335
Robert Griebl1fca5582002-06-04 20:45:46 +0000336 puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
337 }
338 return 0;
339}
340
341#ifdef CONFIG_FEATURE_SECURETTY
342
343static int check_tty ( const char *tty )
344{
345 FILE *fp;
346 int i;
347 char buf[BUFSIZ];
348
Manuel Novoa III cad53642003-03-19 09:13:01 +0000349 if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000350 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000351 for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000352 if ( !isspace ( buf[i] ))
353 break;
354 }
355 buf[++i] = '\0';
356 if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
357 continue;
358
359 if ( strcmp ( buf, tty ) == 0 ) {
360 fclose ( fp );
361 return 1;
362 }
363 }
364 fclose(fp);
365 return 0;
366 }
Eric Andersenf8701482002-11-14 10:58:17 +0000367 /* A missing securetty file is not an error. */
Robert Griebl69b57562002-12-03 19:54:12 +0000368 return 1;
Robert Griebl1fca5582002-06-04 20:45:46 +0000369}
370
371#endif
372
373/* returns 1 if true */
374static int is_my_tty ( const char *tty )
375{
376 struct stat by_name, by_fd;
377
378 if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
379 return 0;
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000380
Robert Griebl1fca5582002-06-04 20:45:46 +0000381 if ( by_name. st_rdev != by_fd. st_rdev )
382 return 0;
383 else
384 return 1;
385}
386
387
388static void motd ( )
389{
390 FILE *fp;
391 register int c;
392
Manuel Novoa III cad53642003-03-19 09:13:01 +0000393 if (( fp = fopen ( bb_path_motd_file, "r" ))) {
Eric Andersenc7bda1c2004-03-15 08:29:22 +0000394 while (( c = getc ( fp )) != EOF )
395 putchar ( c );
Robert Griebl1fca5582002-06-04 20:45:46 +0000396 fclose ( fp );
397 }
398}
399
400
Eric Andersenfdfe2982002-10-10 03:55:09 +0000401#ifdef CONFIG_FEATURE_U_W_TMP
Robert Griebl1fca5582002-06-04 20:45:46 +0000402// vv Taken from tinylogin utmp.c vv
403
404#define _WTMP_FILE "/var/log/wtmp"
405
406#define NO_UTENT \
407 "No utmp entry. You must exec \"login\" from the lowest level \"sh\""
408#define NO_TTY \
409 "Unable to determine your tty name."
410
411/*
412 * checkutmp - see if utmp file is correct for this process
413 *
414 * System V is very picky about the contents of the utmp file
415 * and requires that a slot for the current process exist.
416 * The utmp file is scanned for an entry with the same process
417 * ID. If no entry exists the process exits with a message.
418 *
419 * The "picky" flag is for network and other logins that may
420 * use special flags. It allows the pid checks to be overridden.
421 * This means that getty should never invoke login with any
422 * command line flags.
423 */
424
425static void checkutmp(int picky)
426{
427 char *line;
428 struct utmp *ut;
429 pid_t pid = getpid();
430
431 setutent();
432
433 /* First, try to find a valid utmp entry for this process. */
434 while ((ut = getutent()))
435 if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
436 (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
437 break;
438
439 /* If there is one, just use it, otherwise create a new one. */
440 if (ut) {
441 utent = *ut;
442 } else {
443 if (picky) {
444 puts(NO_UTENT);
445 exit(1);
446 }
447 line = ttyname(0);
448 if (!line) {
449 puts(NO_TTY);
450 exit(1);
451 }
452 if (strncmp(line, "/dev/", 5) == 0)
453 line += 5;
454 memset((void *) &utent, 0, sizeof utent);
455 utent.ut_type = LOGIN_PROCESS;
456 utent.ut_pid = pid;
457 strncpy(utent.ut_line, line, sizeof utent.ut_line);
458 /* XXX - assumes /dev/tty?? */
459 strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
460 strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
461 time(&utent.ut_time);
462 }
463}
464
Robert Griebl1fca5582002-06-04 20:45:46 +0000465/*
466 * setutmp - put a USER_PROCESS entry in the utmp file
467 *
468 * setutmp changes the type of the current utmp entry to
469 * USER_PROCESS. the wtmp file will be updated as well.
470 */
471
472static void setutmp(const char *name, const char *line)
473{
474 utent.ut_type = USER_PROCESS;
475 strncpy(utent.ut_user, name, sizeof utent.ut_user);
476 time(&utent.ut_time);
477 /* other fields already filled in by checkutmp above */
478 setutent();
479 pututline(&utent);
480 endutent();
481 updwtmp(_WTMP_FILE, &utent);
482}
Eric Andersenfdfe2982002-10-10 03:55:09 +0000483#endif /* CONFIG_FEATURE_U_W_TMP */