741d15c9353bd8eb927b84577232f0498de4e3bc
[oweals/busybox.git] / loginutils / login.c
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/time.h>
14 #include <sys/types.h>
15 #include <ctype.h>
16 #include <time.h>
17
18 #include "busybox.h"
19 #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
25
26 #ifdef CONFIG_FEATURE_U_W_TMP
27 // import from utmp.c
28 static void checkutmp(int picky);
29 static void setutmp(const char *name, const char *line);
30 /* Stuff global to this file */
31 struct utmp utent;
32 #endif
33
34 // login defines
35 #define TIMEOUT       60
36 #define EMPTY_USERNAME_COUNT    10
37 #define USERNAME_SIZE 32
38
39
40 static int check_nologin ( int amroot );
41
42 #if defined CONFIG_FEATURE_SECURETTY
43 static int check_tty ( const char *tty );
44
45 #else
46 static inline int check_tty ( const char *tty )  { return 1; }
47
48 #endif
49
50 static int is_my_tty ( const char *tty );
51 static int login_prompt ( char *buf_name );
52 static void motd ( void );
53
54
55 static void alarm_handler ( int sig )
56 {
57         fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT );
58         exit ( EXIT_SUCCESS );
59 }
60
61
62 extern int login_main(int argc, char **argv)
63 {
64         char tty[BUFSIZ];
65         char full_tty[200];
66         char fromhost[512];
67         char username[USERNAME_SIZE];
68         char *tmp;
69         int amroot;
70         int flag;
71         int failed;
72         int count=0;
73         struct passwd *pw, pw_copy;
74 #ifdef CONFIG_WHEEL_GROUP
75         struct group *grp;
76 #endif
77         int opt_preserve = 0;
78         int opt_fflag = 0;
79         char *opt_host = 0;
80         int alarmstarted = 0;   
81 #ifdef CONFIG_SELINUX
82         int flask_enabled = is_flask_enabled();
83         security_id_t sid = 0, old_tty_sid, new_tty_sid;
84 #endif
85
86         username[0]=0;
87         amroot = ( getuid ( ) == 0 );
88         signal ( SIGALRM, alarm_handler );
89         
90         if (( argc > 1 ) && ( TIMEOUT > 0 )) {
91                 alarm ( TIMEOUT );
92                 alarmstarted = 1;
93         }
94
95         while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
96                 switch ( flag ) {
97                 case 'p':
98                         opt_preserve = 1;
99                         break;
100                 case 'f':
101                         /*
102                          * username must be a seperate token
103                          * (-f root, *NOT* -froot). --marekm
104                          */
105                         if ( optarg != argv[optind-1] )
106                                 bb_show_usage( );
107
108                         if ( !amroot )          /* Auth bypass only if real UID is zero */
109                                 bb_error_msg_and_die ( "-f permission denied" );
110                         
111                         safe_strncpy(username, optarg, USERNAME_SIZE);
112                         opt_fflag = 1;
113                         break;
114                 case 'h':
115                         opt_host = optarg;
116                         break;
117                 default:
118                         bb_show_usage( );
119                 }
120         }
121
122         if (optind < argc)             // user from command line (getty)
123                 safe_strncpy(username, argv[optind], USERNAME_SIZE);
124
125         if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 )) 
126                 return EXIT_FAILURE;            /* Must be a terminal */
127
128 #ifdef CONFIG_FEATURE_U_W_TMP
129         checkutmp ( !amroot );
130 #endif
131
132         tmp = ttyname ( 0 );
133         if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 ))
134                 safe_strncpy ( tty, tmp + 5, sizeof( tty ));
135         else
136                 safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
137
138 #ifdef CONFIG_FEATURE_U_W_TMP
139         if ( amroot )
140                 memset ( utent.ut_host, 0, sizeof utent.ut_host );
141 #endif
142         
143         if ( opt_host ) {
144 #ifdef CONFIG_FEATURE_U_W_TMP
145                 safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
146 #endif
147                 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host );
148         }
149         else
150                 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty );
151         
152         setpgrp();
153
154         openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH );
155
156         while ( 1 ) {
157                 failed = 0;
158
159                 if ( !username[0] )
160                         if(!login_prompt ( username ))
161                                 return EXIT_FAILURE;
162
163                 if ( !alarmstarted && ( TIMEOUT > 0 )) {
164                         alarm ( TIMEOUT );
165                         alarmstarted = 1;
166                 }
167
168                 if (!( pw = getpwnam ( username ))) {
169                         pw_copy.pw_name   = "UNKNOWN";
170                         pw_copy.pw_passwd = "!";
171                         opt_fflag = 0;
172                         failed = 1;
173                 } else 
174                         pw_copy = *pw;
175
176                 pw = &pw_copy;
177
178                 if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' ))
179                         failed = 1;
180                 
181                 if ( opt_fflag ) {
182                         opt_fflag = 0;
183                         goto auth_ok;
184                 }
185
186                 if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty )))
187                         failed = 1;
188
189                 /* Don't check the password if password entry is empty (!) */
190                 if ( !pw-> pw_passwd[0] )
191                         goto auth_ok;
192
193                 /* authorization takes place here */
194                 if ( correct_password ( pw ))
195                         goto auth_ok;
196
197                 failed = 1;
198                 
199 auth_ok:
200                 if ( !failed) 
201                         break;
202
203                 { // delay next try
204                         time_t start, now;
205                         
206                         time ( &start );
207                         now = start;
208                         while ( difftime ( now, start ) < FAIL_DELAY) {
209                                 sleep ( FAIL_DELAY );
210                                 time ( &now );
211                         }
212                 }
213
214                 puts("Login incorrect");
215                 username[0] = 0;
216                 if ( ++count == 3 ) {
217                         syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
218                         return EXIT_FAILURE;
219         }
220         }
221                 
222         alarm ( 0 );
223         if ( check_nologin ( pw-> pw_uid == 0 ))
224                 return EXIT_FAILURE;
225
226 #ifdef CONFIG_FEATURE_U_W_TMP
227         setutmp ( username, tty );
228 #endif
229 #ifdef CONFIG_SELINUX
230         if (flask_enabled)
231         {
232                 struct stat st;
233
234                 if (get_default_sid(username, 0, &sid))
235                 {
236                         fprintf(stderr, "Unable to get SID for %s\n", username);
237                         exit(1);
238                 }
239                 if (stat_secure(tty, &st, &old_tty_sid))
240                 {
241                         fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno));
242                         return EXIT_FAILURE;
243                 }
244                 if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
245                 {
246                         fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno));
247                         return EXIT_FAILURE;
248                 }
249                 if(chsid(tty, new_tty_sid) != 0)
250                 {
251                         fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno));
252                         return EXIT_FAILURE;
253                 }
254         }
255         else
256                 sid = 0;
257 #endif
258
259         if ( *tty != '/' ) 
260                 snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
261         else
262                 safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
263         
264         if ( !is_my_tty ( full_tty ))  
265                 syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
266                 
267         /* Try these, but don't complain if they fail 
268          * (for example when the root fs is read only) */
269         chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
270         chmod ( full_tty, 0600 );
271
272         change_identity ( pw );
273         setup_environment ( pw-> pw_shell, 1, !opt_preserve, pw );
274
275         motd ( );
276         signal ( SIGALRM, SIG_DFL );    /* default alarm signal */
277
278         if ( pw-> pw_uid == 0 ) 
279                 syslog ( LOG_INFO, "root login %s\n", fromhost );
280         
281         run_shell ( pw-> pw_shell, 1, 0, 0
282 #ifdef CONFIG_SELINUX
283         , sid
284 #endif
285          );     /* exec the shell finally. */
286         
287         return EXIT_FAILURE;
288 }
289
290
291
292 static int login_prompt ( char *buf_name )
293 {
294         char buf [1024];
295         char *sp, *ep;
296         int i;
297
298         for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
299                 print_login_prompt();
300
301                 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
302                         return 0;
303
304                 if ( !strchr ( buf, '\n' ))
305                         return 0;
306
307                 for ( sp = buf; isspace ( *sp ); sp++ ) { }
308                 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
309
310                 *ep = 0;                
311                 safe_strncpy(buf_name, sp, USERNAME_SIZE);
312                 if(buf_name[0])
313                         return 1;
314         }
315         return 0;
316 }
317
318
319 static int check_nologin ( int amroot )
320 {
321         if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
322                 FILE *fp;
323                 int c;
324
325                 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
326                         while (( c = getc ( fp )) != EOF )
327                                 putchar (( c == '\n' ) ? '\r' : c );
328
329                         fflush ( stdout );
330                         fclose ( fp );
331                 } else {
332                         puts ( "\r\nSystem closed for routine maintenance.\r" );
333                 }
334                 if ( !amroot )
335                         return 1;
336                         
337                 puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
338         }
339         return 0;
340 }
341
342 #ifdef CONFIG_FEATURE_SECURETTY
343
344 static int check_tty ( const char *tty )
345 {
346         FILE *fp;
347         int i;
348         char buf[BUFSIZ];
349
350         if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
351                 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
352                         for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
353                                 if ( !isspace ( buf[i] ))
354                                         break;
355                         }
356                         buf[++i] = '\0';
357                         if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
358                                 continue;
359
360                         if ( strcmp ( buf, tty ) == 0 ) {
361                                 fclose ( fp );
362                                 return 1;
363                         }
364                 }
365                 fclose(fp);
366                 return 0;
367         }
368         /* A missing securetty file is not an error. */
369         return 1;
370 }
371
372 #endif
373
374 /* returns 1 if true */
375 static int is_my_tty ( const char *tty )
376 {
377         struct stat by_name, by_fd;
378
379         if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
380                 return 0;
381                 
382         if ( by_name. st_rdev != by_fd. st_rdev )
383                 return 0;
384         else
385                 return 1;
386 }
387
388
389 static void motd ( )
390 {
391         FILE *fp;
392         register int c;
393
394         if (( fp = fopen ( bb_path_motd_file, "r" ))) {
395                 while (( c = getc ( fp )) != EOF ) 
396                         putchar ( c );          
397                 fclose ( fp );
398         }
399 }
400
401
402 #ifdef CONFIG_FEATURE_U_W_TMP
403 // vv  Taken from tinylogin utmp.c  vv
404
405 #define _WTMP_FILE "/var/log/wtmp"
406
407 #define NO_UTENT \
408         "No utmp entry.  You must exec \"login\" from the lowest level \"sh\""
409 #define NO_TTY \
410         "Unable to determine your tty name."
411
412 /*
413  * checkutmp - see if utmp file is correct for this process
414  *
415  *      System V is very picky about the contents of the utmp file
416  *      and requires that a slot for the current process exist.
417  *      The utmp file is scanned for an entry with the same process
418  *      ID.  If no entry exists the process exits with a message.
419  *
420  *      The "picky" flag is for network and other logins that may
421  *      use special flags.  It allows the pid checks to be overridden.
422  *      This means that getty should never invoke login with any
423  *      command line flags.
424  */
425
426 static void checkutmp(int picky)
427 {
428         char *line;
429         struct utmp *ut;
430         pid_t pid = getpid();
431
432         setutent();
433
434         /* First, try to find a valid utmp entry for this process.  */
435         while ((ut = getutent()))
436                 if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
437                         (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
438                         break;
439
440         /* If there is one, just use it, otherwise create a new one.  */
441         if (ut) {
442                 utent = *ut;
443         } else {
444                 if (picky) {
445                         puts(NO_UTENT);
446                         exit(1);
447                 }
448                 line = ttyname(0);
449                 if (!line) {
450                         puts(NO_TTY);
451                         exit(1);
452                 }
453                 if (strncmp(line, "/dev/", 5) == 0)
454                         line += 5;
455                 memset((void *) &utent, 0, sizeof utent);
456                 utent.ut_type = LOGIN_PROCESS;
457                 utent.ut_pid = pid;
458                 strncpy(utent.ut_line, line, sizeof utent.ut_line);
459                 /* XXX - assumes /dev/tty?? */
460                 strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
461                 strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
462                 time(&utent.ut_time);
463         }
464 }
465
466 /*
467  * setutmp - put a USER_PROCESS entry in the utmp file
468  *
469  *      setutmp changes the type of the current utmp entry to
470  *      USER_PROCESS.  the wtmp file will be updated as well.
471  */
472
473 static void setutmp(const char *name, const char *line)
474 {
475         utent.ut_type = USER_PROCESS;
476         strncpy(utent.ut_user, name, sizeof utent.ut_user);
477         time(&utent.ut_time);
478         /* other fields already filled in by checkutmp above */
479         setutent();
480         pututline(&utent);
481         endutent();
482         updwtmp(_WTMP_FILE, &utent);
483 }
484 #endif /* CONFIG_FEATURE_U_W_TMP */