1 /* $OpenBSD: ids.c,v 1.15 2021/06/30 13:10:04 claudio 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_add().
30 * Does nothing if the pointer is NULL.
31 */
32 void
idents_free(struct ident * p,size_t sz)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
idents_assign_gid(struct sess * sess,struct flist * fl,size_t flsz,const struct ident * ids,size_t idsz)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
idents_assign_uid(struct sess * sess,struct flist * fl,size_t flsz,const struct ident * ids,size_t idsz)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
idents_remap(struct sess * sess,int isgid,struct ident * ids,size_t idsz)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
idents_add(int isgid,struct ident ** ids,size_t * idsz,int32_t id)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
idents_send(struct sess * sess,int fd,const struct ident * ids,size_t idsz)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
idents_recv(struct sess * sess,int fd,struct ident ** ids,size_t * idsz)267 idents_recv(struct sess *sess,
268 int fd, struct ident **ids, size_t *idsz)
269 {
270 uint32_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 assert(id < INT32_MAX);
303 (*ids)[*idsz].id = id;
304 (*ids)[*idsz].name = calloc(sz + 1, 1);
305 if ((*ids)[*idsz].name == NULL) {
306 ERR("calloc");
307 return 0;
308 }
309 if (!io_read_buf(sess, fd, (*ids)[*idsz].name, sz)) {
310 ERRX1("io_read_buf");
311 return 0;
312 }
313 (*idsz)++;
314 }
315
316 return 1;
317 }
318