/* This file is part of Pies. Copyright (C) 2007, 2008, 2009 Sergey Poznyakoff Pies 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. Pies 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 Pies. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include "pies.h" static bool str_eq (const void *elt1, const void *elt2) { return strcmp (elt1, elt2) == 0; } void str_dispose (const void *elt) { free ((void*)elt); } gl_list_t get_user_groups (gl_list_t init_list, const char *user) { int rc; struct group *gr; gl_list_t list; list = gl_list_create_empty(&gl_linked_list_implementation, str_eq, NULL, str_dispose, false); if (init_list) { const void *p; gl_list_iterator_t itr = gl_list_iterator (init_list); while (gl_list_iterator_next (&itr, &p, NULL)) { char *s = xstrdup (p); if (!gl_list_add_last (list, s)) free (s); } gl_list_iterator_free (&itr); } setgrent (); for (rc = 0; rc == 0 && (gr = getgrent ());) { char **p; for (p = gr->gr_mem; *p; p++) if (strcmp (*p, user) == 0) { char *s = xstrdup (gr->gr_name); if (!gl_list_add_last (list, s)) free (s); break; } } endgrent (); return list; } /* Switch to the given UID/GID */ int switch_to_privs (uid_t uid, gid_t gid, gl_list_t retain_groups) { int rc = 0; gid_t *emptygidset; size_t size = 1, j = 1; if (uid == 0) { logmsg (LOG_ERR, _("Refusing to run as root")); return 1; } /* Create a list of supplementary groups */ size = 1 + (retain_groups ? gl_list_size (retain_groups) : 0); emptygidset = xcalloc (size, sizeof emptygidset[0]); emptygidset[0] = gid ? gid : getegid (); if (retain_groups) { const void *p; gl_list_iterator_t itr = gl_list_iterator (retain_groups); while (gl_list_iterator_next (&itr, &p, NULL)) { struct group *group = getgrnam ((const char*)p); if (!group) { logmsg (LOG_ERR, _("unknown group: %s"), (const char*)p); free (emptygidset); return 1; } emptygidset[j++] = group->gr_gid; } gl_list_iterator_free (&itr); } /* Reset group permissions */ if (geteuid () == 0 && setgroups (j, emptygidset)) { logmsg (LOG_ERR, _("setgroups(1, %lu) failed: %s"), (unsigned long) emptygidset[0], 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) logmsg (LOG_ERR, _("setegid(%lu) failed: %s"), (unsigned long) gid, strerror (errno)); #elif defined(HAVE_SETREGID) if ((rc = setregid (gid, gid)) < 0) logmsg (LOG_ERR, _("setregid(%lu,%lu) failed: %s"), (unsigned long) gid, (unsigned long) gid, strerror (errno)); #elif defined(HAVE_SETRESGID) if ((rc = setresgid (gid, gid, gid)) < 0) logmsg (LOG_ERR, _("setresgid(%lu,%lu,%lu) failed: %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_ERR, _("setgid(%lu) failed: %s"), (unsigned long) gid, strerror (errno)); if (rc == 0 && getegid () != gid) { logmsg (LOG_ERR, _("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_ERR, _("setreuid(%lu,-1) failed: %s"), (unsigned long) uid, strerror (errno)); rc = 1; } if (setuid (uid) < 0) { logmsg (LOG_ERR, _("second setuid(%lu) failed: %s"), (unsigned long) uid, strerror (errno)); rc = 1; } } else #endif { logmsg (LOG_ERR, _("setuid(%lu) failed: %s"), (unsigned long) uid, strerror (errno)); rc = 1; } } euid = geteuid (); if (uid != 0 && setuid (0) == 0) { logmsg (LOG_ERR, _("seteuid(0) succeeded when it should not")); rc = 1; } else if (uid != euid && setuid (euid) == 0) { logmsg (LOG_ERR, _("Cannot drop non-root setuid privileges")); rc = 1; } } return rc; } void pies_priv_setup (struct pies_privs *privs) { struct passwd *pw; gl_list_t grplist = 0; if (!privs || !privs->user) return; pw = getpwnam (privs->user); if (!pw) { logmsg (LOG_ERR, _("no such user: %s"), privs->user); exit (EX_CONFIG); } if (privs->allgroups) grplist = get_user_groups (privs->groups, privs->user); if (switch_to_privs (pw->pw_uid, pw->pw_gid, grplist ? grplist : privs->groups)) exit (EX_SOFTWARE); if (grplist) gl_list_free (grplist); } void pies_epriv_setup (struct pies_privs *privs) { uid_t uid; gid_t gid; if (privs) { struct passwd *pw; if (!privs->user) return; pw = getpwnam (privs->user); if (!pw) { logmsg (LOG_ERR, _("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)) { logmsg (LOG_ERR, _("Cannot switch to EGID %lu: %s"), (unsigned long) gid, strerror (errno)); exit (EX_USAGE); } if (seteuid (uid)) { logmsg (LOG_ERR, _("Cannot switch to EUID %lu: %s"), (unsigned long) uid, strerror (errno)); exit (EX_USAGE); } }