Annotation of src/usr.bin/rsync/ids.c, Revision 1.9
1.9 ! deraadt 1: /* $Id: ids.c,v 1.8 2019/02/21 22:13:43 benno Exp $ */
1.1 benno 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>
1.5 florian 20: #include <pwd.h>
1.1 benno 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:
1.3 benno 37: if (p == NULL)
1.1 benno 38: return;
39: for (i = 0; i < sz; i++)
40: free(p[i].name);
41: free(p);
42: }
43:
44: /*
1.5 florian 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.
1.8 benno 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.
1.3 benno 50: */
51: void
1.5 florian 52: idents_assign_gid(struct sess *sess, struct flist *fl, size_t flsz,
53: const struct ident *ids, size_t idsz)
1.3 benno 54: {
55: size_t i, j;
56:
1.7 benno 57: assert(!sess->opts->numeric_ids);
58:
1.3 benno 59: for (i = 0; i < flsz; i++) {
1.5 florian 60: if (fl[i].st.gid == 0)
1.3 benno 61: continue;
1.5 florian 62: for (j = 0; j < idsz; j++)
63: if ((int32_t)fl[i].st.gid == ids[j].id)
1.3 benno 64: break;
1.8 benno 65: if (j < idsz)
66: fl[i].st.gid = ids[j].mapped;
1.3 benno 67: }
68: }
69:
70: /*
1.5 florian 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:
1.7 benno 79: assert(!sess->opts->numeric_ids);
80:
1.5 florian 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;
1.8 benno 87: if (j < idsz)
88: fl[i].st.uid = ids[j].mapped;
1.5 florian 89: }
90: }
91:
92: /*
93: * Given a list of identifiers from the remote host, fill in our local
1.1 benno 94: * identifiers of the same names.
1.6 benno 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.
1.1 benno 99: */
100: void
1.5 florian 101: idents_remap(struct sess *sess, int isgid, struct ident *ids, size_t idsz)
1.1 benno 102: {
1.4 benno 103: size_t i;
1.1 benno 104: struct group *grp;
1.5 florian 105: struct passwd *usr;
106: int32_t id;
1.6 benno 107:
1.7 benno 108: assert(!sess->opts->numeric_ids);
1.1 benno 109:
1.5 florian 110: for (i = 0; i < idsz; i++) {
111: assert(ids[i].id != 0);
112:
113: /* Start by getting our local representation. */
114:
115: if (isgid)
116: id = (grp = getgrnam(ids[i].name)) == NULL ?
117: -1 : grp->gr_gid;
118: else
119: id = (usr = getpwnam(ids[i].name)) == NULL ?
120: -1 : usr->pw_uid;
1.3 benno 121:
1.4 benno 122: /*
1.3 benno 123: * (1) Empty names inherit.
1.5 florian 124: * (2) Unknown identifier names inherit.
125: * (3) Wheel/root inherits.
1.3 benno 126: * (4) Otherwise, use the local identifier.
127: */
128:
1.5 florian 129: if (ids[i].name[0] == '\0')
130: ids[i].mapped = ids[i].id;
131: else if (id <= 0)
132: ids[i].mapped = ids[i].id;
1.1 benno 133: else
1.5 florian 134: ids[i].mapped = id;
1.2 benno 135:
1.9 ! deraadt 136: LOG4(sess, "remapped identifier %s: %d -> %d",
! 137: ids[i].name, ids[i].id, ids[i].mapped);
1.1 benno 138: }
139: }
140:
141: /*
1.5 florian 142: * If "id" is not part of the list of known users or groups (depending
143: * upon "isgid", add it.
144: * This also verifies that the name isn't too long.
145: * Does nothing with user/group zero.
1.1 benno 146: * Return zero on failure, non-zero on success.
147: */
148: int
1.5 florian 149: idents_add(struct sess *sess, int isgid,
150: struct ident **ids, size_t *idsz, int32_t id)
1.1 benno 151: {
152: struct group *grp;
1.5 florian 153: struct passwd *usr;
1.1 benno 154: size_t i, sz;
155: void *pp;
1.5 florian 156: const char *name;
1.1 benno 157:
1.5 florian 158: if (id == 0)
1.3 benno 159: return 1;
160:
1.5 florian 161: for (i = 0; i < *idsz; i++)
162: if ((*ids)[i].id == id)
1.1 benno 163: return 1;
164:
1.4 benno 165: /*
1.5 florian 166: * Look up the reference in a type-specific way.
167: * Make sure that the name length is sane: we transmit it using
168: * a single byte.
1.1 benno 169: */
170:
1.5 florian 171: assert(i == *idsz);
172: if (isgid) {
173: if ((grp = getgrgid((gid_t)id)) == NULL) {
1.9 ! deraadt 174: ERR(sess, "%d: unknown gid", id);
1.5 florian 175: return 0;
176: }
177: name = grp->gr_name;
178: } else {
179: if ((usr = getpwuid((uid_t)id)) == NULL) {
1.9 ! deraadt 180: ERR(sess, "%d: unknown uid", id);
1.5 florian 181: return 0;
182: }
183: name = usr->pw_name;
184: }
185:
186: if ((sz = strlen(name)) > UINT8_MAX) {
1.9 ! deraadt 187: ERRX(sess, "%d: name too long: %s", id, name);
1.1 benno 188: return 0;
1.3 benno 189: } else if (sz == 0) {
1.9 ! deraadt 190: ERRX(sess, "%d: zero-length name", id);
1.1 benno 191: return 0;
192: }
193:
1.5 florian 194: /* Add the identifier to the array. */
1.1 benno 195:
1.5 florian 196: pp = reallocarray(*ids, *idsz + 1, sizeof(struct ident));
1.3 benno 197: if (pp == NULL) {
1.1 benno 198: ERR(sess, "reallocarray");
199: return 0;
200: }
1.5 florian 201: *ids = pp;
202: (*ids)[*idsz].id = id;
203: (*ids)[*idsz].name = strdup(name);
204: if ((*ids)[*idsz].name == NULL) {
1.1 benno 205: ERR(sess, "strdup");
206: return 0;
207: }
208:
1.5 florian 209: LOG4(sess, "adding identifier to list: %s (%u)",
1.9 ! deraadt 210: (*ids)[*idsz].name, (*ids)[*idsz].id);
1.5 florian 211: (*idsz)++;
1.1 benno 212: return 1;
213: }
214:
215: /*
216: * Send a list of struct ident.
217: * See idents_recv().
218: * We should only do this if we're preserving gids/uids.
219: * Return zero on failure, non-zero on success.
220: */
221: int
1.4 benno 222: idents_send(struct sess *sess,
1.1 benno 223: int fd, const struct ident *ids, size_t idsz)
224: {
225: size_t i, sz;
226:
227: for (i = 0; i < idsz; i++) {
1.3 benno 228: assert(ids[i].name != NULL);
229: assert(ids[i].id != 0);
1.1 benno 230: sz = strlen(ids[i].name);
231: assert(sz > 0 && sz <= UINT8_MAX);
232: if (!io_write_int(sess, fd, ids[i].id)) {
233: ERRX1(sess, "io_write_int");
234: return 0;
235: } else if (!io_write_byte(sess, fd, sz)) {
236: ERRX1(sess, "io_write_byte");
237: return 0;
238: } else if (!io_write_buf(sess, fd, ids[i].name, sz)) {
239: ERRX1(sess, "io_write_byte");
240: return 0;
241: }
242: }
243:
244: if (!io_write_int(sess, fd, 0)) {
245: ERRX1(sess, "io_write_int");
246: return 0;
247: }
248:
249: return 1;
250: }
251:
252: /*
253: * Receive a list of struct ident.
254: * See idents_send().
255: * We should only do this if we're preserving gids/uids.
256: * Return zero on failure, non-zero on success.
257: */
258: int
259: idents_recv(struct sess *sess,
260: int fd, struct ident **ids, size_t *idsz)
261: {
262: int32_t id;
263: uint8_t sz;
264: void *pp;
265:
266: for (;;) {
267: if (!io_read_int(sess, fd, &id)) {
268: ERRX1(sess, "io_read_int");
269: return 0;
1.3 benno 270: } else if (id == 0)
1.1 benno 271: break;
1.4 benno 272:
273: pp = reallocarray(*ids,
1.1 benno 274: *idsz + 1, sizeof(struct ident));
1.3 benno 275: if (pp == NULL) {
1.1 benno 276: ERR(sess, "reallocarray");
277: return 0;
278: }
279: *ids = pp;
280: memset(&(*ids)[*idsz], 0, sizeof(struct ident));
1.3 benno 281:
282: /*
1.5 florian 283: * When reading the size, warn if we get a size of zero.
1.3 benno 284: * The spec doesn't allow this, but we might have a
285: * noncomformant or adversarial sender.
286: */
287:
1.1 benno 288: if (!io_read_byte(sess, fd, &sz)) {
289: ERRX1(sess, "io_read_byte");
290: return 0;
1.5 florian 291: } else if (sz == 0)
1.9 ! deraadt 292: WARNX(sess, "zero-length name in identifier list");
1.3 benno 293:
1.1 benno 294: (*ids)[*idsz].id = id;
295: (*ids)[*idsz].name = calloc(sz + 1, 1);
1.3 benno 296: if ((*ids)[*idsz].name == NULL) {
1.1 benno 297: ERR(sess, "calloc");
298: return 0;
299: }
300: if (!io_read_buf(sess, fd, (*ids)[*idsz].name, sz)) {
301: ERRX1(sess, "io_read_buf");
302: return 0;
303: }
304: (*idsz)++;
305: }
306:
307: return 1;
308: }