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

Last change on this file since 821 was 821, checked in by Bruno Cornec, 14 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
Line 
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.