source: MondoRescue/branches/stable/mindi-busybox/loginutils/login.c@ 1247

Last change on this file since 1247 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

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