File: [local] / src / usr.bin / rsync / ids.c (download)
Revision 1.15, Wed Jun 30 13:10:04 2021 UTC (2 years, 10 months ago) by claudio
Branch: MAIN
CVS Tags: OPENBSD_7_5_BASE, OPENBSD_7_5, OPENBSD_7_4_BASE, OPENBSD_7_4, OPENBSD_7_3_BASE, OPENBSD_7_3, OPENBSD_7_2_BASE, OPENBSD_7_2, OPENBSD_7_1_BASE, OPENBSD_7_1, OPENBSD_7_0_BASE, OPENBSD_7_0, HEAD Changes since 1.14: +1 -1 lines
Replace Id: tags with OpenBSD: tags
OK deraadt@
|
/* $OpenBSD: ids.c,v 1.15 2021/06/30 13:10:04 claudio Exp $ */
/*
* Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <assert.h>
#include <grp.h>
#include <inttypes.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "extern.h"
/*
* Free a list of struct ident previously allocated with idents_add().
* Does nothing if the pointer is NULL.
*/
void
idents_free(struct ident *p, size_t sz)
{
size_t i;
if (p == NULL)
return;
for (i = 0; i < sz; i++)
free(p[i].name);
free(p);
}
/*
* 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.
* If we can't find the gid in the list (when, e.g., being sent by the
* daemon), don't try to map it.
*/
void
idents_assign_gid(struct sess *sess, struct flist *fl, size_t flsz,
const struct ident *ids, size_t idsz)
{
size_t i, j;
assert(!sess->opts->numeric_ids);
for (i = 0; i < flsz; i++) {
if (fl[i].st.gid == 0)
continue;
for (j = 0; j < idsz; j++)
if ((int32_t)fl[i].st.gid == ids[j].id)
break;
if (j < idsz)
fl[i].st.gid = ids[j].mapped;
}
}
/*
* 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;
assert(!sess->opts->numeric_ids);
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;
if (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 identifier OR the
* identifier is zero (wheel/root).
* FIXME: we should at least warn when we can't find an identifier, use
* the numeric id, and that numeric id is assigned to a different user.
*/
void
idents_remap(struct sess *sess, int isgid, struct ident *ids, size_t idsz)
{
size_t i;
struct group *grp;
struct passwd *usr;
uint32_t id;
int valid;
assert(!sess->opts->numeric_ids);
for (i = 0; i < idsz; i++) {
assert(ids[i].id != 0);
/* Start by getting our local representation. */
valid = id = 0;
if (isgid) {
grp = getgrnam(ids[i].name);
if (grp) {
id = grp->gr_gid;
valid = 1;
}
} else {
usr = getpwnam(ids[i].name);
if (usr) {
id = usr->pw_uid;
valid = 1;
}
}
/*
* (1) Empty names inherit.
* (2) Unknown identifier names inherit.
* (3) Wheel/root inherits.
* (4) Otherwise, use the local identifier.
*/
if (ids[i].name[0] == '\0')
ids[i].mapped = ids[i].id;
else if (!valid)
ids[i].mapped = ids[i].id;
else
ids[i].mapped = id;
LOG4("remapped identifier %s: %d -> %d",
ids[i].name, ids[i].id, ids[i].mapped);
}
}
/*
* 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_add(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 (id == 0)
return 1;
for (i = 0; i < *idsz; i++)
if ((*ids)[i].id == id)
return 1;
/*
* 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 == *idsz);
if (isgid) {
if ((grp = getgrgid((gid_t)id)) == NULL) {
ERR("%d: unknown gid", id);
return 0;
}
name = grp->gr_name;
} else {
if ((usr = getpwuid((uid_t)id)) == NULL) {
ERR("%d: unknown uid", id);
return 0;
}
name = usr->pw_name;
}
if ((sz = strlen(name)) > UINT8_MAX) {
ERRX("%d: name too long: %s", id, name);
return 0;
} else if (sz == 0) {
ERRX("%d: zero-length name", id);
return 0;
}
/* Add the identifier to the array. */
pp = reallocarray(*ids, *idsz + 1, sizeof(struct ident));
if (pp == NULL) {
ERR("reallocarray");
return 0;
}
*ids = pp;
(*ids)[*idsz].id = id;
(*ids)[*idsz].name = strdup(name);
if ((*ids)[*idsz].name == NULL) {
ERR("strdup");
return 0;
}
LOG4("adding identifier to list: %s (%u)",
(*ids)[*idsz].name, (*ids)[*idsz].id);
(*idsz)++;
return 1;
}
/*
* Send a list of struct ident.
* See idents_recv().
* We should only do this if we're preserving gids/uids.
* Return zero on failure, non-zero on success.
*/
int
idents_send(struct sess *sess,
int fd, const struct ident *ids, size_t idsz)
{
size_t i, sz;
for (i = 0; i < idsz; i++) {
assert(ids[i].name != NULL);
assert(ids[i].id != 0);
sz = strlen(ids[i].name);
assert(sz > 0 && sz <= UINT8_MAX);
if (!io_write_uint(sess, fd, ids[i].id)) {
ERRX1("io_write_uint");
return 0;
} else if (!io_write_byte(sess, fd, sz)) {
ERRX1("io_write_byte");
return 0;
} else if (!io_write_buf(sess, fd, ids[i].name, sz)) {
ERRX1("io_write_buf");
return 0;
}
}
if (!io_write_int(sess, fd, 0)) {
ERRX1("io_write_int");
return 0;
}
return 1;
}
/*
* Receive a list of struct ident.
* See idents_send().
* We should only do this if we're preserving gids/uids.
* Return zero on failure, non-zero on success.
*/
int
idents_recv(struct sess *sess,
int fd, struct ident **ids, size_t *idsz)
{
uint32_t id;
uint8_t sz;
void *pp;
for (;;) {
if (!io_read_uint(sess, fd, &id)) {
ERRX1("io_read_uint");
return 0;
} else if (id == 0)
break;
pp = reallocarray(*ids,
*idsz + 1, sizeof(struct ident));
if (pp == NULL) {
ERR("reallocarray");
return 0;
}
*ids = pp;
memset(&(*ids)[*idsz], 0, sizeof(struct ident));
/*
* 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.
*/
if (!io_read_byte(sess, fd, &sz)) {
ERRX1("io_read_byte");
return 0;
} else if (sz == 0)
WARNX("zero-length name in identifier list");
assert(id < INT32_MAX);
(*ids)[*idsz].id = id;
(*ids)[*idsz].name = calloc(sz + 1, 1);
if ((*ids)[*idsz].name == NULL) {
ERR("calloc");
return 0;
}
if (!io_read_buf(sess, fd, (*ids)[*idsz].name, sz)) {
ERRX1("io_read_buf");
return 0;
}
(*idsz)++;
}
return 1;
}