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