Fixed a hang in the inc_recurse batch-reading code.
[rsync/rsync.git] / uidlist.c
1 /*
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
6  * Copyright (C) 2004-2009 Wayne Davison
7  *
8  * This program is free software; you can redistribute it and/or modify
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.
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  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, visit the http://fsf.org website.
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. */
25
26 #include "rsync.h"
27 #include "ifuncs.h"
28 #include "itypes.h"
29 #include "io.h"
30
31 extern int am_root;
32 extern int preserve_uid;
33 extern int preserve_gid;
34 extern int preserve_acls;
35 extern int numeric_ids;
36 extern char *usermap;
37 extern char *groupmap;
38
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
47 #define NFLAGS_WILD_NAME_MATCH (1<<0)
48 #define NFLAGS_NAME_MATCH (1<<1)
49
50 struct idlist {
51         struct idlist *next;
52         const char *name;
53         id_t id, id2;
54         uint16 flags;
55 };
56
57 static struct idlist *uidlist, *uidmap;
58 static struct idlist *gidlist, *gidmap;
59
60 static struct idlist *add_to_list(struct idlist **root, id_t id, const char *name,
61                                   id_t id2, uint16 flags)
62 {
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;
70         node->flags = flags;
71         *root = node;
72         return node;
73 }
74
75 /* turn a uid into a user name */
76 static const char *uid_to_name(uid_t uid)
77 {
78         struct passwd *pass = getpwuid(uid);
79         if (pass)
80                 return strdup(pass->pw_name);
81         return NULL;
82 }
83
84 /* turn a gid into a group name */
85 static const char *gid_to_name(gid_t gid)
86 {
87         struct group *grp = getgrgid(gid);
88         if (grp)
89                 return strdup(grp->gr_name);
90         return NULL;
91 }
92
93 static int is_in_group(gid_t gid)
94 {
95 #ifdef HAVE_GETGROUPS
96         static gid_t last_in = GID_NONE, last_out;
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) {
104                 gid_t mygid = MY_GID();
105                 if ((ngroups = getgroups(0, NULL)) < 0)
106                         ngroups = 0;
107                 gidset = new_array(GETGROUPS_T, ngroups+1);
108                 if (!gidset)
109                         out_of_memory("is_in_group");
110                 if (ngroups > 0)
111                         ngroups = getgroups(ngroups, gidset);
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;
116                 }
117                 if (n == ngroups)
118                         gidset[ngroups++] = mygid;
119                 if (DEBUG_GTE(OWN, 2)) {
120                         int pos;
121                         char *gidbuf = new_array(char, ngroups*21+32);
122                         if (!gidbuf)
123                                 out_of_memory("is_in_group");
124                         pos = snprintf(gidbuf, 32, "process has %d gid%s: ",
125                                        ngroups, ngroups == 1? "" : "s");
126                         for (n = 0; n < ngroups; n++) {
127                                 pos += snprintf(gidbuf+pos, 21, " %d", (int)gidset[n]);
128                         }
129                         rprintf(FINFO, "%s\n", gidbuf);
130                         free(gidbuf);
131                 }
132         }
133
134         last_in = gid;
135         for (n = 0; n < ngroups; n++) {
136                 if (gidset[n] == gid)
137                         return last_out = 1;
138         }
139         return last_out = 0;
140
141 #else
142         static gid_t mygid = GID_NONE;
143         if (mygid == GID_NONE) {
144                 mygid = MY_GID();
145                 if (DEBUG_GTE(OWN, 2))
146                         rprintf(FINFO, "process has gid %u\n", (unsigned)mygid);
147         }
148         return gid == mygid;
149 #endif
150 }
151
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)
155 {
156         struct idlist *node;
157         int flag;
158         id_t id2;
159
160         if (!name)
161                 name = "";
162
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;
178         }
179         if (node)
180                 id2 = node->id2;
181         else if (*name && id) {
182                 if (idlist_ptr == &uidlist) {
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;
191
192         flag = idlist_ptr == &gidlist && !am_root && !is_in_group(id2) ? FLAG_SKIP_GROUP : 0;
193         node = add_to_list(idlist_ptr, id, *name ? name : NULL, id2, flag);
194
195         if (DEBUG_GTE(OWN, 2)) {
196                 rprintf(FINFO, "%sid %u(%s) maps to %u\n",
197                         idlist_ptr == &uidlist ? "u" : "g",
198                         (unsigned)id, name, (unsigned)id2);
199         }
200
201         return node;
202 }
203
204 /* this function is a definate candidate for a faster algorithm */
205 uid_t match_uid(uid_t uid)
206 {
207         static uid_t last_in = -1, last_out = -1;
208         struct idlist *list;
209
210         if (uid == last_in)
211                 return last_out;
212
213         last_in = uid;
214
215         for (list = uidlist; list; list = list->next) {
216                 if (list->id == uid)
217                         break;
218         }
219
220         if (!list)
221                 list = recv_add_id(&uidlist, uidmap, uid, NULL);
222
223         return last_out = list->id2;
224 }
225
226 gid_t match_gid(gid_t gid, uint16 *flags_ptr)
227 {
228         static struct idlist *last = NULL;
229         struct idlist *list;
230
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)
239                         list = recv_add_id(&gidlist, gidmap, gid, NULL);
240                 last = list;
241         }
242
243         if (flags_ptr && list->flags & FLAG_SKIP_GROUP)
244                 *flags_ptr |= FLAG_SKIP_GROUP;
245         return list->id2;
246 }
247
248 /* Add a uid to the list of uids.  Only called on sending side. */
249 const char *add_uid(uid_t uid)
250 {
251         struct idlist *list;
252         struct idlist *node;
253
254         if (uid == 0)   /* don't map root */
255                 return NULL;
256
257         for (list = uidlist; list; list = list->next) {
258                 if (list->id == uid)
259                         return NULL;
260         }
261
262         node = add_to_list(&uidlist, uid, uid_to_name(uid), 0, 0);
263         return node->name;
264 }
265
266 /* Add a gid to the list of gids.  Only called on sending side. */
267 const char *add_gid(gid_t gid)
268 {
269         struct idlist *list;
270         struct idlist *node;
271
272         if (gid == 0)   /* don't map root */
273                 return NULL;
274
275         for (list = gidlist; list; list = list->next) {
276                 if (list->id == gid)
277                         return NULL;
278         }
279
280         node = add_to_list(&gidlist, gid, gid_to_name(gid), 0, 0);
281         return node->name;
282 }
283
284 /* send a complete uid/gid mapping to the peer */
285 void send_id_list(int f)
286 {
287         struct idlist *list;
288
289         if (preserve_uid || preserve_acls) {
290                 int len;
291                 /* we send sequences of uid/byte-length/name */
292                 for (list = uidlist; list; list = list->next) {
293                         if (!list->name)
294                                 continue;
295                         len = strlen(list->name);
296                         write_varint30(f, list->id);
297                         write_byte(f, len);
298                         write_buf(f, list->name, len);
299                 }
300
301                 /* terminate the uid list with a 0 uid. We explicitly exclude
302                  * 0 from the list */
303                 write_varint30(f, 0);
304         }
305
306         if (preserve_gid || preserve_acls) {
307                 int len;
308                 for (list = gidlist; list; list = list->next) {
309                         if (!list->name)
310                                 continue;
311                         len = strlen(list->name);
312                         write_varint30(f, list->id);
313                         write_byte(f, len);
314                         write_buf(f, list->name, len);
315                 }
316                 write_varint30(f, 0);
317         }
318 }
319
320 uid_t recv_user_name(int f, uid_t uid)
321 {
322         struct idlist *node;
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);
328         if (numeric_ids < 0) {
329                 free(name);
330                 name = NULL;
331         }
332         node = recv_add_id(&uidlist, uidmap, uid, name); /* node keeps name's memory */
333         return node->id2;
334 }
335
336 gid_t recv_group_name(int f, gid_t gid, uint16 *flags_ptr)
337 {
338         struct idlist *node;
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);
344         if (numeric_ids < 0) {
345                 free(name);
346                 name = NULL;
347         }
348         node = recv_add_id(&gidlist, gidmap, gid, name); /* node keeps name's memory */
349         if (flags_ptr && node->flags & FLAG_SKIP_GROUP)
350                 *flags_ptr |= FLAG_SKIP_GROUP;
351         return node->id2;
352 }
353
354 /* recv a complete uid/gid mapping from the peer and map the uid/gid
355  * in the file list to local names */
356 void recv_id_list(int f, struct file_list *flist)
357 {
358         id_t id;
359         int i;
360
361         if ((preserve_uid || preserve_acls) && numeric_ids <= 0) {
362                 /* read the uid list */
363                 while ((id = read_varint30(f)) != 0)
364                         recv_user_name(f, id);
365         }
366
367         if ((preserve_gid || preserve_acls) && numeric_ids <= 0) {
368                 /* read the gid list */
369                 while ((id = read_varint30(f)) != 0)
370                         recv_group_name(f, id, NULL);
371         }
372
373         /* Now convert all the uids/gids from sender values to our values. */
374 #ifdef SUPPORT_ACLS
375         if (preserve_acls && (!numeric_ids || usermap || groupmap))
376                 match_acl_ids();
377 #endif
378         if (am_root && preserve_uid && (!numeric_ids || usermap)) {
379                 for (i = 0; i < flist->used; i++)
380                         F_OWNER(flist->files[i]) = match_uid(F_OWNER(flist->files[i]));
381         }
382         if (preserve_gid && (!am_root || !numeric_ids || groupmap)) {
383                 for (i = 0; i < flist->used; i++) {
384                         F_GROUP(flist->files[i]) = match_gid(F_GROUP(flist->files[i]),
385                                                              &flist->files[i]->flags);
386                 }
387         }
388 }
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 }