/* 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);
}
}