2 * Copyright (c) 2001,2003 Networks Associates Technology, Inc.
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.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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
34 * $FreeBSD: src/lib/libpam/modules/pam_exec/pam_exec.c,v 1.9 2012/04/12 14:02:59 dumbbell Exp $
37 #include <sys/types.h>
46 #include <security/pam_appl.h>
47 #include <security/pam_modules.h>
48 #include <security/openpam.h>
50 #define ENV_ITEM(n) { (n), #n }
55 ENV_ITEM(PAM_SERVICE),
63 int return_prog_exit_status;
66 #define PAM_RV_COUNT 24
69 parse_options(const char *func, int *argc, const char **argv[],
70 struct pe_opts *options)
76 * return_prog_exit_status:
77 * use the program exit status as the return code of pam_exec
79 * stop options parsing; what follows is the command to execute
81 options->return_prog_exit_status = 0;
83 for (i = 0; i < *argc; ++i) {
84 if (strcmp((*argv)[i], "return_prog_exit_status") == 0) {
85 openpam_log(PAM_LOG_DEBUG,
86 "%s: Option \"return_prog_exit_status\" enabled",
88 options->return_prog_exit_status = 1;
90 if (strcmp((*argv)[i], "--") == 0) {
106 _pam_exec(pam_handle_t *pamh __unused,
107 const char *func, int flags __unused, int argc, const char *argv[],
108 struct pe_opts *options)
110 int envlen, i, nitems, pam_err, status;
112 char **envlist, **tmp, *envstr;
113 volatile int childerr;
117 * XXX For additional credit, divert child's stdin/stdout/stderr
118 * to the conversation function.
121 /* Check there's a program name left after parsing options. */
123 openpam_log(PAM_LOG_ERROR, "%s: No program specified: aborting",
125 return (PAM_SERVICE_ERR);
129 * Set up the child's environment list. It consists of the PAM
130 * environment, plus a few hand-picked PAM items, the pam_sm_*
131 * function name calling it and, if return_prog_exit_status is
132 * set, the valid return codes numerical values.
134 envlist = pam_getenvlist(pamh);
135 for (envlen = 0; envlist[envlen] != NULL; ++envlen)
137 nitems = sizeof(env_items) / sizeof(*env_items);
138 /* Count PAM return values put in the environment. */
139 nitems_rv = options->return_prog_exit_status ? PAM_RV_COUNT : 0;
140 tmp = realloc(envlist, (envlen + nitems + 1 + nitems_rv + 1) *
143 openpam_free_envlist(envlist);
144 return (PAM_BUF_ERR);
147 for (i = 0; i < nitems; ++i) {
150 pam_err = pam_get_item(pamh, env_items[i].item, &item);
151 if (pam_err != PAM_SUCCESS || item == NULL)
153 asprintf(&envstr, "%s=%s", env_items[i].name,
155 if (envstr == NULL) {
156 openpam_free_envlist(envlist);
157 return (PAM_BUF_ERR);
159 envlist[envlen++] = envstr;
160 envlist[envlen] = NULL;
163 /* Add the pam_sm_* function name to the environment. */
164 asprintf(&envstr, "PAM_SM_FUNC=%s", func);
165 if (envstr == NULL) {
166 openpam_free_envlist(envlist);
167 return (PAM_BUF_ERR);
169 envlist[envlen++] = envstr;
171 /* Add the PAM return values to the environment. */
172 if (options->return_prog_exit_status) {
173 #define ADD_PAM_RV_TO_ENV(name) \
174 asprintf(&envstr, #name "=%d", name); \
175 if (envstr == NULL) { \
176 openpam_free_envlist(envlist); \
177 return (PAM_BUF_ERR); \
179 envlist[envlen++] = envstr
181 * CAUTION: When adding/removing an item in the list
182 * below, be sure to update the value of PAM_RV_COUNT.
184 ADD_PAM_RV_TO_ENV(PAM_ABORT);
185 ADD_PAM_RV_TO_ENV(PAM_ACCT_EXPIRED);
186 ADD_PAM_RV_TO_ENV(PAM_AUTHINFO_UNAVAIL);
187 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_DISABLE_AGING);
188 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_ERR);
189 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_LOCK_BUSY);
190 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_RECOVERY_ERR);
191 ADD_PAM_RV_TO_ENV(PAM_AUTH_ERR);
192 ADD_PAM_RV_TO_ENV(PAM_BUF_ERR);
193 ADD_PAM_RV_TO_ENV(PAM_CONV_ERR);
194 ADD_PAM_RV_TO_ENV(PAM_CRED_ERR);
195 ADD_PAM_RV_TO_ENV(PAM_CRED_EXPIRED);
196 ADD_PAM_RV_TO_ENV(PAM_CRED_INSUFFICIENT);
197 ADD_PAM_RV_TO_ENV(PAM_CRED_UNAVAIL);
198 ADD_PAM_RV_TO_ENV(PAM_IGNORE);
199 ADD_PAM_RV_TO_ENV(PAM_MAXTRIES);
200 ADD_PAM_RV_TO_ENV(PAM_NEW_AUTHTOK_REQD);
201 ADD_PAM_RV_TO_ENV(PAM_PERM_DENIED);
202 ADD_PAM_RV_TO_ENV(PAM_SERVICE_ERR);
203 ADD_PAM_RV_TO_ENV(PAM_SESSION_ERR);
204 ADD_PAM_RV_TO_ENV(PAM_SUCCESS);
205 ADD_PAM_RV_TO_ENV(PAM_SYSTEM_ERR);
206 ADD_PAM_RV_TO_ENV(PAM_TRY_AGAIN);
207 ADD_PAM_RV_TO_ENV(PAM_USER_UNKNOWN);
210 envlist[envlen] = NULL;
213 * Fork and run the command. By using vfork() instead of fork(),
214 * we can distinguish between an execve() failure and a non-zero
215 * exit status from the command.
218 if ((pid = vfork()) == 0) {
219 execve(argv[0], (char * const *)argv, (char * const *)envlist);
223 openpam_free_envlist(envlist);
225 openpam_log(PAM_LOG_ERROR, "%s: vfork(): %m", func);
226 return (PAM_SYSTEM_ERR);
228 while (waitpid(pid, &status, 0) == -1) {
231 openpam_log(PAM_LOG_ERROR, "%s: waitpid(): %m", func);
232 return (PAM_SYSTEM_ERR);
235 openpam_log(PAM_LOG_ERROR, "%s: execve(): %m", func);
236 return (PAM_SYSTEM_ERR);
238 if (WIFSIGNALED(status)) {
239 openpam_log(PAM_LOG_ERROR, "%s: %s caught signal %d%s",
240 func, argv[0], WTERMSIG(status),
241 WCOREDUMP(status) ? " (core dumped)" : "");
242 return (PAM_SERVICE_ERR);
244 if (!WIFEXITED(status)) {
245 openpam_log(PAM_LOG_ERROR, "%s: unknown status 0x%x",
247 return (PAM_SERVICE_ERR);
250 if (options->return_prog_exit_status) {
251 openpam_log(PAM_LOG_DEBUG,
252 "%s: Use program exit status as return value: %d",
253 func, WEXITSTATUS(status));
254 return (WEXITSTATUS(status));
256 return (WEXITSTATUS(status) == 0 ?
257 PAM_SUCCESS : PAM_PERM_DENIED);
262 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char *argv[])
265 struct pe_opts options;
267 ret = parse_options(__func__, &argc, &argv, &options);
269 return (PAM_SERVICE_ERR);
271 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
274 * We must check that the program returned a valid code for this
280 case PAM_AUTHINFO_UNAVAIL:
284 case PAM_CRED_INSUFFICIENT:
287 case PAM_PERM_DENIED:
288 case PAM_SERVICE_ERR:
290 case PAM_USER_UNKNOWN:
293 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
295 ret = PAM_SERVICE_ERR;
302 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char *argv[])
305 struct pe_opts options;
307 ret = parse_options(__func__, &argc, &argv, &options);
309 return (PAM_SERVICE_ERR);
311 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
314 * We must check that the program returned a valid code for this
323 case PAM_CRED_EXPIRED:
324 case PAM_CRED_UNAVAIL:
326 case PAM_PERM_DENIED:
327 case PAM_SERVICE_ERR:
329 case PAM_USER_UNKNOWN:
332 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
334 ret = PAM_SERVICE_ERR;
341 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char *argv[])
344 struct pe_opts options;
346 ret = parse_options(__func__, &argc, &argv, &options);
348 return (PAM_SERVICE_ERR);
350 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
353 * We must check that the program returned a valid code for this
359 case PAM_ACCT_EXPIRED:
364 case PAM_NEW_AUTHTOK_REQD:
365 case PAM_PERM_DENIED:
366 case PAM_SERVICE_ERR:
368 case PAM_USER_UNKNOWN:
371 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
373 ret = PAM_SERVICE_ERR;
380 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char *argv[])
383 struct pe_opts options;
385 ret = parse_options(__func__, &argc, &argv, &options);
387 return (PAM_SERVICE_ERR);
389 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
392 * We must check that the program returned a valid code for this
401 case PAM_PERM_DENIED:
402 case PAM_SERVICE_ERR:
403 case PAM_SESSION_ERR:
407 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
409 ret = PAM_SERVICE_ERR;
416 pam_sm_close_session(pam_handle_t *pamh, int flags,
417 int argc, const char *argv[])
420 struct pe_opts options;
422 ret = parse_options(__func__, &argc, &argv, &options);
424 return (PAM_SERVICE_ERR);
426 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
429 * We must check that the program returned a valid code for this
438 case PAM_PERM_DENIED:
439 case PAM_SERVICE_ERR:
440 case PAM_SESSION_ERR:
444 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
446 ret = PAM_SERVICE_ERR;
453 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char *argv[])
456 struct pe_opts options;
458 ret = parse_options(__func__, &argc, &argv, &options);
460 return (PAM_SERVICE_ERR);
462 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
465 * We must check that the program returned a valid code for this
471 case PAM_AUTHTOK_DISABLE_AGING:
472 case PAM_AUTHTOK_ERR:
473 case PAM_AUTHTOK_LOCK_BUSY:
474 case PAM_AUTHTOK_RECOVERY_ERR:
478 case PAM_PERM_DENIED:
479 case PAM_SERVICE_ERR:
484 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
486 ret = PAM_SERVICE_ERR;
492 PAM_MODULE_ENTRY("pam_exec");