xref: /openbsd-src/usr.bin/rsync/ids.c (revision b00915975b09768d864990a42fb77354c5f1ad82)
1*b0091597Sclaudio /*	$OpenBSD: ids.c,v 1.15 2021/06/30 13:10:04 claudio Exp $ */
29122c1f8Sbenno /*
39122c1f8Sbenno  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
49122c1f8Sbenno  *
59122c1f8Sbenno  * Permission to use, copy, modify, and distribute this software for any
69122c1f8Sbenno  * purpose with or without fee is hereby granted, provided that the above
79122c1f8Sbenno  * copyright notice and this permission notice appear in all copies.
89122c1f8Sbenno  *
99122c1f8Sbenno  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109122c1f8Sbenno  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119122c1f8Sbenno  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129122c1f8Sbenno  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139122c1f8Sbenno  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149122c1f8Sbenno  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159122c1f8Sbenno  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169122c1f8Sbenno  */
179122c1f8Sbenno #include <assert.h>
189122c1f8Sbenno #include <grp.h>
199122c1f8Sbenno #include <inttypes.h>
208f34fbc5Sflorian #include <pwd.h>
219122c1f8Sbenno #include <stdio.h>
229122c1f8Sbenno #include <stdlib.h>
239122c1f8Sbenno #include <string.h>
249122c1f8Sbenno #include <unistd.h>
259122c1f8Sbenno 
269122c1f8Sbenno #include "extern.h"
279122c1f8Sbenno 
289122c1f8Sbenno /*
29ae581babSclaudio  * Free a list of struct ident previously allocated with idents_add().
309122c1f8Sbenno  * Does nothing if the pointer is NULL.
319122c1f8Sbenno  */
329122c1f8Sbenno void
idents_free(struct ident * p,size_t sz)339122c1f8Sbenno idents_free(struct ident *p, size_t sz)
349122c1f8Sbenno {
359122c1f8Sbenno 	size_t	 i;
369122c1f8Sbenno 
37e5bf8365Sbenno 	if (p == NULL)
389122c1f8Sbenno 		return;
399122c1f8Sbenno 	for (i = 0; i < sz; i++)
409122c1f8Sbenno 		free(p[i].name);
419122c1f8Sbenno 	free(p);
429122c1f8Sbenno }
439122c1f8Sbenno 
449122c1f8Sbenno /*
458f34fbc5Sflorian  * Given a list of files with the identifiers as set by the sender,
468f34fbc5Sflorian  * re-assign the identifiers from the list of remapped ones.
478f34fbc5Sflorian  * Don't ever remap wheel/root.
484cdf811fSbenno  * If we can't find the gid in the list (when, e.g., being sent by the
494cdf811fSbenno  * daemon), don't try to map it.
50e5bf8365Sbenno  */
51e5bf8365Sbenno void
idents_assign_gid(struct sess * sess,struct flist * fl,size_t flsz,const struct ident * ids,size_t idsz)528f34fbc5Sflorian idents_assign_gid(struct sess *sess, struct flist *fl, size_t flsz,
538f34fbc5Sflorian 	const struct ident *ids, size_t idsz)
54e5bf8365Sbenno {
55e5bf8365Sbenno 	size_t	 i, j;
56e5bf8365Sbenno 
5744dad8d1Sbenno 	assert(!sess->opts->numeric_ids);
5844dad8d1Sbenno 
59e5bf8365Sbenno 	for (i = 0; i < flsz; i++) {
608f34fbc5Sflorian 		if (fl[i].st.gid == 0)
61e5bf8365Sbenno 			continue;
628f34fbc5Sflorian 		for (j = 0; j < idsz; j++)
638f34fbc5Sflorian 			if ((int32_t)fl[i].st.gid == ids[j].id)
64e5bf8365Sbenno 				break;
654cdf811fSbenno 		if (j < idsz)
668f34fbc5Sflorian 			fl[i].st.gid = ids[j].mapped;
67e5bf8365Sbenno 	}
68e5bf8365Sbenno }
69e5bf8365Sbenno 
70e5bf8365Sbenno /*
718f34fbc5Sflorian  * Like idents_assign_gid().
728f34fbc5Sflorian  */
738f34fbc5Sflorian void
idents_assign_uid(struct sess * sess,struct flist * fl,size_t flsz,const struct ident * ids,size_t idsz)748f34fbc5Sflorian idents_assign_uid(struct sess *sess, struct flist *fl, size_t flsz,
758f34fbc5Sflorian 	const struct ident *ids, size_t idsz)
768f34fbc5Sflorian {
778f34fbc5Sflorian 	size_t	 i, j;
788f34fbc5Sflorian 
7944dad8d1Sbenno 	assert(!sess->opts->numeric_ids);
8044dad8d1Sbenno 
818f34fbc5Sflorian 	for (i = 0; i < flsz; i++) {
828f34fbc5Sflorian 		if (fl[i].st.uid == 0)
838f34fbc5Sflorian 			continue;
848f34fbc5Sflorian 		for (j = 0; j < idsz; j++)
858f34fbc5Sflorian 			if ((int32_t)fl[i].st.uid == ids[j].id)
868f34fbc5Sflorian 				break;
874cdf811fSbenno 		if (j < idsz)
888f34fbc5Sflorian 			fl[i].st.uid = ids[j].mapped;
898f34fbc5Sflorian 	}
908f34fbc5Sflorian }
918f34fbc5Sflorian 
928f34fbc5Sflorian /*
938f34fbc5Sflorian  * Given a list of identifiers from the remote host, fill in our local
949122c1f8Sbenno  * identifiers of the same names.
95244291d8Sbenno  * Use the remote numeric identifier if we can't find the identifier OR the
96244291d8Sbenno  * identifier is zero (wheel/root).
97244291d8Sbenno  * FIXME: we should at least warn when we can't find an identifier, use
98244291d8Sbenno  * the numeric id, and that numeric id is assigned to a different user.
999122c1f8Sbenno  */
1009122c1f8Sbenno void
idents_remap(struct sess * sess,int isgid,struct ident * ids,size_t idsz)1018f34fbc5Sflorian idents_remap(struct sess *sess, int isgid, struct ident *ids, size_t idsz)
1029122c1f8Sbenno {
1039122c1f8Sbenno 	size_t		 i;
1049122c1f8Sbenno 	struct group	*grp;
1058f34fbc5Sflorian 	struct passwd	*usr;
106aa1dcd86Sderaadt 	uint32_t	 id;
107aa1dcd86Sderaadt 	int		 valid;
1089122c1f8Sbenno 
10944dad8d1Sbenno 	assert(!sess->opts->numeric_ids);
110244291d8Sbenno 
1118f34fbc5Sflorian 	for (i = 0; i < idsz; i++) {
1128f34fbc5Sflorian 		assert(ids[i].id != 0);
1138f34fbc5Sflorian 
1148f34fbc5Sflorian 		/* Start by getting our local representation. */
1158f34fbc5Sflorian 
116aa1dcd86Sderaadt 		valid = id = 0;
117aa1dcd86Sderaadt 		if (isgid) {
118aa1dcd86Sderaadt 			grp = getgrnam(ids[i].name);
119aa1dcd86Sderaadt 			if (grp) {
120aa1dcd86Sderaadt 				id = grp->gr_gid;
121aa1dcd86Sderaadt 				valid = 1;
122aa1dcd86Sderaadt 			}
123aa1dcd86Sderaadt 		} else {
124aa1dcd86Sderaadt 			usr = getpwnam(ids[i].name);
125aa1dcd86Sderaadt 			if (usr) {
126aa1dcd86Sderaadt 				id = usr->pw_uid;
127aa1dcd86Sderaadt 				valid = 1;
128aa1dcd86Sderaadt 			}
129aa1dcd86Sderaadt 		}
130e5bf8365Sbenno 
131e5bf8365Sbenno 		/*
132e5bf8365Sbenno 		 * (1) Empty names inherit.
1338f34fbc5Sflorian 		 * (2) Unknown identifier names inherit.
1348f34fbc5Sflorian 		 * (3) Wheel/root inherits.
135e5bf8365Sbenno 		 * (4) Otherwise, use the local identifier.
136e5bf8365Sbenno 		 */
137e5bf8365Sbenno 
1388f34fbc5Sflorian 		if (ids[i].name[0] == '\0')
1398f34fbc5Sflorian 			ids[i].mapped = ids[i].id;
140aa1dcd86Sderaadt 		else if (!valid)
1418f34fbc5Sflorian 			ids[i].mapped = ids[i].id;
1429122c1f8Sbenno 		else
1438f34fbc5Sflorian 			ids[i].mapped = id;
14483d70d24Sbenno 
145b2a7eac7Sbenno 		LOG4("remapped identifier %s: %d -> %d",
1468f34fbc5Sflorian 		    ids[i].name, ids[i].id, ids[i].mapped);
1479122c1f8Sbenno 	}
1489122c1f8Sbenno }
1499122c1f8Sbenno 
1509122c1f8Sbenno /*
1518f34fbc5Sflorian  * If "id" is not part of the list of known users or groups (depending
1528f34fbc5Sflorian  * upon "isgid", add it.
1538f34fbc5Sflorian  * This also verifies that the name isn't too long.
1548f34fbc5Sflorian  * Does nothing with user/group zero.
1559122c1f8Sbenno  * Return zero on failure, non-zero on success.
1569122c1f8Sbenno  */
1579122c1f8Sbenno int
idents_add(int isgid,struct ident ** ids,size_t * idsz,int32_t id)158ba617adaSbenno idents_add(int isgid, struct ident **ids, size_t *idsz, int32_t id)
1599122c1f8Sbenno {
1609122c1f8Sbenno 	struct group	*grp;
1618f34fbc5Sflorian 	struct passwd	*usr;
1629122c1f8Sbenno 	size_t		 i, sz;
1639122c1f8Sbenno 	void		*pp;
1648f34fbc5Sflorian 	const char	*name;
1659122c1f8Sbenno 
1668f34fbc5Sflorian 	if (id == 0)
167e5bf8365Sbenno 		return 1;
168e5bf8365Sbenno 
1698f34fbc5Sflorian 	for (i = 0; i < *idsz; i++)
1708f34fbc5Sflorian 		if ((*ids)[i].id == id)
1719122c1f8Sbenno 			return 1;
1729122c1f8Sbenno 
1739122c1f8Sbenno 	/*
1748f34fbc5Sflorian 	 * Look up the reference in a type-specific way.
1758f34fbc5Sflorian 	 * Make sure that the name length is sane: we transmit it using
1768f34fbc5Sflorian 	 * a single byte.
1779122c1f8Sbenno 	 */
1789122c1f8Sbenno 
1798f34fbc5Sflorian 	assert(i == *idsz);
1808f34fbc5Sflorian 	if (isgid) {
1818f34fbc5Sflorian 		if ((grp = getgrgid((gid_t)id)) == NULL) {
182b2a7eac7Sbenno 			ERR("%d: unknown gid", id);
1839122c1f8Sbenno 			return 0;
1848f34fbc5Sflorian 		}
1858f34fbc5Sflorian 		name = grp->gr_name;
1868f34fbc5Sflorian 	} else {
1878f34fbc5Sflorian 		if ((usr = getpwuid((uid_t)id)) == NULL) {
188b2a7eac7Sbenno 			ERR("%d: unknown uid", id);
1898f34fbc5Sflorian 			return 0;
1908f34fbc5Sflorian 		}
1918f34fbc5Sflorian 		name = usr->pw_name;
1928f34fbc5Sflorian 	}
1938f34fbc5Sflorian 
1948f34fbc5Sflorian 	if ((sz = strlen(name)) > UINT8_MAX) {
195b2a7eac7Sbenno 		ERRX("%d: name too long: %s", id, name);
1969122c1f8Sbenno 		return 0;
197e5bf8365Sbenno 	} else if (sz == 0) {
198b2a7eac7Sbenno 		ERRX("%d: zero-length name", id);
1999122c1f8Sbenno 		return 0;
2009122c1f8Sbenno 	}
2019122c1f8Sbenno 
2028f34fbc5Sflorian 	/* Add the identifier to the array. */
2039122c1f8Sbenno 
2048f34fbc5Sflorian 	pp = reallocarray(*ids, *idsz + 1, sizeof(struct ident));
205e5bf8365Sbenno 	if (pp == NULL) {
206b2a7eac7Sbenno 		ERR("reallocarray");
2079122c1f8Sbenno 		return 0;
2089122c1f8Sbenno 	}
2098f34fbc5Sflorian 	*ids = pp;
2108f34fbc5Sflorian 	(*ids)[*idsz].id = id;
2118f34fbc5Sflorian 	(*ids)[*idsz].name = strdup(name);
2128f34fbc5Sflorian 	if ((*ids)[*idsz].name == NULL) {
213b2a7eac7Sbenno 		ERR("strdup");
2149122c1f8Sbenno 		return 0;
2159122c1f8Sbenno 	}
2169122c1f8Sbenno 
217b2a7eac7Sbenno 	LOG4("adding identifier to list: %s (%u)",
2188f34fbc5Sflorian 	    (*ids)[*idsz].name, (*ids)[*idsz].id);
2198f34fbc5Sflorian 	(*idsz)++;
2209122c1f8Sbenno 	return 1;
2219122c1f8Sbenno }
2229122c1f8Sbenno 
2239122c1f8Sbenno /*
2249122c1f8Sbenno  * Send a list of struct ident.
2259122c1f8Sbenno  * See idents_recv().
2269122c1f8Sbenno  * We should only do this if we're preserving gids/uids.
2279122c1f8Sbenno  * Return zero on failure, non-zero on success.
2289122c1f8Sbenno  */
2299122c1f8Sbenno int
idents_send(struct sess * sess,int fd,const struct ident * ids,size_t idsz)2309122c1f8Sbenno idents_send(struct sess *sess,
2319122c1f8Sbenno 	int fd, const struct ident *ids, size_t idsz)
2329122c1f8Sbenno {
2339122c1f8Sbenno 	size_t	 i, sz;
2349122c1f8Sbenno 
2359122c1f8Sbenno 	for (i = 0; i < idsz; i++) {
236e5bf8365Sbenno 		assert(ids[i].name != NULL);
237e5bf8365Sbenno 		assert(ids[i].id != 0);
2389122c1f8Sbenno 		sz = strlen(ids[i].name);
2399122c1f8Sbenno 		assert(sz > 0 && sz <= UINT8_MAX);
240aa1dcd86Sderaadt 		if (!io_write_uint(sess, fd, ids[i].id)) {
241b2a7eac7Sbenno 			ERRX1("io_write_uint");
2429122c1f8Sbenno 			return 0;
2439122c1f8Sbenno 		} else if (!io_write_byte(sess, fd, sz)) {
244b2a7eac7Sbenno 			ERRX1("io_write_byte");
2459122c1f8Sbenno 			return 0;
2469122c1f8Sbenno 		} else if (!io_write_buf(sess, fd, ids[i].name, sz)) {
247b2a7eac7Sbenno 			ERRX1("io_write_buf");
2489122c1f8Sbenno 			return 0;
2499122c1f8Sbenno 		}
2509122c1f8Sbenno 	}
2519122c1f8Sbenno 
2529122c1f8Sbenno 	if (!io_write_int(sess, fd, 0)) {
253b2a7eac7Sbenno 		ERRX1("io_write_int");
2549122c1f8Sbenno 		return 0;
2559122c1f8Sbenno 	}
2569122c1f8Sbenno 
2579122c1f8Sbenno 	return 1;
2589122c1f8Sbenno }
2599122c1f8Sbenno 
2609122c1f8Sbenno /*
2619122c1f8Sbenno  * Receive a list of struct ident.
2629122c1f8Sbenno  * See idents_send().
2639122c1f8Sbenno  * We should only do this if we're preserving gids/uids.
2649122c1f8Sbenno  * Return zero on failure, non-zero on success.
2659122c1f8Sbenno  */
2669122c1f8Sbenno int
idents_recv(struct sess * sess,int fd,struct ident ** ids,size_t * idsz)2679122c1f8Sbenno idents_recv(struct sess *sess,
2689122c1f8Sbenno 	int fd, struct ident **ids, size_t *idsz)
2699122c1f8Sbenno {
270ae581babSclaudio 	uint32_t id;
2719122c1f8Sbenno 	uint8_t	 sz;
2729122c1f8Sbenno 	void	*pp;
2739122c1f8Sbenno 
2749122c1f8Sbenno 	for (;;) {
275aa1dcd86Sderaadt 		if (!io_read_uint(sess, fd, &id)) {
276b2a7eac7Sbenno 			ERRX1("io_read_uint");
2779122c1f8Sbenno 			return 0;
278e5bf8365Sbenno 		} else if (id == 0)
2799122c1f8Sbenno 			break;
2809122c1f8Sbenno 
2819122c1f8Sbenno 		pp = reallocarray(*ids,
2829122c1f8Sbenno 			*idsz + 1, sizeof(struct ident));
283e5bf8365Sbenno 		if (pp == NULL) {
284b2a7eac7Sbenno 			ERR("reallocarray");
2859122c1f8Sbenno 			return 0;
2869122c1f8Sbenno 		}
2879122c1f8Sbenno 		*ids = pp;
2889122c1f8Sbenno 		memset(&(*ids)[*idsz], 0, sizeof(struct ident));
289e5bf8365Sbenno 
290e5bf8365Sbenno 		/*
2918f34fbc5Sflorian 		 * When reading the size, warn if we get a size of zero.
292e5bf8365Sbenno 		 * The spec doesn't allow this, but we might have a
293e5bf8365Sbenno 		 * noncomformant or adversarial sender.
294e5bf8365Sbenno 		 */
295e5bf8365Sbenno 
2969122c1f8Sbenno 		if (!io_read_byte(sess, fd, &sz)) {
297b2a7eac7Sbenno 			ERRX1("io_read_byte");
2989122c1f8Sbenno 			return 0;
2998f34fbc5Sflorian 		} else if (sz == 0)
300b2a7eac7Sbenno 			WARNX("zero-length name in identifier list");
301e5bf8365Sbenno 
302ae581babSclaudio 		assert(id < INT32_MAX);
3039122c1f8Sbenno 		(*ids)[*idsz].id = id;
3049122c1f8Sbenno 		(*ids)[*idsz].name = calloc(sz + 1, 1);
305e5bf8365Sbenno 		if ((*ids)[*idsz].name == NULL) {
306b2a7eac7Sbenno 			ERR("calloc");
3079122c1f8Sbenno 			return 0;
3089122c1f8Sbenno 		}
3099122c1f8Sbenno 		if (!io_read_buf(sess, fd, (*ids)[*idsz].name, sz)) {
310b2a7eac7Sbenno 			ERRX1("io_read_buf");
3119122c1f8Sbenno 			return 0;
3129122c1f8Sbenno 		}
3139122c1f8Sbenno 		(*idsz)++;
3149122c1f8Sbenno 	}
3159122c1f8Sbenno 
3169122c1f8Sbenno 	return 1;
3179122c1f8Sbenno }
318