diff options
-rw-r--r-- | usr.bin/rsync/extern.h | 15 | ||||
-rw-r--r-- | usr.bin/rsync/fargs.c | 6 | ||||
-rw-r--r-- | usr.bin/rsync/flist.c | 82 | ||||
-rw-r--r-- | usr.bin/rsync/ids.c | 168 | ||||
-rw-r--r-- | usr.bin/rsync/main.c | 9 |
5 files changed, 192 insertions, 88 deletions
diff --git a/usr.bin/rsync/extern.h b/usr.bin/rsync/extern.h index 3c2a78dc7cb..25dbc6420cd 100644 --- a/usr.bin/rsync/extern.h +++ b/usr.bin/rsync/extern.h @@ -1,4 +1,4 @@ -/* $Id: extern.h,v 1.8 2019/02/12 19:39:57 benno Exp $ */ +/* $Id: extern.h,v 1.9 2019/02/14 18:26:52 florian Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -105,6 +105,7 @@ struct opts { int preserve_perms; /* -p */ int preserve_links; /* -l */ int preserve_gids; /* -g */ + int preserve_uids; /* -u */ int del; /* --delete */ char *rsync_path; /* --rsync-path */ char *ssh_prog; /* --rsh or -e */ @@ -301,13 +302,15 @@ char *symlinkat_read(struct sess *, int, const char *); int sess_stats_send(struct sess *, int); int sess_stats_recv(struct sess *, int); -void idents_free(struct ident *, size_t); -void idents_gid_assign(struct sess *, +int idents_add(struct sess *, int, struct ident **, size_t *, + int32_t); +void idents_assign_gid(struct sess *, + struct flist *, size_t, const struct ident *, size_t); +void idents_assign_uid(struct sess *, struct flist *, size_t, const struct ident *, size_t); -void idents_gid_remap(struct sess *, struct ident *, size_t); -int idents_gid_add(struct sess *, struct ident **, size_t *, - gid_t); +void idents_free(struct ident *, size_t); int idents_recv(struct sess *, int, struct ident **, size_t *); +void idents_remap(struct sess *, int, struct ident *, size_t); int idents_send(struct sess *, int, const struct ident *, size_t); __END_DECLS diff --git a/usr.bin/rsync/fargs.c b/usr.bin/rsync/fargs.c index 2a35291f811..280782aa118 100644 --- a/usr.bin/rsync/fargs.c +++ b/usr.bin/rsync/fargs.c @@ -1,4 +1,4 @@ -/* $Id: fargs.c,v 1.9 2019/02/13 19:13:18 deraadt Exp $ */ +/* $Id: fargs.c,v 1.10 2019/02/14 18:26:52 florian Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -43,7 +43,7 @@ fargs_cmdline(struct sess *sess, const struct fargs *f) argsz += 1; /* dot separator */ argsz += 1; /* sink file */ argsz += 5; /* per-mode maximum */ - argsz += 11; /* shared args */ + argsz += 12; /* shared args */ argsz += 1; /* NULL pointer */ argsz += f->sourcesz; @@ -97,6 +97,8 @@ fargs_cmdline(struct sess *sess, const struct fargs *f) args[i++] = "-l"; if (sess->opts->dry_run) args[i++] = "-n"; + if (sess->opts->preserve_uids) + args[i++] = "-o"; if (sess->opts->preserve_perms) args[i++] = "-p"; if (sess->opts->recursive) diff --git a/usr.bin/rsync/flist.c b/usr.bin/rsync/flist.c index e207f90208b..bc7be5450bc 100644 --- a/usr.bin/rsync/flist.c +++ b/usr.bin/rsync/flist.c @@ -1,4 +1,4 @@ -/* $Id: flist.c,v 1.12 2019/02/12 19:39:57 benno Exp $ */ +/* $Id: flist.c,v 1.13 2019/02/14 18:26:52 florian Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -43,6 +43,7 @@ * information that affects subsequent transmissions. */ #define FLIST_MODE_SAME 0x0002 /* mode is repeat */ +#define FLIST_UID_SAME 0x0008 /* uid is repeat */ #define FLIST_GID_SAME 0x0010 /* gid is repeat */ #define FLIST_NAME_SAME 0x0020 /* name is repeat */ #define FLIST_NAME_LONG 0x0040 /* name >255 bytes */ @@ -241,11 +242,11 @@ int flist_send(struct sess *sess, int fdin, int fdout, const struct flist *fl, size_t flsz) { - size_t i, sz, gidsz = 0; + size_t i, sz, gidsz = 0, uidsz = 0; uint8_t flag; const struct flist *f; const char *fn; - struct ident *gids = NULL; + struct ident *gids = NULL, *uids = NULL; int rc = 0; /* Double-check that we've no pending multiplexed data. */ @@ -309,6 +310,19 @@ flist_send(struct sess *sess, int fdin, int fdout, const struct flist *fl, goto out; } + /* Conditional part: uid. */ + + if (sess->opts->preserve_uids) { + if (!io_write_int(sess, fdout, f->st.uid)) { + ERRX1(sess, "io_write_int"); + goto out; + } + if (!idents_add(sess, 0, &uids, &uidsz, f->st.uid)) { + ERRX1(sess, "idents_add"); + goto out; + } + } + /* Conditional part: gid. */ if (sess->opts->preserve_gids) { @@ -316,8 +330,8 @@ flist_send(struct sess *sess, int fdin, int fdout, const struct flist *fl, ERRX1(sess, "io_write_int"); goto out; } - if (!idents_gid_add(sess, &gids, &gidsz, f->st.gid)) { - ERRX1(sess, "idents_gid_add"); + if (!idents_add(sess, 1, &gids, &gidsz, f->st.gid)) { + ERRX1(sess, "idents_add"); goto out; } } @@ -349,7 +363,15 @@ flist_send(struct sess *sess, int fdin, int fdout, const struct flist *fl, goto out; } - /* Conditionally write gid list and terminator. */ + /* Conditionally write identifier lists. */ + + if (sess->opts->preserve_uids) { + LOG2(sess, "sending uid list: %zu", uidsz); + if (!idents_send(sess, fdout, uids, uidsz)) { + ERRX1(sess, "idents_send"); + goto out; + } + } if (sess->opts->preserve_gids) { LOG2(sess, "sending gid list: %zu", gidsz); @@ -362,6 +384,7 @@ flist_send(struct sess *sess, int fdin, int fdout, const struct flist *fl, rc = 1; out: idents_free(gids, gidsz); + idents_free(uids, uidsz); return rc; } @@ -540,12 +563,12 @@ flist_recv(struct sess *sess, int fd, struct flist **flp, size_t *sz) struct flist *fl = NULL; struct flist *ff; const struct flist *fflast = NULL; - size_t flsz = 0, flmax = 0, lsz, gidsz = 0; + size_t flsz = 0, flmax = 0, lsz, gidsz = 0, uidsz = 0; uint8_t flag; char last[MAXPATHLEN]; uint64_t lval; /* temporary values... */ int32_t ival; - struct ident *gids = NULL; + struct ident *gids = NULL, *uids = NULL; last[0] = '\0'; @@ -607,6 +630,23 @@ flist_recv(struct sess *sess, int fd, struct flist **flp, size_t *sz) } else ff->st.mode = fflast->st.mode; + /* Conditional part: uid. */ + + if (sess->opts->preserve_uids) { + if (!(FLIST_UID_SAME & flag)) { + if (!io_read_int(sess, fd, &ival)) { + ERRX1(sess, "io_read_int"); + goto out; + } + ff->st.uid = ival; + } else if (fflast == NULL) { + ERRX(sess, "same uid " + "without last entry"); + goto out; + } else + ff->st.uid = fflast->st.uid; + } + /* Conditional part: gid. */ if (sess->opts->preserve_gids) { @@ -655,10 +695,15 @@ flist_recv(struct sess *sess, int fd, struct flist **flp, size_t *sz) sess->total_size += ff->st.size; } - /* - * Now conditionally read the group list. - * We then remap all group identifiers to the local ids. - */ + /* Conditionally read the user/group list. */ + + if (sess->opts->preserve_uids) { + if (!idents_recv(sess, fd, &uids, &uidsz)) { + ERRX1(sess, "idents_recv"); + goto out; + } + LOG2(sess, "received uid list: %zu", uidsz); + } if (sess->opts->preserve_gids) { if (!idents_recv(sess, fd, &gids, &gidsz)) { @@ -676,18 +721,25 @@ flist_recv(struct sess *sess, int fd, struct flist **flp, size_t *sz) *sz = flsz; *flp = fl; - /* Lastly, remap and reassign group identifiers. */ + /* Conditionally remap and reassign identifiers. */ + + if (sess->opts->preserve_uids) { + idents_remap(sess, 0, uids, uidsz); + idents_assign_uid(sess, fl, flsz, uids, uidsz); + } if (sess->opts->preserve_gids) { - idents_gid_remap(sess, gids, gidsz); - idents_gid_assign(sess, fl, flsz, gids, gidsz); + idents_remap(sess, 1, gids, gidsz); + idents_assign_gid(sess, fl, flsz, gids, gidsz); } idents_free(gids, gidsz); + idents_free(uids, uidsz); return 1; out: flist_free(fl, flsz); idents_free(gids, gidsz); + idents_free(uids, uidsz); *sz = 0; *flp = NULL; return 0; diff --git a/usr.bin/rsync/ids.c b/usr.bin/rsync/ids.c index fa68b9c132e..fa16a44d803 100644 --- a/usr.bin/rsync/ids.c +++ b/usr.bin/rsync/ids.c @@ -1,4 +1,4 @@ -/* $Id: ids.c,v 1.4 2019/02/12 19:39:57 benno Exp $ */ +/* $Id: ids.c,v 1.5 2019/02/14 18:26:52 florian Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -17,6 +17,7 @@ #include <assert.h> #include <grp.h> #include <inttypes.h> +#include <pwd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -41,125 +42,169 @@ idents_free(struct ident *p, size_t sz) } /* - * Given a list of files with the groups as set by the sender, re-assign - * the groups from the list of remapped group identifiers. - * Don't ever remap group wheel. + * Given a list of files with the identifiers as set by the sender, + * re-assign the identifiers from the list of remapped ones. + * Don't ever remap wheel/root. */ void -idents_gid_assign(struct sess *sess, struct flist *fl, size_t flsz, - const struct ident *gids, size_t gidsz) +idents_assign_gid(struct sess *sess, struct flist *fl, size_t flsz, + const struct ident *ids, size_t idsz) { size_t i, j; for (i = 0; i < flsz; i++) { - if (0 == fl[i].st.gid) + if (fl[i].st.gid == 0) continue; - for (j = 0; j < gidsz; j++) - if ((int32_t)fl[i].st.gid == gids[j].id) + for (j = 0; j < idsz; j++) + if ((int32_t)fl[i].st.gid == ids[j].id) break; - assert(j < gidsz); - fl[i].st.gid = gids[j].mapped; + assert(j < idsz); + fl[i].st.gid = ids[j].mapped; } } /* - * Given a list of groups from the remote host, fill in our local + * Like idents_assign_gid(). + */ +void +idents_assign_uid(struct sess *sess, struct flist *fl, size_t flsz, + const struct ident *ids, size_t idsz) +{ + size_t i, j; + + for (i = 0; i < flsz; i++) { + if (fl[i].st.uid == 0) + continue; + for (j = 0; j < idsz; j++) + if ((int32_t)fl[i].st.uid == ids[j].id) + break; + assert(j < idsz); + fl[i].st.uid = ids[j].mapped; + } +} + +/* + * Given a list of identifiers from the remote host, fill in our local * identifiers of the same names. - * Use the remote numeric identifier if we can't find the group OR the - * group has identifier zero. - * FIXME: what happens if we don't find the local group (we should - * really warn about this), but the remote group identifier maps into a - * different group name for us? + * Use the remote numeric identifier if we can't find the identifier OR + * the identifier is zero (wheel/root). + * FIXME: what happens if we don't find the local identifier (we should + * really warn about this), but the remote identifier maps into a + * different name for us? * These are pretty unexpected things for rsync to do. * Another FIXME because we shouldn't let that happen even though the * reference rsync does. */ void -idents_gid_remap(struct sess *sess, struct ident *gids, size_t gidsz) +idents_remap(struct sess *sess, int isgid, struct ident *ids, size_t idsz) { size_t i; struct group *grp; + struct passwd *usr; + int32_t id; - for (i = 0; i < gidsz; i++) { - assert(gids[i].id != 0); + for (i = 0; i < idsz; i++) { + assert(ids[i].id != 0); + + /* Start by getting our local representation. */ + + if (isgid) + id = (grp = getgrnam(ids[i].name)) == NULL ? + -1 : grp->gr_gid; + else + id = (usr = getpwnam(ids[i].name)) == NULL ? + -1 : usr->pw_uid; /* * (1) Empty names inherit. - * (2) Unknown group names inherit. - * (3) Group wheel inherits. + * (2) Unknown identifier names inherit. + * (3) Wheel/root inherits. * (4) Otherwise, use the local identifier. */ - if (gids[i].name[0] == '\0') - gids[i].mapped = gids[i].id; - else if ((grp = getgrnam(gids[i].name)) == NULL) - gids[i].mapped = gids[i].id; - else if (grp->gr_gid == 0) - gids[i].mapped = gids[i].id; + if (ids[i].name[0] == '\0') + ids[i].mapped = ids[i].id; + else if (id <= 0) + ids[i].mapped = ids[i].id; else - gids[i].mapped = grp->gr_gid; + ids[i].mapped = id; - LOG4(sess, "remapped group %s: %" PRId32 " -> %" PRId32, - gids[i].name, gids[i].id, gids[i].mapped); + LOG4(sess, "remapped identifier %s: %" PRId32 " -> %" PRId32, + ids[i].name, ids[i].id, ids[i].mapped); } } /* - * If "gid" is not part of the list of known groups, add it. - * This also verifies that the group name isn't too long. - * Does nothing with group zero. + * If "id" is not part of the list of known users or groups (depending + * upon "isgid", add it. + * This also verifies that the name isn't too long. + * Does nothing with user/group zero. * Return zero on failure, non-zero on success. */ int -idents_gid_add(struct sess *sess, struct ident **gids, size_t *gidsz, gid_t gid) +idents_add(struct sess *sess, int isgid, + struct ident **ids, size_t *idsz, int32_t id) { struct group *grp; + struct passwd *usr; size_t i, sz; void *pp; + const char *name; - if (gid == 0) + if (id == 0) return 1; - for (i = 0; i < *gidsz; i++) - if ((*gids)[i].id == (int32_t)gid) + for (i = 0; i < *idsz; i++) + if ((*ids)[i].id == id) return 1; /* - * Look us up in /etc/group. - * Make sure that the group name length is sane: we transmit it - * using a single byte. + * Look up the reference in a type-specific way. + * Make sure that the name length is sane: we transmit it using + * a single byte. */ - assert(i == *gidsz); - if ((grp = getgrgid(gid)) == NULL) { - ERR(sess, "%u: unknown gid", gid); - return 0; - } else if ((sz = strlen(grp->gr_name)) > UINT8_MAX) { - ERRX(sess, "%u: group name too long: %s", gid, grp->gr_name); + assert(i == *idsz); + if (isgid) { + if ((grp = getgrgid((gid_t)id)) == NULL) { + ERR(sess, "%" PRId32 ": unknown gid", id); + return 0; + } + name = grp->gr_name; + } else { + if ((usr = getpwuid((uid_t)id)) == NULL) { + ERR(sess, "%" PRId32 ": unknown uid", id); + return 0; + } + name = usr->pw_name; + } + + if ((sz = strlen(name)) > UINT8_MAX) { + ERRX(sess, "%" PRId32 ": name too long: %s", id, name); return 0; } else if (sz == 0) { - ERRX(sess, "%u: group name zero-length", gid); + ERRX(sess, "%" PRId32 ": zero-length name", id); return 0; } - /* Add the group to the array. */ + /* Add the identifier to the array. */ - pp = reallocarray(*gids, *gidsz + 1, sizeof(struct ident)); + pp = reallocarray(*ids, *idsz + 1, sizeof(struct ident)); if (pp == NULL) { ERR(sess, "reallocarray"); return 0; } - *gids = pp; - (*gids)[*gidsz].id = gid; - (*gids)[*gidsz].name = strdup(grp->gr_name); - if (NULL == (*gids)[*gidsz].name) { + *ids = pp; + (*ids)[*idsz].id = id; + (*ids)[*idsz].name = strdup(name); + if ((*ids)[*idsz].name == NULL) { ERR(sess, "strdup"); return 0; } - LOG4(sess, "adding group to list: %s (%u)", - (*gids)[*gidsz].name, (*gids)[*gidsz].id); - (*gidsz)++; + LOG4(sess, "adding identifier to list: %s (%u)", + (*ids)[*idsz].name, (*ids)[*idsz].id); + (*idsz)++; return 1; } @@ -231,8 +276,7 @@ idents_recv(struct sess *sess, memset(&(*ids)[*idsz], 0, sizeof(struct ident)); /* - * When reading the size, warn if we get a group size of - * zero. + * When reading the size, warn if we get a size of zero. * The spec doesn't allow this, but we might have a * noncomformant or adversarial sender. */ @@ -240,9 +284,9 @@ idents_recv(struct sess *sess, if (!io_read_byte(sess, fd, &sz)) { ERRX1(sess, "io_read_byte"); return 0; - } else if (0 == sz) - WARNX(sess, "zero-length group name " - "in group list"); + } else if (sz == 0) + WARNX(sess, "zero-length name " + "in identifier list"); (*ids)[*idsz].id = id; (*ids)[*idsz].name = calloc(sz + 1, 1); diff --git a/usr.bin/rsync/main.c b/usr.bin/rsync/main.c index 38331072388..d9def41c79f 100644 --- a/usr.bin/rsync/main.c +++ b/usr.bin/rsync/main.c @@ -1,4 +1,4 @@ -/* $Id: main.c,v 1.12 2019/02/12 22:19:05 deraadt Exp $ */ +/* $Id: main.c,v 1.13 2019/02/14 18:26:52 florian Exp $ */ /* * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> * @@ -309,7 +309,7 @@ main(int argc, char *argv[]) memset(&opts, 0, sizeof(struct opts)); - while ((c = getopt_long(argc, argv, "e:glnprtv", lopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "e:glnoprtv", lopts, NULL)) != -1) { switch (c) { case 'e': opts.ssh_prog = optarg; @@ -324,6 +324,9 @@ main(int argc, char *argv[]) case 'n': opts.dry_run = 1; break; + case 'o': + opts.preserve_uids = 1; + break; case 'p': opts.preserve_perms = 1; break; @@ -455,7 +458,7 @@ main(int argc, char *argv[]) close(fds[0]); return c ? EXIT_SUCCESS : EXIT_FAILURE; usage: - fprintf(stderr, "usage: %s [-glnprtv] " + fprintf(stderr, "usage: %s [-glnoprtv] " "[-e ssh-prog] [--delete] [--rsync-path=prog] src ... dst\n", getprogname()); return EXIT_FAILURE; |