s3:pdb_samba_dsdb: implement pdb_samba_dsdb_set_trusteddom_pw()
authorStefan Metzmacher <metze@samba.org>
Thu, 5 Feb 2015 09:26:23 +0000 (09:26 +0000)
committerGünther Deschner <gd@samba.org>
Thu, 12 Mar 2015 16:13:43 +0000 (17:13 +0100)
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Guenther Deschner <gd@samba.org>
source3/passdb/pdb_samba_dsdb.c

index 131ae2fce276ec339d9210027a9cd10462c34b2b..a933b2a491a99bd4791091fd63933ed294e6a348 100644 (file)
@@ -2569,7 +2569,284 @@ static bool pdb_samba_dsdb_set_trusteddom_pw(struct pdb_methods *m,
                                      const char* domain, const char* pwd,
                                      const struct dom_sid *sid)
 {
-       return false;
+       struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
+               m->private_data, struct pdb_samba_dsdb_state);
+       TALLOC_CTX *tmp_ctx = talloc_stackframe();
+       const char * const attrs[] = {
+               "trustAuthOutgoing",
+               "trustDirection",
+               "trustType",
+               NULL
+       };
+       struct ldb_message *msg = NULL;
+       int trust_direction_flags;
+       int trust_type;
+       int i;
+       const struct ldb_val *old_val = NULL;
+       struct trustAuthInOutBlob old_blob = {};
+       uint32_t old_version = 0;
+       uint32_t new_version = 0;
+       DATA_BLOB new_utf16 = {};
+       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;
+       NTSTATUS status;
+       bool ok;
+       int ret;
+
+       ret = ldb_transaction_start(state->ldb);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(2, ("Failed to start transaction.\n"));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+
+       ok = samdb_is_pdc(state->ldb);
+       if (!ok) {
+               DEBUG(2, ("Password changes for domain %s are only allowed on a PDC.\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       status = sam_get_results_trust(state->ldb, tmp_ctx, domain,
+                                      NULL, attrs, &msg);
+       if (!NT_STATUS_IS_OK(status)) {
+               /*
+                * This can be called to work out of a domain is
+                * trusted, rather than just to get the password
+                */
+               DEBUG(2, ("Failed to get trusted domain password for %s.  "
+                         "It may not be a trusted domain.\n", domain));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0);
+       if (!(trust_direction_flags & LSA_TRUST_DIRECTION_OUTBOUND)) {
+               DEBUG(2, ("Trusted domain %s is is not an outbound trust, can't set a password.\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       trust_type = ldb_msg_find_attr_as_int(msg, "trustType", 0);
+       switch (trust_type) {
+       case LSA_TRUST_TYPE_DOWNLEVEL:
+       case LSA_TRUST_TYPE_UPLEVEL:
+               break;
+       default:
+               DEBUG(0, ("Trusted domain %s is of type 0x%X - "
+                         "password changes are not supported\n",
+                         domain, (unsigned)trust_type));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       old_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing");
+       if (old_val != NULL) {
+               ndr_err = ndr_pull_struct_blob(old_val, tmp_ctx, &old_blob,
+                               (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob);
+               if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+                       DEBUG(0, ("Failed to get trusted domain password for %s, "
+                                 "attribute trustAuthOutgoing coult not be parsed %s.\n",
+                                 domain,
+                                 ndr_map_error2string(ndr_err)));
+                       TALLOC_FREE(tmp_ctx);
+                       ldb_transaction_cancel(state->ldb);
+                       return false;
+               }
+       }
+
+       for (i=0; i < old_blob.current.count; i++) {
+               struct AuthenticationInformation *a =
+                       &old_blob.current.array[i];
+
+               switch (a->AuthType) {
+               case TRUST_AUTH_TYPE_NONE:
+                       break;
+
+               case TRUST_AUTH_TYPE_VERSION:
+                       old_version = a->AuthInfo.version.version;
+                       break;
+
+               case TRUST_AUTH_TYPE_CLEAR:
+                       break;
+
+               case TRUST_AUTH_TYPE_NT4OWF:
+                       break;
+               }
+       }
+
+       new_version = old_version + 1;
+       ok = convert_string_talloc(tmp_ctx,
+                                  CH_UNIX, CH_UTF16,
+                                  pwd, strlen(pwd),
+                                  (void *)&new_utf16.data,
+                                  &new_utf16.length);
+       if (!ok) {
+               DEBUG(0, ("Failed to generate new_utf16 password for  domain %s\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       if (new_utf16.length < 28) {
+               DEBUG(0, ("new_utf16[%zu] version[%u] for domain %s to short.\n",
+                         new_utf16.length,
+                         (unsigned)new_version,
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+       if (new_utf16.length > 498) {
+               DEBUG(0, ("new_utf16[%zu] version[%u] for domain %s to long.\n",
+                         new_utf16.length,
+                         (unsigned)new_version,
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       new_blob.count = MAX(old_blob.current.count, 2);
+       new_blob.current.array = talloc_zero_array(tmp_ctx,
+                                       struct AuthenticationInformation,
+                                       new_blob.count);
+       if (new_blob.current.array == NULL) {
+               DEBUG(0, ("talloc_zero_array(%u) failed\n",
+                         (unsigned)new_blob.count));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+       new_blob.previous.array = talloc_zero_array(tmp_ctx,
+                                       struct AuthenticationInformation,
+                                       new_blob.count);
+       if (new_blob.current.array == NULL) {
+               DEBUG(0, ("talloc_zero_array(%u) failed\n",
+                         (unsigned)new_blob.count));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       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:
+                       ci->AuthType = TRUST_AUTH_TYPE_CLEAR;
+                       ci->AuthInfo.clear.size = new_utf16.length;
+                       ci->AuthInfo.clear.password = new_utf16.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, tmp_ctx, &new_blob,
+                       (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+               DEBUG(0, ("Failed to generate trustAuthOutgoing for "
+                         "trusted domain password for %s: %s.\n",
+                         domain, ndr_map_error2string(ndr_err)));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       msg->num_elements = 0;
+       ret = ldb_msg_add_empty(msg, "trustAuthOutgoing",
+                               LDB_FLAG_MOD_REPLACE, NULL);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0, ("ldb_msg_add_empty() failed\n"));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+       ret = ldb_msg_add_value(msg, "trustAuthOutgoing",
+                               &new_val, NULL);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0, ("ldb_msg_add_value() failed\n"));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       ret = ldb_modify(state->ldb, msg);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0, ("Failed to replace trustAuthOutgoing for "
+                         "trusted domain password for %s: %s - %s\n",
+                         domain, ldb_strerror(ret), ldb_errstring(state->ldb)));
+               TALLOC_FREE(tmp_ctx);
+               ldb_transaction_cancel(state->ldb);
+               return false;
+       }
+
+       ret = ldb_transaction_commit(state->ldb);
+       if (ret != LDB_SUCCESS) {
+               DEBUG(0, ("Failed to commit trustAuthOutgoing for "
+                         "trusted domain password for %s: %s - %s\n",
+                         domain, ldb_strerror(ret), ldb_errstring(state->ldb)));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+
+       DEBUG(1, ("Added new_version[%u] to trustAuthOutgoing for "
+                 "trusted domain password for %s.\n",
+                 (unsigned)new_version, domain));
+       TALLOC_FREE(tmp_ctx);
+       return true;
 }
 
 static bool pdb_samba_dsdb_del_trusteddom_pw(struct pdb_methods *m,