1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include "ldap_headers.h"
30#include <malloc.h>
31
32/* ******************************************************************** */
33/*									*/
34/* 		Utilities Functions					*/
35/*									*/
36/* ******************************************************************** */
37
38/*
39 * __ldap_to_pamerror():
40 *	converts Native LDAP errors to an equivalent PAM error
41 */
42int
43__ldap_to_pamerror(int ldaperror)
44{
45	switch (ldaperror) {
46		case NS_LDAP_SUCCESS:
47			return (PAM_SUCCESS);
48
49		case NS_LDAP_OP_FAILED:
50			return (PAM_PERM_DENIED);
51
52		case NS_LDAP_MEMORY:
53			return (PAM_BUF_ERR);
54
55		case NS_LDAP_CONFIG:
56			return (PAM_SERVICE_ERR);
57
58		case NS_LDAP_NOTFOUND:
59		case NS_LDAP_INTERNAL:
60		case NS_LDAP_PARTIAL:
61		case NS_LDAP_INVALID_PARAM:
62			return (PAM_SYSTEM_ERR);
63
64		default:
65			return (PAM_SYSTEM_ERR);
66
67	}
68}
69
70/*
71 * authenticate():
72 *	Returns
73 *	  PAM_SUCCESS            if authenticated successfully
74 *	  PAM_NEW_AUTHTOK_REQD   if authenticated but user needs to
75 *                               change password immediately
76 *        PAM_MAXTRIES           if authentication fails due to too
77 *                               many login failures
78 *        PAM_AUTHTOK_EXPIRED    if user password expired
79 *        PAM_PERM_DENIED        if fail to authenticate
80 *        PAM_AUTH_ERR           other errors
81 *
82 *      Also output the second-until-expired data if authenticated
83 *      but the password is about to expire.
84 *	Authentication is checked by calling __ns_ldap_auth.
85 */
86int
87authenticate(ns_cred_t **credpp, char *usrname, char *pwd,
88		int *sec_until_expired)
89{
90	int		result = PAM_AUTH_ERR;
91	int		ldaprc;
92	int		authstried = 0;
93	char		*binddn = NULL;
94	char		**certpath = NULL;
95	ns_auth_t	**app;
96	ns_auth_t	**authpp = NULL;
97	ns_auth_t	*authp = NULL;
98	ns_cred_t	*credp;
99	ns_ldap_error_t	*errorp = NULL;
100
101	if ((credp = (ns_cred_t *)calloc(1, sizeof (ns_cred_t))) == NULL)
102		return (PAM_BUF_ERR);
103
104	/* Fill in the user name and password */
105	if ((usrname == NULL) || (pwd == NULL) || (usrname[0] == '\0') ||
106		(pwd[0] == '\0'))
107		goto out;
108
109	ldaprc = __ns_ldap_uid2dn(usrname, &binddn, NULL, &errorp);
110	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
111		goto out;
112
113	credp->cred.unix_cred.userID = strdup(binddn);
114	credp->cred.unix_cred.passwd = strdup(pwd);
115	if ((credp->cred.unix_cred.userID == NULL) ||
116		(credp->cred.unix_cred.passwd == NULL)) {
117		result = PAM_BUF_ERR;
118		goto out;
119	}
120
121	/* get host certificate path, if one is configured */
122	ldaprc = __ns_ldap_getParam(NS_LDAP_HOST_CERTPATH_P,
123		(void ***)&certpath, &errorp);
124	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
125		goto out;
126	if (certpath && *certpath)
127		credp->hostcertpath = *certpath;
128
129	/* Load the service specific authentication method */
130	ldaprc = __ns_ldap_getServiceAuthMethods("pam_ldap", &authpp, &errorp);
131	if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
132		goto out;
133
134	/*
135	 * if authpp is null, there is no serviceAuthenticationMethod
136	 * try default authenticationMethod
137	 */
138	if (authpp == NULL) {
139		ldaprc = __ns_ldap_getParam(NS_LDAP_AUTH_P, (void ***)&authpp,
140			&errorp);
141		if ((result = __ldap_to_pamerror(ldaprc)) != PAM_SUCCESS)
142			goto out;
143	}
144
145	/*
146	 * if authpp is still null, then can not authenticate, syslog
147	 * error message and return error
148	 */
149	if (authpp == NULL) {
150		syslog(LOG_ERR,
151			"pam_ldap: no authentication method configured");
152		result = PAM_AUTH_ERR;
153		goto out;
154	}
155
156	/*
157	 * Walk the array and try all authentication methods in order except
158	 * for "none".
159	 */
160	for (app = authpp; *app; app++) {
161		authp = *app;
162		/* what about disabling other mechanisms? "tls:sasl/EXTERNAL" */
163		if (authp->type == NS_LDAP_AUTH_NONE)
164			continue;
165		authstried++;
166		credp->auth.type = authp->type;
167		credp->auth.tlstype = authp->tlstype;
168		credp->auth.saslmech = authp->saslmech;
169		credp->auth.saslopt = authp->saslopt;
170		ldaprc = __ns_ldap_auth(credp, 0, &errorp, NULL, NULL);
171
172		/*
173		 * If rc is NS_LDAP_SUCCESS, done. If not,
174		 * check rc and error info to see if
175		 * there's any password management data.
176		 * If yes, set appropriate PAM result code
177		 * and exit.
178		 */
179		if (ldaprc == NS_LDAP_SUCCESS) {
180			/*
181			 * authenticated and no
182			 * password management info, done.
183			 */
184			result = PAM_SUCCESS;
185			goto out;
186		} else if (ldaprc == NS_LDAP_SUCCESS_WITH_INFO) {
187			/*
188			 * authenticated but need to deal with
189			 * password management info
190			 */
191			result = PAM_SUCCESS;
192
193			/*
194			 * clear sec_until_expired just in case
195			 * there's no error info
196			 */
197			if (sec_until_expired)
198				*sec_until_expired = 0;
199
200			if (errorp) {
201				if (errorp->pwd_mgmt.status ==
202					NS_PASSWD_ABOUT_TO_EXPIRE) {
203					/*
204					 * password about to expire;
205					 * retrieve "seconds until expired"
206					 */
207					if (sec_until_expired)
208						*sec_until_expired =
209						errorp->
210						pwd_mgmt.sec_until_expired;
211				} else if (errorp->pwd_mgmt.status ==
212					NS_PASSWD_CHANGE_NEEDED)
213					/*
214					 * indicate that passwd need to change
215					 * right away
216					 */
217					result = PAM_NEW_AUTHTOK_REQD;
218
219				(void) __ns_ldap_freeError(&errorp);
220			}
221			goto out;
222		} else if (ldaprc == NS_LDAP_INTERNAL) {
223
224			if (errorp) {
225				/*
226				 * If error due to password policy, set
227				 * appropriate PAM result code and exit.
228				 */
229				if (errorp->pwd_mgmt.status ==
230					NS_PASSWD_RETRY_EXCEEDED)
231					result = PAM_MAXTRIES;
232				else if (errorp->pwd_mgmt.status ==
233					NS_PASSWD_EXPIRED)
234					result = PAM_AUTHTOK_EXPIRED;
235				else {
236					/*
237					 * If invalid credential,
238					 * return PAM_AUTH_ERR.
239					 */
240					if (errorp->status ==
241						LDAP_INVALID_CREDENTIALS)
242						result = PAM_AUTH_ERR;
243				}
244				(void) __ns_ldap_freeError(&errorp);
245				goto out;
246			}
247		}
248
249		/* done with the error info, clean it up */
250		if (errorp)
251			(void) __ns_ldap_freeError(&errorp);
252	}
253	if (authstried == 0) {
254		syslog(LOG_ERR,
255			"pam_ldap: no legal authentication method configured");
256		result = PAM_AUTH_ERR;
257		goto out;
258	}
259	result = PAM_PERM_DENIED;
260
261out:
262	if (binddn)
263		free(binddn);
264
265	if (credp && (result == PAM_SUCCESS ||
266		result == PAM_NEW_AUTHTOK_REQD))
267		if (credpp)
268			*credpp = credp;
269	else
270		(void) __ns_ldap_freeCred(&credp);
271
272	if (authpp)
273		(void) __ns_ldap_freeParam((void ***)&authpp);
274
275	if (errorp)
276		(void) __ns_ldap_freeError(&errorp);
277
278	return (result);
279}
280