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 #pragma ident "%Z%%M% %I% %E% SMI" 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(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( 245 pam_handle_t *pamh, 246 int flags, 247 int argc, 248 const char **argv) 249 { 250 251 char *user = NULL; 252 int result = PAM_AUTH_ERR; 253 int debug = 0; 254 int i; 255 char *password = NULL; 256 ns_cred_t *credp = NULL; 257 int nowarn = 0; 258 int seconds = 0, grace = 0; 259 ldap_authtok_data *status; 260 261 for (i = 0; i < argc; i++) { 262 if (strcmp(argv[i], "debug") == 0) 263 debug = 1; 264 else if (strcasecmp(argv[i], "nowarn") == 0) { 265 nowarn = 1; 266 flags = flags | PAM_SILENT; 267 } 268 else 269 syslog(LOG_DEBUG, 270 "pam_ldap pam_sm_acct_mgmt: " 271 "illegal option %s", 272 argv[i]); 273 } 274 275 if ((result = pam_get_item(pamh, PAM_USER, (void **)&user)) 276 != PAM_SUCCESS) 277 goto out; 278 279 if (debug) 280 syslog(LOG_DEBUG, 281 "ldap pam_sm_acct_mgmt(%s), flags = %x %s", 282 (user)?user:"no-user", flags, 283 (nowarn)? ", nowarn": ""); 284 285 if (user == NULL) { 286 result = PAM_USER_UNKNOWN; 287 goto out; 288 } 289 290 /* retrieve the password from the PAM handle */ 291 result = pam_get_item(pamh, PAM_AUTHTOK, (void **) &password); 292 if (password == NULL) { 293 if (debug) 294 syslog(LOG_DEBUG, "ldap pam_sm_acct_mgmt: " 295 "no password for user %s", user); 296 /* Do local account checking */ 297 result = get_account_mgmt(user, &seconds, &grace); 298 } else { 299 /* Try to authenticate to get password management info */ 300 result = authenticate(&credp, user, 301 password, &seconds); 302 } 303 304 /* 305 * process the password management info. 306 * If user needs to change the password immediately, 307 * just return the rc. 308 * Otherwise, reset rc to the appropriate PAM error or 309 * warn the user about password expiration. 310 */ 311 if (result == PAM_MAXTRIES) { 312 /* exceed retry limit, denied access to account */ 313 if (!(flags & PAM_SILENT)) 314 display_acct_unlock_time(pamh, seconds); 315 result = PAM_PERM_DENIED; 316 } else if (result == PAM_ACCT_EXPIRED) 317 /* account is inactivated */ 318 result = PAM_ACCT_EXPIRED; 319 else if (result == PAM_AUTHTOK_EXPIRED) { 320 if (!(flags & PAM_SILENT)) 321 warn_user_passwd_expired(pamh, grace); 322 /* password expired, check for grace logins */ 323 if (grace > 0) 324 result = PAM_SUCCESS; 325 else 326 result = PAM_AUTHTOK_EXPIRED; 327 } else if (result == PAM_NEW_AUTHTOK_REQD) { 328 /* password has been reset by administrator */ 329 if (!(flags & PAM_SILENT)) 330 display_passwd_reset_msg(pamh); 331 result = PAM_NEW_AUTHTOK_REQD; 332 } else if (result == PAM_SUCCESS) { 333 /* 334 * warn the user if the password 335 * is about to expire. 336 */ 337 if (!(flags & PAM_SILENT) && 338 seconds > 0) 339 warn_user_passwd_will_expire(pamh, 340 seconds); 341 342 } 343 344 out: 345 if (credp != NULL) 346 (void) __ns_ldap_freeCred(&credp); 347 348 /* store the password aging status in the pam handle */ 349 if (result != PAM_SUCCESS) { 350 int pam_res; 351 ldap_authtok_data *authtok_data; 352 353 pam_res = pam_get_data( 354 pamh, LDAP_AUTHTOK_DATA, (const void **)&authtok_data); 355 356 if ((status = (ldap_authtok_data *)calloc 357 (1, sizeof (ldap_authtok_data))) == NULL) { 358 return (PAM_BUF_ERR); 359 } 360 361 if (pam_res == PAM_SUCCESS) 362 (void) memcpy(status, authtok_data, 363 sizeof (ldap_authtok_data)); 364 365 status->age_status = result; 366 if (pam_set_data(pamh, LDAP_AUTHTOK_DATA, status, ldap_cleanup) 367 != PAM_SUCCESS) { 368 free(status); 369 return (PAM_SERVICE_ERR); 370 } 371 } 372 373 return (result); 374 } 375