blob: c2bada258f637b6ec589cafbae2f617bf5cf5af6 [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];
Robert Griebl1fca5582002-06-04 20:45:46 +000068 char *tmp;
69 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;
Robert Griebl1fca5582002-06-04 20:45:46 +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;
Robert Griebl1fca5582002-06-04 20:45:46 +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" );
Robert Griebl1fca5582002-06-04 20:45:46 +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
122 if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 ))
123 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 );
130 if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 ))
131 safe_strncpy ( tty, tmp + 5, sizeof( tty ));
132 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
Robert Griebl1fca5582002-06-04 20:45:46 +0000139
140 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 );
148
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;
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
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;
195
196auth_ok:
197 if ( !failed)
198 break;
199
200 { // delay next try
201 time_t start, now;
202
203 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 }
Robert Griebl1fca5582002-06-04 20:45:46 +0000218
219 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
Robert Griebl1fca5582002-06-04 20:45:46 +0000256 if ( *tty != '/' )
257 snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
258 else
259 safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
260
261 if ( !is_my_tty ( full_tty ))
262 syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
263
264 /* Try these, but don't complain if they fail
265 * (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 );
270 setup_environment ( pw-> pw_shell, 1, !opt_preserve, pw );
271
272 motd ( );
273 signal ( SIGALRM, SIG_DFL ); /* default alarm signal */
274
275 if ( pw-> pw_uid == 0 )
276 syslog ( LOG_INFO, "root login %s\n", fromhost );
277
Eric Andersen9e480452003-07-03 10:07:04 +0000278 run_shell ( pw-> pw_shell, 1, 0, 0
279#ifdef CONFIG_SELINUX
280 , sid
281#endif
282 ); /* exec the shell finally. */
Robert Griebl1fca5582002-06-04 20:45:46 +0000283
284 return EXIT_FAILURE;
285}
286
287
288
Eric Andersen0fbff132002-06-22 17:49:29 +0000289static int login_prompt ( char *buf_name )
Robert Griebl1fca5582002-06-04 20:45:46 +0000290{
291 char buf [1024];
292 char *sp, *ep;
Eric Andersen0fbff132002-06-22 17:49:29 +0000293 int i;
Robert Griebl1fca5582002-06-04 20:45:46 +0000294
Eric Andersen0fbff132002-06-22 17:49:29 +0000295 for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000296 print_login_prompt();
Robert Griebl1fca5582002-06-04 20:45:46 +0000297
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000298 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
299 return 0;
300
Eric Andersen0fbff132002-06-22 17:49:29 +0000301 if ( !strchr ( buf, '\n' ))
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000302 return 0;
Robert Griebl1fca5582002-06-04 20:45:46 +0000303
Glenn L McGrath9e5d6c02003-01-21 20:55:56 +0000304 for ( sp = buf; isspace ( *sp ); sp++ ) { }
305 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
306
307 *ep = 0;
Eric Andersen0fbff132002-06-22 17:49:29 +0000308 safe_strncpy(buf_name, sp, USERNAME_SIZE);
309 if(buf_name[0])
310 return 1;
311 }
312 return 0;
Robert Griebl1fca5582002-06-04 20:45:46 +0000313}
314
315
316static int check_nologin ( int amroot )
317{
Manuel Novoa III cad53642003-03-19 09:13:01 +0000318 if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000319 FILE *fp;
320 int c;
321
Manuel Novoa III cad53642003-03-19 09:13:01 +0000322 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000323 while (( c = getc ( fp )) != EOF )
324 putchar (( c == '\n' ) ? '\r' : c );
325
326 fflush ( stdout );
327 fclose ( fp );
328 } else {
329 puts ( "\r\nSystem closed for routine maintenance.\r" );
330 }
331 if ( !amroot )
332 return 1;
333
334 puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
335 }
336 return 0;
337}
338
339#ifdef CONFIG_FEATURE_SECURETTY
340
341static int check_tty ( const char *tty )
342{
343 FILE *fp;
344 int i;
345 char buf[BUFSIZ];
346
Manuel Novoa III cad53642003-03-19 09:13:01 +0000347 if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000348 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000349 for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000350 if ( !isspace ( buf[i] ))
351 break;
352 }
353 buf[++i] = '\0';
354 if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
355 continue;
356
357 if ( strcmp ( buf, tty ) == 0 ) {
358 fclose ( fp );
359 return 1;
360 }
361 }
362 fclose(fp);
363 return 0;
364 }
Eric Andersenf8701482002-11-14 10:58:17 +0000365 /* A missing securetty file is not an error. */
Robert Griebl69b57562002-12-03 19:54:12 +0000366 return 1;
Robert Griebl1fca5582002-06-04 20:45:46 +0000367}
368
369#endif
370
371/* returns 1 if true */
372static int is_my_tty ( const char *tty )
373{
374 struct stat by_name, by_fd;
375
376 if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
377 return 0;
378
379 if ( by_name. st_rdev != by_fd. st_rdev )
380 return 0;
381 else
382 return 1;
383}
384
385
386static void motd ( )
387{
388 FILE *fp;
389 register int c;
390
Manuel Novoa III cad53642003-03-19 09:13:01 +0000391 if (( fp = fopen ( bb_path_motd_file, "r" ))) {
Robert Griebl1fca5582002-06-04 20:45:46 +0000392 while (( c = getc ( fp )) != EOF )
393 putchar ( c );
394 fclose ( fp );
395 }
396}
397
398
Eric Andersenfdfe2982002-10-10 03:55:09 +0000399#ifdef CONFIG_FEATURE_U_W_TMP
Robert Griebl1fca5582002-06-04 20:45:46 +0000400// vv Taken from tinylogin utmp.c vv
401
402#define _WTMP_FILE "/var/log/wtmp"
403
404#define NO_UTENT \
405 "No utmp entry. You must exec \"login\" from the lowest level \"sh\""
406#define NO_TTY \
407 "Unable to determine your tty name."
408
409/*
410 * checkutmp - see if utmp file is correct for this process
411 *
412 * System V is very picky about the contents of the utmp file
413 * and requires that a slot for the current process exist.
414 * The utmp file is scanned for an entry with the same process
415 * ID. If no entry exists the process exits with a message.
416 *
417 * The "picky" flag is for network and other logins that may
418 * use special flags. It allows the pid checks to be overridden.
419 * This means that getty should never invoke login with any
420 * command line flags.
421 */
422
423static void checkutmp(int picky)
424{
425 char *line;
426 struct utmp *ut;
427 pid_t pid = getpid();
428
429 setutent();
430
431 /* First, try to find a valid utmp entry for this process. */
432 while ((ut = getutent()))
433 if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
434 (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
435 break;
436
437 /* If there is one, just use it, otherwise create a new one. */
438 if (ut) {
439 utent = *ut;
440 } else {
441 if (picky) {
442 puts(NO_UTENT);
443 exit(1);
444 }
445 line = ttyname(0);
446 if (!line) {
447 puts(NO_TTY);
448 exit(1);
449 }
450 if (strncmp(line, "/dev/", 5) == 0)
451 line += 5;
452 memset((void *) &utent, 0, sizeof utent);
453 utent.ut_type = LOGIN_PROCESS;
454 utent.ut_pid = pid;
455 strncpy(utent.ut_line, line, sizeof utent.ut_line);
456 /* XXX - assumes /dev/tty?? */
457 strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
458 strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
459 time(&utent.ut_time);
460 }
461}
462
Robert Griebl1fca5582002-06-04 20:45:46 +0000463/*
464 * setutmp - put a USER_PROCESS entry in the utmp file
465 *
466 * setutmp changes the type of the current utmp entry to
467 * USER_PROCESS. the wtmp file will be updated as well.
468 */
469
470static void setutmp(const char *name, const char *line)
471{
472 utent.ut_type = USER_PROCESS;
473 strncpy(utent.ut_user, name, sizeof utent.ut_user);
474 time(&utent.ut_time);
475 /* other fields already filled in by checkutmp above */
476 setutent();
477 pututline(&utent);
478 endutent();
479 updwtmp(_WTMP_FILE, &utent);
480}
Eric Andersenfdfe2982002-10-10 03:55:09 +0000481#endif /* CONFIG_FEATURE_U_W_TMP */