source: MondoRescue/branches/stable/mindi-busybox/loginutils/passwd.c@ 821

Last change on this file since 821 was 821, checked in by Bruno Cornec, 18 years ago

Addition of busybox 1.2.1 as a mindi-busybox new package
This should avoid delivering binary files in mindi not built there (Fedora and Debian are quite serious about that)

File size: 8.7 KB
RevLine 
[821]1/* vi: set sw=4 ts=4: */
2#include <fcntl.h>
3#include <stdio.h>
4#include <string.h>
5#include <signal.h>
6#include <sys/stat.h>
7#include <sys/types.h>
8#include <unistd.h>
9#include <utime.h>
10#include <syslog.h>
11#include <time.h>
12#include <sys/resource.h>
13#include <errno.h>
14
15#include "busybox.h"
16
17static char crypt_passwd[128];
18
19static int create_backup(const char *backup, FILE * fp);
20static int new_password(const struct passwd *pw, int amroot, int algo);
21static void set_filesize_limit(int blocks);
22
23
24static int get_algo(char *a)
25{
26 int x = 1; /* standard: MD5 */
27
28 if (strcasecmp(a, "des") == 0)
29 x = 0;
30 return x;
31}
32
33
34static int update_passwd(const struct passwd *pw, const char *crypt_pw)
35{
36 char filename[1024];
37 char buf[1025];
38 char buffer[80];
39 char username[32];
40 char *pw_rest;
41 int mask;
42 int continued;
43 FILE *fp;
44 FILE *out_fp;
45 struct stat sb;
46 struct flock lock;
47
48#if ENABLE_FEATURE_SHADOWPASSWDS
49 if (access(bb_path_shadow_file, F_OK) == 0) {
50 snprintf(filename, sizeof filename, "%s", bb_path_shadow_file);
51 } else
52#endif
53 {
54 snprintf(filename, sizeof filename, "%s", bb_path_passwd_file);
55 }
56
57 if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) {
58 /* return 0; */
59 return 1;
60 }
61
62 /* Lock the password file before updating */
63 lock.l_type = F_WRLCK;
64 lock.l_whence = SEEK_SET;
65 lock.l_start = 0;
66 lock.l_len = 0;
67 if (fcntl(fileno(fp), F_SETLK, &lock) < 0) {
68 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
69 return 1;
70 }
71 lock.l_type = F_UNLCK;
72
73 snprintf(buf, sizeof buf, "%s-", filename);
74 if (create_backup(buf, fp)) {
75 fcntl(fileno(fp), F_SETLK, &lock);
76 fclose(fp);
77 return 1;
78 }
79 snprintf(buf, sizeof buf, "%s+", filename);
80 mask = umask(0777);
81 out_fp = fopen(buf, "w");
82 umask(mask);
83 if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777))
84 || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) {
85 fcntl(fileno(fp), F_SETLK, &lock);
86 fclose(fp);
87 fclose(out_fp);
88 return 1;
89 }
90
91 continued = 0;
92 snprintf(username, sizeof username, "%s:", pw->pw_name);
93 rewind(fp);
94 while (!feof(fp)) {
95 fgets(buffer, sizeof buffer, fp);
96 if (!continued) { /* Check to see if we're updating this line. */
97 if (strncmp(username, buffer, strlen(username)) == 0) {
98 /* we have a match. */
99 pw_rest = strchr(buffer, ':');
100 *pw_rest++ = '\0';
101 pw_rest = strchr(pw_rest, ':');
102 fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest);
103 } else {
104 fputs(buffer, out_fp);
105 }
106 } else {
107 fputs(buffer, out_fp);
108 }
109 if (buffer[strlen(buffer) - 1] == '\n') {
110 continued = 0;
111 } else {
112 continued = 1;
113 }
114 memset(buffer, 0, sizeof buffer);
115 }
116
117 if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) {
118 unlink(buf);
119 fcntl(fileno(fp), F_SETLK, &lock);
120 fclose(fp);
121 return 1;
122 }
123 if (rename(buf, filename) < 0) {
124 fcntl(fileno(fp), F_SETLK, &lock);
125 fclose(fp);
126 return 1;
127 } else {
128 fcntl(fileno(fp), F_SETLK, &lock);
129 fclose(fp);
130 return 0;
131 }
132}
133
134
135int passwd_main(int argc, char **argv)
136{
137 int amroot;
138 char *cp;
139 char *np;
140 char *name;
141 char *myname;
142 int flag;
143 int algo = 1; /* -a - password algorithm */
144 int lflg = 0; /* -l - lock account */
145 int uflg = 0; /* -u - unlock account */
146 int dflg = 0; /* -d - delete password */
147 const struct passwd *pw;
148
149#if ENABLE_FEATURE_SHADOWPASSWDS
150 const struct spwd *sp;
151#endif
152 amroot = (getuid() == 0);
153 openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
154 while ((flag = getopt(argc, argv, "a:dlu")) != EOF) {
155 switch (flag) {
156 case 'a':
157 algo = get_algo(optarg);
158 break;
159 case 'd':
160 dflg++;
161 break;
162 case 'l':
163 lflg++;
164 break;
165 case 'u':
166 uflg++;
167 break;
168 default:
169 bb_show_usage();
170 }
171 }
172 myname = (char *) bb_xstrdup(bb_getpwuid(NULL, getuid(), -1));
173 /* exits on error */
174 if (optind < argc) {
175 name = argv[optind];
176 } else {
177 name = myname;
178 }
179 if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) {
180 bb_show_usage();
181 }
182 pw = getpwnam(name);
183 if (!pw) {
184 bb_error_msg_and_die("Unknown user %s\n", name);
185 }
186 if (!amroot && pw->pw_uid != getuid()) {
187 syslog(LOG_WARNING, "can't change pwd for `%s'", name);
188 bb_error_msg_and_die("Permission denied.\n");
189 }
190#if ENABLE_FEATURE_SHADOWPASSWDS
191 sp = getspnam(name);
192 if (!sp) {
193 sp = (struct spwd *) pwd_to_spwd(pw);
194 }
195 cp = sp->sp_pwdp;
196 np = sp->sp_namp;
197#else
198 cp = pw->pw_passwd;
199 np = name;
200#endif
201
202 safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
203 if (!(dflg || lflg || uflg)) {
204 if (!amroot) {
205 if (cp[0] == '!') {
206 syslog(LOG_WARNING, "password locked for `%s'", np);
207 bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np);
208 }
209 }
210 printf("Changing password for %s\n", name);
211 if (new_password(pw, amroot, algo)) {
212 bb_error_msg_and_die( "The password for %s is unchanged.\n", name);
213 }
214 } else if (lflg) {
215 if (crypt_passwd[0] != '!') {
216 memmove(&crypt_passwd[1], crypt_passwd,
217 sizeof crypt_passwd - 1);
218 crypt_passwd[sizeof crypt_passwd - 1] = '\0';
219 crypt_passwd[0] = '!';
220 }
221 } else if (uflg) {
222 if (crypt_passwd[0] == '!') {
223 memmove(crypt_passwd, &crypt_passwd[1],
224 sizeof crypt_passwd - 1);
225 }
226 } else if (dflg) {
227 crypt_passwd[0] = '\0';
228 }
229 set_filesize_limit(30000);
230 signal(SIGHUP, SIG_IGN);
231 signal(SIGINT, SIG_IGN);
232 signal(SIGQUIT, SIG_IGN);
233 umask(077);
234 xsetuid(0);
235 if (!update_passwd(pw, crypt_passwd)) {
236 syslog(LOG_INFO, "password for `%s' changed by user `%s'", name,
237 myname);
238 printf("Password changed.\n");
239 } else {
240 syslog(LOG_WARNING, "an error occurred updating the password file");
241 bb_error_msg_and_die("An error occurred updating the password file.\n");
242 }
243 return (0);
244}
245
246
247
248static int create_backup(const char *backup, FILE * fp)
249{
250 struct stat sb;
251 struct utimbuf ub;
252 FILE *bkfp;
253 int c, mask;
254
255 if (fstat(fileno(fp), &sb))
256 /* return -1; */
257 return 1;
258
259 mask = umask(077);
260 bkfp = fopen(backup, "w");
261 umask(mask);
262 if (!bkfp)
263 /* return -1; */
264 return 1;
265
266 /* TODO: faster copy, not one-char-at-a-time. --marekm */
267 rewind(fp);
268 while ((c = getc(fp)) != EOF) {
269 if (putc(c, bkfp) == EOF)
270 break;
271 }
272 if (c != EOF || fflush(bkfp)) {
273 fclose(bkfp);
274 /* return -1; */
275 return 1;
276 }
277 if (fclose(bkfp))
278 /* return -1; */
279 return 1;
280
281 ub.actime = sb.st_atime;
282 ub.modtime = sb.st_mtime;
283 utime(backup, &ub);
284 return 0;
285}
286
287static int i64c(int i)
288{
289 if (i <= 0)
290 return ('.');
291 if (i == 1)
292 return ('/');
293 if (i >= 2 && i < 12)
294 return ('0' - 2 + i);
295 if (i >= 12 && i < 38)
296 return ('A' - 12 + i);
297 if (i >= 38 && i < 63)
298 return ('a' - 38 + i);
299 return ('z');
300}
301
302static char *crypt_make_salt(void)
303{
304 time_t now;
305 static unsigned long x;
306 static char result[3];
307
308 time(&now);
309 x += now + getpid() + clock();
310 result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
311 result[1] = i64c(((x >> 12) ^ x) & 077);
312 result[2] = '\0';
313 return result;
314}
315
316
317static int new_password(const struct passwd *pw, int amroot, int algo)
318{
319 char *clear;
320 char *cipher;
321 char *cp;
322 char salt[12]; /* "$N$XXXXXXXX" or "XX" */
323 char orig[200];
324 char pass[200];
325
326 if (!amroot && crypt_passwd[0]) {
327 if (!(clear = bb_askpass(0, "Old password:"))) {
328 /* return -1; */
329 return 1;
330 }
331 cipher = pw_encrypt(clear, crypt_passwd);
332 if (strcmp(cipher, crypt_passwd) != 0) {
333 syslog(LOG_WARNING, "incorrect password for `%s'",
334 pw->pw_name);
335 bb_do_delay(FAIL_DELAY);
336 fprintf(stderr, "Incorrect password.\n");
337 /* return -1; */
338 return 1;
339 }
340 safe_strncpy(orig, clear, sizeof(orig));
341 memset(clear, 0, strlen(clear));
342 memset(cipher, 0, strlen(cipher));
343 } else {
344 orig[0] = '\0';
345 }
346 if (! (cp=bb_askpass(0, "Enter the new password (minimum of 5, maximum of 8 characters)\n"
347 "Please use a combination of upper and lower case letters and numbers.\n"
348 "Enter new password: ")))
349 {
350 memset(orig, 0, sizeof orig);
351 /* return -1; */
352 return 1;
353 }
354 safe_strncpy(pass, cp, sizeof(pass));
355 memset(cp, 0, strlen(cp));
356 /* if (!obscure(orig, pass, pw)) { */
357 if (obscure(orig, pass, pw)) {
358 if (amroot) {
359 printf("\nWarning: weak password (continuing).\n");
360 } else {
361 /* return -1; */
362 return 1;
363 }
364 }
365 if (!(cp = bb_askpass(0, "Re-enter new password: "))) {
366 memset(orig, 0, sizeof orig);
367 /* return -1; */
368 return 1;
369 }
370 if (strcmp(cp, pass)) {
371 fprintf(stderr, "Passwords do not match.\n");
372 /* return -1; */
373 return 1;
374 }
375 memset(cp, 0, strlen(cp));
376 memset(orig, 0, sizeof(orig));
377 memset(salt, 0, sizeof(salt));
378
379 if (algo == 1) {
380 strcpy(salt, "$1$");
381 strcat(salt, crypt_make_salt());
382 strcat(salt, crypt_make_salt());
383 strcat(salt, crypt_make_salt());
384 }
385
386 strcat(salt, crypt_make_salt());
387 cp = pw_encrypt(pass, salt);
388
389 memset(pass, 0, sizeof pass);
390 safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
391 return 0;
392}
393
394static void set_filesize_limit(int blocks)
395{
396 struct rlimit rlimit_fsize;
397
398 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks;
399 setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
400}
Note: See TracBrowser for help on using the repository browser.