From 81640ab2b9ad954d4952aed43a70d7874da1c463 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 26 Feb 2009 00:50:24 +0200 Subject: Switch to non-privileged UID/GID before startup. * src/userprivs.c: New file. * src/Makefile.am (wydawca_SOURCES): Add userprivs.c * src/config.c (cb_access_method_params): Add missing gl_list_iterator_free. (cb_user, cb_supp_groups): New callbacks. (wydawca_kw): New keywords: user and group. * src/wydawca.c (wydawca_uid, wydawca_gid) (wydawca_supp_groupc, wydawca_supp_groups): New variables. (wydawca_set_uid, wydawca_set_gid, wydawca_set_privs) (wydawca_set_triplet_privs, wydawca_set_root_privs): Remove. (main): --dry-run implies --cron. Switch to non-privileged UID/GID before startup. * src/wydawca.h (wydawca_uid, wydawca_gid) (wydawca_supp_groupc, wydawca_supp_groups): New declarations. * src/mail.c (do_notify): Duplicate admin_address, it gets freed in do_notify. * src/directive.c, src/diskio.c, src/lock.c, src/triplet.c: Update. --- src/Makefile.am | 1 + src/config.c | 82 +++++++++++++++++++++++++++++++++++++++ src/directive.c | 2 - src/diskio.c | 68 ++++++++++++-------------------- src/lock.c | 2 +- src/mail.c | 2 +- src/triplet.c | 6 +-- src/userprivs.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/wydawca.c | 90 +++++++++++------------------------------- src/wydawca.h | 14 ++++--- 10 files changed, 260 insertions(+), 125 deletions(-) create mode 100644 src/userprivs.c (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index c91669e..993ce0d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,6 +36,7 @@ wydawca_SOURCES=\ sql.h\ tcpwrap.c\ triplet.c\ + userprivs.c\ verify.c\ wydawca.c\ wydawca.h\ diff --git a/src/config.c b/src/config.c index b1f339f..b6e2533 100644 --- a/src/config.c +++ b/src/config.c @@ -978,6 +978,7 @@ cb_access_method_params (enum gconf_callback_command cmd, meth->parmv[i] = xstrdup (vp->v.string); } + gl_list_iterator_free (&itr); meth->parmv[i] = NULL; } return 0; @@ -1224,6 +1225,82 @@ cb_spool (enum gconf_callback_command cmd, } +static int +cb_user (enum gconf_callback_command cmd, + gconf_locus_t *locus, + void *varptr, + gconf_value_t *value, + void *cb_data) +{ + int rc; + struct passwd *pw; + + if (assert_string_arg (locus, cmd, value)) + return 1; + + pw = getpwnam (value->v.string); + if (!pw) + { + gconf_error (locus, 0, _("no such user: %s"), value->v.string); + return 1; + } + + wydawca_uid = pw->pw_uid; + wydawca_gid = pw->pw_gid; + return 0; +} + +static int +cb_supp_groups (enum gconf_callback_command cmd, + gconf_locus_t *locus, + void *varptr, + gconf_value_t *value, + void *cb_data) +{ + if (cmd != gconf_callback_set_value) + { + gconf_error (locus, 0, _("Unexpected block statement")); + return 1; + } + if (!value || value->type != GCONF_TYPE_LIST) + { + gconf_error (locus, 0, _("expected list value")); + return 1; + } + + wydawca_supp_groupc = gl_list_size (value->v.list); + if (wydawca_supp_groupc == 0) + wydawca_supp_groups = NULL; + else + { + int i; + gl_list_iterator_t itr; + const void *p; + + wydawca_supp_groups = xcalloc (wydawca_supp_groupc, + sizeof (wydawca_supp_groups[0])); + itr = gl_list_iterator (value->v.list); + for (i = 0; gl_list_iterator_next (&itr, &p, NULL); i++) + { + const gconf_value_t *vp = p; + struct group *grp; + + if (assert_string_arg (locus, cmd, vp)) + break; + grp = getgrnam (vp->v.string); + if (!grp) + { + gconf_error (locus, 0, _("no such group: %s"), value->v.string); + break; + } + wydawca_supp_groups[i] = grp->gr_gid; + } + gl_list_iterator_free (&itr); + } + return 0; +} + + static struct gconf_keyword wydawca_kw[] = { { "daemon", NULL, N_("Enable daemon mode"), @@ -1237,6 +1314,11 @@ static struct gconf_keyword wydawca_kw[] = { { "pidfile", N_("file"), N_("Set pid file name"), gconf_type_string, &pidfile }, + { "user", N_("name"), N_("Run with UID and GID of this user"), + gconf_type_string, NULL, 0, cb_user }, + { "group", NULL, N_("Retain these supplementary groups"), + gconf_type_string|GCONF_LIST, NULL, 0, cb_supp_groups }, + { "locking", NULL, N_("Enable or disable locking"), gconf_type_bool, &enable_locking }, { "lockdir", N_("dir"), N_("Set directory for lock files"), diff --git a/src/directive.c b/src/directive.c index 2915fee..416095f 100644 --- a/src/directive.c +++ b/src/directive.c @@ -337,9 +337,7 @@ process_directives (struct file_triplet *trp, const struct spool *spool) break; case filename_dir: - wydawca_set_root_privs (); rc = verify_detached_signature (trp, spool); - wydawca_set_triplet_privs (trp); if (rc == 0) { if (move_file (trp, spool, file_dist, relative_dir) diff --git a/src/diskio.c b/src/diskio.c index fbd1050..35ba71e 100644 --- a/src/diskio.c +++ b/src/diskio.c @@ -58,9 +58,9 @@ concat_dir (const char *base, const char *name, size_t *pbaselen) } /* Create the directory DIR, eventually creating all intermediate directories - starting from DIR + BASELEN, with owner UID and GID. */ + starting from DIR + BASELEN. */ int -create_hierarchy (char *dir, size_t baselen, uid_t uid, gid_t gid) +create_hierarchy (char *dir, size_t baselen) { int rc; struct stat st; @@ -92,7 +92,7 @@ create_hierarchy (char *dir, size_t baselen, uid_t uid, gid_t gid) *p = 0; } - rc = create_hierarchy (dir, baselen, uid, gid); + rc = create_hierarchy (dir, baselen); if (rc == 0) { if (p) @@ -103,11 +103,6 @@ create_hierarchy (char *dir, size_t baselen, uid_t uid, gid_t gid) dir, strerror (errno)); rc = 1; } - if (chown (dir, uid, gid)) - { - logmsg (LOG_NOTICE, _("cannot change ownership of %s: %s"), - dir, strerror (errno)); - } } return rc; } @@ -116,18 +111,14 @@ create_hierarchy (char *dir, size_t baselen, uid_t uid, gid_t gid) NAME). Use UID and GID as owner ids. Do nothing if dry_run_mode is set. */ char * -create_directory (const char *base, const char *name, uid_t uid, gid_t gid) +create_directory (const char *base, const char *name) { size_t baselen; char *dir = concat_dir (base, name, &baselen); if (!dry_run_mode) { - int rc; - wydawca_set_root_privs (); - rc = create_hierarchy (dir, baselen, uid, gid); - wydawca_set_privs (uid, gid); - if (rc) + if (create_hierarchy (dir, baselen)) { free (dir); dir = NULL; @@ -137,9 +128,9 @@ create_directory (const char *base, const char *name, uid_t uid, gid_t gid) } -/* Copy FILE to DST_FILE, creating the latter with owner UID and GID. */ +/* Copy FILE to DST_FILE. */ int -copy_file (const char *file, const char *dst_file, uid_t uid, gid_t gid) +copy_file (const char *file, const char *dst_file) { int in_fd, out_fd; struct stat st; @@ -223,10 +214,9 @@ copy_file (const char *file, const char *dst_file, uid_t uid, gid_t gid) } /* Move FILE to DST_FILE. If they reside on different devices, use copy_file - + unlink. - UID and GID give DST_FILE ownership. */ + + unlink. */ int -do_move_file (const char *file, const char *dst_file, uid_t uid, gid_t gid) +do_move_file (const char *file, const char *dst_file) { int rc = 0; @@ -234,7 +224,7 @@ do_move_file (const char *file, const char *dst_file, uid_t uid, gid_t gid) { if (errno == EXDEV) { - if (copy_file (file, dst_file, uid, gid)) + if (copy_file (file, dst_file)) { logmsg (LOG_CRIT, _("cannot copy %s to %s: %s"), file, dst_file, strerror (errno)); @@ -299,13 +289,12 @@ tar_append_file (const char *archive, const char *file) DST_DIR - Directory part of DST_FILE. FILE - File part of DST_FILE; can contain subdirs. ARCHIVE - Archive descriptor. - UID, GID - Ownership RELDIR - Directory part of FILE Do nothing if dry_run_mode is set. */ int backup_file (const char *dst_file, const char *dst_dir, const char *file, - const struct archive_descr *archive, uid_t uid, gid_t gid, + const struct archive_descr *archive, const char *reldir) { int rc = 0; @@ -313,9 +302,9 @@ backup_file (const char *dst_file, const char *dst_dir, const char *file, char *file_name; if (archive->name[0] == '/') - adir = create_directory (archive->name, reldir, uid, gid); + adir = create_directory (archive->name, reldir); else - adir = create_directory (dst_dir, archive->name, uid, gid); + adir = create_directory (dst_dir, archive->name); if (!adir) return 1; @@ -347,7 +336,7 @@ backup_file (const char *dst_file, const char *dst_dir, const char *file, file_name, archive_file_name); if (!dry_run_mode) { - rc = do_move_file (file_name, archive_file_name, uid, gid); + rc = do_move_file (file_name, archive_file_name); if (rc) { logmsg (LOG_ERR, _("backing `%s' up as `%s' failed: %s"), @@ -366,7 +355,7 @@ backup_file (const char *dst_file, const char *dst_dir, const char *file, logmsg (LOG_DEBUG, _("archiving `%s' to `%s'"), dst_file, file_name); if (!dry_run_mode) { - rc = do_move_file (dst_file, file_name, uid, gid); + rc = do_move_file (dst_file, file_name); if (rc) logmsg (LOG_ERR, _("archiving `%s' as `%s' failed: %s"), dst_file, file_name, strerror (errno)); @@ -380,7 +369,7 @@ backup_file (const char *dst_file, const char *dst_dir, const char *file, for the argument description. */ int do_archive_file (const char *dst_file, const char *dst_dir, const char *file, - const struct archive_descr *archive, uid_t uid, gid_t gid, + const struct archive_descr *archive, const char *reldir) { switch (archive->type) @@ -389,7 +378,7 @@ do_archive_file (const char *dst_file, const char *dst_dir, const char *file, break; case archive_directory: - return backup_file (dst_file, dst_dir, file, archive, uid, gid, reldir); + return backup_file (dst_file, dst_dir, file, archive, reldir); case archive_tar: if (tar_append_file (archive->name, dst_file)) @@ -415,8 +404,7 @@ dir_move_file (struct file_triplet *trp, const struct spool *spool, { char *dst_file; int rc = 0; - char *dst_dir = create_directory (spool->dest_dir, reldir, - TRIPLET_UID (trp), TRIPLET_GID (trp)); + char *dst_dir = create_directory (spool->dest_dir, reldir); if (!dst_dir) return 1; @@ -428,12 +416,10 @@ dir_move_file (struct file_triplet *trp, const struct spool *spool, if (access (dst_file, F_OK) == 0) rc = do_archive_file (dst_file, dst_dir, trp->file[file_id].name, - &spool->archive, - TRIPLET_UID (trp), TRIPLET_GID (trp), reldir); + &spool->archive, reldir); if (!dry_run_mode && rc == 0) - rc = do_move_file (trp->file[file_id].name, dst_file, - TRIPLET_UID (trp), TRIPLET_GID (trp)); + rc = do_move_file (trp->file[file_id].name, dst_file); free (dst_file); free (dst_dir); @@ -453,8 +439,7 @@ archive_single_file (struct file_triplet *trp, const struct spool *spool, { char *dst_file; int rc = 0; - char *dst_dir = create_directory (spool->dest_dir, reldir, - TRIPLET_UID (trp), TRIPLET_GID (trp)); + char *dst_dir = create_directory (spool->dest_dir, reldir); if (!dst_dir) return 1; @@ -474,7 +459,7 @@ archive_single_file (struct file_triplet *trp, const struct spool *spool, if (debug_level) logmsg (LOG_DEBUG, _("archiving file `%s'"), dst_file); rc = do_archive_file (dst_file, dst_dir, file_name, &spool->archive, - TRIPLET_UID (trp), TRIPLET_GID (trp), reldir); + reldir); if (rc == 0) UPDATE_STATS (STAT_ARCHIVES); } @@ -543,8 +528,7 @@ dir_symlink_file (struct file_triplet *trp, const struct spool *spool, { int rc = 0; struct saved_cwd cwd; - char *dst_dir = create_directory (spool->dest_dir, reldir, - TRIPLET_UID (trp), TRIPLET_GID (trp)); + char *dst_dir = create_directory (spool->dest_dir, reldir); char *src, *dst; if (!dst_dir) @@ -588,8 +572,7 @@ dir_symlink_file (struct file_triplet *trp, const struct spool *spool, char *dir; *p = 0; - dir = create_directory (spool->dest_dir, dst, - TRIPLET_UID (trp), TRIPLET_GID (trp)); + dir = create_directory (spool->dest_dir, dst); if (!dir) rc = 1; else @@ -703,8 +686,7 @@ dir_rmsymlink_file (struct file_triplet *trp, const struct spool *spool, char *dst_file; int rc = 0; char *signame; - char *dst_dir = create_directory (spool->dest_dir, reldir, - TRIPLET_UID (trp), TRIPLET_GID (trp)); + char *dst_dir = create_directory (spool->dest_dir, reldir); if (!dst_dir) return 1; diff --git a/src/lock.c b/src/lock.c index d94731c..3b8787d 100644 --- a/src/lock.c +++ b/src/lock.c @@ -233,7 +233,7 @@ wydawca_lock_init () { if (!lockdir) lockdir = xstrdup (LOCALSTATEDIR "/lock/" PACKAGE); - if (create_hierarchy (lockdir, 0, getuid (), getgid ())) + if (create_hierarchy (lockdir, 0)) exit (EX_OSFILE); } } diff --git a/src/mail.c b/src/mail.c index ea74b8c..ba77f49 100644 --- a/src/mail.c +++ b/src/mail.c @@ -335,7 +335,7 @@ do_notify (struct file_triplet *trp, enum notification_event ev, switch (ntf->tgt) { case notify_admin: - rcpt = admin_address; + rcpt = mu_address_dup (admin_address); break; case notify_user: diff --git a/src/triplet.c b/src/triplet.c index 614e2b5..e060089 100644 --- a/src/triplet.c +++ b/src/triplet.c @@ -199,11 +199,7 @@ triplet_processor (void *data, void *proc_data) case triplet_complete: if (debug_level) logmsg (LOG_DEBUG, _("processing triplet `%s'"), trp->name); - if (wydawca_set_triplet_privs (trp) == 0) - { - process_directives (trp, spool); - wydawca_set_root_privs (); - } + process_directives (trp, spool); return true; case triplet_incomplete: diff --git a/src/userprivs.c b/src/userprivs.c new file mode 100644 index 0000000..e6a18c8 --- /dev/null +++ b/src/userprivs.c @@ -0,0 +1,118 @@ +/* wydawca - automatic release submission daemon + Copyright (C) 2007, 2009 Sergey Poznyakoff + + Wydawca is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3 of the License, or (at your + option) any later version. + + Wydawca 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 wydawca. If not, see . */ + +#include "wydawca.h" + +int +wydawca_userprivs (uid_t uid, gid_t gid, gid_t *grplist, size_t ngrp) +{ + int rc = 0; + size_t size = 1, j = 1; + + if (uid == 0) + return 0; + + /* Reset group permissions */ + if (geteuid () == 0 && setgroups (ngrp, grplist)) + { + logmsg (LOG_CRIT, "setgroups(%lu, %lu...): %s", + ngrp, grplist[0], 0, strerror (errno)); + return rc; + } + + /* Switch to the user's gid. On some OSes the effective gid must + be reset first */ + +#if defined(HAVE_SETEGID) + if ((rc = setegid (gid)) < 0) + logmsg (LOG_CRIT, "setegid(%lu): %s", gid, strerror (errno)); +#elif defined(HAVE_SETREGID) + if ((rc = setregid (gid, gid)) < 0) + logmsg (LOG_CRIT, "setregid(%lu,%lu)d: %s", + (unsigned long) gid, (unsigned long) gid, strerror (errno)); +#elif defined(HAVE_SETRESGID) + if ((rc = setresgid (gid, gid, gid)) < 0) + logmsg (LOG_CRIT, "setresgid(%lu,%lu,%lu): %s", + (unsigned long) gid, (unsigned long) gid, + (unsigned long) gid, + strerror (errno)); +#endif + + if (rc == 0 && gid != 0) + { + if ((rc = setgid (gid)) < 0 && getegid () != gid) + logmsg (LOG_CRIT, "setgid(%lu): %s", + (unsigned long) gid, strerror (errno)); + if (rc == 0 && getegid () != gid) + { + logmsg (LOG_CRIT, _("cannot set effective gid to %lu"), + (unsigned long) gid); + rc = 1; + } + } + + /* Now reset uid */ + if (rc == 0 && uid != 0) + { + uid_t euid; + + if (setuid (uid) + || geteuid () != uid + || (getuid () != uid + && (geteuid () == 0 || getuid () == 0))) + { + +#if defined(HAVE_SETREUID) + if (geteuid () != uid) + { + if (setreuid (uid, -1) < 0) + { + logmsg (LOG_CRIT, "setreuid(%lu,-1): %s", + (unsigned long) uid, strerror (errno)); + rc = 1; + } + if (setuid (uid) < 0) + { + logmsg (LOG_CRIT, "setreuid(%lu,-1): %s", + (unsigned long) uid, strerror (errno)); + rc = 1; + } + } + else +#endif + { + logmsg (LOG_CRIT, "setuid(%lu): %s", + (unsigned long) uid, strerror (errno)); + rc = 1; + } + } + + euid = geteuid (); + if (uid != 0 && setuid (0) == 0) + { + logmsg (LOG_CRIT, _("seteuid(0) succeeded when it should not")); + rc = 1; + } + else if (uid != euid && setuid (euid) == 0) + { + logmsg (LOG_CRIT, _("cannot drop non-root setuid privileges")); + rc = 1; + } + + } + + return rc; +} diff --git a/src/wydawca.c b/src/wydawca.c index 48c27e9..f02ac99 100644 --- a/src/wydawca.c +++ b/src/wydawca.c @@ -19,6 +19,10 @@ #include "argmatch.h" #include "version-etc.h" +uid_t wydawca_uid; +gid_t wydawca_gid; +size_t wydawca_supp_groupc; +gid_t *wydawca_supp_groups; char *conffile = SYSCONFDIR "/wydawca.rc" ; int debug_level; int dry_run_mode; @@ -265,73 +269,6 @@ collect_uids (int argc, char **argv) } -static int -wydawca_set_uid (uid_t uid) -{ - int rc; - - if (getuid () != 0) - return 0; -#if defined(HAVE_SETREUID) - rc = setreuid (0, uid); -#elif defined(HAVE_SETRESUID) - rc = setresuid (-1, uid, -1); -#elif defined(HAVE_SETEUID) - rc = seteuid (uid); -#else -# error "No way to reset user privileges?" -#endif - if (rc < 0) - logmsg (LOG_ERR, _("cannot switch to UID %d: %s (r=%d, e=%d)"), - uid, strerror (errno), getuid (), geteuid ()); - return rc; -} - -static int -wydawca_set_gid (gid_t gid) -{ - int rc; - - if (getuid () != 0) - return 0; -#if defined(HAVE_SETREGID) - rc = setregid (0, gid); -#elif defined(HAVE_SETRESGID) - rc = setresgid (-1, gid, -1); -#elif defined(HAVE_SETEGID) - rc = setegid (gid); -#else -# error "No way to reset user privileges?" -#endif - if (rc < 0) - logmsg (LOG_ERR, _("cannot switch to GID %d: %s (r=%d, e=%d)"), - gid, strerror (errno), getgid (), getegid ()); - return rc; -} - -int -wydawca_set_privs (uid_t uid, gid_t gid) -{ - if (wydawca_set_gid (gid)) - return -1; - if (wydawca_set_uid (uid)) - return -1; - return 0; -} - -int -wydawca_set_triplet_privs (struct file_triplet *trp) -{ - return wydawca_set_privs (TRIPLET_UID (trp), TRIPLET_GID (trp)); -} - -int -wydawca_set_root_privs () -{ - return wydawca_set_privs (0, 0); -} - - char **x_argv; extern int reconfigure; @@ -377,6 +314,8 @@ main (int argc, char **argv) if (lint_mode) exit (0); + if (dry_run_mode) + cron_option = 1; if (cron_option) daemon_mode = 0; if (foreground_option >= 0) @@ -394,6 +333,22 @@ main (int argc, char **argv) log_printer = syslog_printer; } + if (getgid () == 0) + { + if (wydawca_uid == 0) + { + if (!force_startup) + { + logmsg (LOG_CRIT, _("won't run with root privileges")); + exit (EX_UNAVAILABLE); + } + } + else if (wydawca_userprivs (wydawca_uid, wydawca_gid, + wydawca_supp_groups, + wydawca_supp_groupc)) + exit (EX_UNAVAILABLE); + } + mail_init (); wydawca_lock_init (); @@ -414,7 +369,6 @@ main (int argc, char **argv) if (reconfigure) { int i; - wydawca_set_uid (0); for (i = getdtablesize (); i > 2; i--) close (i); remove_pidfile (); diff --git a/src/wydawca.h b/src/wydawca.h index f786271..81e6509 100644 --- a/src/wydawca.h +++ b/src/wydawca.h @@ -303,6 +303,10 @@ void make_default_meta (struct metadef kwexp[5], const char *user, /* Global variables */ +extern uid_t wydawca_uid; +extern gid_t wydawca_gid; +extern size_t wydawca_supp_groupc; +extern gid_t *wydawca_supp_groups; extern char *conffile; /* Configuration file name */ extern int debug_level; /* Debugging level */ extern int dry_run_mode; /* Dry run indicator */ @@ -355,8 +359,8 @@ size_t trim_length (const char *str); size_t trim (char *str); void logmsg (int prio, char *fmt, ...) GSC_PRINTFLIKE(2,3); int test_dir (const char *name, int *ec); -char *create_directory (const char *base, const char *name, - uid_t uid, gid_t gid); +char *create_directory (const char *base, const char *name); +int create_hierarchy (char *dir, size_t baselen); void parse_config (void); void log_output (int prio, const char *prog, FILE *fp); @@ -428,9 +432,6 @@ int process_directives (struct file_triplet *trp, int enabled_spool_p (const struct spool *spool); -int wydawca_set_privs (uid_t uid, gid_t gid); -int wydawca_set_triplet_privs (struct file_triplet *trp); -int wydawca_set_root_privs (void); int parse_time_interval (const char *str, time_t *pint, const char **endp); @@ -538,3 +539,6 @@ void wydawca_lock_init (void); /* tcpwrap.h */ extern struct gconf_keyword tcpwrapper_kw[]; int tcpwrap_access(int fd); + +/* userprivs.c */ +int wydawca_userprivs (uid_t uid, gid_t gid, gid_t *grplist, size_t ngrp); -- cgit v1.2.1