Ignore:
Timestamp:
Nov 4, 2007, 3:16:40 AM (16 years ago)
Author:
Bruno Cornec
Message:

Update to busybox 1.7.2

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.5/mindi-busybox/loginutils/passwd.c

    r821 r1765  
    11/* 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>
     2/*
     3 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     4 */
     5
     6#include "libbb.h"
    107#include <syslog.h>
    11 #include <time.h>
    12 #include <sys/resource.h>
    13 #include <errno.h>
    14 
    15 #include "busybox.h"
    16 
    17 static char crypt_passwd[128];
    18 
    19 static int create_backup(const char *backup, FILE * fp);
    20 static int new_password(const struct passwd *pw, int amroot, int algo);
    21 static void set_filesize_limit(int blocks);
    22 
    23 
    24 static int get_algo(char *a)
     8
     9
     10static void nuke_str(char *str)
    2511{
    26     int x = 1;                  /* standard: MD5 */
    27 
    28     if (strcasecmp(a, "des") == 0)
    29         x = 0;
    30     return x;
     12    if (str) memset(str, 0, strlen(str));
    3113}
    3214
    33 
    34 static int update_passwd(const struct passwd *pw, const char *crypt_pw)
     15static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
    3516{
    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);
     17    char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */
     18    char *orig = (char*)"";
     19    char *newp = NULL;
     20    char *cipher = NULL;
     21    char *cp = NULL;
     22    char *ret = NULL; /* failure so far */
     23
     24    if (myuid && pw->pw_passwd[0]) {
     25        orig = bb_askpass(0, "Old password:"); /* returns ptr to static */
     26        if (!orig)
     27            goto err_ret;
     28        cipher = pw_encrypt(orig, pw->pw_passwd); /* returns ptr to static */
     29        if (strcmp(cipher, pw->pw_passwd) != 0) {
     30            syslog(LOG_WARNING, "incorrect password for '%s'",
     31                pw->pw_name);
     32            bb_do_delay(FAIL_DELAY);
     33            puts("Incorrect password");
     34            goto err_ret;
    10835        }
    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     }
     36    }
     37    orig = xstrdup(orig); /* or else bb_askpass() will destroy it */
     38    newp = bb_askpass(0, "New password:"); /* returns ptr to static */
     39    if (!newp)
     40        goto err_ret;
     41    newp = xstrdup(newp); /* we are going to bb_askpass() again, so save it */
     42    if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
     43     && obscure(orig, newp, pw) && myuid)
     44        goto err_ret; /* non-root is not allowed to have weak passwd */
     45
     46    cp = bb_askpass(0, "Retype password:");
     47    if (!cp)
     48        goto err_ret;
     49    if (strcmp(cp, newp)) {
     50        puts("Passwords don't match");
     51        goto err_ret;
     52    }
     53
     54    crypt_make_salt(salt, 1, 0); /* des */
     55    if (algo) { /* MD5 */
     56        strcpy(salt, "$1$");
     57        crypt_make_salt(salt + 3, 4, 0);
     58    }
     59    /* pw_encrypt returns ptr to static */
     60    ret = xstrdup(pw_encrypt(newp, salt));
     61    /* whee, success! */
     62
     63 err_ret:
     64    nuke_str(orig);
     65    if (ENABLE_FEATURE_CLEAN_UP) free(orig);
     66    nuke_str(newp);
     67    if (ENABLE_FEATURE_CLEAN_UP) free(newp);
     68    nuke_str(cipher);
     69    nuke_str(cp);
     70    return ret;
    13271}
    13372
    134 
     73int passwd_main(int argc, char **argv);
    13574int passwd_main(int argc, char **argv)
    13675{
    137     int amroot;
    138     char *cp;
    139     char *np;
     76    enum {
     77        OPT_algo = 0x1, /* -a - password algorithm */
     78        OPT_lock = 0x2, /* -l - lock account */
     79        OPT_unlock = 0x4, /* -u - unlock account */
     80        OPT_delete = 0x8, /* -d - delete password */
     81        OPT_lud = 0xe,
     82        STATE_ALGO_md5 = 0x10,
     83        //STATE_ALGO_des = 0x20, not needed yet
     84    };
     85    unsigned opt;
     86    int rc;
     87    const char *opt_a = "";
     88    const char *filename;
     89    char *myname;
    14090    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;
     91    char *newp;
     92    struct passwd *pw;
     93    uid_t myuid;
     94    struct rlimit rlimit_fsize;
     95    char c;
    14896
    14997#if ENABLE_FEATURE_SHADOWPASSWDS
    150     const struct spwd *sp;
     98    /* Using _r function to avoid pulling in static buffers */
     99    struct spwd spw;
     100    struct spwd *result;
     101    char buffer[256];
    151102#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];
     103
     104    logmode = LOGMODE_BOTH;
     105    openlog(applet_name, LOG_NOWAIT, LOG_AUTH);
     106    opt = getopt32(argv, "a:lud", &opt_a);
     107    //argc -= optind;
     108    argv += optind;
     109
     110    if (strcasecmp(opt_a, "des") != 0) /* -a */
     111        opt |= STATE_ALGO_md5;
     112    //else
     113    //  opt |= STATE_ALGO_des;
     114    myuid = getuid();
     115    /* -l, -u, -d require root priv and username argument */
     116    if ((opt & OPT_lud) && (myuid || !argv[0]))
     117        bb_show_usage();
     118
     119    /* Will complain and die if username not found */
     120    myname = xstrdup(bb_getpwuid(NULL, -1, myuid));
     121    name = argv[0] ? argv[0] : myname;
     122
     123    pw = getpwnam(name);
     124    if (!pw) bb_error_msg_and_die("unknown user %s", name);
     125    if (myuid && pw->pw_uid != myuid) {
     126        /* LOGMODE_BOTH */
     127        bb_error_msg_and_die("%s can't change password for %s", myname, name);
     128    }
     129
     130#if ENABLE_FEATURE_SHADOWPASSWDS
     131    /* getspnam_r() can lie! Even if user isn't in shadow, it can
     132     * return success (pwd field was seen set to "!" in this case) */
     133    if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result)
     134     || LONE_CHAR(spw.sp_pwdp, '!')) {
     135        /* LOGMODE_BOTH */
     136        bb_error_msg("no record of %s in %s, using %s",
     137                name, bb_path_shadow_file,
     138                bb_path_passwd_file);
    176139    } 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;
     140        pw->pw_passwd = spw.sp_pwdp;
     141    }
    200142#endif
    201143
    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             }
     144    /* Decide what the new password will be */
     145    newp = NULL;
     146    c = pw->pw_passwd[0] - '!';
     147    if (!(opt & OPT_lud)) {
     148        if (myuid && !c) { /* passwd starts with '!' */
     149            /* LOGMODE_BOTH */
     150            bb_error_msg_and_die("cannot change "
     151                    "locked password for %s", name);
    209152        }
    210153        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);
     154        newp = new_password(pw, myuid, opt & STATE_ALGO_md5);
     155        if (!newp) {
     156            logmode = LOGMODE_STDIO;
     157            bb_error_msg_and_die("password for %s is unchanged", name);
    213158        }
    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);
     159    } else if (opt & OPT_lock) {
     160        if (!c) goto skip; /* passwd starts with '!' */
     161        newp = xasprintf("!%s", pw->pw_passwd);
     162    } else if (opt & OPT_unlock) {
     163        if (c) goto skip; /* not '!' */
     164        /* pw->pw_passwd pints to static storage,
     165         * strdup'ing to avoid nasty surprizes */
     166        newp = xstrdup(&pw->pw_passwd[1]);
     167    } else if (opt & OPT_delete) {
     168        //newp = xstrdup("");
     169        newp = (char*)"";
     170    }
     171
     172    rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
     173    setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
    230174    signal(SIGHUP, SIG_IGN);
    231175    signal(SIGINT, SIG_IGN);
     
    233177    umask(077);
    234178    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 
    248 static 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);
     179
     180#if ENABLE_FEATURE_SHADOWPASSWDS
     181    filename = bb_path_shadow_file;
     182    rc = update_passwd(bb_path_shadow_file, name, newp);
     183    if (rc == 0) /* no lines updated, no errors detected */
     184#endif
     185    {
     186        filename = bb_path_passwd_file;
     187        rc = update_passwd(bb_path_passwd_file, name, newp);
     188    }
     189    /* LOGMODE_BOTH */
     190    if (rc < 0)
     191        bb_error_msg_and_die("cannot update password file %s",
     192                filename);
     193    bb_info_msg("Password for %s changed by %s", name, myname);
     194
     195    //if (ENABLE_FEATURE_CLEAN_UP) free(newp);
     196 skip:
     197    if (!newp) {
     198        bb_error_msg_and_die("password for %s is already %slocked",
     199            name, (opt & OPT_unlock) ? "un" : "");
     200    }
     201    if (ENABLE_FEATURE_CLEAN_UP) free(myname);
    284202    return 0;
    285203}
    286 
    287 static 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 
    302 static 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 
    317 static 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 
    394 static 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 TracChangeset for help on using the changeset viewer.