/*
 * $Id: b0bb060604900b543ade96e239ef7d9f1135154a $
 *
 * This file is part of nsmtp.
 *
 * Copyright (c) 2007, 2008 Niklas Olmes <niklas@noxa.de>
 *                          http://www.nevm.de/nsmtp/
 *                          NEVM-RIPE
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License Version 2
 * as published by the Free Software Foundation.
 *
 * 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, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

/*
 * NOTE: This is untested, since we are not using this in this form.
 */

/*
 * DJBs checkpwd Interface:  http://cr.yp.to/checkpwd/interface.html
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <grp.h>

#include "alloc.h"
#include "log.h"
#include "misc.h"
#include "check.h"
#include "ldapauth.h"
#include "relay.h"
#include "maildir.h"
#include "config.h"

#define E_FAIL		1
#define E_USAGE		2
#define E_TEMPFAIL	111

static const char rcsid[] = "$Id: b0bb060604900b543ade96e239ef7d9f1135154a $";

int
main(int argc, char *argv[])
{
	char buf[512], home[256];
	char *env[16];
	const char *u, *p;
	char *maildir;
	char *ip;
	ssize_t rd, r;
	gid_t groups[2];
	unsigned int uid, gid;
	int e;

	if (argc < 2) {
		(void)fprintf(stderr, "Usage: %s prog [arg1 [arg2 ... [argn]]]\n", argv[0]);
		return E_USAGE;
	}

	rd = 0;
	for (;;) {
		do
			r = read(3, buf + rd, sizeof(buf) - rd);
		while ((r == -1) && (errno == EINTR));
		if (r == -1)
			return E_TEMPFAIL;
		else if (r == 0)
			break;
		if ((rd += r) >= (ssize_t)sizeof(buf))
			return E_USAGE;
	}
	(void)close(3);

	/*
	 * Extract user and pass, ignore timestamp and rest
	 */
	r = 0;
	while (r < rd - 3 && buf[r] != '\0')
		r++;
	if (buf[r] != '\0')
		return E_USAGE;
	u = buf;
	p = buf + strlen(u) + 1;

	openlog("nsmtp(checkpwd)", LOG_PID, LOG_MAIL);

	/*
	 * Authenticate against LDAP
	 */
	read_ldap_cfg();

	if ((e = ldapauth(u, p)) > 0) {
		xinfo("info: FAILED for user=<%s> prog=<%s> argc=(%d)", u, argv[1], argc);
		free_ldap_cfg();
		return E_FAIL;
	} else if (e < 0) {
		xinfo("info: TEMPFAIL for user=<%s> prog=<%s> argc=(%d)", u, argv[1], argc);
		free_ldap_cfg();
		return E_TEMPFAIL;
	}

	/*
	 * Temporarily allow relaying for the user's IP address
	 */
	if ((ip = getenv("TCPREMOTEIP")) != NULL)
		temp_allow_relay(ip);
	else
		xwarn("warn: could not get remote ip address from environment, can't allow relaying");

	/*
	 * Get Settings from LDAP
	 */
	if ((e = get_maildir_and_uid(u, &maildir, &uid)) > 0) {
		xwarn("warn: FAILED for user=<%s>, auth success but could not get maildir and/or uid", u);
		free_ldap_cfg();
		return E_FAIL;
	} else if (e < 0) {
		xwarn("warn: TEMPFAIL for user=<%s>, auth success but temporary failure getting maildir and uid", u);
		free_ldap_cfg();
		return E_TEMPFAIL;
	}

	free_ldap_cfg();

	gid = uid;
	(void)snprintf(home, sizeof(home), "%s/%s", nsmc_mail_root, maildir);

	xinfo("info: SUCCESS for user=<%s>, mailhome=<%s> expanded=<%s> uid=(%d) gid=(%d), prog=<%s> argc=(%d)", u, maildir, home, uid, gid, argv[1], argc);

	/*
	 * Drop privileges
	 */
	groups[0] = gid;
	groups[1] = 65534;
	if (setgroups(1, groups) != 0) {
		xfatal("fatal: setgroups(1, %d) failed: \"%s\"", gid, strerror(errno));
		return E_FAIL;
	}
	if (setgid(gid) != 0) {
		xfatal("fatal: setgid(%d) failed: \"%s\"", gid, strerror(errno));
		return E_FAIL;
	}
	if (setuid(uid) != 0) {
		xfatal("fatal: setuid(%d) failed: \"%s\"", uid, strerror(errno));
		return E_FAIL;
	}
	if (chdir(home) != 0) {
		xfatal("fatal: chdir(%s) failed: \"%s\"", home, strerror(errno));
		return E_TEMPFAIL;
	}

	/*
	 * Set up environment
	 */
	nsm_asprintf(&env[0], "HOME=%s", home);
	nsm_asprintf(&env[1], "USER=%s", u);
	nsm_asprintf(&env[2], "SHELL=%s", nsmc_nologin);
	nsm_asprintf(&env[3], "PATH=%s", getenv("PATH") != NULL ? getenv("PATH") : nsmc_cleanpath);
	env[4] = NULL;
	if (execve(argv[1], argv+1, env) == -1)
		xfatal("fatal: execve(%s) failed: \"%s\"", argv[1], strerror(errno));

	/*
	 * Normally not reached but if so, fail gracefully
	 */
	return E_TEMPFAIL;
}
