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