99f66b53bcfae03ee5bb3e25390f9eb56de9e26b
[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         const 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 )
131                 safe_strncpy ( tty, tmp, 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         tmp = pw-> pw_shell;
271         if(!tmp || !*tmp)
272                 tmp = DEFAULT_SHELL;
273         setup_environment ( tmp, 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         run_shell ( tmp, 1, 0, 0
281 #ifdef CONFIG_SELINUX
282         , sid
283 #endif
284          );     /* exec the shell finally. */
285
286         return EXIT_FAILURE;
287 }
288
289
290
291 static int login_prompt ( char *buf_name )
292 {
293         char buf [1024];
294         char *sp, *ep;
295         int i;
296
297         for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
298                 print_login_prompt();
299
300                 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
301                         return 0;
302
303                 if ( !strchr ( buf, '\n' ))
304                         return 0;
305
306                 for ( sp = buf; isspace ( *sp ); sp++ ) { }
307                 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
308
309                 *ep = 0;
310                 safe_strncpy(buf_name, sp, USERNAME_SIZE);
311                 if(buf_name[0])
312                         return 1;
313         }
314         return 0;
315 }
316
317
318 static int check_nologin ( int amroot )
319 {
320         if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
321                 FILE *fp;
322                 int c;
323
324                 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
325                         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;
335
336                 puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
337         }
338         return 0;
339 }
340
341 #ifdef CONFIG_FEATURE_SECURETTY
342
343 static int check_tty ( const char *tty )
344 {
345         FILE *fp;
346         int i;
347         char buf[BUFSIZ];
348
349         if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
350                 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
351                         for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
352                                 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         }
367         /* A missing securetty file is not an error. */
368         return 1;
369 }
370
371 #endif
372
373 /* returns 1 if true */
374 static 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;
380
381         if ( by_name. st_rdev != by_fd. st_rdev )
382                 return 0;
383         else
384                 return 1;
385 }
386
387
388 static void motd ( )
389 {
390         FILE *fp;
391         register int c;
392
393         if (( fp = fopen ( bb_path_motd_file, "r" ))) {
394                 while (( c = getc ( fp )) != EOF )
395                         putchar ( c );
396                 fclose ( fp );
397         }
398 }
399
400
401 #ifdef CONFIG_FEATURE_U_W_TMP
402 // 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
425 static 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
465 /*
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
472 static 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 }
483 #endif /* CONFIG_FEATURE_U_W_TMP */