summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr.bin/rsync/extern.h15
-rw-r--r--usr.bin/rsync/fargs.c6
-rw-r--r--usr.bin/rsync/flist.c82
-rw-r--r--usr.bin/rsync/ids.c168
-rw-r--r--usr.bin/rsync/main.c9
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;