blob: b83db0083624bf47866c9f2c9b914781632646c0 [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>
Eric Andersen27f64e12002-06-23 04:24:25 +000018
Denis Vlasenkoab24e182006-11-30 16:41:15 +000019static void nuke_str(char *str)
Eric Andersen27f64e12002-06-23 04:24:25 +000020{
Denis Vlasenkoab24e182006-11-30 16:41:15 +000021 if (str) memset(str, 0, strlen(str));
Eric Andersen27f64e12002-06-23 04:24:25 +000022}
23
Denys Vlasenko12a43272011-05-13 03:19:01 +020024static char* new_password(const struct passwd *pw, uid_t myuid, const char *algo)
Eric Andersen27f64e12002-06-23 04:24:25 +000025{
Denys Vlasenko12a43272011-05-13 03:19:01 +020026 char salt[MAX_PW_SALT_LEN];
Denis Vlasenko06c0a712007-01-29 22:51:44 +000027 char *orig = (char*)"";
Denis Vlasenkoab24e182006-11-30 16:41:15 +000028 char *newp = NULL;
Denis Vlasenkoab24e182006-11-30 16:41:15 +000029 char *cp = NULL;
30 char *ret = NULL; /* failure so far */
Eric Andersen27f64e12002-06-23 04:24:25 +000031
Denys Vlasenko12a43272011-05-13 03:19:01 +020032 if (myuid != 0 && pw->pw_passwd[0]) {
Denis Vlasenkofdddab02008-06-12 16:56:52 +000033 char *encrypted;
34
Denys Vlasenkoc6fb2a62009-11-02 19:18:49 +010035 orig = bb_ask_stdin("Old password: "); /* returns ptr to static */
Denis Vlasenkoab24e182006-11-30 16:41:15 +000036 if (!orig)
37 goto err_ret;
Denis Vlasenkofdddab02008-06-12 16:56:52 +000038 encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
39 if (strcmp(encrypted, pw->pw_passwd) != 0) {
Denys Vlasenko12a43272011-05-13 03:19:01 +020040 syslog(LOG_WARNING, "incorrect password for %s", pw->pw_name);
Denys Vlasenko7d4e7a22011-03-08 21:07:05 +010041 bb_do_delay(LOGIN_FAIL_DELAY);
Denis Vlasenkod78b4332006-09-23 12:30:03 +000042 puts("Incorrect password");
Denis Vlasenkoab24e182006-11-30 16:41:15 +000043 goto err_ret;
Eric Andersen27f64e12002-06-23 04:24:25 +000044 }
Denys Vlasenko12a43272011-05-13 03:19:01 +020045 if (ENABLE_FEATURE_CLEAN_UP)
46 free(encrypted);
Eric Andersen27f64e12002-06-23 04:24:25 +000047 }
Bernhard Reutner-Fischer82b14292008-12-03 18:48:39 +000048 orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */
Denys Vlasenkoc6fb2a62009-11-02 19:18:49 +010049 newp = bb_ask_stdin("New password: "); /* returns ptr to static */
Denis Vlasenkoab24e182006-11-30 16:41:15 +000050 if (!newp)
51 goto err_ret;
Bernhard Reutner-Fischer82b14292008-12-03 18:48:39 +000052 newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
Denis Vlasenko8eb3b392006-12-19 00:33:53 +000053 if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
Denys Vlasenko12a43272011-05-13 03:19:01 +020054 && obscure(orig, newp, pw)
55 && myuid != 0
56 ) {
Denis Vlasenko16c2c702006-12-12 18:11:58 +000057 goto err_ret; /* non-root is not allowed to have weak passwd */
Denys Vlasenko12a43272011-05-13 03:19:01 +020058 }
Eric Andersen27f64e12002-06-23 04:24:25 +000059
Denys Vlasenkoc6fb2a62009-11-02 19:18:49 +010060 cp = bb_ask_stdin("Retype password: ");
Denis Vlasenkoab24e182006-11-30 16:41:15 +000061 if (!cp)
62 goto err_ret;
Denys Vlasenko12a43272011-05-13 03:19:01 +020063 if (strcmp(cp, newp) != 0) {
Denis Vlasenkobecd8c52006-12-01 21:34:20 +000064 puts("Passwords don't match");
Denis Vlasenkoab24e182006-11-30 16:41:15 +000065 goto err_ret;
66 }
67
Denys Vlasenko12a43272011-05-13 03:19:01 +020068 crypt_make_pw_salt(salt, algo);
69
Denis Vlasenkofdddab02008-06-12 16:56:52 +000070 /* pw_encrypt returns malloced str */
71 ret = pw_encrypt(newp, salt, 1);
Denis Vlasenkoab24e182006-11-30 16:41:15 +000072 /* whee, success! */
Ned Ludd79197642006-04-21 00:40:35 +000073
Denis Vlasenkoab24e182006-11-30 16:41:15 +000074 err_ret:
75 nuke_str(orig);
76 if (ENABLE_FEATURE_CLEAN_UP) free(orig);
Denys Vlasenko12a43272011-05-13 03:19:01 +020077
Denis Vlasenkoab24e182006-11-30 16:41:15 +000078 nuke_str(newp);
79 if (ENABLE_FEATURE_CLEAN_UP) free(newp);
Denys Vlasenko12a43272011-05-13 03:19:01 +020080
Denis Vlasenkoab24e182006-11-30 16:41:15 +000081 nuke_str(cp);
82 return ret;
Eric Andersen27f64e12002-06-23 04:24:25 +000083}
84
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000085int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000086int passwd_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoab24e182006-11-30 16:41:15 +000087{
88 enum {
Denys Vlasenko12a43272011-05-13 03:19:01 +020089 OPT_algo = (1 << 0), /* -a - password algorithm */
90 OPT_lock = (1 << 1), /* -l - lock account */
91 OPT_unlock = (1 << 2), /* -u - unlock account */
92 OPT_delete = (1 << 3), /* -d - delete password */
93 OPT_lud = OPT_lock | OPT_unlock | OPT_delete,
Denis Vlasenkoab24e182006-11-30 16:41:15 +000094 };
95 unsigned opt;
Denis Vlasenko21d10142007-07-20 21:28:41 +000096 int rc;
Tanguy Pruvot8a6c2c22012-04-28 00:24:09 +020097 const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO;
Denis Vlasenkoab24e182006-11-30 16:41:15 +000098 const char *filename;
99 char *myname;
100 char *name;
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000101 char *newp;
102 struct passwd *pw;
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000103 uid_t myuid;
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000104 struct rlimit rlimit_fsize;
105 char c;
Denis Vlasenko5df955f2007-03-13 13:01:14 +0000106#if ENABLE_FEATURE_SHADOWPASSWDS
107 /* Using _r function to avoid pulling in static buffers */
108 struct spwd spw;
Denis Vlasenko5df955f2007-03-13 13:01:14 +0000109 char buffer[256];
110#endif
111
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000112 logmode = LOGMODE_BOTH;
Denis Vlasenko54ac03a2009-03-11 15:59:49 +0000113 openlog(applet_name, 0, LOG_AUTH);
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000114 opt = getopt32(argv, "a:lud", &opt_a);
Denis Vlasenko3734b942007-07-27 11:20:10 +0000115 //argc -= optind;
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000116 argv += optind;
117
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000118 myuid = getuid();
Denis Vlasenko3734b942007-07-27 11:20:10 +0000119 /* -l, -u, -d require root priv and username argument */
Denys Vlasenko12a43272011-05-13 03:19:01 +0200120 if ((opt & OPT_lud) && (myuid != 0 || !argv[0]))
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000121 bb_show_usage();
122
Denis Vlasenko3734b942007-07-27 11:20:10 +0000123 /* Will complain and die if username not found */
Denis Vlasenko0c68a872008-12-02 22:56:59 +0000124 myname = xstrdup(xuid2uname(myuid));
Denis Vlasenko3734b942007-07-27 11:20:10 +0000125 name = argv[0] ? argv[0] : myname;
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000126
Denis Vlasenkod7a805e2008-12-03 19:05:55 +0000127 pw = xgetpwnam(name);
Denys Vlasenko12a43272011-05-13 03:19:01 +0200128 if (myuid != 0 && pw->pw_uid != myuid) {
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000129 /* LOGMODE_BOTH */
130 bb_error_msg_and_die("%s can't change password for %s", myname, name);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000131 }
132
Denis Vlasenkob5a122b2006-12-30 14:46:51 +0000133#if ENABLE_FEATURE_SHADOWPASSWDS
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000134 {
135 /* getspnam_r may return 0 yet set result to NULL.
136 * At least glibc 2.4 does this. Be extra paranoid here. */
137 struct spwd *result = NULL;
Denys Vlasenko8ced1e52010-01-09 22:21:55 +0100138 errno = 0;
139 if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0
140 || !result /* no error, but no record found either */
141 || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */
142 ) {
143 if (errno != ENOENT) {
144 /* LOGMODE_BOTH */
145 bb_perror_msg("no record of %s in %s, using %s",
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000146 name, bb_path_shadow_file,
147 bb_path_passwd_file);
Denys Vlasenko8ced1e52010-01-09 22:21:55 +0100148 }
149 /* else: /etc/shadow does not exist,
150 * apparently we are on a shadow-less system,
151 * no surprise there */
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000152 } else {
153 pw->pw_passwd = result->sp_pwdp;
154 }
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000155 }
Denis Vlasenkob5a122b2006-12-30 14:46:51 +0000156#endif
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000157
158 /* Decide what the new password will be */
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000159 newp = NULL;
160 c = pw->pw_passwd[0] - '!';
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000161 if (!(opt & OPT_lud)) {
Denys Vlasenko12a43272011-05-13 03:19:01 +0200162 if (myuid != 0 && !c) { /* passwd starts with '!' */
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000163 /* LOGMODE_BOTH */
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100164 bb_error_msg_and_die("can't change "
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000165 "locked password for %s", name);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000166 }
167 printf("Changing password for %s\n", name);
Denys Vlasenko12a43272011-05-13 03:19:01 +0200168 newp = new_password(pw, myuid, opt_a);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000169 if (!newp) {
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000170 logmode = LOGMODE_STDIO;
171 bb_error_msg_and_die("password for %s is unchanged", name);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000172 }
173 } else if (opt & OPT_lock) {
Denys Vlasenko12a43272011-05-13 03:19:01 +0200174 if (!c)
175 goto skip; /* passwd starts with '!' */
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000176 newp = xasprintf("!%s", pw->pw_passwd);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000177 } else if (opt & OPT_unlock) {
Denys Vlasenko12a43272011-05-13 03:19:01 +0200178 if (c)
179 goto skip; /* not '!' */
Denis Vlasenko15ca51e2007-10-29 19:25:45 +0000180 /* pw->pw_passwd points to static storage,
Denis Vlasenko3734b942007-07-27 11:20:10 +0000181 * strdup'ing to avoid nasty surprizes */
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000182 newp = xstrdup(&pw->pw_passwd[1]);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000183 } else if (opt & OPT_delete) {
Denys Vlasenko12a43272011-05-13 03:19:01 +0200184 newp = (char*)"";
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000185 }
186
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000187 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
188 setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
Denis Vlasenko25591c32008-02-16 22:58:56 +0000189 bb_signals(0
190 + (1 << SIGHUP)
191 + (1 << SIGINT)
192 + (1 << SIGQUIT)
193 , SIG_IGN);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000194 umask(077);
195 xsetuid(0);
Denis Vlasenko1d10aaf2007-07-27 11:22:34 +0000196
197#if ENABLE_FEATURE_SHADOWPASSWDS
198 filename = bb_path_shadow_file;
Denis Vlasenko829bbd32009-04-14 00:51:05 +0000199 rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
Tito Ragusa9eb7bfd2011-04-05 00:18:33 +0200200 if (rc > 0)
201 /* password in /etc/shadow was updated */
Denys Vlasenko12a43272011-05-13 03:19:01 +0200202 newp = (char*) "x";
Tito Ragusa9eb7bfd2011-04-05 00:18:33 +0200203 if (rc >= 0)
204 /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */
Denis Vlasenko1d10aaf2007-07-27 11:22:34 +0000205#endif
206 {
207 filename = bb_path_passwd_file;
Denis Vlasenko829bbd32009-04-14 00:51:05 +0000208 rc = update_passwd(bb_path_passwd_file, name, newp, NULL);
Denis Vlasenko1d10aaf2007-07-27 11:22:34 +0000209 }
210 /* LOGMODE_BOTH */
Denis Vlasenko21d10142007-07-20 21:28:41 +0000211 if (rc < 0)
Denys Vlasenko12a43272011-05-13 03:19:01 +0200212 bb_error_msg_and_die("can't update password file %s", filename);
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000213 bb_info_msg("Password for %s changed by %s", name, myname);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000214
Denys Vlasenko12a43272011-05-13 03:19:01 +0200215 /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */
Denis Vlasenko3734b942007-07-27 11:20:10 +0000216 skip:
Denis Vlasenkobecd8c52006-12-01 21:34:20 +0000217 if (!newp) {
218 bb_error_msg_and_die("password for %s is already %slocked",
219 name, (opt & OPT_unlock) ? "un" : "");
220 }
Denys Vlasenko12a43272011-05-13 03:19:01 +0200221
222 if (ENABLE_FEATURE_CLEAN_UP)
223 free(myname);
Denis Vlasenkoab24e182006-11-30 16:41:15 +0000224 return 0;
225}