diff options
author | Andrew Bartlett <abartlet@samba.org> | 2014-12-04 17:23:29 +1300 |
---|---|---|
committer | Karolin Seeger <kseeger@samba.org> | 2015-01-15 14:54:47 +0100 |
commit | ef7fb904a97f00babb33affa0bfc8d2f5bb5ce32 (patch) | |
tree | 21d3a6df0f59ed4ca356b22417d03100c6f89d39 /source4/dsdb/samdb | |
parent | 9d62b6764e99737fd7b914163237a8767d1224b1 (diff) | |
download | samba-ef7fb904a97f00babb33affa0bfc8d2f5bb5ce32.tar.gz samba-ef7fb904a97f00babb33affa0bfc8d2f5bb5ce32.tar.xz samba-ef7fb904a97f00babb33affa0bfc8d2f5bb5ce32.zip |
CVE-2014-8143:dsdb-samldb: Check for extended access rights before we allow changes to userAccountControl
This requires an additional control to be used in the
LSA server to add domain trust account objects.
Bug: https://bugzilla.samba.org/show_bug.cgi?id=10993
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Garming Sam <garming@catalyst.net.nz>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Autobuild-User(master): Karolin Seeger <kseeger@samba.org>
Autobuild-Date(master): Thu Jan 15 14:54:47 CET 2015 on sn-devel-104
Diffstat (limited to 'source4/dsdb/samdb')
-rw-r--r-- | source4/dsdb/samdb/ldb_modules/samldb.c | 190 | ||||
-rw-r--r-- | source4/dsdb/samdb/samdb.h | 6 |
2 files changed, 195 insertions, 1 deletions
diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 7619bbb066..54e2e5e629 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -33,6 +33,7 @@ #include "includes.h" #include "libcli/ldap/ldap_ndr.h" #include "ldb_module.h" +#include "auth/auth.h" #include "dsdb/samdb/samdb.h" #include "dsdb/samdb/ldb_modules/util.h" #include "dsdb/samdb/ldb_modules/ridalloc.h" @@ -944,6 +945,10 @@ static int samldb_schema_info_update(struct samldb_ctx *ac) } static int samldb_prim_group_tester(struct samldb_ctx *ac, uint32_t rid); +static int samldb_check_user_account_control_acl(struct samldb_ctx *ac, + struct dom_sid *sid, + uint32_t user_account_control, + uint32_t user_account_control_old); /* * "Objectclass" trigger (MS-SAMR 3.1.1.8.1) @@ -1039,7 +1044,6 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) el = ldb_msg_find_element(ac->msg, "userAccountControl"); if (el != NULL) { uint32_t user_account_control, account_type; - /* Step 1.3: "userAccountControl" -> "sAMAccountType" mapping */ user_account_control = ldb_msg_find_attr_as_uint(ac->msg, "userAccountControl", @@ -1155,6 +1159,12 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac) return ret; } } + + ret = samldb_check_user_account_control_acl(ac, NULL, + user_account_control, 0); + if (ret != LDB_SUCCESS) { + return ret; + } } break; } @@ -1442,6 +1452,172 @@ static int samldb_prim_group_trigger(struct samldb_ctx *ac) return ret; } +/** + * Validate that the restriction in point 5 of MS-SAMR 3.1.1.8.10 userAccountControl is honoured + * + */ +static int samldb_check_user_account_control_acl(struct samldb_ctx *ac, + struct dom_sid *sid, + uint32_t user_account_control, + uint32_t user_account_control_old) +{ + int i, ret = 0; + bool need_acl_check = false; + struct ldb_result *res; + const char * const sd_attrs[] = {"ntSecurityDescriptor", NULL}; + struct security_token *user_token; + struct security_descriptor *domain_sd; + struct ldb_dn *domain_dn = ldb_get_default_basedn(ldb_module_get_ctx(ac->module)); + const struct uac_to_guid { + uint32_t uac; + const char *oid; + const char *guid; + enum sec_privilege privilege; + bool delete_is_privileged; + const char *error_string; + } map[] = { + { + .uac = UF_PASSWD_NOTREQD, + .guid = GUID_DRS_UPDATE_PASSWORD_NOT_REQUIRED_BIT, + .error_string = "Adding the UF_PASSWD_NOTREQD bit in userAccountControl requires the Update-Password-Not-Required-Bit right that was not given on the Domain object" + }, + { + .uac = UF_DONT_EXPIRE_PASSWD, + .guid = GUID_DRS_UNEXPIRE_PASSWORD, + .error_string = "Adding the UF_DONT_EXPIRE_PASSWD bit in userAccountControl requires the Unexpire-Password right that was not given on the Domain object" + }, + { + .uac = UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED, + .guid = GUID_DRS_ENABLE_PER_USER_REVERSIBLY_ENCRYPTED_PASSWORD, + .error_string = "Adding the UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED bit in userAccountControl requires the Enable-Per-User-Reversibly-Encrypted-Password right that was not given on the Domain object" + }, + { + .uac = UF_SERVER_TRUST_ACCOUNT, + .guid = GUID_DRS_DS_INSTALL_REPLICA, + .error_string = "Adding the UF_SERVER_TRUST_ACCOUNT bit in userAccountControl requires the DS-Install-Replica right that was not given on the Domain object" + }, + { + .uac = UF_PARTIAL_SECRETS_ACCOUNT, + .guid = GUID_DRS_DS_INSTALL_REPLICA, + .error_string = "Adding the UF_PARTIAL_SECRETS_ACCOUNT bit in userAccountControl requires the DS-Install-Replica right that was not given on the Domain object" + }, + { + .uac = UF_INTERDOMAIN_TRUST_ACCOUNT, + .oid = DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID, + .error_string = "Updating the UF_INTERDOMAIN_TRUST_ACCOUNT bit in userAccountControl is not permitted over LDAP. This bit is restricted to the LSA CreateTrustedDomain interface", + .delete_is_privileged = true + }, + { + .uac = UF_TRUSTED_FOR_DELEGATION, + .privilege = SEC_PRIV_ENABLE_DELEGATION, + .delete_is_privileged = true, + .error_string = "Updating the UF_TRUSTED_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege" + }, + { + .uac = UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION, + .privilege = SEC_PRIV_ENABLE_DELEGATION, + .delete_is_privileged = true, + .error_string = "Updating the UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION bit in userAccountControl is not permitted without the SeEnableDelegationPrivilege" + } + + }; + + if (dsdb_module_am_system(ac->module)) { + return LDB_SUCCESS; + } + + for (i = 0; i < ARRAY_SIZE(map); i++) { + if (user_account_control & map[i].uac) { + need_acl_check = true; + break; + } + } + if (need_acl_check == false) { + return LDB_SUCCESS; + } + + user_token = acl_user_token(ac->module); + if (user_token == NULL) { + return LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + + ret = dsdb_module_search_dn(ac->module, ac, &res, + domain_dn, + sd_attrs, + DSDB_FLAG_NEXT_MODULE | DSDB_SEARCH_SHOW_DELETED, + ac->req); + if (ret != LDB_SUCCESS) { + return ret; + } + if (res->count != 1) { + return ldb_module_operr(ac->module); + } + + ret = dsdb_get_sd_from_ldb_message(ldb_module_get_ctx(ac->module), + ac, res->msgs[0], &domain_sd); + + if (ret != LDB_SUCCESS) { + return ret; + } + + for (i = 0; i < ARRAY_SIZE(map); i++) { + uint32_t this_uac_new = user_account_control & map[i].uac; + uint32_t this_uac_old = user_account_control_old & map[i].uac; + if (this_uac_new != this_uac_old) { + if (this_uac_old != 0) { + if (map[i].delete_is_privileged == false) { + continue; + } + } + if (map[i].oid) { + struct ldb_control *control = ldb_request_get_control(ac->req, map[i].oid); + if (control == NULL) { + ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + } else if (map[i].privilege != SEC_PRIV_INVALID) { + bool have_priv = security_token_has_privilege(user_token, + map[i].privilege); + if (have_priv == false) { + ret = LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS; + } + } else { + ret = acl_check_extended_right(ac, domain_sd, + user_token, + map[i].guid, + SEC_ADS_CONTROL_ACCESS, + sid); + } + if (ret != LDB_SUCCESS) { + break; + } + } + } + if (ret == LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS) { + switch (ac->req->operation) { + case LDB_ADD: + ldb_asprintf_errstring(ldb_module_get_ctx(ac->module), + "Failed to add %s: %s", + ldb_dn_get_linearized(ac->msg->dn), + map[i].error_string); + break; + case LDB_MODIFY: + ldb_asprintf_errstring(ldb_module_get_ctx(ac->module), + "Failed to modify %s: %s", + ldb_dn_get_linearized(ac->msg->dn), + map[i].error_string); + break; + default: + return ldb_module_operr(ac->module); + } + if (map[i].guid) { + dsdb_acl_debug(domain_sd, acl_user_token(ac->module), + domain_dn, + true, + 10); + } + } + return ret; +} /** * This function is called on LDB modify operations. It performs some additions/ @@ -1467,6 +1643,7 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) struct ldb_val *val; struct ldb_val computer_val; struct ldb_message *tmp_msg; + struct dom_sid *sid; int ret; struct ldb_result *res; const char * const attrs[] = { @@ -1475,6 +1652,7 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) "userAccountControl", "msDS-User-Account-Control-Computed", "lockoutTime", + "objectSid", NULL }; bool is_computer = false; @@ -1671,6 +1849,16 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) ldb_msg_remove_attr(ac->msg, "userAccountControl"); } + sid = samdb_result_dom_sid(res, res->msgs[0], "objectSid"); + if (sid == NULL) { + return ldb_module_operr(ac->module); + } + + ret = samldb_check_user_account_control_acl(ac, sid, new_uac, old_uac); + if (ret != LDB_SUCCESS) { + return ret; + } + return LDB_SUCCESS; } diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h index 7f77d4e382..4f57343e05 100644 --- a/source4/dsdb/samdb/samdb.h +++ b/source4/dsdb/samdb/samdb.h @@ -135,6 +135,12 @@ struct dsdb_control_password_change { */ #define DSDB_CONTROL_SEC_DESC_PROPAGATION_OID "1.3.6.1.4.1.7165.4.3.21" +/* + * passed when creating a interdomain trust account through LSA + * to relax constraints in the samldb ldb module. + */ +#define DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID "1.3.6.1.4.1.7165.4.3.23" + #define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1" struct dsdb_extended_replicated_object { struct ldb_message *msg; |