CVE-2014-8143:dsdb-samldb: Check for extended access rights before we allow changes...
authorAndrew Bartlett <abartlet@samba.org>
Thu, 4 Dec 2014 04:23:29 +0000 (17:23 +1300)
committerKarolin Seeger <kseeger@samba.org>
Mon, 12 Jan 2015 20:04:47 +0000 (21:04 +0100)
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: Stefan Metzmacher <metze@samba.org>
librpc/idl/security.idl
source4/dsdb/samdb/ldb_modules/samldb.c
source4/dsdb/samdb/samdb.h
source4/rpc_server/lsa/dcesrv_lsa.c
source4/setup/schema_samba4.ldif

index d886b517160726fbf797f42055e439b1ec892114..2600d49ec940e839d3c632a17e932a866c0ec87d 100644 (file)
@@ -653,14 +653,21 @@ interface security
        const string GUID_DRS_CHANGE_RID_MASTER       = "d58d5f36-0a98-11d1-adbb-00c04fd8d5cd";
        const string GUID_DRS_CHANGE_SCHEMA_MASTER    = "e12b56b6-0a95-11d1-adbb-00c04fd8d5cd";
        const string GUID_DRS_GET_CHANGES             = "1131f6aa-9c07-11d1-f79f-00c04fc2dcd2";
+       const string GUID_DRS_REPL_SYNCRONIZE         = "1131f6ab-9c07-11d1-f79f-00c04fc2dcd2";
+       const string GUID_DRS_MANAGE_TOPOLOGY         = "1131f6ac-9c07-11d1-f79f-00c04fc2dcd2";
        const string GUID_DRS_GET_ALL_CHANGES         = "1131f6ad-9c07-11d1-f79f-00c04fc2dcd2";
+       const string GUID_DRS_RO_REPL_SECRET_SYNC     = "1131f6ae-9c07-11d1-f79f-00c04fc2dcd2";
        const string GUID_DRS_GET_FILTERED_ATTRIBUTES = "89e95b76-444d-4c62-991a-0facbeda640c";
-       const string GUID_DRS_MANAGE_TOPOLOGY         = "1131f6ac-9c07-11d1-f79f-00c04fc2dcd2";
        const string GUID_DRS_MONITOR_TOPOLOGY        = "f98340fb-7c5b-4cdb-a00b-2ebdfa115a96";
-       const string GUID_DRS_REPL_SYNCRONIZE         = "1131f6ab-9c07-11d1-f79f-00c04fc2dcd2";
-       const string GUID_DRS_RO_REPL_SECRET_SYNC     = "1131f6ae-9c07-11d1-f79f-00c04fc2dcd2";
        const string GUID_DRS_USER_CHANGE_PASSWORD    = "ab721a53-1e2f-11d0-9819-00aa0040529b";
        const string GUID_DRS_FORCE_CHANGE_PASSWORD   = "00299570-246d-11d0-a768-00aa006e0529";
+        const string GUID_DRS_UPDATE_PASSWORD_NOT_REQUIRED_BIT
+                                                     = "280f369c-67c7-438e-ae98-1d46f3c6f541";
+        const string GUID_DRS_UNEXPIRE_PASSWORD       = "ccc2dc7d-a6ad-4a7a-8846-c04e3cc53501";
+        const string GUID_DRS_ENABLE_PER_USER_REVERSIBLY_ENCRYPTED_PASSWORD
+                                                     = "05c74c5e-4deb-43b4-bd9f-86664c2a7fd5";
+        const string GUID_DRS_DS_INSTALL_REPLICA      = "9923a32a-3607-11d2-b9be-0000f87a36b2";
+
 
        /***************************************************************/
        /* validated writes guids */
index da9c966ddd9b24a13bcdf83ec22a362b74f8d66f..20f593297ab96073afd9abb455754599a280ddd0 100644 (file)
@@ -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"
@@ -943,6 +944,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)
@@ -1037,7 +1042,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",
@@ -1134,6 +1138,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;
        }
@@ -1421,6 +1431,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/
@@ -1432,9 +1608,10 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac)
        uint32_t user_account_control, old_user_account_control, account_type;
        struct ldb_message_element *el;
        struct ldb_message *tmp_msg;
+       struct dom_sid *sid;
        int ret;
        struct ldb_result *res;
-       const char * const attrs[] = { "userAccountControl", "objectClass", NULL };
+       const char * const attrs[] = { "userAccountControl", "objectClass", "objectSid", NULL };
        unsigned int i;
        bool is_computer = false;
 
@@ -1563,6 +1740,17 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac)
                el->flags = LDB_FLAG_MOD_REPLACE;
        }
 
+       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, user_account_control,
+                                                   old_user_account_control);
+       if (ret != LDB_SUCCESS) {
+               return ret;
+       }
+
        return LDB_SUCCESS;
 }
 
index 7605c65cdd77299bdcbe52116f578292e33b9fa6..c848d871f30fcf6466670c6d9484ba764199ea37 100644 (file)
@@ -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;
index bdd0777757825f8b13ed565fc9b3c6de25982d3b..648e31dbde6d7bd4cdeb3211007491b8a6177392 100644 (file)
@@ -791,6 +791,7 @@ static NTSTATUS add_trust_user(TALLOC_CTX *mem_ctx,
                               struct trustAuthInOutBlob *in,
                               struct ldb_dn **user_dn)
 {
+       struct ldb_request *req;
        struct ldb_message *msg;
        struct ldb_dn *dn;
        uint32_t i;
@@ -851,7 +852,19 @@ static NTSTATUS add_trust_user(TALLOC_CTX *mem_ctx,
        }
 
        /* create the trusted_domain user account */
-       ret = ldb_add(sam_ldb, msg);
+       ret = ldb_build_add_req(&req, sam_ldb, mem_ctx, msg, NULL, NULL,
+                               ldb_op_default_callback, NULL);
+       if (ret != LDB_SUCCESS) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = ldb_request_add_control(req, DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
+                                     false, NULL);
+       if (ret != LDB_SUCCESS) {
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ret = dsdb_autotransaction_request(sam_ldb, req);
        if (ret != LDB_SUCCESS) {
                DEBUG(0,("Failed to create user record %s: %s\n",
                         ldb_dn_get_linearized(msg->dn),
index 94aedb099d8f24264412eae4b6db3b80279c6880..22f0bc13d0aff1e09535b74e6533142543de3871 100644 (file)
 #Allocated: DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA 1.3.6.1.4.1.7165.4.3.19.1
 #Allocated: DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID 1.3.6.1.4.1.7165.4.3.20
 #Allocated: DSDB_CONTROL_SEC_DESC_PROPAGATION_OID 1.3.6.1.4.1.7165.4.3.21
+#Allocated: DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID 1.3.6.1.4.1.7165.4.3.23
 
 # Extended 1.3.6.1.4.1.7165.4.4.x
 #Allocated: DSDB_EXTENDED_REPLICATED_OBJECTS_OID 1.3.6.1.4.1.7165.4.4.1