pam_exec.c revision 164154
134192Sjdp/*-
255687Sjdp * Copyright (c) 2001,2003 Networks Associates Technology, Inc.
334192Sjdp * All rights reserved.
434192Sjdp *
534192Sjdp * This software was developed for the FreeBSD Project by ThinkSec AS and
634192Sjdp * NAI Labs, the Security Research Division of Network Associates, Inc.
734192Sjdp * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
834192Sjdp * DARPA CHATS research program.
934192Sjdp *
1034192Sjdp * Redistribution and use in source and binary forms, with or without
1134192Sjdp * modification, are permitted provided that the following conditions
1234192Sjdp * are met:
1334192Sjdp * 1. Redistributions of source code must retain the above copyright
1434192Sjdp *    notice, this list of conditions and the following disclaimer.
1534192Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1634192Sjdp *    notice, this list of conditions and the following disclaimer in the
1734192Sjdp *    documentation and/or other materials provided with the distribution.
1834192Sjdp * 3. The name of the author may not be used to endorse or promote
1934192Sjdp *    products derived from this software without specific prior written
2034192Sjdp *    permission.
2134192Sjdp *
2234192Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2334192Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2434192Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2550476Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2634192Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2734192Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2834192Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2934192Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3034192Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3134192Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3250608Sjdp * SUCH DAMAGE.
3334192Sjdp */
3435529Sdfr
3534192Sjdp#include <sys/cdefs.h>
3634192Sjdp__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_exec/pam_exec.c 164154 2006-11-10 23:33:25Z des $");
3734192Sjdp
3845501Sjdp#include <sys/types.h>
3945501Sjdp#include <sys/wait.h>
4034192Sjdp
4134192Sjdp#include <errno.h>
4234192Sjdp#include <stdlib.h>
4334192Sjdp#include <string.h>
4434192Sjdp#include <unistd.h>
4534192Sjdp
4634192Sjdp#include <security/pam_appl.h>
4734192Sjdp#include <security/pam_modules.h>
4834192Sjdp#include <security/openpam.h>
4934192Sjdp
5034192Sjdp#define ENV_ITEM(n) { (n), #n }
5134192Sjdpstatic struct {
5250609Sjdp	int item;
5334192Sjdp	const char *name;
5434192Sjdp} env_items[] = {
5555687Sjdp	ENV_ITEM(PAM_SERVICE),
5650608Sjdp	ENV_ITEM(PAM_USER),
5760938Sjake	ENV_ITEM(PAM_TTY),
5850608Sjdp	ENV_ITEM(PAM_RHOST),
5950608Sjdp	ENV_ITEM(PAM_RUSER),
6050608Sjdp};
6160938Sjake
6250608Sjdpstatic int
6355687Sjdp_pam_exec(pam_handle_t *pamh __unused, int flags __unused,
6455687Sjdp    int argc, const char *argv[])
6555687Sjdp{
6655687Sjdp	int envlen, i, nitems, pam_err, status;
6760938Sjake	char *env, **envlist, **tmp;
6855687Sjdp	volatile int childerr;
6955687Sjdp	pid_t pid;
7055687Sjdp
7160938Sjake	if (argc < 1)
7255687Sjdp		return (PAM_SERVICE_ERR);
7355687Sjdp
7434192Sjdp	/*
7534192Sjdp	 * XXX For additional credit, divert child's stdin/stdout/stderr
7634192Sjdp	 * to the conversation function.
7734192Sjdp	 */
7834192Sjdp
7934192Sjdp	/*
8062801Sjdp	 * Set up the child's environment list.  It consists of the PAM
8162801Sjdp	 * environment, plus a few hand-picked PAM items.
8262801Sjdp	 */
8362801Sjdp	envlist = pam_getenvlist(pamh);
8462801Sjdp	for (envlen = 0; envlist[envlen] != NULL; ++envlen)
8562801Sjdp		/* nothing */ ;
8662801Sjdp	nitems = sizeof(env_items) / sizeof(*env_items);
8762801Sjdp	tmp = realloc(envlist, (envlen + nitems + 1) * sizeof(*envlist));
8862801Sjdp	if (tmp == NULL) {
8962801Sjdp		openpam_free_envlist(envlist);
9062801Sjdp		return (PAM_BUF_ERR);
9162801Sjdp	}
9262801Sjdp	envlist = tmp;
9362801Sjdp	for (i = 0; i < nitems; ++i) {
9462801Sjdp		const void *item;
9562801Sjdp		char *envstr;
9662801Sjdp
9734192Sjdp		pam_err = pam_get_item(pamh, env_items[i].item, &item);
9834192Sjdp		if (pam_err != PAM_SUCCESS || item == NULL)
9934192Sjdp			continue;
10034192Sjdp		asprintf(&envstr, "%s=%s", env_items[i].name, item);
10134192Sjdp		if (envstr == NULL) {
10250977Sjdp			openpam_free_envlist(envlist);
10350977Sjdp			return (PAM_BUF_ERR);
10450977Sjdp		}
10550977Sjdp		envlist[envlen++] = envstr;
10634192Sjdp		envlist[envlen] = NULL;
10734192Sjdp	}
10834192Sjdp
10934192Sjdp	/*
11034192Sjdp	 * Fork and run the command.  By using vfork() instead of fork(),
11134192Sjdp	 * we can distinguish between an execve() failure and a non-zero
11238467Sjb	 * exit code from the command.
11338467Sjb	 */
11434192Sjdp	childerr = 0;
11534192Sjdp	if ((pid = vfork()) == 0) {
11634192Sjdp		execve(argv[0], (char * const *)argv, (char * const *)envlist);
11734192Sjdp		childerr = errno;
11834192Sjdp		_exit(1);
11934192Sjdp	}
12034192Sjdp	openpam_free_envlist(envlist);
12134192Sjdp	if (pid == -1) {
12234192Sjdp		openpam_log(PAM_LOG_ERROR, "vfork(): %m");
12334192Sjdp		return (PAM_SYSTEM_ERR);
12438467Sjb	}
12534192Sjdp	if (waitpid(pid, &status, 0) == -1) {
12638467Sjb		openpam_log(PAM_LOG_ERROR, "waitpid(): %m");
12734192Sjdp		return (PAM_SYSTEM_ERR);
12838467Sjb	}
12934192Sjdp	if (childerr != 0) {
13050610Sjdp		openpam_log(PAM_LOG_ERROR, "execve(): %m");
13134192Sjdp		return (PAM_SYSTEM_ERR);
13234192Sjdp	}
13345501Sjdp	if (WIFSIGNALED(status)) {
13438467Sjb		openpam_log(PAM_LOG_ERROR, "%s caught signal %d%s",
13534192Sjdp		    argv[0], WTERMSIG(status),
13638816Sdfr		    WCOREDUMP(status) ? " (core dumped)" : "");
13738816Sdfr		return (PAM_SYSTEM_ERR);
13838467Sjb	}
13934192Sjdp	if (!WIFEXITED(status)) {
14038816Sdfr		openpam_log(PAM_LOG_ERROR, "unknown status 0x%x", status);
14138816Sdfr		return (PAM_SYSTEM_ERR);
14238467Sjb	}
14334192Sjdp	if (WEXITSTATUS(status) != 0) {
14434192Sjdp		openpam_log(PAM_LOG_ERROR, "%s returned code %d",
14534192Sjdp		    argv[0], WEXITSTATUS(status));
14638816Sdfr		return (PAM_SYSTEM_ERR);
14734192Sjdp	}
14838816Sdfr	return (PAM_SUCCESS);
14934192Sjdp}
15034192Sjdp
15134192SjdpPAM_EXTERN int
15234192Sjdppam_sm_authenticate(pam_handle_t *pamh, int flags,
15334192Sjdp    int argc, const char *argv[])
15455687Sjdp{
15555687Sjdp
15634192Sjdp	return (_pam_exec(pamh, flags, argc, argv));
15734192Sjdp}
15834192Sjdp
15934192SjdpPAM_EXTERN int
16034192Sjdppam_sm_setcred(pam_handle_t *pamh, int flags,
16138740Sjdp    int argc, const char *argv[])
16256780Sjdp{
16335529Sdfr
16435529Sdfr	return (_pam_exec(pamh, flags, argc, argv));
16550977Sjdp}
16650977Sjdp
16750977SjdpPAM_EXTERN int
16850977Sjdppam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
16934192Sjdp    int argc, const char *argv[])
17034192Sjdp{
17134192Sjdp
17234192Sjdp	return (_pam_exec(pamh, flags, argc, argv));
17334192Sjdp}
17448871Sjdp
17550609SjdpPAM_EXTERN int
17634192Sjdppam_sm_open_session(pam_handle_t *pamh, int flags,
17734192Sjdp    int argc, const char *argv[])
17834192Sjdp{
17938816Sdfr
18034192Sjdp	return (_pam_exec(pamh, flags, argc, argv));
18138816Sdfr}
18238816Sdfr
18338816SdfrPAM_EXTERN int
18438816Sdfrpam_sm_close_session(pam_handle_t *pamh, int flags,
18538816Sdfr    int argc, const char *argv[])
18650608Sjdp{
18750608Sjdp
18845501Sjdp	return (_pam_exec(pamh, flags, argc, argv));
18962801Sjdp}
19050608Sjdp
19150608SjdpPAM_EXTERN int
19245501Sjdppam_sm_chauthtok(pam_handle_t *pamh, int flags,
19356780Sjdp    int argc, const char *argv[])
19456780Sjdp{
19545501Sjdp
19638816Sdfr	return (_pam_exec(pamh, flags, argc, argv));
19738816Sdfr}
19838816Sdfr
19934192SjdpPAM_MODULE_ENTRY("pam_exec");
200