c2bada258f637b6ec589cafbae2f617bf5cf5af6
[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         alarm ( TIMEOUT );
90         alarmstarted = 1;
91         
92         while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
93                 switch ( flag ) {
94                 case 'p':
95                         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] )
103                                 bb_show_usage( );
104
105                         if ( !amroot )          /* Auth bypass only if real UID is zero */
106                                 bb_error_msg_and_die ( "-f permission denied" );
107                         
108                         safe_strncpy(username, optarg, USERNAME_SIZE);
109                         opt_fflag = 1;
110                         break;
111                 case 'h':
112                         opt_host = optarg;
113                         break;
114                 default:
115                         bb_show_usage( );
116                 }
117         }
118
119         if (optind < argc)             // user from command line (getty)
120                 safe_strncpy(username, argv[optind], USERNAME_SIZE);
121
122         if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 )) 
123                 return EXIT_FAILURE;            /* Must be a terminal */
124
125 #ifdef CONFIG_FEATURE_U_W_TMP
126         checkutmp ( !amroot );
127 #endif
128
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
135 #ifdef CONFIG_FEATURE_U_W_TMP
136         if ( amroot )
137                 memset ( utent.ut_host, 0, sizeof utent.ut_host );
138 #endif
139         
140         if ( opt_host ) {
141 #ifdef CONFIG_FEATURE_U_W_TMP
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         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                 
196 auth_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");
212                 username[0] = 0;
213                 if ( ++count == 3 ) {
214                         syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
215                         return EXIT_FAILURE;
216         }
217         }
218                 
219         alarm ( 0 );
220         if ( check_nologin ( pw-> pw_uid == 0 ))
221                 return EXIT_FAILURE;
222
223 #ifdef CONFIG_FEATURE_U_W_TMP
224         setutmp ( username, tty );
225 #endif
226 #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
256         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         
278         run_shell ( pw-> pw_shell, 1, 0, 0
279 #ifdef CONFIG_SELINUX
280         , sid
281 #endif
282          );     /* exec the shell finally. */
283         
284         return EXIT_FAILURE;
285 }
286
287
288
289 static int login_prompt ( char *buf_name )
290 {
291         char buf [1024];
292         char *sp, *ep;
293         int i;
294
295         for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
296                 print_login_prompt();
297
298                 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
299                         return 0;
300
301                 if ( !strchr ( buf, '\n' ))
302                         return 0;
303
304                 for ( sp = buf; isspace ( *sp ); sp++ ) { }
305                 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
306
307                 *ep = 0;                
308                 safe_strncpy(buf_name, sp, USERNAME_SIZE);
309                 if(buf_name[0])
310                         return 1;
311         }
312         return 0;
313 }
314
315
316 static int check_nologin ( int amroot )
317 {
318         if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
319                 FILE *fp;
320                 int c;
321
322                 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
323                         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
341 static int check_tty ( const char *tty )
342 {
343         FILE *fp;
344         int i;
345         char buf[BUFSIZ];
346
347         if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
348                 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
349                         for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
350                                 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         }
365         /* A missing securetty file is not an error. */
366         return 1;
367 }
368
369 #endif
370
371 /* returns 1 if true */
372 static 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
386 static void motd ( )
387 {
388         FILE *fp;
389         register int c;
390
391         if (( fp = fopen ( bb_path_motd_file, "r" ))) {
392                 while (( c = getc ( fp )) != EOF ) 
393                         putchar ( c );          
394                 fclose ( fp );
395         }
396 }
397
398
399 #ifdef CONFIG_FEATURE_U_W_TMP
400 // 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
423 static 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
463 /*
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
470 static 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 }
481 #endif /* CONFIG_FEATURE_U_W_TMP */