aboutsummaryrefslogtreecommitdiff
path: root/lib/userprivs.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/userprivs.c')
-rw-r--r--lib/userprivs.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/lib/userprivs.c b/lib/userprivs.c
new file mode 100644
index 0000000..8d13c38
--- /dev/null
+++ b/lib/userprivs.c
@@ -0,0 +1,291 @@
+/* This file is part of Mailfromd.
+ Copyright (C) 2007, 2008 Sergey Poznyakoff
+
+ This program 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, or (at your option)
+ any later version.
+
+ 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, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <mailutils/assoc.h>
+#include <mailutils/errno.h>
+#include <mailutils/error.h>
+#include <mailutils/errno.h>
+#include <mailutils/nls.h>
+#include <mailutils/list.h>
+#include <mailutils/iterator.h>
+/* FIXME: */
+#include <sysexits.h>
+#include "libpies.h"
+
+int
+get_user_groups (mu_list_t *pgrouplist, const char *user)
+{
+ int rc;
+ struct group *gr;
+ mu_list_t list;
+
+ if (!*pgrouplist)
+ {
+ rc = mu_list_create (pgrouplist);
+ if (rc)
+ {
+ mu_error (_("%s: cannot create list: %s"),
+ "get_user_groups", mu_strerror (rc));
+ return rc;
+ }
+ }
+ list = *pgrouplist;
+ setgrent ();
+ for (rc = 0; rc == 0 && (gr = getgrent ());)
+ {
+ char **p;
+ for (p = gr->gr_mem; *p; p++)
+ if (strcmp (*p, user) == 0)
+ {
+ /* FIXME: Avoid duplicating gids */
+ rc = mu_list_append (list, (void *) gr->gr_gid);
+ if (rc)
+ mu_error (_("%s: cannot append to list: %s"),
+ "get_user_groups", mu_strerror (rc));
+ break;
+ }
+ }
+ endgrent ();
+ return rc;
+}
+
+/* Switch to the given UID/GID */
+int
+switch_to_privs (uid_t uid, gid_t gid, mu_list_t retain_groups)
+{
+ int rc = 0;
+ gid_t *emptygidset;
+ size_t size = 1, j = 1;
+ mu_iterator_t itr;
+
+ if (uid == 0)
+ {
+ mu_error (_("Refusing to run as root"));
+ return 1;
+ }
+
+ /* Create a list of supplementary groups */
+ mu_list_count (retain_groups, &size);
+ size++;
+ emptygidset = xmalloc (size * sizeof emptygidset[0]);
+ emptygidset[0] = gid ? gid : getegid ();
+
+ if (mu_list_get_iterator (retain_groups, &itr) == 0)
+ {
+ for (mu_iterator_first (itr);
+ !mu_iterator_is_done (itr); mu_iterator_next (itr))
+ mu_iterator_current (itr, (void **) (emptygidset + j++));
+ mu_iterator_destroy (&itr);
+ }
+
+ /* Reset group permissions */
+ if (geteuid () == 0 && setgroups (j, emptygidset))
+ {
+ mu_error (_("setgroups(1, %lu) failed: %s"),
+ (unsigned long) emptygidset[0], mu_strerror (errno));
+ rc = 1;
+ }
+ free (emptygidset);
+
+ /* 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)
+ mu_error (_("setegid(%lu) failed: %s"),
+ (unsigned long) gid, mu_strerror (errno));
+#elif defined(HAVE_SETREGID)
+ if ((rc = setregid (gid, gid)) < 0)
+ mu_error (_("setregid(%lu,%lu) failed: %s"),
+ (unsigned long) gid, (unsigned long) gid, mu_strerror (errno));
+#elif defined(HAVE_SETRESGID)
+ if ((rc = setresgid (gid, gid, gid)) < 0)
+ mu_error (_("setresgid(%lu,%lu,%lu) failed: %s"),
+ (unsigned long) gid,
+ (unsigned long) gid, (unsigned long) gid, mu_strerror (errno));
+#endif
+
+ if (rc == 0 && gid != 0)
+ {
+ if ((rc = setgid (gid)) < 0 && getegid () != gid)
+ mu_error (_("setgid(%lu) failed: %s"),
+ (unsigned long) gid, mu_strerror (errno));
+ if (rc == 0 && getegid () != gid)
+ {
+ mu_error (_("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)
+ {
+ mu_error (_("setreuid(%lu,-1) failed: %s"),
+ (unsigned long) uid, mu_strerror (errno));
+ rc = 1;
+ }
+ if (setuid (uid) < 0)
+ {
+ mu_error (_("second setuid(%lu) failed: %s"),
+ (unsigned long) uid, mu_strerror (errno));
+ rc = 1;
+ }
+ }
+ else
+#endif
+ {
+ mu_error (_("setuid(%lu) failed: %s"),
+ (unsigned long) uid, mu_strerror (errno));
+ rc = 1;
+ }
+ }
+
+ euid = geteuid ();
+ if (uid != 0 && setuid (0) == 0)
+ {
+ mu_error (_("seteuid(0) succeeded when it should not"));
+ rc = 1;
+ }
+ else if (uid != euid && setuid (euid) == 0)
+ {
+ mu_error (_("Cannot drop non-root setuid privileges"));
+ rc = 1;
+ }
+
+ }
+
+ return rc;
+}
+
+
+static int
+translate_item (void *item, void *data)
+{
+ mu_list_t dst = data;
+ struct group *group = getgrnam (item);
+ if (!group)
+ {
+ mu_error (_("Unknown group: %s"), (char *) item);
+ return 1;
+ }
+ return mu_list_append (dst, (void *) group->gr_gid);
+}
+
+static int
+grouplist_translate (mu_list_t * pdst, mu_list_t src)
+{
+ mu_list_t dst;
+ int rc;
+
+ if (!src)
+ return 0;
+ rc = mu_list_create (&dst);
+ if (rc)
+ {
+ mu_error (_("%s: cannot create list: %s"),
+ "grouplist_translate", mu_strerror (rc));
+ return rc;
+ }
+ *pdst = dst;
+ return mu_list_do (src, translate_item, dst);
+}
+
+void
+mf_priv_setup (struct mf_privs *privs)
+{
+ struct passwd *pw;
+ mu_list_t grp = NULL;
+
+ if (!privs || !privs->user)
+ return;
+
+ pw = getpwnam (privs->user);
+ if (!pw)
+ {
+ mu_error (_("No such user: %s"), privs->user);
+ exit (EX_CONFIG);
+ }
+
+ grouplist_translate (&grp, privs->groups);
+ if (privs->allgroups && get_user_groups (&grp, privs->user))
+ exit (EX_CONFIG);
+ if (switch_to_privs (pw->pw_uid, pw->pw_gid, grp))
+ exit (EX_SOFTWARE);
+ mu_list_destroy (&grp);
+}
+
+
+void
+mf_epriv_setup (struct mf_privs *privs)
+{
+ uid_t uid;
+ gid_t gid;
+
+ if (privs)
+ {
+ struct passwd *pw;
+ if (!privs->user)
+ return;
+
+ pw = getpwnam (privs->user);
+ if (!pw)
+ {
+ mu_error (_("No such user: %s"), privs->user);
+ exit (EX_CONFIG);
+ }
+ uid = pw->pw_uid;
+ gid = pw->pw_gid;
+ }
+ else
+ {
+ uid = 0;
+ gid = 0;
+ }
+
+ if (setegid (gid))
+ {
+ mu_error (_("Cannot switch to EGID %lu: %s"),
+ (unsigned long) gid, mu_strerror (errno));
+ exit (EX_USAGE);
+ }
+ if (seteuid (uid))
+ {
+ mu_error (_("Cannot switch to EUID %lu: %s"),
+ (unsigned long) uid, mu_strerror (errno));
+ exit (EX_USAGE);
+ }
+}

Return to:

Send suggestions and report system problems to the System administrator.