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
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
38 #include <sys/types.h>
47 #include <security/pam_appl.h>
48 #include <security/pam_modules.h>
49 #include <security/openpam.h>
51 #define ENV_ITEM(n) { (n), #n }
56 ENV_ITEM(PAM_SERVICE),
64 int return_prog_exit_status;
67 #define PAM_RV_COUNT 24
70 parse_options(const char *func, int *argc, const char **argv[],
71 struct pe_opts *options)
77 * return_prog_exit_status:
78 * use the program exit status as the return code of pam_exec
80 * stop options parsing; what follows is the command to execute
82 options->return_prog_exit_status = 0;
84 for (i = 0; i < *argc; ++i) {
85 if (strcmp((*argv)[i], "return_prog_exit_status") == 0) {
86 openpam_log(PAM_LOG_DEBUG,
87 "%s: Option \"return_prog_exit_status\" enabled",
89 options->return_prog_exit_status = 1;
91 if (strcmp((*argv)[i], "--") == 0) {
107 _pam_exec(pam_handle_t *pamh __unused,
108 const char *func, int flags __unused, int argc, const char *argv[],
109 struct pe_opts *options)
111 int envlen, i, nitems, pam_err, status;
113 char **envlist, **tmp, *envstr;
114 volatile int childerr;
118 * XXX For additional credit, divert child's stdin/stdout/stderr
119 * to the conversation function.
122 /* Check there's a program name left after parsing options. */
124 openpam_log(PAM_LOG_ERROR, "%s: No program specified: aborting",
126 return (PAM_SERVICE_ERR);
130 * Set up the child's environment list. It consists of the PAM
131 * environment, plus a few hand-picked PAM items, the pam_sm_*
132 * function name calling it and, if return_prog_exit_status is
133 * set, the valid return codes numerical values.
135 envlist = pam_getenvlist(pamh);
136 for (envlen = 0; envlist[envlen] != NULL; ++envlen)
138 nitems = sizeof(env_items) / sizeof(*env_items);
139 /* Count PAM return values put in the environment. */
140 nitems_rv = options->return_prog_exit_status ? PAM_RV_COUNT : 0;
141 tmp = realloc(envlist, (envlen + nitems + 1 + nitems_rv + 1) *
144 openpam_free_envlist(envlist);
145 return (PAM_BUF_ERR);
148 for (i = 0; i < nitems; ++i) {
151 pam_err = pam_get_item(pamh, env_items[i].item, &item);
152 if (pam_err != PAM_SUCCESS || item == NULL)
154 asprintf(&envstr, "%s=%s", env_items[i].name,
156 if (envstr == NULL) {
157 openpam_free_envlist(envlist);
158 return (PAM_BUF_ERR);
160 envlist[envlen++] = envstr;
161 envlist[envlen] = NULL;
164 /* Add the pam_sm_* function name to the environment. */
165 asprintf(&envstr, "PAM_SM_FUNC=%s", func);
166 if (envstr == NULL) {
167 openpam_free_envlist(envlist);
168 return (PAM_BUF_ERR);
170 envlist[envlen++] = envstr;
172 /* Add the PAM return values to the environment. */
173 if (options->return_prog_exit_status) {
174 #define ADD_PAM_RV_TO_ENV(name) \
175 asprintf(&envstr, #name "=%d", name); \
176 if (envstr == NULL) { \
177 openpam_free_envlist(envlist); \
178 return (PAM_BUF_ERR); \
180 envlist[envlen++] = envstr
182 * CAUTION: When adding/removing an item in the list
183 * below, be sure to update the value of PAM_RV_COUNT.
185 ADD_PAM_RV_TO_ENV(PAM_ABORT);
186 ADD_PAM_RV_TO_ENV(PAM_ACCT_EXPIRED);
187 ADD_PAM_RV_TO_ENV(PAM_AUTHINFO_UNAVAIL);
188 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_DISABLE_AGING);
189 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_ERR);
190 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_LOCK_BUSY);
191 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_RECOVERY_ERR);
192 ADD_PAM_RV_TO_ENV(PAM_AUTH_ERR);
193 ADD_PAM_RV_TO_ENV(PAM_BUF_ERR);
194 ADD_PAM_RV_TO_ENV(PAM_CONV_ERR);
195 ADD_PAM_RV_TO_ENV(PAM_CRED_ERR);
196 ADD_PAM_RV_TO_ENV(PAM_CRED_EXPIRED);
197 ADD_PAM_RV_TO_ENV(PAM_CRED_INSUFFICIENT);
198 ADD_PAM_RV_TO_ENV(PAM_CRED_UNAVAIL);
199 ADD_PAM_RV_TO_ENV(PAM_IGNORE);
200 ADD_PAM_RV_TO_ENV(PAM_MAXTRIES);
201 ADD_PAM_RV_TO_ENV(PAM_NEW_AUTHTOK_REQD);
202 ADD_PAM_RV_TO_ENV(PAM_PERM_DENIED);
203 ADD_PAM_RV_TO_ENV(PAM_SERVICE_ERR);
204 ADD_PAM_RV_TO_ENV(PAM_SESSION_ERR);
205 ADD_PAM_RV_TO_ENV(PAM_SUCCESS);
206 ADD_PAM_RV_TO_ENV(PAM_SYSTEM_ERR);
207 ADD_PAM_RV_TO_ENV(PAM_TRY_AGAIN);
208 ADD_PAM_RV_TO_ENV(PAM_USER_UNKNOWN);
211 envlist[envlen] = NULL;
214 * Fork and run the command. By using vfork() instead of fork(),
215 * we can distinguish between an execve() failure and a non-zero
216 * exit status from the command.
219 if ((pid = vfork()) == 0) {
220 execve(argv[0], (char * const *)argv, (char * const *)envlist);
224 openpam_free_envlist(envlist);
226 openpam_log(PAM_LOG_ERROR, "%s: vfork(): %m", func);
227 return (PAM_SYSTEM_ERR);
229 while (waitpid(pid, &status, 0) == -1) {
232 openpam_log(PAM_LOG_ERROR, "%s: waitpid(): %m", func);
233 return (PAM_SYSTEM_ERR);
236 openpam_log(PAM_LOG_ERROR, "%s: execve(): %m", func);
237 return (PAM_SYSTEM_ERR);
239 if (WIFSIGNALED(status)) {
240 openpam_log(PAM_LOG_ERROR, "%s: %s caught signal %d%s",
241 func, argv[0], WTERMSIG(status),
242 WCOREDUMP(status) ? " (core dumped)" : "");
243 return (PAM_SERVICE_ERR);
245 if (!WIFEXITED(status)) {
246 openpam_log(PAM_LOG_ERROR, "%s: unknown status 0x%x",
248 return (PAM_SERVICE_ERR);
251 if (options->return_prog_exit_status) {
252 openpam_log(PAM_LOG_DEBUG,
253 "%s: Use program exit status as return value: %d",
254 func, WEXITSTATUS(status));
255 return (WEXITSTATUS(status));
257 return (WEXITSTATUS(status) == 0 ?
258 PAM_SUCCESS : PAM_PERM_DENIED);
263 pam_sm_authenticate(pam_handle_t *pamh, int flags,
264 int argc, const char *argv[])
267 struct pe_opts options;
269 ret = parse_options(__func__, &argc, &argv, &options);
271 return (PAM_SERVICE_ERR);
273 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
276 * We must check that the program returned a valid code for this
282 case PAM_AUTHINFO_UNAVAIL:
286 case PAM_CRED_INSUFFICIENT:
289 case PAM_PERM_DENIED:
290 case PAM_SERVICE_ERR:
292 case PAM_USER_UNKNOWN:
295 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
297 ret = PAM_SERVICE_ERR;
304 pam_sm_setcred(pam_handle_t *pamh, int flags,
305 int argc, const char *argv[])
308 struct pe_opts options;
310 ret = parse_options(__func__, &argc, &argv, &options);
312 return (PAM_SERVICE_ERR);
314 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
317 * We must check that the program returned a valid code for this
326 case PAM_CRED_EXPIRED:
327 case PAM_CRED_UNAVAIL:
329 case PAM_PERM_DENIED:
330 case PAM_SERVICE_ERR:
332 case PAM_USER_UNKNOWN:
335 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
337 ret = PAM_SERVICE_ERR;
344 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
345 int argc, const char *argv[])
348 struct pe_opts options;
350 ret = parse_options(__func__, &argc, &argv, &options);
352 return (PAM_SERVICE_ERR);
354 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
357 * We must check that the program returned a valid code for this
363 case PAM_ACCT_EXPIRED:
368 case PAM_NEW_AUTHTOK_REQD:
369 case PAM_PERM_DENIED:
370 case PAM_SERVICE_ERR:
372 case PAM_USER_UNKNOWN:
375 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
377 ret = PAM_SERVICE_ERR;
384 pam_sm_open_session(pam_handle_t *pamh, int flags,
385 int argc, const char *argv[])
388 struct pe_opts options;
390 ret = parse_options(__func__, &argc, &argv, &options);
392 return (PAM_SERVICE_ERR);
394 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
397 * We must check that the program returned a valid code for this
406 case PAM_PERM_DENIED:
407 case PAM_SERVICE_ERR:
408 case PAM_SESSION_ERR:
412 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
414 ret = PAM_SERVICE_ERR;
421 pam_sm_close_session(pam_handle_t *pamh, int flags,
422 int argc, const char *argv[])
425 struct pe_opts options;
427 ret = parse_options(__func__, &argc, &argv, &options);
429 return (PAM_SERVICE_ERR);
431 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
434 * We must check that the program returned a valid code for this
443 case PAM_PERM_DENIED:
444 case PAM_SERVICE_ERR:
445 case PAM_SESSION_ERR:
449 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
451 ret = PAM_SERVICE_ERR;
458 pam_sm_chauthtok(pam_handle_t *pamh, int flags,
459 int argc, const char *argv[])
462 struct pe_opts options;
464 ret = parse_options(__func__, &argc, &argv, &options);
466 return (PAM_SERVICE_ERR);
468 ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
471 * We must check that the program returned a valid code for this
477 case PAM_AUTHTOK_DISABLE_AGING:
478 case PAM_AUTHTOK_ERR:
479 case PAM_AUTHTOK_LOCK_BUSY:
480 case PAM_AUTHTOK_RECOVERY_ERR:
484 case PAM_PERM_DENIED:
485 case PAM_SERVICE_ERR:
490 openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
492 ret = PAM_SERVICE_ERR;
498 PAM_MODULE_ENTRY("pam_exec");