197182Sdes/*-
2110448Sdes * Copyright (c) 2001,2003 Networks Associates Technology, Inc.
397182Sdes * All rights reserved.
497182Sdes *
597182Sdes * This software was developed for the FreeBSD Project by ThinkSec AS and
697182Sdes * NAI Labs, the Security Research Division of Network Associates, Inc.
797182Sdes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
897182Sdes * DARPA CHATS research program.
997182Sdes *
1097182Sdes * Redistribution and use in source and binary forms, with or without
1197182Sdes * modification, are permitted provided that the following conditions
1297182Sdes * are met:
1397182Sdes * 1. Redistributions of source code must retain the above copyright
1497182Sdes *    notice, this list of conditions and the following disclaimer.
1597182Sdes * 2. Redistributions in binary form must reproduce the above copyright
1697182Sdes *    notice, this list of conditions and the following disclaimer in the
1797182Sdes *    documentation and/or other materials provided with the distribution.
1897182Sdes * 3. The name of the author may not be used to endorse or promote
1997182Sdes *    products derived from this software without specific prior written
2097182Sdes *    permission.
2197182Sdes *
2297182Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2397182Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2497182Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2597182Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2697182Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2797182Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2897182Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2997182Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3097182Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3197182Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3297182Sdes * SUCH DAMAGE.
3397182Sdes */
3497182Sdes
3597182Sdes#include <sys/cdefs.h>
3697182Sdes__FBSDID("$FreeBSD$");
3797182Sdes
3897182Sdes#include <sys/types.h>
3997182Sdes#include <sys/wait.h>
4097182Sdes
4197182Sdes#include <errno.h>
42194188Sed#include <stdio.h>
4397182Sdes#include <stdlib.h>
4497182Sdes#include <string.h>
4597182Sdes#include <unistd.h>
4697182Sdes
4797182Sdes#include <security/pam_appl.h>
4897182Sdes#include <security/pam_modules.h>
4997182Sdes#include <security/openpam.h>
5097182Sdes
51141102Sdes#define ENV_ITEM(n) { (n), #n }
52141102Sdesstatic struct {
53141102Sdes	int item;
54141102Sdes	const char *name;
55141102Sdes} env_items[] = {
56141102Sdes	ENV_ITEM(PAM_SERVICE),
57141102Sdes	ENV_ITEM(PAM_USER),
58141102Sdes	ENV_ITEM(PAM_TTY),
59141102Sdes	ENV_ITEM(PAM_RHOST),
60141102Sdes	ENV_ITEM(PAM_RUSER),
61141102Sdes};
62141102Sdes
63234184Sdumbbellstruct pe_opts {
64234184Sdumbbell	int	return_prog_exit_status;
65234184Sdumbbell};
66234184Sdumbbell
67233507Sdumbbell#define	PAM_RV_COUNT 24
68233507Sdumbbell
6997182Sdesstatic int
70234184Sdumbbellparse_options(const char *func, int *argc, const char **argv[],
71234184Sdumbbell    struct pe_opts *options)
7297182Sdes{
73234184Sdumbbell	int i;
7497182Sdes
7597182Sdes	/*
76233507Sdumbbell	 * Parse options:
77233507Sdumbbell	 *   return_prog_exit_status:
78233507Sdumbbell	 *     use the program exit status as the return code of pam_exec
79233507Sdumbbell	 *   --:
80233507Sdumbbell	 *     stop options parsing; what follows is the command to execute
81141102Sdes	 */
82234184Sdumbbell	options->return_prog_exit_status = 0;
83234184Sdumbbell
84234184Sdumbbell	for (i = 0; i < *argc; ++i) {
85234184Sdumbbell		if (strcmp((*argv)[i], "return_prog_exit_status") == 0) {
86233507Sdumbbell			openpam_log(PAM_LOG_DEBUG,
87233507Sdumbbell			    "%s: Option \"return_prog_exit_status\" enabled",
88233507Sdumbbell			    func);
89234184Sdumbbell			options->return_prog_exit_status = 1;
90233507Sdumbbell		} else {
91234184Sdumbbell			if (strcmp((*argv)[i], "--") == 0) {
92234184Sdumbbell				(*argc)--;
93234184Sdumbbell				(*argv)++;
94233507Sdumbbell			}
95233507Sdumbbell
96233507Sdumbbell			break;
97233507Sdumbbell		}
98233507Sdumbbell	}
99233507Sdumbbell
100234184Sdumbbell	(*argc) -= i;
101234184Sdumbbell	(*argv) += i;
102233507Sdumbbell
103234184Sdumbbell	return (0);
104234184Sdumbbell}
105234184Sdumbbell
106234184Sdumbbellstatic int
107234184Sdumbbell_pam_exec(pam_handle_t *pamh __unused,
108234184Sdumbbell    const char *func, int flags __unused, int argc, const char *argv[],
109234184Sdumbbell    struct pe_opts *options)
110234184Sdumbbell{
111234184Sdumbbell	int envlen, i, nitems, pam_err, status;
112234184Sdumbbell	int nitems_rv;
113234184Sdumbbell	char **envlist, **tmp, *envstr;
114234184Sdumbbell	volatile int childerr;
115234184Sdumbbell	pid_t pid;
116234184Sdumbbell
117234184Sdumbbell	/*
118234184Sdumbbell	 * XXX For additional credit, divert child's stdin/stdout/stderr
119234184Sdumbbell	 * to the conversation function.
120234184Sdumbbell	 */
121234184Sdumbbell
122233507Sdumbbell	/* Check there's a program name left after parsing options. */
123233507Sdumbbell	if (argc < 1) {
124233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s: No program specified: aborting",
125233507Sdumbbell		    func);
126233507Sdumbbell		return (PAM_SERVICE_ERR);
127233507Sdumbbell	}
128233507Sdumbbell
129233507Sdumbbell	/*
130233507Sdumbbell	 * Set up the child's environment list. It consists of the PAM
131233507Sdumbbell	 * environment, plus a few hand-picked PAM items, the pam_sm_*
132233507Sdumbbell	 * function name calling it and, if return_prog_exit_status is
133233507Sdumbbell	 * set, the valid return codes numerical values.
134233507Sdumbbell	 */
135110446Sdes	envlist = pam_getenvlist(pamh);
136141102Sdes	for (envlen = 0; envlist[envlen] != NULL; ++envlen)
137141102Sdes		/* nothing */ ;
138141102Sdes	nitems = sizeof(env_items) / sizeof(*env_items);
139233507Sdumbbell	/* Count PAM return values put in the environment. */
140234184Sdumbbell	nitems_rv = options->return_prog_exit_status ? PAM_RV_COUNT : 0;
141233507Sdumbbell	tmp = realloc(envlist, (envlen + nitems + 1 + nitems_rv + 1) *
142233507Sdumbbell	    sizeof(*envlist));
143141102Sdes	if (tmp == NULL) {
144141102Sdes		openpam_free_envlist(envlist);
145141102Sdes		return (PAM_BUF_ERR);
146141102Sdes	}
147141102Sdes	envlist = tmp;
148141102Sdes	for (i = 0; i < nitems; ++i) {
149141102Sdes		const void *item;
150141102Sdes
151141102Sdes		pam_err = pam_get_item(pamh, env_items[i].item, &item);
152141102Sdes		if (pam_err != PAM_SUCCESS || item == NULL)
153141102Sdes			continue;
154234184Sdumbbell		asprintf(&envstr, "%s=%s", env_items[i].name,
155234184Sdumbbell		    (const char *)item);
156141102Sdes		if (envstr == NULL) {
157141102Sdes			openpam_free_envlist(envlist);
158141102Sdes			return (PAM_BUF_ERR);
159141102Sdes		}
160141102Sdes		envlist[envlen++] = envstr;
161141102Sdes		envlist[envlen] = NULL;
162141102Sdes	}
163141102Sdes
164233507Sdumbbell	/* Add the pam_sm_* function name to the environment. */
165233507Sdumbbell	asprintf(&envstr, "PAM_SM_FUNC=%s", func);
166233507Sdumbbell	if (envstr == NULL) {
167233507Sdumbbell		openpam_free_envlist(envlist);
168233507Sdumbbell		return (PAM_BUF_ERR);
169233507Sdumbbell	}
170233507Sdumbbell	envlist[envlen++] = envstr;
171233507Sdumbbell
172233507Sdumbbell	/* Add the PAM return values to the environment. */
173234184Sdumbbell	if (options->return_prog_exit_status) {
174233507Sdumbbell#define	ADD_PAM_RV_TO_ENV(name)						\
175233507Sdumbbell		asprintf(&envstr, #name "=%d", name);			\
176233507Sdumbbell		if (envstr == NULL) {					\
177233507Sdumbbell			openpam_free_envlist(envlist);			\
178233507Sdumbbell			return (PAM_BUF_ERR);				\
179233507Sdumbbell		}							\
180233507Sdumbbell		envlist[envlen++] = envstr
181233507Sdumbbell		/*
182233507Sdumbbell		 * CAUTION: When adding/removing an item in the list
183233507Sdumbbell		 * below, be sure to update the value of PAM_RV_COUNT.
184233507Sdumbbell		 */
185233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_ABORT);
186233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_ACCT_EXPIRED);
187233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_AUTHINFO_UNAVAIL);
188233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_DISABLE_AGING);
189233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_ERR);
190233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_LOCK_BUSY);
191233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_AUTHTOK_RECOVERY_ERR);
192233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_AUTH_ERR);
193233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_BUF_ERR);
194233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_CONV_ERR);
195233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_CRED_ERR);
196233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_CRED_EXPIRED);
197233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_CRED_INSUFFICIENT);
198233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_CRED_UNAVAIL);
199233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_IGNORE);
200233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_MAXTRIES);
201233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_NEW_AUTHTOK_REQD);
202233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_PERM_DENIED);
203233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_SERVICE_ERR);
204233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_SESSION_ERR);
205233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_SUCCESS);
206233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_SYSTEM_ERR);
207233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_TRY_AGAIN);
208233507Sdumbbell		ADD_PAM_RV_TO_ENV(PAM_USER_UNKNOWN);
209233507Sdumbbell	}
210233507Sdumbbell
211233507Sdumbbell	envlist[envlen] = NULL;
212233507Sdumbbell
213141102Sdes	/*
214141102Sdes	 * Fork and run the command.  By using vfork() instead of fork(),
215141102Sdes	 * we can distinguish between an execve() failure and a non-zero
216233507Sdumbbell	 * exit status from the command.
217141102Sdes	 */
21897182Sdes	childerr = 0;
21997182Sdes	if ((pid = vfork()) == 0) {
220141102Sdes		execve(argv[0], (char * const *)argv, (char * const *)envlist);
22197182Sdes		childerr = errno;
22297182Sdes		_exit(1);
223110446Sdes	}
224141102Sdes	openpam_free_envlist(envlist);
225110446Sdes	if (pid == -1) {
226233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s: vfork(): %m", func);
22797182Sdes		return (PAM_SYSTEM_ERR);
22897182Sdes	}
229233507Sdumbbell	while (waitpid(pid, &status, 0) == -1) {
230233507Sdumbbell		if (errno == EINTR)
231233507Sdumbbell			continue;
232233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s: waitpid(): %m", func);
23397182Sdes		return (PAM_SYSTEM_ERR);
23497182Sdes	}
23597182Sdes	if (childerr != 0) {
236233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s: execve(): %m", func);
23797182Sdes		return (PAM_SYSTEM_ERR);
23897182Sdes	}
23997182Sdes	if (WIFSIGNALED(status)) {
240233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s: %s caught signal %d%s",
241233507Sdumbbell		    func, argv[0], WTERMSIG(status),
24297182Sdes		    WCOREDUMP(status) ? " (core dumped)" : "");
243233507Sdumbbell		return (PAM_SERVICE_ERR);
24497182Sdes	}
24597182Sdes	if (!WIFEXITED(status)) {
246233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s: unknown status 0x%x",
247233507Sdumbbell		    func, status);
248233507Sdumbbell		return (PAM_SERVICE_ERR);
24997182Sdes	}
250233507Sdumbbell
251234184Sdumbbell	if (options->return_prog_exit_status) {
252233507Sdumbbell		openpam_log(PAM_LOG_DEBUG,
253233507Sdumbbell		    "%s: Use program exit status as return value: %d",
254233507Sdumbbell		    func, WEXITSTATUS(status));
255233507Sdumbbell		return (WEXITSTATUS(status));
256233507Sdumbbell	} else {
257233507Sdumbbell		return (WEXITSTATUS(status) == 0 ?
258233507Sdumbbell		    PAM_SUCCESS : PAM_PERM_DENIED);
25997182Sdes	}
26097182Sdes}
26197182Sdes
26297182SdesPAM_EXTERN int
26397182Sdespam_sm_authenticate(pam_handle_t *pamh, int flags,
26497182Sdes    int argc, const char *argv[])
26597182Sdes{
266233507Sdumbbell	int ret;
267234184Sdumbbell	struct pe_opts options;
26897182Sdes
269234184Sdumbbell	ret = parse_options(__func__, &argc, &argv, &options);
270234184Sdumbbell	if (ret != 0)
271234184Sdumbbell		return (PAM_SERVICE_ERR);
272233507Sdumbbell
273234184Sdumbbell	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
274234184Sdumbbell
275233507Sdumbbell	/*
276233507Sdumbbell	 * We must check that the program returned a valid code for this
277233507Sdumbbell	 * function.
278233507Sdumbbell	 */
279233507Sdumbbell	switch (ret) {
280233507Sdumbbell	case PAM_SUCCESS:
281233507Sdumbbell	case PAM_ABORT:
282233507Sdumbbell	case PAM_AUTHINFO_UNAVAIL:
283233507Sdumbbell	case PAM_AUTH_ERR:
284233507Sdumbbell	case PAM_BUF_ERR:
285233507Sdumbbell	case PAM_CONV_ERR:
286233507Sdumbbell	case PAM_CRED_INSUFFICIENT:
287233507Sdumbbell	case PAM_IGNORE:
288233507Sdumbbell	case PAM_MAXTRIES:
289233507Sdumbbell	case PAM_PERM_DENIED:
290233507Sdumbbell	case PAM_SERVICE_ERR:
291233507Sdumbbell	case PAM_SYSTEM_ERR:
292233507Sdumbbell	case PAM_USER_UNKNOWN:
293233507Sdumbbell		break;
294233507Sdumbbell	default:
295233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
296233507Sdumbbell		    argv[0], ret);
297233507Sdumbbell		ret = PAM_SERVICE_ERR;
298233507Sdumbbell	}
299233507Sdumbbell
300233507Sdumbbell	return (ret);
30197182Sdes}
30297182Sdes
30397182SdesPAM_EXTERN int
30497182Sdespam_sm_setcred(pam_handle_t *pamh, int flags,
30597182Sdes    int argc, const char *argv[])
30697182Sdes{
307233507Sdumbbell	int ret;
308234184Sdumbbell	struct pe_opts options;
30997182Sdes
310234184Sdumbbell	ret = parse_options(__func__, &argc, &argv, &options);
311234184Sdumbbell	if (ret != 0)
312234184Sdumbbell		return (PAM_SERVICE_ERR);
313233507Sdumbbell
314234184Sdumbbell	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
315234184Sdumbbell
316233507Sdumbbell	/*
317233507Sdumbbell	 * We must check that the program returned a valid code for this
318233507Sdumbbell	 * function.
319233507Sdumbbell	 */
320233507Sdumbbell	switch (ret) {
321233507Sdumbbell	case PAM_SUCCESS:
322233507Sdumbbell	case PAM_ABORT:
323233507Sdumbbell	case PAM_BUF_ERR:
324233507Sdumbbell	case PAM_CONV_ERR:
325233507Sdumbbell	case PAM_CRED_ERR:
326233507Sdumbbell	case PAM_CRED_EXPIRED:
327233507Sdumbbell	case PAM_CRED_UNAVAIL:
328233507Sdumbbell	case PAM_IGNORE:
329233507Sdumbbell	case PAM_PERM_DENIED:
330233507Sdumbbell	case PAM_SERVICE_ERR:
331233507Sdumbbell	case PAM_SYSTEM_ERR:
332233507Sdumbbell	case PAM_USER_UNKNOWN:
333233507Sdumbbell		break;
334233507Sdumbbell	default:
335233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
336233507Sdumbbell		    argv[0], ret);
337233507Sdumbbell		ret = PAM_SERVICE_ERR;
338233507Sdumbbell	}
339233507Sdumbbell
340233507Sdumbbell	return (ret);
34197182Sdes}
34297182Sdes
34397182SdesPAM_EXTERN int
34497182Sdespam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
34597182Sdes    int argc, const char *argv[])
34697182Sdes{
347233507Sdumbbell	int ret;
348234184Sdumbbell	struct pe_opts options;
34997182Sdes
350234184Sdumbbell	ret = parse_options(__func__, &argc, &argv, &options);
351234184Sdumbbell	if (ret != 0)
352234184Sdumbbell		return (PAM_SERVICE_ERR);
353233507Sdumbbell
354234184Sdumbbell	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
355234184Sdumbbell
356233507Sdumbbell	/*
357233507Sdumbbell	 * We must check that the program returned a valid code for this
358233507Sdumbbell	 * function.
359233507Sdumbbell	 */
360233507Sdumbbell	switch (ret) {
361233507Sdumbbell	case PAM_SUCCESS:
362233507Sdumbbell	case PAM_ABORT:
363233507Sdumbbell	case PAM_ACCT_EXPIRED:
364233507Sdumbbell	case PAM_AUTH_ERR:
365233507Sdumbbell	case PAM_BUF_ERR:
366233507Sdumbbell	case PAM_CONV_ERR:
367233507Sdumbbell	case PAM_IGNORE:
368233507Sdumbbell	case PAM_NEW_AUTHTOK_REQD:
369233507Sdumbbell	case PAM_PERM_DENIED:
370233507Sdumbbell	case PAM_SERVICE_ERR:
371233507Sdumbbell	case PAM_SYSTEM_ERR:
372233507Sdumbbell	case PAM_USER_UNKNOWN:
373233507Sdumbbell		break;
374233507Sdumbbell	default:
375233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
376233507Sdumbbell		    argv[0], ret);
377233507Sdumbbell		ret = PAM_SERVICE_ERR;
378233507Sdumbbell	}
379233507Sdumbbell
380233507Sdumbbell	return (ret);
38197182Sdes}
38297182Sdes
38397182SdesPAM_EXTERN int
38497182Sdespam_sm_open_session(pam_handle_t *pamh, int flags,
38597182Sdes    int argc, const char *argv[])
38697182Sdes{
387233507Sdumbbell	int ret;
388234184Sdumbbell	struct pe_opts options;
38997182Sdes
390234184Sdumbbell	ret = parse_options(__func__, &argc, &argv, &options);
391234184Sdumbbell	if (ret != 0)
392234184Sdumbbell		return (PAM_SERVICE_ERR);
393233507Sdumbbell
394234184Sdumbbell	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
395234184Sdumbbell
396233507Sdumbbell	/*
397233507Sdumbbell	 * We must check that the program returned a valid code for this
398233507Sdumbbell	 * function.
399233507Sdumbbell	 */
400233507Sdumbbell	switch (ret) {
401233507Sdumbbell	case PAM_SUCCESS:
402233507Sdumbbell	case PAM_ABORT:
403233507Sdumbbell	case PAM_BUF_ERR:
404233507Sdumbbell	case PAM_CONV_ERR:
405233507Sdumbbell	case PAM_IGNORE:
406233507Sdumbbell	case PAM_PERM_DENIED:
407233507Sdumbbell	case PAM_SERVICE_ERR:
408233507Sdumbbell	case PAM_SESSION_ERR:
409233507Sdumbbell	case PAM_SYSTEM_ERR:
410233507Sdumbbell		break;
411233507Sdumbbell	default:
412233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
413233507Sdumbbell		    argv[0], ret);
414233507Sdumbbell		ret = PAM_SERVICE_ERR;
415233507Sdumbbell	}
416233507Sdumbbell
417233507Sdumbbell	return (ret);
41897182Sdes}
41997182Sdes
42097182SdesPAM_EXTERN int
42197182Sdespam_sm_close_session(pam_handle_t *pamh, int flags,
42297182Sdes    int argc, const char *argv[])
42397182Sdes{
424233507Sdumbbell	int ret;
425234184Sdumbbell	struct pe_opts options;
42697182Sdes
427234184Sdumbbell	ret = parse_options(__func__, &argc, &argv, &options);
428234184Sdumbbell	if (ret != 0)
429234184Sdumbbell		return (PAM_SERVICE_ERR);
430233507Sdumbbell
431234184Sdumbbell	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
432234184Sdumbbell
433233507Sdumbbell	/*
434233507Sdumbbell	 * We must check that the program returned a valid code for this
435233507Sdumbbell	 * function.
436233507Sdumbbell	 */
437233507Sdumbbell	switch (ret) {
438233507Sdumbbell	case PAM_SUCCESS:
439233507Sdumbbell	case PAM_ABORT:
440233507Sdumbbell	case PAM_BUF_ERR:
441233507Sdumbbell	case PAM_CONV_ERR:
442233507Sdumbbell	case PAM_IGNORE:
443233507Sdumbbell	case PAM_PERM_DENIED:
444233507Sdumbbell	case PAM_SERVICE_ERR:
445233507Sdumbbell	case PAM_SESSION_ERR:
446233507Sdumbbell	case PAM_SYSTEM_ERR:
447233507Sdumbbell		break;
448233507Sdumbbell	default:
449233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
450233507Sdumbbell		    argv[0], ret);
451233507Sdumbbell		ret = PAM_SERVICE_ERR;
452233507Sdumbbell	}
453233507Sdumbbell
454233507Sdumbbell	return (ret);
45597182Sdes}
45697182Sdes
45797182SdesPAM_EXTERN int
45897182Sdespam_sm_chauthtok(pam_handle_t *pamh, int flags,
45997182Sdes    int argc, const char *argv[])
46097182Sdes{
461233507Sdumbbell	int ret;
462234184Sdumbbell	struct pe_opts options;
46397182Sdes
464234184Sdumbbell	ret = parse_options(__func__, &argc, &argv, &options);
465234184Sdumbbell	if (ret != 0)
466234184Sdumbbell		return (PAM_SERVICE_ERR);
467233507Sdumbbell
468234184Sdumbbell	ret = _pam_exec(pamh, __func__, flags, argc, argv, &options);
469234184Sdumbbell
470233507Sdumbbell	/*
471233507Sdumbbell	 * We must check that the program returned a valid code for this
472233507Sdumbbell	 * function.
473233507Sdumbbell	 */
474233507Sdumbbell	switch (ret) {
475233507Sdumbbell	case PAM_SUCCESS:
476233507Sdumbbell	case PAM_ABORT:
477233507Sdumbbell	case PAM_AUTHTOK_DISABLE_AGING:
478233507Sdumbbell	case PAM_AUTHTOK_ERR:
479233507Sdumbbell	case PAM_AUTHTOK_LOCK_BUSY:
480233507Sdumbbell	case PAM_AUTHTOK_RECOVERY_ERR:
481233507Sdumbbell	case PAM_BUF_ERR:
482233507Sdumbbell	case PAM_CONV_ERR:
483233507Sdumbbell	case PAM_IGNORE:
484233507Sdumbbell	case PAM_PERM_DENIED:
485233507Sdumbbell	case PAM_SERVICE_ERR:
486233507Sdumbbell	case PAM_SYSTEM_ERR:
487233507Sdumbbell	case PAM_TRY_AGAIN:
488233507Sdumbbell		break;
489233507Sdumbbell	default:
490233507Sdumbbell		openpam_log(PAM_LOG_ERROR, "%s returned invalid code %d",
491233507Sdumbbell		    argv[0], ret);
492233507Sdumbbell		ret = PAM_SERVICE_ERR;
493233507Sdumbbell	}
494233507Sdumbbell
495233507Sdumbbell	return (ret);
49697182Sdes}
49797182Sdes
49897182SdesPAM_MODULE_ENTRY("pam_exec");
499