Annotation of embedaddon/rsync/uidlist.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:  * Handle the mapping of uid/gid and user/group names between systems.
                      3:  *
                      4:  * Copyright (C) 1996 Andrew Tridgell
                      5:  * Copyright (C) 1996 Paul Mackerras
1.1.1.2 ! misho       6:  * Copyright (C) 2004-2013 Wayne Davison
1.1       misho       7:  *
                      8:  * This program is free software; you can redistribute it and/or modify
                      9:  * it under the terms of the GNU General Public License as published by
                     10:  * the Free Software Foundation; either version 3 of the License, or
                     11:  * (at your option) any later version.
                     12:  *
                     13:  * This program is distributed in the hope that it will be useful,
                     14:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
                     15:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     16:  * GNU General Public License for more details.
                     17:  *
                     18:  * You should have received a copy of the GNU General Public License along
                     19:  * with this program; if not, visit the http://fsf.org website.
                     20:  */
                     21: 
                     22: /* If the source username/group does not exist on the target then use
                     23:  * the numeric IDs.  Never do any mapping for uid=0 or gid=0 as these
                     24:  * are special. */
                     25: 
                     26: #include "rsync.h"
1.1.1.2 ! misho      27: #include "ifuncs.h"
        !            28: #include "itypes.h"
1.1       misho      29: #include "io.h"
                     30: 
                     31: extern int am_root;
                     32: extern int preserve_uid;
                     33: extern int preserve_gid;
                     34: extern int preserve_acls;
                     35: extern int numeric_ids;
1.1.1.2 ! misho      36: extern gid_t our_gid;
        !            37: extern char *usermap;
        !            38: extern char *groupmap;
1.1       misho      39: 
                     40: #ifdef HAVE_GETGROUPS
                     41: # ifndef GETGROUPS_T
                     42: #  define GETGROUPS_T gid_t
                     43: # endif
                     44: #endif
                     45: 
1.1.1.2 ! misho      46: #define NFLAGS_WILD_NAME_MATCH (1<<0)
        !            47: #define NFLAGS_NAME_MATCH (1<<1)
        !            48: 
        !            49: union name_or_id {
        !            50:     const char *name;
        !            51:     id_t max_id;
        !            52: };
        !            53: 
1.1       misho      54: struct idlist {
                     55:        struct idlist *next;
1.1.1.2 ! misho      56:        union name_or_id u;
1.1       misho      57:        id_t id, id2;
                     58:        uint16 flags;
                     59: };
                     60: 
1.1.1.2 ! misho      61: static struct idlist *uidlist, *uidmap;
        !            62: static struct idlist *gidlist, *gidmap;
        !            63: 
        !            64: static id_t id_parse(const char *num_str)
        !            65: {
        !            66:        id_t tmp, num = 0;
        !            67:        const char *cp = num_str;
        !            68: 
        !            69:        while (*cp) {
        !            70:                if (!isDigit(cp)) {
        !            71:                  invalid_num:
        !            72:                        rprintf(FERROR, "Invalid ID number: %s\n", num_str);
        !            73:                        exit_cleanup(RERR_SYNTAX);
        !            74:                }
        !            75:                tmp = num * 10 + *cp++ - '0';
        !            76:                if (tmp < num)
        !            77:                        goto invalid_num;
        !            78:                num = tmp;
        !            79:        }
        !            80: 
        !            81:        return num;
        !            82: }
1.1       misho      83: 
1.1.1.2 ! misho      84: static struct idlist *add_to_list(struct idlist **root, id_t id, union name_or_id noiu,
1.1       misho      85:                                  id_t id2, uint16 flags)
                     86: {
                     87:        struct idlist *node = new(struct idlist);
                     88:        if (!node)
                     89:                out_of_memory("add_to_list");
                     90:        node->next = *root;
1.1.1.2 ! misho      91:        node->u = noiu;
1.1       misho      92:        node->id = id;
                     93:        node->id2 = id2;
                     94:        node->flags = flags;
                     95:        *root = node;
                     96:        return node;
                     97: }
                     98: 
                     99: /* turn a uid into a user name */
1.1.1.2 ! misho     100: char *uid_to_user(uid_t uid)
1.1       misho     101: {
                    102:        struct passwd *pass = getpwuid(uid);
                    103:        if (pass)
                    104:                return strdup(pass->pw_name);
                    105:        return NULL;
                    106: }
                    107: 
                    108: /* turn a gid into a group name */
1.1.1.2 ! misho     109: char *gid_to_group(gid_t gid)
1.1       misho     110: {
                    111:        struct group *grp = getgrgid(gid);
                    112:        if (grp)
                    113:                return strdup(grp->gr_name);
                    114:        return NULL;
                    115: }
                    116: 
1.1.1.2 ! misho     117: /* Parse a user name or (optionally) a number into a uid */
        !           118: int user_to_uid(const char *name, uid_t *uid_p, BOOL num_ok)
1.1       misho     119: {
1.1.1.2 ! misho     120:        struct passwd *pass;
        !           121:        if (!name || !*name)
        !           122:                return 0;
        !           123:        if (num_ok && name[strspn(name, "0123456789")] == '\0') {
        !           124:                *uid_p = id_parse(name);
        !           125:                return 1;
        !           126:        }
        !           127:        if (!(pass = getpwnam(name)))
        !           128:                return 0;
        !           129:        *uid_p = pass->pw_uid;
        !           130:        return 1;
1.1       misho     131: }
                    132: 
1.1.1.2 ! misho     133: /* Parse a group name or (optionally) a number into a gid */
        !           134: int group_to_gid(const char *name, gid_t *gid_p, BOOL num_ok)
1.1       misho     135: {
1.1.1.2 ! misho     136:        struct group *grp;
        !           137:        if (!name || !*name)
        !           138:                return 0;
        !           139:        if (num_ok && name[strspn(name, "0123456789")] == '\0') {
        !           140:                *gid_p = id_parse(name);
        !           141:                return 1;
        !           142:        }
        !           143:        if (!(grp = getgrnam(name)))
        !           144:                return 0;
        !           145:        *gid_p = grp->gr_gid;
        !           146:        return 1;
1.1       misho     147: }
                    148: 
                    149: static int is_in_group(gid_t gid)
                    150: {
                    151: #ifdef HAVE_GETGROUPS
                    152:        static gid_t last_in;
                    153:        static int ngroups = -2, last_out = -1;
                    154:        static GETGROUPS_T *gidset;
                    155:        int n;
                    156: 
                    157:        if (gid == last_in && last_out >= 0)
                    158:                return last_out;
                    159:        if (ngroups < -1) {
                    160:                if ((ngroups = getgroups(0, NULL)) < 0)
                    161:                        ngroups = 0;
                    162:                gidset = new_array(GETGROUPS_T, ngroups+1);
                    163:                if (!gidset)
                    164:                        out_of_memory("is_in_group");
                    165:                if (ngroups > 0)
                    166:                        ngroups = getgroups(ngroups, gidset);
                    167:                /* The default gid might not be in the list on some systems. */
                    168:                for (n = 0; n < ngroups; n++) {
1.1.1.2 ! misho     169:                        if (gidset[n] == our_gid)
1.1       misho     170:                                break;
                    171:                }
                    172:                if (n == ngroups)
1.1.1.2 ! misho     173:                        gidset[ngroups++] = our_gid;
        !           174:                if (DEBUG_GTE(OWN, 2)) {
1.1       misho     175:                        int pos;
                    176:                        char *gidbuf = new_array(char, ngroups*21+32);
                    177:                        if (!gidbuf)
                    178:                                out_of_memory("is_in_group");
                    179:                        pos = snprintf(gidbuf, 32, "process has %d gid%s: ",
                    180:                                       ngroups, ngroups == 1? "" : "s");
                    181:                        for (n = 0; n < ngroups; n++) {
                    182:                                pos += snprintf(gidbuf+pos, 21, " %d", (int)gidset[n]);
                    183:                        }
                    184:                        rprintf(FINFO, "%s\n", gidbuf);
                    185:                        free(gidbuf);
                    186:                }
                    187:        }
                    188: 
                    189:        last_in = gid;
                    190:        for (n = 0; n < ngroups; n++) {
                    191:                if (gidset[n] == gid)
                    192:                        return last_out = 1;
                    193:        }
                    194:        return last_out = 0;
                    195: 
                    196: #else
1.1.1.2 ! misho     197:        return gid == our_gid;
1.1       misho     198: #endif
                    199: }
                    200: 
1.1.1.2 ! misho     201: /* Add a uid/gid to its list of ids.  Only called on receiving side. */
        !           202: static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idmap,
        !           203:                                  id_t id, const char *name)
1.1       misho     204: {
                    205:        struct idlist *node;
1.1.1.2 ! misho     206:        union name_or_id noiu;
        !           207:        int flag;
        !           208:        id_t id2;
1.1       misho     209: 
1.1.1.2 ! misho     210:        noiu.name = name; /* ensure that add_to_list() gets the raw value. */
        !           211:        if (!name)
        !           212:                name = "";
1.1       misho     213: 
1.1.1.2 ! misho     214:        for (node = idmap; node; node = node->next) {
        !           215:                if (node->flags & NFLAGS_WILD_NAME_MATCH) {
        !           216:                        if (!wildmatch(node->u.name, name))
        !           217:                                continue;
        !           218:                } else if (node->flags & NFLAGS_NAME_MATCH) {
        !           219:                        if (strcmp(node->u.name, name) != 0)
        !           220:                                continue;
        !           221:                } else if (node->u.max_id) {
        !           222:                        if (id < node->id || id > node->u.max_id)
        !           223:                                continue;
        !           224:                } else {
        !           225:                        if (node->id != id)
        !           226:                                continue;
        !           227:                }
        !           228:                break;
1.1       misho     229:        }
1.1.1.2 ! misho     230:        if (node)
        !           231:                id2 = node->id2;
        !           232:        else if (*name && id) {
        !           233:                if (idlist_ptr == &uidlist) {
        !           234:                        uid_t uid;
        !           235:                        id2 = user_to_uid(name, &uid, False) ? uid : id;
        !           236:                } else {
        !           237:                        gid_t gid;
        !           238:                        id2 = group_to_gid(name, &gid, False) ? gid : id;
        !           239:                }
        !           240:        } else
        !           241:                id2 = id;
1.1       misho     242: 
1.1.1.2 ! misho     243:        flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0;
        !           244:        node = add_to_list(idlist_ptr, id, noiu, id2, flag);
1.1       misho     245: 
1.1.1.2 ! misho     246:        if (DEBUG_GTE(OWN, 2)) {
        !           247:                rprintf(FINFO, "%sid %u(%s) maps to %u\n",
        !           248:                        idlist_ptr == &uidlist ? "u" : "g",
        !           249:                        (unsigned)id, name, (unsigned)id2);
1.1       misho     250:        }
                    251: 
                    252:        return node;
                    253: }
                    254: 
                    255: /* this function is a definate candidate for a faster algorithm */
                    256: uid_t match_uid(uid_t uid)
                    257: {
1.1.1.2 ! misho     258:        static struct idlist *last = NULL;
1.1       misho     259:        struct idlist *list;
                    260: 
1.1.1.2 ! misho     261:        if (last && uid == last->id)
        !           262:                return last->id2;
1.1       misho     263: 
                    264:        for (list = uidlist; list; list = list->next) {
                    265:                if (list->id == uid)
1.1.1.2 ! misho     266:                        break;
1.1       misho     267:        }
                    268: 
1.1.1.2 ! misho     269:        if (!list)
        !           270:                list = recv_add_id(&uidlist, uidmap, uid, NULL);
        !           271:        last = list;
        !           272: 
        !           273:        return list->id2;
1.1       misho     274: }
                    275: 
                    276: gid_t match_gid(gid_t gid, uint16 *flags_ptr)
                    277: {
                    278:        static struct idlist *last = NULL;
                    279:        struct idlist *list;
                    280: 
                    281:        if (last && gid == last->id)
                    282:                list = last;
                    283:        else {
                    284:                for (list = gidlist; list; list = list->next) {
                    285:                        if (list->id == gid)
                    286:                                break;
                    287:                }
                    288:                if (!list)
1.1.1.2 ! misho     289:                        list = recv_add_id(&gidlist, gidmap, gid, NULL);
1.1       misho     290:                last = list;
                    291:        }
                    292: 
                    293:        if (flags_ptr && list->flags & FLAG_SKIP_GROUP)
                    294:                *flags_ptr |= FLAG_SKIP_GROUP;
                    295:        return list->id2;
                    296: }
                    297: 
                    298: /* Add a uid to the list of uids.  Only called on sending side. */
                    299: const char *add_uid(uid_t uid)
                    300: {
                    301:        struct idlist *list;
                    302:        struct idlist *node;
1.1.1.2 ! misho     303:        union name_or_id noiu;
1.1       misho     304: 
                    305:        if (uid == 0)   /* don't map root */
                    306:                return NULL;
                    307: 
                    308:        for (list = uidlist; list; list = list->next) {
                    309:                if (list->id == uid)
                    310:                        return NULL;
                    311:        }
                    312: 
1.1.1.2 ! misho     313:        noiu.name = uid_to_user(uid);
        !           314:        node = add_to_list(&uidlist, uid, noiu, 0, 0);
        !           315:        return node->u.name;
1.1       misho     316: }
                    317: 
                    318: /* Add a gid to the list of gids.  Only called on sending side. */
                    319: const char *add_gid(gid_t gid)
                    320: {
                    321:        struct idlist *list;
                    322:        struct idlist *node;
1.1.1.2 ! misho     323:        union name_or_id noiu;
1.1       misho     324: 
                    325:        if (gid == 0)   /* don't map root */
                    326:                return NULL;
                    327: 
                    328:        for (list = gidlist; list; list = list->next) {
                    329:                if (list->id == gid)
                    330:                        return NULL;
                    331:        }
                    332: 
1.1.1.2 ! misho     333:        noiu.name = gid_to_group(gid);
        !           334:        node = add_to_list(&gidlist, gid, noiu, 0, 0);
        !           335:        return node->u.name;
1.1       misho     336: }
                    337: 
                    338: /* send a complete uid/gid mapping to the peer */
                    339: void send_id_list(int f)
                    340: {
                    341:        struct idlist *list;
                    342: 
                    343:        if (preserve_uid || preserve_acls) {
                    344:                int len;
                    345:                /* we send sequences of uid/byte-length/name */
                    346:                for (list = uidlist; list; list = list->next) {
1.1.1.2 ! misho     347:                        if (!list->u.name)
1.1       misho     348:                                continue;
1.1.1.2 ! misho     349:                        len = strlen(list->u.name);
1.1       misho     350:                        write_varint30(f, list->id);
                    351:                        write_byte(f, len);
1.1.1.2 ! misho     352:                        write_buf(f, list->u.name, len);
1.1       misho     353:                }
                    354: 
                    355:                /* terminate the uid list with a 0 uid. We explicitly exclude
                    356:                 * 0 from the list */
                    357:                write_varint30(f, 0);
                    358:        }
                    359: 
                    360:        if (preserve_gid || preserve_acls) {
                    361:                int len;
                    362:                for (list = gidlist; list; list = list->next) {
1.1.1.2 ! misho     363:                        if (!list->u.name)
1.1       misho     364:                                continue;
1.1.1.2 ! misho     365:                        len = strlen(list->u.name);
1.1       misho     366:                        write_varint30(f, list->id);
                    367:                        write_byte(f, len);
1.1.1.2 ! misho     368:                        write_buf(f, list->u.name, len);
1.1       misho     369:                }
                    370:                write_varint30(f, 0);
                    371:        }
                    372: }
                    373: 
                    374: uid_t recv_user_name(int f, uid_t uid)
                    375: {
                    376:        struct idlist *node;
                    377:        int len = read_byte(f);
                    378:        char *name = new_array(char, len+1);
                    379:        if (!name)
                    380:                out_of_memory("recv_user_name");
                    381:        read_sbuf(f, name, len);
                    382:        if (numeric_ids < 0) {
                    383:                free(name);
                    384:                name = NULL;
                    385:        }
1.1.1.2 ! misho     386:        node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */
1.1       misho     387:        return node->id2;
                    388: }
                    389: 
                    390: gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr)
                    391: {
                    392:        struct idlist *node;
                    393:        int len = read_byte(f);
                    394:        char *name = new_array(char, len+1);
                    395:        if (!name)
                    396:                out_of_memory("recv_group_name");
                    397:        read_sbuf(f, name, len);
                    398:        if (numeric_ids < 0) {
                    399:                free(name);
                    400:                name = NULL;
                    401:        }
1.1.1.2 ! misho     402:        node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */
1.1       misho     403:        if (flags_ptr && node->flags & FLAG_SKIP_GROUP)
                    404:                *flags_ptr |= FLAG_SKIP_GROUP;
                    405:        return node->id2;
                    406: }
                    407: 
                    408: /* recv a complete uid/gid mapping from the peer and map the uid/gid
                    409:  * in the file list to local names */
                    410: void recv_id_list(int f, struct file_list *flist)
                    411: {
                    412:        id_t id;
                    413:        int i;
                    414: 
                    415:        if ((preserve_uid || preserve_acls) && numeric_ids <= 0) {
                    416:                /* read the uid list */
                    417:                while ((id = read_varint30(f)) != 0)
                    418:                        recv_user_name(f, id);
                    419:        }
                    420: 
                    421:        if ((preserve_gid || preserve_acls) && numeric_ids <= 0) {
                    422:                /* read the gid list */
                    423:                while ((id = read_varint30(f)) != 0)
                    424:                        recv_group_name(f, id, NULL);
                    425:        }
                    426: 
                    427:        /* Now convert all the uids/gids from sender values to our values. */
                    428: #ifdef SUPPORT_ACLS
1.1.1.2 ! misho     429:        if (preserve_acls && (!numeric_ids || usermap || groupmap))
1.1       misho     430:                match_acl_ids();
                    431: #endif
1.1.1.2 ! misho     432:        if (am_root && preserve_uid && (!numeric_ids || usermap)) {
1.1       misho     433:                for (i = 0; i < flist->used; i++)
                    434:                        F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
                    435:        }
1.1.1.2 ! misho     436:        if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
1.1       misho     437:                for (i = 0; i < flist->used; i++) {
                    438:                        F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]),
                    439:                                                             &flist->files[i]->flags);
                    440:                }
                    441:        }
                    442: }
1.1.1.2 ! misho     443: 
        !           444: void parse_name_map(char *map, BOOL usernames)
        !           445: {
        !           446:        struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap;
        !           447:        struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist;
        !           448:        char *colon, *cp = map + strlen(map);
        !           449:        union name_or_id noiu;
        !           450:        id_t id1;
        !           451:        uint16 flags;
        !           452: 
        !           453:        /* Parse the list in reverse, so the order in the struct is right. */
        !           454:        while (1) {
        !           455:                while (cp > map && cp[-1] != ',') cp--;
        !           456:                if (!(colon = strchr(cp, ':'))) {
        !           457:                        rprintf(FERROR, "No colon found in --%smap: %s\n",
        !           458:                                usernames ? "user" : "group", cp);
        !           459:                        exit_cleanup(RERR_SYNTAX);
        !           460:                }
        !           461:                if (!colon[1]) {
        !           462:                        rprintf(FERROR, "No name found after colon --%smap: %s\n",
        !           463:                                usernames ? "user" : "group", cp);
        !           464:                        exit_cleanup(RERR_SYNTAX);
        !           465:                }
        !           466:                *colon = '\0';
        !           467: 
        !           468:                if (isDigit(cp)) {
        !           469:                        char *dash = strchr(cp, '-');
        !           470:                        if (strspn(cp, "0123456789-") != (size_t)(colon - cp)
        !           471:                         || (dash && (!dash[1] || strchr(dash+1, '-')))) {
        !           472:                                rprintf(FERROR, "Invalid number in --%smap: %s\n",
        !           473:                                        usernames ? "user" : "group", cp);
        !           474:                                exit_cleanup(RERR_SYNTAX);
        !           475:                        }
        !           476:                        if (dash)
        !           477:                                noiu.max_id = id_parse(dash+1);
        !           478:                        else
        !           479:                                noiu.max_id = 0;
        !           480:                        flags = 0;
        !           481:                        id1 = id_parse(cp);
        !           482:                } else if (strpbrk(cp, "*[?")) {
        !           483:                        flags = NFLAGS_WILD_NAME_MATCH;
        !           484:                        noiu.name = cp;
        !           485:                        id1 = 0;
        !           486:                } else {
        !           487:                        flags = NFLAGS_NAME_MATCH;
        !           488:                        noiu.name = cp;
        !           489:                        id1 = 0;
        !           490:                }
        !           491: 
        !           492:                if (usernames) {
        !           493:                        uid_t uid;
        !           494:                        if (user_to_uid(colon+1, &uid, True))
        !           495:                                add_to_list(idmap_ptr, id1, noiu, uid, flags);
        !           496:                        else {
        !           497:                                rprintf(FERROR,
        !           498:                                    "Unknown --usermap name on receiver: %s\n",
        !           499:                                    colon+1);
        !           500:                        }
        !           501:                } else {
        !           502:                        gid_t gid;
        !           503:                        if (group_to_gid(colon+1, &gid, True))
        !           504:                                add_to_list(idmap_ptr, id1, noiu, gid, flags);
        !           505:                        else {
        !           506:                                rprintf(FERROR,
        !           507:                                    "Unknown --groupmap name on receiver: %s\n",
        !           508:                                    colon+1);
        !           509:                        }
        !           510:                }
        !           511: 
        !           512:                if (cp == map)
        !           513:                        break;
        !           514: 
        !           515:                *--cp = '\0'; /* replace comma */
        !           516:        }
        !           517: 
        !           518:        /* The 0 user/group doesn't get its name sent, so add it explicitly. */
        !           519:        recv_add_id(idlist_ptr, *idmap_ptr, 0,
        !           520:                    numeric_ids ? NULL : usernames ? uid_to_user(0) : gid_to_group(0));
        !           521: }
        !           522: 
        !           523: #ifdef HAVE_GETGROUPLIST
        !           524: const char *getallgroups(uid_t uid, gid_t *gid_list, int *size_ptr)
        !           525: {
        !           526:        struct passwd *pw;
        !           527:        if ((pw = getpwuid(uid)) == NULL)
        !           528:                return "getpwuid failed";
        !           529:        /* Get all the process's groups, with the pw_gid group first. */
        !           530:        if (getgrouplist(pw->pw_name, pw->pw_gid, gid_list, size_ptr) < 0)
        !           531:                return "getgrouplist failed";
        !           532:        /* Paranoia: is the default group not first in the list? */
        !           533:        if (gid_list[0] != pw->pw_gid) {
        !           534:                int j;
        !           535:                for (j = 0; j < *size_ptr; j++) {
        !           536:                        if (gid_list[j] == pw->pw_gid) {
        !           537:                                gid_list[j] = gid_list[0];
        !           538:                                gid_list[0] = pw->pw_gid;
        !           539:                                break;
        !           540:                        }
        !           541:                }
        !           542:        }
        !           543:        return NULL;
        !           544: }
        !           545: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>