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