Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/loginutils/login.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/loginutils/login.c
r1765 r2725 1 1 /* vi: set sw=4 ts=4: */ 2 2 /* 3 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.3 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 4 4 */ 5 6 5 #include "libbb.h" 7 #include <utmp.h> 6 #include <syslog.h> 7 #if ENABLE_FEATURE_UTMP 8 # include <utmp.h> /* USER_PROCESS */ 9 #endif 8 10 #include <sys/resource.h> 9 #include <syslog.h>10 11 11 12 #if ENABLE_SELINUX 12 # include <selinux/selinux.h> /* for is_selinux_enabled() */13 # include <selinux/get_context_list.h> /* for get_default_context() */14 # include <selinux/flask.h> /* for security class definitions */13 # include <selinux/selinux.h> /* for is_selinux_enabled() */ 14 # include <selinux/get_context_list.h> /* for get_default_context() */ 15 # include <selinux/flask.h> /* for security class definitions */ 15 16 #endif 16 17 17 18 #if ENABLE_PAM 18 19 /* PAM may include <locale.h>. We may need to undefine bbox's stub define: */ 19 # undef setlocale20 # undef setlocale 20 21 /* For some obscure reason, PAM is not in pam/xxx, but in security/xxx. 21 22 * Apparently they like to confuse people. */ 22 # include <security/pam_appl.h>23 # include <security/pam_misc.h>23 # include <security/pam_appl.h> 24 # include <security/pam_misc.h> 24 25 static const struct pam_conv conv = { 25 26 misc_conv, … … 37 38 static char* short_tty; 38 39 39 #if ENABLE_FEATURE_UTMP40 /* vv Taken from tinylogin utmp.c vv */41 /*42 * read_or_build_utent - see if utmp file is correct for this process43 *44 * System V is very picky about the contents of the utmp file45 * and requires that a slot for the current process exist.46 * The utmp file is scanned for an entry with the same process47 * ID. If no entry exists the process exits with a message.48 *49 * The "picky" flag is for network and other logins that may50 * use special flags. It allows the pid checks to be overridden.51 * This means that getty should never invoke login with any52 * command line flags.53 */54 55 static void read_or_build_utent(struct utmp *utptr, int picky)56 {57 struct utmp *ut;58 pid_t pid = getpid();59 60 setutent();61 62 /* First, try to find a valid utmp entry for this process. */63 while ((ut = getutent()))64 if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&65 (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))66 break;67 68 /* If there is one, just use it, otherwise create a new one. */69 if (ut) {70 *utptr = *ut;71 } else {72 if (picky)73 bb_error_msg_and_die("no utmp entry found");74 75 memset(utptr, 0, sizeof(*utptr));76 utptr->ut_type = LOGIN_PROCESS;77 utptr->ut_pid = pid;78 strncpy(utptr->ut_line, short_tty, sizeof(utptr->ut_line));79 /* This one is only 4 chars wide. Try to fit something80 * remotely meaningful by skipping "tty"... */81 strncpy(utptr->ut_id, short_tty + 3, sizeof(utptr->ut_id));82 strncpy(utptr->ut_user, "LOGIN", sizeof(utptr->ut_user));83 utptr->ut_time = time(NULL);84 }85 if (!picky) /* root login */86 memset(utptr->ut_host, 0, sizeof(utptr->ut_host));87 }88 89 /*90 * write_utent - put a USER_PROCESS entry in the utmp file91 *92 * write_utent changes the type of the current utmp entry to93 * USER_PROCESS. the wtmp file will be updated as well.94 */95 static void write_utent(struct utmp *utptr, const char *username)96 {97 utptr->ut_type = USER_PROCESS;98 strncpy(utptr->ut_user, username, sizeof(utptr->ut_user));99 utptr->ut_time = time(NULL);100 /* other fields already filled in by read_or_build_utent above */101 setutent();102 pututline(utptr);103 endutent();104 #if ENABLE_FEATURE_WTMP105 if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {106 close(creat(bb_path_wtmp_file, 0664));107 }108 updwtmp(bb_path_wtmp_file, utptr);109 #endif110 }111 #else /* !ENABLE_FEATURE_UTMP */112 #define read_or_build_utent(utptr, picky) ((void)0)113 #define write_utent(utptr, username) ((void)0)114 #endif /* !ENABLE_FEATURE_UTMP */115 116 40 #if ENABLE_FEATURE_NOLOGIN 117 static void die_if_nologin _and_non_root(int amroot)41 static void die_if_nologin(void) 118 42 { 119 43 FILE *fp; 120 44 int c; 121 122 if (access("/etc/nologin", F_OK)) 45 int empty = 1; 46 47 fp = fopen_for_read("/etc/nologin"); 48 if (!fp) /* assuming it does not exist */ 123 49 return; 124 50 125 fp = fopen("/etc/nologin", "r");126 if (fp) {127 while ((c = getc(fp)) != EOF)128 putchar((c=='\n') ? '\r' :c);129 fflush(stdout);130 fclose(fp);131 } else51 while ((c = getc(fp)) != EOF) { 52 if (c == '\n') 53 bb_putchar('\r'); 54 bb_putchar(c); 55 empty = 0; 56 } 57 if (empty) 132 58 puts("\r\nSystem closed for routine maintenance\r"); 133 if (!amroot) 134 exit(1); 135 puts("\r\n[Disconnect bypassed -- root login allowed]\r"); 59 60 fclose(fp); 61 fflush_all(); 62 /* Users say that they do need this prior to exit: */ 63 tcdrain(STDOUT_FILENO); 64 exit(EXIT_FAILURE); 136 65 } 137 66 #else 138 static ALWAYS_INLINE void die_if_nologin_and_non_root(int amroot) {} 67 # define die_if_nologin() ((void)0) 139 68 #endif 140 69 … … 142 71 static int check_securetty(void) 143 72 { 144 FILE *fp; 145 int i; 146 char buf[256]; 147 148 fp = fopen("/etc/securetty", "r"); 149 if (!fp) { 150 /* A missing securetty file is not an error. */ 151 return 1; 152 } 153 while (fgets(buf, sizeof(buf)-1, fp)) { 154 for (i = strlen(buf)-1; i >= 0; --i) { 155 if (!isspace(buf[i])) 156 break; 157 } 158 buf[++i] = '\0'; 159 if (!buf[0] || (buf[0] == '#')) 160 continue; 161 if (strcmp(buf, short_tty) == 0) { 162 fclose(fp); 163 return 1; 164 } 165 } 166 fclose(fp); 167 return 0; 73 char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */ 74 parser_t *parser = config_open2("/etc/securetty", fopen_for_read); 75 while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) { 76 if (strcmp(buf, short_tty) == 0) 77 break; 78 buf = NULL; 79 } 80 config_close(parser); 81 /* buf != NULL here if config file was not found, empty 82 * or line was found which equals short_tty */ 83 return buf != NULL; 168 84 } 169 85 #else 170 86 static ALWAYS_INLINE int check_securetty(void) { return 1; } 87 #endif 88 89 #if ENABLE_SELINUX 90 static void initselinux(char *username, char *full_tty, 91 security_context_t *user_sid) 92 { 93 security_context_t old_tty_sid, new_tty_sid; 94 95 if (!is_selinux_enabled()) 96 return; 97 98 if (get_default_context(username, NULL, user_sid)) { 99 bb_error_msg_and_die("can't get SID for %s", username); 100 } 101 if (getfilecon(full_tty, &old_tty_sid) < 0) { 102 bb_perror_msg_and_die("getfilecon(%s) failed", full_tty); 103 } 104 if (security_compute_relabel(*user_sid, old_tty_sid, 105 SECCLASS_CHR_FILE, &new_tty_sid) != 0) { 106 bb_perror_msg_and_die("security_change_sid(%s) failed", full_tty); 107 } 108 if (setfilecon(full_tty, new_tty_sid) != 0) { 109 bb_perror_msg_and_die("chsid(%s, %s) failed", full_tty, new_tty_sid); 110 } 111 } 112 #endif 113 114 #if ENABLE_LOGIN_SCRIPTS 115 static void run_login_script(struct passwd *pw, char *full_tty) 116 { 117 char *t_argv[2]; 118 119 t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT"); 120 if (t_argv[0]) { 121 t_argv[1] = NULL; 122 xsetenv("LOGIN_TTY", full_tty); 123 xsetenv("LOGIN_USER", pw->pw_name); 124 xsetenv("LOGIN_UID", utoa(pw->pw_uid)); 125 xsetenv("LOGIN_GID", utoa(pw->pw_gid)); 126 xsetenv("LOGIN_SHELL", pw->pw_shell); 127 spawn_and_wait(t_argv); /* NOMMU-friendly */ 128 unsetenv("LOGIN_TTY"); 129 unsetenv("LOGIN_USER"); 130 unsetenv("LOGIN_UID"); 131 unsetenv("LOGIN_GID"); 132 unsetenv("LOGIN_SHELL"); 133 } 134 } 135 #else 136 void run_login_script(struct passwd *pw, char *full_tty); 171 137 #endif 172 138 … … 181 147 do { 182 148 c = getchar(); 183 if (c == EOF) exit(1); 149 if (c == EOF) 150 exit(EXIT_FAILURE); 184 151 if (c == '\n') { 185 if (!--cntdown) exit(1); 152 if (!--cntdown) 153 exit(EXIT_FAILURE); 186 154 goto prompt; 187 155 } 188 } while (isspace(c)); 156 } while (isspace(c)); /* maybe isblank? */ 189 157 190 158 *buf++ = c; 191 159 if (!fgets(buf, size_buf-2, stdin)) 192 exit( 1);160 exit(EXIT_FAILURE); 193 161 if (!strchr(buf, '\n')) 194 exit(1); 195 while (isgraph(*buf)) buf++; 162 exit(EXIT_FAILURE); 163 while ((unsigned char)*buf > ' ') 164 buf++; 196 165 *buf = '\0'; 197 166 } … … 202 171 203 172 fd = open(bb_path_motd_file, O_RDONLY); 204 if (fd ) {205 fflush (stdout);173 if (fd >= 0) { 174 fflush_all(); 206 175 bb_copyfd_eof(fd, STDOUT_FILENO); 207 176 close(fd); … … 209 178 } 210 179 211 static void alarm_handler(int sig ATTRIBUTE_UNUSED)180 static void alarm_handler(int sig UNUSED_PARAM) 212 181 { 213 182 /* This is the escape hatch! Poor serial line users and the like … … 215 184 * We don't want to block here */ 216 185 ndelay_on(1); 217 ndelay_on(2);218 186 printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT); 219 exit(EXIT_SUCCESS); 220 } 221 222 int login_main(int argc, char **argv); 223 int login_main(int argc, char **argv) 187 fflush_all(); 188 /* unix API is brain damaged regarding O_NONBLOCK, 189 * we should undo it, or else we can affect other processes */ 190 ndelay_off(1); 191 _exit(EXIT_SUCCESS); 192 } 193 194 int login_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 195 int login_main(int argc UNUSED_PARAM, char **argv) 224 196 { 225 197 enum { … … 230 202 char *fromhost; 231 203 char username[USERNAME_SIZE]; 232 const char * tmp;233 int amroot;204 const char *shell; 205 int run_by_root; 234 206 unsigned opt; 235 207 int count = 0; 236 208 struct passwd *pw; 237 209 char *opt_host = NULL; 238 char *opt_user = NULL; 239 char full_tty[TTYNAME_SIZE]; 240 USE_SELINUX(security_context_t user_sid = NULL;) 241 USE_FEATURE_UTMP(struct utmp utent;) 242 USE_PAM(pam_handle_t *pamh;) 243 USE_PAM(int pamret;) 244 USE_PAM(const char *failed_msg;) 245 246 short_tty = full_tty; 210 char *opt_user = opt_user; /* for compiler */ 211 char *full_tty; 212 IF_SELINUX(security_context_t user_sid = NULL;) 213 #if ENABLE_PAM 214 int pamret; 215 pam_handle_t *pamh; 216 const char *pamuser; 217 const char *failed_msg; 218 struct passwd pwdstruct; 219 char pwdbuf[256]; 220 #endif 221 247 222 username[0] = '\0'; 248 amroot = (getuid() == 0);249 223 signal(SIGALRM, alarm_handler); 250 224 alarm(TIMEOUT); 225 226 /* More of suid paranoia if called by non-root: */ 227 /* Clear dangerous stuff, set PATH */ 228 run_by_root = !sanitize_env_if_suid(); 251 229 252 230 /* Mandatory paranoia for suid applet: … … 258 236 opt = getopt32(argv, "f:h:p", &opt_user, &opt_host); 259 237 if (opt & LOGIN_OPT_f) { 260 if (! amroot)238 if (!run_by_root) 261 239 bb_error_msg_and_die("-f is for root only"); 262 240 safe_strncpy(username, opt_user, sizeof(username)); 263 241 } 264 if (optind < argc) /* user from command line (getty) */ 265 safe_strncpy(username, argv[optind], sizeof(username)); 242 argv += optind; 243 if (argv[0]) /* user from command line (getty) */ 244 safe_strncpy(username, argv[0], sizeof(username)); 266 245 267 246 /* Let's find out and memorize our tty */ 268 if (!isatty(0) || !isatty(1) || !isatty(2)) 269 return EXIT_FAILURE; /* Must be a terminal */ 270 safe_strncpy(full_tty, "UNKNOWN", sizeof(full_tty)); 271 tmp = ttyname(0); 272 if (tmp) { 273 safe_strncpy(full_tty, tmp, sizeof(full_tty)); 274 if (strncmp(full_tty, "/dev/", 5) == 0) 275 short_tty = full_tty + 5; 276 } 277 278 read_or_build_utent(&utent, !amroot); 247 if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO)) 248 return EXIT_FAILURE; /* Must be a terminal */ 249 full_tty = xmalloc_ttyname(STDIN_FILENO); 250 if (!full_tty) 251 full_tty = xstrdup("UNKNOWN"); 252 short_tty = skip_dev_pfx(full_tty); 279 253 280 254 if (opt_host) { 281 USE_FEATURE_UTMP(282 safe_strncpy(utent.ut_host, opt_host, sizeof(utent.ut_host));283 )284 255 fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host); 285 } else 256 } else { 286 257 fromhost = xasprintf(" on '%s'", short_tty); 258 } 287 259 288 260 /* Was breaking "login <username>" from shell command line: */ 289 261 /*bb_setpgrp();*/ 290 262 291 openlog(applet_name, LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);263 openlog(applet_name, LOG_PID | LOG_CONS, LOG_AUTH); 292 264 293 265 while (1) { 266 /* flush away any type-ahead (as getty does) */ 267 tcflush(0, TCIFLUSH); 268 294 269 if (!username[0]) 295 270 get_username_or_die(username, sizeof(username)); … … 298 273 pamret = pam_start("login", username, &conv, &pamh); 299 274 if (pamret != PAM_SUCCESS) { 300 failed_msg = " pam_start";275 failed_msg = "start"; 301 276 goto pam_auth_failed; 302 277 } … … 304 279 pamret = pam_set_item(pamh, PAM_TTY, short_tty); 305 280 if (pamret != PAM_SUCCESS) { 306 failed_msg = " pam_set_item(TTY)";281 failed_msg = "set_item(TTY)"; 307 282 goto pam_auth_failed; 308 283 } 309 284 pamret = pam_authenticate(pamh, 0); 310 285 if (pamret != PAM_SUCCESS) { 311 failed_msg = " pam_authenticate";286 failed_msg = "authenticate"; 312 287 goto pam_auth_failed; 313 288 /* TODO: or just "goto auth_failed" … … 319 294 pamret = pam_acct_mgmt(pamh, 0); 320 295 if (pamret != PAM_SUCCESS) { 321 failed_msg = "acc ount setup";296 failed_msg = "acct_mgmt"; 322 297 goto pam_auth_failed; 323 298 } 324 299 /* read user back */ 325 { 326 const char *pamuser; 327 /* gcc: "dereferencing type-punned pointer breaks aliasing rules..." 328 * thus we cast to (void*) */ 329 if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) { 330 failed_msg = "pam_get_item(USER)"; 331 goto pam_auth_failed; 332 } 333 safe_strncpy(username, pamuser, sizeof(username)); 334 } 335 /* If we get here, the user was authenticated, and is 336 * granted access. */ 337 pw = getpwnam(username); 338 if (pw) 339 break; 340 goto auth_failed; 300 pamuser = NULL; 301 /* gcc: "dereferencing type-punned pointer breaks aliasing rules..." 302 * thus we cast to (void*) */ 303 if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) { 304 failed_msg = "get_item(USER)"; 305 goto pam_auth_failed; 306 } 307 if (!pamuser || !pamuser[0]) 308 goto auth_failed; 309 safe_strncpy(username, pamuser, sizeof(username)); 310 /* Don't use "pw = getpwnam(username);", 311 * PAM is said to be capable of destroying static storage 312 * used by getpwnam(). We are using safe(r) function */ 313 pw = NULL; 314 getpwnam_r(username, &pwdstruct, pwdbuf, sizeof(pwdbuf), &pw); 315 if (!pw) 316 goto auth_failed; 317 pamret = pam_open_session(pamh, 0); 318 if (pamret != PAM_SUCCESS) { 319 failed_msg = "open_session"; 320 goto pam_auth_failed; 321 } 322 pamret = pam_setcred(pamh, PAM_ESTABLISH_CRED); 323 if (pamret != PAM_SUCCESS) { 324 failed_msg = "setcred"; 325 goto pam_auth_failed; 326 } 327 break; /* success, continue login process */ 328 341 329 pam_auth_failed: 342 bb_error_msg("%s failed: %s (%d)", failed_msg, pam_strerror(pamh, pamret), pamret); 330 /* syslog, because we don't want potential attacker 331 * to know _why_ login failed */ 332 syslog(LOG_WARNING, "pam_%s call failed: %s (%d)", failed_msg, 333 pam_strerror(pamh, pamret), pamret); 343 334 safe_strncpy(username, "UNKNOWN", sizeof(username)); 344 335 #else /* not PAM */ … … 374 365 syslog(LOG_WARNING, "invalid password for '%s'%s", 375 366 username, fromhost); 367 368 if (ENABLE_FEATURE_CLEAN_UP) 369 free(fromhost); 370 376 371 return EXIT_FAILURE; 377 372 } 378 373 username[0] = '\0'; 379 } 374 } /* while (1) */ 380 375 381 376 alarm(0); 382 die_if_nologin_and_non_root(pw->pw_uid == 0); 383 384 write_utent(&utent, username); 385 386 #if ENABLE_SELINUX 387 if (is_selinux_enabled()) { 388 security_context_t old_tty_sid, new_tty_sid; 389 390 if (get_default_context(username, NULL, &user_sid)) { 391 bb_error_msg_and_die("cannot get SID for %s", 392 username); 393 } 394 if (getfilecon(full_tty, &old_tty_sid) < 0) { 395 bb_perror_msg_and_die("getfilecon(%s) failed", 396 full_tty); 397 } 398 if (security_compute_relabel(user_sid, old_tty_sid, 399 SECCLASS_CHR_FILE, &new_tty_sid) != 0) { 400 bb_perror_msg_and_die("security_change_sid(%s) failed", 401 full_tty); 402 } 403 if (setfilecon(full_tty, new_tty_sid) != 0) { 404 bb_perror_msg_and_die("chsid(%s, %s) failed", 405 full_tty, new_tty_sid); 406 } 407 } 408 #endif 377 /* We can ignore /etc/nologin if we are logging in as root, 378 * it doesn't matter whether we are run by root or not */ 379 if (pw->pw_uid != 0) 380 die_if_nologin(); 381 382 IF_SELINUX(initselinux(username, full_tty, &user_sid)); 383 409 384 /* Try these, but don't complain if they fail. 410 385 * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */ … … 412 387 fchmod(0, 0600); 413 388 414 if (ENABLE_LOGIN_SCRIPTS) { 415 char *t_argv[2]; 416 417 t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT"); 418 if (t_argv[0]) { 419 t_argv[1] = NULL; 420 xsetenv("LOGIN_TTY", full_tty); 421 xsetenv("LOGIN_USER", pw->pw_name); 422 xsetenv("LOGIN_UID", utoa(pw->pw_uid)); 423 xsetenv("LOGIN_GID", utoa(pw->pw_gid)); 424 xsetenv("LOGIN_SHELL", pw->pw_shell); 425 xspawn(t_argv); /* NOMMU-friendly */ 426 /* All variables are unset by setup_environment */ 427 wait(NULL); 428 } 429 } 389 update_utmp(getpid(), USER_PROCESS, short_tty, username, run_by_root ? opt_host : NULL); 390 391 /* We trust environment only if we run by root */ 392 if (ENABLE_LOGIN_SCRIPTS && run_by_root) 393 run_login_script(pw, full_tty); 430 394 431 395 change_identity(pw); 432 tmp= pw->pw_shell;433 if (! tmp || !*tmp)434 tmp= DEFAULT_SHELL;435 /* setup_environment params: shell, loginshell, changeenv, pw */436 setup_environment(tmp, 1, !(opt & LOGIN_OPT_p), pw);437 /* FIXME: login shell = 1 -> 3rd parameter is ignored! */396 shell = pw->pw_shell; 397 if (!shell || !shell[0]) 398 shell = DEFAULT_SHELL; 399 setup_environment(shell, 400 (!(opt & LOGIN_OPT_p) * SETUP_ENV_CLEARENV) + SETUP_ENV_CHANGEENV, 401 pw); 438 402 439 403 motd(); … … 441 405 if (pw->pw_uid == 0) 442 406 syslog(LOG_INFO, "root login%s", fromhost); 443 #if ENABLE_SELINUX 407 408 if (ENABLE_FEATURE_CLEAN_UP) 409 free(fromhost); 410 444 411 /* well, a simple setexeccon() here would do the job as well, 445 412 * but let's play the game for now */ 446 set_current_security_context(user_sid); 447 #endif 413 IF_SELINUX(set_current_security_context(user_sid);) 448 414 449 415 // util-linux login also does: … … 456 422 // If this stuff is really needed, add it and explain why! 457 423 458 /* set signals to defaults */ 459 signal(SIGALRM, SIG_DFL); 424 /* Set signals to defaults */ 425 /* Non-ignored signals revert to SIG_DFL on exec anyway */ 426 /*signal(SIGALRM, SIG_DFL);*/ 427 460 428 /* Is this correct? This way user can ctrl-c out of /etc/profile, 461 429 * potentially creating security breach (tested with bash 3.0). … … 463 431 * Maybe bash is buggy? 464 432 * Need to find out what standards say about /bin/login - 465 * should itleave SIGINT etc enabled or disabled? */433 * should we leave SIGINT etc enabled or disabled? */ 466 434 signal(SIGINT, SIG_DFL); 467 435 468 436 /* Exec login shell with no additional parameters */ 469 run_shell( tmp, 1, NULL, NULL);437 run_shell(shell, 1, NULL, NULL); 470 438 471 439 /* return EXIT_FAILURE; - not reached */
Note:
See TracChangeset
for help on using the changeset viewer.