blob: aa75dd260fa73cc6db133391592de84051216f7e [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/*
3 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
4 */
5
Eric Andersen27f64e12002-06-23 04:24:25 +00006#include "busybox.h"
Rob Landleyd921b2e2006-08-03 15:41:12 +00007#include <syslog.h>
Eric Andersen27f64e12002-06-23 04:24:25 +00008
9static char crypt_passwd[128];
10
11static int create_backup(const char *backup, FILE * fp);
12static int new_password(const struct passwd *pw, int amroot, int algo);
13static void set_filesize_limit(int blocks);
14
15
Eric Andersen14f5c8d2005-04-16 19:39:00 +000016static int get_algo(char *a)
Eric Andersen27f64e12002-06-23 04:24:25 +000017{
Glenn L McGrath3aeaee32003-02-08 23:20:02 +000018 int x = 1; /* standard: MD5 */
Eric Andersen27f64e12002-06-23 04:24:25 +000019
Glenn L McGrath3aeaee32003-02-08 23:20:02 +000020 if (strcasecmp(a, "des") == 0)
21 x = 0;
Eric Andersen27f64e12002-06-23 04:24:25 +000022 return x;
23}
24
25
Mike Frysingere87ae0b2006-01-08 11:15:53 +000026static int update_passwd(const struct passwd *pw, const char *crypt_pw)
Eric Andersen27f64e12002-06-23 04:24:25 +000027{
28 char filename[1024];
29 char buf[1025];
30 char buffer[80];
31 char username[32];
32 char *pw_rest;
Eric Andersen27f64e12002-06-23 04:24:25 +000033 int mask;
34 int continued;
35 FILE *fp;
36 FILE *out_fp;
37 struct stat sb;
38 struct flock lock;
39
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +000040#if ENABLE_FEATURE_SHADOWPASSWDS
Manuel Novoa III cad53642003-03-19 09:13:01 +000041 if (access(bb_path_shadow_file, F_OK) == 0) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000042 snprintf(filename, sizeof filename, "%s", bb_path_shadow_file);
Glenn L McGrath995d96a2004-09-15 02:39:09 +000043 } else
44#endif
45 {
Manuel Novoa III cad53642003-03-19 09:13:01 +000046 snprintf(filename, sizeof filename, "%s", bb_path_passwd_file);
Eric Andersen27f64e12002-06-23 04:24:25 +000047 }
48
49 if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) {
50 /* return 0; */
51 return 1;
52 }
53
54 /* Lock the password file before updating */
55 lock.l_type = F_WRLCK;
56 lock.l_whence = SEEK_SET;
57 lock.l_start = 0;
58 lock.l_len = 0;
59 if (fcntl(fileno(fp), F_SETLK, &lock) < 0) {
60 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
61 return 1;
62 }
63 lock.l_type = F_UNLCK;
64
65 snprintf(buf, sizeof buf, "%s-", filename);
66 if (create_backup(buf, fp)) {
67 fcntl(fileno(fp), F_SETLK, &lock);
68 fclose(fp);
69 return 1;
70 }
71 snprintf(buf, sizeof buf, "%s+", filename);
72 mask = umask(0777);
73 out_fp = fopen(buf, "w");
74 umask(mask);
75 if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777))
76 || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) {
77 fcntl(fileno(fp), F_SETLK, &lock);
78 fclose(fp);
79 fclose(out_fp);
80 return 1;
81 }
82
83 continued = 0;
84 snprintf(username, sizeof username, "%s:", pw->pw_name);
85 rewind(fp);
86 while (!feof(fp)) {
87 fgets(buffer, sizeof buffer, fp);
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +000088 if (!continued) { /* Check to see if we're updating this line. */
89 if (strncmp(username, buffer, strlen(username)) == 0) {
90 /* we have a match. */
Eric Andersen27f64e12002-06-23 04:24:25 +000091 pw_rest = strchr(buffer, ':');
92 *pw_rest++ = '\0';
93 pw_rest = strchr(pw_rest, ':');
94 fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest);
95 } else {
96 fputs(buffer, out_fp);
97 }
98 } else {
99 fputs(buffer, out_fp);
100 }
101 if (buffer[strlen(buffer) - 1] == '\n') {
102 continued = 0;
103 } else {
104 continued = 1;
105 }
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +0000106 memset(buffer, 0, sizeof buffer);
Eric Andersen27f64e12002-06-23 04:24:25 +0000107 }
108
109 if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) {
110 unlink(buf);
111 fcntl(fileno(fp), F_SETLK, &lock);
112 fclose(fp);
113 return 1;
114 }
115 if (rename(buf, filename) < 0) {
116 fcntl(fileno(fp), F_SETLK, &lock);
117 fclose(fp);
118 return 1;
119 } else {
120 fcntl(fileno(fp), F_SETLK, &lock);
121 fclose(fp);
122 return 0;
123 }
124}
125
126
Rob Landleydfba7412006-03-06 20:47:33 +0000127int passwd_main(int argc, char **argv)
Eric Andersen27f64e12002-06-23 04:24:25 +0000128{
129 int amroot;
130 char *cp;
131 char *np;
132 char *name;
133 char *myname;
134 int flag;
Glenn L McGrathb77158a2003-09-04 08:21:36 +0000135 int algo = 1; /* -a - password algorithm */
Eric Andersen27f64e12002-06-23 04:24:25 +0000136 int lflg = 0; /* -l - lock account */
137 int uflg = 0; /* -u - unlock account */
138 int dflg = 0; /* -d - delete password */
139 const struct passwd *pw;
Eric Andersen27f64e12002-06-23 04:24:25 +0000140
Eric Andersen27f64e12002-06-23 04:24:25 +0000141 amroot = (getuid() == 0);
142 openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
143 while ((flag = getopt(argc, argv, "a:dlu")) != EOF) {
144 switch (flag) {
145 case 'a':
146 algo = get_algo(optarg);
147 break;
148 case 'd':
149 dflg++;
150 break;
151 case 'l':
152 lflg++;
153 break;
154 case 'u':
155 uflg++;
156 break;
157 default:
Manuel Novoa III cad53642003-03-19 09:13:01 +0000158 bb_show_usage();
Eric Andersen27f64e12002-06-23 04:24:25 +0000159 }
160 }
Rob Landleyd921b2e2006-08-03 15:41:12 +0000161 myname = (char *) xstrdup(bb_getpwuid(NULL, getuid(), -1));
Eric Andersen7eb79ff2004-09-02 22:21:41 +0000162 /* exits on error */
Eric Andersen27f64e12002-06-23 04:24:25 +0000163 if (optind < argc) {
164 name = argv[optind];
165 } else {
166 name = myname;
167 }
168 if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000169 bb_show_usage();
Eric Andersen27f64e12002-06-23 04:24:25 +0000170 }
171 pw = getpwnam(name);
172 if (!pw) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000173 bb_error_msg_and_die("Unknown user %s\n", name);
Eric Andersen27f64e12002-06-23 04:24:25 +0000174 }
175 if (!amroot && pw->pw_uid != getuid()) {
176 syslog(LOG_WARNING, "can't change pwd for `%s'", name);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000177 bb_error_msg_and_die("Permission denied.\n");
Eric Andersen27f64e12002-06-23 04:24:25 +0000178 }
Rob Landleyab7d9be2006-07-11 16:19:17 +0000179 if (ENABLE_FEATURE_SHADOWPASSWDS) {
180 struct spwd *sp = getspnam(name);
181 if (!sp) bb_error_msg_and_die("Unknown user %s", name);
182 cp = sp->sp_pwdp;
183 } else cp = pw->pw_passwd;
Eric Andersen27f64e12002-06-23 04:24:25 +0000184
Rob Landleyab7d9be2006-07-11 16:19:17 +0000185 np = name;
Eric Andersen27f64e12002-06-23 04:24:25 +0000186 safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
187 if (!(dflg || lflg || uflg)) {
188 if (!amroot) {
189 if (cp[0] == '!') {
190 syslog(LOG_WARNING, "password locked for `%s'", np);
Manuel Novoa III cad53642003-03-19 09:13:01 +0000191 bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np);
Eric Andersen27f64e12002-06-23 04:24:25 +0000192 }
193 }
194 printf("Changing password for %s\n", name);
195 if (new_password(pw, amroot, algo)) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000196 bb_error_msg_and_die( "The password for %s is unchanged.\n", name);
Eric Andersen27f64e12002-06-23 04:24:25 +0000197 }
198 } else if (lflg) {
199 if (crypt_passwd[0] != '!') {
200 memmove(&crypt_passwd[1], crypt_passwd,
201 sizeof crypt_passwd - 1);
202 crypt_passwd[sizeof crypt_passwd - 1] = '\0';
203 crypt_passwd[0] = '!';
204 }
205 } else if (uflg) {
206 if (crypt_passwd[0] == '!') {
207 memmove(crypt_passwd, &crypt_passwd[1],
208 sizeof crypt_passwd - 1);
209 }
210 } else if (dflg) {
211 crypt_passwd[0] = '\0';
212 }
213 set_filesize_limit(30000);
214 signal(SIGHUP, SIG_IGN);
215 signal(SIGINT, SIG_IGN);
216 signal(SIGQUIT, SIG_IGN);
217 umask(077);
Rob Landleyafb94ec2006-07-16 08:06:34 +0000218 xsetuid(0);
Eric Andersen27f64e12002-06-23 04:24:25 +0000219 if (!update_passwd(pw, crypt_passwd)) {
220 syslog(LOG_INFO, "password for `%s' changed by user `%s'", name,
221 myname);
222 printf("Password changed.\n");
223 } else {
Eric Andersen549df2e2002-07-03 04:54:52 +0000224 syslog(LOG_WARNING, "an error occurred updating the password file");
Manuel Novoa III cad53642003-03-19 09:13:01 +0000225 bb_error_msg_and_die("An error occurred updating the password file.\n");
Eric Andersen27f64e12002-06-23 04:24:25 +0000226 }
Rob Landleyab7d9be2006-07-11 16:19:17 +0000227 if (ENABLE_FEATURE_CLEAN_UP) free(myname);
Eric Andersen27f64e12002-06-23 04:24:25 +0000228 return (0);
229}
230
231
232
233static int create_backup(const char *backup, FILE * fp)
234{
235 struct stat sb;
236 struct utimbuf ub;
237 FILE *bkfp;
238 int c, mask;
239
240 if (fstat(fileno(fp), &sb))
241 /* return -1; */
242 return 1;
243
244 mask = umask(077);
245 bkfp = fopen(backup, "w");
246 umask(mask);
247 if (!bkfp)
248 /* return -1; */
249 return 1;
250
251 /* TODO: faster copy, not one-char-at-a-time. --marekm */
252 rewind(fp);
253 while ((c = getc(fp)) != EOF) {
254 if (putc(c, bkfp) == EOF)
255 break;
256 }
257 if (c != EOF || fflush(bkfp)) {
258 fclose(bkfp);
259 /* return -1; */
260 return 1;
261 }
262 if (fclose(bkfp))
263 /* return -1; */
264 return 1;
265
266 ub.actime = sb.st_atime;
267 ub.modtime = sb.st_mtime;
268 utime(backup, &ub);
269 return 0;
270}
271
272static int i64c(int i)
273{
274 if (i <= 0)
275 return ('.');
276 if (i == 1)
277 return ('/');
278 if (i >= 2 && i < 12)
279 return ('0' - 2 + i);
280 if (i >= 12 && i < 38)
281 return ('A' - 12 + i);
282 if (i >= 38 && i < 63)
283 return ('a' - 38 + i);
284 return ('z');
285}
286
287static char *crypt_make_salt(void)
288{
289 time_t now;
290 static unsigned long x;
291 static char result[3];
292
293 time(&now);
294 x += now + getpid() + clock();
295 result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
296 result[1] = i64c(((x >> 12) ^ x) & 077);
297 result[2] = '\0';
298 return result;
299}
300
301
302static int new_password(const struct passwd *pw, int amroot, int algo)
303{
304 char *clear;
305 char *cipher;
306 char *cp;
Ned Ludd79197642006-04-21 00:40:35 +0000307 char salt[12]; /* "$N$XXXXXXXX" or "XX" */
Eric Andersen27f64e12002-06-23 04:24:25 +0000308 char orig[200];
309 char pass[200];
Eric Andersen27f64e12002-06-23 04:24:25 +0000310
311 if (!amroot && crypt_passwd[0]) {
Eric Andersen6f9a7782004-05-01 01:27:30 +0000312 if (!(clear = bb_askpass(0, "Old password:"))) {
Eric Andersen27f64e12002-06-23 04:24:25 +0000313 /* return -1; */
314 return 1;
315 }
316 cipher = pw_encrypt(clear, crypt_passwd);
317 if (strcmp(cipher, crypt_passwd) != 0) {
318 syslog(LOG_WARNING, "incorrect password for `%s'",
319 pw->pw_name);
Rob Landley84cb7672006-01-06 20:59:09 +0000320 bb_do_delay(FAIL_DELAY);
Eric Andersen27f64e12002-06-23 04:24:25 +0000321 fprintf(stderr, "Incorrect password.\n");
322 /* return -1; */
323 return 1;
324 }
325 safe_strncpy(orig, clear, sizeof(orig));
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +0000326 memset(clear, 0, strlen(clear));
327 memset(cipher, 0, strlen(cipher));
Eric Andersen27f64e12002-06-23 04:24:25 +0000328 } else {
329 orig[0] = '\0';
330 }
Eric Andersen6f9a7782004-05-01 01:27:30 +0000331 if (! (cp=bb_askpass(0, "Enter the new password (minimum of 5, maximum of 8 characters)\n"
Eric Andersen549df2e2002-07-03 04:54:52 +0000332 "Please use a combination of upper and lower case letters and numbers.\n"
333 "Enter new password: ")))
Eric Andersen27f64e12002-06-23 04:24:25 +0000334 {
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +0000335 memset(orig, 0, sizeof orig);
Eric Andersen27f64e12002-06-23 04:24:25 +0000336 /* return -1; */
337 return 1;
338 }
339 safe_strncpy(pass, cp, sizeof(pass));
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +0000340 memset(cp, 0, strlen(cp));
Eric Andersen27f64e12002-06-23 04:24:25 +0000341 /* if (!obscure(orig, pass, pw)) { */
342 if (obscure(orig, pass, pw)) {
343 if (amroot) {
344 printf("\nWarning: weak password (continuing).\n");
345 } else {
346 /* return -1; */
347 return 1;
348 }
349 }
Eric Andersen6f9a7782004-05-01 01:27:30 +0000350 if (!(cp = bb_askpass(0, "Re-enter new password: "))) {
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +0000351 memset(orig, 0, sizeof orig);
Eric Andersen27f64e12002-06-23 04:24:25 +0000352 /* return -1; */
353 return 1;
354 }
355 if (strcmp(cp, pass)) {
356 fprintf(stderr, "Passwords do not match.\n");
357 /* return -1; */
358 return 1;
359 }
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +0000360 memset(cp, 0, strlen(cp));
361 memset(orig, 0, sizeof(orig));
Ned Ludd79197642006-04-21 00:40:35 +0000362 memset(salt, 0, sizeof(salt));
Eric Andersen27f64e12002-06-23 04:24:25 +0000363
364 if (algo == 1) {
Ned Ludd79197642006-04-21 00:40:35 +0000365 strcpy(salt, "$1$");
366 strcat(salt, crypt_make_salt());
367 strcat(salt, crypt_make_salt());
368 strcat(salt, crypt_make_salt());
369 }
370
371 strcat(salt, crypt_make_salt());
372 cp = pw_encrypt(pass, salt);
373
Bernhard Reutner-Fischer30385572006-01-31 17:57:48 +0000374 memset(pass, 0, sizeof pass);
Eric Andersen27f64e12002-06-23 04:24:25 +0000375 safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
376 return 0;
377}
378
379static void set_filesize_limit(int blocks)
380{
381 struct rlimit rlimit_fsize;
382
383 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks;
384 setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
385}