blob: 1509089328df8476a7adbf5d4c5847defc2b17ac [file] [log] [blame]
Eric Andersen27f64e12002-06-23 04:24:25 +00001/* vi: set sw=4 ts=4: */
"Robert P. J. Day"801ab142006-07-12 07:56:04 +00002/*
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +02003 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
"Robert P. J. Day"801ab142006-07-12 07:56:04 +00004 */
Pere Orga6a3e01d2011-04-01 22:56:30 +02005
6//usage:#define passwd_trivial_usage
7//usage: "[OPTIONS] [USER]"
8//usage:#define passwd_full_usage "\n\n"
Denys Vlasenko3a240212011-05-13 03:31:45 +02009//usage: "Change USER's password (default: current user)"
10//usage: "\n"
Denys Vlasenko12a43272011-05-13 03:19:01 +020011//usage: "\n -a ALG Encryption method"
Denys Vlasenko3a240212011-05-13 03:31:45 +020012//usage: "\n -d Set password to ''"
Pere Orga6a3e01d2011-04-01 22:56:30 +020013//usage: "\n -l Lock (disable) account"
Denys Vlasenko3a240212011-05-13 03:31:45 +020014//usage: "\n -u Unlock (enable) account"
Pere Orga6a3e01d2011-04-01 22:56:30 +020015
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000016#include "libbb.h"
Rob Landleyd921b2e2006-08-03 15:41:12 +000017#include <syslog.h>
Tanguy Pruvot823694d2012-11-18 13:20:29 +010018#include <sys/resource.h> /* setrlimit */
Eric Andersen27f64e12002-06-23 04:24:25 +000019
Denys Vlasenko12a43272011-05-13 03:19:01 +020020static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo)
Eric Andersen27f64e12002-06-23 04:24:25 +000021{
Denys Vlasenko12a43272011-05-13 03:19:01 +020022 char salt[MAX_PW_SALT_LEN];
Denis Vlasenko06c0a712007-01-29 22:51:44 +000023 char *orig = (char*)"";
Denis Vlasenkoab24e182006-11-30 16:41:15 +000024 char *newp = NULL;
Denis Vlasenkoab24e182006-11-30 16:41:15 +000025 char *cp = NULL;
26 char *ret = NULL; /* failure so far */
Eric Andersen27f64e12002-06-23 04:24:25 +000027
Denys Vlasenko12a43272011-05-13 03:19:01 +020028 if (myuid != 0 && pw->pw_passwd[0]) {
Denis Vlasenkofdddab02008-06-12 16:56:52 +000029 char *encrypted;
30
Denys Vlasenkoc6fb2a62009-11-02 19:18:49 +010031 orig = bb_ask_stdin("Old password: "); /* returns ptr to static */
Denis Vlasenkoab24e182006-11-30 16:41:15 +000032 if (!orig)
33 goto err_ret;
Denis Vlasenkofdddab02008-06-12 16:56:52 +000034 encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
35 if (strcmp(encrypted, pw->pw_passwd) != 0) {
Denys Vlasenko12a43272011-05-13 03:19:01 +020036 syslog(LOG_WARNING, "incorrect password for %s", pw->pw_name);
Denys Vlasenko7d4e7a22011-03-08 21:07:05 +010037 bb_do_delay(LOGIN_FAIL_DELAY);
Denis Vlasenkod78b4332006-09-23 12:30:03 +000038 puts("Incorrect password");
Denis Vlasenkoab24e182006-11-30 16:41:15 +000039 goto err_ret;
Eric Andersen27f64e12002-06-23 04:24:25 +000040 }
Denys Vlasenko12a43272011-05-13 03:19:01 +020041 if (ENABLE_FEATURE_CLEAN_UP)
42 free(encrypted);
Eric Andersen27f64e12002-06-23 04:24:25 +000043 }
Bernhard Reutner-Fischer82b14292008-12-03 18:48:39 +000044 orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */
Denys Vlasenkoc6fb2a62009-11-02 19:18:49 +010045 newp = bb_ask_stdin("New password: "); /* returns ptr to static */
Denis Vlasenkoab24e182006-11-30 16:41:15 +000046 if (!newp)
47 goto err_ret;
Bernhard Reutner-Fischer82b14292008-12-03 18:48:39 +000048 newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
Denis Vlasenko8eb3b392006-12-19 00:33:53 +000049 if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
Denys Vlasenko12a43272011-05-13 03:19:01 +020050 && obscure(orig, newp, pw)
51 && myuid != 0
52 ) {
Denis Vlasenko16c2c702006-12-12 18:11:58 +000053 goto err_ret; /* non-root is not allowed to have weak passwd */
Denys Vlasenko12a43272011-05-13 03:19:01 +020054 }
Eric Andersen27f64e12002-06-23 04:24:25 +000055
Denys Vlasenkoc6fb2a62009-11-02 19:18:49 +010056 cp = bb_ask_stdin("Retype password: ");
Denis Vlasenkoab24e182006-11-30 16:41:15 +000057 if (!cp)
58 goto err_ret;
Denys Vlasenko12a43272011-05-13 03:19:01 +020059 if (strcmp(cp, newp) != 0) {
Denis Vlasenkobecd8c52006-12-01 21:34:20 +000060 puts("Passwords don't match");
Denis Vlasenkoab24e182006-11-30 16:41:15 +000061 goto err_ret;
62 }
63
Denys Vlasenko12a43272011-05-13 03:19:01 +020064 crypt_make_pw_salt(salt, algo);
65
Denis Vlasenkofdddab02008-06-12 16:56:52 +000066 /* pw_encrypt returns malloced str */
67 ret = pw_encrypt(newp, salt, 1);
Denis Vlasenkoab24e182006-11-30 16:41:15 +000068 /* whee, success! */
Ned Ludd79197642006-04-21 00:40:35 +000069
Denis Vlasenkoab24e182006-11-30 16:41:15 +000070 err_ret:
71 nuke_str(orig);
72 if (ENABLE_FEATURE_CLEAN_UP) free(orig);
Denys Vlasenko12a43272011-05-13 03:19:01 +020073
Denis Vlasenkoab24e182006-11-30 16:41:15 +000074 nuke_str(newp);
75 if (ENABLE_FEATURE_CLEAN_UP) free(newp);
Denys Vlasenko12a43272011-05-13 03:19:01 +020076
Denis Vlasenkoab24e182006-11-30 16:41:15 +000077 nuke_str(cp);
78 return ret;
Eric Andersen27f64e12002-06-23 04:24:25 +000079}
80
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000081int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000082int passwd_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoab24e182006-11-30 16:41:15 +000083{
84 enum {
Denys Vlasenko12a43272011-05-13 03:19:01 +020085 OPT_algo = (1 << 0), /* -a - password algorithm */
86 OPT_lock = (1 << 1), /* -l - lock account */
87 OPT_unlock = (1 << 2), /* -u - unlock account */
88 OPT_delete = (1 << 3), /* -d - delete password */
89 OPT_lud = OPT_lock | OPT_unlock | OPT_delete,
Denis Vlasenkoab24e182006-11-30 16:41:15 +000090 };
91 unsigned opt;
Denis Vlasenko21d10142007-07-20 21:28:41 +000092 int rc;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020093 const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
Denis Vlasenkoab24e182006-11-30 16:41:15 +000094 const char *filename;
95 char *myname;
96 char *name;
Denis Vlasenkobecd8c52006-12-01 21:34:20 +000097 char *newp;
98 struct passwd *pw;
Denis Vlasenkoab24e182006-11-30 16:41:15 +000099 uid_t myuid;
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000100 struct rlimit rlimit_fsize;
101 char c;
Denis Vlasenko5df955f2007-03-13 13:01:14 +0000102#if ENABLE_FEATURE_SHADOWPASSWDS
103 /* Using _r function to avoid pulling in static buffers */
104 struct spwd spw;
Denis Vlasenko5df955f2007-03-13 13:01:14 +0000105 char buffer[256];
106#endif
107
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000108 logmode = LOGMODE_BOTH;
Denis Vlasenko54ac03a2009-03-11 15:59:49 +0000109 openlog(applet_name, 0, LOG_AUTH);
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000110 opt = getopt32(argv, "a:lud", &opt_a);
Denis Vlasenko3734b942007-07-27 11:20:10 +0000111 //argc -= optind;
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000112 argv += optind;
113
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000114 myuid = getuid();
Denis Vlasenko3734b942007-07-27 11:20:10 +0000115 /* -l, -u, -d require root priv and username argument */
Denys Vlasenko12a43272011-05-13 03:19:01 +0200116 if ((opt & OPT_lud) && (myuid != 0 || !argv[0]))
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000117 bb_show_usage();
118
Denis Vlasenko3734b942007-07-27 11:20:10 +0000119 /* Will complain and die if username not found */
Denis Vlasenko0c68a872008-12-02 22:56:59 +0000120 myname = xstrdup(xuid2uname(myuid));
Denis Vlasenko3734b942007-07-27 11:20:10 +0000121 name = argv[0] ? argv[0] : myname;
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000122
Denis Vlasenkod7a805e2008-12-03 19:05:55 +0000123 pw = xgetpwnam(name);
Denys Vlasenko12a43272011-05-13 03:19:01 +0200124 if (myuid != 0 && pw->pw_uid != myuid) {
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000125 /* LOGMODE_BOTH */
126 bb_error_msg_and_die("%s can't change password for %s", myname, name);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000127 }
128
Denis Vlasenkob5a122b2006-12-30 14:46:51 +0000129#if ENABLE_FEATURE_SHADOWPASSWDS
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000130 {
131 /* getspnam_r may return 0 yet set result to NULL.
132 * At least glibc 2.4 does this. Be extra paranoid here. */
133 struct spwd *result = NULL;
Denys Vlasenko8ced1e52010-01-09 22:21:55 +0100134 errno = 0;
135 if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0
136 || !result /* no error, but no record found either */
137 || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */
138 ) {
139 if (errno != ENOENT) {
140 /* LOGMODE_BOTH */
141 bb_perror_msg("no record of %s in %s, using %s",
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000142 name, bb_path_shadow_file,
143 bb_path_passwd_file);
Denys Vlasenko8ced1e52010-01-09 22:21:55 +0100144 }
145 /* else: /etc/shadow does not exist,
146 * apparently we are on a shadow-less system,
147 * no surprise there */
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000148 } else {
149 pw->pw_passwd = result->sp_pwdp;
150 }
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000151 }
Denis Vlasenkob5a122b2006-12-30 14:46:51 +0000152#endif
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000153
154 /* Decide what the new password will be */
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000155 newp = NULL;
156 c = pw->pw_passwd[0] - '!';
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000157 if (!(opt & OPT_lud)) {
Denys Vlasenko12a43272011-05-13 03:19:01 +0200158 if (myuid != 0 && !c) { /* passwd starts with '!' */
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000159 /* LOGMODE_BOTH */
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100160 bb_error_msg_and_die("can't change "
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000161 "locked password for %s", name);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000162 }
163 printf("Changing password for %s\n", name);
Denys Vlasenko12a43272011-05-13 03:19:01 +0200164 newp = new_password(pw, myuid, opt_a);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000165 if (!newp) {
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000166 logmode = LOGMODE_STDIO;
167 bb_error_msg_and_die("password for %s is unchanged", name);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000168 }
169 } else if (opt & OPT_lock) {
Denys Vlasenko12a43272011-05-13 03:19:01 +0200170 if (!c)
171 goto skip; /* passwd starts with '!' */
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000172 newp = xasprintf("!%s", pw->pw_passwd);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000173 } else if (opt & OPT_unlock) {
Denys Vlasenko12a43272011-05-13 03:19:01 +0200174 if (c)
175 goto skip; /* not '!' */
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000176 /* pw->pw_passwd points to static storage,
Denis Vlasenko3734b942007-07-27 11:20:10 +0000177 * strdup'ing to avoid nasty surprizes */
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000178 newp = xstrdup(&pw->pw_passwd[1]);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000179 } else if (opt & OPT_delete) {
Denys Vlasenko12a43272011-05-13 03:19:01 +0200180 newp = (char*)"";
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000181 }
182
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000183 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
184 setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
Denis Vlasenko25591c32008-02-16 22:58:56 +0000185 bb_signals(0
186 + (1 << SIGHUP)
187 + (1 << SIGINT)
188 + (1 << SIGQUIT)
189 , SIG_IGN);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000190 umask(077);
191 xsetuid(0);
Denis Vlasenko1d10aaf2007-07-27 11:22:34 +0000192
193#if ENABLE_FEATURE_SHADOWPASSWDS
194 filename = bb_path_shadow_file;
Denis Vlasenko829bbd32009-04-14 00:51:05 +0000195 rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
Tito Ragusa9eb7bfd2011-04-05 00:18:33 +0200196 if (rc > 0)
197 /* password in /etc/shadow was updated */
Denys Vlasenko12a43272011-05-13 03:19:01 +0200198 newp = (char*) "x";
Tito Ragusa9eb7bfd2011-04-05 00:18:33 +0200199 if (rc >= 0)
200 /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */
Denis Vlasenko1d10aaf2007-07-27 11:22:34 +0000201#endif
202 {
203 filename = bb_path_passwd_file;
Denis Vlasenko829bbd32009-04-14 00:51:05 +0000204 rc = update_passwd(bb_path_passwd_file, name, newp, NULL);
Denis Vlasenko1d10aaf2007-07-27 11:22:34 +0000205 }
206 /* LOGMODE_BOTH */
Denis Vlasenko21d10142007-07-20 21:28:41 +0000207 if (rc < 0)
Denys Vlasenko12a43272011-05-13 03:19:01 +0200208 bb_error_msg_and_die("can't update password file %s", filename);
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000209 bb_info_msg("Password for %s changed by %s", name, myname);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000210
Denys Vlasenko12a43272011-05-13 03:19:01 +0200211 /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */
Denis Vlasenko3734b942007-07-27 11:20:10 +0000212 skip:
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000213 if (!newp) {
214 bb_error_msg_and_die("password for %s is already %slocked",
215 name, (opt & OPT_unlock) ? "un" : "");
216 }
Denys Vlasenko12a43272011-05-13 03:19:01 +0200217
218 if (ENABLE_FEATURE_CLEAN_UP)
219 free(myname);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000220 return 0;
221}