]
)
+dnl
+dnl Check for headers, functions and libraries required to support
+dnl keeping readall capabilities
+dnl
+AC_CHECK_HEADERS(sys/prctl.h sys/capability.h)
+AC_CHECK_FUNCS(prctl setreuid)
+AC_CHECK_LIB([cap], [cap_set_proc], [CAP_LIBS="-lcap"], [CAP_LIBS=])
+if test x$CAP_LIBS = x-lcap; then
+ AC_DEFINE(HAVE_LIBCAP, 1, [Define if you have libcap])
+fi
+AC_SUBST(CAP_LIBS)
+
AC_SUBST(FDLIBS)
AC_DEFINE(FDLIBS)
DEBUG=@DEBUG@
GETTEXT_LIBS = @LIBINTL@
+CAP_LIBS = @CAP_LIBS@
PYTHON_LIBS = @PYTHON_LIBS@
PYTHON_INC = @PYTHON_INCDIR@
@echo "Linking $@ ..."
$(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -L../lib -L../cats -L../findlib -o $@ $(SVROBJS) \
-lbacfind -lbacsql -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(DB_LIBS) $(LIBS) \
- $(WRAPLIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+ $(WRAPLIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) $(CAP_LIBS)
static-bacula-dir: Makefile $(SVROBJS) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbacpy$(DEFAULT_ARCHIVE_TYPE) ../cats/libbacsql$(DEFAULT_ARCHIVE_TYPE) ../findlib/libbacfind$(DEFAULT_ARCHIVE_TYPE)
$(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -static -L../lib -L../cats -L../findlib -o $@ $(SVROBJS) \
-lbacfind -lbacsql -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(DB_LIBS) $(LIBS) \
- $(WRAPLIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+ $(WRAPLIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) $(CAP_LIBS)
strip $@
Makefile: $(srcdir)/Makefile.in $(topdir)/config.status
load_dir_plugins(director->plugin_directory);
- drop(uid, gid); /* reduce privileges if requested */
+ drop(uid, gid, false); /* reduce privileges if requested */
/* If we are in testing mode, we don't try to fix the catalog */
cat_op mode=(test_config)?CHECK_CONNECTION:UPDATE_AND_FIX;
# these are the objects that are changed by the .configure process
EXTRAOBJS = @OBJLIST@
+CAP_LIBS = @CAP_LIBS@
FDLIBS = @FDLIBS@ # extra libs for File daemon
# extra items for linking on Win32
@echo "Linking $@ ..."
$(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -L../lib -L../findlib -o $@ $(SVROBJS) \
$(WIN32LIBS) $(FDLIBS) -lbacfind -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(LIBS) \
- $(DLIB) $(WRAPLIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+ $(DLIB) $(WRAPLIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) $(CAP_LIBS)
static-bacula-fd: Makefile $(SVROBJS) ../findlib/libbacfind.a ../lib/libbacpy$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE) @WIN32@
$(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -static -L../lib -L../findlib -o $@ $(SVROBJS) \
$(WIN32LIBS) $(FDLIBS) -lbacfind -lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(LIBS) \
- $(DLIB) $(WRAPLIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+ $(DLIB) $(WRAPLIBS) $(GETTEXT_LIBS) $(OPENSSL_LIBS) $(CAP_LIBS)
strip $@
Makefile: $(srcdir)/Makefile.in $(topdir)/config.status
" -dt print timestamp in debug output\n"
" -f run in foreground (for debugging)\n"
" -g groupid\n"
+" -k keep readall capabilities\n"
" -s no signals (for debugging)\n"
" -t test configuration file and exit\n"
" -u userid\n"
{
int ch;
bool test_config = false;
+ bool keep_readall_caps = false;
char *uid = NULL;
char *gid = NULL;
#ifdef HAVE_PYTHON
init_msg(NULL, NULL);
daemon_start_time = time(NULL);
- while ((ch = getopt(argc, argv, "c:d:fg:stu:v?")) != -1) {
+ while ((ch = getopt(argc, argv, "c:d:fg:kstu:v?")) != -1) {
switch (ch) {
case 'c': /* configuration file */
if (configfile != NULL) {
gid = optarg;
break;
+ case 'k':
+ keep_readall_caps = true;
+ break;
+
case 's':
no_signals = true;
break;
usage();
}
+ if (!uid && keep_readall_caps) {
+ Emsg0(M_ERROR, 0, _("-k option has no meaning without -u option.\n"));
+ exit(1);
+ }
+
server_tid = pthread_self();
if (!no_signals) {
init_signals(terminate_filed);
load_fd_plugins(me->plugin_directory);
- drop(uid, gid);
+ drop(uid, gid, keep_readall_caps);
#ifdef BOMB
me += 1000000;
thisdir = src/lib
DEBUG=@DEBUG@
+CAP_LIBS = @CAP_LIBS@
first_rule: all
dummy:
cram-md5.c crc32.c crypto.c daemon.c edit.c fnmatch.c \
guid_to_name.c hmac.c jcr.c lex.c alist.c dlist.c \
md5.c message.c mem_pool.c openssl.c \
- plugins.c queue.c bregex.c \
+ plugins.c priv.c queue.c bregex.c \
rwlock.c scan.c serial.c sha1.c \
signal.c smartall.c rblist.c tls.c tree.c \
util.c var.c watchdog.c workq.c btimers.c \
libbac.la: Makefile $(LIBBAC_OBJS)
@echo "Making $@ ..."
- $(LIBTOOL_LINK) $(CXX) $(DEFS) $(DEBUG) $(LDFLAGS) -o $@ $(LIBBAC_OBJS) -export-dynamic -rpath $(libdir) -version-info $(LIBBAC_LT_CURRENT):$(LIBBAC_LT_REVISION):$(LIBBAC_LT_AGE) $(WRAPLIBS)
+ $(LIBTOOL_LINK) $(CXX) $(DEFS) $(DEBUG) $(LDFLAGS) -o $@ $(LIBBAC_OBJS) -export-dynamic -rpath $(libdir) -version-info $(LIBBAC_LT_CURRENT):$(LIBBAC_LT_REVISION):$(LIBBAC_LT_AGE) $(WRAPLIBS) $(CAP_LIBS)
libbaccfg.a: $(LIBBACCFG_OBJS)
@echo "Making $@ ..."
#include "bacula.h"
-#ifdef HAVE_AIX_OS
-extern "C" int initgroups(const char *,int);
-#endif
-
static pthread_mutex_t timer_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t timer = PTHREAD_COND_INITIALIZER;
}
-/*
- * Drop to privilege new userid and new gid if non-NULL
- */
-void drop(char *uname, char *gname)
-{
-#if defined(HAVE_PWD_H) && defined(HAVE_GRP_H)
- struct passwd *passw = NULL;
- struct group *group = NULL;
- gid_t gid;
- uid_t uid;
- char username[1000];
-
- Dmsg2(900, "uname=%s gname=%s\n", uname?uname:"NONE", gname?gname:"NONE");
- if (!uname && !gname) {
- return; /* Nothing to do */
- }
-
- if (uname) {
- if ((passw = getpwnam(uname)) == NULL) {
- berrno be;
- Emsg2(M_ERROR_TERM, 0, _("Could not find userid=%s: ERR=%s\n"), uname,
- be.bstrerror());
- }
- } else {
- if ((passw = getpwuid(getuid())) == NULL) {
- berrno be;
- Emsg1(M_ERROR_TERM, 0, _("Could not find password entry. ERR=%s\n"),
- be.bstrerror());
- } else {
- uname = passw->pw_name;
- }
- }
- /* Any OS uname pointer may get overwritten, so save name, uid, and gid */
- bstrncpy(username, uname, sizeof(username));
- uid = passw->pw_uid;
- gid = passw->pw_gid;
- if (gname) {
- if ((group = getgrnam(gname)) == NULL) {
- berrno be;
- Emsg2(M_ERROR_TERM, 0, _("Could not find group=%s: ERR=%s\n"), gname,
- be.bstrerror());
- }
- gid = group->gr_gid;
- }
- if (initgroups(username, gid)) {
- berrno be;
- if (gname) {
- Emsg3(M_ERROR_TERM, 0, _("Could not initgroups for group=%s, userid=%s: ERR=%s\n"),
- gname, username, be.bstrerror());
- } else {
- Emsg2(M_ERROR_TERM, 0, _("Could not initgroups for userid=%s: ERR=%s\n"),
- username, be.bstrerror());
- }
- }
- if (gname) {
- if (setgid(gid)) {
- berrno be;
- Emsg2(M_ERROR_TERM, 0, _("Could not set group=%s: ERR=%s\n"), gname,
- be.bstrerror());
- }
- }
- if (setuid(uid)) {
- berrno be;
- Emsg1(M_ERROR_TERM, 0, _("Could not set specified userid: %s\n"), username);
- }
-#endif
-}
-
-
/* BSDI does not have this. This is a *poor* simulation */
#ifndef HAVE_STRTOLL
long long int
--- /dev/null
+/*
+ Bacula® - The Network Backup Solution
+
+ Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
+
+ The main author of Bacula is Kern Sibbald, with contributions from
+ many others, a complete list can be found in the file AUTHORS.
+ This program is Free Software; you can redistribute it and/or
+ modify it under the terms of version two of the GNU General Public
+ License as published by the Free Software Foundation and included
+ in the file LICENSE.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+ Bacula® is a registered trademark of Kern Sibbald.
+ The licensor of Bacula is the Free Software Foundation Europe
+ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
+ Switzerland, email:ftf@fsfeurope.org.
+*/
+
+#include "bacula.h"
+
+#undef ENABLE_KEEP_READALL_CAPS_SUPPORT
+#if defined(HAVE_SYS_PRCTL_H) && defined(HAVE_SYS_CAPABILITY_H) && \
+ defined(HAVE_PRCTL) && defined(HAVE_SETREUID) && defined(HAVE_LIBCAP)
+# include <sys/prctl.h>
+# include <sys/capability.h>
+# if defined(PR_SET_KEEPCAPS)
+# define ENABLE_KEEP_READALL_CAPS_SUPPORT
+# endif
+#endif
+
+#ifdef HAVE_AIX_OS
+extern "C" int initgroups(const char *,int);
+#endif
+
+/*
+ * Lower privileges by switching to new UID and GID if non-NULL.
+ * If requested, keep readall capabilities after switch.
+ */
+void drop(char *uname, char *gname, bool keep_readall_caps)
+{
+ struct passwd *passw = NULL;
+ struct group *group = NULL;
+ gid_t gid;
+ uid_t uid;
+ char username[1000];
+
+ Dmsg2(900, "uname=%s gname=%s\n", uname?uname:"NONE", gname?gname:"NONE");
+ if (!uname && !gname) {
+ return; /* Nothing to do */
+ }
+
+ if (uname) {
+ if ((passw = getpwnam(uname)) == NULL) {
+ berrno be;
+ Emsg2(M_ERROR_TERM, 0, _("Could not find userid=%s: ERR=%s\n"), uname,
+ be.bstrerror());
+ }
+ } else {
+ if ((passw = getpwuid(getuid())) == NULL) {
+ berrno be;
+ Emsg1(M_ERROR_TERM, 0, _("Could not find password entry. ERR=%s\n"),
+ be.bstrerror());
+ } else {
+ uname = passw->pw_name;
+ }
+ }
+ /* Any OS uname pointer may get overwritten, so save name, uid, and gid */
+ bstrncpy(username, uname, sizeof(username));
+ uid = passw->pw_uid;
+ gid = passw->pw_gid;
+ if (gname) {
+ if ((group = getgrnam(gname)) == NULL) {
+ berrno be;
+ Emsg2(M_ERROR_TERM, 0, _("Could not find group=%s: ERR=%s\n"), gname,
+ be.bstrerror());
+ }
+ gid = group->gr_gid;
+ }
+ if (initgroups(username, gid)) {
+ berrno be;
+ if (gname) {
+ Emsg3(M_ERROR_TERM, 0, _("Could not initgroups for group=%s, userid=%s: ERR=%s\n"),
+ gname, username, be.bstrerror());
+ } else {
+ Emsg2(M_ERROR_TERM, 0, _("Could not initgroups for userid=%s: ERR=%s\n"),
+ username, be.bstrerror());
+ }
+ }
+ if (gname) {
+ if (setgid(gid)) {
+ berrno be;
+ Emsg2(M_ERROR_TERM, 0, _("Could not set group=%s: ERR=%s\n"), gname,
+ be.bstrerror());
+ }
+ }
+ if (keep_readall_caps) {
+#ifdef ENABLE_KEEP_READALL_CAPS_SUPPORT
+ cap_t caps;
+
+ if (prctl(PR_SET_KEEPCAPS, 1)) {
+ berrno be;
+ Emsg1(M_ERROR_TERM, 0, _("prctl failed: ERR=%s\n"), be.bstrerror());
+ }
+ if (setreuid(uid, uid)) {
+ berrno be;
+ Emsg1(M_ERROR_TERM, 0, _("setreuid failed: ERR=%s\n"), be.bstrerror());
+ }
+ if (!(caps = cap_from_text("cap_dac_read_search=ep"))) {
+ berrno be;
+ Emsg1(M_ERROR_TERM, 0, _("cap_from_text failed: ERR=%s\n"), be.bstrerror());
+ }
+ if (cap_set_proc(caps) < 0) {
+ berrno be;
+ Emsg1(M_ERROR_TERM, 0, _("cap_set_proc failed: ERR=%s\n"), be.bstrerror());
+ }
+ cap_free(caps);
+#else
+ Emsg0(M_ERROR_TERM, 0, _("Keep readall capabilities is not implemented on this platform yet\n"));
+#endif
+ } else if (setuid(uid)) {
+ berrno be;
+ Emsg1(M_ERROR_TERM, 0, _("Could not set specified userid: %s\n"), username);
+ }
+}
int pool_sprintf (char *pool_buf, const char *fmt, ...);
void create_pid_file (char *dir, const char *progname, int port);
int delete_pid_file (char *dir, const char *progname, int port);
-void drop (char *uid, char *gid);
+void drop (char *uid, char *gid, bool keep_readall_caps);
int bmicrosleep (int32_t sec, int32_t usec);
char *bfgets (char *s, int size, FILE *fd);
void make_unique_filename (POOLMEM **name, int Id, char *what);
# these are the objects that are changed by the .configure process
EXTRAOBJS = @OBJLIST@
+CAP_LIBS = @CAP_LIBS@
FDLIBS=@FDLIBS@
@echo "Linking $@ ..."
$(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -L../lib -o $@ $(SDOBJS) $(FDLIBS) \
-lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \
- $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+ $(GETTEXT_LIBS) $(OPENSSL_LIBS) $(CAP_LIBS)
static-bacula-sd: Makefile $(SDOBJS) ../lib/libbacpy$(DEFAULT_ARCHIVE_TYPE) ../lib/libbaccfg$(DEFAULT_ARCHIVE_TYPE) ../lib/libbac$(DEFAULT_ARCHIVE_TYPE)
$(LIBTOOL_LINK) $(CXX) $(WLDFLAGS) $(LDFLAGS) -static -L../lib -o $@ $(SDOBJS) $(FDLIBS) \
-lbacpy -lbaccfg -lbac -lm $(PYTHON_LIBS) $(DLIB) $(LIBS) $(WRAPLIBS) \
- $(GETTEXT_LIBS) $(OPENSSL_LIBS)
+ $(GETTEXT_LIBS) $(OPENSSL_LIBS) $(CAP_LIBS)
strip $@
btape.o: btape.c
load_sd_plugins(me->plugin_directory);
- drop(uid, gid);
+ drop(uid, gid, false);
cleanup_old_files();