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