Commit | Line | Data |
---|---|---|
ade7292a | 1 | /* |
0f78b815 WD |
2 | * Handle the mapping of uid/gid and user/group names between systems. |
3 | * | |
4 | * Copyright (C) 1996 Andrew Tridgell | |
5 | * Copyright (C) 1996 Paul Mackerras | |
b3bf9b9d | 6 | * Copyright (C) 2004-2009 Wayne Davison |
0f78b815 WD |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
8e41b68e WD |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 3 of the License, or | |
11 | * (at your option) any later version. | |
0f78b815 WD |
12 | * |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
e7c67065 | 18 | * You should have received a copy of the GNU General Public License along |
4fd842f9 | 19 | * with this program; if not, visit the http://fsf.org website. |
0f78b815 WD |
20 | */ |
21 | ||
22 | /* If the source username/group does not exist on the target then use | |
23 | * the numeric IDs. Never do any mapping for uid=0 or gid=0 as these | |
24 | * are special. */ | |
f6c34742 AT |
25 | |
26 | #include "rsync.h" | |
2df20057 WD |
27 | #include "ifuncs.h" |
28 | #include "itypes.h" | |
ab14d01a | 29 | #include "io.h" |
f6c34742 | 30 | |
283887d7 | 31 | extern int am_root; |
f6c34742 AT |
32 | extern int preserve_uid; |
33 | extern int preserve_gid; | |
1c3344a1 | 34 | extern int preserve_acls; |
f6c34742 | 35 | extern int numeric_ids; |
2df20057 WD |
36 | extern char *usermap; |
37 | extern char *groupmap; | |
f6c34742 | 38 | |
142a5e7b WD |
39 | #ifdef HAVE_GETGROUPS |
40 | # ifndef GETGROUPS_T | |
41 | # define GETGROUPS_T gid_t | |
42 | # endif | |
43 | #endif | |
44 | ||
45 | #define GID_NONE ((gid_t)-1) | |
46 | ||
2df20057 WD |
47 | #define NFLAGS_WILD_NAME_MATCH (1<<0) |
48 | #define NFLAGS_NAME_MATCH (1<<1) | |
49 | ||
f6c34742 AT |
50 | struct idlist { |
51 | struct idlist *next; | |
c78cb8f3 | 52 | const char *name; |
d6b422a6 WD |
53 | id_t id, id2; |
54 | uint16 flags; | |
f6c34742 AT |
55 | }; |
56 | ||
2df20057 WD |
57 | static struct idlist *uidlist, *uidmap; |
58 | static struct idlist *gidlist, *gidmap; | |
f6c34742 | 59 | |
c78cb8f3 | 60 | static struct idlist *add_to_list(struct idlist **root, id_t id, const char *name, |
d6b422a6 | 61 | id_t id2, uint16 flags) |
f6c34742 | 62 | { |
d49def48 WD |
63 | struct idlist *node = new(struct idlist); |
64 | if (!node) | |
65 | out_of_memory("add_to_list"); | |
66 | node->next = *root; | |
67 | node->name = name; | |
68 | node->id = id; | |
69 | node->id2 = id2; | |
d6b422a6 | 70 | node->flags = flags; |
d49def48 WD |
71 | *root = node; |
72 | return node; | |
f6c34742 AT |
73 | } |
74 | ||
f6c34742 | 75 | /* turn a uid into a user name */ |
c78cb8f3 | 76 | static const char *uid_to_name(uid_t uid) |
f6c34742 AT |
77 | { |
78 | struct passwd *pass = getpwuid(uid); | |
d49def48 WD |
79 | if (pass) |
80 | return strdup(pass->pw_name); | |
f6c34742 AT |
81 | return NULL; |
82 | } | |
83 | ||
84 | /* turn a gid into a group name */ | |
c78cb8f3 | 85 | static const char *gid_to_name(gid_t gid) |
f6c34742 AT |
86 | { |
87 | struct group *grp = getgrgid(gid); | |
d49def48 WD |
88 | if (grp) |
89 | return strdup(grp->gr_name); | |
f6c34742 AT |
90 | return NULL; |
91 | } | |
92 | ||
5b540e86 WD |
93 | static int is_in_group(gid_t gid) |
94 | { | |
4f5b0756 | 95 | #ifdef HAVE_GETGROUPS |
a2687b64 | 96 | static gid_t last_in = GID_NONE, last_out; |
5b540e86 WD |
97 | static int ngroups = -2; |
98 | static GETGROUPS_T *gidset; | |
99 | int n; | |
100 | ||
101 | if (gid == last_in) | |
102 | return last_out; | |
103 | if (ngroups < -1) { | |
670d8abf | 104 | gid_t mygid = MY_GID(); |
f567e9b3 | 105 | if ((ngroups = getgroups(0, NULL)) < 0) |
dbd8811b | 106 | ngroups = 0; |
72fc7ec5 | 107 | gidset = new_array(GETGROUPS_T, ngroups+1); |
f567e9b3 WD |
108 | if (!gidset) |
109 | out_of_memory("is_in_group"); | |
72fc7ec5 | 110 | if (ngroups > 0) |
5b540e86 | 111 | ngroups = getgroups(ngroups, gidset); |
72fc7ec5 WD |
112 | /* The default gid might not be in the list on some systems. */ |
113 | for (n = 0; n < ngroups; n++) { | |
114 | if (gidset[n] == mygid) | |
115 | break; | |
5b540e86 | 116 | } |
72fc7ec5 WD |
117 | if (n == ngroups) |
118 | gidset[ngroups++] = mygid; | |
951e826b | 119 | if (DEBUG_GTE(OWN, 2)) { |
187e9c24 | 120 | int pos; |
f567e9b3 WD |
121 | char *gidbuf = new_array(char, ngroups*21+32); |
122 | if (!gidbuf) | |
123 | out_of_memory("is_in_group"); | |
10944395 WD |
124 | pos = snprintf(gidbuf, 32, "process has %d gid%s: ", |
125 | ngroups, ngroups == 1? "" : "s"); | |
84fa865c | 126 | for (n = 0; n < ngroups; n++) { |
10944395 | 127 | pos += snprintf(gidbuf+pos, 21, " %d", (int)gidset[n]); |
84fa865c | 128 | } |
187e9c24 | 129 | rprintf(FINFO, "%s\n", gidbuf); |
f567e9b3 | 130 | free(gidbuf); |
84fa865c | 131 | } |
5b540e86 WD |
132 | } |
133 | ||
134 | last_in = gid; | |
5b540e86 | 135 | for (n = 0; n < ngroups; n++) { |
a2687b64 WD |
136 | if (gidset[n] == gid) |
137 | return last_out = 1; | |
5b540e86 | 138 | } |
a2687b64 | 139 | return last_out = 0; |
5b540e86 WD |
140 | |
141 | #else | |
a2687b64 | 142 | static gid_t mygid = GID_NONE; |
187e9c24 | 143 | if (mygid == GID_NONE) { |
670d8abf | 144 | mygid = MY_GID(); |
951e826b | 145 | if (DEBUG_GTE(OWN, 2)) |
d6b422a6 | 146 | rprintf(FINFO, "process has gid %u\n", (unsigned)mygid); |
187e9c24 | 147 | } |
a2687b64 | 148 | return gid == mygid; |
5b540e86 WD |
149 | #endif |
150 | } | |
151 | ||
2df20057 WD |
152 | /* Add a uid/gid to its list of ids. Only called on receiving side. */ |
153 | static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idmap, | |
154 | id_t id, const char *name) | |
d49def48 | 155 | { |
d49def48 | 156 | struct idlist *node; |
2df20057 WD |
157 | int flag; |
158 | id_t id2; | |
d49def48 | 159 | |
2df20057 WD |
160 | if (!name) |
161 | name = ""; | |
d49def48 | 162 | |
2df20057 WD |
163 | for (node = idmap; node; node = node->next) { |
164 | if (node->flags & NFLAGS_WILD_NAME_MATCH) { | |
165 | if (!wildmatch(node->name, name)) | |
166 | continue; | |
167 | } else if (node->flags & NFLAGS_NAME_MATCH) { | |
168 | if (strcmp(node->name, name) != 0) | |
169 | continue; | |
170 | } else if (node->name) { | |
171 | if (id < node->id || id > (unsigned long)node->name) | |
172 | continue; | |
173 | } else { | |
174 | if (node->id != id) | |
175 | continue; | |
176 | } | |
177 | break; | |
d49def48 | 178 | } |
2df20057 WD |
179 | if (node) |
180 | id2 = node->id2; | |
181 | else if (*name && id) { | |
46d68be3 | 182 | if (idlist_ptr == &uidlist) { |
2df20057 WD |
183 | uid_t uid; |
184 | id2 = name_to_uid(name, &uid) ? uid : id; | |
185 | } else { | |
186 | gid_t gid; | |
187 | id2 = name_to_gid(name, &gid) ? gid : id; | |
188 | } | |
189 | } else | |
190 | id2 = id; | |
d49def48 | 191 | |
46d68be3 | 192 | flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0; |
2df20057 | 193 | node = add_to_list(idlist_ptr, id, *name ? name : NULL, id2, flag); |
d49def48 | 194 | |
951e826b | 195 | if (DEBUG_GTE(OWN, 2)) { |
2df20057 | 196 | rprintf(FINFO, "%sid %u(%s) maps to %u\n", |
46d68be3 | 197 | idlist_ptr == &uidlist ? "u" : "g", |
2df20057 | 198 | (unsigned)id, name, (unsigned)id2); |
d49def48 WD |
199 | } |
200 | ||
d6b422a6 | 201 | return node; |
d49def48 WD |
202 | } |
203 | ||
ade7292a | 204 | /* this function is a definate candidate for a faster algorithm */ |
496c809f | 205 | uid_t match_uid(uid_t uid) |
ade7292a | 206 | { |
2df20057 | 207 | static uid_t last_in = -1, last_out = -1; |
d49def48 WD |
208 | struct idlist *list; |
209 | ||
ade7292a WD |
210 | if (uid == last_in) |
211 | return last_out; | |
212 | ||
213 | last_in = uid; | |
214 | ||
d49def48 | 215 | for (list = uidlist; list; list = list->next) { |
d6b422a6 | 216 | if (list->id == uid) |
2df20057 | 217 | break; |
ade7292a WD |
218 | } |
219 | ||
2df20057 WD |
220 | if (!list) |
221 | list = recv_add_id(&uidlist, uidmap, uid, NULL); | |
222 | ||
223 | return last_out = list->id2; | |
ade7292a WD |
224 | } |
225 | ||
d6b422a6 | 226 | gid_t match_gid(gid_t gid, uint16 *flags_ptr) |
f6c34742 | 227 | { |
4504b225 | 228 | static struct idlist *last = NULL; |
d49def48 WD |
229 | struct idlist *list; |
230 | ||
4504b225 WD |
231 | if (last && gid == last->id) |
232 | list = last; | |
233 | else { | |
234 | for (list = gidlist; list; list = list->next) { | |
235 | if (list->id == gid) | |
236 | break; | |
237 | } | |
238 | if (!list) | |
2df20057 | 239 | list = recv_add_id(&gidlist, gidmap, gid, NULL); |
4504b225 | 240 | last = list; |
f6c34742 | 241 | } |
d49def48 | 242 | |
d6b422a6 WD |
243 | if (flags_ptr && list->flags & FLAG_SKIP_GROUP) |
244 | *flags_ptr |= FLAG_SKIP_GROUP; | |
4504b225 | 245 | return list->id2; |
f6c34742 AT |
246 | } |
247 | ||
d49def48 | 248 | /* Add a uid to the list of uids. Only called on sending side. */ |
c78cb8f3 | 249 | const char *add_uid(uid_t uid) |
f6c34742 | 250 | { |
d49def48 | 251 | struct idlist *list; |
496c809f | 252 | struct idlist *node; |
f6c34742 | 253 | |
d49def48 | 254 | if (uid == 0) /* don't map root */ |
496c809f | 255 | return NULL; |
f6c34742 | 256 | |
d49def48 | 257 | for (list = uidlist; list; list = list->next) { |
d6b422a6 | 258 | if (list->id == uid) |
496c809f | 259 | return NULL; |
f6c34742 AT |
260 | } |
261 | ||
d6b422a6 | 262 | node = add_to_list(&uidlist, uid, uid_to_name(uid), 0, 0); |
496c809f | 263 | return node->name; |
f6c34742 AT |
264 | } |
265 | ||
d49def48 | 266 | /* Add a gid to the list of gids. Only called on sending side. */ |
c78cb8f3 | 267 | const char *add_gid(gid_t gid) |
f6c34742 | 268 | { |
d49def48 | 269 | struct idlist *list; |
496c809f | 270 | struct idlist *node; |
f6c34742 | 271 | |
d49def48 | 272 | if (gid == 0) /* don't map root */ |
496c809f | 273 | return NULL; |
f6c34742 | 274 | |
d49def48 | 275 | for (list = gidlist; list; list = list->next) { |
d6b422a6 | 276 | if (list->id == gid) |
496c809f | 277 | return NULL; |
f6c34742 AT |
278 | } |
279 | ||
d6b422a6 | 280 | node = add_to_list(&gidlist, gid, gid_to_name(gid), 0, 0); |
496c809f | 281 | return node->name; |
f6c34742 AT |
282 | } |
283 | ||
f6c34742 | 284 | /* send a complete uid/gid mapping to the peer */ |
d6b422a6 | 285 | void send_id_list(int f) |
f6c34742 AT |
286 | { |
287 | struct idlist *list; | |
288 | ||
1c3344a1 | 289 | if (preserve_uid || preserve_acls) { |
d49def48 | 290 | int len; |
f6c34742 | 291 | /* we send sequences of uid/byte-length/name */ |
d49def48 WD |
292 | for (list = uidlist; list; list = list->next) { |
293 | if (!list->name) | |
294 | continue; | |
295 | len = strlen(list->name); | |
f31514ad | 296 | write_varint30(f, list->id); |
f6c34742 AT |
297 | write_byte(f, len); |
298 | write_buf(f, list->name, len); | |
f6c34742 AT |
299 | } |
300 | ||
301 | /* terminate the uid list with a 0 uid. We explicitly exclude | |
84fa865c | 302 | * 0 from the list */ |
f31514ad | 303 | write_varint30(f, 0); |
f6c34742 AT |
304 | } |
305 | ||
1c3344a1 | 306 | if (preserve_gid || preserve_acls) { |
d49def48 WD |
307 | int len; |
308 | for (list = gidlist; list; list = list->next) { | |
309 | if (!list->name) | |
310 | continue; | |
311 | len = strlen(list->name); | |
f31514ad | 312 | write_varint30(f, list->id); |
f6c34742 AT |
313 | write_byte(f, len); |
314 | write_buf(f, list->name, len); | |
f6c34742 | 315 | } |
f31514ad | 316 | write_varint30(f, 0); |
f6c34742 AT |
317 | } |
318 | } | |
319 | ||
496c809f | 320 | uid_t recv_user_name(int f, uid_t uid) |
283887d7 | 321 | { |
d6b422a6 | 322 | struct idlist *node; |
283887d7 WD |
323 | int len = read_byte(f); |
324 | char *name = new_array(char, len+1); | |
325 | if (!name) | |
326 | out_of_memory("recv_user_name"); | |
327 | read_sbuf(f, name, len); | |
0b52f94d WD |
328 | if (numeric_ids < 0) { |
329 | free(name); | |
330 | name = NULL; | |
331 | } | |
2df20057 | 332 | node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */ |
d6b422a6 | 333 | return node->id2; |
283887d7 WD |
334 | } |
335 | ||
d6b422a6 | 336 | gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr) |
283887d7 | 337 | { |
d6b422a6 | 338 | struct idlist *node; |
283887d7 WD |
339 | int len = read_byte(f); |
340 | char *name = new_array(char, len+1); | |
341 | if (!name) | |
342 | out_of_memory("recv_group_name"); | |
343 | read_sbuf(f, name, len); | |
0b52f94d WD |
344 | if (numeric_ids < 0) { |
345 | free(name); | |
346 | name = NULL; | |
347 | } | |
2df20057 | 348 | node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */ |
d6b422a6 WD |
349 | if (flags_ptr && node->flags & FLAG_SKIP_GROUP) |
350 | *flags_ptr |= FLAG_SKIP_GROUP; | |
351 | return node->id2; | |
283887d7 WD |
352 | } |
353 | ||
f6c34742 | 354 | /* recv a complete uid/gid mapping from the peer and map the uid/gid |
84fa865c | 355 | * in the file list to local names */ |
d6b422a6 | 356 | void recv_id_list(int f, struct file_list *flist) |
f6c34742 | 357 | { |
d6b422a6 WD |
358 | id_t id; |
359 | int i; | |
f6c34742 | 360 | |
0b52f94d | 361 | if ((preserve_uid || preserve_acls) && numeric_ids <= 0) { |
f6c34742 | 362 | /* read the uid list */ |
f31514ad | 363 | while ((id = read_varint30(f)) != 0) |
d6b422a6 | 364 | recv_user_name(f, id); |
f6c34742 AT |
365 | } |
366 | ||
0b52f94d | 367 | if ((preserve_gid || preserve_acls) && numeric_ids <= 0) { |
d49def48 | 368 | /* read the gid list */ |
f31514ad | 369 | while ((id = read_varint30(f)) != 0) |
d6b422a6 | 370 | recv_group_name(f, id, NULL); |
f6c34742 AT |
371 | } |
372 | ||
a217c453 | 373 | /* Now convert all the uids/gids from sender values to our values. */ |
1c3344a1 | 374 | #ifdef SUPPORT_ACLS |
2df20057 | 375 | if (preserve_acls && (!numeric_ids || usermap || groupmap)) |
a217c453 | 376 | match_acl_ids(); |
1c3344a1 | 377 | #endif |
2df20057 | 378 | if (am_root && preserve_uid && (!numeric_ids || usermap)) { |
9decb4d2 | 379 | for (i = 0; i < flist->used; i++) |
d6b422a6 | 380 | F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i])); |
d49def48 | 381 | } |
2df20057 | 382 | if (preserve_gid && (!am_root || !numeric_ids || groupmap)) { |
9decb4d2 | 383 | for (i = 0; i < flist->used; i++) { |
d6b422a6 WD |
384 | F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]), |
385 | &flist->files[i]->flags); | |
386 | } | |
5e58e3f9 | 387 | } |
f6c34742 | 388 | } |
2df20057 WD |
389 | |
390 | void parse_name_map(char *map, BOOL usernames) | |
391 | { | |
392 | struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap; | |
393 | struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist; | |
394 | char *colon, *end, *name, *cp = map + strlen(map); | |
395 | id_t id1; | |
396 | uint16 flags; | |
397 | ||
398 | /* Parse the list in reverse, so the order in the struct is right. */ | |
399 | while (1) { | |
400 | end = cp; | |
401 | while (cp > map && cp[-1] != ',') cp--; | |
402 | if (!(colon = strchr(cp, ':'))) { | |
403 | rprintf(FERROR, "No colon found in --%smap: %s\n", | |
404 | usernames ? "user" : "group", cp); | |
405 | exit_cleanup(RERR_SYNTAX); | |
406 | } | |
407 | if (!colon[1]) { | |
408 | rprintf(FERROR, "No name found after colon --%smap: %s\n", | |
409 | usernames ? "user" : "group", cp); | |
410 | exit_cleanup(RERR_SYNTAX); | |
411 | } | |
412 | *colon = '\0'; | |
413 | ||
414 | if (isDigit(cp)) { | |
415 | char *dash = strchr(cp, '-'); | |
416 | if (strspn(cp, "0123456789-") != (size_t)(colon - cp) | |
417 | || (dash && (!dash[1] || strchr(dash+1, '-')))) { | |
418 | bad_number: | |
419 | rprintf(FERROR, "Invalid number in --%smap: %s\n", | |
420 | usernames ? "user" : "group", cp); | |
421 | exit_cleanup(RERR_SYNTAX); | |
422 | } | |
423 | if (dash) | |
424 | name = (char *)atol(dash+1); | |
425 | else | |
426 | name = (char *)0; | |
427 | flags = 0; | |
428 | id1 = atol(cp); | |
429 | } else if (strpbrk(cp, "*[?")) { | |
430 | flags = NFLAGS_WILD_NAME_MATCH; | |
431 | name = cp; | |
432 | id1 = 0; | |
433 | } else { | |
434 | flags = NFLAGS_NAME_MATCH; | |
435 | name = cp; | |
436 | id1 = 0; | |
437 | } | |
438 | ||
439 | if (isDigit(colon+1)) { | |
440 | if (strspn(colon+1, "0123456789") != (size_t)(end - colon - 1)) { | |
441 | cp = colon+1; | |
442 | goto bad_number; | |
443 | } | |
444 | add_to_list(idmap_ptr, id1, name, atol(colon+1), flags); | |
445 | } else if (usernames) { | |
446 | uid_t uid; | |
447 | if (name_to_uid(colon+1, &uid)) | |
448 | add_to_list(idmap_ptr, id1, name, uid, flags); | |
449 | else { | |
450 | rprintf(FERROR, | |
451 | "Unknown --usermap name on receiver: %s\n", | |
452 | colon+1); | |
453 | } | |
454 | } else { | |
455 | gid_t gid; | |
456 | if (name_to_gid(colon+1, &gid)) | |
457 | add_to_list(idmap_ptr, id1, name, gid, flags); | |
458 | else { | |
459 | rprintf(FERROR, | |
460 | "Unknown --groupmap name on receiver: %s\n", | |
461 | colon+1); | |
462 | } | |
463 | } | |
464 | ||
465 | if (cp == map) | |
466 | break; | |
467 | ||
468 | *--cp = '\0'; /* replace comma */ | |
469 | } | |
470 | ||
471 | /* The 0 user/group doesn't get its name sent, so add it explicitly. */ | |
472 | recv_add_id(idlist_ptr, *idmap_ptr, 0, | |
473 | numeric_ids ? NULL : usernames ? uid_to_name(0) : gid_to_name(0)); | |
474 | } |