xref: /openbsd/usr.bin/rsync/ids.c (revision 4cfece93)
1 /*	$Id: ids.c,v 1.13 2019/05/08 21:30:11 benno Exp $ */
2 /*
3  * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 #include <assert.h>
18 #include <grp.h>
19 #include <inttypes.h>
20 #include <pwd.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 
26 #include "extern.h"
27 
28 /*
29  * Free a list of struct ident previously allocated with idents_gid_add().
30  * Does nothing if the pointer is NULL.
31  */
32 void
33 idents_free(struct ident *p, size_t sz)
34 {
35 	size_t	 i;
36 
37 	if (p == NULL)
38 		return;
39 	for (i = 0; i < sz; i++)
40 		free(p[i].name);
41 	free(p);
42 }
43 
44 /*
45  * Given a list of files with the identifiers as set by the sender,
46  * re-assign the identifiers from the list of remapped ones.
47  * Don't ever remap wheel/root.
48  * If we can't find the gid in the list (when, e.g., being sent by the
49  * daemon), don't try to map it.
50  */
51 void
52 idents_assign_gid(struct sess *sess, struct flist *fl, size_t flsz,
53 	const struct ident *ids, size_t idsz)
54 {
55 	size_t	 i, j;
56 
57 	assert(!sess->opts->numeric_ids);
58 
59 	for (i = 0; i < flsz; i++) {
60 		if (fl[i].st.gid == 0)
61 			continue;
62 		for (j = 0; j < idsz; j++)
63 			if ((int32_t)fl[i].st.gid == ids[j].id)
64 				break;
65 		if (j < idsz)
66 			fl[i].st.gid = ids[j].mapped;
67 	}
68 }
69 
70 /*
71  * Like idents_assign_gid().
72  */
73 void
74 idents_assign_uid(struct sess *sess, struct flist *fl, size_t flsz,
75 	const struct ident *ids, size_t idsz)
76 {
77 	size_t	 i, j;
78 
79 	assert(!sess->opts->numeric_ids);
80 
81 	for (i = 0; i < flsz; i++) {
82 		if (fl[i].st.uid == 0)
83 			continue;
84 		for (j = 0; j < idsz; j++)
85 			if ((int32_t)fl[i].st.uid == ids[j].id)
86 				break;
87 		if (j < idsz)
88 			fl[i].st.uid = ids[j].mapped;
89 	}
90 }
91 
92 /*
93  * Given a list of identifiers from the remote host, fill in our local
94  * identifiers of the same names.
95  * Use the remote numeric identifier if we can't find the identifier OR the
96  * identifier is zero (wheel/root).
97  * FIXME: we should at least warn when we can't find an identifier, use
98  * the numeric id, and that numeric id is assigned to a different user.
99  */
100 void
101 idents_remap(struct sess *sess, int isgid, struct ident *ids, size_t idsz)
102 {
103 	size_t		 i;
104 	struct group	*grp;
105 	struct passwd	*usr;
106 	uint32_t	 id;
107 	int		valid;
108 
109 	assert(!sess->opts->numeric_ids);
110 
111 	for (i = 0; i < idsz; i++) {
112 		assert(ids[i].id != 0);
113 
114 		/* Start by getting our local representation. */
115 
116 		valid = id = 0;
117 		if (isgid) {
118 			grp = getgrnam(ids[i].name);
119 			if (grp) {
120 				id = grp->gr_gid;
121 				valid = 1;
122 			}
123 		} else {
124 			usr = getpwnam(ids[i].name);
125 			if (usr) {
126 				id = usr->pw_uid;
127 				valid = 1;
128 			}
129 		}
130 
131 		/*
132 		 * (1) Empty names inherit.
133 		 * (2) Unknown identifier names inherit.
134 		 * (3) Wheel/root inherits.
135 		 * (4) Otherwise, use the local identifier.
136 		 */
137 
138 		if (ids[i].name[0] == '\0')
139 			ids[i].mapped = ids[i].id;
140 		else if (!valid)
141 			ids[i].mapped = ids[i].id;
142 		else
143 			ids[i].mapped = id;
144 
145 		LOG4("remapped identifier %s: %d -> %d",
146 		    ids[i].name, ids[i].id, ids[i].mapped);
147 	}
148 }
149 
150 /*
151  * If "id" is not part of the list of known users or groups (depending
152  * upon "isgid", add it.
153  * This also verifies that the name isn't too long.
154  * Does nothing with user/group zero.
155  * Return zero on failure, non-zero on success.
156  */
157 int
158 idents_add(int isgid, struct ident **ids, size_t *idsz, int32_t id)
159 {
160 	struct group	*grp;
161 	struct passwd	*usr;
162 	size_t		 i, sz;
163 	void		*pp;
164 	const char	*name;
165 
166 	if (id == 0)
167 		return 1;
168 
169 	for (i = 0; i < *idsz; i++)
170 		if ((*ids)[i].id == id)
171 			return 1;
172 
173 	/*
174 	 * Look up the reference in a type-specific way.
175 	 * Make sure that the name length is sane: we transmit it using
176 	 * a single byte.
177 	 */
178 
179 	assert(i == *idsz);
180 	if (isgid) {
181 		if ((grp = getgrgid((gid_t)id)) == NULL) {
182 			ERR("%d: unknown gid", id);
183 			return 0;
184 		}
185 		name = grp->gr_name;
186 	} else {
187 		if ((usr = getpwuid((uid_t)id)) == NULL) {
188 			ERR("%d: unknown uid", id);
189 			return 0;
190 		}
191 		name = usr->pw_name;
192 	}
193 
194 	if ((sz = strlen(name)) > UINT8_MAX) {
195 		ERRX("%d: name too long: %s", id, name);
196 		return 0;
197 	} else if (sz == 0) {
198 		ERRX("%d: zero-length name", id);
199 		return 0;
200 	}
201 
202 	/* Add the identifier to the array. */
203 
204 	pp = reallocarray(*ids, *idsz + 1, sizeof(struct ident));
205 	if (pp == NULL) {
206 		ERR("reallocarray");
207 		return 0;
208 	}
209 	*ids = pp;
210 	(*ids)[*idsz].id = id;
211 	(*ids)[*idsz].name = strdup(name);
212 	if ((*ids)[*idsz].name == NULL) {
213 		ERR("strdup");
214 		return 0;
215 	}
216 
217 	LOG4("adding identifier to list: %s (%u)",
218 	    (*ids)[*idsz].name, (*ids)[*idsz].id);
219 	(*idsz)++;
220 	return 1;
221 }
222 
223 /*
224  * Send a list of struct ident.
225  * See idents_recv().
226  * We should only do this if we're preserving gids/uids.
227  * Return zero on failure, non-zero on success.
228  */
229 int
230 idents_send(struct sess *sess,
231 	int fd, const struct ident *ids, size_t idsz)
232 {
233 	size_t	 i, sz;
234 
235 	for (i = 0; i < idsz; i++) {
236 		assert(ids[i].name != NULL);
237 		assert(ids[i].id != 0);
238 		sz = strlen(ids[i].name);
239 		assert(sz > 0 && sz <= UINT8_MAX);
240 		if (!io_write_uint(sess, fd, ids[i].id)) {
241 			ERRX1("io_write_uint");
242 			return 0;
243 		} else if (!io_write_byte(sess, fd, sz)) {
244 			ERRX1("io_write_byte");
245 			return 0;
246 		} else if (!io_write_buf(sess, fd, ids[i].name, sz)) {
247 			ERRX1("io_write_buf");
248 			return 0;
249 		}
250 	}
251 
252 	if (!io_write_int(sess, fd, 0)) {
253 		ERRX1("io_write_int");
254 		return 0;
255 	}
256 
257 	return 1;
258 }
259 
260 /*
261  * Receive a list of struct ident.
262  * See idents_send().
263  * We should only do this if we're preserving gids/uids.
264  * Return zero on failure, non-zero on success.
265  */
266 int
267 idents_recv(struct sess *sess,
268 	int fd, struct ident **ids, size_t *idsz)
269 {
270 	int32_t	 id;
271 	uint8_t	 sz;
272 	void	*pp;
273 
274 	for (;;) {
275 		if (!io_read_uint(sess, fd, &id)) {
276 			ERRX1("io_read_uint");
277 			return 0;
278 		} else if (id == 0)
279 			break;
280 
281 		pp = reallocarray(*ids,
282 			*idsz + 1, sizeof(struct ident));
283 		if (pp == NULL) {
284 			ERR("reallocarray");
285 			return 0;
286 		}
287 		*ids = pp;
288 		memset(&(*ids)[*idsz], 0, sizeof(struct ident));
289 
290 		/*
291 		 * When reading the size, warn if we get a size of zero.
292 		 * The spec doesn't allow this, but we might have a
293 		 * noncomformant or adversarial sender.
294 		 */
295 
296 		if (!io_read_byte(sess, fd, &sz)) {
297 			ERRX1("io_read_byte");
298 			return 0;
299 		} else if (sz == 0)
300 			WARNX("zero-length name in identifier list");
301 
302 		(*ids)[*idsz].id = id;
303 		(*ids)[*idsz].name = calloc(sz + 1, 1);
304 		if ((*ids)[*idsz].name == NULL) {
305 			ERR("calloc");
306 			return 0;
307 		}
308 		if (!io_read_buf(sess, fd, (*ids)[*idsz].name, sz)) {
309 			ERRX1("io_read_buf");
310 			return 0;
311 		}
312 		(*idsz)++;
313 	}
314 
315 	return 1;
316 }
317