Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/libbb/update_passwd.c

    r1765 r2725  
    88 *
    99 * Moved from loginutils/passwd.c by Alexander Shishkin <virtuoso@slind.org>
     10 *
     11 * Modified to be able to add or delete users, groups and users to/from groups
     12 * by Tito Ragusa <farmatito@tiscali.it>
     13 *
     14 * Licensed under GPLv2, see file LICENSE in this source tree.
    1015 */
    11 
    1216#include "libbb.h"
    1317
    14 int update_passwd(const char *filename, const char *username,
    15             const char *new_pw)
     18#if ENABLE_SELINUX
     19static void check_selinux_update_passwd(const char *username)
    1620{
     21    security_context_t context;
     22    char *seuser;
     23
     24    if (getuid() != (uid_t)0 || is_selinux_enabled() == 0)
     25        return;  /* No need to check */
     26
     27    if (getprevcon_raw(&context) < 0)
     28        bb_perror_msg_and_die("getprevcon failed");
     29    seuser = strtok(context, ":");
     30    if (!seuser)
     31        bb_error_msg_and_die("invalid context '%s'", context);
     32    if (strcmp(seuser, username) != 0) {
     33        if (checkPasswdAccess(PASSWD__PASSWD) != 0)
     34            bb_error_msg_and_die("SELinux: access denied");
     35    }
     36    if (ENABLE_FEATURE_CLEAN_UP)
     37        freecon(context);
     38}
     39#else
     40# define check_selinux_update_passwd(username) ((void)0)
     41#endif
     42
     43/*
     44 1) add a user: update_passwd(FILE, USER, REMAINING_PWLINE, NULL)
     45    only if CONFIG_ADDUSER=y and applet_name[0] == 'a' like in adduser
     46
     47 2) add a group: update_passwd(FILE, GROUP, REMAINING_GRLINE, NULL)
     48    only if CONFIG_ADDGROUP=y and applet_name[0] == 'a' like in addgroup
     49
     50 3) add a user to a group: update_passwd(FILE, GROUP, NULL, MEMBER)
     51    only if CONFIG_FEATURE_ADDUSER_TO_GROUP=y, applet_name[0] == 'a'
     52    like in addgroup and member != NULL
     53
     54 4) delete a user: update_passwd(FILE, USER, NULL, NULL)
     55
     56 5) delete a group: update_passwd(FILE, GROUP, NULL, NULL)
     57
     58 6) delete a user from a group: update_passwd(FILE, GROUP, NULL, MEMBER)
     59    only if CONFIG_FEATURE_DEL_USER_FROM_GROUP=y and member != NULL
     60
     61 7) change user's password: update_passwd(FILE, USER, NEW_PASSWD, NULL)
     62    only if CONFIG_PASSWD=y and applet_name[0] == 'p' like in passwd
     63    or if CONFIG_CHPASSWD=y and applet_name[0] == 'c' like in chpasswd
     64
     65 This function does not validate the arguments fed to it
     66 so the calling program should take care of that.
     67
     68 Returns number of lines changed, or -1 on error.
     69*/
     70int FAST_FUNC update_passwd(const char *filename,
     71        const char *name,
     72        const char *new_passwd,
     73        const char *member)
     74{
     75#if !(ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP)
     76#define member NULL
     77#endif
    1778    struct stat sb;
    1879    struct flock lock;
     
    2182    char *fnamesfx;
    2283    char *sfx_char;
     84    char *name_colon;
    2385    unsigned user_len;
    2486    int old_fd;
    2587    int new_fd;
    2688    int i;
    27     int cnt = 0;
     89    int changed_lines;
    2890    int ret = -1; /* failure */
     91    /* used as a bool: "are we modifying /etc/shadow?" */
     92#if ENABLE_FEATURE_SHADOWPASSWDS
     93    const char *shadow = strstr(filename, "shadow");
     94#else
     95# define shadow NULL
     96#endif
     97
     98    filename = xmalloc_follow_symlinks(filename);
     99    if (filename == NULL)
     100        return ret;
     101
     102    check_selinux_update_passwd(name);
    29103
    30104    /* New passwd file, "/etc/passwd+" for now */
    31105    fnamesfx = xasprintf("%s+", filename);
    32106    sfx_char = &fnamesfx[strlen(fnamesfx)-1];
    33     username = xasprintf("%s:", username);
    34     user_len = strlen(username);
    35 
    36     old_fp = fopen(filename, "r+");
    37     if (!old_fp)
     107    name_colon = xasprintf("%s:", name);
     108    user_len = strlen(name_colon);
     109
     110    if (shadow)
     111        old_fp = fopen(filename, "r+");
     112    else
     113        old_fp = fopen_or_warn(filename, "r+");
     114    if (!old_fp) {
     115        if (shadow)
     116            ret = 0; /* missing shadow is not an error */
    38117        goto free_mem;
     118    }
    39119    old_fd = fileno(old_fp);
     120
     121    selinux_preserve_fcontext(old_fd);
    40122
    41123    /* Try to create "/etc/passwd+". Wait if it exists. */
     
    48130        usleep(100000); /* 0.1 sec */
    49131    } while (--i);
    50     bb_perror_msg("cannot create '%s'", fnamesfx);
     132    bb_perror_msg("can't create '%s'", fnamesfx);
    51133    goto close_old_fp;
    52134
    53135 created:
    54     if (!fstat(old_fd, &sb)) {
     136    if (fstat(old_fd, &sb) == 0) {
    55137        fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */
    56138        fchown(new_fd, sb.st_uid, sb.st_gid);
    57139    }
    58     new_fp = fdopen(new_fd, "w");
    59     if (!new_fp) {
    60         close(new_fd);
    61         goto unlink_new;
    62     }
     140    errno = 0;
     141    new_fp = xfdopen_for_write(new_fd);
    63142
    64143    /* Backup file is "/etc/passwd-" */
     
    68147    /* Create backup as a hardlink to current */
    69148    if (i || link(filename, fnamesfx))
    70         bb_perror_msg("warning: cannot create backup copy '%s'", fnamesfx);
     149        bb_perror_msg("warning: can't create backup copy '%s'",
     150                fnamesfx);
    71151    *sfx_char = '+';
    72152
     
    77157    lock.l_len = 0;
    78158    if (fcntl(old_fd, F_SETLK, &lock) < 0)
    79         bb_perror_msg("warning: cannot lock '%s'", filename);
     159        bb_perror_msg("warning: can't lock '%s'", filename);
    80160    lock.l_type = F_UNLCK;
    81161
    82162    /* Read current password file, write updated /etc/passwd+ */
     163    changed_lines = 0;
    83164    while (1) {
    84         char *line = xmalloc_fgets(old_fp);
    85         if (!line) break; /* EOF/error */
    86         if (strncmp(username, line, user_len) == 0) {
    87             /* we have a match with "username:"... */
    88             const char *cp = line + user_len;
    89             /* now cp -> old passwd, skip it: */
    90             cp = strchrnul(cp, ':');
    91             /* now cp -> ':' after old passwd or -> "" */
    92             fprintf(new_fp, "%s%s%s", username, new_pw, cp);
    93             cnt++;
     165        char *cp, *line;
     166
     167        line = xmalloc_fgetline(old_fp);
     168        if (!line) /* EOF/error */
     169            break;
     170        if (strncmp(name_colon, line, user_len) != 0) {
     171            fprintf(new_fp, "%s\n", line);
     172            goto next;
     173        }
     174
     175        /* We have a match with "name:"... */
     176        cp = line + user_len; /* move past name: */
     177
     178#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
     179        if (member) {
     180            /* It's actually /etc/group+, not /etc/passwd+ */
     181            if (ENABLE_FEATURE_ADDUSER_TO_GROUP
     182             && applet_name[0] == 'a'
     183            ) {
     184                /* Add user to group */
     185                fprintf(new_fp, "%s%s%s\n", line,
     186                    last_char_is(line, ':') ? "" : ",",
     187                    member);
     188                changed_lines++;
     189            } else if (ENABLE_FEATURE_DEL_USER_FROM_GROUP
     190            /* && applet_name[0] == 'd' */
     191            ) {
     192                /* Delete user from group */
     193                char *tmp;
     194                const char *fmt = "%s";
     195
     196                /* find the start of the member list: last ':' */
     197                cp = strrchr(line, ':');
     198                /* cut it */
     199                *cp++ = '\0';
     200                /* write the cut line name:passwd:gid:
     201                 * or name:!:: */
     202                fprintf(new_fp, "%s:", line);
     203                /* parse the tokens of the member list */
     204                tmp = cp;
     205                while ((cp = strsep(&tmp, ",")) != NULL) {
     206                    if (strcmp(member, cp) != 0) {
     207                        fprintf(new_fp, fmt, cp);
     208                        fmt = ",%s";
     209                    } else {
     210                        /* found member, skip it */
     211                        changed_lines++;
     212                    }
     213                }
     214                fprintf(new_fp, "\n");
     215            }
    94216        } else
    95             fputs(line, new_fp);
     217#endif
     218        if ((ENABLE_PASSWD && applet_name[0] == 'p')
     219         || (ENABLE_CHPASSWD && applet_name[0] == 'c')
     220        ) {
     221            /* Change passwd */
     222            cp = strchrnul(cp, ':'); /* move past old passwd */
     223
     224            if (shadow && *cp == ':') {
     225                /* /etc/shadow's field 3 (passwd change date) needs updating */
     226                /* move past old change date */
     227                cp = strchrnul(cp + 1, ':');
     228                /* "name:" + "new_passwd" + ":" + "change date" + ":rest of line" */
     229                fprintf(new_fp, "%s%s:%u%s\n", name_colon, new_passwd,
     230                    (unsigned)(time(NULL)) / (24*60*60), cp);
     231            } else {
     232                /* "name:" + "new_passwd" + ":rest of line" */
     233                fprintf(new_fp, "%s%s%s\n", name_colon, new_passwd, cp);
     234            }
     235            changed_lines++;
     236        } /* else delete user or group: skip the line */
     237 next:
    96238        free(line);
    97239    }
     240
     241    if (changed_lines == 0) {
     242#if ENABLE_FEATURE_ADDUSER_TO_GROUP || ENABLE_FEATURE_DEL_USER_FROM_GROUP
     243        if (member) {
     244            if (ENABLE_ADDGROUP && applet_name[0] == 'a')
     245                bb_error_msg("can't find %s in %s", name, filename);
     246            if (ENABLE_DELGROUP && applet_name[0] == 'd')
     247                bb_error_msg("can't find %s in %s", member, filename);
     248        }
     249#endif
     250        if ((ENABLE_ADDUSER || ENABLE_ADDGROUP)
     251         && applet_name[0] == 'a' && !member
     252        ) {
     253            /* add user or group */
     254            fprintf(new_fp, "%s%s\n", name_colon, new_passwd);
     255            changed_lines++;
     256        }
     257    }
     258
    98259    fcntl(old_fd, F_SETLK, &lock);
    99260
    100261    /* We do want all of them to execute, thus | instead of || */
     262    errno = 0;
    101263    if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
    102264     || rename(fnamesfx, filename)
    103265    ) {
    104266        /* At least one of those failed */
     267        bb_perror_nomsg();
    105268        goto unlink_new;
    106269    }
    107     ret = cnt; /* whee, success! */
     270    /* Success: ret >= 0 */
     271    ret = changed_lines;
    108272
    109273 unlink_new:
    110     if (ret < 0) unlink(fnamesfx);
     274    if (ret < 0)
     275        unlink(fnamesfx);
    111276
    112277 close_old_fp:
     
    115280 free_mem:
    116281    free(fnamesfx);
    117     free((char*)username);
     282    free((char *)filename);
     283    free(name_colon);
    118284    return ret;
    119285}
Note: See TracChangeset for help on using the changeset viewer.