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 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 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 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 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 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 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 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