static void change_passwd (struct group *gr) #endif { char *cp; static char pass[BUFSIZ]; int retries; /* * A new password is to be entered and it must be encrypted, etc. * The password will be prompted for twice, and both entries must be * identical. There is no need to validate the old password since * the invoker is either the group owner, or root. */ printf (_("Changing the password for group %s\n"), group); for (retries = 0; retries < RETRIES; retries++) { cp = getpass (_("New Password: "******"Re-enter new password: "******"They don't match; try again")); } } if (retries == RETRIES) { fprintf (stderr, _("%s: Try again later\n"), Prog); exit (1); } cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL)); memzero (pass, sizeof pass); #ifdef SHADOWGRP if (is_shadowgrp) { sg->sg_passwd = cp; } else #endif { gr->gr_passwd = cp; } }
int cryptpw_main(int argc UNUSED_PARAM, char **argv) { /* Supports: cryptpw -m sha256 PASS 'rounds=999999999$SALT' */ char salt[MAX_PW_SALT_LEN + sizeof("rounds=999999999$")]; char *salt_ptr; char *password; const char *opt_m, *opt_S; int fd; #if ENABLE_LONG_OPTS static const char mkpasswd_longopts[] ALIGN1 = "stdin\0" No_argument "s" "password-fd\0" Required_argument "P" "salt\0" Required_argument "S" "method\0" Required_argument "m" ; applet_long_options = mkpasswd_longopts; #endif fd = STDIN_FILENO; opt_m = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; opt_S = NULL; /* at most two non-option arguments; -P NUM */ opt_complementary = "?2"; getopt32(argv, "sP:+S:m:a:", &fd, &opt_S, &opt_m, &opt_m); argv += optind; /* have no idea how to handle -s... */ if (argv[0] && !opt_S) opt_S = argv[1]; salt_ptr = crypt_make_pw_salt(salt, opt_m); if (opt_S) /* put user's data after the "$N$" prefix */ safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1)); xmove_fd(fd, STDIN_FILENO); password = argv[0]; if (!password) { /* Only mkpasswd, and only from tty, prompts. * Otherwise it is a plain read. */ password = (ENABLE_MKPASSWD && isatty(STDIN_FILENO) && applet_name[0] == 'm') ? bb_ask_stdin("Password: ") : xmalloc_fgetline(stdin) ; /* may still be NULL on EOF/error */ } if (password) puts(pw_encrypt(password, salt, 1)); return EXIT_SUCCESS; }
/* * valid - compare encrypted passwords * * Valid() compares the DES encrypted password from the password file * against the password which the user has entered after it has been * encrypted using the same salt as the original. Entries which do * not have a password file entry have a NULL pw_name field and this * is used to indicate that a dummy salt must be used to encrypt the * password anyway. */ bool valid (const char *password, const struct passwd *ent) { const char *encrypted; /*@observer@*/const char *salt; /* * Start with blank or empty password entries. Always encrypt * a password if no such user exists. Only if the ID exists and * the password is really empty do you return quickly. This * routine is meant to waste CPU time. */ if ((NULL != ent->pw_name) && ('\0' == ent->pw_passwd[0])) { if ('\0' == password[0]) { return true; /* user entered nothing */ } else { return false; /* user entered something! */ } } /* * If there is no entry then we need a salt to use. */ if ((NULL == ent->pw_name) || ('\0' == ent->pw_passwd[0])) { salt = "xx"; } else { salt = ent->pw_passwd; } /* * Now, perform the encryption using the salt from before on * the users input. Since we always encrypt the string, it * should be very difficult to determine if the user exists by * looking at execution time. */ encrypted = pw_encrypt (password, salt); /* * One last time we must deal with there being no password file * entry for the user. We use the pw_name == NULL idiom to * cause non-existent users to not be validated. */ if ( (NULL != ent->pw_name) && (strcmp (encrypted, ent->pw_passwd) == 0)) { return true; } else { return false; } }
int cryptpw_main(int argc UNUSED_PARAM, char **argv) { char salt[MAX_PW_SALT_LEN]; char *salt_ptr; const char *opt_m, *opt_S; int fd; #if ENABLE_LONG_OPTS static const char mkpasswd_longopts[] ALIGN1 = "stdin\0" No_argument "s" "password-fd\0" Required_argument "P" "salt\0" Required_argument "S" "method\0" Required_argument "m" ; applet_long_options = mkpasswd_longopts; #endif fd = STDIN_FILENO; opt_m = "d"; opt_S = NULL; /* at most two non-option arguments; -P NUM */ opt_complementary = "?2:P+"; getopt32(argv, "sP:S:m:a:", &fd, &opt_S, &opt_m, &opt_m); argv += optind; /* have no idea how to handle -s... */ if (argv[0] && !opt_S) opt_S = argv[1]; salt_ptr = crypt_make_pw_salt(salt, opt_m); if (opt_S) safe_strncpy(salt_ptr, opt_S, sizeof(salt) - (sizeof("$N$")-1)); xmove_fd(fd, STDIN_FILENO); puts(pw_encrypt( argv[0] ? argv[0] : ( /* Only mkpasswd, and only from tty, prompts. * Otherwise it is a plain read. */ (isatty(STDIN_FILENO) && applet_name[0] == 'm') ? bb_ask_stdin("Password: ") : xmalloc_fgetline(stdin) ), salt, 1)); return EXIT_SUCCESS; }
int check_auth(char *login, char *passwd) { char *cpass; struct spwd *spwd= getspnam(login); #ifdef NEED_UID struct passwd *pwd; #endif #if defined(CHECK_LOGIN_EXPIRATION) || defined(CHECK_PASSWORD_EXPIRATION) int today= time(NULL)/(24*60*60); #endif if (spwd == NULL) return(errno == EACCES ? STATUS_INT_NOROOT : STATUS_UNKNOWN); #ifdef NEED_UID if ((pwd= getpwnam(login)) == NULL) return(STATUS_UNKNOWN); hisuid= pwd->pw_uid; haveuid= 1; #endif #ifdef MIN_UNIX_UID if (hisuid < MIN_UNIX_UID) return(STATUS_BLOCKED); #endif #ifdef SHADOW_JFH cpass= pw_encrypt(passwd, spwd->sp_pwdp); #else cpass= crypt(passwd, spwd->sp_pwdp); #endif if (strcmp(cpass, spwd->sp_pwdp)) return STATUS_INVALID; #ifdef CHECK_LOGIN_EXPIRATION if (spwd->sp_expire >= 0 && spwd->sp_expire < today) return STATUS_EXPIRED; #endif #ifdef CHECK_PASSWORD_EXPIRATION /* Root forced password expiration */ if (spwd->sp_lstchg == 0) return STATUS_PW_EXPIRED; /* Normal password expiration */ /* We used to have sp_lstchg + sp_max + sp_inact < today * here, apparantly during the inact period you are only supposed to be able * to get on to change your password, not anything else, show we shouldn't * allow access during that period. */ if (spwd->sp_max >= 0 && spwd->sp_lstchg + spwd->sp_max < today) return STATUS_PW_EXPIRED; #endif return STATUS_OK; }
int cryptpw_main(int argc, char **argv) { char salt[sizeof("$N$XXXXXXXX")]; if (!getopt32(argc, argv, "a:", NULL) || argv[optind - 1][0] != 'd') { strcpy(salt, "$1$"); /* Too ugly, and needs even more magic to handle endianness: */ //((uint32_t*)&salt)[0] = '$' + '1'*0x100 + '$'*0x10000; /* Hope one day gcc will do it itself (inlining strcpy) */ crypt_make_salt(salt + 3, 4); /* md5 */ } else { crypt_make_salt(salt, 1); /* des */ } puts(pw_encrypt(argv[optind] ? argv[optind] : xmalloc_getline(stdin), salt)); return 0; }
static void update_passwd (struct passwd *pwd, const char *password) { void *crypt_arg = NULL; if (crypt_method != NULL) { #ifdef USE_SHA_CRYPT if (sflg) { crypt_arg = &sha_rounds; } #endif } if ((crypt_method != NULL) && (0 == strcmp(crypt_method, "NONE"))) { pwd->pw_passwd = (char *)password; } else { pwd->pw_passwd = pw_encrypt (password, crypt_make_salt (crypt_method, crypt_arg)); } }
int correct_password(const struct passwd *pw) { char *unencrypted, *encrypted; const char *correct; int r; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ struct spwd spw; char buffer[256]; #endif /* fake salt. crypt() can choke otherwise. */ correct = "aa"; if (!pw) { /* "aa" will never match */ goto fake_it; } correct = pw->pw_passwd; #if ENABLE_FEATURE_SHADOWPASSWDS if ((correct[0] == 'x' || correct[0] == '*') && !correct[1]) { /* getspnam_r may return 0 yet set result to NULL. * At least glibc 2.4 does this. Be extra paranoid here. */ struct spwd *result = NULL; r = getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result); correct = (r || !result) ? "aa" : result->sp_pwdp; } #endif if (!correct[0]) /* empty password field? */ return 1; fake_it: unencrypted = bb_askpass(0, "Password: "); if (!unencrypted) { return 0; } encrypted = pw_encrypt(unencrypted, correct, 1); r = (strcmp(encrypted, correct) == 0); free(encrypted); memset(unencrypted, 0, strlen(unencrypted)); return r; }
int cryptpw_main(int argc UNUSED_PARAM, char **argv) { char salt[sizeof("$N$XXXXXXXX")]; char *opt_a; if (!getopt32(argv, "a:", &opt_a) || opt_a[0] != 'd') { salt[0] = '$'; salt[1] = '1'; salt[2] = '$'; crypt_make_salt(salt + 3, 4, 0); /* md5 */ #if TESTING strcpy(salt + 3, "ajg./bcf"); #endif } else { crypt_make_salt(salt, 1, 0); /* des */ #if TESTING strcpy(salt, "a."); #endif } puts(pw_encrypt(argv[optind] ? argv[optind] : xmalloc_fgetline(stdin), salt, 1)); return 0; }
extern int vlock_main(int argc, char **argv) { sigset_t sig; struct sigaction sa; struct vt_mode vtm; int times = 0; struct termios term; if (argc > 2) { bb_show_usage(); } if (argc == 2) { if (strncmp(argv[1], "-a", 2)) { bb_show_usage(); } else { o_lock_all = 1; } } if ((pw = getpwuid(getuid())) == NULL) { bb_error_msg_and_die("no password for uid %d\n", getuid()); } #ifdef CONFIG_FEATURE_SHADOWPASSWDS if ((strcmp(pw->pw_passwd, "x") == 0) || (strcmp(pw->pw_passwd, "*") == 0)) { if ((spw = getspuid(getuid())) == NULL) { bb_error_msg_and_die("could not read shadow password for uid %d: %s\n", getuid(), strerror(errno)); } if (spw->sp_pwdp) { pw->pw_passwd = spw->sp_pwdp; } } #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') { bb_error_msg_and_die("Account disabled for uid %d\n", getuid()); } /* we no longer need root privs */ setuid(getuid()); setgid(getgid()); if ((vfd = open("/dev/tty", O_RDWR)) < 0) { bb_error_msg_and_die("/dev/tty"); }; if (ioctl(vfd, VT_GETMODE, &vtm) < 0) { bb_error_msg_and_die("/dev/tty"); }; /* mask a bunch of signals */ sigprocmask(SIG_SETMASK, NULL, &sig); sigdelset(&sig, SIGUSR1); sigdelset(&sig, SIGUSR2); sigaddset(&sig, SIGTSTP); sigaddset(&sig, SIGTTIN); sigaddset(&sig, SIGTTOU); sigaddset(&sig, SIGHUP); sigaddset(&sig, SIGCHLD); sigaddset(&sig, SIGQUIT); sigaddset(&sig, SIGINT); sigemptyset(&(sa.sa_mask)); sa.sa_flags = SA_RESTART; sa.sa_handler = release_vt; sigaction(SIGUSR1, &sa, NULL); sa.sa_handler = acquire_vt; sigaction(SIGUSR2, &sa, NULL); /* need to handle some signals so that we don't get killed by them */ sa.sa_handler = SIG_IGN; sigaction(SIGHUP, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGTSTP, &sa, NULL); ovtm = vtm; vtm.mode = VT_PROCESS; vtm.relsig = SIGUSR1; vtm.acqsig = SIGUSR2; ioctl(vfd, VT_SETMODE, &vtm); tcgetattr(STDIN_FILENO, &oterm); term = oterm; term.c_iflag &= ~BRKINT; term.c_iflag |= IGNBRK; term.c_lflag &= ~ISIG; term.c_lflag &= ~(ECHO | ECHOCTL); tcsetattr(STDIN_FILENO, TCSANOW, &term); do { char *pass, *crypt_pass; char prompt[100]; if (o_lock_all) { printf("All Virtual Consoles locked.\n"); } else { printf("This Virtual Console locked.\n"); } fflush(stdout); snprintf(prompt, 100, "%s's password: "******"getpass"); restore_terminal(); exit(1); } crypt_pass = pw_encrypt(pass, pw->pw_passwd); if (strncmp(crypt_pass, pw->pw_passwd, sizeof(crypt_pass)) == 0) { memset(pass, 0, strlen(pass)); memset(crypt_pass, 0, strlen(crypt_pass)); restore_terminal(); return 0; } memset(pass, 0, strlen(pass)); memset(crypt_pass, 0, strlen(crypt_pass)); if (isatty(STDIN_FILENO) == 0) { perror("isatty"); restore_terminal(); exit(1); } sleep(++times); printf("Password incorrect.\n"); if (times >= 3) { sleep(15); times = 2; } } while (1); }
int main (int argc, char **argv) { char buf[BUFSIZ]; char *name; char *newpwd; char *cp; #ifdef SHADOWGRP const struct sgrp *sg; struct sgrp newsg; #endif const struct group *gr; struct group newgr; int errors = 0; int line = 0; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); process_flags (argc, argv); OPENLOG ("chgpasswd"); check_perms (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif open_files (); /* * Read each line, separating the group name from the password. The * group entry for each group will be looked up in the appropriate * file (gshadow or group) and the password changed. */ while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) { line++; cp = strrchr (buf, '\n'); if (NULL != cp) { *cp = '\0'; } else { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; continue; } /* * The group's name is the first field. It is separated from * the password with a ":" character which is replaced with a * NUL to give the new password. The new password will then * be encrypted in the normal fashion with a new salt * generated, unless the '-e' is given, in which case it is * assumed to already be encrypted. */ name = buf; cp = strchr (name, ':'); if (NULL != cp) { *cp = '\0'; cp++; } else { fprintf (stderr, _("%s: line %d: missing new password\n"), Prog, line); errors++; continue; } newpwd = cp; if ( (!eflg) && ( (NULL == crypt_method) || (0 != strcmp (crypt_method, "NONE")))) { void *arg = NULL; const char *salt; if (md5flg) { crypt_method = "MD5"; } #ifdef USE_SHA_CRYPT if (sflg) { arg = &sha_rounds; } #endif salt = crypt_make_salt (crypt_method, arg); cp = pw_encrypt (newpwd, salt); if (NULL == cp) { fprintf (stderr, _("%s: failed to crypt password with salt '%s': %s\n"), Prog, salt, strerror (errno)); fail_exit (1); } } /* * Get the group file entry for this group. The group must * already exist. */ gr = gr_locate (name); if (NULL == gr) { fprintf (stderr, _("%s: line %d: group '%s' does not exist\n"), Prog, line, name); errors++; continue; } #ifdef SHADOWGRP if (is_shadow_grp) { /* The gshadow entry should be updated if the * group entry has a password set to 'x'. * But on the other hand, if there is already both * a group and a gshadow password, it's preferable * to update both. */ sg = sgr_locate (name); if ( (NULL == sg) && (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) == 0)) { static char *empty = NULL; /* If the password is set to 'x' in * group, but there are no entries in * gshadow, create one. */ newsg.sg_name = name; /* newsg.sg_passwd = NULL; will be set later */ newsg.sg_adm = ∅ newsg.sg_mem = dup_list (gr->gr_mem); sg = &newsg; } } else { sg = NULL; } #endif /* * The freshly encrypted new password is merged into the * group's entry. */ #ifdef SHADOWGRP if (NULL != sg) { newsg = *sg; newsg.sg_passwd = cp; } if ( (NULL == sg) || (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) != 0)) #endif { newgr = *gr; newgr.gr_passwd = cp; } /* * The updated group file entry is then put back and will * be written to the group file later, after all the * other entries have been updated as well. */ #ifdef SHADOWGRP if (NULL != sg) { if (sgr_update (&newsg) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, sgr_dbname (), newsg.sg_name); errors++; continue; } } if ( (NULL == sg) || (strcmp (gr->gr_passwd, SHADOW_PASSWD_STRING) != 0)) #endif { if (gr_update (&newgr) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, gr_dbname (), newgr.gr_name); errors++; continue; } } } /* * Any detected errors will cause the entire set of changes to be * aborted. Unlocking the group file will cause all of the * changes to be ignored. Otherwise the file is closed, causing the * changes to be written out all at once, and then unlocked * afterwards. */ if (0 != errors) { fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog); fail_exit (1); } close_files (); nscd_flush_cache ("group"); return (0); }
int main (int argc, char **argv) { char buf[BUFSIZ]; char *name; char *newpwd; char *cp; #ifndef USE_PAM const struct spwd *sp; struct spwd newsp; const struct passwd *pw; struct passwd newpw; #endif /* !USE_PAM */ int errors = 0; int line = 0; Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_flags (argc, argv); OPENLOG ("chpasswd"); check_perms (); #ifndef USE_PAM is_shadow_pwd = spw_file_present (); open_files (); #endif /* * Read each line, separating the user name from the password. The * password entry for each user will be looked up in the appropriate * file (shadow or passwd) and the password changed. For shadow * files the last change date is set directly, for passwd files the * last change date is set in the age only if aging information is * present. */ while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) { line++; cp = strrchr (buf, '\n'); if (NULL != cp) { *cp = '\0'; } else { if (feof (stdin) == 0) { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; continue; } } /* * The username is the first field. It is separated from the * password with a ":" character which is replaced with a * NUL to give the new password. The new password will then * be encrypted in the normal fashion with a new salt * generated, unless the '-e' is given, in which case it is * assumed to already be encrypted. */ name = buf; cp = strchr (name, ':'); if (NULL != cp) { *cp = '\0'; cp++; } else { fprintf (stderr, _("%s: line %d: missing new password\n"), Prog, line); errors++; continue; } newpwd = cp; #ifdef USE_PAM if (do_pam_passwd_non_interractive ("chpasswd", name, newpwd) != 0) { fprintf (stderr, _("%s: (line %d, user %s) password not changed\n"), Prog, line, name); errors++; } #else /* !USE_PAM */ if ( !eflg && ( (NULL == crypt_method) || (0 != strcmp (crypt_method, "NONE")))) { void *arg = NULL; if (md5flg) { crypt_method = "MD5"; } else if (crypt_method != NULL) { #ifdef USE_SHA_CRYPT if (sflg) { arg = &sha_rounds; } #endif } else { crypt_method = NULL; } cp = pw_encrypt (newpwd, crypt_make_salt(crypt_method, arg)); } /* * Get the password file entry for this user. The user must * already exist. */ pw = pw_locate (name); if (NULL == pw) { fprintf (stderr, _("%s: line %d: user '%s' does not exist\n"), Prog, line, name); errors++; continue; } if (is_shadow_pwd) { sp = spw_locate (name); } else { sp = NULL; } /* * The freshly encrypted new password is merged into the * user's password file entry and the last password change * date is set to the current date. */ if (NULL != sp) { newsp = *sp; newsp.sp_pwdp = cp; newsp.sp_lstchg = (long) time ((time_t *)NULL) / SCALE; if (0 == newsp.sp_lstchg) { /* Better disable aging than requiring a * password change */ newsp.sp_lstchg = -1; } } else { newpw = *pw; newpw.pw_passwd = cp; } /* * The updated password file entry is then put back and will * be written to the password file later, after all the * other entries have been updated as well. */ if (NULL != sp) { if (spw_update (&newsp) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, spw_dbname (), newsp.sp_namp); errors++; continue; } } else { if (pw_update (&newpw) == 0) { fprintf (stderr, _("%s: line %d: failed to prepare the new %s entry '%s'\n"), Prog, line, pw_dbname (), newpw.pw_name); errors++; continue; } } #endif /* !USE_PAM */ } /* * Any detected errors will cause the entire set of changes to be * aborted. Unlocking the password file will cause all of the * changes to be ignored. Otherwise the file is closed, causing the * changes to be written out all at once, and then unlocked * afterwards. * * With PAM, it is not possible to delay the update of the * password database. */ if (0 != errors) { #ifndef USE_PAM fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog); #endif fail_exit (1); } #ifndef USE_PAM /* Save the changes */ close_files (); #endif nscd_flush_cache ("passwd"); return (0); }
/* * add_passwd - add or update the encrypted password */ static int add_passwd (struct passwd *pwd, const char *password) { const struct spwd *sp; struct spwd spent; char *cp; #ifndef USE_PAM void *crypt_arg = NULL; if (crypt_method != NULL) { #ifdef USE_SHA_CRYPT if (sflg) { crypt_arg = &sha_rounds; } #endif /* USE_SHA_CRYPT */ } /* * In the case of regular password files, this is real easy - pwd * points to the entry in the password file. Shadow files are * harder since there are zillions of things to do ... */ if (!is_shadow) { return update_passwd (pwd, password); } #endif /* USE_PAM */ /* * Do the first and easiest shadow file case. The user already * exists in the shadow password file. */ sp = spw_locate (pwd->pw_name); #ifndef USE_PAM if (NULL != sp) { spent = *sp; if ( (NULL != crypt_method) && (0 == strcmp(crypt_method, "NONE"))) { spent.sp_pwdp = (char *)password; } else { const char *salt = crypt_make_salt (crypt_method, crypt_arg); cp = pw_encrypt (password, salt); if (NULL == cp) { fprintf (stderr, _("%s: failed to crypt password with salt '%s': %s\n"), Prog, salt, strerror (errno)); return 1; } spent.sp_pwdp = cp; } spent.sp_lstchg = (long) gettime () / SCALE; if (0 == spent.sp_lstchg) { /* Better disable aging than requiring a password * change */ spent.sp_lstchg = -1; } return (spw_update (&spent) == 0); } /* * Pick the next easiest case - the user has an encrypted password * which isn't equal to "x". The password was set to "x" earlier * when the entry was created, so this user would have to have had * the password set someplace else. */ if (strcmp (pwd->pw_passwd, "x") != 0) { return update_passwd (pwd, password); } #else /* USE_PAM */ /* * If there is already a shadow entry, do not touch it. * If there is already a passwd entry with a password, do not * touch it. * The password will be updated later for all users using PAM. */ if ( (NULL != sp) || (strcmp (pwd->pw_passwd, "x") != 0)) { return 0; } #endif /* USE_PAM */ /* * Now the really hard case - I need to create an entirely new * shadow password file entry. */ spent.sp_namp = pwd->pw_name; #ifndef USE_PAM if ((crypt_method != NULL) && (0 == strcmp(crypt_method, "NONE"))) { spent.sp_pwdp = (char *)password; } else { const char *salt = crypt_make_salt (crypt_method, crypt_arg); cp = pw_encrypt (password, salt); if (NULL == cp) { fprintf (stderr, _("%s: failed to crypt password with salt '%s': %s\n"), Prog, salt, strerror (errno)); return 1; } spent.sp_pwdp = cp; } #else /* * Lock the password. * The password will be updated later for all users using PAM. */ spent.sp_pwdp = "!"; #endif spent.sp_lstchg = (long) gettime () / SCALE; if (0 == spent.sp_lstchg) { /* Better disable aging than requiring a password change */ spent.sp_lstchg = -1; } spent.sp_min = getdef_num ("PASS_MIN_DAYS", 0); /* 10000 is infinity this week */ spent.sp_max = getdef_num ("PASS_MAX_DAYS", 10000); spent.sp_warn = getdef_num ("PASS_WARN_AGE", -1); spent.sp_inact = -1; spent.sp_expire = -1; spent.sp_flag = SHADOW_SP_FLAG_UNSET; return (spw_update (&spent) == 0); }
int main(int argc, char **argv) { struct dialup *dial; struct dialup dent; struct stat sb; FILE *fp; char *sh = 0; char *cp; char pass[BUFSIZ]; int fd; int found = 0; int opt; Prog = Basename(argv[0]); setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); openlog(Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH); while ((opt = getopt (argc, argv, "a:d:")) != EOF) { switch (opt) { case 'a': aflg++; sh = optarg; break; case 'd': dflg++; sh = optarg; break; default: usage (); } } if (! aflg && ! dflg) aflg++; if (! sh) { if (optind >= argc) usage (); else sh = argv[optind]; } if (aflg + dflg != 1) usage (); /* * Add a new shell to the password file, or update an existing * entry. Begin by getting an encrypted password for this * shell. */ if (aflg) { int tries = 3; dent.du_shell = sh; dent.du_passwd = ""; /* XXX warning: const */ again: if (! (cp = getpass(_("Shell password: "******"re-enter Shell password: "******"%s: Passwords do not match, try again.\n"), Prog); if (--tries) goto again; exit(1); } strzero(cp); dent.du_passwd = pw_encrypt(pass, crypt_make_salt()); strzero(pass); } /* * Create the temporary file for the updated dialup password * information to be placed into. Turn it into a (FILE *) * for use by putduent(). */ if ((fd = open (DTMP, O_CREAT|O_EXCL|O_RDWR, 0600)) < 0) { snprintf(pass, sizeof pass, _("%s: can't create %s"), Prog, DTMP); perror (pass); exit (1); } if (! (fp = fdopen (fd, "r+"))) { snprintf(pass, sizeof pass, _("%s: can't open %s"), Prog, DTMP); perror (pass); unlink (DTMP); exit (1); } /* * Scan the dialup password file for the named entry, * copying out other entries along the way. Copying * stops when a match is found or the file runs out. */ while ((dial = getduent ())) { if (strcmp (dial->du_shell, sh) == 0) { found = 1; break; } if (putduent (dial, fp)) goto failure; } /* * To delete the entry, just don't copy it. To update * the entry, output the modified version - works with * new entries as well. */ if (dflg && ! found) { fprintf(stderr, _("%s: Shell %s not found.\n"), Prog, sh); goto failure; } if (aflg) if (putduent (&dent, fp)) goto failure; /* * Now copy out the remaining entries. Flush and close the * new file before doing anything nasty to the existing * file. */ while ((dial = getduent ())) if (putduent (dial, fp)) goto failure; if (fflush (fp)) goto failure; fclose (fp); /* * If the original file did not exist, we must create a new * file with owner "root" and mode 400. Otherwise we copy * the modes from the existing file to the new file. * * After this is done the new file will replace the old file. */ pwd_init(); if (! stat (DIALPWD, &sb)) { chown (DTMP, sb.st_uid, sb.st_gid); chmod (DTMP, sb.st_mode); unlink (DIALPWD); } else { chown (DTMP, 0, 0); chmod (DTMP, 0400); } if (! link (DTMP, DIALPWD)) unlink (DTMP); if (aflg && ! found) SYSLOG((LOG_INFO, DIALADD, sh)); else if (aflg && found) SYSLOG((LOG_INFO, DIALCHG, sh)); else if (dflg) SYSLOG((LOG_INFO, DIALREM, sh)); closelog(); sync (); exit (0); failure: unlink (DTMP); closelog(); exit (1); }
int pw_auth (const char *cipher, const char *user, int reason, const char *input) { char prompt[1024]; char *clear = NULL; const char *cp; int retval; #ifdef SKEY int use_skey = 0; char challenge_info[40]; struct skey skey; #endif /* * There are programs for adding and deleting authentication data. */ if (reason == PW_ADD || reason == PW_DELETE) return 0; /* * There are even programs for changing the user name ... */ if (reason == PW_CHANGE && input != (char *) 0) return 0; /* * WARNING: * * When we change a password and we are root, we don't prompt. * This is so root can change any password without having to * know it. This is a policy decision that might have to be * revisited. */ if (reason == PW_CHANGE && getuid () == 0) return 0; /* * WARNING: * * When we are logging in a user with no ciphertext password, * we don't prompt for the password or anything. In reality * the user could just hit <ENTER>, so it doesn't really * matter. */ if (cipher == (char *) 0 || *cipher == '\0') return 0; #ifdef SKEY /* * If the user has an S/KEY entry show them the pertinent info * and then we can try validating the created cyphertext and the SKEY. * If there is no SKEY information we default to not using SKEY. */ if (skeychallenge (&skey, user, challenge_info) == 0) use_skey = 1; #endif /* * Prompt for the password as required. FTPD and REXECD both * get the cleartext password for us. */ if (reason != PW_FTP && reason != PW_REXEC && !input) { if (!(cp = getdef_str ("LOGIN_STRING"))) cp = _(PROMPT); #ifdef SKEY if (use_skey) printf ("[%s]\n", challenge_info); #endif snprintf (prompt, sizeof prompt, cp, user); clear = getpass (prompt); if (!clear) { static char c[1]; c[0] = '\0'; clear = c; } input = clear; } /* * Convert the cleartext password into a ciphertext string. * If the two match, the return value will be zero, which is * SUCCESS. Otherwise we see if SKEY is being used and check * the results there as well. */ retval = strcmp (pw_encrypt (input, cipher), cipher); #ifdef SKEY /* * If (1) The password fails to match, and * (2) The password is empty and * (3) We are using OPIE or S/Key, then * ...Re-prompt, with echo on. * -- AR 8/22/1999 */ if (retval && !input[0] && (use_skey)) { strncat (prompt, "(Echo on) ", (sizeof (prompt) - strlen (prompt))); clear = getpass_with_echo (prompt); if (!clear) { static char c[1]; c[0] = '\0'; clear = c; } input = clear; } if (retval && use_skey) { int passcheck = -1; if (skeyverify (&skey, input) == 0) passcheck = skey.n; if (passcheck > 0) retval = 0; } #endif /* * Things like RADIUS authentication may need the password - * if the external variable wipe_clear_pass is zero, we will * not wipe it (the caller should wipe clear_pass when it is * no longer needed). --marekm */ clear_pass = clear; if (wipe_clear_pass && clear && *clear) strzero (clear); return retval; }
int sulogin_main(int argc UNUSED_PARAM, char **argv) { char *cp; int timeout = 0; struct passwd *pwd; const char *shell; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ char buffer[256]; struct spwd spw; #endif logmode = LOGMODE_BOTH; openlog(applet_name, 0, LOG_AUTH); opt_complementary = "t+"; /* -t N */ getopt32(argv, "t:", &timeout); argv += optind; if (argv[0]) { close(0); close(1); dup(xopen(argv[0], O_RDWR)); close(2); dup(0); } /* Malicious use like "sulogin /dev/sda"? */ if (!isatty(0) || !isatty(1) || !isatty(2)) { logmode = LOGMODE_SYSLOG; bb_error_msg_and_die("not a tty"); } /* Clear dangerous stuff, set PATH */ sanitize_env_if_suid(); pwd = getpwuid(0); if (!pwd) { goto auth_error; } #if ENABLE_FEATURE_SHADOWPASSWDS { /* getspnam_r may return 0 yet set result to NULL. * At least glibc 2.4 does this. Be extra paranoid here. */ struct spwd *result = NULL; int r = getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result); if (r || !result) { goto auth_error; } pwd->pw_passwd = result->sp_pwdp; } #endif while (1) { char *encrypted; int r; /* cp points to a static buffer */ cp = bb_ask(STDIN_FILENO, timeout, "Give root password for system maintenance\n" "(or type Control-D for normal startup):"); if (!cp) { /* ^D, ^C, timeout, or read error */ bb_info_msg("Normal startup"); return 0; } encrypted = pw_encrypt(cp, pwd->pw_passwd, 1); r = strcmp(encrypted, pwd->pw_passwd); free(encrypted); if (r == 0) { break; } bb_do_delay(LOGIN_FAIL_DELAY); bb_info_msg("Login incorrect"); } memset(cp, 0, strlen(cp)); // signal(SIGALRM, SIG_DFL); bb_info_msg("System Maintenance Mode"); IF_SELINUX(renew_current_security_context()); shell = getenv("SUSHELL"); if (!shell) shell = getenv("sushell"); if (!shell) shell = pwd->pw_shell; /* Exec login shell with no additional parameters. Never returns. */ run_shell(shell, 1, NULL, NULL); auth_error: bb_error_msg_and_die("no password entry for root"); }
int cryptpw_main(int argc UNUSED_PARAM, char **argv) { /* $N$ + sha_salt_16_bytes + NUL */ char salt[3 + 16 + 1]; char *salt_ptr; const char *opt_m, *opt_S; int len; int fd; #if ENABLE_LONG_OPTS static const char mkpasswd_longopts[] ALIGN1 = "stdin\0" No_argument "s" "password-fd\0" Required_argument "P" "salt\0" Required_argument "S" "method\0" Required_argument "m" ; applet_long_options = mkpasswd_longopts; #endif fd = STDIN_FILENO; opt_m = "d"; opt_S = NULL; /* at most two non-option arguments; -P NUM */ opt_complementary = "?2:P+"; getopt32(argv, "sP:S:m:a:", &fd, &opt_S, &opt_m, &opt_m); argv += optind; /* have no idea how to handle -s... */ if (argv[0] && !opt_S) opt_S = argv[1]; len = 2/2; salt_ptr = salt; if (opt_m[0] != 'd') { /* not des */ len = 8/2; /* so far assuming md5 */ *salt_ptr++ = '$'; *salt_ptr++ = '1'; *salt_ptr++ = '$'; #if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA if (opt_m[0] == 's') { /* sha */ salt[1] = '5' + (strcmp(opt_m, "sha512") == 0); len = 16/2; } #endif } if (opt_S) safe_strncpy(salt_ptr, opt_S, sizeof(salt) - 3); else crypt_make_salt(salt_ptr, len, 0); xmove_fd(fd, STDIN_FILENO); puts(pw_encrypt( argv[0] ? argv[0] : ( /* Only mkpasswd, and only from tty, prompts. * Otherwise it is a plain read. */ (isatty(STDIN_FILENO) && applet_name[0] == 'm') ? bb_ask_stdin("Password: ") : xmalloc_fgetline(stdin) ), salt, 1)); return EXIT_SUCCESS; }
static const char* set_passwd(const char *name, const char *passwd, char **msg) { FILE *f = NULL; struct spwd *spwd, new_spwd; const char *en_passwd; /* encrypted password */ struct stat st; assert(name); assert(passwd); /* check password format */ if ((passwd[0] != '$') || (passwd[1] != '0' && passwd[1] != '1' && passwd[1] != '5' && passwd[1] != '6') || (passwd[2] != '$')) { asprintf(msg, "Wrong password format (user %s).", name); return (NULL); } if (passwd[1] == '0') { /* encrypt the password */ get_login_defs(); en_passwd = pw_encrypt(&(passwd[3]), crypt_make_salt(NULL, NULL)); } else { en_passwd = passwd; } /* * store encrypted password into shadow */ /* lock shadow file */ if (lckpwdf() != 0) { *msg = strdup("Failed to acquire shadow file lock."); return (NULL); } /* init position in shadow */ setspent(); /* open new shadow */ f = fopen(SHADOW_COPY, "w"); if (f == NULL) { asprintf(msg, "Unable to prepare shadow copy (%s).", strerror(errno)); endspent(); ulckpwdf(); return (NULL); } /* get file stat of the original file to make a nice copy of it */ stat(SHADOW_ORIG, &st); fchmod(fileno(f), st.st_mode); fchown(fileno(f), st.st_uid, st.st_gid); while ((spwd = getspent()) != NULL) { if (strcmp(spwd->sp_namp, name) == 0) { /* * we have the entry to change, * make the copy, modifying the original * structure doesn't seem as a good idea */ memcpy(&new_spwd, spwd, sizeof(struct spwd)); new_spwd.sp_pwdp = (char*) en_passwd; spwd = &new_spwd; } /* store the record into the shadow copy */ putspent(spwd, f); } endspent(); fclose(f); if (rename(SHADOW_COPY, SHADOW_ORIG) == -1) { asprintf(msg, "Unable to rewrite shadow database (%s).", strerror(errno)); unlink(SHADOW_COPY); ulckpwdf(); return (NULL); } ulckpwdf(); return (en_passwd); }
extern int sulogin_main(int argc, char **argv) { char *cp; char *device = (char *) 0; const char *name = "root"; int timeout = 0; static char pass[BUFSIZ]; struct passwd pwent; struct passwd *pwd; time_t start, now; const char **p; #ifdef CONFIG_FEATURE_SHADOWPASSWDS struct spwd *spwd = NULL; #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ rg_openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); if (argc > 1) { if (strncmp(argv[1], "-t", 2) == 0) { if (strcmp(argv[1], "-t") == 0) { if (argc > 2) { timeout = atoi(argv[2]); if (argc > 3) { device = argv[3]; } } } else { if (argc > 2) { device = argv[2]; } } } else { device = argv[1]; } if (device) { close(0); close(1); close(2); if (open(device, O_RDWR) >= 0) { dup(0); dup(0); } else { syslog(LOG_WARNING, "cannot open %s\n", device); exit(EXIT_FAILURE); } } } if (access(bb_path_passwd_file, 0) == -1) { syslog(LOG_WARNING, "No password file\n"); bb_error_msg_and_die("No password file\n"); } if (!isatty(0) || !isatty(1) || !isatty(2)) { exit(EXIT_FAILURE); } /* Clear out anything dangerous from the environment */ for (p = forbid; *p; p++) unsetenv(*p); signal(SIGALRM, catchalarm); if (!(pwd = getpwnam(name))) { syslog(LOG_WARNING, "No password entry for `root'\n"); bb_error_msg_and_die("No password entry for `root'\n"); } pwent = *pwd; #ifdef CONFIG_FEATURE_SHADOWPASSWDS spwd = NULL; if (pwd && ((strcmp(pwd->pw_passwd, "x") == 0) || (strcmp(pwd->pw_passwd, "*") == 0))) { endspent(); spwd = getspnam(name); if (spwd) { pwent.pw_passwd = spwd->sp_pwdp; } } #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ while (1) { cp = bb_askpass(timeout, SULOGIN_PROMPT); if (!cp || !*cp) { puts("\n"); fflush(stdout); syslog(LOG_INFO, "Normal startup\n"); exit(EXIT_SUCCESS); } else { safe_strncpy(pass, cp, sizeof(pass)); bzero(cp, strlen(cp)); } if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 0) { break; } time(&start); now = start; while (difftime(now, start) < FAIL_DELAY) { sleep(FAIL_DELAY); time(&now); } puts("Login incorrect"); fflush(stdout); syslog(LOG_WARNING, "Incorrect root password\n"); } bzero(pass, strlen(pass)); signal(SIGALRM, SIG_DFL); puts("Entering System Maintenance Mode\n"); fflush(stdout); syslog(LOG_INFO, "System Maintenance Mode\n"); run_shell(pwent.pw_shell, 1, 0, 0); return (0); }
int sulogin_main(int argc, char **argv) { char *cp; int timeout = 0; char *timeout_arg; const char * const *p; struct passwd *pwd; const char *shell; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ char buffer[256]; struct spwd spw; struct spwd *result; #endif logmode = LOGMODE_BOTH; openlog(applet_name, 0, LOG_AUTH); if (getopt32(argc, argv, "t:", &timeout_arg)) { timeout = xatoi_u(timeout_arg); } if (argv[optind]) { close(0); close(1); dup(xopen(argv[optind], O_RDWR)); close(2); dup(0); } if (!isatty(0) || !isatty(1) || !isatty(2)) { logmode = LOGMODE_SYSLOG; bb_error_msg_and_die("not a tty"); } /* Clear out anything dangerous from the environment */ for (p = forbid; *p; p++) unsetenv(*p); signal(SIGALRM, catchalarm); pwd = getpwuid(0); if (!pwd) { goto auth_error; } #if ENABLE_FEATURE_SHADOWPASSWDS if (getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result)) { goto auth_error; } pwd->pw_passwd = spw.sp_pwdp; #endif while (1) { /* cp points to a static buffer that is zeroed every time */ cp = bb_askpass(timeout, "Give root password for system maintenance\n" "(or type Control-D for normal startup):"); if (!cp || !*cp) { bb_info_msg("Normal startup"); return 0; } if (strcmp(pw_encrypt(cp, pwd->pw_passwd), pwd->pw_passwd) == 0) { break; } bb_do_delay(FAIL_DELAY); bb_error_msg("login incorrect"); } memset(cp, 0, strlen(cp)); signal(SIGALRM, SIG_DFL); bb_info_msg("System Maintenance Mode"); USE_SELINUX(renew_current_security_context()); shell = getenv("SUSHELL"); if (!shell) shell = getenv("sushell"); if (!shell) { shell = "/bin/sh"; if (pwd->pw_shell[0]) shell = pwd->pw_shell; } run_shell(shell, 1, 0, 0); /* never returns */ auth_error: bb_error_msg_and_die("no password entry for 'root'"); }
static int #if defined(USE_PAM) || defined(_AIX) isNoPassAllowed( const char *un ) { struct passwd *pw = 0; # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */ struct spwd *spw; # endif #else isNoPassAllowed( const char *un, struct passwd *pw ) { #endif struct group *gr; char **fp; int hg; if (!*un) return 0; if (cursource != PWSRC_MANUAL) return 1; for (hg = 0, fp = td->noPassUsers; *fp; fp++) if (**fp == '@') hg = 1; else if (!strcmp( un, *fp )) return 1; else if (!strcmp( "*", *fp )) { #if defined(USE_PAM) || defined(_AIX) if (!(pw = getpwnam( un ))) return 0; if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') continue; # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */ if ((spw = getspnam( un )) && (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*')) continue; # endif #endif if (pw->pw_uid) return 1; } #if defined(USE_PAM) || defined(_AIX) if (hg && (pw || (pw = getpwnam( un )))) { #else if (hg) { #endif for (setgrent(); (gr = getgrent()); ) for (fp = td->noPassUsers; *fp; fp++) if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) { if (pw->pw_gid == gr->gr_gid) { endgrent(); return 1; } for (; *gr->gr_mem; gr->gr_mem++) if (!strcmp( un, *gr->gr_mem )) { endgrent(); return 1; } } endgrent(); } return 0; } #if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT) # define LC_RET0 do { login_close(lc); return 0; } while(0) #else # define LC_RET0 return 0 #endif int verify( GConvFunc gconv, int rootok ) { #ifdef USE_PAM const char *psrv; struct pam_data pdata; int pretc, pnopass; char psrvb[64]; #elif defined(_AIX) char *msg, *curret; int i, reenter; #else struct stat st; const char *nolg; char *buf; int fd; # ifdef HAVE_GETUSERSHELL char *s; # endif # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) int tim, expir, warntime, quietlog; # endif #endif debug( "verify ...\n" ); #ifdef USE_PAM pnopass = FALSE; if (!strcmp( curtype, "classic" )) { if (!gconv( GCONV_USER, 0 )) return 0; if (isNoPassAllowed( curuser )) { gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { pnopass = TRUE; sprintf( psrvb, "%.31s-np", PAMService ); psrv = psrvb; } else psrv = PAMService; } else psrv = PAMService; pdata.usecur = TRUE; } else { sprintf( psrvb, "%.31s-%.31s", PAMService, curtype ); psrv = psrvb; pdata.usecur = FALSE; } pdata.gconv = gconv; if (!doPAMAuth( psrv, &pdata )) return 0; #elif defined(_AIX) if ((td->displayType & d_location) == dForeign) { char *tmpch; strncpy( hostname, td->name, sizeof(hostname) - 1 ); hostname[sizeof(hostname)-1] = '\0'; if ((tmpch = strchr( hostname, ':' ))) *tmpch = '\0'; } else hostname[0] = '\0'; /* tty names should only be 15 characters long */ # if 0 for (i = 0; i < 15 && td->name[i]; i++) { if (td->name[i] == ':' || td->name[i] == '.') tty[i] = '_'; else tty[i] = td->name[i]; } tty[i] = '\0'; # else memcpy( tty, "/dev/xdm/", 9 ); for (i = 0; i < 6 && td->name[i]; i++) { if (td->name[i] == ':' || td->name[i] == '.') tty[9 + i] = '_'; else tty[9 + i] = td->name[i]; } tty[9 + i] = '\0'; # endif if (!strcmp( curtype, "classic" )) { if (!gconv( GCONV_USER, 0 )) return 0; if (isNoPassAllowed( curuser )) { gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { debug( "accepting despite empty password\n" ); goto done; } } else if (!gconv( GCONV_PASS, 0 )) return 0; enduserdb(); msg = NULL; if ((i = authenticate( curuser, curpass, &reenter, &msg ))) { debug( "authenticate() failed: %s\n", msg ); if (msg) free( msg ); loginfailed( curuser, hostname, tty ); if (i == ENOENT || i == ESAD) V_RET_AUTH; else V_RET_FAIL( 0 ); } if (reenter) { logError( "authenticate() requests more data: %s\n", msg ); free( msg ); V_RET_FAIL( 0 ); } } else if (!strcmp( curtype, "generic" )) { if (!gconv( GCONV_USER, 0 )) return 0; for (curret = 0;;) { msg = NULL; if ((i = authenticate( curuser, curret, &reenter, &msg ))) { debug( "authenticate() failed: %s\n", msg ); if (msg) free( msg ); loginfailed( curuser, hostname, tty ); if (i == ENOENT || i == ESAD) V_RET_AUTH; else V_RET_FAIL( 0 ); } if (curret) free( curret ); if (!reenter) break; if (!(curret = gconv( GCONV_HIDDEN, msg ))) return 0; free( msg ); } } else { logError( "Unsupported authentication type %\"s requested\n", curtype ); V_RET_FAIL( 0 ); } if (msg) { displayStr( V_MSG_INFO, msg ); free( msg ); } done: #else if (strcmp( curtype, "classic" )) { logError( "Unsupported authentication type %\"s requested\n", curtype ); V_RET_FAIL( 0 ); } if (!gconv( GCONV_USER, 0 )) return 0; if (!(p = getpwnam( curuser ))) { debug( "getpwnam() failed.\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { debug( "account is locked\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } # ifdef USESHADOW if ((sp = getspnam( curuser ))) { p->pw_passwd = sp->sp_pwdp; if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { debug( "account is locked\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } } else debug( "getspnam() failed: %m. Are you root?\n" ); # endif if (!*p->pw_passwd) { if (!td->allowNullPasswd) { debug( "denying user with empty password\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } goto nplogin; } if (isNoPassAllowed( curuser, p )) { nplogin: gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { debug( "accepting password-less login\n" ); goto done; } } else if (!gconv( GCONV_PASS, 0 )) return 0; # ifdef KERBEROS if (p->pw_uid) { int ret; char realm[REALM_SZ]; if (krb_get_lrealm( realm, 1 )) { logError( "Cannot get KerberosIV realm.\n" ); V_RET_FAIL( 0 ); } sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name ); krb_set_tkt_string( krbtkfile ); unlink( krbtkfile ); ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" ); if (ret == KSUCCESS) { chown( krbtkfile, p->pw_uid, p->pw_gid ); debug( "KerberosIV verify succeeded\n" ); goto done; } else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) { logError( "KerberosIV verification failure %\"s for %s\n", krb_get_err_text( ret ), curuser ); krbtkfile[0] = '\0'; V_RET_FAIL( 0 ); } debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) ); } krbtkfile[0] = '\0'; # endif /* KERBEROS */ # if defined(ultrix) || defined(__ultrix__) if (authenticate_user( p, curpass, NULL ) < 0) # elif defined(HAVE_PW_ENCRYPT) if (strcmp( pw_encrypt( curpass, p->pw_passwd ), p->pw_passwd )) # elif defined(HAVE_CRYPT) if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd )) # else if (strcmp( curpass, p->pw_passwd )) # endif { debug( "password verify failed\n" ); V_RET_AUTH; } done: #endif /* !defined(USE_PAM) && !defined(_AIX) */ debug( "restrict %s ...\n", curuser ); #if defined(USE_PAM) || defined(_AIX) if (!(p = getpwnam( curuser ))) { logError( "getpwnam(%s) failed.\n", curuser ); V_RET_FAIL( 0 ); } #endif if (!p->pw_uid) { if (!rootok && !td->allowRootLogin) V_RET_FAIL( "Root logins are not allowed" ); return 1; /* don't deny root to log in */ } #ifdef USE_PAM debug( " pam_acct_mgmt() ...\n" ); pretc = pam_acct_mgmt( pamh, 0 ); reInitErrorLog(); debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) ); if (pretc == PAM_NEW_AUTHTOK_REQD) { pdata.usecur = FALSE; pdata.gconv = conv_interact; /* pam will have output a message already, so no prepareErrorGreet() */ if (gconv != conv_interact || pnopass) { pam_end( pamh, PAM_SUCCESS ); pamh = 0; gSendInt( V_CHTOK_AUTH ); /* this cannot auth the wrong user, as only classic auths get here */ while (!doPAMAuth( PAMService, &pdata )) if (pdata.abort) return 0; gSendInt( V_PRE_OK ); } else gSendInt( V_CHTOK ); for (;;) { debug( " pam_chauthtok() ...\n" ); pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK ); reInitErrorLog(); debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) ); if (pdata.abort) { pam_end( pamh, PAM_SUCCESS ); pamh = 0; return 0; } if (pretc == PAM_SUCCESS) break; /* effectively there is only PAM_AUTHTOK_ERR */ gSendInt( V_FAIL ); } if (curpass) free( curpass ); curpass = newpass; newpass = 0; } else if (pretc != PAM_SUCCESS) { pam_end( pamh, pretc ); pamh = 0; V_RET_AUTH; } #elif defined(_AIX) /* USE_PAM */ msg = NULL; if (loginrestrictions( curuser, ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN, tty, &msg ) == -1) { debug( "loginrestrictions() - %s\n", msg ? msg : "error" ); loginfailed( curuser, hostname, tty ); prepareErrorGreet(); if (msg) { displayStr( V_MSG_ERR, msg ); free( msg ); } gSendInt( V_AUTH ); return 0; } if (msg) free( (void *)msg ); #endif /* USE_PAM || _AIX */ #ifndef _AIX # ifdef HAVE_SETUSERCONTEXT # ifdef HAVE_LOGIN_GETCLASS lc = login_getclass( p->pw_class ); # else lc = login_getpwclass( p ); # endif if (!lc) V_RET_FAIL( 0 ); p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell ); # endif # ifndef USE_PAM /* restrict_expired */ # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) # if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW)) if (sp) # endif { # define DEFAULT_WARN (2L * 7L) /* Two weeks */ tim = time( NULL ) / 86400L; # ifdef HAVE_SETUSERCONTEXT quietlog = login_getcapbool( lc, "hushlogin", 0 ); warntime = login_getcaptime( lc, "warnexpire", DEFAULT_WARN * 86400L, DEFAULT_WARN * 86400L ) / 86400L; # else quietlog = 0; # ifdef USESHADOW warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN; # else warntime = DEFAULT_WARN; # endif # endif # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE if (p->pw_expire) { expir = p->pw_expire / 86400L; # else if (sp->sp_expire != -1) { expir = sp->sp_expire; # endif if (tim > expir) { displayStr( V_MSG_ERR, "Your account has expired;" " please contact your system administrator" ); gSendInt( V_FAIL ); LC_RET0; } else if (tim > (expir - warntime) && !quietlog) { displayMsg( V_MSG_INFO, "Warning: your account will expire in %d day(s)", expir - tim ); } } # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE if (p->pw_change) { expir = p->pw_change / 86400L; # else if (!sp->sp_lstchg) { displayStr( V_MSG_ERR, "You are required to change your password immediately" " (root enforced)" ); /* XXX todo password change */ gSendInt( V_FAIL ); LC_RET0; } else if (sp->sp_max != -1) { expir = sp->sp_lstchg + sp->sp_max; if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) { displayStr( V_MSG_ERR, "Your account has expired;" " please contact your system administrator" ); gSendInt( V_FAIL ); LC_RET0; } # endif if (tim > expir) { displayStr( V_MSG_ERR, "You are required to change your password immediately" " (password aged)" ); /* XXX todo password change */ gSendInt( V_FAIL ); LC_RET0; } else if (tim > (expir - warntime) && !quietlog) { displayMsg( V_MSG_INFO, "Warning: your password will expire in %d day(s)", expir - tim ); } } } # endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */ /* restrict_nologin */ # ifndef _PATH_NOLOGIN # define _PATH_NOLOGIN "/etc/nologin" # endif if (( # ifdef HAVE_SETUSERCONTEXT /* Do we ignore a nologin file? */ !login_getcapbool( lc, "ignorenologin", 0 )) && (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) || # endif !stat( (nolg = _PATH_NOLOGIN), &st ))) { if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) { if ((buf = Malloc( st.st_size + 1 ))) { if (read( fd, buf, st.st_size ) == st.st_size) { close( fd ); buf[st.st_size] = 0; displayStr( V_MSG_ERR, buf ); free( buf ); gSendInt( V_FAIL ); LC_RET0; } free( buf ); } close( fd ); } displayStr( V_MSG_ERR, "Logins are not allowed at the moment.\nTry again later" ); gSendInt( V_FAIL ); LC_RET0; } /* restrict_time */ # if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK) if (!auth_timeok( lc, time( NULL ) )) { displayStr( V_MSG_ERR, "You are not allowed to login at the moment" ); gSendInt( V_FAIL ); LC_RET0; } # endif # ifdef HAVE_GETUSERSHELL for (;;) { if (!(s = getusershell())) { debug( "shell not in /etc/shells\n" ); endusershell(); V_RET_FAIL( "Your login shell is not listed in /etc/shells" ); } if (!strcmp( s, p->pw_shell )) { endusershell(); break; } } # endif # endif /* !USE_PAM */ /* restrict_nohome */ # ifdef HAVE_SETUSERCONTEXT if (login_getcapbool( lc, "requirehome", 0 )) { struct stat st; if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) { displayStr( V_MSG_ERR, "Home folder not available" ); gSendInt( V_FAIL ); LC_RET0; } } # endif #endif /* !_AIX */ return 1; } static const char *envvars[] = { "TZ", /* SYSV and SVR4, but never hurts */ #ifdef _AIX "AUTHSTATE", /* for kerberos */ #endif NULL }; #if defined(USE_PAM) && defined(HAVE_INITGROUPS) static int num_saved_gids; static gid_t *saved_gids; static int saveGids( void ) { num_saved_gids = getgroups( 0, 0 ); if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids ))) return 0; if (getgroups( num_saved_gids, saved_gids ) < 0) { logError( "saving groups failed: %m\n" ); return 0; } return 1; } static int restoreGids( void ) { if (setgroups( num_saved_gids, saved_gids ) < 0) { logError( "restoring groups failed: %m\n" ); return 0; } if (setgid( p->pw_gid ) < 0) { logError( "restoring gid failed: %m\n" ); return 0; } return 1; } #endif /* USE_PAM && HAVE_INITGROUPS */ static int resetGids( void ) { #ifdef HAVE_INITGROUPS if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) { logError( "restoring groups failed: %m\n" ); return 0; } #endif if (setgid( 0 ) < 0) { logError( "restoring gid failed: %m\n" ); return 0; } return 1; } static int setGid( const char *name, int gid ) { if (setgid( gid ) < 0) { logError( "setgid(%d) (user %s) failed: %m\n", gid, name ); return 0; } #ifdef HAVE_INITGROUPS if (initgroups( name, gid ) < 0) { logError( "initgroups for %s failed: %m\n", name ); setgid( 0 ); return 0; } #endif /* QNX4 doesn't support multi-groups, no initgroups() */ return 1; } static int setUid( const char *name, int uid ) { if (setuid( uid ) < 0) { logError( "setuid(%d) (user %s) failed: %m\n", uid, name ); return 0; } return 1; } static int setUser( const char *name, int uid, int gid ) { if (setGid( name, gid )) { if (setUid( name, uid )) return 1; resetGids(); } return 0; } #if defined(SECURE_RPC) || defined(K5AUTH) static void nukeAuth( int len, const char *name ) { int i; for (i = 0; i < td->authNum; i++) if (td->authorizations[i]->name_length == len && !memcmp( td->authorizations[i]->name, name, len )) { memcpy( &td->authorizations[i], &td->authorizations[i+1], sizeof(td->authorizations[i]) * (--td->authNum - i) ); break; } } #endif static void mergeSessionArgs( int cansave ) { char *mfname; const char *fname; int i, needsave; mfname = 0; fname = ".dmrc"; if ((!curdmrc || newdmrc) && *dmrcDir) if (strApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 )) fname = mfname; needsave = 0; if (!curdmrc) { curdmrc = iniLoad( fname ); if (!curdmrc) { strDup( &curdmrc, "[Desktop]\nSession=default\n" ); needsave = 1; } } if (newdmrc) { curdmrc = iniMerge( curdmrc, newdmrc ); needsave = 1; } if (needsave && cansave) if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) { for (i = 0; mfname[i]; i++) if (mfname[i] == '/') { mfname[i] = 0; mkdir( mfname, 0755 ); mfname[i] = '/'; } iniSave( curdmrc, mfname ); } if (mfname) free( mfname ); } static int createClientLog( const char *log ) { char randstr[32], *randstrp = 0, *lname; int lfd; for (;;) { struct expando macros[] = { { 'd', 0, td->name }, { 'u', 0, curuser }, { 'r', 0, randstrp }, { 0, 0, 0 } }; if (!(lname = expandMacros( log, macros ))) exit( 1 ); unlink( lname ); if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) >= 0) { dup2( lfd, 1 ); dup2( lfd, 2 ); close( lfd ); free( lname ); return TRUE; } if (errno != EEXIST || !macros[2].uses) { free( lname ); return FALSE; } logInfo( "Session log file %s not usable, trying another one.\n", lname ); free( lname ); sprintf( randstr, "%d", secureRandom() ); randstrp = randstr; } }
int sulogin_main(int argc, char **argv) { char *cp; char *device = NULL; const char *name = "root"; int timeout = 0; #define pass bb_common_bufsiz1 struct passwd pwent; struct passwd *pwd; const char * const *p; #if ENABLE_FEATURE_SHADOWPASSWDS struct spwd *spwd = NULL; #endif openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); if (argc > 1) { if (strncmp(argv[1], "-t", 2) == 0) { if (argv[1][2] == '\0') { /* -t NN */ if (argc > 2) { timeout = atoi(argv[2]); if (argc > 3) { device = argv[3]; } } } else { /* -tNNN */ timeout = atoi(&argv[1][2]); if (argc > 2) { device = argv[2]; } } } else { device = argv[1]; } if (device) { close(0); close(1); close(2); if (open(device, O_RDWR) == 0) { dup(0); dup(0); } else { syslog(LOG_WARNING, "cannot open %s\n", device); exit(EXIT_FAILURE); } } } if (access(bb_path_passwd_file, 0) == -1) { syslog(LOG_WARNING, "No password file\n"); bb_error_msg_and_die("No password file\n"); } if (!isatty(0) || !isatty(1) || !isatty(2)) { exit(EXIT_FAILURE); } /* Clear out anything dangerous from the environment */ for (p = forbid; *p; p++) unsetenv(*p); signal(SIGALRM, catchalarm); if (!(pwd = getpwnam(name))) { syslog(LOG_WARNING, "No password entry for `root'\n"); bb_error_msg_and_die("No password entry for `root'\n"); } pwent = *pwd; #if ENABLE_FEATURE_SHADOWPASSWDS spwd = NULL; if (pwd && ((strcmp(pwd->pw_passwd, "x") == 0) || (strcmp(pwd->pw_passwd, "*") == 0))) { endspent(); spwd = getspnam(name); if (spwd) { pwent.pw_passwd = spwd->sp_pwdp; } } #endif while (1) { cp = bb_askpass(timeout, SULOGIN_PROMPT); if (!cp || !*cp) { puts("\n"); fflush(stdout); syslog(LOG_INFO, "Normal startup\n"); exit(EXIT_SUCCESS); } else { safe_strncpy(pass, cp, sizeof(pass)); memset(cp, 0, strlen(cp)); } if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 0) { break; } bb_do_delay(FAIL_DELAY); puts("Login incorrect"); fflush(stdout); syslog(LOG_WARNING, "Incorrect root password\n"); } memset(pass, 0, strlen(pass)); signal(SIGALRM, SIG_DFL); puts("Entering System Maintenance Mode\n"); fflush(stdout); syslog(LOG_INFO, "System Maintenance Mode\n"); #if ENABLE_SELINUX renew_current_security_context(); #endif run_shell(pwent.pw_shell, 1, 0, 0); return (0); }
/* * new_password - validate old password and replace with new (both old and * new in global "char crypt_passwd[128]") */ static int new_password (const struct passwd *pw) { char *clear; /* Pointer to clear text */ char *cipher; /* Pointer to cipher text */ char *cp; /* Pointer to getpass() response */ char orig[200]; /* Original password */ char pass[200]; /* New password */ int i; /* Counter for retries */ int warned; int pass_max_len = -1; char *method; #ifdef HAVE_LIBCRACK_HIST int HistUpdate (const char *, const char *); #endif /* HAVE_LIBCRACK_HIST */ /* * Authenticate the user. The user will be prompted for their own * password. */ if (!amroot && crypt_passwd[0]) { clear = getpass (_("Old password: "******"incorrect password for %s", pw->pw_name)); sleep (1); fprintf (stderr, _("Incorrect password for %s.\n"), pw->pw_name); return -1; } STRFCPY (orig, clear); strzero (clear); strzero (cipher); } else { orig[0] = '\0'; } /* * Get the new password. The user is prompted for the new password * and has five tries to get it right. The password will be tested * for strength, unless it is the root user. This provides an escape * for initial login passwords. */ if ((method = getdef_str ("ENCRYPT_METHOD")) == NULL) { if (!getdef_bool ("MD5_CRYPT_ENAB")) { pass_max_len = getdef_num ("PASS_MAX_LEN", 8); } } else { if ( (strcmp (method, "MD5") == 0) #ifdef USE_SHA_CRYPT || (strcmp (method, "SHA256") == 0) || (strcmp (method, "SHA512") == 0) #endif /* USE_SHA_CRYPT */ ) { pass_max_len = -1; } else { pass_max_len = getdef_num ("PASS_MAX_LEN", 8); } } if (!qflg) { if (pass_max_len == -1) { printf (_( "Enter the new password (minimum of %d characters)\n" "Please use a combination of upper and lower case letters and numbers.\n"), getdef_num ("PASS_MIN_LEN", 5)); } else { printf (_( "Enter the new password (minimum of %d, maximum of %d characters)\n" "Please use a combination of upper and lower case letters and numbers.\n"), getdef_num ("PASS_MIN_LEN", 5), pass_max_len); } } warned = 0; for (i = getdef_num ("PASS_CHANGE_TRIES", 5); i > 0; i--) { cp = getpass (_("New password: "******"Try again.")); continue; } /* * If enabled, warn about weak passwords even if you are * root (enter this password again to use it anyway). * --marekm */ if (amroot && !warned && getdef_bool ("PASS_ALWAYS_WARN") && (!obscure (orig, pass, pw) || reuse (pass, pw))) { puts (_("\nWarning: weak password (enter it again to use it anyway).")); warned++; continue; } cp = getpass (_("Re-enter new password: "******"They don't match; try again.\n"), stderr); } else { strzero (cp); break; } } memzero (orig, sizeof orig); if (i == 0) { memzero (pass, sizeof pass); return -1; } /* * Encrypt the password, then wipe the cleartext password. */ cp = pw_encrypt (pass, crypt_make_salt (NULL, NULL)); memzero (pass, sizeof pass); #ifdef HAVE_LIBCRACK_HIST HistUpdate (pw->pw_name, crypt_passwd); #endif /* HAVE_LIBCRACK_HIST */ STRFCPY (crypt_passwd, cp); return 0; }
int chpasswd_main(int argc UNUSED_PARAM, char **argv) { char *name; const char *algo = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; int opt; if (getuid() != 0) bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); opt = getopt32long(argv, "^" "emc:" "\0" "m--ec:e--mc:c--em", chpasswd_longopts, &algo ); while ((name = xmalloc_fgetline(stdin)) != NULL) { char *free_me; char *pass; int rc; pass = strchr(name, ':'); if (!pass) bb_error_msg_and_die("missing new password"); *pass++ = '\0'; xuname2uid(name); /* dies if there is no such user */ free_me = NULL; if (!(opt & OPT_ENC)) { char salt[MAX_PW_SALT_LEN]; if (opt & OPT_MD5) { /* Force MD5 if the -m flag is set */ algo = "md5"; } crypt_make_pw_salt(salt, algo); free_me = pass = pw_encrypt(pass, salt, 0); } /* This is rather complex: if user is not found in /etc/shadow, * we try to find & change his passwd in /etc/passwd */ #if ENABLE_FEATURE_SHADOWPASSWDS rc = update_passwd(bb_path_shadow_file, name, pass, NULL); if (rc > 0) /* password in /etc/shadow was updated */ pass = (char*)"x"; if (rc >= 0) /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ #endif rc = update_passwd(bb_path_passwd_file, name, pass, NULL); /* LOGMODE_BOTH logs to syslog also */ logmode = LOGMODE_BOTH; if (rc < 0) bb_error_msg_and_die("an error occurred updating password for %s", name); if (rc) bb_error_msg("password for '%s' changed", name); logmode = LOGMODE_STDIO; free(name); free(free_me); } return EXIT_SUCCESS; }
int sulogin_main(int argc, char **argv) { char *cp; int timeout = 0; char *timeout_arg; struct passwd *pwd; const char *shell; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ char buffer[256]; struct spwd spw; #endif logmode = LOGMODE_BOTH; openlog(applet_name, 0, LOG_AUTH); if (getopt32(argv, "t:", &timeout_arg)) { timeout = xatoi_u(timeout_arg); } if (argv[optind]) { close(0); close(1); dup(xopen(argv[optind], O_RDWR)); close(2); dup(0); } if (!isatty(0) || !isatty(1) || !isatty(2)) { logmode = LOGMODE_SYSLOG; bb_error_msg_and_die("not a tty"); } /* Clear dangerous stuff, set PATH */ sanitize_env_for_suid(); // bb_askpass() already handles this // signal(SIGALRM, catchalarm); pwd = getpwuid(0); if (!pwd) { goto auth_error; } #if ENABLE_FEATURE_SHADOWPASSWDS { /* getspnam_r may return 0 yet set result to NULL. * At least glibc 2.4 does this. Be extra paranoid here. */ struct spwd *result = NULL; int r = getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result); if (r || !result) { goto auth_error; } pwd->pw_passwd = result->sp_pwdp; } #endif while (1) { /* cp points to a static buffer that is zeroed every time */ cp = bb_askpass(timeout, "Give root password for system maintenance\n" "(or type Control-D for normal startup):"); if (!cp || !*cp) { bb_info_msg("Normal startup"); return 0; } if (strcmp(pw_encrypt(cp, pwd->pw_passwd), pwd->pw_passwd) == 0) { break; } bb_do_delay(FAIL_DELAY); bb_error_msg("login incorrect"); } memset(cp, 0, strlen(cp)); // signal(SIGALRM, SIG_DFL); bb_info_msg("System Maintenance Mode"); USE_SELINUX(renew_current_security_context()); shell = getenv("SUSHELL"); if (!shell) shell = getenv("sushell"); if (!shell) { shell = "/bin/sh"; if (pwd->pw_shell[0]) shell = pwd->pw_shell; } /* Exec login shell with no additional parameters. Never returns. */ run_shell(shell, 1, NULL, NULL); auth_error: bb_error_msg_and_die("no password entry for root"); }