s4:dsdb/common: supported trusted domains in samdb_set_password_sid()
authorStefan Metzmacher <metze@samba.org>
Thu, 5 Feb 2015 10:42:08 +0000 (10:42 +0000)
committerStefan Metzmacher <metze@samba.org>
Wed, 8 Jul 2015 16:38:21 +0000 (18:38 +0200)
We also need to update trustAuthIncoming of the trustedDomain object.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
source4/dsdb/common/util.c

index 5b39131f014bab21ed0ee7bf03d8e9f2038a318d..6447d06ef6a3801121be605496d007ee1308e428 100644 (file)
@@ -2079,7 +2079,7 @@ int samdb_set_password_callback(struct ldb_request *req, struct ldb_reply *ares)
  * Results: NT_STATUS_OK, NT_STATUS_INVALID_PARAMETER, NT_STATUS_UNSUCCESSFUL,
  *   NT_STATUS_WRONG_PASSWORD, NT_STATUS_PASSWORD_RESTRICTION
  */
-NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+static NTSTATUS samdb_set_password_internal(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
                            struct ldb_dn *user_dn, struct ldb_dn *domain_dn,
                            const DATA_BLOB *new_password,
                            const struct samr_Password *lmNewHash,
@@ -2087,7 +2087,8 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
                            const struct samr_Password *lmOldHash,
                            const struct samr_Password *ntOldHash,
                            enum samPwdChangeReason *reject_reason,
-                           struct samr_DomInfo1 **_dominfo)
+                           struct samr_DomInfo1 **_dominfo,
+                           bool permit_interdomain_trust)
 {
        struct ldb_message *msg;
        struct ldb_message_element *el;
@@ -2178,6 +2179,16 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
                        return NT_STATUS_NO_MEMORY;
                }
        }
+       if (permit_interdomain_trust) {
+               ret = ldb_request_add_control(req,
+                                             DSDB_CONTROL_PERMIT_INTERDOMAIN_TRUST_UAC_OID,
+                                             false, NULL);
+               if (ret != LDB_SUCCESS) {
+                       talloc_free(req);
+                       talloc_free(msg);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
        ret = ldb_request_add_control(req,
                                      DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID,
                                      true, NULL);
@@ -2263,6 +2274,25 @@ NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
        return status;
 }
 
+NTSTATUS samdb_set_password(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+                           struct ldb_dn *user_dn, struct ldb_dn *domain_dn,
+                           const DATA_BLOB *new_password,
+                           const struct samr_Password *lmNewHash,
+                           const struct samr_Password *ntNewHash,
+                           const struct samr_Password *lmOldHash,
+                           const struct samr_Password *ntOldHash,
+                           enum samPwdChangeReason *reject_reason,
+                           struct samr_DomInfo1 **_dominfo)
+{
+       return samdb_set_password_internal(ldb, mem_ctx,
+                           user_dn, domain_dn,
+                           new_password,
+                           lmNewHash, ntNewHash,
+                           lmOldHash, ntOldHash,
+                           reject_reason, _dominfo,
+                           false); /* reject trusts */
+}
+
 /*
  * Sets the user password using plaintext UTF16 (attribute "new_password") or
  * LM (attribute "lmNewHash") or NT (attribute "ntNewHash") hash. Also pass
@@ -2295,10 +2325,13 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
        TALLOC_CTX *frame = talloc_stackframe();
        NTSTATUS nt_status;
        const char * const user_attrs[] = {
+               "userAccountControl",
+               "sAMAccountName",
                NULL
        };
        struct ldb_message *user_msg = NULL;
        int ret;
+       uint32_t uac = 0;
 
        ret = ldb_transaction_start(ldb);
        if (ret != LDB_SUCCESS) {
@@ -2321,12 +2354,333 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
                return NT_STATUS_NO_SUCH_USER;
        }
 
-       nt_status = samdb_set_password(ldb, mem_ctx,
-                                      user_msg->dn, NULL,
-                                      new_password,
-                                      lmNewHash, ntNewHash,
-                                      lmOldHash, ntOldHash,
-                                      reject_reason, _dominfo);
+       uac = ldb_msg_find_attr_as_uint(user_msg, "userAccountControl", 0);
+       if (!(uac & UF_ACCOUNT_TYPE_MASK)) {
+               ldb_transaction_cancel(ldb);
+               DEBUG(1, ("samdb_set_password_sid: invalid "
+                         "userAccountControl[0x%08X] for SID[%s] DN[%s], "
+                         "returning NO_SUCH_USER\n",
+                         (unsigned)uac, dom_sid_string(frame, user_sid),
+                         ldb_dn_get_linearized(user_msg->dn)));
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_SUCH_USER;
+       }
+
+       if (uac & UF_INTERDOMAIN_TRUST_ACCOUNT) {
+               const char * const tdo_attrs[] = {
+                       "trustAuthIncoming",
+                       "trustDirection",
+                       NULL
+               };
+               struct ldb_message *tdo_msg = NULL;
+               const char *account_name = NULL;
+               uint32_t trust_direction;
+               uint32_t i;
+               const struct ldb_val *old_val = NULL;
+               struct trustAuthInOutBlob old_blob = {};
+               uint32_t old_version = 0;
+               struct AuthenticationInformation *old_version_a = NULL;
+               uint32_t _new_version = 0;
+               struct trustAuthInOutBlob new_blob = {};
+               struct ldb_val new_val = {};
+               struct timeval tv = timeval_current();
+               NTTIME now = timeval_to_nttime(&tv);
+               enum ndr_err_code ndr_err;
+
+               if (new_password == NULL && ntNewHash == NULL) {
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(1, ("samdb_set_password_sid: "
+                                 "no new password provided "
+                                 "sAMAccountName for SID[%s] DN[%s], "
+                                 "returning INVALID_PARAMETER\n",
+                                 dom_sid_string(frame, user_sid),
+                                 ldb_dn_get_linearized(user_msg->dn)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (new_password != NULL && ntNewHash != NULL) {
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(1, ("samdb_set_password_sid: "
+                                 "two new passwords provided "
+                                 "sAMAccountName for SID[%s] DN[%s], "
+                                 "returning INVALID_PARAMETER\n",
+                                 dom_sid_string(frame, user_sid),
+                                 ldb_dn_get_linearized(user_msg->dn)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (new_password != NULL && (new_password->length % 2)) {
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(2, ("samdb_set_password_sid: "
+                                 "invalid utf16 length (%zu) "
+                                 "sAMAccountName for SID[%s] DN[%s], "
+                                 "returning WRONG_PASSWORD\n",
+                                 new_password->length,
+                                 dom_sid_string(frame, user_sid),
+                                 ldb_dn_get_linearized(user_msg->dn)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
+
+               if (new_password != NULL && new_password->length >= 500) {
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(2, ("samdb_set_password_sid: "
+                                 "utf16 password too long (%zu) "
+                                 "sAMAccountName for SID[%s] DN[%s], "
+                                 "returning WRONG_PASSWORD\n",
+                                 new_password->length,
+                                 dom_sid_string(frame, user_sid),
+                                 ldb_dn_get_linearized(user_msg->dn)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_WRONG_PASSWORD;
+               }
+
+               account_name = ldb_msg_find_attr_as_string(user_msg,
+                                                       "sAMAccountName", NULL);
+               if (account_name == NULL) {
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(1, ("samdb_set_password_sid: missing "
+                                 "sAMAccountName for SID[%s] DN[%s], "
+                                 "returning NO_SUCH_USER\n",
+                                 dom_sid_string(frame, user_sid),
+                                 ldb_dn_get_linearized(user_msg->dn)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_SUCH_USER;
+               }
+
+               nt_status = dsdb_trust_search_tdo_by_type(ldb,
+                                                         SEC_CHAN_DOMAIN,
+                                                         account_name,
+                                                         tdo_attrs,
+                                                         frame, &tdo_msg);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(1, ("samdb_set_password_sid: dsdb_trust_search_tdo "
+                                 "failed(%s) for sAMAccountName[%s] SID[%s] DN[%s], "
+                                 "returning INTERNAL_DB_CORRUPTION\n",
+                                 nt_errstr(nt_status), account_name,
+                                 dom_sid_string(frame, user_sid),
+                                 ldb_dn_get_linearized(user_msg->dn)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+
+               trust_direction = ldb_msg_find_attr_as_int(tdo_msg,
+                                                          "trustDirection", 0);
+               if (!(trust_direction & LSA_TRUST_DIRECTION_INBOUND)) {
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(1, ("samdb_set_password_sid: direction[0x%08X] is "
+                                 "not inbound for sAMAccountName[%s] "
+                                 "DN[%s] TDO[%s], "
+                                 "returning INTERNAL_DB_CORRUPTION\n",
+                                 (unsigned)trust_direction,
+                                 account_name,
+                                 ldb_dn_get_linearized(user_msg->dn),
+                                 ldb_dn_get_linearized(tdo_msg->dn)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+
+               old_val = ldb_msg_find_ldb_val(tdo_msg, "trustAuthIncoming");
+               if (old_val != NULL) {
+                       ndr_err = ndr_pull_struct_blob(old_val, frame, &old_blob,
+                                       (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
+                       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                               ldb_transaction_cancel(ldb);
+                               DEBUG(1, ("samdb_set_password_sid: "
+                                         "failed(%s) to parse "
+                                         "trustAuthOutgoing sAMAccountName[%s] "
+                                         "DN[%s] TDO[%s], "
+                                         "returning INTERNAL_DB_CORRUPTION\n",
+                                         ndr_map_error2string(ndr_err),
+                                         account_name,
+                                         ldb_dn_get_linearized(user_msg->dn),
+                                         ldb_dn_get_linearized(tdo_msg->dn)));
+
+                               TALLOC_FREE(frame);
+                               return NT_STATUS_INTERNAL_DB_CORRUPTION;
+                       }
+               }
+
+               for (i = old_blob.current.count; i > 0; i--) {
+                       struct AuthenticationInformation *a =
+                               &old_blob.current.array[i - 1];
+
+                       switch (a->AuthType) {
+                       case TRUST_AUTH_TYPE_NONE:
+                               if (i == old_blob.current.count) {
+                                       /*
+                                        * remove TRUST_AUTH_TYPE_NONE at the
+                                        * end
+                                        */
+                                       old_blob.current.count--;
+                               }
+                               break;
+
+                       case TRUST_AUTH_TYPE_VERSION:
+                               old_version_a = a;
+                               old_version = a->AuthInfo.version.version;
+                               break;
+
+                       case TRUST_AUTH_TYPE_CLEAR:
+                               break;
+
+                       case TRUST_AUTH_TYPE_NT4OWF:
+                               break;
+                       }
+               }
+
+               if (new_version == NULL) {
+                       _new_version = 0;
+                       new_version = &_new_version;
+               }
+
+               if (old_version_a != NULL && *new_version != (old_version + 1)) {
+                       old_version_a->LastUpdateTime = now;
+                       old_version_a->AuthType = TRUST_AUTH_TYPE_NONE;
+               }
+
+               new_blob.count = MAX(old_blob.current.count, 2);
+               new_blob.current.array = talloc_zero_array(frame,
+                                               struct AuthenticationInformation,
+                                               new_blob.count);
+               if (new_blob.current.array == NULL) {
+                       ldb_transaction_cancel(ldb);
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               new_blob.previous.array = talloc_zero_array(frame,
+                                               struct AuthenticationInformation,
+                                               new_blob.count);
+               if (new_blob.current.array == NULL) {
+                       ldb_transaction_cancel(ldb);
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               for (i = 0; i < old_blob.current.count; i++) {
+                       struct AuthenticationInformation *o =
+                               &old_blob.current.array[i];
+                       struct AuthenticationInformation *p =
+                               &new_blob.previous.array[i];
+
+                       *p = *o;
+                       new_blob.previous.count++;
+               }
+               for (; i < new_blob.count; i++) {
+                       struct AuthenticationInformation *pi =
+                               &new_blob.previous.array[i];
+
+                       if (i == 0) {
+                               /*
+                                * new_blob.previous is still empty so
+                                * we'll do new_blob.previous = new_blob.current
+                                * below.
+                                */
+                               break;
+                       }
+
+                       pi->LastUpdateTime = now;
+                       pi->AuthType = TRUST_AUTH_TYPE_NONE;
+                       new_blob.previous.count++;
+               }
+
+               for (i = 0; i < new_blob.count; i++) {
+                       struct AuthenticationInformation *ci =
+                               &new_blob.current.array[i];
+
+                       ci->LastUpdateTime = now;
+                       switch (i) {
+                       case 0:
+                               if (ntNewHash != NULL) {
+                                       ci->AuthType = TRUST_AUTH_TYPE_NT4OWF;
+                                       ci->AuthInfo.nt4owf.password = *ntNewHash;
+                                       break;
+                               }
+
+                               ci->AuthType = TRUST_AUTH_TYPE_CLEAR;
+                               ci->AuthInfo.clear.size = new_password->length;
+                               ci->AuthInfo.clear.password = new_password->data;
+                               break;
+                       case 1:
+                               ci->AuthType = TRUST_AUTH_TYPE_VERSION;
+                               ci->AuthInfo.version.version = *new_version;
+                               break;
+                       default:
+                               ci->AuthType = TRUST_AUTH_TYPE_NONE;
+                               break;
+                       }
+
+                       new_blob.current.count++;
+               }
+
+               if (new_blob.previous.count == 0) {
+                       TALLOC_FREE(new_blob.previous.array);
+                       new_blob.previous = new_blob.current;
+               }
+
+               ndr_err = ndr_push_struct_blob(&new_val, frame, &new_blob,
+                               (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(1, ("samdb_set_password_sid: "
+                                 "failed(%s) to generate "
+                                 "trustAuthOutgoing sAMAccountName[%s] "
+                                 "DN[%s] TDO[%s], "
+                                 "returning UNSUCCESSFUL\n",
+                                 ndr_map_error2string(ndr_err),
+                                 account_name,
+                                 ldb_dn_get_linearized(user_msg->dn),
+                                 ldb_dn_get_linearized(tdo_msg->dn)));
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_UNSUCCESSFUL;
+               }
+
+               tdo_msg->num_elements = 0;
+               TALLOC_FREE(tdo_msg->elements);
+
+               ret = ldb_msg_add_empty(tdo_msg, "trustAuthIncoming",
+                                       LDB_FLAG_MOD_REPLACE, NULL);
+               if (ret != LDB_SUCCESS) {
+                       ldb_transaction_cancel(ldb);
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ret = ldb_msg_add_value(tdo_msg, "trustAuthIncoming",
+                                       &new_val, NULL);
+               if (ret != LDB_SUCCESS) {
+                       ldb_transaction_cancel(ldb);
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               ret = ldb_modify(ldb, tdo_msg);
+               if (ret != LDB_SUCCESS) {
+                       nt_status = dsdb_ldb_err_to_ntstatus(ret);
+                       ldb_transaction_cancel(ldb);
+                       DEBUG(1, ("samdb_set_password_sid: "
+                                 "failed to replace "
+                                 "trustAuthOutgoing sAMAccountName[%s] "
+                                 "DN[%s] TDO[%s], "
+                                 "%s - %s\n",
+                                 account_name,
+                                 ldb_dn_get_linearized(user_msg->dn),
+                                 ldb_dn_get_linearized(tdo_msg->dn),
+                                 nt_errstr(nt_status), ldb_errstring(ldb)));
+                       TALLOC_FREE(frame);
+                       return nt_status;
+               }
+       }
+
+       nt_status = samdb_set_password_internal(ldb, mem_ctx,
+                                               user_msg->dn, NULL,
+                                               new_password,
+                                               lmNewHash, ntNewHash,
+                                               lmOldHash, ntOldHash,
+                                               reject_reason, _dominfo,
+                                               true); /* permit trusts */
        if (!NT_STATUS_IS_OK(nt_status)) {
                ldb_transaction_cancel(ldb);
                TALLOC_FREE(frame);