2 * SPDX-License-Identifier: BSD-3-Clause
4 * Copyright (c) 2001,2003 Networks Associates Technology, Inc.
7 * This software was developed for the FreeBSD Project by ThinkSec AS and
8 * NAI Labs, the Security Research Division of Network Associates, Inc.
9 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
10 * DARPA CHATS research program.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. The name of the author may not be used to endorse or promote
21 * products derived from this software without specific prior written
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * $FreeBSD: head/lib/libpam/modules/pam_exec/pam_exec.c 315164 2017-03-12 17:41:51Z pfg $
39 #include <sys/types.h>
48 #include <security/pam_appl.h>
49 #include <security/pam_modules.h>
50 #include <security/openpam.h>
52 #define ENV_ITEM(n) { (n), #n }
57 ENV_ITEM(PAM_SERVICE
),
65 int return_prog_exit_status
;
68 #define PAM_RV_COUNT 24
71 parse_options(const char *func
, int *argc
, const char **argv
[],
72 struct pe_opts
*options
)
78 * return_prog_exit_status:
79 * use the program exit status as the return code of pam_exec
81 * stop options parsing; what follows is the command to execute
83 options
->return_prog_exit_status
= 0;
85 for (i
= 0; i
< *argc
; ++i
) {
86 if (strcmp((*argv
)[i
], "return_prog_exit_status") == 0) {
87 openpam_log(PAM_LOG_DEBUG
,
88 "%s: Option \"return_prog_exit_status\" enabled",
90 options
->return_prog_exit_status
= 1;
92 if (strcmp((*argv
)[i
], "--") == 0) {
108 _pam_exec(pam_handle_t
*pamh __unused
,
109 const char *func
, int flags __unused
, int argc
, const char *argv
[],
110 struct pe_opts
*options
)
112 int envlen
, i
, nitems
, pam_err
, status
;
114 char **envlist
, **tmp
, *envstr
;
115 volatile int childerr
;
119 * XXX For additional credit, divert child's stdin/stdout/stderr
120 * to the conversation function.
123 /* Check there's a program name left after parsing options. */
125 openpam_log(PAM_LOG_ERROR
, "%s: No program specified: aborting",
127 return (PAM_SERVICE_ERR
);
131 * Set up the child's environment list. It consists of the PAM
132 * environment, plus a few hand-picked PAM items, the pam_sm_*
133 * function name calling it and, if return_prog_exit_status is
134 * set, the valid return codes numerical values.
136 envlist
= pam_getenvlist(pamh
);
137 for (envlen
= 0; envlist
[envlen
] != NULL
; ++envlen
)
139 nitems
= sizeof(env_items
) / sizeof(*env_items
);
140 /* Count PAM return values put in the environment. */
141 nitems_rv
= options
->return_prog_exit_status
? PAM_RV_COUNT
: 0;
142 tmp
= realloc(envlist
, (envlen
+ nitems
+ 1 + nitems_rv
+ 1) *
145 openpam_free_envlist(envlist
);
146 return (PAM_BUF_ERR
);
149 for (i
= 0; i
< nitems
; ++i
) {
152 pam_err
= pam_get_item(pamh
, env_items
[i
].item
, &item
);
153 if (pam_err
!= PAM_SUCCESS
|| item
== NULL
)
155 asprintf(&envstr
, "%s=%s", env_items
[i
].name
,
157 if (envstr
== NULL
) {
158 openpam_free_envlist(envlist
);
159 return (PAM_BUF_ERR
);
161 envlist
[envlen
++] = envstr
;
162 envlist
[envlen
] = NULL
;
165 /* Add the pam_sm_* function name to the environment. */
166 asprintf(&envstr
, "PAM_SM_FUNC=%s", func
);
167 if (envstr
== NULL
) {
168 openpam_free_envlist(envlist
);
169 return (PAM_BUF_ERR
);
171 envlist
[envlen
++] = envstr
;
173 /* Add the PAM return values to the environment. */
174 if (options
->return_prog_exit_status
) {
175 #define ADD_PAM_RV_TO_ENV(name) \
176 asprintf(&envstr, #name "=%d", name); \
177 if (envstr == NULL) { \
178 openpam_free_envlist(envlist); \
179 return (PAM_BUF_ERR); \
181 envlist[envlen++] = envstr
183 * CAUTION: When adding/removing an item in the list
184 * below, be sure to update the value of PAM_RV_COUNT.
186 ADD_PAM_RV_TO_ENV(PAM_ABORT
);
187 ADD_PAM_RV_TO_ENV(PAM_ACCT_EXPIRED
);
188 ADD_PAM_RV_TO_ENV(PAM_AUTHINFO_UNAVAIL
);
189 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_DISABLE_AGING
);
190 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_ERR
);
191 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_LOCK_BUSY
);
192 ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_RECOVERY_ERR
);
193 ADD_PAM_RV_TO_ENV(PAM_AUTH_ERR
);
194 ADD_PAM_RV_TO_ENV(PAM_BUF_ERR
);
195 ADD_PAM_RV_TO_ENV(PAM_CONV_ERR
);
196 ADD_PAM_RV_TO_ENV(PAM_CRED_ERR
);
197 ADD_PAM_RV_TO_ENV(PAM_CRED_EXPIRED
);
198 ADD_PAM_RV_TO_ENV(PAM_CRED_INSUFFICIENT
);
199 ADD_PAM_RV_TO_ENV(PAM_CRED_UNAVAIL
);
200 ADD_PAM_RV_TO_ENV(PAM_IGNORE
);
201 ADD_PAM_RV_TO_ENV(PAM_MAXTRIES
);
202 ADD_PAM_RV_TO_ENV(PAM_NEW_AUTHTOK_REQD
);
203 ADD_PAM_RV_TO_ENV(PAM_PERM_DENIED
);
204 ADD_PAM_RV_TO_ENV(PAM_SERVICE_ERR
);
205 ADD_PAM_RV_TO_ENV(PAM_SESSION_ERR
);
206 ADD_PAM_RV_TO_ENV(PAM_SUCCESS
);
207 ADD_PAM_RV_TO_ENV(PAM_SYSTEM_ERR
);
208 ADD_PAM_RV_TO_ENV(PAM_TRY_AGAIN
);
209 ADD_PAM_RV_TO_ENV(PAM_USER_UNKNOWN
);
212 envlist
[envlen
] = NULL
;
215 * Fork and run the command. By using vfork() instead of fork(),
216 * we can distinguish between an execve() failure and a non-zero
217 * exit status from the command.
220 if ((pid
= vfork()) == 0) {
221 execve(argv
[0], (char * const *)argv
, (char * const *)envlist
);
225 openpam_free_envlist(envlist
);
227 openpam_log(PAM_LOG_ERROR
, "%s: vfork(): %m", func
);
228 return (PAM_SYSTEM_ERR
);
230 while (waitpid(pid
, &status
, 0) == -1) {
233 openpam_log(PAM_LOG_ERROR
, "%s: waitpid(): %m", func
);
234 return (PAM_SYSTEM_ERR
);
237 openpam_log(PAM_LOG_ERROR
, "%s: execve(): %m", func
);
238 return (PAM_SYSTEM_ERR
);
240 if (WIFSIGNALED(status
)) {
241 openpam_log(PAM_LOG_ERROR
, "%s: %s caught signal %d%s",
242 func
, argv
[0], WTERMSIG(status
),
243 WCOREDUMP(status
) ? " (core dumped)" : "");
244 return (PAM_SERVICE_ERR
);
246 if (!WIFEXITED(status
)) {
247 openpam_log(PAM_LOG_ERROR
, "%s: unknown status 0x%x",
249 return (PAM_SERVICE_ERR
);
252 if (options
->return_prog_exit_status
) {
253 openpam_log(PAM_LOG_DEBUG
,
254 "%s: Use program exit status as return value: %d",
255 func
, WEXITSTATUS(status
));
256 return (WEXITSTATUS(status
));
258 return (WEXITSTATUS(status
) == 0 ?
259 PAM_SUCCESS
: PAM_PERM_DENIED
);
264 pam_sm_authenticate(pam_handle_t
*pamh
, int flags
,
265 int argc
, const char *argv
[])
268 struct pe_opts options
;
270 ret
= parse_options(__func__
, &argc
, &argv
, &options
);
272 return (PAM_SERVICE_ERR
);
274 ret
= _pam_exec(pamh
, __func__
, flags
, argc
, argv
, &options
);
277 * We must check that the program returned a valid code for this
283 case PAM_AUTHINFO_UNAVAIL
:
287 case PAM_CRED_INSUFFICIENT
:
290 case PAM_PERM_DENIED
:
291 case PAM_SERVICE_ERR
:
293 case PAM_USER_UNKNOWN
:
296 openpam_log(PAM_LOG_ERROR
, "%s returned invalid code %d",
298 ret
= PAM_SERVICE_ERR
;
305 pam_sm_setcred(pam_handle_t
*pamh
, int flags
,
306 int argc
, const char *argv
[])
309 struct pe_opts options
;
311 ret
= parse_options(__func__
, &argc
, &argv
, &options
);
313 return (PAM_SERVICE_ERR
);
315 ret
= _pam_exec(pamh
, __func__
, flags
, argc
, argv
, &options
);
318 * We must check that the program returned a valid code for this
327 case PAM_CRED_EXPIRED
:
328 case PAM_CRED_UNAVAIL
:
330 case PAM_PERM_DENIED
:
331 case PAM_SERVICE_ERR
:
333 case PAM_USER_UNKNOWN
:
336 openpam_log(PAM_LOG_ERROR
, "%s returned invalid code %d",
338 ret
= PAM_SERVICE_ERR
;
345 pam_sm_acct_mgmt(pam_handle_t
*pamh
, int flags
,
346 int argc
, const char *argv
[])
349 struct pe_opts options
;
351 ret
= parse_options(__func__
, &argc
, &argv
, &options
);
353 return (PAM_SERVICE_ERR
);
355 ret
= _pam_exec(pamh
, __func__
, flags
, argc
, argv
, &options
);
358 * We must check that the program returned a valid code for this
364 case PAM_ACCT_EXPIRED
:
369 case PAM_NEW_AUTHTOK_REQD
:
370 case PAM_PERM_DENIED
:
371 case PAM_SERVICE_ERR
:
373 case PAM_USER_UNKNOWN
:
376 openpam_log(PAM_LOG_ERROR
, "%s returned invalid code %d",
378 ret
= PAM_SERVICE_ERR
;
385 pam_sm_open_session(pam_handle_t
*pamh
, int flags
,
386 int argc
, const char *argv
[])
389 struct pe_opts options
;
391 ret
= parse_options(__func__
, &argc
, &argv
, &options
);
393 return (PAM_SERVICE_ERR
);
395 ret
= _pam_exec(pamh
, __func__
, flags
, argc
, argv
, &options
);
398 * We must check that the program returned a valid code for this
407 case PAM_PERM_DENIED
:
408 case PAM_SERVICE_ERR
:
409 case PAM_SESSION_ERR
:
413 openpam_log(PAM_LOG_ERROR
, "%s returned invalid code %d",
415 ret
= PAM_SERVICE_ERR
;
422 pam_sm_close_session(pam_handle_t
*pamh
, int flags
,
423 int argc
, const char *argv
[])
426 struct pe_opts options
;
428 ret
= parse_options(__func__
, &argc
, &argv
, &options
);
430 return (PAM_SERVICE_ERR
);
432 ret
= _pam_exec(pamh
, __func__
, flags
, argc
, argv
, &options
);
435 * We must check that the program returned a valid code for this
444 case PAM_PERM_DENIED
:
445 case PAM_SERVICE_ERR
:
446 case PAM_SESSION_ERR
:
450 openpam_log(PAM_LOG_ERROR
, "%s returned invalid code %d",
452 ret
= PAM_SERVICE_ERR
;
459 pam_sm_chauthtok(pam_handle_t
*pamh
, int flags
,
460 int argc
, const char *argv
[])
463 struct pe_opts options
;
465 ret
= parse_options(__func__
, &argc
, &argv
, &options
);
467 return (PAM_SERVICE_ERR
);
469 ret
= _pam_exec(pamh
, __func__
, flags
, argc
, argv
, &options
);
472 * We must check that the program returned a valid code for this
478 case PAM_AUTHTOK_DISABLE_AGING
:
479 case PAM_AUTHTOK_ERR
:
480 case PAM_AUTHTOK_LOCK_BUSY
:
481 case PAM_AUTHTOK_RECOVERY_ERR
:
485 case PAM_PERM_DENIED
:
486 case PAM_SERVICE_ERR
:
491 openpam_log(PAM_LOG_ERROR
, "%s returned invalid code %d",
493 ret
= PAM_SERVICE_ERR
;
499 PAM_MODULE_ENTRY("pam_exec");