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