X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=loginutils%2Fpasswd.c;h=b7b7423fd6ac0278be0be90d0f3ac961c0b44a54;hb=517a82c5b6b5e279f3e96a6774445a2952ca312b;hp=5d8380d4cbd92ccede3b93aeead21cb0515b5b02;hpb=d5bd137a247145afabfbc3a3c376ad8787381d8f;p=oweals%2Fbusybox.git diff --git a/loginutils/passwd.c b/loginutils/passwd.c index 5d8380d4c..b7b7423fd 100644 --- a/loginutils/passwd.c +++ b/loginutils/passwd.c @@ -1,400 +1,245 @@ /* vi: set sw=4 ts=4: */ -#include -#include -#include -#include -#include -#include -#include -#include +/* + * Licensed under GPLv2 or later, see file LICENSE in this source tree. + */ +//config:config PASSWD +//config: bool "passwd" +//config: default y +//config: select FEATURE_SYSLOG +//config: help +//config: passwd changes passwords for user and group accounts. A normal user +//config: may only change the password for his/her own account, the super user +//config: may change the password for any account. The administrator of a group +//config: may change the password for the group. +//config: +//config: Note that Busybox binary must be setuid root for this applet to +//config: work properly. +//config: +//config:config FEATURE_PASSWD_WEAK_CHECK +//config: bool "Check new passwords for weakness" +//config: default y +//config: depends on PASSWD +//config: help +//config: With this option passwd will refuse new passwords which are "weak". + +//applet:/* Needs to be run by root or be suid root - needs to change /etc/{passwd,shadow}: */ +//applet:IF_PASSWD(APPLET(passwd, BB_DIR_USR_BIN, BB_SUID_REQUIRE)) + +//kbuild:lib-$(CONFIG_PASSWD) += passwd.o + +//usage:#define passwd_trivial_usage +//usage: "[OPTIONS] [USER]" +//usage:#define passwd_full_usage "\n\n" +//usage: "Change USER's password (default: current user)" +//usage: "\n" +//usage: "\n -a ALG "CRYPT_METHODS_HELP_STR +//usage: "\n -d Set password to ''" +//usage: "\n -l Lock (disable) account" +//usage: "\n -u Unlock (enable) account" + +#include "libbb.h" #include -#include -#include -#include +#include /* setrlimit */ -#include "busybox.h" +static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo) +{ + char salt[MAX_PW_SALT_LEN]; + char *orig = (char*)""; + char *newp = NULL; + char *cp = NULL; + char *ret = NULL; /* failure so far */ + + if (myuid != 0 && pw->pw_passwd[0]) { + char *encrypted; + + orig = bb_ask_stdin("Old password: "); /* returns ptr to static */ + if (!orig) + goto err_ret; + encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */ + if (strcmp(encrypted, pw->pw_passwd) != 0) { + syslog(LOG_WARNING, "incorrect password for %s", pw->pw_name); + bb_do_delay(LOGIN_FAIL_DELAY); + puts("Incorrect password"); + goto err_ret; + } + if (ENABLE_FEATURE_CLEAN_UP) + free(encrypted); + } + orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */ + newp = bb_ask_stdin("New password: "); /* returns ptr to static */ + if (!newp) + goto err_ret; + newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */ + if (ENABLE_FEATURE_PASSWD_WEAK_CHECK + && obscure(orig, newp, pw) + && myuid != 0 + ) { + goto err_ret; /* non-root is not allowed to have weak passwd */ + } -static char crypt_passwd[128]; + cp = bb_ask_stdin("Retype password: "); + if (!cp) + goto err_ret; + if (strcmp(cp, newp) != 0) { + puts("Passwords don't match"); + goto err_ret; + } -static int create_backup(const char *backup, FILE * fp); -static int new_password(const struct passwd *pw, int amroot, int algo); -static void set_filesize_limit(int blocks); + crypt_make_pw_salt(salt, algo); + /* pw_encrypt returns malloced str */ + ret = pw_encrypt(newp, salt, 1); + /* whee, success! */ -static int get_algo(char *a) -{ - int x = 1; /* standard: MD5 */ + err_ret: + nuke_str(orig); + if (ENABLE_FEATURE_CLEAN_UP) free(orig); - if (strcasecmp(a, "des") == 0) - x = 0; - return x; -} + nuke_str(newp); + if (ENABLE_FEATURE_CLEAN_UP) free(newp); + nuke_str(cp); + return ret; +} -static int update_passwd(const struct passwd *pw, char *crypt_pw) +int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; +int passwd_main(int argc UNUSED_PARAM, char **argv) { - char filename[1024]; - char buf[1025]; - char buffer[80]; - char username[32]; - char *pw_rest; - int mask; - int continued; - FILE *fp; - FILE *out_fp; - struct stat sb; - struct flock lock; - -#ifdef CONFIG_FEATURE_SHADOWPASSWDS - if (access(bb_path_shadow_file, F_OK) == 0) { - snprintf(filename, sizeof filename, "%s", bb_path_shadow_file); - } else + enum { + OPT_algo = (1 << 0), /* -a - password algorithm */ + OPT_lock = (1 << 1), /* -l - lock account */ + OPT_unlock = (1 << 2), /* -u - unlock account */ + OPT_delete = (1 << 3), /* -d - delete password */ + OPT_lud = OPT_lock | OPT_unlock | OPT_delete, + }; + unsigned opt; + int rc; + const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; + const char *filename; + char *myname; + char *name; + char *newp; + struct passwd *pw; + uid_t myuid; + struct rlimit rlimit_fsize; + char c; +#if ENABLE_FEATURE_SHADOWPASSWDS + /* Using _r function to avoid pulling in static buffers */ + struct spwd spw; + char buffer[256]; #endif - { - snprintf(filename, sizeof filename, "%s", bb_path_passwd_file); - } - if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) { - /* return 0; */ - return 1; - } + logmode = LOGMODE_BOTH; + openlog(applet_name, 0, LOG_AUTH); + opt = getopt32(argv, "a:lud", &opt_a); + //argc -= optind; + argv += optind; - /* Lock the password file before updating */ - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - if (fcntl(fileno(fp), F_SETLK, &lock) < 0) { - fprintf(stderr, "%s: %s\n", filename, strerror(errno)); - return 1; - } - lock.l_type = F_UNLCK; + myuid = getuid(); + /* -l, -u, -d require root priv and username argument */ + if ((opt & OPT_lud) && (myuid != 0 || !argv[0])) + bb_show_usage(); - snprintf(buf, sizeof buf, "%s-", filename); - if (create_backup(buf, fp)) { - fcntl(fileno(fp), F_SETLK, &lock); - fclose(fp); - return 1; - } - snprintf(buf, sizeof buf, "%s+", filename); - mask = umask(0777); - out_fp = fopen(buf, "w"); - umask(mask); - if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777)) - || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) { - fcntl(fileno(fp), F_SETLK, &lock); - fclose(fp); - fclose(out_fp); - return 1; + /* Will complain and die if username not found */ + myname = xstrdup(xuid2uname(myuid)); + name = argv[0] ? argv[0] : myname; + + pw = xgetpwnam(name); + if (myuid != 0 && pw->pw_uid != myuid) { + /* LOGMODE_BOTH */ + bb_error_msg_and_die("%s can't change password for %s", myname, name); } - continued = 0; - snprintf(username, sizeof username, "%s:", pw->pw_name); - rewind(fp); - while (!feof(fp)) { - fgets(buffer, sizeof buffer, fp); - if (!continued) { // Check to see if we're updating this line. - if (strncmp(username, buffer, strlen(username)) == 0) { // we have a match. - pw_rest = strchr(buffer, ':'); - *pw_rest++ = '\0'; - pw_rest = strchr(pw_rest, ':'); - fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest); - } else { - fputs(buffer, out_fp); +#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; + errno = 0; + if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0 + || !result /* no error, but no record found either */ + || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */ + ) { + if (errno != ENOENT) { + /* LOGMODE_BOTH */ + bb_perror_msg("no record of %s in %s, using %s", + name, bb_path_shadow_file, + bb_path_passwd_file); } + /* else: /etc/shadow does not exist, + * apparently we are on a shadow-less system, + * no surprise there */ } else { - fputs(buffer, out_fp); - } - if (buffer[strlen(buffer) - 1] == '\n') { - continued = 0; - } else { - continued = 1; + pw->pw_passwd = result->sp_pwdp; } - bzero(buffer, sizeof buffer); } +#endif - if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) { - unlink(buf); - fcntl(fileno(fp), F_SETLK, &lock); - fclose(fp); - return 1; - } - if (rename(buf, filename) < 0) { - fcntl(fileno(fp), F_SETLK, &lock); - fclose(fp); - return 1; - } else { - fcntl(fileno(fp), F_SETLK, &lock); - fclose(fp); - return 0; - } -} - - -extern int passwd_main(int argc, char **argv) -{ - int amroot; - char *cp; - char *np; - char *name; - char *myname; - int flag; - int algo = 1; /* -a - password algorithm */ - int lflg = 0; /* -l - lock account */ - int uflg = 0; /* -u - unlock account */ - int dflg = 0; /* -d - delete password */ - const struct passwd *pw; - -#ifdef CONFIG_FEATURE_SHADOWPASSWDS - const struct spwd *sp; -#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ - amroot = (getuid() == 0); - openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); - while ((flag = getopt(argc, argv, "a:dlu")) != EOF) { - switch (flag) { - case 'a': - algo = get_algo(optarg); - break; - case 'd': - dflg++; - break; - case 'l': - lflg++; - break; - case 'u': - uflg++; - break; - default: - bb_show_usage(); - } - } - myname = (char *) bb_xstrdup(bb_getpwuid(NULL, getuid(), -1)); - /* exits on error */ - if (optind < argc) { - name = argv[optind]; - } else { - name = myname; - } - if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) { - bb_show_usage(); - } - pw = getpwnam(name); - if (!pw) { - bb_error_msg_and_die("Unknown user %s\n", name); - } - if (!amroot && pw->pw_uid != getuid()) { - syslog(LOG_WARNING, "can't change pwd for `%s'", name); - bb_error_msg_and_die("Permission denied.\n"); - } -#ifdef CONFIG_FEATURE_SHADOWPASSWDS - sp = getspnam(name); - if (!sp) { - sp = (struct spwd *) pwd_to_spwd(pw); - } - cp = sp->sp_pwdp; - np = sp->sp_namp; -#else - cp = pw->pw_passwd; - np = name; -#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ - - safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); - if (!(dflg || lflg || uflg)) { - if (!amroot) { - if (cp[0] == '!') { - syslog(LOG_WARNING, "password locked for `%s'", np); - bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np); - } + /* Decide what the new password will be */ + newp = NULL; + c = pw->pw_passwd[0] - '!'; + if (!(opt & OPT_lud)) { + if (myuid != 0 && !c) { /* passwd starts with '!' */ + /* LOGMODE_BOTH */ + bb_error_msg_and_die("can't change " + "locked password for %s", name); } printf("Changing password for %s\n", name); - if (new_password(pw, amroot, algo)) { - bb_error_msg_and_die( "The password for %s is unchanged.\n", name); - } - } else if (lflg) { - if (crypt_passwd[0] != '!') { - memmove(&crypt_passwd[1], crypt_passwd, - sizeof crypt_passwd - 1); - crypt_passwd[sizeof crypt_passwd - 1] = '\0'; - crypt_passwd[0] = '!'; - } - } else if (uflg) { - if (crypt_passwd[0] == '!') { - memmove(crypt_passwd, &crypt_passwd[1], - sizeof crypt_passwd - 1); + newp = new_password(pw, myuid, opt_a); + if (!newp) { + logmode = LOGMODE_STDIO; + bb_error_msg_and_die("password for %s is unchanged", name); } - } else if (dflg) { - crypt_passwd[0] = '\0'; - } - set_filesize_limit(30000); - signal(SIGHUP, SIG_IGN); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); + } else if (opt & OPT_lock) { + if (!c) + goto skip; /* passwd starts with '!' */ + newp = xasprintf("!%s", pw->pw_passwd); + } else if (opt & OPT_unlock) { + if (c) + goto skip; /* not '!' */ + /* pw->pw_passwd points to static storage, + * strdup'ing to avoid nasty surprizes */ + newp = xstrdup(&pw->pw_passwd[1]); + } else if (opt & OPT_delete) { + newp = (char*)""; + } + + rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000; + setrlimit(RLIMIT_FSIZE, &rlimit_fsize); + bb_signals(0 + + (1 << SIGHUP) + + (1 << SIGINT) + + (1 << SIGQUIT) + , SIG_IGN); umask(077); - if (setuid(0)) { - syslog(LOG_ERR, "can't setuid(0)"); - bb_error_msg_and_die( "Cannot change ID to root.\n"); - } - if (!update_passwd(pw, crypt_passwd)) { - syslog(LOG_INFO, "password for `%s' changed by user `%s'", name, - myname); - printf("Password changed.\n"); - } else { - syslog(LOG_WARNING, "an error occurred updating the password file"); - bb_error_msg_and_die("An error occurred updating the password file.\n"); - } - return (0); -} - - - -static int create_backup(const char *backup, FILE * fp) -{ - struct stat sb; - struct utimbuf ub; - FILE *bkfp; - int c, mask; - - if (fstat(fileno(fp), &sb)) - /* return -1; */ - return 1; - - mask = umask(077); - bkfp = fopen(backup, "w"); - umask(mask); - if (!bkfp) - /* return -1; */ - return 1; - - /* TODO: faster copy, not one-char-at-a-time. --marekm */ - rewind(fp); - while ((c = getc(fp)) != EOF) { - if (putc(c, bkfp) == EOF) - break; - } - if (c != EOF || fflush(bkfp)) { - fclose(bkfp); - /* return -1; */ - return 1; - } - if (fclose(bkfp)) - /* return -1; */ - return 1; - - ub.actime = sb.st_atime; - ub.modtime = sb.st_mtime; - utime(backup, &ub); - return 0; -} - -static int i64c(int i) -{ - if (i <= 0) - return ('.'); - if (i == 1) - return ('/'); - if (i >= 2 && i < 12) - return ('0' - 2 + i); - if (i >= 12 && i < 38) - return ('A' - 12 + i); - if (i >= 38 && i < 63) - return ('a' - 38 + i); - return ('z'); -} - -static char *crypt_make_salt(void) -{ - time_t now; - static unsigned long x; - static char result[3]; - - time(&now); - x += now + getpid() + clock(); - result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077); - result[1] = i64c(((x >> 12) ^ x) & 077); - result[2] = '\0'; - return result; -} - - -static int new_password(const struct passwd *pw, int amroot, int algo) -{ - char *clear; - char *cipher; - char *cp; - char orig[200]; - char pass[200]; - time_t start, now; - - if (!amroot && crypt_passwd[0]) { - if (!(clear = bb_askpass(0, "Old password:"))) { - /* return -1; */ - return 1; - } - cipher = pw_encrypt(clear, crypt_passwd); - if (strcmp(cipher, crypt_passwd) != 0) { - syslog(LOG_WARNING, "incorrect password for `%s'", - pw->pw_name); - time(&start); - now = start; - while (difftime(now, start) < FAIL_DELAY) { - sleep(FAIL_DELAY); - time(&now); - } - fprintf(stderr, "Incorrect password.\n"); - /* return -1; */ - return 1; - } - safe_strncpy(orig, clear, sizeof(orig)); - bzero(clear, strlen(clear)); - bzero(cipher, strlen(cipher)); - } else { - orig[0] = '\0'; - } - if (! (cp=bb_askpass(0, "Enter the new password (minimum of 5, maximum of 8 characters)\n" - "Please use a combination of upper and lower case letters and numbers.\n" - "Enter new password: "))) + xsetuid(0); + +#if ENABLE_FEATURE_SHADOWPASSWDS + filename = bb_path_shadow_file; + rc = update_passwd(bb_path_shadow_file, name, newp, NULL); + if (rc > 0) + /* password in /etc/shadow was updated */ + newp = (char*) "x"; + if (rc >= 0) + /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ +#endif { - bzero(orig, sizeof orig); - /* return -1; */ - return 1; + filename = bb_path_passwd_file; + rc = update_passwd(bb_path_passwd_file, name, newp, NULL); } - safe_strncpy(pass, cp, sizeof(pass)); - bzero(cp, strlen(cp)); - /* if (!obscure(orig, pass, pw)) { */ - if (obscure(orig, pass, pw)) { - if (amroot) { - printf("\nWarning: weak password (continuing).\n"); - } else { - /* return -1; */ - return 1; - } - } - if (!(cp = bb_askpass(0, "Re-enter new password: "))) { - bzero(orig, sizeof orig); - /* return -1; */ - return 1; - } - if (strcmp(cp, pass)) { - fprintf(stderr, "Passwords do not match.\n"); - /* return -1; */ - return 1; + /* LOGMODE_BOTH */ + if (rc < 0) + bb_error_msg_and_die("can't update password file %s", filename); + bb_error_msg("password for %s changed by %s", name, myname); + + /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */ + skip: + if (!newp) { + bb_error_msg_and_die("password for %s is already %slocked", + name, (opt & OPT_unlock) ? "un" : ""); } - bzero(cp, strlen(cp)); - bzero(orig, sizeof(orig)); - if (algo == 1) { - cp = pw_encrypt(pass, "$1$"); - } else - cp = pw_encrypt(pass, crypt_make_salt()); - bzero(pass, sizeof pass); - safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd)); + if (ENABLE_FEATURE_CLEAN_UP) + free(myname); return 0; } - -static void set_filesize_limit(int blocks) -{ - struct rlimit rlimit_fsize; - - rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks; - setrlimit(RLIMIT_FSIZE, &rlimit_fsize); -}