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 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 *
26 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
27 */
28
29 #include "ldap_headers.h"
30
31 /*ARGSUSED*/
32 static void
ldap_cleanup(pam_handle_t * pamh,void * data,int pam_status)33 ldap_cleanup(
34 pam_handle_t *pamh,
35 void *data,
36 int pam_status)
37 {
38 free((ldap_authtok_data *)data);
39 }
40
41 /*
42 * warn_user_passwd_will_expire - warn the user when the password will
43 * expire.
44 */
45
46 static void
warn_user_passwd_will_expire(pam_handle_t * pamh,int sec_until_expired)47 warn_user_passwd_will_expire(
48 pam_handle_t *pamh,
49 int sec_until_expired)
50 {
51 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
52 int days = 0, hours = 0;
53 int seconds_d = 0, seconds_h = 0;
54
55 days = sec_until_expired / 86400;
56 seconds_d = sec_until_expired % 86400;
57 hours = (days * 24) + seconds_d / 3600;
58 seconds_h = seconds_d % 3600;
59
60 if (sec_until_expired <= (86400 * 2)) {
61 if (seconds_d <= 3600 && days == 0)
62 (void) snprintf(messages[0], sizeof (messages[0]),
63 dgettext(TEXT_DOMAIN,
64 "Your password will expire within one hour."));
65 else
66 (void) snprintf(messages[0], sizeof (messages[0]),
67 dgettext(TEXT_DOMAIN,
68 "Your password will expire in %d hours."),
69 (seconds_h == 0) ? hours : hours + 1);
70 } else {
71 (void) snprintf(messages[0], sizeof (messages[0]),
72 dgettext(TEXT_DOMAIN,
73 "Your password will expire in %d days."),
74 (seconds_d == 0) ? days : days + 1);
75 }
76
77 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
78 }
79
80 /*
81 * display_acct_unlock_time - Display the time left for the account to
82 * get auto unlocked after the maximum login failures has reached.
83 */
84 static void
display_acct_unlock_time(pam_handle_t * pamh,int sec_b4_unlock)85 display_acct_unlock_time(pam_handle_t *pamh, int sec_b4_unlock)
86 {
87 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
88 int days = 0, hours = 0;
89 int seconds_d = 0, seconds_h = 0;
90
91 /* Account is locked forever */
92 if (sec_b4_unlock == -1) {
93 (void) snprintf(messages[0], sizeof (messages[0]),
94 dgettext(TEXT_DOMAIN,
95 "Your account is locked, please contact administrator."));
96 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1,
97 messages, NULL);
98 return;
99 }
100
101 days = sec_b4_unlock / 86400;
102 seconds_d = sec_b4_unlock % 86400;
103 hours = (days * 24) + seconds_d / 3600;
104 seconds_h = seconds_d % 3600;
105
106 if (sec_b4_unlock <= (86400 * 2)) {
107 if (seconds_d <= 3600 && days == 0)
108 (void) snprintf(messages[0], sizeof (messages[0]),
109 dgettext(TEXT_DOMAIN,
110 "Your account is locked and will be unlocked"
111 " within one hour."));
112 else
113 (void) snprintf(messages[0], sizeof (messages[0]),
114 dgettext(TEXT_DOMAIN,
115 "Your account is locked and will be unlocked"
116 " in %d hours."),
117 (seconds_h == 0) ? hours : hours + 1);
118 } else {
119 (void) snprintf(messages[0], sizeof (messages[0]),
120 dgettext(TEXT_DOMAIN,
121 "Your account is locked and will be unlocked"
122 " in %d days."),
123 (seconds_d == 0) ? days : days + 1);
124 }
125
126 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
127 }
128
129 /*
130 * warn_user_passwd_expired - warn the user that the password has expired
131 */
132 static void
warn_user_passwd_expired(pam_handle_t * pamh,int grace)133 warn_user_passwd_expired(pam_handle_t *pamh, int grace)
134 {
135 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
136
137 if (grace)
138 (void) snprintf(messages[0], sizeof (messages[0]),
139 dgettext(TEXT_DOMAIN,
140 "Your password has expired. "
141 "Number of grace logins allowed are %d."),
142 grace);
143 else
144 (void) snprintf(messages[0], sizeof (messages[0]),
145 dgettext(TEXT_DOMAIN,
146 "Your password has expired."));
147
148 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
149 }
150
151 /*
152 * display_passwd_reset_msg - tell user that password has been reset by
153 * administrator
154 */
155 static void
display_passwd_reset_msg(pam_handle_t * pamh)156 display_passwd_reset_msg(pam_handle_t *pamh)
157 {
158 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
159
160 (void) snprintf(messages[0], sizeof (messages[0]),
161 dgettext(TEXT_DOMAIN,
162 "Your password has been reset by administrator."));
163
164 (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
165 }
166
167 /*
168 * Retreives account management related attributes for the user using
169 * default binding and does local account checks .
170 *
171 * Return Value: PAM_SUCCESS - If account is valid, seconds param will have
172 * seconds left for password to expire
173 * PAM_ACCT_EXPIRED - If account is inactive
174 * PAM_NEW_AUTHTOK_REQD - Password is reset by admin
175 * PAM_AUTHTOK_EXPIRED - User password has expired, grace
176 * param will have no. of grace logins allowed
177 * PAM_MAXTRIES - If maximum failure of wrong password has reached
178 * seconds param will have no. of seconds for the
179 * account to get unlocked
180 * PAM_AUTH_ERR - Failure return code
181 */
182 static int
get_account_mgmt(const char * user,int * seconds,int * grace)183 get_account_mgmt(const char *user, int *seconds, int *grace)
184 {
185 int rc = PAM_AUTH_ERR;
186 AcctUsableResponse_t acctResp;
187
188 (void *)memset((void*)&acctResp, 0, sizeof (acctResp));
189 /* get the values for local account checking */
190 if ((rc = __ns_ldap_getAcctMgmt(user, &acctResp))
191 != NS_LDAP_SUCCESS) {
192 syslog(LOG_DEBUG,
193 "__ns_ldap_getAcctMgmt() failed for %s with error %d",
194 user, rc);
195 return (PAM_AUTH_ERR);
196 }
197
198 if (acctResp.choice == 0) {
199 /* should be able to login */
200 *seconds =
201 acctResp.AcctUsableResp.seconds_before_expiry;
202 return (PAM_SUCCESS);
203 } else if (acctResp.choice == 1) {
204 /* cannot login */
205 if (acctResp.AcctUsableResp.more_info.inactive)
206 /* entry inactive */
207 return (PAM_ACCT_EXPIRED);
208 if (acctResp.AcctUsableResp.more_info.reset)
209 /* password reset by administrator */
210 return (PAM_NEW_AUTHTOK_REQD);
211 if (acctResp.AcctUsableResp.more_info.expired) {
212 /*
213 * password expired, check for grace logins.
214 */
215 *grace =
216 acctResp.AcctUsableResp.more_info.rem_grace;
217 return (PAM_AUTHTOK_EXPIRED);
218 }
219 if (acctResp.AcctUsableResp.more_info.sec_b4_unlock) {
220 /* max failures reached, seconds before unlock */
221 *seconds =
222 acctResp.AcctUsableResp.more_info.sec_b4_unlock;
223 return (PAM_MAXTRIES);
224 }
225 }
226 return (PAM_AUTH_ERR);
227 }
228
229 /*
230 * pam_sm_acct_mgmt main account managment routine.
231 * This routine relies on the LDAP
232 * directory server to provide the
233 * password aging and account lockout
234 * information. This is done by first
235 * trying to authenticate the user and
236 * then checking the password status
237 * returned.
238 *
239 * Returns: module error or specific
240 * error on failure.
241 */
242
243 int
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)244 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
245 {
246
247 const char *user = NULL;
248 int result = PAM_AUTH_ERR;
249 int debug = 0;
250 int i;
251 const char *password = NULL;
252 ns_cred_t *credp = NULL;
253 int nowarn = 0;
254 int seconds = 0, grace = 0;
255 ldap_authtok_data *status;
256
257 for (i = 0; i < argc; i++) {
258 if (strcmp(argv[i], "debug") == 0)
259 debug = 1;
260 else if (strcasecmp(argv[i], "nowarn") == 0) {
261 nowarn = 1;
262 flags = flags | PAM_SILENT;
263 }
264 else
265 syslog(LOG_DEBUG,
266 "pam_ldap pam_sm_acct_mgmt: "
267 "illegal option %s",
268 argv[i]);
269 }
270
271 if ((result = pam_get_item(pamh, PAM_USER, (const void **)&user)) !=
272 PAM_SUCCESS) {
273 goto out;
274 }
275
276 if (debug)
277 syslog(LOG_DEBUG,
278 "ldap pam_sm_acct_mgmt(%s), flags = %x %s",
279 (user)?user:"no-user", flags,
280 (nowarn)? ", nowarn": "");
281
282 if (user == NULL) {
283 result = PAM_USER_UNKNOWN;
284 goto out;
285 }
286
287 /* retrieve the password from the PAM handle */
288 result = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&password);
289 if (password == NULL) {
290 if (debug)
291 syslog(LOG_DEBUG, "ldap pam_sm_acct_mgmt: "
292 "no password for user %s", user);
293 /* Do local account checking */
294 result = get_account_mgmt(user, &seconds, &grace);
295 } else {
296 /* Try to authenticate to get password management info */
297 result = authenticate(&credp, user,
298 password, &seconds);
299 }
300
301 /*
302 * process the password management info.
303 * If user needs to change the password immediately,
304 * just return the rc.
305 * Otherwise, reset rc to the appropriate PAM error or
306 * warn the user about password expiration.
307 */
308 if (result == PAM_MAXTRIES) {
309 /* exceed retry limit, denied access to account */
310 if (!(flags & PAM_SILENT))
311 display_acct_unlock_time(pamh, seconds);
312 result = PAM_PERM_DENIED;
313 } else if (result == PAM_ACCT_EXPIRED)
314 /* account is inactivated */
315 result = PAM_ACCT_EXPIRED;
316 else if (result == PAM_AUTHTOK_EXPIRED) {
317 if (!(flags & PAM_SILENT))
318 warn_user_passwd_expired(pamh, grace);
319 /* password expired, check for grace logins */
320 if (grace > 0)
321 result = PAM_SUCCESS;
322 else
323 result = PAM_AUTHTOK_EXPIRED;
324 } else if (result == PAM_NEW_AUTHTOK_REQD) {
325 /* password has been reset by administrator */
326 if (!(flags & PAM_SILENT))
327 display_passwd_reset_msg(pamh);
328 result = PAM_NEW_AUTHTOK_REQD;
329 } else if (result == PAM_SUCCESS) {
330 /*
331 * warn the user if the password
332 * is about to expire.
333 */
334 if (!(flags & PAM_SILENT) &&
335 seconds > 0)
336 warn_user_passwd_will_expire(pamh,
337 seconds);
338
339 }
340
341 out:
342 if (credp != NULL)
343 (void) __ns_ldap_freeCred(&credp);
344
345 /* store the password aging status in the pam handle */
346 if (result != PAM_SUCCESS) {
347 int pam_res;
348 ldap_authtok_data *authtok_data;
349
350 pam_res = pam_get_data(
351 pamh, LDAP_AUTHTOK_DATA, (const void **)&authtok_data);
352
353 if ((status = (ldap_authtok_data *)calloc
354 (1, sizeof (ldap_authtok_data))) == NULL) {
355 return (PAM_BUF_ERR);
356 }
357
358 if (pam_res == PAM_SUCCESS)
359 (void) memcpy(status, authtok_data,
360 sizeof (ldap_authtok_data));
361
362 status->age_status = result;
363 if (pam_set_data(pamh, LDAP_AUTHTOK_DATA, status, ldap_cleanup)
364 != PAM_SUCCESS) {
365 free(status);
366 return (PAM_SERVICE_ERR);
367 }
368 }
369
370 return (result);
371 }
372