openpam_dispatch.c revision 91100
1193326Sed/*-
2193326Sed * Copyright (c) 2002 Networks Associates Technologies, Inc.
3193326Sed * All rights reserved.
4193326Sed *
5193326Sed * This software was developed for the FreeBSD Project by ThinkSec AS and
6193326Sed * NAI Labs, the Security Research Division of Network Associates, Inc.
7193326Sed * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8193326Sed * DARPA CHATS research program.
9193326Sed *
10193326Sed * Redistribution and use in source and binary forms, with or without
11193326Sed * modification, are permitted provided that the following conditions
12193326Sed * are met:
13193326Sed * 1. Redistributions of source code must retain the above copyright
14193326Sed *    notice, this list of conditions and the following disclaimer.
15193326Sed * 2. Redistributions in binary form must reproduce the above copyright
16193326Sed *    notice, this list of conditions and the following disclaimer in the
17193326Sed *    documentation and/or other materials provided with the distribution.
18193326Sed * 3. The name of the author may not be used to endorse or promote
19193326Sed *    products derived from this software without specific prior written
20202379Srdivacky *    permission.
21224145Sdim *
22218893Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24224145Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25193326Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26193326Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27193326Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28198092Srdivacky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29193326Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30193326Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31193326Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32193326Sed * SUCH DAMAGE.
33193326Sed *
34198092Srdivacky * $Id$
35207619Srdivacky */
36193326Sed
37193326Sed#include <sys/param.h>
38198398Srdivacky
39193326Sed#include <security/pam_appl.h>
40210299Sed
41193326Sed#include "openpam_impl.h"
42193326Sed
43193326Sed#if !defined(OPENPAM_RELAX_CHECKS)
44212904Sdimstatic void _openpam_check_error_code(int, int);
45193326Sed#else
46193326Sed#define _openpam_check_error_code(a, b)
47221345Sdim#endif /* !defined(OPENPAM_RELAX_CHECKS) */
48198092Srdivacky
49193326Sed/*
50218893Sdim * OpenPAM internal
51193326Sed *
52193326Sed * Execute a module chain
53193326Sed */
54193326Sed
55193326Sedint
56193326Sedopenpam_dispatch(pam_handle_t *pamh,
57193326Sed	int primitive,
58193326Sed	int flags)
59193326Sed{
60193326Sed	pam_chain_t *chain;
61193326Sed	int err, fail, r;
62204643Srdivacky
63193326Sed	if (pamh == NULL)
64193326Sed		return (PAM_SYSTEM_ERR);
65193326Sed
66193326Sed	/* prevent recursion */
67193326Sed	if (pamh->current != NULL) {
68224145Sdim		openpam_log(PAM_LOG_ERROR, "indirect recursion");
69193326Sed		return (PAM_ABORT);
70193326Sed	}
71193326Sed
72193326Sed	/* pick a chain */
73193326Sed	switch (primitive) {
74193326Sed	case PAM_SM_AUTHENTICATE:
75208600Srdivacky	case PAM_SM_SETCRED:
76212904Sdim		chain = pamh->chains[PAM_AUTH];
77218893Sdim		break;
78218893Sdim	case PAM_SM_ACCT_MGMT:
79193326Sed		chain = pamh->chains[PAM_ACCOUNT];
80210299Sed		break;
81210299Sed	case PAM_SM_OPEN_SESSION:
82210299Sed	case PAM_SM_CLOSE_SESSION:
83210299Sed		chain = pamh->chains[PAM_SESSION];
84210299Sed		break;
85210299Sed	case PAM_SM_CHAUTHTOK:
86210299Sed		chain = pamh->chains[PAM_PASSWORD];
87212904Sdim		break;
88212904Sdim	default:
89212904Sdim		return (PAM_SYSTEM_ERR);
90212904Sdim	}
91210299Sed
92212904Sdim	/* execute */
93210299Sed	for (err = fail = 0; chain != NULL; chain = chain->next) {
94210299Sed		openpam_log(PAM_LOG_DEBUG, "calling %s() in %s",
95210299Sed		    _pam_sm_func_name[primitive], chain->module->path);
96210299Sed		if (chain->module->func[primitive] == NULL) {
97210299Sed			openpam_log(PAM_LOG_ERROR, "%s: no %s()",
98212904Sdim			    chain->module->path, _pam_sm_func_name[primitive]);
99212904Sdim			continue;
100212904Sdim		} else {
101212904Sdim			pamh->current = chain;
102212904Sdim			r = (chain->module->func[primitive])(pamh, flags,
103210299Sed			    chain->optc, (const char **)chain->optv);
104210299Sed			pamh->current = NULL;
105218893Sdim			openpam_log(PAM_LOG_DEBUG, "%s: %s(): %s",
106218893Sdim			    chain->module->path, _pam_sm_func_name[primitive],
107218893Sdim			    pam_strerror(pamh, r));
108218893Sdim		}
109218893Sdim
110218893Sdim		if (r == PAM_IGNORE)
111218893Sdim			continue;
112218893Sdim		if (r == PAM_SUCCESS) {
113218893Sdim			/*
114218893Sdim			 * For pam_setcred(), treat "sufficient" as
115218893Sdim			 * "optional".
116218893Sdim			 *
117218893Sdim			 * Note that Solaris libpam does not terminate
118218893Sdim			 * the chain here if a required module has
119218893Sdim			 * previously failed.  I'm not sure why.
120218893Sdim			 */
121218893Sdim			if (chain->flag == PAM_SUFFICIENT &&
122218893Sdim			    primitive != PAM_SM_SETCRED)
123218893Sdim				break;
124218893Sdim			continue;
125218893Sdim		}
126218893Sdim
127212904Sdim		_openpam_check_error_code(primitive, r);
128212904Sdim
129212904Sdim		/*
130212904Sdim		 * Record the return code from the first module to
131210299Sed		 * fail.  If a required module fails, record the
132212904Sdim		 * return code from the first required module to fail.
133212904Sdim		 */
134212904Sdim		if (err == 0)
135212904Sdim			err = r;
136212904Sdim		if (chain->flag == PAM_REQUIRED && !fail) {
137212904Sdim			openpam_log(PAM_LOG_DEBUG, "required module failed");
138210299Sed			fail = 1;
139210299Sed			err = r;
140210299Sed		}
141210299Sed
142210299Sed		/*
143210299Sed		 * If a requisite module fails, terminate the chain
144210299Sed		 * immediately.
145210299Sed		 */
146210299Sed		if (chain->flag == PAM_REQUISITE) {
147210299Sed			openpam_log(PAM_LOG_DEBUG, "requisite module failed");
148210299Sed			fail = 1;
149210299Sed			break;
150210299Sed		}
151210299Sed	}
152210299Sed
153210299Sed	if (!fail)
154210299Sed		err = PAM_SUCCESS;
155210299Sed	openpam_log(PAM_LOG_DEBUG, "returning: %s", pam_strerror(pamh, err));
156210299Sed	return (err);
157210299Sed}
158212904Sdim
159212904Sdim#if !defined(OPENPAM_RELAX_CHECKS)
160212904Sdimstatic void
161212904Sdim_openpam_check_error_code(int primitive, int r)
162212904Sdim{
163212904Sdim	/* common error codes */
164212904Sdim	if (r == PAM_SUCCESS ||
165212904Sdim	    r == PAM_SERVICE_ERR ||
166212904Sdim	    r == PAM_BUF_ERR ||
167212904Sdim	    r == PAM_CONV_ERR ||
168212904Sdim	    r == PAM_PERM_DENIED ||
169210299Sed	    r == PAM_ABORT)
170210299Sed		return;
171210299Sed
172210299Sed	/* specific error codes */
173210299Sed	switch (primitive) {
174210299Sed	case PAM_SM_AUTHENTICATE:
175210299Sed		if (r == PAM_AUTH_ERR ||
176210299Sed		    r == PAM_CRED_INSUFFICIENT ||
177212904Sdim		    r == PAM_AUTHINFO_UNAVAIL ||
178212904Sdim		    r == PAM_USER_UNKNOWN ||
179212904Sdim		    r == PAM_MAXTRIES)
180212904Sdim			return;
181210299Sed		break;
182212904Sdim	case PAM_SM_SETCRED:
183210299Sed		if (r == PAM_CRED_UNAVAIL ||
184212904Sdim		    r == PAM_CRED_EXPIRED ||
185224145Sdim		    r == PAM_USER_UNKNOWN ||
186224145Sdim		    r == PAM_CRED_ERR)
187210299Sed			return;
188224145Sdim		break;
189224145Sdim	case PAM_SM_ACCT_MGMT:
190224145Sdim		if (r == PAM_USER_UNKNOWN ||
191224145Sdim		    r == PAM_AUTH_ERR ||
192224145Sdim		    r == PAM_NEW_AUTHTOK_REQD ||
193224145Sdim		    r == PAM_ACCT_EXPIRED)
194224145Sdim			return;
195224145Sdim		break;
196210299Sed	case PAM_SM_OPEN_SESSION:
197224145Sdim	case PAM_SM_CLOSE_SESSION:
198224145Sdim		if (r == PAM_SESSION_ERR)
199224145Sdim			return;
200224145Sdim		break;
201224145Sdim	case PAM_SM_CHAUTHTOK:
202224145Sdim		if (r == PAM_PERM_DENIED ||
203224145Sdim		    r == PAM_AUTHTOK_ERR ||
204224145Sdim		    r == PAM_AUTHTOK_RECOVERY_ERR ||
205224145Sdim		    r == PAM_AUTHTOK_LOCK_BUSY ||
206224145Sdim		    r == PAM_AUTHTOK_DISABLE_AGING)
207224145Sdim			return;
208224145Sdim		break;
209224145Sdim	}
210224145Sdim
211224145Sdim	openpam_log(PAM_LOG_ERROR, "%s(): unexpected return value %d",
212224145Sdim	    _pam_sm_func_name[primitive], r);
213224145Sdim}
214224145Sdim#endif /* !defined(OPENPAM_RELAX_CHECKS) */
215224145Sdim
216224145Sdim/*
217224145Sdim * NODOC
218224145Sdim *
219210299Sed * Error codes:
220210299Sed */
221210299Sed