pam_exec.c revision 150339
1/*-
2 * Copyright (c) 2001,2003 Networks Associates Technology, Inc.
3 * All rights reserved.
4 *
5 * This software was developed for the FreeBSD Project by ThinkSec AS and
6 * NAI Labs, the Security Research Division of Network Associates, Inc.
7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8 * DARPA CHATS research program.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote
19 *    products derived from this software without specific prior written
20 *    permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD: head/lib/libpam/modules/pam_exec/pam_exec.c 150339 2005-09-19 18:43:11Z cperciva $");
37
38#include <sys/types.h>
39#include <sys/wait.h>
40
41#include <errno.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45
46#include <security/pam_appl.h>
47#include <security/pam_modules.h>
48#include <security/openpam.h>
49
50#define ENV_ITEM(n) { (n), #n }
51static struct {
52	int item;
53	const char *name;
54} env_items[] = {
55	ENV_ITEM(PAM_SERVICE),
56	ENV_ITEM(PAM_USER),
57	ENV_ITEM(PAM_TTY),
58	ENV_ITEM(PAM_RHOST),
59	ENV_ITEM(PAM_RUSER),
60};
61
62static int
63_pam_exec(pam_handle_t *pamh __unused, int flags __unused,
64    int argc, const char *argv[])
65{
66	int childerr, envlen, i, nitems, pam_err, status;
67	char *env, **envlist, **tmp;
68	pid_t pid;
69
70	if (argc < 1)
71		return (PAM_SERVICE_ERR);
72
73	/*
74	 * XXX For additional credit, divert child's stdin/stdout/stderr
75	 * to the conversation function.
76	 */
77
78	/*
79	 * Set up the child's environment list.  It consists of the PAM
80	 * environment, plus a few hand-picked PAM items.
81	 */
82	envlist = pam_getenvlist(pamh);
83	for (envlen = 0; envlist[envlen] != NULL; ++envlen)
84		/* nothing */ ;
85	nitems = sizeof(env_items) / sizeof(*env_items);
86	tmp = realloc(envlist, (envlen + nitems + 1) * sizeof(*envlist));
87	if (tmp == NULL) {
88		openpam_free_envlist(envlist);
89		return (PAM_BUF_ERR);
90	}
91	envlist = tmp;
92	for (i = 0; i < nitems; ++i) {
93		const void *item;
94		char *envstr;
95
96		pam_err = pam_get_item(pamh, env_items[i].item, &item);
97		if (pam_err != PAM_SUCCESS || item == NULL)
98			continue;
99		asprintf(&envstr, "%s=%s", env_items[i].name, item);
100		if (envstr == NULL) {
101			openpam_free_envlist(envlist);
102			return (PAM_BUF_ERR);
103		}
104		envlist[envlen++] = envstr;
105		envlist[envlen] = NULL;
106	}
107
108	/*
109	 * Fork and run the command.  By using vfork() instead of fork(),
110	 * we can distinguish between an execve() failure and a non-zero
111	 * exit code from the command.
112	 */
113	childerr = 0;
114	if ((pid = vfork()) == 0) {
115		execve(argv[0], (char * const *)argv, (char * const *)envlist);
116		childerr = errno;
117		_exit(1);
118	}
119	openpam_free_envlist(envlist);
120	if (pid == -1) {
121		openpam_log(PAM_LOG_ERROR, "vfork(): %m");
122		return (PAM_SYSTEM_ERR);
123	}
124	if (waitpid(pid, &status, 0) == -1) {
125		openpam_log(PAM_LOG_ERROR, "waitpid(): %m");
126		return (PAM_SYSTEM_ERR);
127	}
128	if (childerr != 0) {
129		openpam_log(PAM_LOG_ERROR, "execve(): %m");
130		return (PAM_SYSTEM_ERR);
131	}
132	if (WIFSIGNALED(status)) {
133		openpam_log(PAM_LOG_ERROR, "%s caught signal %d%s",
134		    argv[0], WTERMSIG(status),
135		    WCOREDUMP(status) ? " (core dumped)" : "");
136		return (PAM_SYSTEM_ERR);
137	}
138	if (!WIFEXITED(status)) {
139		openpam_log(PAM_LOG_ERROR, "unknown status 0x%x", status);
140		return (PAM_SYSTEM_ERR);
141	}
142	if (WEXITSTATUS(status) != 0) {
143		openpam_log(PAM_LOG_ERROR, "%s returned code %d",
144		    argv[0], WEXITSTATUS(status));
145		return (PAM_SYSTEM_ERR);
146	}
147	return (PAM_SUCCESS);
148}
149
150PAM_EXTERN int
151pam_sm_authenticate(pam_handle_t *pamh, int flags,
152    int argc, const char *argv[])
153{
154
155	return (_pam_exec(pamh, flags, argc, argv));
156}
157
158PAM_EXTERN int
159pam_sm_setcred(pam_handle_t *pamh, int flags,
160    int argc, const char *argv[])
161{
162
163	return (_pam_exec(pamh, flags, argc, argv));
164}
165
166PAM_EXTERN int
167pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
168    int argc, const char *argv[])
169{
170
171	return (_pam_exec(pamh, flags, argc, argv));
172}
173
174PAM_EXTERN int
175pam_sm_open_session(pam_handle_t *pamh, int flags,
176    int argc, const char *argv[])
177{
178
179	return (_pam_exec(pamh, flags, argc, argv));
180}
181
182PAM_EXTERN int
183pam_sm_close_session(pam_handle_t *pamh, int flags,
184    int argc, const char *argv[])
185{
186
187	return (_pam_exec(pamh, flags, argc, argv));
188}
189
190PAM_EXTERN int
191pam_sm_chauthtok(pam_handle_t *pamh, int flags,
192    int argc, const char *argv[])
193{
194
195	return (_pam_exec(pamh, flags, argc, argv));
196}
197
198PAM_MODULE_ENTRY("pam_exec");
199