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; |
3b83a220 | 36 | extern gid_t our_gid; |
2df20057 WD |
37 | extern char *usermap; |
38 | extern char *groupmap; | |
f6c34742 | 39 | |
142a5e7b WD |
40 | #ifdef HAVE_GETGROUPS |
41 | # ifndef GETGROUPS_T | |
42 | # define GETGROUPS_T gid_t | |
43 | # endif | |
44 | #endif | |
45 | ||
46 | #define GID_NONE ((gid_t)-1) | |
47 | ||
2df20057 WD |
48 | #define NFLAGS_WILD_NAME_MATCH (1<<0) |
49 | #define NFLAGS_NAME_MATCH (1<<1) | |
50 | ||
f6c34742 AT |
51 | struct idlist { |
52 | struct idlist *next; | |
c78cb8f3 | 53 | const char *name; |
d6b422a6 WD |
54 | id_t id, id2; |
55 | uint16 flags; | |
f6c34742 AT |
56 | }; |
57 | ||
2df20057 WD |
58 | static struct idlist *uidlist, *uidmap; |
59 | static struct idlist *gidlist, *gidmap; | |
f6c34742 | 60 | |
c78cb8f3 | 61 | static struct idlist *add_to_list(struct idlist **root, id_t id, const char *name, |
d6b422a6 | 62 | id_t id2, uint16 flags) |
f6c34742 | 63 | { |
d49def48 WD |
64 | struct idlist *node = new(struct idlist); |
65 | if (!node) | |
66 | out_of_memory("add_to_list"); | |
67 | node->next = *root; | |
68 | node->name = name; | |
69 | node->id = id; | |
70 | node->id2 = id2; | |
d6b422a6 | 71 | node->flags = flags; |
d49def48 WD |
72 | *root = node; |
73 | return node; | |
f6c34742 AT |
74 | } |
75 | ||
f6c34742 | 76 | /* turn a uid into a user name */ |
c78cb8f3 | 77 | static const char *uid_to_name(uid_t uid) |
f6c34742 AT |
78 | { |
79 | struct passwd *pass = getpwuid(uid); | |
d49def48 WD |
80 | if (pass) |
81 | return strdup(pass->pw_name); | |
f6c34742 AT |
82 | return NULL; |
83 | } | |
84 | ||
85 | /* turn a gid into a group name */ | |
c78cb8f3 | 86 | static const char *gid_to_name(gid_t gid) |
f6c34742 AT |
87 | { |
88 | struct group *grp = getgrgid(gid); | |
d49def48 WD |
89 | if (grp) |
90 | return strdup(grp->gr_name); | |
f6c34742 AT |
91 | return NULL; |
92 | } | |
93 | ||
5b540e86 WD |
94 | static int is_in_group(gid_t gid) |
95 | { | |
4f5b0756 | 96 | #ifdef HAVE_GETGROUPS |
a2687b64 | 97 | static gid_t last_in = GID_NONE, last_out; |
5b540e86 WD |
98 | static int ngroups = -2; |
99 | static GETGROUPS_T *gidset; | |
100 | int n; | |
101 | ||
102 | if (gid == last_in) | |
103 | return last_out; | |
104 | if (ngroups < -1) { | |
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++) { | |
3b83a220 | 114 | if (gidset[n] == our_gid) |
72fc7ec5 | 115 | break; |
5b540e86 | 116 | } |
72fc7ec5 | 117 | if (n == ngroups) |
3b83a220 | 118 | gidset[ngroups++] = our_gid; |
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 | |
3b83a220 | 142 | return gid == our_gid; |
5b540e86 WD |
143 | #endif |
144 | } | |
145 | ||
2df20057 WD |
146 | /* Add a uid/gid to its list of ids. Only called on receiving side. */ |
147 | static struct idlist *recv_add_id(struct idlist **idlist_ptr, struct idlist *idmap, | |
148 | id_t id, const char *name) | |
d49def48 | 149 | { |
d49def48 | 150 | struct idlist *node; |
2df20057 WD |
151 | int flag; |
152 | id_t id2; | |
d49def48 | 153 | |
2df20057 WD |
154 | if (!name) |
155 | name = ""; | |
d49def48 | 156 | |
2df20057 WD |
157 | for (node = idmap; node; node = node->next) { |
158 | if (node->flags & NFLAGS_WILD_NAME_MATCH) { | |
159 | if (!wildmatch(node->name, name)) | |
160 | continue; | |
161 | } else if (node->flags & NFLAGS_NAME_MATCH) { | |
162 | if (strcmp(node->name, name) != 0) | |
163 | continue; | |
164 | } else if (node->name) { | |
165 | if (id < node->id || id > (unsigned long)node->name) | |
166 | continue; | |
167 | } else { | |
168 | if (node->id != id) | |
169 | continue; | |
170 | } | |
171 | break; | |
d49def48 | 172 | } |
2df20057 WD |
173 | if (node) |
174 | id2 = node->id2; | |
175 | else if (*name && id) { | |
46d68be3 | 176 | if (idlist_ptr == &uidlist) { |
2df20057 | 177 | uid_t uid; |
56017d31 | 178 | id2 = user_to_uid(name, &uid, False) ? uid : id; |
2df20057 WD |
179 | } else { |
180 | gid_t gid; | |
56017d31 | 181 | id2 = group_to_gid(name, &gid, False) ? gid : id; |
2df20057 WD |
182 | } |
183 | } else | |
184 | id2 = id; | |
d49def48 | 185 | |
46d68be3 | 186 | flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0; |
2df20057 | 187 | node = add_to_list(idlist_ptr, id, *name ? name : NULL, id2, flag); |
d49def48 | 188 | |
951e826b | 189 | if (DEBUG_GTE(OWN, 2)) { |
2df20057 | 190 | rprintf(FINFO, "%sid %u(%s) maps to %u\n", |
46d68be3 | 191 | idlist_ptr == &uidlist ? "u" : "g", |
2df20057 | 192 | (unsigned)id, name, (unsigned)id2); |
d49def48 WD |
193 | } |
194 | ||
d6b422a6 | 195 | return node; |
d49def48 WD |
196 | } |
197 | ||
ade7292a | 198 | /* this function is a definate candidate for a faster algorithm */ |
496c809f | 199 | uid_t match_uid(uid_t uid) |
ade7292a | 200 | { |
2df20057 | 201 | static uid_t last_in = -1, last_out = -1; |
d49def48 WD |
202 | struct idlist *list; |
203 | ||
ade7292a WD |
204 | if (uid == last_in) |
205 | return last_out; | |
206 | ||
207 | last_in = uid; | |
208 | ||
d49def48 | 209 | for (list = uidlist; list; list = list->next) { |
d6b422a6 | 210 | if (list->id == uid) |
2df20057 | 211 | break; |
ade7292a WD |
212 | } |
213 | ||
2df20057 WD |
214 | if (!list) |
215 | list = recv_add_id(&uidlist, uidmap, uid, NULL); | |
216 | ||
217 | return last_out = list->id2; | |
ade7292a WD |
218 | } |
219 | ||
d6b422a6 | 220 | gid_t match_gid(gid_t gid, uint16 *flags_ptr) |
f6c34742 | 221 | { |
4504b225 | 222 | static struct idlist *last = NULL; |
d49def48 WD |
223 | struct idlist *list; |
224 | ||
4504b225 WD |
225 | if (last && gid == last->id) |
226 | list = last; | |
227 | else { | |
228 | for (list = gidlist; list; list = list->next) { | |
229 | if (list->id == gid) | |
230 | break; | |
231 | } | |
232 | if (!list) | |
2df20057 | 233 | list = recv_add_id(&gidlist, gidmap, gid, NULL); |
4504b225 | 234 | last = list; |
f6c34742 | 235 | } |
d49def48 | 236 | |
d6b422a6 WD |
237 | if (flags_ptr && list->flags & FLAG_SKIP_GROUP) |
238 | *flags_ptr |= FLAG_SKIP_GROUP; | |
4504b225 | 239 | return list->id2; |
f6c34742 AT |
240 | } |
241 | ||
d49def48 | 242 | /* Add a uid to the list of uids. Only called on sending side. */ |
c78cb8f3 | 243 | const char *add_uid(uid_t uid) |
f6c34742 | 244 | { |
d49def48 | 245 | struct idlist *list; |
496c809f | 246 | struct idlist *node; |
f6c34742 | 247 | |
d49def48 | 248 | if (uid == 0) /* don't map root */ |
496c809f | 249 | return NULL; |
f6c34742 | 250 | |
d49def48 | 251 | for (list = uidlist; list; list = list->next) { |
d6b422a6 | 252 | if (list->id == uid) |
496c809f | 253 | return NULL; |
f6c34742 AT |
254 | } |
255 | ||
d6b422a6 | 256 | node = add_to_list(&uidlist, uid, uid_to_name(uid), 0, 0); |
496c809f | 257 | return node->name; |
f6c34742 AT |
258 | } |
259 | ||
d49def48 | 260 | /* Add a gid to the list of gids. Only called on sending side. */ |
c78cb8f3 | 261 | const char *add_gid(gid_t gid) |
f6c34742 | 262 | { |
d49def48 | 263 | struct idlist *list; |
496c809f | 264 | struct idlist *node; |
f6c34742 | 265 | |
d49def48 | 266 | if (gid == 0) /* don't map root */ |
496c809f | 267 | return NULL; |
f6c34742 | 268 | |
d49def48 | 269 | for (list = gidlist; list; list = list->next) { |
d6b422a6 | 270 | if (list->id == gid) |
496c809f | 271 | return NULL; |
f6c34742 AT |
272 | } |
273 | ||
d6b422a6 | 274 | node = add_to_list(&gidlist, gid, gid_to_name(gid), 0, 0); |
496c809f | 275 | return node->name; |
f6c34742 AT |
276 | } |
277 | ||
f6c34742 | 278 | /* send a complete uid/gid mapping to the peer */ |
d6b422a6 | 279 | void send_id_list(int f) |
f6c34742 AT |
280 | { |
281 | struct idlist *list; | |
282 | ||
1c3344a1 | 283 | if (preserve_uid || preserve_acls) { |
d49def48 | 284 | int len; |
f6c34742 | 285 | /* we send sequences of uid/byte-length/name */ |
d49def48 WD |
286 | for (list = uidlist; list; list = list->next) { |
287 | if (!list->name) | |
288 | continue; | |
289 | len = strlen(list->name); | |
f31514ad | 290 | write_varint30(f, list->id); |
f6c34742 AT |
291 | write_byte(f, len); |
292 | write_buf(f, list->name, len); | |
f6c34742 AT |
293 | } |
294 | ||
295 | /* terminate the uid list with a 0 uid. We explicitly exclude | |
84fa865c | 296 | * 0 from the list */ |
f31514ad | 297 | write_varint30(f, 0); |
f6c34742 AT |
298 | } |
299 | ||
1c3344a1 | 300 | if (preserve_gid || preserve_acls) { |
d49def48 WD |
301 | int len; |
302 | for (list = gidlist; list; list = list->next) { | |
303 | if (!list->name) | |
304 | continue; | |
305 | len = strlen(list->name); | |
f31514ad | 306 | write_varint30(f, list->id); |
f6c34742 AT |
307 | write_byte(f, len); |
308 | write_buf(f, list->name, len); | |
f6c34742 | 309 | } |
f31514ad | 310 | write_varint30(f, 0); |
f6c34742 AT |
311 | } |
312 | } | |
313 | ||
496c809f | 314 | uid_t recv_user_name(int f, uid_t uid) |
283887d7 | 315 | { |
d6b422a6 | 316 | struct idlist *node; |
283887d7 WD |
317 | int len = read_byte(f); |
318 | char *name = new_array(char, len+1); | |
319 | if (!name) | |
320 | out_of_memory("recv_user_name"); | |
321 | read_sbuf(f, name, len); | |
0b52f94d WD |
322 | if (numeric_ids < 0) { |
323 | free(name); | |
324 | name = NULL; | |
325 | } | |
2df20057 | 326 | node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */ |
d6b422a6 | 327 | return node->id2; |
283887d7 WD |
328 | } |
329 | ||
d6b422a6 | 330 | gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr) |
283887d7 | 331 | { |
d6b422a6 | 332 | struct idlist *node; |
283887d7 WD |
333 | int len = read_byte(f); |
334 | char *name = new_array(char, len+1); | |
335 | if (!name) | |
336 | out_of_memory("recv_group_name"); | |
337 | read_sbuf(f, name, len); | |
0b52f94d WD |
338 | if (numeric_ids < 0) { |
339 | free(name); | |
340 | name = NULL; | |
341 | } | |
2df20057 | 342 | node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */ |
d6b422a6 WD |
343 | if (flags_ptr && node->flags & FLAG_SKIP_GROUP) |
344 | *flags_ptr |= FLAG_SKIP_GROUP; | |
345 | return node->id2; | |
283887d7 WD |
346 | } |
347 | ||
f6c34742 | 348 | /* recv a complete uid/gid mapping from the peer and map the uid/gid |
84fa865c | 349 | * in the file list to local names */ |
d6b422a6 | 350 | void recv_id_list(int f, struct file_list *flist) |
f6c34742 | 351 | { |
d6b422a6 WD |
352 | id_t id; |
353 | int i; | |
f6c34742 | 354 | |
0b52f94d | 355 | if ((preserve_uid || preserve_acls) && numeric_ids <= 0) { |
f6c34742 | 356 | /* read the uid list */ |
f31514ad | 357 | while ((id = read_varint30(f)) != 0) |
d6b422a6 | 358 | recv_user_name(f, id); |
f6c34742 AT |
359 | } |
360 | ||
0b52f94d | 361 | if ((preserve_gid || preserve_acls) && numeric_ids <= 0) { |
d49def48 | 362 | /* read the gid list */ |
f31514ad | 363 | while ((id = read_varint30(f)) != 0) |
d6b422a6 | 364 | recv_group_name(f, id, NULL); |
f6c34742 AT |
365 | } |
366 | ||
a217c453 | 367 | /* Now convert all the uids/gids from sender values to our values. */ |
1c3344a1 | 368 | #ifdef SUPPORT_ACLS |
2df20057 | 369 | if (preserve_acls && (!numeric_ids || usermap || groupmap)) |
a217c453 | 370 | match_acl_ids(); |
1c3344a1 | 371 | #endif |
2df20057 | 372 | if (am_root && preserve_uid && (!numeric_ids || usermap)) { |
9decb4d2 | 373 | for (i = 0; i < flist->used; i++) |
d6b422a6 | 374 | F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i])); |
d49def48 | 375 | } |
2df20057 | 376 | if (preserve_gid && (!am_root || !numeric_ids || groupmap)) { |
9decb4d2 | 377 | for (i = 0; i < flist->used; i++) { |
d6b422a6 WD |
378 | F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]), |
379 | &flist->files[i]->flags); | |
380 | } | |
5e58e3f9 | 381 | } |
f6c34742 | 382 | } |
2df20057 WD |
383 | |
384 | void parse_name_map(char *map, BOOL usernames) | |
385 | { | |
386 | struct idlist **idmap_ptr = usernames ? &uidmap : &gidmap; | |
387 | struct idlist **idlist_ptr = usernames ? &uidlist : &gidlist; | |
388 | char *colon, *end, *name, *cp = map + strlen(map); | |
389 | id_t id1; | |
390 | uint16 flags; | |
391 | ||
392 | /* Parse the list in reverse, so the order in the struct is right. */ | |
393 | while (1) { | |
394 | end = cp; | |
395 | while (cp > map && cp[-1] != ',') cp--; | |
396 | if (!(colon = strchr(cp, ':'))) { | |
397 | rprintf(FERROR, "No colon found in --%smap: %s\n", | |
398 | usernames ? "user" : "group", cp); | |
399 | exit_cleanup(RERR_SYNTAX); | |
400 | } | |
401 | if (!colon[1]) { | |
402 | rprintf(FERROR, "No name found after colon --%smap: %s\n", | |
403 | usernames ? "user" : "group", cp); | |
404 | exit_cleanup(RERR_SYNTAX); | |
405 | } | |
406 | *colon = '\0'; | |
407 | ||
408 | if (isDigit(cp)) { | |
409 | char *dash = strchr(cp, '-'); | |
410 | if (strspn(cp, "0123456789-") != (size_t)(colon - cp) | |
411 | || (dash && (!dash[1] || strchr(dash+1, '-')))) { | |
2df20057 WD |
412 | rprintf(FERROR, "Invalid number in --%smap: %s\n", |
413 | usernames ? "user" : "group", cp); | |
414 | exit_cleanup(RERR_SYNTAX); | |
415 | } | |
416 | if (dash) | |
417 | name = (char *)atol(dash+1); | |
418 | else | |
419 | name = (char *)0; | |
420 | flags = 0; | |
421 | id1 = atol(cp); | |
422 | } else if (strpbrk(cp, "*[?")) { | |
423 | flags = NFLAGS_WILD_NAME_MATCH; | |
424 | name = cp; | |
425 | id1 = 0; | |
426 | } else { | |
427 | flags = NFLAGS_NAME_MATCH; | |
428 | name = cp; | |
429 | id1 = 0; | |
430 | } | |
431 | ||
56017d31 | 432 | if (usernames) { |
2df20057 | 433 | uid_t uid; |
56017d31 | 434 | if (user_to_uid(colon+1, &uid, True)) |
2df20057 WD |
435 | add_to_list(idmap_ptr, id1, name, uid, flags); |
436 | else { | |
437 | rprintf(FERROR, | |
438 | "Unknown --usermap name on receiver: %s\n", | |
439 | colon+1); | |
440 | } | |
441 | } else { | |
442 | gid_t gid; | |
56017d31 | 443 | if (group_to_gid(colon+1, &gid, True)) |
2df20057 WD |
444 | add_to_list(idmap_ptr, id1, name, gid, flags); |
445 | else { | |
446 | rprintf(FERROR, | |
447 | "Unknown --groupmap name on receiver: %s\n", | |
448 | colon+1); | |
449 | } | |
450 | } | |
451 | ||
452 | if (cp == map) | |
453 | break; | |
454 | ||
455 | *--cp = '\0'; /* replace comma */ | |
456 | } | |
457 | ||
458 | /* The 0 user/group doesn't get its name sent, so add it explicitly. */ | |
459 | recv_add_id(idlist_ptr, *idmap_ptr, 0, | |
460 | numeric_ids ? NULL : usernames ? uid_to_name(0) : gid_to_name(0)); | |
461 | } |