Commit | Line | Data |
---|---|---|
c44efccb WD |
1 | Depends-On-Patch: acls.diff |
2 | Depends-On-Patch: xattrs.diff | |
3 | ||
4 | This patch adds a new option: --fake-super, which tells rsync to copy in a | |
5 | fake super-user mode that stores various file attributes in an extended- | |
6 | attribute value instead of as real file-system attributes. The items | |
7 | affected are: | |
8 | ||
9 | mode the real mode of a file is always (666 & umask) while | |
10 | the real mode of a directory is always (777 & umask). | |
11 | ||
12 | rdev devices and special files are created as zero-length | |
13 | normal files. | |
14 | ||
15 | uid the real owner is always left unchanged. | |
16 | ||
17 | gid the real group is always left unchanged. | |
18 | ||
19 | A daemon can set "fake super = yes" in the rsync.conf file for any module | |
20 | that you'd like to run without root perms while pretending it has them (the | |
21 | client cannot affect this). | |
22 | ||
23 | The --fake-super option only affects the side where the option is used. To | |
24 | affect the remote side of a remote-shell connection, specify an rsync path: | |
25 | ||
26 | rsync -av --rsync-path='rsync --fake-super' /src/ host:/dest/ | |
27 | ||
28 | For a local copy where you want to affect only one side or the other, | |
29 | you'll need to turn the copy into a remote copy to localhost. | |
30 | ||
31 | After applying this patch, run these commands for a successful build: | |
32 | ||
33 | ./prepare-source | |
34 | ./configure | |
35 | make | |
36 | ||
37 | --- old/Makefile.in | |
38 | +++ new/Makefile.in | |
39 | @@ -41,7 +41,7 @@ popt_OBJS=popt/findme.o popt/popt.o po | |
40 | popt/popthelp.o popt/poptparse.o | |
41 | OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@ | |
42 | ||
43 | -TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o | |
44 | +TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattr.o | |
45 | ||
46 | # Programs we must have to run the test cases | |
47 | CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \ | |
48 | @@ -83,11 +83,11 @@ getgroups$(EXEEXT): getgroups.o | |
49 | getfsdev$(EXEEXT): getfsdev.o | |
50 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS) | |
51 | ||
52 | -TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o | |
53 | +TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o lib/sysxattr.o | |
54 | trimslash$(EXEEXT): $(TRIMSLASH_OBJ) | |
55 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS) | |
56 | ||
57 | -T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o t_stub.o lib/compat.o lib/snprintf.o | |
58 | +T_UNSAFE_OBJ = t_unsafe.o syscall.o util.o t_stub.o lib/compat.o lib/snprintf.o lib/sysxattr.o | |
59 | t_unsafe$(EXEEXT): $(T_UNSAFE_OBJ) | |
60 | $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(T_UNSAFE_OBJ) $(LIBS) | |
61 | ||
62 | --- old/clientserver.c | |
63 | +++ new/clientserver.c | |
64 | @@ -284,6 +284,9 @@ static int rsync_module(int f_in, int f_ | |
65 | pid_t pre_exec_pid = 0; | |
66 | char *request = NULL; | |
67 | ||
68 | + if (lp_fake_super(i) != 0) | |
69 | + am_root = -1; | |
70 | + | |
71 | if (!allow_access(addr, host, lp_hosts_allow(i), lp_hosts_deny(i))) { | |
72 | rprintf(FLOG, "rsync denied on module %s from %s (%s)\n", | |
73 | name, host, addr); | |
74 | --- old/generator.c | |
75 | +++ new/generator.c | |
76 | @@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi | |
77 | recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT, | |
78 | code, f_out); | |
79 | ||
80 | - /* We need to ensure that any dirs we create have writeable | |
81 | + /* We need to ensure that any dirs we create have rwx | |
82 | * permissions during the time we are putting files within | |
83 | * them. This is then fixed after the transfer is done. */ | |
84 | #ifdef HAVE_CHMOD | |
85 | - if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR) | |
86 | + if (am_root <= 0 && S_ISDIR(file->mode) | |
87 | + && (file->mode & S_IRWXU) != S_IRWXU | |
88 | && dir_tweaking) { | |
89 | - mode_t mode = file->mode | S_IWUSR; /* user write */ | |
90 | + mode_t mode = file->mode | S_IRWXU; /* user rwx */ | |
91 | char *fname = local_name ? local_name : fbuf; | |
92 | if (do_chmod(fname, mode) < 0) { | |
93 | rsyserr(FERROR, errno, | |
94 | --- old/loadparm.c | |
95 | +++ new/loadparm.c | |
96 | @@ -150,6 +150,7 @@ typedef struct | |
97 | int syslog_facility; | |
98 | int timeout; | |
99 | ||
100 | + BOOL fake_super; | |
101 | BOOL ignore_errors; | |
102 | BOOL ignore_nonreadable; | |
103 | BOOL list; | |
104 | @@ -197,6 +198,7 @@ static service sDefault = | |
105 | /* syslog_facility; */ LOG_DAEMON, | |
106 | /* timeout; */ 0, | |
107 | ||
108 | + /* fake_super; */ False, | |
109 | /* ignore_errors; */ False, | |
110 | /* ignore_nonreadable; */ False, | |
111 | /* list; */ True, | |
112 | @@ -298,6 +300,7 @@ static struct parm_struct parm_table[] = | |
113 | {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0}, | |
114 | {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0}, | |
115 | {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0}, | |
116 | + {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0}, | |
117 | {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0}, | |
118 | {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0}, | |
119 | {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0}, | |
120 | @@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max | |
121 | FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity) | |
122 | FN_LOCAL_INTEGER(lp_timeout, timeout) | |
123 | ||
124 | +FN_LOCAL_BOOL(lp_fake_super, fake_super) | |
125 | FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors) | |
126 | FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable) | |
127 | FN_LOCAL_BOOL(lp_list, list) | |
128 | @@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals | |
129 | ||
130 | if (pszFname) | |
131 | pstrcpy(n2,pszFname); | |
132 | - else if (am_server && !am_root) | |
133 | + else if (am_server && am_root <= 0) | |
134 | pstrcpy(n2,RSYNCD_USERCONF); | |
135 | else | |
136 | pstrcpy(n2,RSYNCD_SYSCONF); | |
137 | --- old/options.c | |
138 | +++ new/options.c | |
139 | @@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION; | |
140 | int sparse_files = 0; | |
141 | int do_compression = 0; | |
142 | int def_compress_level = Z_DEFAULT_COMPRESSION; | |
143 | -int am_root = 0; | |
144 | +int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */ | |
145 | int am_server = 0; | |
146 | int am_sender = 0; | |
147 | int am_generator = 0; | |
148 | @@ -330,6 +330,7 @@ void usage(enum logcode F) | |
149 | rprintf(F," -t, --times preserve times\n"); | |
150 | rprintf(F," -O, --omit-dir-times omit directories when preserving times\n"); | |
151 | rprintf(F," --super receiver attempts super-user activities\n"); | |
152 | + rprintf(F," --fake-super fake root by storing/reading ownership/etc in EAs\n"); | |
153 | rprintf(F," -S, --sparse handle sparse files efficiently\n"); | |
154 | rprintf(F," -n, --dry-run show what would have been transferred\n"); | |
155 | rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n"); | |
156 | @@ -454,6 +455,7 @@ static struct poptOption long_options[] | |
157 | {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 }, | |
158 | {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 }, | |
159 | {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 }, | |
160 | + {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 }, | |
161 | {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 }, | |
162 | {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 }, | |
163 | {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 }, | |
164 | --- old/rsync.c | |
165 | +++ new/rsync.c | |
166 | @@ -197,6 +197,12 @@ int set_file_attrs(char *fname, struct f | |
167 | (long)sxp->st.st_gid, (long)file->gid); | |
168 | } | |
169 | } | |
170 | + if (am_root < 0) { | |
171 | + if (change_uid) | |
172 | + sxp->st.st_uid = file->uid; | |
173 | + if (change_gid) | |
174 | + sxp->st.st_gid = file->gid; | |
175 | + } else | |
176 | if (do_lchown(fname, | |
177 | change_uid ? file->uid : sxp->st.st_uid, | |
178 | change_gid ? file->gid : sxp->st.st_gid) != 0) { | |
179 | @@ -206,7 +212,7 @@ int set_file_attrs(char *fname, struct f | |
180 | change_uid ? "chown" : "chgrp", | |
181 | full_fname(fname)); | |
182 | goto cleanup; | |
183 | - } | |
184 | + } else | |
185 | /* a lchown had been done - we have to re-stat if the | |
186 | * destination had the setuid or setgid bits set due | |
187 | * to the side effect of the chown call */ | |
188 | @@ -237,7 +243,16 @@ int set_file_attrs(char *fname, struct f | |
189 | ||
190 | #ifdef HAVE_CHMOD | |
191 | if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) { | |
192 | - int ret = do_chmod(fname, new_mode); | |
193 | + int ret; | |
194 | + if (am_root < 0) { | |
195 | + mode_t mode = 0666 & ~orig_umask; | |
196 | + if ((sxp->st.st_mode & CHMOD_BITS) != mode) | |
197 | + ret = do_chmod(fname, mode); | |
198 | + else | |
199 | + ret = 0; | |
200 | + sxp->st.st_mode = new_mode; | |
201 | + } else | |
202 | + ret = do_chmod(fname, new_mode); | |
203 | if (ret < 0) { | |
204 | rsyserr(FERROR, errno, | |
205 | "failed to set permissions on %s", | |
206 | @@ -249,6 +264,22 @@ int set_file_attrs(char *fname, struct f | |
207 | } | |
208 | #endif | |
209 | ||
210 | + if (am_root < 0) { | |
211 | + int write_it = updated; | |
212 | + if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode)) { | |
213 | + if (file->u.rdev != sxp->st.st_rdev) { | |
214 | + sxp->st.st_rdev = file->u.rdev; | |
215 | + write_it = 1; | |
216 | + } | |
217 | + } else | |
218 | + sxp->st.st_rdev = 0; | |
219 | + if (write_it && set_stat_xattr(fname, &sxp->st) < 0) { | |
220 | + rsyserr(FERROR, errno, | |
221 | + "write of stat xattr failed for %s", | |
222 | + full_fname(fname)); | |
223 | + } | |
224 | + } | |
225 | + | |
226 | if (verbose > 1 && flags & ATTRS_REPORT) { | |
227 | if (updated) | |
228 | rprintf(FCLIENT, "%s\n", fname); | |
229 | --- old/rsync.h | |
230 | +++ new/rsync.h | |
231 | @@ -35,6 +35,8 @@ | |
232 | ||
233 | #define BACKUP_SUFFIX "~" | |
234 | ||
235 | +#define FAKE_XATTR "user.fake%stat" | |
236 | + | |
237 | /* a non-zero CHAR_OFFSET makes the rolling sum stronger, but is | |
238 | incompatible with older versions :-( */ | |
239 | #define CHAR_OFFSET 0 | |
240 | --- old/syscall.c | |
241 | +++ new/syscall.c | |
242 | @@ -22,12 +22,14 @@ | |
243 | */ | |
244 | ||
245 | #include "rsync.h" | |
246 | +#include "lib/sysxattr.h" | |
247 | ||
248 | #if !defined MKNOD_CREATES_SOCKETS && defined HAVE_SYS_UN_H | |
249 | #include <sys/un.h> | |
250 | #endif | |
251 | ||
252 | extern int dry_run; | |
253 | +extern int am_root; | |
254 | extern int read_only; | |
255 | extern int list_only; | |
256 | extern int preserve_perms; | |
257 | @@ -79,6 +81,15 @@ int do_mknod(char *pathname, mode_t mode | |
258 | { | |
259 | if (dry_run) return 0; | |
260 | RETURN_ERROR_IF_RO_OR_LO; | |
261 | + | |
262 | + /* For --fake-super, we create a normal file with mode 0600. */ | |
263 | + if (am_root < 0) { | |
264 | + int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); | |
265 | + if (fd < 0 || close(fd) < 0) | |
266 | + return -1; | |
267 | + return 0; | |
268 | + } | |
269 | + | |
270 | #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO | |
271 | if (S_ISFIFO(mode)) | |
272 | return mkfifo(pathname, mode); | |
273 | @@ -133,6 +144,7 @@ int do_open(const char *pathname, int fl | |
274 | } | |
275 | ||
276 | #ifdef HAVE_CHMOD | |
277 | + | |
278 | int do_chmod(const char *path, mode_t mode) | |
279 | { | |
280 | int code; | |
281 | @@ -215,23 +227,69 @@ int do_mkstemp(char *template, mode_t pe | |
282 | #endif | |
283 | } | |
284 | ||
285 | +int get_stat_xattr(const char *fname, STRUCT_STAT *st) | |
286 | +{ | |
287 | + int mode, rdev, uid, gid, len; | |
288 | + char buf[256]; | |
289 | + | |
290 | + len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1); | |
291 | + if (len < 0 || len >= (int)sizeof buf) { | |
292 | + if (errno == ENOTSUP || errno == ENOATTR) | |
293 | + return 0; | |
294 | + return -1; | |
295 | + } | |
296 | + buf[len] = '\0'; | |
297 | + | |
298 | + if (sscanf(buf, "%o %d %d:%d", &mode, &rdev, &uid, &gid) != 4) { | |
299 | + errno = EINVAL; | |
300 | + return -1; | |
301 | + } | |
302 | + | |
303 | + st->st_mode = mode; | |
304 | + st->st_rdev = rdev; | |
305 | + st->st_uid = uid; | |
306 | + st->st_gid = gid; | |
307 | + | |
308 | + return 0; | |
309 | +} | |
310 | + | |
311 | +int set_stat_xattr(const char *fname, STRUCT_STAT *st) | |
312 | +{ | |
313 | + char buf[256]; | |
314 | + int len; | |
315 | + if (dry_run) return 0; | |
316 | + RETURN_ERROR_IF_RO_OR_LO; | |
317 | + len = snprintf(buf, sizeof buf, "%o %d %d:%d", | |
318 | + (int)st->st_mode, (int)st->st_rdev, | |
319 | + (int)st->st_uid, (int)st->st_gid); | |
320 | + return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0); | |
321 | +} | |
322 | + | |
323 | int do_stat(const char *fname, STRUCT_STAT *st) | |
324 | { | |
325 | + int ret; | |
326 | #ifdef USE_STAT64_FUNCS | |
327 | - return stat64(fname, st); | |
328 | + ret = stat64(fname, st); | |
329 | #else | |
330 | - return stat(fname, st); | |
331 | + ret = stat(fname, st); | |
332 | #endif | |
333 | + if (am_root < 0 && ret == 0) | |
334 | + get_stat_xattr(fname, st); | |
335 | + return ret; | |
336 | } | |
337 | ||
338 | int do_lstat(const char *fname, STRUCT_STAT *st) | |
339 | { | |
340 | #ifdef SUPPORT_LINKS | |
341 | + int ret; | |
342 | # ifdef USE_STAT64_FUNCS | |
343 | - return lstat64(fname, st); | |
344 | + ret = lstat64(fname, st); | |
345 | # else | |
346 | - return lstat(fname, st); | |
347 | + ret = lstat(fname, st); | |
348 | # endif | |
349 | + if (am_root < 0 && ret == 0) | |
350 | + get_stat_xattr(fname, st); | |
351 | + return ret; | |
352 | #else | |
353 | return do_stat(fname, st); | |
354 | #endif | |
355 | --- old/t_unsafe.c | |
356 | +++ new/t_unsafe.c | |
357 | @@ -24,7 +24,11 @@ | |
358 | ||
359 | #include "rsync.h" | |
360 | ||
361 | -int dry_run, read_only, list_only, verbose; | |
362 | +int dry_run = 0; | |
363 | +int am_root = 0; | |
364 | +int read_only = 0; | |
365 | +int list_only = 0; | |
366 | +int verbose = 0; | |
367 | int preserve_perms = 0; | |
368 | ||
369 | int | |
370 | --- old/tls.c | |
371 | +++ new/tls.c | |
372 | @@ -39,6 +39,7 @@ | |
373 | ||
374 | /* These are to make syscall.o shut up. */ | |
375 | int dry_run = 0; | |
376 | +int am_root = 0; /* TODO: add option to set this to -1. */ | |
377 | int read_only = 1; | |
378 | int list_only = 0; | |
379 | int preserve_perms = 0; | |
380 | --- old/trimslash.c | |
381 | +++ new/trimslash.c | |
382 | @@ -23,6 +23,7 @@ | |
383 | ||
384 | /* These are to make syscall.o shut up. */ | |
385 | int dry_run = 0; | |
386 | +int am_root = 0; | |
387 | int read_only = 1; | |
388 | int list_only = 0; | |
389 | int preserve_perms = 0; | |
390 | --- old/xattr.c | |
391 | +++ new/xattr.c | |
392 | @@ -26,6 +26,7 @@ | |
393 | #ifdef SUPPORT_XATTRS | |
394 | ||
395 | extern int dry_run; | |
396 | +extern int am_root; | |
397 | extern unsigned int file_struct_len; | |
398 | ||
399 | #define RSYNC_XAL_INITIAL 5 | |
400 | @@ -130,9 +131,15 @@ static int rsync_xal_get(const char *fna | |
401 | if (name_size == 0) | |
402 | return 0; | |
403 | for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) { | |
404 | - rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL); | |
405 | + rsync_xa *rxas; | |
406 | ||
407 | len = strlen(name) + 1; | |
408 | + if (am_root < 0 && len == sizeof FAKE_XATTR | |
409 | + && name[9] == '%' && strcmp(name, FAKE_XATTR) == 0) | |
410 | + continue; | |
411 | + | |
412 | + rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL); | |
413 | + | |
414 | datum_size = sys_lgetxattr(fname, name, NULL, 0); | |
415 | if (datum_size < 0) { | |
416 | if (errno == ENOTSUP) | |
417 | @@ -285,10 +292,19 @@ void receive_xattr(struct file_struct *f | |
418 | out_of_memory("receive_xattr"); | |
419 | read_buf(f, ptr, name_len); | |
420 | read_buf(f, ptr + name_len, datum_len); | |
421 | + | |
422 | + if (am_root < 0 && name_len == sizeof FAKE_XATTR | |
423 | + && ptr[9] == '%' && strcmp(ptr, FAKE_XATTR) == 0) { | |
424 | + free(ptr); | |
425 | + temp_xattr.count--; | |
426 | + continue; | |
427 | + } | |
428 | + | |
429 | rxa->name_len = name_len; | |
430 | rxa->datum_len = datum_len; | |
431 | rxa->name = ptr; | |
432 | rxa->datum = ptr + name_len; | |
433 | + | |
434 | #ifdef HAVE_OSX_XATTRS | |
435 | if (strncmp(rxa->name, unique_prefix, upre_len) == 0) { | |
436 | rxa->name_len -= upre_len; |