File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / rsync / uidlist.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Oct 14 07:51:14 2013 UTC (10 years, 8 months ago) by misho
Branches: rsync, MAIN
CVS tags: RSYNC3_1_0, HEAD
v 3.1.0

    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-2013 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 gid_t our_gid;
   37: extern char *usermap;
   38: extern char *groupmap;
   39: 
   40: #ifdef HAVE_GETGROUPS
   41: # ifndef GETGROUPS_T
   42: #  define GETGROUPS_T gid_t
   43: # endif
   44: #endif
   45: 
   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: 
   54: struct idlist {
   55: 	struct idlist *next;
   56: 	union name_or_id u;
   57: 	id_t id, id2;
   58: 	uint16 flags;
   59: };
   60: 
   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: }
   83: 
   84: static struct idlist *add_to_list(struct idlist **root, id_t id, union name_or_id noiu,
   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;
   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: char *uid_to_user(uid_t uid)
  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 */
  109: char *gid_to_group(gid_t gid)
  110: {
  111: 	struct group *grp = getgrgid(gid);
  112: 	if (grp)
  113: 		return strdup(grp->gr_name);
  114: 	return NULL;
  115: }
  116: 
  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)
  119: {
  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;
  131: }
  132: 
  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)
  135: {
  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;
  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++) {
  169: 			if (gidset[n] == our_gid)
  170: 				break;
  171: 		}
  172: 		if (n == ngroups)
  173: 			gidset[ngroups++] = our_gid;
  174: 		if (DEBUG_GTE(OWN, 2)) {
  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
  197: 	return gid == our_gid;
  198: #endif
  199: }
  200: 
  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)
  204: {
  205: 	struct idlist *node;
  206: 	union name_or_id noiu;
  207: 	int flag;
  208: 	id_t id2;
  209: 
  210: 	noiu.name = name; /* ensure that add_to_list() gets the raw value. */
  211: 	if (!name)
  212: 		name = "";
  213: 
  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;
  229: 	}
  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;
  242: 
  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);
  245: 
  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);
  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: {
  258: 	static struct idlist *last = NULL;
  259: 	struct idlist *list;
  260: 
  261: 	if (last && uid == last->id)
  262: 		return last->id2;
  263: 
  264: 	for (list = uidlist; list; list = list->next) {
  265: 		if (list->id == uid)
  266: 			break;
  267: 	}
  268: 
  269: 	if (!list)
  270: 		list = recv_add_id(&uidlist, uidmap, uid, NULL);
  271: 	last = list;
  272: 
  273: 	return list->id2;
  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)
  289: 			list = recv_add_id(&gidlist, gidmap, gid, NULL);
  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;
  303: 	union name_or_id noiu;
  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: 
  313: 	noiu.name = uid_to_user(uid);
  314: 	node = add_to_list(&uidlist, uid, noiu, 0, 0);
  315: 	return node->u.name;
  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;
  323: 	union name_or_id noiu;
  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: 
  333: 	noiu.name = gid_to_group(gid);
  334: 	node = add_to_list(&gidlist, gid, noiu, 0, 0);
  335: 	return node->u.name;
  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) {
  347: 			if (!list->u.name)
  348: 				continue;
  349: 			len = strlen(list->u.name);
  350: 			write_varint30(f, list->id);
  351: 			write_byte(f, len);
  352: 			write_buf(f, list->u.name, len);
  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) {
  363: 			if (!list->u.name)
  364: 				continue;
  365: 			len = strlen(list->u.name);
  366: 			write_varint30(f, list->id);
  367: 			write_byte(f, len);
  368: 			write_buf(f, list->u.name, len);
  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: 	}
  386: 	node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */
  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: 	}
  402: 	node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */
  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
  429: 	if (preserve_acls && (!numeric_ids || usermap || groupmap))
  430: 		match_acl_ids();
  431: #endif
  432: 	if (am_root && preserve_uid && (!numeric_ids || usermap)) {
  433: 		for (i = 0; i < flist->used; i++)
  434: 			F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
  435: 	}
  436: 	if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
  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: }
  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>