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