2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include "ldap_headers.h"
2N/A
2N/A/*ARGSUSED*/
2N/Astatic void
2N/Aldap_cleanup(
2N/A pam_handle_t *pamh,
2N/A void *data,
2N/A int pam_status)
2N/A{
2N/A free((ldap_authtok_data *)data);
2N/A}
2N/A
2N/A/*
2N/A * warn_user_passwd_will_expire - warn the user when the password will
2N/A * expire.
2N/A */
2N/A
2N/Astatic void
2N/Awarn_user_passwd_will_expire(
2N/A pam_handle_t *pamh,
2N/A int sec_until_expired)
2N/A{
2N/A char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
2N/A int days = 0, hours = 0;
2N/A int seconds_d = 0, seconds_h = 0;
2N/A
2N/A days = sec_until_expired / 86400;
2N/A seconds_d = sec_until_expired % 86400;
2N/A hours = (days * 24) + seconds_d / 3600;
2N/A seconds_h = seconds_d % 3600;
2N/A
2N/A if (sec_until_expired <= (86400 * 2)) {
2N/A if (seconds_d <= 3600 && days == 0)
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your password will expire within one hour."));
2N/A else
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your password will expire in %d hours."),
2N/A (seconds_h == 0) ? hours : hours + 1);
2N/A } else {
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your password will expire in %d days."),
2N/A (seconds_d == 0) ? days : days + 1);
2N/A }
2N/A
2N/A (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
2N/A}
2N/A
2N/A/*
2N/A * display_acct_unlock_time - Display the time left for the account to
2N/A * get auto unlocked after the maximum login failures has reached.
2N/A */
2N/Astatic void
2N/Adisplay_acct_unlock_time(pam_handle_t *pamh, int sec_b4_unlock)
2N/A{
2N/A char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
2N/A int days = 0, hours = 0;
2N/A int seconds_d = 0, seconds_h = 0;
2N/A
2N/A /* Account is locked forever */
2N/A if (sec_b4_unlock == -1) {
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your account is locked, please contact administrator."));
2N/A (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1,
2N/A messages, NULL);
2N/A return;
2N/A }
2N/A
2N/A days = sec_b4_unlock / 86400;
2N/A seconds_d = sec_b4_unlock % 86400;
2N/A hours = (days * 24) + seconds_d / 3600;
2N/A seconds_h = seconds_d % 3600;
2N/A
2N/A if (sec_b4_unlock <= (86400 * 2)) {
2N/A if (seconds_d <= 3600 && days == 0)
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your account is locked and will be unlocked"
2N/A " within one hour."));
2N/A else
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your account is locked and will be unlocked"
2N/A " in %d hours."),
2N/A (seconds_h == 0) ? hours : hours + 1);
2N/A } else {
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your account is locked and will be unlocked"
2N/A " in %d days."),
2N/A (seconds_d == 0) ? days : days + 1);
2N/A }
2N/A
2N/A (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
2N/A}
2N/A
2N/A/*
2N/A * warn_user_passwd_expired - warn the user that the password has expired
2N/A */
2N/Astatic void
2N/Awarn_user_passwd_expired(pam_handle_t *pamh, int grace)
2N/A{
2N/A char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
2N/A
2N/A if (grace)
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your password has expired. "
2N/A "Number of grace logins allowed are %d."),
2N/A grace);
2N/A else
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your password has expired."));
2N/A
2N/A (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
2N/A}
2N/A
2N/A/*
2N/A * display_passwd_reset_msg - tell user that password has been reset by
2N/A * administrator
2N/A */
2N/Astatic void
2N/Adisplay_passwd_reset_msg(pam_handle_t *pamh)
2N/A{
2N/A char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
2N/A
2N/A (void) snprintf(messages[0], sizeof (messages[0]),
2N/A dgettext(TEXT_DOMAIN,
2N/A "Your password has been reset by administrator."));
2N/A
2N/A (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1, messages, NULL);
2N/A}
2N/A
2N/A/*
2N/A * Retreives account management related attributes for the user using
2N/A * default binding and does local account checks .
2N/A *
2N/A * Return Value: PAM_SUCCESS - If account is valid, seconds param will have
2N/A * seconds left for password to expire
2N/A * PAM_ACCT_EXPIRED - If account is inactive
2N/A * PAM_NEW_AUTHTOK_REQD - Password is reset by admin
2N/A * PAM_AUTHTOK_EXPIRED - User password has expired, grace
2N/A * param will have no. of grace logins allowed
2N/A * PAM_MAXTRIES - If maximum failure of wrong password has reached
2N/A * seconds param will have no. of seconds for the
2N/A * account to get unlocked
2N/A * PAM_AUTH_ERR - Failure return code
2N/A */
2N/Astatic int
2N/Aget_account_mgmt(char *user, int *seconds, int *grace)
2N/A{
2N/A int rc = PAM_AUTH_ERR;
2N/A AcctUsableResponse_t acctResp;
2N/A
2N/A (void *)memset((void*)&acctResp, 0, sizeof (acctResp));
2N/A /* get the values for local account checking */
2N/A if ((rc = __ns_ldap_getAcctMgmt(user, &acctResp))
2N/A != NS_LDAP_SUCCESS) {
2N/A syslog(LOG_DEBUG,
2N/A "__ns_ldap_getAcctMgmt() failed for %s with error %d",
2N/A user, rc);
2N/A return (PAM_AUTH_ERR);
2N/A }
2N/A
2N/A if (acctResp.choice == 0) {
2N/A /* should be able to login */
2N/A *seconds =
2N/A acctResp.AcctUsableResp.seconds_before_expiry;
2N/A return (PAM_SUCCESS);
2N/A } else if (acctResp.choice == 1) {
2N/A /* cannot login */
2N/A if (acctResp.AcctUsableResp.more_info.inactive)
2N/A /* entry inactive */
2N/A return (PAM_ACCT_EXPIRED);
2N/A if (acctResp.AcctUsableResp.more_info.reset)
2N/A /* password reset by administrator */
2N/A return (PAM_NEW_AUTHTOK_REQD);
2N/A if (acctResp.AcctUsableResp.more_info.expired) {
2N/A /*
2N/A * password expired, check for grace logins.
2N/A */
2N/A *grace =
2N/A acctResp.AcctUsableResp.more_info.rem_grace;
2N/A return (PAM_AUTHTOK_EXPIRED);
2N/A }
2N/A if (acctResp.AcctUsableResp.more_info.sec_b4_unlock) {
2N/A /* max failures reached, seconds before unlock */
2N/A *seconds =
2N/A acctResp.AcctUsableResp.more_info.sec_b4_unlock;
2N/A return (PAM_MAXTRIES);
2N/A }
2N/A }
2N/A return (PAM_AUTH_ERR);
2N/A}
2N/A
2N/A/*
2N/A * pam_sm_acct_mgmt main account managment routine.
2N/A * This routine relies on the LDAP
2N/A * directory server to provide the
2N/A * password aging and account lockout
2N/A * information. This is done by first
2N/A * trying to authenticate the user and
2N/A * then checking the password status
2N/A * returned.
2N/A *
2N/A * Returns: module error or specific
2N/A * error on failure.
2N/A */
2N/A
2N/Aint
2N/Apam_sm_acct_mgmt(
2N/A pam_handle_t *pamh,
2N/A int flags,
2N/A int argc,
2N/A const char **argv)
2N/A{
2N/A
2N/A char *user = NULL;
2N/A int result = PAM_AUTH_ERR;
2N/A int debug = 0;
2N/A int i;
2N/A char *password = NULL;
2N/A ns_cred_t *credp = NULL;
2N/A int nowarn = 0;
2N/A int seconds = 0, grace = 0;
2N/A ldap_authtok_data *status;
2N/A
2N/A for (i = 0; i < argc; i++) {
2N/A if (strcmp(argv[i], "debug") == 0)
2N/A debug = 1;
2N/A else if (strcasecmp(argv[i], "nowarn") == 0) {
2N/A nowarn = 1;
2N/A flags = flags | PAM_SILENT;
2N/A }
2N/A else
2N/A syslog(LOG_DEBUG,
2N/A "pam_ldap pam_sm_acct_mgmt: "
2N/A "illegal option %s",
2N/A argv[i]);
2N/A }
2N/A
2N/A if ((result = pam_get_item(pamh, PAM_USER, (void **)&user))
2N/A != PAM_SUCCESS)
2N/A goto out;
2N/A
2N/A if (debug)
2N/A syslog(LOG_DEBUG,
2N/A "ldap pam_sm_acct_mgmt(%s), flags = %x %s",
2N/A (user)?user:"no-user", flags,
2N/A (nowarn)? ", nowarn": "");
2N/A
2N/A if (user == NULL) {
2N/A result = PAM_USER_UNKNOWN;
2N/A goto out;
2N/A }
2N/A
2N/A /* retrieve the password from the PAM handle */
2N/A result = pam_get_item(pamh, PAM_AUTHTOK, (void **) &password);
2N/A if (password == NULL) {
2N/A if (debug)
2N/A syslog(LOG_DEBUG, "ldap pam_sm_acct_mgmt: "
2N/A "no password for user %s", user);
2N/A /* Do local account checking */
2N/A result = get_account_mgmt(user, &seconds, &grace);
2N/A } else {
2N/A /* Try to authenticate to get password management info */
2N/A result = authenticate(&credp, user,
2N/A password, &seconds);
2N/A }
2N/A
2N/A /*
2N/A * process the password management info.
2N/A * If user needs to change the password immediately,
2N/A * just return the rc.
2N/A * Otherwise, reset rc to the appropriate PAM error or
2N/A * warn the user about password expiration.
2N/A */
2N/A if (result == PAM_MAXTRIES) {
2N/A /* exceed retry limit, denied access to account */
2N/A if (!(flags & PAM_SILENT))
2N/A display_acct_unlock_time(pamh, seconds);
2N/A result = PAM_PERM_DENIED;
2N/A } else if (result == PAM_ACCT_EXPIRED)
2N/A /* account is inactivated */
2N/A result = PAM_ACCT_EXPIRED;
2N/A else if (result == PAM_AUTHTOK_EXPIRED) {
2N/A if (!(flags & PAM_SILENT))
2N/A warn_user_passwd_expired(pamh, grace);
2N/A /* password expired, check for grace logins */
2N/A if (grace > 0)
2N/A result = PAM_SUCCESS;
2N/A else
2N/A result = PAM_AUTHTOK_EXPIRED;
2N/A } else if (result == PAM_NEW_AUTHTOK_REQD) {
2N/A /* password has been reset by administrator */
2N/A if (!(flags & PAM_SILENT))
2N/A display_passwd_reset_msg(pamh);
2N/A result = PAM_NEW_AUTHTOK_REQD;
2N/A } else if (result == PAM_SUCCESS) {
2N/A /*
2N/A * warn the user if the password
2N/A * is about to expire.
2N/A */
2N/A if (!(flags & PAM_SILENT) &&
2N/A seconds > 0)
2N/A warn_user_passwd_will_expire(pamh,
2N/A seconds);
2N/A
2N/A }
2N/A
2N/Aout:
2N/A if (credp != NULL)
2N/A (void) __ns_ldap_freeCred(&credp);
2N/A
2N/A /* store the password aging status in the pam handle */
2N/A if (result != PAM_SUCCESS) {
2N/A int pam_res;
2N/A ldap_authtok_data *authtok_data;
2N/A
2N/A pam_res = pam_get_data(
2N/A pamh, LDAP_AUTHTOK_DATA, (const void **)&authtok_data);
2N/A
2N/A if ((status = (ldap_authtok_data *)calloc
2N/A (1, sizeof (ldap_authtok_data))) == NULL) {
2N/A return (PAM_BUF_ERR);
2N/A }
2N/A
2N/A if (pam_res == PAM_SUCCESS)
2N/A (void) memcpy(status, authtok_data,
2N/A sizeof (ldap_authtok_data));
2N/A
2N/A status->age_status = result;
2N/A if (pam_set_data(pamh, LDAP_AUTHTOK_DATA, status, ldap_cleanup)
2N/A != PAM_SUCCESS) {
2N/A free(status);
2N/A return (PAM_SERVICE_ERR);
2N/A }
2N/A }
2N/A
2N/A return (result);
2N/A}