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