Commit | Line | Data |
---|---|---|
ade7292a | 1 | /* |
f6c34742 AT |
2 | Copyright (C) Andrew Tridgell 1996 |
3 | Copyright (C) Paul Mackerras 1996 | |
ade7292a | 4 | |
f6c34742 AT |
5 | This program is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published by | |
7 | the Free Software Foundation; either version 2 of the License, or | |
8 | (at your option) any later version. | |
ade7292a | 9 | |
f6c34742 AT |
10 | This program is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | GNU General Public License for more details. | |
ade7292a | 14 | |
f6c34742 AT |
15 | You should have received a copy of the GNU General Public License |
16 | along with this program; if not, write to the Free Software | |
17 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
18 | */ | |
19 | ||
20 | /* handle the mapping of uid/gid and user/group names between systems. | |
21 | If the source username/group does not exist on the target then use | |
22 | the numeric ids. Never do any mapping for uid=0 or gid=0 as these | |
23 | are special. | |
24 | */ | |
25 | ||
26 | #include "rsync.h" | |
27 | ||
1df395f7 WD |
28 | #ifdef HAVE_GETGROUPS |
29 | # if !defined(GETGROUPS_T) | |
30 | # define GETGROUPS_T gid_t | |
31 | # endif | |
72fc7ec5 WD |
32 | # ifndef NGROUPS_MAX |
33 | /* It ought to be defined, but just in case. */ | |
34 | # define NGROUPS_MAX 32 | |
35 | # endif | |
36 | #endif | |
37 | ||
84fa865c | 38 | extern int verbose; |
f6c34742 AT |
39 | extern int preserve_uid; |
40 | extern int preserve_gid; | |
41 | extern int numeric_ids; | |
460f6b99 | 42 | extern int am_root; |
f6c34742 AT |
43 | |
44 | struct idlist { | |
45 | struct idlist *next; | |
46 | int id, id2; | |
47 | char *name; | |
48 | }; | |
49 | ||
50 | static struct idlist *uidlist; | |
51 | static struct idlist *gidlist; | |
52 | ||
53 | static struct idlist *add_list(int id, char *name) | |
54 | { | |
58cadc86 | 55 | struct idlist *list = new(struct idlist); |
f6c34742 AT |
56 | if (!list) out_of_memory("add_list"); |
57 | list->next = NULL; | |
58 | list->name = strdup(name); | |
59 | if (!list->name) out_of_memory("add_list"); | |
60 | list->id = (int)id; | |
61 | return list; | |
62 | } | |
63 | ||
64 | ||
65 | ||
66 | /* turn a uid into a user name */ | |
67 | static char *uid_to_name(uid_t uid) | |
68 | { | |
69 | struct passwd *pass = getpwuid(uid); | |
70 | if (pass) return(pass->pw_name); | |
71 | return NULL; | |
72 | } | |
73 | ||
74 | /* turn a gid into a group name */ | |
75 | static char *gid_to_name(gid_t gid) | |
76 | { | |
77 | struct group *grp = getgrgid(gid); | |
78 | if (grp) return(grp->gr_name); | |
79 | return NULL; | |
80 | } | |
81 | ||
f6c34742 AT |
82 | static int map_uid(int id, char *name) |
83 | { | |
8ef4ffd6 | 84 | uid_t uid; |
0be976ec | 85 | if (uid != 0 && name_to_uid(name, &uid)) |
8ef4ffd6 | 86 | return uid; |
f6c34742 AT |
87 | return id; |
88 | } | |
89 | ||
90 | static int map_gid(int id, char *name) | |
91 | { | |
8ef4ffd6 | 92 | gid_t gid; |
0be976ec | 93 | if (gid != 0 && name_to_gid(name, &gid)) |
8ef4ffd6 | 94 | return gid; |
f6c34742 AT |
95 | return id; |
96 | } | |
97 | ||
5b540e86 WD |
98 | static int is_in_group(gid_t gid) |
99 | { | |
1df395f7 | 100 | #ifdef HAVE_GETGROUPS |
a2687b64 | 101 | static gid_t last_in = GID_NONE, last_out; |
5b540e86 WD |
102 | static int ngroups = -2; |
103 | static GETGROUPS_T *gidset; | |
104 | int n; | |
105 | ||
106 | if (gid == last_in) | |
107 | return last_out; | |
108 | if (ngroups < -1) { | |
670d8abf | 109 | gid_t mygid = MY_GID(); |
dbd8811b WD |
110 | if ((ngroups = getgroups(0, 0)) < 0) |
111 | ngroups = 0; | |
72fc7ec5 WD |
112 | gidset = new_array(GETGROUPS_T, ngroups+1); |
113 | if (ngroups > 0) | |
5b540e86 | 114 | ngroups = getgroups(ngroups, gidset); |
72fc7ec5 WD |
115 | /* The default gid might not be in the list on some systems. */ |
116 | for (n = 0; n < ngroups; n++) { | |
117 | if (gidset[n] == mygid) | |
118 | break; | |
5b540e86 | 119 | } |
72fc7ec5 WD |
120 | if (n == ngroups) |
121 | gidset[ngroups++] = mygid; | |
84fa865c | 122 | if (verbose > 3) { |
187e9c24 WD |
123 | char gidbuf[NGROUPS_MAX*16+32]; |
124 | int pos; | |
125 | sprintf(gidbuf, "process has %d gid%s: ", | |
126 | ngroups, ngroups == 1? "" : "s"); | |
127 | pos = strlen(gidbuf); | |
84fa865c | 128 | for (n = 0; n < ngroups; n++) { |
187e9c24 WD |
129 | sprintf(gidbuf+pos, " %ld", (long)gidset[n]); |
130 | pos += strlen(gidbuf+pos); | |
84fa865c | 131 | } |
187e9c24 | 132 | rprintf(FINFO, "%s\n", gidbuf); |
84fa865c | 133 | } |
5b540e86 WD |
134 | } |
135 | ||
136 | last_in = gid; | |
5b540e86 | 137 | for (n = 0; n < ngroups; n++) { |
a2687b64 WD |
138 | if (gidset[n] == gid) |
139 | return last_out = 1; | |
5b540e86 | 140 | } |
a2687b64 | 141 | return last_out = 0; |
5b540e86 WD |
142 | |
143 | #else | |
a2687b64 | 144 | static gid_t mygid = GID_NONE; |
187e9c24 | 145 | if (mygid == GID_NONE) { |
670d8abf | 146 | mygid = MY_GID(); |
187e9c24 WD |
147 | if (verbose > 3) |
148 | rprintf(FINFO, "process has gid %ld\n", (long)mygid); | |
149 | } | |
a2687b64 | 150 | return gid == mygid; |
5b540e86 WD |
151 | #endif |
152 | } | |
153 | ||
ade7292a WD |
154 | /* this function is a definate candidate for a faster algorithm */ |
155 | static uid_t match_uid(uid_t uid) | |
156 | { | |
157 | static uid_t last_in, last_out; | |
158 | struct idlist *list = uidlist; | |
159 | ||
160 | if (uid == last_in) | |
161 | return last_out; | |
162 | ||
163 | last_in = uid; | |
164 | ||
165 | while (list) { | |
166 | if (list->id == (int)uid) { | |
167 | last_out = (uid_t)list->id2; | |
168 | return last_out; | |
169 | } | |
170 | list = list->next; | |
171 | } | |
172 | ||
173 | last_out = uid; | |
174 | return last_out; | |
175 | } | |
176 | ||
f6c34742 AT |
177 | static gid_t match_gid(gid_t gid) |
178 | { | |
a2687b64 | 179 | static gid_t last_in = GID_NONE, last_out = GID_NONE; |
f6c34742 AT |
180 | struct idlist *list = gidlist; |
181 | ||
a2687b64 WD |
182 | if (gid == last_in) |
183 | return last_out; | |
f6c34742 AT |
184 | |
185 | last_in = gid; | |
186 | ||
187 | while (list) { | |
188 | if (list->id == (int)gid) { | |
189 | last_out = (gid_t)list->id2; | |
190 | return last_out; | |
191 | } | |
192 | list = list->next; | |
193 | } | |
194 | ||
dbd8811b | 195 | if (am_root || is_in_group(gid)) |
460f6b99 DD |
196 | last_out = gid; |
197 | else | |
a60e2dca | 198 | last_out = GID_NONE; |
f6c34742 AT |
199 | return last_out; |
200 | } | |
201 | ||
202 | /* add a uid to the list of uids */ | |
203 | void add_uid(uid_t uid) | |
204 | { | |
205 | struct idlist *list = uidlist; | |
206 | char *name; | |
207 | ||
208 | if (numeric_ids) return; | |
209 | ||
210 | /* don't map root */ | |
211 | if (uid==0) return; | |
212 | ||
213 | if (!list) { | |
214 | if (!(name = uid_to_name(uid))) return; | |
215 | uidlist = add_list((int)uid, name); | |
216 | return; | |
217 | } | |
218 | ||
219 | while (list->next) { | |
220 | if (list->id == (int)uid) return; | |
221 | list = list->next; | |
222 | } | |
223 | ||
224 | if (list->id == (int)uid) return; | |
225 | ||
226 | if (!(name = uid_to_name(uid))) return; | |
227 | ||
228 | list->next = add_list((int)uid, name); | |
229 | } | |
230 | ||
231 | /* add a gid to the list of gids */ | |
232 | void add_gid(gid_t gid) | |
233 | { | |
234 | struct idlist *list = gidlist; | |
235 | char *name; | |
236 | ||
237 | if (numeric_ids) return; | |
238 | ||
239 | /* don't map root */ | |
240 | if (gid==0) return; | |
241 | ||
242 | if (!list) { | |
243 | if (!(name = gid_to_name(gid))) return; | |
244 | gidlist = add_list((int)gid, name); | |
245 | return; | |
246 | } | |
247 | ||
248 | while (list->next) { | |
249 | if (list->id == (int)gid) return; | |
250 | list = list->next; | |
251 | } | |
252 | ||
253 | if (list->id == (int)gid) return; | |
254 | ||
255 | if (!(name = gid_to_name(gid))) return; | |
256 | ||
257 | list->next = add_list((int)gid, name); | |
258 | } | |
259 | ||
260 | ||
261 | /* send a complete uid/gid mapping to the peer */ | |
262 | void send_uid_list(int f) | |
263 | { | |
264 | struct idlist *list; | |
265 | ||
266 | if (numeric_ids) return; | |
267 | ||
268 | if (preserve_uid) { | |
269 | /* we send sequences of uid/byte-length/name */ | |
270 | list = uidlist; | |
271 | while (list) { | |
272 | int len = strlen(list->name); | |
273 | write_int(f, list->id); | |
274 | write_byte(f, len); | |
275 | write_buf(f, list->name, len); | |
276 | list = list->next; | |
277 | } | |
278 | ||
279 | /* terminate the uid list with a 0 uid. We explicitly exclude | |
84fa865c | 280 | * 0 from the list */ |
f6c34742 AT |
281 | write_int(f, 0); |
282 | } | |
283 | ||
284 | if (preserve_gid) { | |
285 | list = gidlist; | |
286 | while (list) { | |
287 | int len = strlen(list->name); | |
288 | write_int(f, list->id); | |
289 | write_byte(f, len); | |
290 | write_buf(f, list->name, len); | |
291 | list = list->next; | |
292 | } | |
293 | write_int(f, 0); | |
294 | } | |
295 | } | |
296 | ||
297 | /* recv a complete uid/gid mapping from the peer and map the uid/gid | |
84fa865c | 298 | * in the file list to local names */ |
f6c34742 AT |
299 | void recv_uid_list(int f, struct file_list *flist) |
300 | { | |
301 | int id, i; | |
302 | char *name; | |
303 | struct idlist *list; | |
304 | ||
305 | if (numeric_ids) return; | |
306 | ||
307 | if (preserve_uid) { | |
308 | /* read the uid list */ | |
309 | list = uidlist; | |
5b540e86 | 310 | while ((id = read_int(f)) != 0) { |
f6c34742 | 311 | int len = read_byte(f); |
58cadc86 | 312 | name = new_array(char, len+1); |
f6c34742 | 313 | if (!name) out_of_memory("recv_uid_list"); |
575f2fca | 314 | read_sbuf(f, name, len); |
f6c34742 AT |
315 | if (!list) { |
316 | uidlist = add_list(id, name); | |
317 | list = uidlist; | |
318 | } else { | |
319 | list->next = add_list(id, name); | |
320 | list = list->next; | |
321 | } | |
322 | list->id2 = map_uid(id, name); | |
323 | free(name); | |
f6c34742 | 324 | } |
84fa865c WD |
325 | if (verbose > 3) { |
326 | for (list = uidlist; list; list = list->next) { | |
555b0e20 WD |
327 | rprintf(FINFO, "uid %ld (%s) maps to %ld\n", |
328 | (long)list->id, list->name, | |
84fa865c WD |
329 | (long)list->id2); |
330 | } | |
331 | } | |
f6c34742 AT |
332 | } |
333 | ||
334 | ||
335 | if (preserve_gid) { | |
336 | /* and the gid list */ | |
337 | list = gidlist; | |
5b540e86 | 338 | while ((id = read_int(f)) != 0) { |
f6c34742 | 339 | int len = read_byte(f); |
58cadc86 | 340 | name = new_array(char, len+1); |
f6c34742 | 341 | if (!name) out_of_memory("recv_uid_list"); |
575f2fca | 342 | read_sbuf(f, name, len); |
f6c34742 AT |
343 | if (!list) { |
344 | gidlist = add_list(id, name); | |
345 | list = gidlist; | |
346 | } else { | |
347 | list->next = add_list(id, name); | |
348 | list = list->next; | |
349 | } | |
350 | list->id2 = map_gid(id, name); | |
5b540e86 | 351 | if (!am_root && !is_in_group(list->id2)) |
a60e2dca | 352 | list->id2 = GID_NONE; |
f6c34742 | 353 | free(name); |
f6c34742 | 354 | } |
84fa865c WD |
355 | if (verbose > 3) { |
356 | for (list = gidlist; list; list = list->next) { | |
555b0e20 WD |
357 | rprintf(FINFO, "gid %ld (%s) maps to %ld\n", |
358 | (long)list->id, list->name, | |
84fa865c WD |
359 | (long)list->id2); |
360 | } | |
361 | } | |
f6c34742 AT |
362 | } |
363 | ||
460f6b99 | 364 | if (!(am_root && preserve_uid) && !preserve_gid) return; |
f6c34742 AT |
365 | |
366 | /* now convert the uid/gid of all files in the list to the mapped | |
84fa865c | 367 | * uid/gid */ |
0be976ec WD |
368 | for (i = 0; i < flist->count; i++) { |
369 | if (am_root && preserve_uid && flist->files[i]->uid != 0) | |
3ec4dd97 | 370 | flist->files[i]->uid = match_uid(flist->files[i]->uid); |
0be976ec | 371 | if (preserve_gid && (!am_root || flist->files[i]->gid != 0)) |
3ec4dd97 | 372 | flist->files[i]->gid = match_gid(flist->files[i]->gid); |
f6c34742 AT |
373 | } |
374 | } |