/* -*- c -*-
This file is part of GNU Anubis.
Copyright (C) 2001-2014 The Anubis Team.
GNU Anubis 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.
GNU Anubis 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 GNU Anubis. If not, see .
*/
#include "headers.h"
#include
#include
#include "extern.h"
#include "rcfile.h"
#ifdef HAVE_PAM
pam_handle_t *pamh;
static struct pam_conv conv = {
misc_conv,
NULL
};
#endif /* HAVE_PAM */
static char *pidfile;
OPTIONS_BEGIN(gnu,"anubis",[])
OPTION(bind, b, [HOST:]PORT,
[])
BEGIN
parse_mtahost (optarg, &session.anubis, &session.anubis_port);
if (session.anubis && strlen (session.anubis) != 0)
topt |= T_NAMES;
rc_disable_keyword (CF_INIT | CF_SUPERVISOR, "bind");
END
OPTION(remote-mta, r, [HOST:]PORT,
Specify a remote SMTP host name or IP address;
the default is 25)
BEGIN
parse_mtaport (optarg, &session.mta, &session.mta_port);
END
OPTION(local-mta, l, FILE,
[])
BEGIN
int rc;
int argc;
if ((rc = argcv_get (optarg, "", "#", &argc, &session.execargs)))
anubis_error (EX_SOFTWARE, rc, _("argcv_get failed"));
session.execpath = strdup (session.execargs[0]);
topt |= T_LOCAL_MTA;
rc_disable_keyword (CF_INIT | CF_SUPERVISOR, "local-mta");
END
OPTION(mode, m, MODE,
[])
BEGIN
if (anubis_set_mode (optarg))
anubis_error (1, 0, _("invalid mode: %s"), optarg);
rc_disable_keyword (CF_INIT | CF_SUPERVISOR, "mode");
END
OPTION(foreground, f,,
Foreground mode)
BEGIN
topt |= T_FOREGROUND_INIT;
END
OPTION(from,, EMAIL,
Specify sender address (implies MDA mode)))
BEGIN
assign_string (&from_address, optarg);
END
OPTION(stdio, i,,
Use the SMTP protocol (OMP/Tunnel) as described
in RFC 821 on standard input and output))
BEGIN
topt |= T_STDINOUT;
END
GROUP(Output options)
OPTION(silent, s,, Work silently)
BEGIN
options.termlevel = SILENT;
rc_disable_keyword (CF_INIT | CF_SUPERVISOR, "termlevel");
END
OPTION(verbose, v,, Work noisily)
BEGIN
options.termlevel = VERBOSE;
rc_disable_keyword (CF_INIT | CF_SUPERVISOR, "termlevel");
END
OPTION(debug, D,, Debug mode)
BEGIN
options.termlevel = DEBUG;
rc_disable_keyword (CF_INIT | CF_SUPERVISOR, "termlevel");
END
GROUP(Miscellaneous options)
OPTION(altrc,, FILE,
Specify alternate system configuration file)
BEGIN
options.altrc = optarg;
topt |= T_ALTRC;
END
OPTION(norc,,,
Ignore system configuration file)
BEGIN
topt |= T_NORC;
END
OPTION(check-config, c, DEBUG-LEVEL,
Run the configuration file syntax checker)
BEGIN
rc_set_debug_level (optarg);
topt |= T_CHECK_CONFIG;
END
OPTION(show-config-options,,,
Print a list of configuration options used to build GNU Anubis)
BEGIN
print_config_options ();
END
OPTION(location-column,,,
Print location column numbers in parser diagnostics)
BEGIN
topt |= T_LOCATION_COLUMN;
END
OPTION(relax-perm-check,,,
Do not check user configuration file permissions)
BEGIN
topt |= T_RELAX_PERM_CHECK;
END
OPTION(pid-file,, FILE,
Store the PID of the running daemon in FILE)
BEGIN
pidfile = optarg;
rc_disable_keyword (CF_INIT | CF_SUPERVISOR, "termlevel");
END
OPTIONS_END
int x_argc;
char **x_argv;
void
get_options (int argc, char *argv[])
{
GETOPT(argc, argv)
x_argc = argc - optind;
x_argv = argv + optind;
if (from_address) /* Force MDA mode */
anubis_mode = anubis_mda;
}
/*********************
Get a home directory
**********************/
void
get_homedir (char *user, char *buf, int maxsize)
{
struct passwd *pwd;
memset (buf, 0, maxsize);
if (user == 0)
return;
pwd = getpwnam (user);
if (pwd)
strncpy (buf, (char *) pwd->pw_dir, maxsize - 1);
else
{
char *p = getenv ("HOME");
if (p)
strncpy (buf, p, maxsize - 1);
else
strncpy (buf, "", 1);
}
return;
}
/*****************************
Get a real user name (login)
******************************/
void
anubis_getlogin (char **buf)
{
struct passwd *pwd;
pwd = getpwuid (getuid ());
assign_string (buf, pwd ? pwd->pw_name : getlogin ());
}
/*******************
Check current RUID
********************/
int
check_superuser (void)
{
if (getuid () == 0)
return 1; /* a super-user */
return 0;
}
/*******************************************
Set USER's RGID, RUID, and home directory.
********************************************/
/* Change to the given uid/gid. Clear the supplementary group list.
On success returns 0.
On failure returns 1 (or exits, depending on topt settings. See
anubis_error) */
static int
change_privs (uid_t uid, gid_t gid)
{
int rc = 0;
gid_t emptygidset[1];
/* Reset group permissions */
emptygidset[0] = gid ? gid : getegid();
if (geteuid() == 0 && setgroups(1, emptygidset))
{
anubis_error (0, errno,
_("setgroups(1, %lu) failed"),
(u_long) emptygidset[0]);
rc = 1;
}
/* 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)
anubis_error (0, errno, _("setegid(%lu) failed"), (u_long) gid);
#elif defined(HAVE_SETREGID)
if ((rc = setregid(gid, gid)) < 0)
anubis_error (0, errno, _("setregid(%lu,%lu) failed"),
(u_long) gid, (u_long) gid);
#elif defined(HAVE_SETRESGID)
if ((rc = setresgid(gid, gid, gid)) < 0)
anubis_error (0, errno, _("setresgid(%lu,%lu,%lu) failed"),
(u_long) gid,
(u_long) gid,
(u_long) gid);
#endif
if (rc == 0 && gid != 0)
{
if ((rc = setgid(gid)) < 0 && getegid() != gid)
anubis_error (0, errno, _("setgid(%lu) failed"), (u_long) gid);
if (rc == 0 && getegid() != gid)
{
anubis_error (0, errno, _("cannot set effective gid to %lu"),
(u_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)
{
anubis_error (0, errno, _("setreuid(%lu,-1) failed"),
(u_long) uid);
rc = 1;
}
if (setuid(uid) < 0)
{
anubis_error (0, errno, _("second setuid(%lu) failed"),
(u_long) uid);
rc = 1;
}
}
else
#endif
{
anubis_error (0, errno, _("setuid(%lu) failed"), (u_long) uid);
rc = 1;
}
}
euid = geteuid();
if (uid != 0 && setuid(0) == 0)
{
anubis_error (0, 0, _("seteuid(0) succeeded when it should not"));
rc = 1;
}
else if (uid != euid && setuid(euid) == 0)
{
anubis_error (0, 0, _("cannot drop non-root setuid privileges"));
rc = 1;
}
}
return rc;
}
void
anubis_changeowner (const char *user)
{
#ifdef HAVE_PAM
int pam_retval;
#endif
struct passwd *pwd;
if (user == NULL || check_superuser () == 0)
return;
#ifdef HAVE_PAM
pam_retval = pam_start ("anubis", user, &conv, &pamh);
if (pam_retval == PAM_SUCCESS)
pam_retval = pam_authenticate (pamh, 0);
if (pam_retval == PAM_SUCCESS)
pam_retval = pam_acct_mgmt (pamh, 0);
if (pam_retval == PAM_SUCCESS)
pam_retval = pam_open_session (pamh, 0);
if (pam_retval == PAM_SUCCESS)
info (VERBOSE, _("PAM: Session opened (restrictions applied)."));
else
{
info (NORMAL, _("PAM: Not authenticated to use GNU Anubis."));
quit (EXIT_FAILURE);
}
#endif /* HAVE_PAM */
pwd = getpwnam (user);
if (pwd)
{
if (change_privs (pwd->pw_uid, pwd->pw_gid))
quit (EXIT_FAILURE);
chdir (pwd->pw_dir);
info (VERBOSE, _("UID:%d (%s), GID:%d, EUID:%d, EGID:%d"),
(int) getuid (), pwd->pw_name, (int) getgid (),
(int) geteuid (), (int) getegid ());
}
return;
}
int
check_username (char *user)
{
struct passwd *pwd;
if (user == NULL)
return 0;
pwd = getpwnam (user);
if (pwd == 0)
{
int i = 0;
int digits = 0;
int len = strlen (user);
for (i = len - 1; i >= 0; i--)
{
if (isdigit ((u_char) user[i]))
digits++;
}
if (digits == len)
{
int uid = atoi (user);
pwd = getpwuid (uid);
if (pwd != 0)
strncpy (user, (char *) pwd->pw_name, 64);
else
{
info (NORMAL, _("Invalid user ID: %s"), user);
return 0; /* FALSE */
}
}
else
{
info (NORMAL, _("Invalid user name: %s"), user);
return 0; /* FALSE */
}
}
return 1; /* TRUE */
}
/*************************
Check a file permissions
**************************/
int
check_filemode (char *path)
{
struct stat st;
if (path == 0)
return 0;
if (stat (path, &st) == -1)
return 0;
if ((st.st_mode & S_IRWXG) || (st.st_mode & S_IRWXO))
{
anubis_error (0, 0, _("Wrong permissions on %s. Set 0600."), path);
return 0; /* FALSE */
}
return 1; /* TRUE */
}
/*************************
Check does a file exist?
**************************/
int
check_filename (char *path, time_t *timep)
{
struct stat st;
if (path == NULL)
return 0;
if (stat (path, &st) == -1)
{
anubis_error (0, errno, "%s", path);
return 0; /* FALSE */
}
if (!(st.st_mode & S_IFREG) || !(st.st_mode & S_IFLNK))
{
anubis_error (0, 0,
_("%s is not a regular file or a symbolic link."), path);
return 0; /* FALSE */
}
if (timep)
{
time_t mtime = *timep;
*timep = st.st_mtime;
return st.st_mtime > mtime;
}
return 1; /* TRUE */
}
/* Select working mode */
int
anubis_set_mode (char *modename)
{
if (strcmp (modename, "transparent") == 0)
anubis_mode = anubis_transparent;
else if (strcmp (modename, "proxy") == 0)
anubis_mode = anubis_proxy;
#if WITH_GSASL
else if (strcmp (modename, "auth") == 0)
anubis_mode = anubis_authenticate;
#endif
else if (strcmp (modename, "mda") == 0)
anubis_mode = anubis_mda;
else
return 1;
return 0;
}
void
write_pid_file (void)
{
FILE *fp;
if (!pidfile)
pidfile = "/var/run/" DEFAULT_PIDFILE;
fp = fopen (pidfile, "w");
if (!fp)
anubis_error (0, errno, _("Cannot open pid file '%s'"), pidfile);
else
{
fprintf (fp, "%ld\n", (unsigned long) getpid ());
fclose (fp);
}
}
/* EOF */