From e9fa11b04ed6cdaf160ae911eda04f014a6aa4d5 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 21 Dec 2010 15:45:37 +0100 Subject: [PATCH] Add LDAP access control based on NDS attributes --- src/config/SSSDConfig.py | 3 + src/config/etc/sssd.api.d/sssd-ldap.conf | 3 + src/man/sssd-ldap.5.xml | 50 +++++++++ src/providers/ipa/ipa_common.c | 5 +- src/providers/ldap/ldap_common.c | 10 ++- src/providers/ldap/ldap_init.c | 1 + src/providers/ldap/sdap.h | 6 + src/providers/ldap/sdap_access.c | 177 ++++++++++++++++++++++++++++++ src/providers/ldap/sdap_access.h | 1 + 9 files changed, 253 insertions(+), 3 deletions(-) diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py index 5114a17..52b9d0a 100644 --- a/src/config/SSSDConfig.py +++ b/src/config/SSSDConfig.py @@ -188,6 +188,9 @@ option_strings = { 'ldap_user_ad_account_expires' : _('accountExpires attribute of AD'), 'ldap_user_ad_user_account_control' : _('userAccountControl attribute of AD'), 'ldap_ns_account_lock' : _('nsAccountLock attribute'), + 'ldap_user_nds_login_disabled' : _('loginDisabled attribute of NDS'), + 'ldap_user_nds_login_expiration_time' : _('loginExpirationTime attribute of NDS'), + 'ldap_user_nds_login_allowed_time_map' : _('loginAllowedTimeMap attribute of NDS'), 'ldap_group_search_base' : _('Base DN for group lookups'), # not used # 'ldap_group_search_scope' : _('Scope of group lookups'), diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf index ce9ec51..18321ca 100644 --- a/src/config/etc/sssd.api.d/sssd-ldap.conf +++ b/src/config/etc/sssd.api.d/sssd-ldap.conf @@ -67,6 +67,9 @@ ldap_pwd_attribute = str, None, false ldap_user_ad_account_expires = str, None, false ldap_user_ad_user_account_control = str, None, false ldap_ns_account_lock = str, None, false +ldap_user_nds_login_disabled = str, None, false +ldap_user_nds_login_expiration_time = str, None, false +ldap_user_nds_login_allowed_time_map = str, None, false ldap_group_search_base = str, None, false ldap_group_search_scope = str, None, false ldap_group_search_filter = str, None, false diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml index e554cac..7c364ab 100644 --- a/src/man/sssd-ldap.5.xml +++ b/src/man/sssd-ldap.5.xml @@ -493,6 +493,47 @@ + ldap_user_nds_login_disabled (string) + + + When using ldap_account_expire_policy=nds, this + attribute determines if access is allowed or not. + + + Default: loginDisabled + + + + + + ldap_user_nds_login_expiration_time (string) + + + When using ldap_account_expire_policy=nds, this + attribute determines until which date access is + granted. + + + Default: loginDisabled + + + + + + ldap_user_nds_login_allowed_time_map (string) + + + When using ldap_account_expire_policy=nds, this + attribute determines the hours of a day in a week + when access is granted. + + + Default: loginAllowedTimeMap + + + + + ldap_user_principal (string) @@ -1296,6 +1337,15 @@ ldap_access_filter = memberOf=cn=allowedusers,ou=Groups,dc=example,dc=com access is allowed or not. + nds: the values of + ldap_user_nds_login_allowed_time_map, + ldap_user_nds_login_disabled and + ldap_user_nds_login_expiration_time are used to + check if access is allowed. If both attributes are + missing access is granted. + + + Default: Empty diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index f490309..9b4c34e 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -132,7 +132,10 @@ struct sdap_attr_map ipa_user_map[] = { { "ldap_user_ad_account_expires", "accountExpires", SYSDB_AD_ACCOUNT_EXPIRES, NULL}, { "ldap_user_ad_user_account_control", "userAccountControl", SYSDB_AD_USER_ACCOUNT_CONTROL, NULL}, { "ldap_ns_account_lock", "nsAccountLock", SYSDB_NS_ACCOUNT_LOCK, NULL}, - { "ldap_user_authorized_host", "host", SYSDB_AUTHORIZED_HOST, NULL } + { "ldap_user_authorized_host", "host", SYSDB_AUTHORIZED_HOST, NULL }, + { "ldap_user_nds_login_disabled", "loginDisabled", SYSDB_NDS_LOGIN_DISABLED, NULL }, + { "ldap_user_nds_login_expiration_time", "loginExpirationTime", SYSDB_NDS_LOGIN_EXPIRATION_TIME, NULL }, + { "ldap_user_nds_login_allowed_time_map", "loginAllowedTimeMap", SYSDB_NDS_LOGIN_ALLOWED_TIME_MAP, NULL } }; struct sdap_attr_map ipa_group_map[] = { diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 9796204..b509882 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -135,7 +135,10 @@ struct sdap_attr_map rfc2307_user_map[] = { { "ldap_user_ad_account_expires", "accountExpires", SYSDB_AD_ACCOUNT_EXPIRES, NULL}, { "ldap_user_ad_user_account_control", "userAccountControl", SYSDB_AD_USER_ACCOUNT_CONTROL, NULL}, { "ldap_ns_account_lock", "nsAccountLock", SYSDB_NS_ACCOUNT_LOCK, NULL}, - { "ldap_user_authorized_host", "host", SYSDB_AUTHORIZED_HOST, NULL } + { "ldap_user_authorized_host", "host", SYSDB_AUTHORIZED_HOST, NULL }, + { "ldap_user_nds_login_disabled", "loginDisabled", SYSDB_NDS_LOGIN_DISABLED, NULL }, + { "ldap_user_nds_login_expiration_time", "loginExpirationTime", SYSDB_NDS_LOGIN_EXPIRATION_TIME, NULL }, + { "ldap_user_nds_login_allowed_time_map", "loginAllowedTimeMap", SYSDB_NDS_LOGIN_ALLOWED_TIME_MAP, NULL } }; struct sdap_attr_map rfc2307_group_map[] = { @@ -179,7 +182,10 @@ struct sdap_attr_map rfc2307bis_user_map[] = { { "ldap_user_ad_account_expires", "accountExpires", SYSDB_AD_ACCOUNT_EXPIRES, NULL}, { "ldap_user_ad_user_account_control", "userAccountControl", SYSDB_AD_USER_ACCOUNT_CONTROL, NULL}, { "ldap_ns_account_lock", "nsAccountLock", SYSDB_NS_ACCOUNT_LOCK, NULL}, - { "ldap_user_authorized_host", "host", SYSDB_AUTHORIZED_HOST, NULL } + { "ldap_user_authorized_host", "host", SYSDB_AUTHORIZED_HOST, NULL }, + { "ldap_user_nds_login_disabled", "loginDisabled", SYSDB_NDS_LOGIN_DISABLED, NULL }, + { "ldap_user_nds_login_expiration_time", "loginExpirationTime", SYSDB_NDS_LOGIN_EXPIRATION_TIME, NULL }, + { "ldap_user_nds_login_allowed_time_map", "loginAllowedTimeMap", SYSDB_NDS_LOGIN_ALLOWED_TIME_MAP, NULL } }; struct sdap_attr_map rfc2307bis_group_map[] = { diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c index 62588b7..c00c0a9 100644 --- a/src/providers/ldap/ldap_init.c +++ b/src/providers/ldap/ldap_init.c @@ -347,6 +347,7 @@ int sssm_ldap_access_init(struct be_ctx *bectx, } else { if (strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_SHADOW) != 0 && strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_AD) != 0 && + strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_NDS) != 0 && strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_RHDS) != 0 && strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_IPA) != 0 && strcasecmp(dummy, LDAP_ACCOUNT_EXPIRE_389DS) != 0) { diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index 4b662c1..b42a5eb 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -122,6 +122,9 @@ struct sdap_ppolicy_data { #define SYSDB_AD_ACCOUNT_EXPIRES "adAccountExpires" #define SYSDB_AD_USER_ACCOUNT_CONTROL "adUserAccountControl" +#define SYSDB_NDS_LOGIN_DISABLED "ndsLoginDisabled" +#define SYSDB_NDS_LOGIN_EXPIRATION_TIME "ndsLoginExpirationTime" +#define SYSDB_NDS_LOGIN_ALLOWED_TIME_MAP "ndsLoginAllowedTimeMap" #define SDAP_ROOTDSE_ATTR_NAMING_CONTEXTS "namingContexts" #define SDAP_ROOTDSE_ATTR_DEFAULT_NAMING_CONTEXT "defaultNamingContext" @@ -238,6 +241,9 @@ enum sdap_user_attrs { SDAP_AT_AD_USER_ACCOUNT_CONTROL, SDAP_AT_NS_ACCOUNT_LOCK, SDAP_AT_AUTHORIZED_HOST, + SDAP_AT_NDS_LOGIN_DISABLED, + SDAP_AT_NDS_LOGIN_EXPIRATION_TIME, + SDAP_AT_NDS_LOGIN_ALLOWED_TIME_MAP, SDAP_OPTS_USER /* attrs counter */ }; diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c index 9b598bc..9bb2f2e 100644 --- a/src/providers/ldap/sdap_access.c +++ b/src/providers/ldap/sdap_access.c @@ -22,6 +22,9 @@ along with this program. If not, see . */ +#define _XOPEN_SOURCE 500 /* for strptime() */ +#include +#undef _XOPEN_SOURCE #include #include #include @@ -470,6 +473,174 @@ static errno_t sdap_account_expired_rhds(struct pam_data *pd, return EOK; } +#define NDS_DISABLE_MSG "The user account is disabled on the server" +#define NDS_EXPIRED_MSG "The user account is expired" +#define NDS_TIME_MAP_MSG "The user account is not allowed at this time" + +static bool nds_check_expired(const char *exp_time_str) +{ + char *end; + struct tm tm; + time_t expire_time; + time_t now; + + if (exp_time_str == NULL) { + DEBUG(9, ("ndsLoginExpirationTime is not set, access granted.\n")); + return false; + } + + memset(&tm, 0, sizeof(tm)); + + end = strptime(exp_time_str, "%Y%m%d%H%M%SZ", &tm); + if (end == NULL) { + DEBUG(1, ("NDS expire date [%s] invalid.\n", exp_time_str)); + return true; + } + if (*end != '\0') { + DEBUG(1, ("NDS expire date [%s] contains extra characters.\n", + exp_time_str)); + return true; + } + + expire_time = mktime(&tm); + if (expire_time == -1) { + DEBUG(1, ("mktime failed to convert [%s].\n", exp_time_str)); + return true; + } + + tzset(); + expire_time -= timezone; + now = time(NULL); + DEBUG(9, ("Time info: tzname[0] [%s] tzname[1] [%s] timezone [%d] " + "daylight [%d] now [%d] expire_time [%d].\n", tzname[0], + tzname[1], timezone, daylight, now, expire_time)); + + if (difftime(now, expire_time) > 0.0) { + DEBUG(4, ("NDS account expired.\n")); + return true; + } + + return false; +} + +/* There is no real documentation of the byte string value of + * loginAllowedTimeMap, but some good example code in + * http://http://developer.novell.com/documentation/samplecode/extjndi_sample/CheckBind.java.html + */ +static bool nds_check_time_map(const struct ldb_val *time_map) +{ + time_t now; + struct tm *tm_now; + size_t map_index; + div_t q; + uint8_t mask = 0; + + if (time_map == NULL) { + DEBUG(9, ("loginAllowedTimeMap is missing, access granted.\n")); + return false; + } + + if (time_map->length != 42) { + DEBUG(4, ("Allowed time map has the wrong size, " + "got [%d], expected 42.\n", time_map->length)); + return true; + } + + now = time(NULL); + tm_now = gmtime(&now); + + map_index = tm_now->tm_wday * 48 + tm_now->tm_hour * 2 + + (tm_now->tm_min < 30 ? 0 : 1); + + if (map_index > 335) { + DEBUG(1, ("Unexpected index value [%d] for time map.\n", index)); + return true; + } + + q = div(map_index, 8); + + if (q.quot > 41 || q.quot < 0 || q.rem > 7 || q.rem < 0) { + DEBUG(1, ("Unexpected result of div(), [%d][%d][%d].\n", + index, q.quot, q.rem)); + return true; + } + + if (q.rem > 0) { + mask = 1 << q.rem; + } + + if (time_map->data[q.quot] & mask) { + DEBUG(4, ("Access allowed by time map.\n")); + return false; + } + + return true; +} + +static errno_t sdap_account_expired_nds(struct pam_data *pd, + struct ldb_message *user_entry, + int *pam_status) +{ + bool locked = true; + int ret; + const char *exp_time_str; + const struct ldb_val *time_map; + + DEBUG(6, ("Performing NDS access check for user [%s]\n", pd->user)); + + locked = ldb_msg_find_attr_as_bool(user_entry, SYSDB_NDS_LOGIN_DISABLED, + false); + DEBUG(9, ("Account for user [%s] is%s disabled.\n", pd->user, + locked ? "" : " not")); + + if (locked) { + ret = pam_add_response(pd, SSS_PAM_SYSTEM_INFO, + sizeof(NDS_DISABLE_MSG), + (const uint8_t *) NDS_DISABLE_MSG); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + } + } else { + exp_time_str = ldb_msg_find_attr_as_string(user_entry, + SYSDB_NDS_LOGIN_EXPIRATION_TIME, + NULL); + locked = nds_check_expired(exp_time_str); + + DEBUG(9, ("Account for user [%s] is%s expired.\n", pd->user, + locked ? "" : " not")); + + if (locked) { + ret = pam_add_response(pd, SSS_PAM_SYSTEM_INFO, + sizeof(NDS_EXPIRED_MSG), + (const uint8_t *) NDS_EXPIRED_MSG); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + } + } else { + time_map = ldb_msg_find_ldb_val(user_entry, + SYSDB_NDS_LOGIN_ALLOWED_TIME_MAP); + + locked = nds_check_time_map(time_map); + + DEBUG(9, ("Account for user [%s] is%s locked at this time.\n", + pd->user, locked ? "" : " not")); + + if (locked) { + ret = pam_add_response(pd, SSS_PAM_SYSTEM_INFO, + sizeof(NDS_TIME_MAP_MSG), + (const uint8_t *) NDS_TIME_MAP_MSG); + if (ret != EOK) { + DEBUG(1, ("pam_add_response failed.\n")); + } + } + } + } + + *pam_status = locked ? PAM_PERM_DENIED : PAM_SUCCESS; + + return EOK; +} + struct sdap_account_expired_req_ctx { int pam_status; }; @@ -525,6 +696,12 @@ static struct tevent_req *sdap_account_expired_send(TALLOC_CTX *mem_ctx, DEBUG(1, ("sdap_account_expired_rhds failed.\n")); goto done; } + } else if (strcasecmp(expire, LDAP_ACCOUNT_EXPIRE_NDS) == 0) { + ret = sdap_account_expired_nds(pd, user_entry, &state->pam_status); + if (ret != EOK) { + DEBUG(1, ("sdap_account_expired_nds failed.\n")); + goto done; + } } else { DEBUG(1, ("Unsupported LDAP account expire policy [%s]. " "Access denied.\n", expire)); diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h index d78fdb1..6263e25 100644 --- a/src/providers/ldap/sdap_access.h +++ b/src/providers/ldap/sdap_access.h @@ -39,6 +39,7 @@ #define LDAP_ACCOUNT_EXPIRE_RHDS "rhds" #define LDAP_ACCOUNT_EXPIRE_IPA "ipa" #define LDAP_ACCOUNT_EXPIRE_389DS "389ds" +#define LDAP_ACCOUNT_EXPIRE_NDS "nds" enum ldap_access_rule { LDAP_ACCESS_EMPTY = -1, -- 1.7.5.4