xref: /illumos-gate/usr/src/lib/pam_modules/ldap/ldap_utils.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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  */
42 int
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  */
86 int
87 authenticate(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 
261 out:
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