File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / uidlist.c
Revision 1.1.1.4 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Mar 17 00:32:36 2021 UTC (3 years, 3 months ago) by misho
Branches: rsync, MAIN
CVS tags: v3_2_3, HEAD
rsync 3.2.3

    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
    6:  * Copyright (C) 2004-2020 Wayne Davison
    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"
   27: #include "ifuncs.h"
   28: #include "itypes.h"
   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;
   36: extern int xmit_id0_names;
   37: extern pid_t namecvt_pid;
   38: extern gid_t our_gid;
   39: extern char *usermap;
   40: extern char *groupmap;
   41: 
   42: #ifdef HAVE_GETGROUPS
   43: # ifndef GETGROUPS_T
   44: #  define GETGROUPS_T gid_t
   45: # endif
   46: #endif
   47: 
   48: #define NFLAGS_WILD_NAME_MATCH (1<<0)
   49: #define NFLAGS_NAME_MATCH (1<<1)
   50: 
   51: union name_or_id {
   52: 	const char *name;
   53: 	id_t max_id;
   54: };
   55: 
   56: struct idlist {
   57: 	struct idlist *next;
   58: 	union name_or_id u;
   59: 	id_t id, id2;
   60: 	uint16 flags;
   61: };
   62: 
   63: static struct idlist *uidlist, *uidmap;
   64: static struct idlist *gidlist, *gidmap;
   65: 
   66: static id_t id_parse(const char *num_str)
   67: {
   68: 	id_t tmp, num = 0;
   69: 	const char *cp = num_str;
   70: 
   71: 	while (*cp) {
   72: 		if (!isDigit(cp)) {
   73: 		  invalid_num:
   74: 			rprintf(FERROR, "Invalid ID number: %s\n", num_str);
   75: 			exit_cleanup(RERR_SYNTAX);
   76: 		}
   77: 		tmp = num * 10 + *cp++ - '0';
   78: 		if (tmp < num)
   79: 			goto invalid_num;
   80: 		num = tmp;
   81: 	}
   82: 
   83: 	return num;
   84: }
   85: 
   86: static struct idlist *add_to_list(struct idlist **root, id_t id, union name_or_id noiu,
   87: 				  id_t id2, uint16 flags)
   88: {
   89: 	struct idlist *node = new(struct idlist);
   90: 	node->next = *root;
   91: 	node->u = noiu;
   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 */
  100: const char *uid_to_user(uid_t uid)
  101: {
  102: 	const char *name = NULL;
  103: 
  104: 	if (namecvt_pid) {
  105: 		id_t id = uid;
  106: 		namecvt_call("uid", &name, &id);
  107: 	} else {
  108: 		struct passwd *pass = getpwuid(uid);
  109: 		if (pass)
  110: 			name = strdup(pass->pw_name);
  111: 	}
  112: 
  113: 	return name;
  114: }
  115: 
  116: /* turn a gid into a group name */
  117: const char *gid_to_group(gid_t gid)
  118: {
  119: 	const char *name = NULL;
  120: 
  121: 	if (namecvt_pid) {
  122: 		id_t id = gid;
  123: 		namecvt_call("gid", &name, &id);
  124: 	} else {
  125: 		struct group *grp = getgrgid(gid);
  126: 		if (grp)
  127: 			name = strdup(grp->gr_name);
  128: 	}
  129: 
  130: 	return name;
  131: }
  132: 
  133: /* Parse a user name or (optionally) a number into a uid */
  134: int user_to_uid(const char *name, uid_t *uid_p, BOOL num_ok)
  135: {
  136: 	if (!name || !*name)
  137: 		return 0;
  138: 
  139: 	if (num_ok && name[strspn(name, "0123456789")] == '\0') {
  140: 		*uid_p = id_parse(name);
  141: 		return 1;
  142: 	}
  143: 
  144: 	if (namecvt_pid) {
  145: 		id_t id;
  146: 		if (!namecvt_call("usr", &name, &id))
  147: 			return 0;
  148: 		*uid_p = id;
  149: 	} else {
  150: 		struct passwd *pass = getpwnam(name);
  151: 		if (!pass)
  152: 			return 0;
  153: 		*uid_p = pass->pw_uid;
  154: 	}
  155: 
  156: 	return 1;
  157: }
  158: 
  159: /* Parse a group name or (optionally) a number into a gid */
  160: int group_to_gid(const char *name, gid_t *gid_p, BOOL num_ok)
  161: {
  162: 	if (!name || !*name)
  163: 		return 0;
  164: 
  165: 	if (num_ok && name[strspn(name, "0123456789")] == '\0') {
  166: 		*gid_p = id_parse(name);
  167: 		return 1;
  168: 	}
  169: 
  170: 	if (namecvt_pid) {
  171: 		id_t id;
  172: 		if (!namecvt_call("grp", &name, &id))
  173: 			return 0;
  174: 		*gid_p = id;
  175: 	} else {
  176: 		struct group *grp = getgrnam(name);
  177: 		if (!grp)
  178: 			return 0;
  179: 		*gid_p = grp->gr_gid;
  180: 	}
  181: 
  182: 	return 1;
  183: }
  184: 
  185: static int is_in_group(gid_t gid)
  186: {
  187: #ifdef HAVE_GETGROUPS
  188: 	static gid_t last_in;
  189: 	static int ngroups = -2, last_out = -1;
  190: 	static GETGROUPS_T *gidset;
  191: 	int n;
  192: 
  193: 	if (gid == last_in && last_out >= 0)
  194: 		return last_out;
  195: 	if (ngroups < -1) {
  196: 		if ((ngroups = getgroups(0, NULL)) < 0)
  197: 			ngroups = 0;
  198: 		gidset = new_array(GETGROUPS_T, ngroups+1);
  199: 		if (ngroups > 0)
  200: 			ngroups = getgroups(ngroups, gidset);
  201: 		/* The default gid might not be in the list on some systems. */
  202: 		for (n = 0; n < ngroups; n++) {
  203: 			if (gidset[n] == our_gid)
  204: 				break;
  205: 		}
  206: 		if (n == ngroups)
  207: 			gidset[ngroups++] = our_gid;
  208: 		if (DEBUG_GTE(OWN, 2)) {
  209: 			int pos;
  210: 			char *gidbuf = new_array(char, ngroups*21+32);
  211: 			pos = snprintf(gidbuf, 32, "process has %d gid%s: ", ngroups, ngroups == 1? "" : "s");
  212: 			for (n = 0; n < ngroups; n++) {
  213: 				pos += snprintf(gidbuf+pos, 21, " %d", (int)gidset[n]);
  214: 			}
  215: 			rprintf(FINFO, "%s\n", gidbuf);
  216: 			free(gidbuf);
  217: 		}
  218: 	}
  219: 
  220: 	last_in = gid;
  221: 	for (n = 0; n < ngroups; n++) {
  222: 		if (gidset[n] == gid)
  223: 			return last_out = 1;
  224: 	}
  225: 	return last_out = 0;
  226: 
  227: #else
  228: 	return gid == our_gid;
  229: #endif
  230: }
  231: 
  232: /* Add a uid/gid to its list of ids.  Only called on receiving side. */
  233: static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idmap,
  234: 				  id_t id, const char *name)
  235: {
  236: 	struct idlist *node;
  237: 	union name_or_id noiu;
  238: 	int flag;
  239: 	id_t id2;
  240: 
  241: 	noiu.name = name; /* ensure that add_to_list() gets the raw value. */
  242: 	if (!name)
  243: 		name = "";
  244: 
  245: 	for (node = idmap; node; node = node->next) {
  246: 		if (node->flags & NFLAGS_WILD_NAME_MATCH) {
  247: 			if (!wildmatch(node->u.name, name))
  248: 				continue;
  249: 		} else if (node->flags & NFLAGS_NAME_MATCH) {
  250: 			if (strcmp(node->u.name, name) != 0)
  251: 				continue;
  252: 		} else if (node->u.max_id) {
  253: 			if (id < node->id || id > node->u.max_id)
  254: 				continue;
  255: 		} else {
  256: 			if (node->id != id)
  257: 				continue;
  258: 		}
  259: 		break;
  260: 	}
  261: 	if (node)
  262: 		id2 = node->id2;
  263: 	else if (*name && id) {
  264: 		if (idlist_ptr == &uidlist) {
  265: 			uid_t uid;
  266: 			id2 = user_to_uid(name, &uid, False) ? uid : id;
  267: 		} else {
  268: 			gid_t gid;
  269: 			id2 = group_to_gid(name, &gid, False) ? gid : id;
  270: 		}
  271: 	} else
  272: 		id2 = id;
  273: 
  274: 	flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0;
  275: 	node = add_to_list(idlist_ptr, id, noiu, id2, flag);
  276: 
  277: 	if (DEBUG_GTE(OWN, 2)) {
  278: 		rprintf(FINFO, "%sid %u(%s) maps to %u\n",
  279: 			idlist_ptr == &uidlist ? "u" : "g",
  280: 			(unsigned)id, name, (unsigned)id2);
  281: 	}
  282: 
  283: 	return node;
  284: }
  285: 
  286: /* this function is a definite candidate for a faster algorithm */
  287: uid_t match_uid(uid_t uid)
  288: {
  289: 	static struct idlist *last = NULL;
  290: 	struct idlist *list;
  291: 
  292: 	if (last && uid == last->id)
  293: 		return last->id2;
  294: 
  295: 	for (list = uidlist; list; list = list->next) {
  296: 		if (list->id == uid)
  297: 			break;
  298: 	}
  299: 
  300: 	if (!list)
  301: 		list = recv_add_id(&uidlist, uidmap, uid, NULL);
  302: 	last = list;
  303: 
  304: 	return list->id2;
  305: }
  306: 
  307: gid_t match_gid(gid_t gid, uint16 *flags_ptr)
  308: {
  309: 	static struct idlist *last = NULL;
  310: 	struct idlist *list;
  311: 
  312: 	if (last && gid == last->id)
  313: 		list = last;
  314: 	else {
  315: 		for (list = gidlist; list; list = list->next) {
  316: 			if (list->id == gid)
  317: 				break;
  318: 		}
  319: 		if (!list)
  320: 			list = recv_add_id(&gidlist, gidmap, gid, NULL);
  321: 		last = list;
  322: 	}
  323: 
  324: 	if (flags_ptr && list->flags & FLAG_SKIP_GROUP)
  325: 		*flags_ptr |= FLAG_SKIP_GROUP;
  326: 	return list->id2;
  327: }
  328: 
  329: /* Add a uid to the list of uids.  Only called on sending side. */
  330: const char *add_uid(uid_t uid)
  331: {
  332: 	struct idlist *list;
  333: 	struct idlist *node;
  334: 	union name_or_id noiu;
  335: 
  336: 	for (list = uidlist; list; list = list->next) {
  337: 		if (list->id == uid)
  338: 			return NULL;
  339: 	}
  340: 
  341: 	noiu.name = uid_to_user(uid);
  342: 	node = add_to_list(&uidlist, uid, noiu, 0, 0);
  343: 	return node->u.name;
  344: }
  345: 
  346: /* Add a gid to the list of gids.  Only called on sending side. */
  347: const char *add_gid(gid_t gid)
  348: {
  349: 	struct idlist *list;
  350: 	struct idlist *node;
  351: 	union name_or_id noiu;
  352: 
  353: 	for (list = gidlist; list; list = list->next) {
  354: 		if (list->id == gid)
  355: 			return NULL;
  356: 	}
  357: 
  358: 	noiu.name = gid_to_group(gid);
  359: 	node = add_to_list(&gidlist, gid, noiu, 0, 0);
  360: 	return node->u.name;
  361: }
  362: 
  363: static void send_one_name(int f, id_t id, const char *name)
  364: {
  365: 	int len;
  366: 
  367: 	if (!name)
  368: 		name = "";
  369: 	if ((len = strlen(name)) > 255) /* Impossible? */
  370: 		len = 255;
  371: 
  372: 	write_varint30(f, id);
  373: 	write_byte(f, len);
  374: 	if (len)
  375: 		write_buf(f, name, len);
  376: }
  377: 
  378: static void send_one_list(int f, struct idlist *idlist, int usernames)
  379: {
  380: 	struct idlist *list;
  381: 
  382: 	/* we send sequences of id/byte-len/name */
  383: 	for (list = idlist; list; list = list->next) {
  384: 		if (list->id && list->u.name)
  385: 			send_one_name(f, list->id, list->u.name);
  386: 	}
  387: 
  388: 	/* Terminate the uid list with 0 (which was excluded above).
  389: 	 * A modern rsync also sends the name of id 0. */
  390: 	if (xmit_id0_names)
  391: 		send_one_name(f, 0, usernames ? uid_to_user(0) : gid_to_group(0));
  392: 	else
  393: 		write_varint30(f, 0);
  394: }
  395: 
  396: /* send a complete uid/gid mapping to the peer */
  397: void send_id_lists(int f)
  398: {
  399: 	if (preserve_uid || preserve_acls)
  400: 		send_one_list(f, uidlist, 1);
  401: 
  402: 	if (preserve_gid || preserve_acls)
  403: 		send_one_list(f, gidlist, 0);
  404: }
  405: 
  406: uid_t recv_user_name(int f, uid_t uid)
  407: {
  408: 	struct idlist *node;
  409: 	int len = read_byte(f);
  410: 	char *name;
  411: 
  412: 	if (len) {
  413: 		name = new_array(char, len+1);
  414: 		read_sbuf(f, name, len);
  415: 		if (numeric_ids < 0) {
  416: 			free(name);
  417: 			name = NULL;
  418: 		}
  419: 	} else
  420: 		name = NULL;
  421: 
  422: 	node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */
  423: 	return node->id2;
  424: }
  425: 
  426: gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr)
  427: {
  428: 	struct idlist *node;
  429: 	int len = read_byte(f);
  430: 	char *name;
  431: 
  432: 	if (len) {
  433: 		name = new_array(char, len+1);
  434: 		read_sbuf(f, name, len);
  435: 		if (numeric_ids < 0) {
  436: 			free(name);
  437: 			name = NULL;
  438: 		}
  439: 	} else
  440: 		name = NULL;
  441: 
  442: 	node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */
  443: 	if (flags_ptr && node->flags & FLAG_SKIP_GROUP)
  444: 		*flags_ptr |= FLAG_SKIP_GROUP;
  445: 	return node->id2;
  446: }
  447: 
  448: /* recv a complete uid/gid mapping from the peer and map the uid/gid
  449:  * in the file list to local names */
  450: void recv_id_list(int f, struct file_list *flist)
  451: {
  452: 	id_t id;
  453: 	int i;
  454: 
  455: 	if ((preserve_uid || preserve_acls) && numeric_ids <= 0) {
  456: 		/* read the uid list */
  457: 		while ((id = read_varint30(f)) != 0)
  458: 			recv_user_name(f, id);
  459: 		if (xmit_id0_names)
  460: 			recv_user_name(f, 0);
  461: 	}
  462: 
  463: 	if ((preserve_gid || preserve_acls) && numeric_ids <= 0) {
  464: 		/* read the gid list */
  465: 		while ((id = read_varint30(f)) != 0)
  466: 			recv_group_name(f, id, NULL);
  467: 		if (xmit_id0_names)
  468: 			recv_group_name(f, 0, NULL);
  469: 	}
  470: 
  471: 	/* Now convert all the uids/gids from sender values to our values. */
  472: #ifdef SUPPORT_ACLS
  473: 	if (preserve_acls && (!numeric_ids || usermap || groupmap))
  474: 		match_acl_ids();
  475: #endif
  476: 	if (am_root && preserve_uid && (!numeric_ids || usermap)) {
  477: 		for (i = 0; i < flist->used; i++)
  478: 			F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
  479: 	}
  480: 	if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
  481: 		for (i = 0; i < flist->used; i++) {
  482: 			F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]), &flist->files[i]->flags);
  483: 		}
  484: 	}
  485: }
  486: 
  487: void parse_name_map(char *map, BOOL usernames)
  488: {
  489: 	struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap;
  490: 	struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist;
  491: 	char *colon, *cp = map + strlen(map);
  492: 	union name_or_id noiu;
  493: 	id_t id1;
  494: 	uint16 flags;
  495: 
  496: 	/* Parse the list in reverse, so the order in the struct is right. */
  497: 	while (1) {
  498: 		while (cp > map && cp[-1] != ',') cp--;
  499: 		if (!(colon = strchr(cp, ':'))) {
  500: 			rprintf(FERROR, "No colon found in --%smap: %s\n",
  501: 				usernames ? "user" : "group", cp);
  502: 			exit_cleanup(RERR_SYNTAX);
  503: 		}
  504: 		if (!colon[1]) {
  505: 			rprintf(FERROR, "No name found after colon --%smap: %s\n",
  506: 				usernames ? "user" : "group", cp);
  507: 			exit_cleanup(RERR_SYNTAX);
  508: 		}
  509: 		*colon = '\0';
  510: 
  511: 		if (isDigit(cp)) {
  512: 			char *dash = strchr(cp, '-');
  513: 			if (strspn(cp, "0123456789-") != (size_t)(colon - cp)
  514: 			 || (dash && (!dash[1] || strchr(dash+1, '-')))) {
  515: 				rprintf(FERROR, "Invalid number in --%smap: %s\n",
  516: 					usernames ? "user" : "group", cp);
  517: 				exit_cleanup(RERR_SYNTAX);
  518: 			}
  519: 			if (dash) {
  520: 				*dash = '\0';
  521: 				noiu.max_id = id_parse(dash+1);
  522: 			} else
  523: 				noiu.max_id = 0;
  524: 			flags = 0;
  525: 			id1 = id_parse(cp);
  526: 			if (dash)
  527: 				*dash = '-';
  528: 		} else if (strpbrk(cp, "*[?")) {
  529: 			flags = NFLAGS_WILD_NAME_MATCH;
  530: 			noiu.name = cp;
  531: 			id1 = 0;
  532: 		} else {
  533: 			flags = NFLAGS_NAME_MATCH;
  534: 			noiu.name = cp;
  535: 			id1 = 0;
  536: 		}
  537: 
  538: 		if (usernames) {
  539: 			uid_t uid;
  540: 			if (user_to_uid(colon+1, &uid, True))
  541: 				add_to_list(idmap_ptr, id1, noiu, uid, flags);
  542: 			else {
  543: 				rprintf(FERROR, "Unknown --usermap name on receiver: %s\n", colon+1);
  544: 			}
  545: 		} else {
  546: 			gid_t gid;
  547: 			if (group_to_gid(colon+1, &gid, True))
  548: 				add_to_list(idmap_ptr, id1, noiu, gid, flags);
  549: 			else {
  550: 				rprintf(FERROR, "Unknown --groupmap name on receiver: %s\n", colon+1);
  551: 			}
  552: 		}
  553: 
  554: 		if (cp == map)
  555: 			break;
  556: 
  557: 		*--cp = '\0'; /* replace comma */
  558: 	}
  559: 
  560: 	/* If the sender isn't going to xmit the id0 name, we assume it's "root". */
  561: 	if (!xmit_id0_names)
  562: 		recv_add_id(idlist_ptr, *idmap_ptr, 0, numeric_ids ? NULL : "root");
  563: }
  564: 
  565: #ifdef HAVE_GETGROUPLIST
  566: const char *getallgroups(uid_t uid, item_list *gid_list)
  567: {
  568: 	struct passwd *pw;
  569: 	gid_t *gid_array;
  570: 	int size;
  571: 
  572: 	if ((pw = getpwuid(uid)) == NULL)
  573: 		return "getpwuid failed";
  574: 
  575: 	gid_list->count = 0; /* We're overwriting any items in the list */
  576: 	(void)EXPAND_ITEM_LIST(gid_list, gid_t, 32);
  577: 	size = gid_list->malloced;
  578: 
  579: 	/* Get all the process's groups, with the pw_gid group first. */
  580: 	if (getgrouplist(pw->pw_name, pw->pw_gid, gid_list->items, &size) < 0) {
  581: 		if (size > (int)gid_list->malloced) {
  582: 			gid_list->count = gid_list->malloced;
  583: 			(void)EXPAND_ITEM_LIST(gid_list, gid_t, size);
  584: 			if (getgrouplist(pw->pw_name, pw->pw_gid, gid_list->items, &size) < 0)
  585: 				size = -1;
  586: 		} else
  587: 			size = -1;
  588: 		if (size < 0)
  589: 			return "getgrouplist failed";
  590: 	}
  591: 	gid_list->count = size;
  592: 	gid_array = gid_list->items;
  593: 
  594: 	/* Paranoia: is the default group not first in the list? */
  595: 	if (gid_array[0] != pw->pw_gid) {
  596: 		int j;
  597: 		for (j = 1; j < size; j++) {
  598: 			if (gid_array[j] == pw->pw_gid)
  599: 				break;
  600: 		}
  601: 		if (j == size) { /* The default group wasn't found! */
  602: 			(void)EXPAND_ITEM_LIST(gid_list, gid_t, size+1);
  603: 			gid_array = gid_list->items;
  604: 		}
  605: 		gid_array[j] = gid_array[0];
  606: 		gid_array[0] = pw->pw_gid;
  607: 	}
  608: 
  609: 	return NULL;
  610: }
  611: #endif

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