patch for linux capabilities

Michael Glasgow glasgow at beer.net
Mon Jan 26 19:36:23 GMT 2004


I was wondering if it might be possible for an rsync developer to
look over the attached patch (tested on Linux 2.4.24 against the
rsync-2.6.0 release), and offer suggestions on how I could improve it.

Basically I want to use Linux finer grained capabilities to retain
only CAP_SYS_CHROOT & CAP_DAC_READ_SEARCH when rsync drops root
privs.  That way I can take whole system backups as a (mostly)
unprivileged user.

Probably it should be some kind of config file option or something
rather than a simple compile-time option.  Also it would be nice
to drop root much earlier if possible, but where?

Any suggestions are much appreciated!

BTW, editing rsync source was not my first choice on how to tackle
this.  I would much rather use a small separate wrapper program to
set the capabilities and then exec rsync, but alas capabilities
cannot be passed across an exec.  And it doesn't look like that's
going to change anytime soon either:

http://www.ussg.iu.edu/hypermail/linux/kernel/0310.2/index.html#1119

-- 
Michael Glasgow <glasgow at beer.net>
-------------- next part --------------
diff -urN rsync-2.6.0.orig/clientserver.c rsync-2.6.0/clientserver.c
--- rsync-2.6.0.orig/clientserver.c	2003-09-10 23:00:19.000000000 -0500
+++ rsync-2.6.0/clientserver.c	2004-01-26 11:27:53.000000000 -0600
@@ -25,6 +25,18 @@
  * rsyncd.
  **/
 
+#ifdef HAVE_LINUX_CAPS
+#ifdef _POSIX_SOURCE
+#undef _POSIX_SOURCE     
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#define _POSIX_SOURCE
+#else
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#endif
+#endif
+
 #include "rsync.h"
 
 extern int module_id;
@@ -217,6 +229,10 @@
 	int start_glob=0;
 	int ret;
 	char *request=NULL;
+#ifdef HAVE_LINUX_CAPS
+	cap_t cp;
+	cap_value_t newcaps[2] = { CAP_SYS_CHROOT, CAP_DAC_READ_SEARCH };
+#endif
 	extern int am_sender;
 	extern int am_server;
 	extern int am_daemon;
@@ -373,12 +389,46 @@
 		}
 #endif
 
+#ifdef HAVE_LINUX_CAPS
+		if (setreuid(uid, 0)) {
+			rsyserr(FERROR, errno, "setreuid(%d,0) failed", (int) uid);
+			io_printf(f_out, "@ERROR: setreuid failed\n");
+			return -1;
+		}
+		if( prctl(PR_SET_KEEPCAPS, 1) < 0 ) {
+			rsyserr(FERROR, errno, "prctl failed");
+			io_printf(f_out, "@ERROR: prctl failed\n");
+			return -1;
+		}
+		if( (cp = cap_init()) == NULL ) {
+			rsyserr(FERROR, errno, "cap_init failed");
+			io_printf(f_out, "@ERROR: cap_init failed\n");
+			return -1;
+		}
+		if( cap_set_flag(cp, CAP_PERMITTED, 2, newcaps, CAP_SET) < 0 ||
+		cap_set_flag(cp, CAP_INHERITABLE, 2, newcaps, CAP_SET) < 0 ) {
+			rsyserr(FERROR, errno, "cap_set_flag failed");
+			io_printf(f_out, "@ERROR: cap_set_flag failed\n");
+			return -1;
+		}
+		if( cap_set_proc(cp) < 0 ) {
+			rsyserr(FERROR, errno, "cap_set_proc failed");
+			io_printf(f_out, "@ERROR: cap_set_proc failed\n");
+			return -1;
+		}
+#endif
+
 		if (setuid(uid)) {
 			rsyserr(FERROR, errno, "setuid %d failed", (int) uid);
 			io_printf(f_out, "@ERROR: setuid failed\n");
 			return -1;
 		}
 
+#ifdef HAVE_LINUX_CAPS
+		cap_set_flag(cp, CAP_EFFECTIVE, 2, newcaps, CAP_SET);
+		cap_set_proc(cp);
+#endif
+
 		am_root = (getuid() == 0);
 	}
 


More information about the rsync mailing list