www.usr.com/support/gpl/USR9108_release1.5.tar.gz
[bcm963xx.git] / userapps / opensource / busybox / 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 separate 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 if ( tmp && *tmp == '/' )
133                 safe_strncpy ( tty, tmp, sizeof( tty ));
134         else
135                 safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
136
137 #ifdef CONFIG_FEATURE_U_W_TMP
138         if ( amroot )
139                 memset ( utent.ut_host, 0, sizeof utent.ut_host );
140 #endif
141
142         if ( opt_host ) {
143 #ifdef CONFIG_FEATURE_U_W_TMP
144                 safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
145 #endif
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 #ifdef CONFIG_FEATURE_U_W_TMP
226         setutmp ( username, tty );
227 #endif
228 #ifdef CONFIG_SELINUX
229         if (flask_enabled)
230         {
231                 struct stat st;
232
233                 if (get_default_sid(username, 0, &sid))
234                 {
235                         fprintf(stderr, "Unable to get SID for %s\n", username);
236                         exit(1);
237                 }
238                 if (stat_secure(tty, &st, &old_tty_sid))
239                 {
240                         fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno));
241                         return EXIT_FAILURE;
242                 }
243                 if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
244                 {
245                         fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno));
246                         return EXIT_FAILURE;
247                 }
248                 if(chsid(tty, new_tty_sid) != 0)
249                 {
250                         fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno));
251                         return EXIT_FAILURE;
252                 }
253         }
254         else
255                 sid = 0;
256 #endif
257
258         if ( *tty != '/' )
259                 snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
260         else
261                 safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
262
263         if ( !is_my_tty ( full_tty ))
264                 syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
265
266         /* Try these, but don't complain if they fail
267          * (for example when the root fs is read only) */
268         chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
269         chmod ( full_tty, 0600 );
270
271         change_identity ( pw );
272         tmp = pw-> pw_shell;
273         if(!tmp || !*tmp)
274                 tmp = DEFAULT_SHELL;
275         setup_environment ( tmp, 1, !opt_preserve, pw );
276
277         motd ( );
278         signal ( SIGALRM, SIG_DFL );    /* default alarm signal */
279
280         if ( pw-> pw_uid == 0 )
281                 syslog ( LOG_INFO, "root login %s\n", fromhost );
282         run_shell ( tmp, 1, 0, 0
283 #ifdef CONFIG_SELINUX
284         , sid
285 #endif
286          );     /* exec the shell finally. */
287
288         return EXIT_FAILURE;
289 }
290
291
292
293 static int login_prompt ( char *buf_name )
294 {
295         char buf [1024];
296         char *sp, *ep;
297         int i;
298
299         for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
300                 print_login_prompt();
301
302                 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
303                         return 0;
304
305                 if ( !strchr ( buf, '\n' ))
306                         return 0;
307
308                 for ( sp = buf; isspace ( *sp ); sp++ ) { }
309                 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
310
311                 *ep = 0;
312                 safe_strncpy(buf_name, sp, USERNAME_SIZE);
313                 if(buf_name[0])
314                         return 1;
315         }
316         return 0;
317 }
318
319
320 static int check_nologin ( int amroot )
321 {
322         if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
323                 FILE *fp;
324                 int c;
325
326                 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
327                         while (( c = getc ( fp )) != EOF )
328                                 putchar (( c == '\n' ) ? '\r' : c );
329
330                         fflush ( stdout );
331                         fclose ( fp );
332                 } else {
333                         puts ( "\r\nSystem closed for routine maintenance.\r" );
334                 }
335                 if ( !amroot )
336                         return 1;
337
338                 puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
339         }
340         return 0;
341 }
342
343 #ifdef CONFIG_FEATURE_SECURETTY
344
345 static int check_tty ( const char *tty )
346 {
347         FILE *fp;
348         int i;
349         char buf[BUFSIZ];
350
351         if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
352                 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
353                         for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
354                                 if ( !isspace ( buf[i] ))
355                                         break;
356                         }
357                         buf[++i] = '\0';
358                         if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
359                                 continue;
360
361                         if ( strcmp ( buf, tty ) == 0 ) {
362                                 fclose ( fp );
363                                 return 1;
364                         }
365                 }
366                 fclose(fp);
367                 return 0;
368         }
369         /* A missing securetty file is not an error. */
370         return 1;
371 }
372
373 #endif
374
375 /* returns 1 if true */
376 static int is_my_tty ( const char *tty )
377 {
378         struct stat by_name, by_fd;
379
380         if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
381                 return 0;
382
383         if ( by_name. st_rdev != by_fd. st_rdev )
384                 return 0;
385         else
386                 return 1;
387 }
388
389
390 static void motd ( )
391 {
392         FILE *fp;
393         register int c;
394
395         if (( fp = fopen ( bb_path_motd_file, "r" ))) {
396                 while (( c = getc ( fp )) != EOF )
397                         putchar ( c );
398                 fclose ( fp );
399         }
400 }
401
402
403 #ifdef CONFIG_FEATURE_U_W_TMP
404 // vv  Taken from tinylogin utmp.c  vv
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         if (access(_PATH_WTMP, R_OK|W_OK) == -1) {
482                 close(creat(_PATH_WTMP, 0664));
483         }
484         updwtmp(_PATH_WTMP, &utent);
485 }
486 #endif /* CONFIG_FEATURE_U_W_TMP */