1 /* vi: set sw=4 ts=4: */
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
6 //usage:#define passwd_trivial_usage
7 //usage: "[OPTIONS] [USER]"
8 //usage:#define passwd_full_usage "\n\n"
9 //usage: "Change USER's password. If no USER is specified,\n"
10 //usage: "changes the password for the current user.\n"
12 //usage: "\n -a ALG Algorithm to use for password (des, md5)" /* ", sha1)" */
13 //usage: "\n -d Delete password for the account"
14 //usage: "\n -l Lock (disable) account"
15 //usage: "\n -u Unlock (re-enable) account"
20 static void nuke_str(char *str)
22 if (str) memset(str, 0, strlen(str));
25 static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
27 char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */
28 char *orig = (char*)"";
31 char *ret = NULL; /* failure so far */
33 if (myuid && pw->pw_passwd[0]) {
36 orig = bb_ask_stdin("Old password: "); /* returns ptr to static */
39 encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
40 if (strcmp(encrypted, pw->pw_passwd) != 0) {
41 syslog(LOG_WARNING, "incorrect password for %s",
43 bb_do_delay(LOGIN_FAIL_DELAY);
44 puts("Incorrect password");
47 if (ENABLE_FEATURE_CLEAN_UP) free(encrypted);
49 orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */
50 newp = bb_ask_stdin("New password: "); /* returns ptr to static */
53 newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
54 if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
55 && obscure(orig, newp, pw) && myuid)
56 goto err_ret; /* non-root is not allowed to have weak passwd */
58 cp = bb_ask_stdin("Retype password: ");
61 if (strcmp(cp, newp)) {
62 puts("Passwords don't match");
66 crypt_make_salt(salt, 1, 0); /* des */
69 crypt_make_salt(salt + 3, 4, 0);
71 /* pw_encrypt returns malloced str */
72 ret = pw_encrypt(newp, salt, 1);
77 if (ENABLE_FEATURE_CLEAN_UP) free(orig);
79 if (ENABLE_FEATURE_CLEAN_UP) free(newp);
84 int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
85 int passwd_main(int argc UNUSED_PARAM, char **argv)
88 OPT_algo = 0x1, /* -a - password algorithm */
89 OPT_lock = 0x2, /* -l - lock account */
90 OPT_unlock = 0x4, /* -u - unlock account */
91 OPT_delete = 0x8, /* -d - delete password */
93 STATE_ALGO_md5 = 0x10,
94 //STATE_ALGO_des = 0x20, not needed yet
98 const char *opt_a = "";
105 struct rlimit rlimit_fsize;
107 #if ENABLE_FEATURE_SHADOWPASSWDS
108 /* Using _r function to avoid pulling in static buffers */
113 logmode = LOGMODE_BOTH;
114 openlog(applet_name, 0, LOG_AUTH);
115 opt = getopt32(argv, "a:lud", &opt_a);
119 if (strcasecmp(opt_a, "des") != 0) /* -a */
120 opt |= STATE_ALGO_md5;
122 // opt |= STATE_ALGO_des;
124 /* -l, -u, -d require root priv and username argument */
125 if ((opt & OPT_lud) && (myuid || !argv[0]))
128 /* Will complain and die if username not found */
129 myname = xstrdup(xuid2uname(myuid));
130 name = argv[0] ? argv[0] : myname;
132 pw = xgetpwnam(name);
133 if (myuid && pw->pw_uid != myuid) {
135 bb_error_msg_and_die("%s can't change password for %s", myname, name);
138 #if ENABLE_FEATURE_SHADOWPASSWDS
140 /* getspnam_r may return 0 yet set result to NULL.
141 * At least glibc 2.4 does this. Be extra paranoid here. */
142 struct spwd *result = NULL;
144 if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0
145 || !result /* no error, but no record found either */
146 || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */
148 if (errno != ENOENT) {
150 bb_perror_msg("no record of %s in %s, using %s",
151 name, bb_path_shadow_file,
152 bb_path_passwd_file);
154 /* else: /etc/shadow does not exist,
155 * apparently we are on a shadow-less system,
156 * no surprise there */
158 pw->pw_passwd = result->sp_pwdp;
163 /* Decide what the new password will be */
165 c = pw->pw_passwd[0] - '!';
166 if (!(opt & OPT_lud)) {
167 if (myuid && !c) { /* passwd starts with '!' */
169 bb_error_msg_and_die("can't change "
170 "locked password for %s", name);
172 printf("Changing password for %s\n", name);
173 newp = new_password(pw, myuid, opt & STATE_ALGO_md5);
175 logmode = LOGMODE_STDIO;
176 bb_error_msg_and_die("password for %s is unchanged", name);
178 } else if (opt & OPT_lock) {
179 if (!c) goto skip; /* passwd starts with '!' */
180 newp = xasprintf("!%s", pw->pw_passwd);
181 } else if (opt & OPT_unlock) {
182 if (c) goto skip; /* not '!' */
183 /* pw->pw_passwd points to static storage,
184 * strdup'ing to avoid nasty surprizes */
185 newp = xstrdup(&pw->pw_passwd[1]);
186 } else if (opt & OPT_delete) {
187 newp = (char*)""; //xstrdup("");
190 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
191 setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
200 #if ENABLE_FEATURE_SHADOWPASSWDS
201 filename = bb_path_shadow_file;
202 rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
204 /* password in /etc/shadow was updated */
205 newp = (char*) "x"; //xstrdup("x");
207 /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */
210 filename = bb_path_passwd_file;
211 rc = update_passwd(bb_path_passwd_file, name, newp, NULL);
215 bb_error_msg_and_die("can't update password file %s",
217 bb_info_msg("Password for %s changed by %s", name, myname);
219 //if (ENABLE_FEATURE_CLEAN_UP) free(newp);
222 bb_error_msg_and_die("password for %s is already %slocked",
223 name, (opt & OPT_unlock) ? "un" : "");
225 if (ENABLE_FEATURE_CLEAN_UP) free(myname);