9c74bfef501a451d565805acee69d17dd812e18d
[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-2007 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 version 2 as
10  * published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
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
28 #ifdef HAVE_GETGROUPS
29 # ifndef GETGROUPS_T
30 #  define GETGROUPS_T gid_t
31 # endif
32 #endif
33
34 extern int verbose;
35 extern int am_root;
36 extern int preserve_uid;
37 extern int preserve_gid;
38 extern int numeric_ids;
39
40 struct idlist {
41         struct idlist *next;
42         int id, id2;
43         char *name;
44 };
45
46 static struct idlist *uidlist;
47 static struct idlist *gidlist;
48
49 static struct idlist *add_to_list(struct idlist **root, int id, char *name,
50                                   int id2)
51 {
52         struct idlist *node = new(struct idlist);
53         if (!node)
54                 out_of_memory("add_to_list");
55         node->next = *root;
56         node->name = name;
57         node->id = id;
58         node->id2 = id2;
59         *root = node;
60         return node;
61 }
62
63 /* turn a uid into a user name */
64 static char *uid_to_name(uid_t uid)
65 {
66         struct passwd *pass = getpwuid(uid);
67         if (pass)
68                 return strdup(pass->pw_name);
69         return NULL;
70 }
71
72 /* turn a gid into a group name */
73 static char *gid_to_name(gid_t gid)
74 {
75         struct group *grp = getgrgid(gid);
76         if (grp)
77                 return strdup(grp->gr_name);
78         return NULL;
79 }
80
81 static uid_t map_uid(uid_t id, char *name)
82 {
83         uid_t uid;
84         if (id != 0 && name_to_uid(name, &uid))
85                 return uid;
86         return id;
87 }
88
89 static gid_t map_gid(gid_t id, char *name)
90 {
91         gid_t gid;
92         if (id != 0 && name_to_gid(name, &gid))
93                 return gid;
94         return id;
95 }
96
97 static int is_in_group(gid_t gid)
98 {
99 #ifdef HAVE_GETGROUPS
100         static gid_t last_in = GID_NONE, last_out;
101         static int ngroups = -2;
102         static GETGROUPS_T *gidset;
103         int n;
104
105         if (gid == last_in)
106                 return last_out;
107         if (ngroups < -1) {
108                 gid_t mygid = MY_GID();
109                 if ((ngroups = getgroups(0, NULL)) < 0)
110                         ngroups = 0;
111                 gidset = new_array(GETGROUPS_T, ngroups+1);
112                 if (!gidset)
113                         out_of_memory("is_in_group");
114                 if (ngroups > 0)
115                         ngroups = getgroups(ngroups, gidset);
116                 /* The default gid might not be in the list on some systems. */
117                 for (n = 0; n < ngroups; n++) {
118                         if (gidset[n] == mygid)
119                                 break;
120                 }
121                 if (n == ngroups)
122                         gidset[ngroups++] = mygid;
123                 if (verbose > 3) {
124                         int pos;
125                         char *gidbuf = new_array(char, ngroups*21+32);
126                         if (!gidbuf)
127                                 out_of_memory("is_in_group");
128                         pos = snprintf(gidbuf, 32, "process has %d gid%s: ",
129                                        ngroups, ngroups == 1? "" : "s");
130                         for (n = 0; n < ngroups; n++) {
131                                 pos += snprintf(gidbuf+pos, 21, " %d", (int)gidset[n]);
132                         }
133                         rprintf(FINFO, "%s\n", gidbuf);
134                         free(gidbuf);
135                 }
136         }
137
138         last_in = gid;
139         for (n = 0; n < ngroups; n++) {
140                 if (gidset[n] == gid)
141                         return last_out = 1;
142         }
143         return last_out = 0;
144
145 #else
146         static gid_t mygid = GID_NONE;
147         if (mygid == GID_NONE) {
148                 mygid = MY_GID();
149                 if (verbose > 3)
150                         rprintf(FINFO, "process has gid %d\n", (int)mygid);
151         }
152         return gid == mygid;
153 #endif
154 }
155
156 /* Add a uid to the list of uids.  Only called on receiving side. */
157 static uid_t recv_add_uid(uid_t id, char *name)
158 {
159         uid_t id2 = name ? map_uid(id, name) : id;
160         struct idlist *node;
161
162         node = add_to_list(&uidlist, (int)id, name, (int)id2);
163
164         if (verbose > 3) {
165                 rprintf(FINFO, "uid %d(%s) maps to %d\n",
166                         (int)id, name ? name : "", (int)id2);
167         }
168
169         return id2;
170 }
171
172 /* Add a gid to the list of gids.  Only called on receiving side. */
173 static gid_t recv_add_gid(gid_t id, char *name)
174 {
175         gid_t id2 = name ? map_gid(id, name) : id;
176         struct idlist *node;
177
178         if (!am_root && !is_in_group(id2))
179                 id2 = GID_NONE;
180         node = add_to_list(&gidlist, (int)id, name, (int)id2);
181
182         if (verbose > 3) {
183                 rprintf(FINFO, "gid %d(%s) maps to %d\n",
184                         (int)id, name ? name : "", (int)id2);
185         }
186
187         return id2;
188 }
189
190 /* this function is a definate candidate for a faster algorithm */
191 uid_t match_uid(uid_t uid)
192 {
193         static uid_t last_in, last_out;
194         struct idlist *list;
195
196         if (uid == 0)
197                 return 0;
198
199         if (uid == last_in)
200                 return last_out;
201
202         last_in = uid;
203
204         for (list = uidlist; list; list = list->next) {
205                 if (list->id == (int)uid)
206                         return last_out = (uid_t)list->id2;
207         }
208
209         return last_out = uid;
210 }
211
212 gid_t match_gid(gid_t gid)
213 {
214         static gid_t last_in = GID_NONE, last_out = GID_NONE;
215         struct idlist *list;
216
217         if (gid == GID_NONE)
218                 return GID_NONE;
219
220         if (gid == last_in)
221                 return last_out;
222
223         last_in = gid;
224
225         for (list = gidlist; list; list = list->next) {
226                 if (list->id == (int)gid)
227                         return last_out = (gid_t)list->id2;
228         }
229
230         return last_out = recv_add_gid(gid, NULL);
231 }
232
233 /* Add a uid to the list of uids.  Only called on sending side. */
234 char *add_uid(uid_t uid)
235 {
236         struct idlist *list;
237         struct idlist *node;
238
239         if (uid == 0)   /* don't map root */
240                 return NULL;
241
242         for (list = uidlist; list; list = list->next) {
243                 if (list->id == (int)uid)
244                         return NULL;
245         }
246
247         node = add_to_list(&uidlist, (int)uid, uid_to_name(uid), 0);
248         return node->name;
249 }
250
251 /* Add a gid to the list of gids.  Only called on sending side. */
252 char *add_gid(gid_t gid)
253 {
254         struct idlist *list;
255         struct idlist *node;
256
257         if (gid == 0)   /* don't map root */
258                 return NULL;
259
260         for (list = gidlist; list; list = list->next) {
261                 if (list->id == (int)gid)
262                         return NULL;
263         }
264
265         node = add_to_list(&gidlist, (int)gid, gid_to_name(gid), 0);
266         return node->name;
267 }
268
269 /* send a complete uid/gid mapping to the peer */
270 void send_uid_list(int f)
271 {
272         struct idlist *list;
273
274         if (preserve_uid) {
275                 int len;
276                 /* we send sequences of uid/byte-length/name */
277                 for (list = uidlist; list; list = list->next) {
278                         if (!list->name)
279                                 continue;
280                         len = strlen(list->name);
281                         write_int(f, list->id);
282                         write_byte(f, len);
283                         write_buf(f, list->name, len);
284                 }
285
286                 /* terminate the uid list with a 0 uid. We explicitly exclude
287                  * 0 from the list */
288                 write_int(f, 0);
289         }
290
291         if (preserve_gid) {
292                 int len;
293                 for (list = gidlist; list; list = list->next) {
294                         if (!list->name)
295                                 continue;
296                         len = strlen(list->name);
297                         write_int(f, list->id);
298                         write_byte(f, len);
299                         write_buf(f, list->name, len);
300                 }
301                 write_int(f, 0);
302         }
303 }
304
305 uid_t recv_user_name(int f, uid_t uid)
306 {
307         int len = read_byte(f);
308         char *name = new_array(char, len+1);
309         if (!name)
310                 out_of_memory("recv_user_name");
311         read_sbuf(f, name, len);
312         return recv_add_uid(uid, name); /* node keeps name's memory */
313 }
314
315 gid_t recv_group_name(int f, gid_t gid)
316 {
317         int len = read_byte(f);
318         char *name = new_array(char, len+1);
319         if (!name)
320                 out_of_memory("recv_group_name");
321         read_sbuf(f, name, len);
322         return recv_add_gid(gid, name); /* node keeps name's memory */
323 }
324
325 /* recv a complete uid/gid mapping from the peer and map the uid/gid
326  * in the file list to local names */
327 void recv_uid_list(int f, struct file_list *flist)
328 {
329         int id, i;
330
331         if (preserve_uid && !numeric_ids) {
332                 /* read the uid list */
333                 while ((id = read_int(f)) != 0)
334                         recv_user_name(f, (uid_t)id);
335         }
336
337         if (preserve_gid && !numeric_ids) {
338                 /* read the gid list */
339                 while ((id = read_int(f)) != 0)
340                         recv_group_name(f, (gid_t)id);
341         }
342
343         /* Now convert all the uids/gids from sender values to our values. */
344         if (am_root && preserve_uid && !numeric_ids) {
345                 for (i = 0; i < flist->count; i++)
346                         F_OWNER(flist->files[i]) = match_uid(F_UID(flist->files[i]));
347         }
348         if (preserve_gid && (!am_root || !numeric_ids)) {
349                 for (i = 0; i < flist->count; i++)
350                         F_GROUP(flist->files[i]) = match_gid(F_GID(flist->files[i]));
351         }
352 }