version 1.4, 2019/02/12 19:39:57 |
version 1.5, 2019/02/14 18:26:52 |
|
|
#include <assert.h> |
#include <assert.h> |
#include <grp.h> |
#include <grp.h> |
#include <inttypes.h> |
#include <inttypes.h> |
|
#include <pwd.h> |
#include <stdio.h> |
#include <stdio.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
|
} |
} |
|
|
/* |
/* |
* Given a list of files with the groups as set by the sender, re-assign |
* Given a list of files with the identifiers as set by the sender, |
* the groups from the list of remapped group identifiers. |
* re-assign the identifiers from the list of remapped ones. |
* Don't ever remap group wheel. |
* Don't ever remap wheel/root. |
*/ |
*/ |
void |
void |
idents_gid_assign(struct sess *sess, struct flist *fl, size_t flsz, |
idents_assign_gid(struct sess *sess, struct flist *fl, size_t flsz, |
const struct ident *gids, size_t gidsz) |
const struct ident *ids, size_t idsz) |
{ |
{ |
size_t i, j; |
size_t i, j; |
|
|
for (i = 0; i < flsz; i++) { |
for (i = 0; i < flsz; i++) { |
if (0 == fl[i].st.gid) |
if (fl[i].st.gid == 0) |
continue; |
continue; |
for (j = 0; j < gidsz; j++) |
for (j = 0; j < idsz; j++) |
if ((int32_t)fl[i].st.gid == gids[j].id) |
if ((int32_t)fl[i].st.gid == ids[j].id) |
break; |
break; |
assert(j < gidsz); |
assert(j < idsz); |
fl[i].st.gid = gids[j].mapped; |
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. |
* identifiers of the same names. |
* Use the remote numeric identifier if we can't find the group OR the |
* Use the remote numeric identifier if we can't find the identifier OR |
* group has identifier zero. |
* the identifier is zero (wheel/root). |
* FIXME: what happens if we don't find the local group (we should |
* FIXME: what happens if we don't find the local identifier (we should |
* really warn about this), but the remote group identifier maps into a |
* really warn about this), but the remote identifier maps into a |
* different group name for us? |
* different name for us? |
* These are pretty unexpected things for rsync to do. |
* These are pretty unexpected things for rsync to do. |
* Another FIXME because we shouldn't let that happen even though the |
* Another FIXME because we shouldn't let that happen even though the |
* reference rsync does. |
* reference rsync does. |
*/ |
*/ |
void |
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; |
size_t i; |
struct group *grp; |
struct group *grp; |
|
struct passwd *usr; |
|
int32_t id; |
|
|
for (i = 0; i < gidsz; i++) { |
for (i = 0; i < idsz; i++) { |
assert(gids[i].id != 0); |
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. |
* (1) Empty names inherit. |
* (2) Unknown group names inherit. |
* (2) Unknown identifier names inherit. |
* (3) Group wheel inherits. |
* (3) Wheel/root inherits. |
* (4) Otherwise, use the local identifier. |
* (4) Otherwise, use the local identifier. |
*/ |
*/ |
|
|
if (gids[i].name[0] == '\0') |
if (ids[i].name[0] == '\0') |
gids[i].mapped = gids[i].id; |
ids[i].mapped = ids[i].id; |
else if ((grp = getgrnam(gids[i].name)) == NULL) |
else if (id <= 0) |
gids[i].mapped = gids[i].id; |
ids[i].mapped = ids[i].id; |
else if (grp->gr_gid == 0) |
|
gids[i].mapped = gids[i].id; |
|
else |
else |
gids[i].mapped = grp->gr_gid; |
ids[i].mapped = id; |
|
|
LOG4(sess, "remapped group %s: %" PRId32 " -> %" PRId32, |
LOG4(sess, "remapped identifier %s: %" PRId32 " -> %" PRId32, |
gids[i].name, gids[i].id, gids[i].mapped); |
ids[i].name, ids[i].id, ids[i].mapped); |
} |
} |
} |
} |
|
|
/* |
/* |
* If "gid" is not part of the list of known groups, add it. |
* If "id" is not part of the list of known users or groups (depending |
* This also verifies that the group name isn't too long. |
* upon "isgid", add it. |
* Does nothing with group zero. |
* This also verifies that the name isn't too long. |
|
* Does nothing with user/group zero. |
* Return zero on failure, non-zero on success. |
* Return zero on failure, non-zero on success. |
*/ |
*/ |
int |
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 group *grp; |
|
struct passwd *usr; |
size_t i, sz; |
size_t i, sz; |
void *pp; |
void *pp; |
|
const char *name; |
|
|
if (gid == 0) |
if (id == 0) |
return 1; |
return 1; |
|
|
for (i = 0; i < *gidsz; i++) |
for (i = 0; i < *idsz; i++) |
if ((*gids)[i].id == (int32_t)gid) |
if ((*ids)[i].id == id) |
return 1; |
return 1; |
|
|
/* |
/* |
* Look us up in /etc/group. |
* Look up the reference in a type-specific way. |
* Make sure that the group name length is sane: we transmit it |
* Make sure that the name length is sane: we transmit it using |
* using a single byte. |
* a single byte. |
*/ |
*/ |
|
|
assert(i == *gidsz); |
assert(i == *idsz); |
if ((grp = getgrgid(gid)) == NULL) { |
if (isgid) { |
ERR(sess, "%u: unknown gid", gid); |
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; |
return 0; |
} else if ((sz = strlen(grp->gr_name)) > UINT8_MAX) { |
|
ERRX(sess, "%u: group name too long: %s", gid, grp->gr_name); |
|
return 0; |
|
} else if (sz == 0) { |
} else if (sz == 0) { |
ERRX(sess, "%u: group name zero-length", gid); |
ERRX(sess, "%" PRId32 ": zero-length name", id); |
return 0; |
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) { |
if (pp == NULL) { |
ERR(sess, "reallocarray"); |
ERR(sess, "reallocarray"); |
return 0; |
return 0; |
} |
} |
*gids = pp; |
*ids = pp; |
(*gids)[*gidsz].id = gid; |
(*ids)[*idsz].id = id; |
(*gids)[*gidsz].name = strdup(grp->gr_name); |
(*ids)[*idsz].name = strdup(name); |
if (NULL == (*gids)[*gidsz].name) { |
if ((*ids)[*idsz].name == NULL) { |
ERR(sess, "strdup"); |
ERR(sess, "strdup"); |
return 0; |
return 0; |
} |
} |
|
|
LOG4(sess, "adding group to list: %s (%u)", |
LOG4(sess, "adding identifier to list: %s (%u)", |
(*gids)[*gidsz].name, (*gids)[*gidsz].id); |
(*ids)[*idsz].name, (*ids)[*idsz].id); |
(*gidsz)++; |
(*idsz)++; |
return 1; |
return 1; |
} |
} |
|
|
|
|
memset(&(*ids)[*idsz], 0, sizeof(struct ident)); |
memset(&(*ids)[*idsz], 0, sizeof(struct ident)); |
|
|
/* |
/* |
* When reading the size, warn if we get a group size of |
* When reading the size, warn if we get a size of zero. |
* zero. |
|
* The spec doesn't allow this, but we might have a |
* The spec doesn't allow this, but we might have a |
* noncomformant or adversarial sender. |
* noncomformant or adversarial sender. |
*/ |
*/ |
|
|
if (!io_read_byte(sess, fd, &sz)) { |
if (!io_read_byte(sess, fd, &sz)) { |
ERRX1(sess, "io_read_byte"); |
ERRX1(sess, "io_read_byte"); |
return 0; |
return 0; |
} else if (0 == sz) |
} else if (sz == 0) |
WARNX(sess, "zero-length group name " |
WARNX(sess, "zero-length name " |
"in group list"); |
"in identifier list"); |
|
|
(*ids)[*idsz].id = id; |
(*ids)[*idsz].id = id; |
(*ids)[*idsz].name = calloc(sz + 1, 1); |
(*ids)[*idsz].name = calloc(sz + 1, 1); |