/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
#include <stdio.h> /* for AIX */
#include <unistd.h>
#include "lib.h"
#include "str.h"
#include "restrict-access.h"
#include "env-util.h"
#include "ipwd.h"
#include <time.h>
#ifdef HAVE_PR_SET_DUMPABLE
#endif
{
}
{
const char *ret;
else
return ret;
}
{
const char *ret;
else
return ret;
}
const char *gid_source)
{
/* everything is already set */
return;
}
if (setgid(primary_gid) == 0)
return;
if (gid_source != NULL)
"(This binary should probably be called with "
"process group set to %s instead of %s)",
get_uid_str(geteuid()),
}
primary_gid == getegid()) {
/* privileged_gid is hopefully in saved ID. if not,
there's nothing we can do about it. */
return;
}
#ifdef HAVE_SETRESGID
i_fatal("setresgid(%s,%s,%s) failed with euid=%s: %m",
}
#else
if (geteuid() == 0) {
/* real, effective, saved -> privileged_gid */
if (setgid(privileged_gid) < 0) {
i_fatal("setgid(%s) failed: %m",
}
}
/* real, effective -> primary_gid
saved -> keep */
i_fatal("setregid(%s,%s) failed with euid=%s: %m",
get_uid_str(geteuid()));
}
#endif
}
{
i_fatal("getgroups() failed: %m");
/* @UNSAFE */
i_fatal("getgroups() failed: %m");
*gid_count_r = ret;
return gid_list;
}
bool *have_root_group)
{
/* @UNSAFE */
unsigned int i, used;
(set->last_valid_gid == 0 ||
if (gid_list[i] == 0)
*have_root_group = TRUE;
}
}
}
{
return gid;
case -1:
case 0:
default:
}
}
bool preserve_existing, bool *have_root_group)
{
unsigned int i, gid_count;
bool add_primary_gid;
/* if we're using a privileged GID, we can temporarily drop our
effective GID. we still want to be able to use its privileges,
so add it to supplementary groups. */
if (preserve_existing) {
/* see if the list already contains the primary GID */
for (i = 0; i < gid_count; i++) {
if (gid_list[i] == process_primary_gid) {
break;
}
}
} else {
gid_count = 0;
}
if (gid_count == 0) {
/* Some OSes don't like an empty groups list,
so use the primary GID as the only one. */
gid_list[0] = process_primary_gid;
gid_count = 1;
}
if (gid != process_primary_gid)
}
if (add_primary_gid)
}
i_fatal("setgroups(%s) failed: Too many extra groups",
set->extra_groups);
} else {
i_fatal("setgroups() failed: %m");
}
}
}
static const char *
{
get_uid_str(geteuid()));
} else {
"process user set to %s instead of %s)",
}
}
{
bool allow_root_gid;
if (!is_root &&
!set->allow_setuid_root &&
getuid() == 0) {
/* recover current effective UID */
target_uid = geteuid();
else
i_assert(target_uid > 0);
/* try to elevate to root */
if (seteuid(0) < 0)
i_fatal("seteuid(0) failed: %m");
}
/* set the primary/privileged group */
if (process_privileged_gid == process_primary_gid) {
/* a pointless configuration, ignore it */
}
have_root_group = process_primary_gid == 0;
} else {
}
/* set system user's groups */
process_primary_gid) < 0) {
i_fatal("initgroups(%s, %s) failed: %m",
}
}
/* add extra groups. if we set system user's groups, drop the
restricted groups at the same time. */
} T_END;
/* chrooting */
/* kludge: localtime() must be called before chroot(),
or the timezone isn't known */
time_t t = 0;
(void)localtime(&t);
/* makes static analyzers happy, and is more secure */
if (chdir("/") != 0)
i_fatal("chdir(/) failed: %m");
}
}
}
/* uid last */
if (setuid(target_uid) != 0)
}
/* verify that we actually dropped the privileges */
if (setuid(0) == 0) {
if (!allow_root &&
i_fatal("This process must not be run as root");
i_fatal("We couldn't drop root privileges");
}
}
if (set->first_valid_gid != 0)
else if (process_primary_gid == 0 || process_privileged_gid == 0)
else
if (!allow_root_gid && target_uid != 0 &&
if (process_primary_gid == 0)
i_fatal("GID 0 isn't permitted");
i_fatal("We couldn't drop root group privileges "
"(wanted=%s, gid=%s, egid=%s)",
}
}
}
{
}
}
}
}
}
if (set->first_valid_gid != 0) {
}
if (set->last_valid_gid != 0) {
}
}
{
}
{
const char *value;
}
}
}
}
}
}
{
/* clear the environment, so we don't fail if we get back here */
env_remove("RESTRICT_SETUID");
/* if we're dropping privileges before executing and
a privileged group is set, the groups must be fixed
after exec */
env_remove("RESTRICT_SETGID");
env_remove("RESTRICT_SETGID_PRIV");
}
env_remove("RESTRICT_GID_FIRST");
env_remove("RESTRICT_GID_LAST");
if (getuid() != 0)
env_remove("RESTRICT_SETEXTRAGROUPS");
else {
/* Preserve RESTRICT_SETEXTRAGROUPS, so if we're again dropping
more privileges we'll still preserve the extra groups. This
mainly means preserving service { extra_groups } for lmtp
and doveadm accesses. */
}
env_remove("RESTRICT_USER");
env_remove("RESTRICT_CHROOT");
}
const char *restrict_access_get_current_chroot(void)
{
return chroot_dir;
}
{
#ifdef HAVE_PR_SET_DUMPABLE
i_error("prctl(PR_SET_DUMPABLE) failed: %m");
#endif
}
bool restrict_access_get_dumpable(void)
{
#ifdef HAVE_PR_SET_DUMPABLE
i_error("prctl(PR_GET_DUMPABLE) failed: %m");
return allow;
#endif
return TRUE;
}
{
}
}
int restrict_access_use_priv_gid(void)
{
return 0;
if (setegid(process_privileged_gid) < 0) {
i_error("setegid(privileged) failed: %m");
return -1;
}
return 0;
}
void restrict_access_drop_priv_gid(void)
{
if (!process_using_priv_gid)
return;
if (setegid(process_primary_gid) < 0)
i_fatal("setegid(primary) failed: %m");
}
bool restrict_access_have_priv_gid(void)
{
}