pdb_samba_dsdb: implement pdb_samba_dsdb_enum_trusteddoms()
[bbaumbach/samba-autobuild/.git] / source3 / passdb / pdb_samba_dsdb.c
index 01e747a00fc71ea054681fb69f2c39ac861ce4c3..d70a867505d564edbdfdd60180fad1d63adeaac8 100644 (file)
@@ -28,6 +28,8 @@
 #include "libcli/security/dom_sid.h"
 #include "source4/winbind/idmap.h"
 #include "librpc/gen_ndr/ndr_security.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "librpc/gen_ndr/ndr_lsa.h"
 #include "libds/common/flag_mapping.h"
 #include "source4/lib/events/events.h"
 #include "source4/auth/session.h"
@@ -35,6 +37,9 @@
 #include "lib/param/param.h"
 #include "source4/dsdb/common/util.h"
 #include "source3/include/secrets.h"
+#include "source4/auth/auth_sam.h"
+#include "auth/credentials/credentials.h"
+#include "lib/util/base64.h"
 
 struct pdb_samba_dsdb_state {
        struct tevent_context *ev;
@@ -259,9 +264,13 @@ static NTSTATUS pdb_samba_dsdb_init_sam_from_priv(struct pdb_methods *m,
                pdb_set_workstations(sam, str, PDB_SET);
        }
 
-       str = ldb_msg_find_attr_as_string(msg, "userParameters",
-                                           NULL);
-       if (str != NULL) {
+       blob = ldb_msg_find_ldb_val(msg, "userParameters");
+       if (blob != NULL) {
+               str = base64_encode_data_blob(frame, *blob);
+               if (str == NULL) {
+                       DEBUG(0, ("base64_encode_data_blob() failed\n"));
+                       goto fail;
+               }
                pdb_set_munged_dial(sam, str, PDB_SET);
        }
 
@@ -339,6 +348,7 @@ static int pdb_samba_dsdb_replace_by_sam(struct pdb_samba_dsdb_state *state,
 
        msg = ldb_msg_new(frame);
        if (!msg) {
+               talloc_free(frame);
                return false;
        }
 
@@ -356,7 +366,7 @@ static int pdb_samba_dsdb_replace_by_sam(struct pdb_samba_dsdb_state *state,
        /* If we set a plaintext password, the system will
         * force the pwdLastSet to now() */
        if (need_update(sam, PDB_PASSLASTSET)) {
-               dsdb_flags = DSDB_PASSWORD_BYPASS_LAST_SET;
+               dsdb_flags |= DSDB_PASSWORD_BYPASS_LAST_SET;
 
                ret |= pdb_samba_dsdb_add_time(msg, "pwdLastSet",
                                           pdb_get_pass_last_set_time(sam));
@@ -375,6 +385,7 @@ static int pdb_samba_dsdb_replace_by_sam(struct pdb_samba_dsdb_state *state,
                                           pw, strlen(pw),
                                           (void *)&pw_utf16.data,
                                           &pw_utf16.length)) {
+                       talloc_free(frame);
                        return LDB_ERR_OPERATIONS_ERROR;
                }
                ret |= ldb_msg_add_value(msg, "clearTextPassword", &pw_utf16, NULL);
@@ -432,10 +443,10 @@ static int pdb_samba_dsdb_replace_by_sam(struct pdb_samba_dsdb_state *state,
                                invalid_history = true;
                        } else {
                                unsigned int i;
-                               static const uint8_t zeros[16];
                                /* Parse the history into the correct format */
                                for (i = 0; i < current_hist_len; i++) {
-                                       if (memcmp(&history[i*PW_HISTORY_ENTRY_LEN], zeros, 16) != 0) {
+                                       if (!all_zero(&history[i*PW_HISTORY_ENTRY_LEN],
+                                                     16)) {
                                                /* If the history is in the old format, with a salted hash, then we can't migrate it to AD format */
                                                invalid_history = true;
                                                break;
@@ -462,7 +473,7 @@ static int pdb_samba_dsdb_replace_by_sam(struct pdb_samba_dsdb_state *state,
                }
                if (changed_lm_pw || changed_nt_pw || changed_history) {
                        /* These attributes can only be modified directly by using a special control */
-                       dsdb_flags = DSDB_BYPASS_PASSWORD_HASH;
+                       dsdb_flags |= DSDB_BYPASS_PASSWORD_HASH;
                }
        }
 
@@ -553,8 +564,25 @@ static int pdb_samba_dsdb_replace_by_sam(struct pdb_samba_dsdb_state *state,
 
        /* This will need work, it is actually a UTF8 'string' with internal NULLs, to handle TS parameters */
        if (need_update(sam, PDB_MUNGEDDIAL)) {
-               ret |= ldb_msg_add_string(msg, "userParameters",
-                                         pdb_get_munged_dial(sam));
+               const char *base64_munged_dial = NULL;
+
+               base64_munged_dial = pdb_get_munged_dial(sam);
+               if (base64_munged_dial != NULL && strlen(base64_munged_dial) > 0) {
+                       struct ldb_val blob;
+
+                       blob = base64_decode_data_blob_talloc(msg,
+                                                       base64_munged_dial);
+                       if (blob.data == NULL) {
+                               DEBUG(0, ("Failed to decode userParameters from "
+                                         "munged dialback string[%s] for %s\n",
+                                         base64_munged_dial,
+                                         ldb_dn_get_linearized(msg->dn)));
+                               talloc_free(frame);
+                               return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
+                       }
+                       ret |= ldb_msg_add_steal_value(msg, "userParameters",
+                                                      &blob);
+               }
        }
 
        if (need_update(sam, PDB_COUNTRY_CODE)) {
@@ -632,7 +660,13 @@ static NTSTATUS pdb_samba_dsdb_getsamupriv(struct pdb_samba_dsdb_state *state,
 static NTSTATUS pdb_samba_dsdb_getsampwfilter(struct pdb_methods *m,
                                          struct pdb_samba_dsdb_state *state,
                                          struct samu *sam_acct,
-                                         const char *exp_fmt, ...) _PRINTF_ATTRIBUTE(4, 5)
+                                         const char *exp_fmt, ...)
+                                         PRINTF_ATTRIBUTE(4,5);
+
+static NTSTATUS pdb_samba_dsdb_getsampwfilter(struct pdb_methods *m,
+                                         struct pdb_samba_dsdb_state *state,
+                                         struct samu *sam_acct,
+                                         const char *exp_fmt, ...)
 {
        struct ldb_message *priv;
        NTSTATUS status;
@@ -703,8 +737,8 @@ static NTSTATUS pdb_samba_dsdb_getsampwsid(struct pdb_methods *m,
 
 static NTSTATUS pdb_samba_dsdb_create_user(struct pdb_methods *m,
                                    TALLOC_CTX *mem_ctx,
-                                   const char *name, uint32 acct_flags,
-                                   uint32 *rid)
+                                   const char *name, uint32_t acct_flags,
+                                   uint32_t *rid)
 {
        struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
                m->private_data, struct pdb_samba_dsdb_state);
@@ -857,12 +891,17 @@ static NTSTATUS pdb_samba_dsdb_update_login_attempts(struct pdb_methods *m,
        return NT_STATUS_NOT_IMPLEMENTED;
 }
 
+static NTSTATUS pdb_samba_dsdb_getgrfilter(struct pdb_methods *m,
+                                          GROUP_MAP *map,
+                                          const char *exp_fmt, ...)
+                                          PRINTF_ATTRIBUTE(3,4);
+
 static NTSTATUS pdb_samba_dsdb_getgrfilter(struct pdb_methods *m, GROUP_MAP *map,
-                                   const char *exp_fmt, ...) _PRINTF_ATTRIBUTE(4, 5)
+                                   const char *exp_fmt, ...)
 {
        struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
                m->private_data, struct pdb_samba_dsdb_state);
-       const char *attrs[] = { "objectSid", "description", "samAccountName", "groupType",
+       const char *attrs[] = { "objectClass", "objectSid", "description", "samAccountName", "groupType",
                                NULL };
        struct ldb_message *msg;
        va_list ap;
@@ -921,15 +960,13 @@ static NTSTATUS pdb_samba_dsdb_getgrfilter(struct pdb_methods *m, GROUP_MAP *map
                        return NT_STATUS_INTERNAL_DB_CORRUPTION;
                }
 
-               map->sid_name_use = SID_NAME_DOM_GRP;
-
                ZERO_STRUCT(id_map);
                id_map.sid = sid;
                id_maps[0] = &id_map;
                id_maps[1] = NULL;
 
                status = idmap_sids_to_xids(state->idmap_ctx, tmp_ctx, id_maps);
-               talloc_free(tmp_ctx);
+
                if (!NT_STATUS_IS_OK(status)) {
                        talloc_free(tmp_ctx);
                        return status;
@@ -988,7 +1025,7 @@ static NTSTATUS pdb_samba_dsdb_getgrsid(struct pdb_methods *m, GROUP_MAP *map,
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = pdb_samba_dsdb_getgrfilter(m, map, filter);
+       status = pdb_samba_dsdb_getgrfilter(m, map, "%s", filter);
        TALLOC_FREE(filter);
        return status;
 }
@@ -1011,6 +1048,7 @@ static NTSTATUS pdb_samba_dsdb_getgrgid(struct pdb_methods *m, GROUP_MAP *map,
 
        status = idmap_xids_to_sids(state->idmap_ctx, tmp_ctx, id_maps);
        if (!NT_STATUS_IS_OK(status)) {
+               talloc_free(tmp_ctx);
                return status;
        }
        status = pdb_samba_dsdb_getgrsid(m, map, *id_map.sid);
@@ -1031,14 +1069,14 @@ static NTSTATUS pdb_samba_dsdb_getgrnam(struct pdb_methods *m, GROUP_MAP *map,
                return NT_STATUS_NO_MEMORY;
        }
 
-       status = pdb_samba_dsdb_getgrfilter(m, map, filter);
+       status = pdb_samba_dsdb_getgrfilter(m, map, "%s", filter);
        TALLOC_FREE(filter);
        return status;
 }
 
 static NTSTATUS pdb_samba_dsdb_create_dom_group(struct pdb_methods *m,
                                         TALLOC_CTX *mem_ctx, const char *name,
-                                        uint32 *rid)
+                                        uint32_t *rid)
 {
        struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
                m->private_data, struct pdb_samba_dsdb_state);
@@ -1060,7 +1098,7 @@ static NTSTATUS pdb_samba_dsdb_create_dom_group(struct pdb_methods *m,
 }
 
 static NTSTATUS pdb_samba_dsdb_delete_dom_group(struct pdb_methods *m,
-                                        TALLOC_CTX *mem_ctx, uint32 rid)
+                                        TALLOC_CTX *mem_ctx, uint32_t rid)
 {
        const char *attrs[] = { NULL };
        struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
@@ -1435,7 +1473,7 @@ static NTSTATUS pdb_samba_dsdb_mod_groupmem_by_sid(struct pdb_methods *m,
 
 static NTSTATUS pdb_samba_dsdb_mod_groupmem(struct pdb_methods *m,
                                     TALLOC_CTX *mem_ctx,
-                                    uint32 grouprid, uint32 memberrid,
+                                    uint32_t grouprid, uint32_t memberrid,
                                     int mod_op)
 {
        struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
@@ -1464,7 +1502,7 @@ static NTSTATUS pdb_samba_dsdb_mod_groupmem(struct pdb_methods *m,
 
 static NTSTATUS pdb_samba_dsdb_add_groupmem(struct pdb_methods *m,
                                     TALLOC_CTX *mem_ctx,
-                                    uint32 group_rid, uint32 member_rid)
+                                    uint32_t group_rid, uint32_t member_rid)
 {
        return pdb_samba_dsdb_mod_groupmem(m, mem_ctx, group_rid, member_rid,
                                    LDB_FLAG_MOD_ADD);
@@ -1472,14 +1510,14 @@ static NTSTATUS pdb_samba_dsdb_add_groupmem(struct pdb_methods *m,
 
 static NTSTATUS pdb_samba_dsdb_del_groupmem(struct pdb_methods *m,
                                     TALLOC_CTX *mem_ctx,
-                                    uint32 group_rid, uint32 member_rid)
+                                    uint32_t group_rid, uint32_t member_rid)
 {
        return pdb_samba_dsdb_mod_groupmem(m, mem_ctx, group_rid, member_rid,
                                       LDB_FLAG_MOD_DELETE);
 }
 
 static NTSTATUS pdb_samba_dsdb_create_alias(struct pdb_methods *m,
-                                    const char *name, uint32 *rid)
+                                    const char *name, uint32_t *rid)
 {
        TALLOC_CTX *frame = talloc_stackframe();
        struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
@@ -1521,6 +1559,7 @@ static NTSTATUS pdb_samba_dsdb_delete_alias(struct pdb_methods *m,
 
        if (ldb_transaction_start(state->ldb) != LDB_SUCCESS) {
                DEBUG(0, ("Failed to start transaction in dsdb_add_domain_alias(): %s\n", ldb_errstring(state->ldb)));
+               talloc_free(tmp_ctx);
                return NT_STATUS_INTERNAL_ERROR;
        }
 
@@ -1542,15 +1581,18 @@ static NTSTATUS pdb_samba_dsdb_delete_alias(struct pdb_methods *m,
                DEBUG(10, ("ldb_delete failed %s\n",
                           ldb_errstring(state->ldb)));
                ldb_transaction_cancel(state->ldb);
+               talloc_free(tmp_ctx);
                return NT_STATUS_LDAP(rc);
        }
 
        if (ldb_transaction_commit(state->ldb) != LDB_SUCCESS) {
                DEBUG(0, ("Failed to commit transaction in pdb_samba_dsdb_delete_alias(): %s\n",
                          ldb_errstring(state->ldb)));
+               talloc_free(tmp_ctx);
                return NT_STATUS_INTERNAL_ERROR;
        }
 
+       talloc_free(tmp_ctx);
        return NT_STATUS_OK;
 }
 
@@ -1763,7 +1805,7 @@ static NTSTATUS pdb_samba_dsdb_enum_alias_memberships(struct pdb_methods *m,
 static NTSTATUS pdb_samba_dsdb_lookup_rids(struct pdb_methods *m,
                                    const struct dom_sid *domain_sid,
                                    int num_rids,
-                                   uint32 *rids,
+                                   uint32_t *rids,
                                    const char **names,
                                    enum lsa_SidType *lsa_attrs)
 {
@@ -1789,7 +1831,7 @@ static NTSTATUS pdb_samba_dsdb_lookup_names(struct pdb_methods *m,
                                     const struct dom_sid *domain_sid,
                                     int num_names,
                                     const char **pp_names,
-                                    uint32 *rids,
+                                    uint32_t *rids,
                                     enum lsa_SidType *attrs)
 {
        return NT_STATUS_NOT_IMPLEMENTED;
@@ -1866,10 +1908,16 @@ static void pdb_samba_dsdb_search_end(struct pdb_search *search)
        talloc_free(state);
 }
 
+static bool pdb_samba_dsdb_search_filter(struct pdb_methods *m,
+                                        struct pdb_search *search,
+                                        struct pdb_samba_dsdb_search_state **pstate,
+                                        const char *exp_fmt, ...)
+                                        PRINTF_ATTRIBUTE(4, 5);
+
 static bool pdb_samba_dsdb_search_filter(struct pdb_methods *m,
                                     struct pdb_search *search,
                                     struct pdb_samba_dsdb_search_state **pstate,
-                                    const char *exp_fmt, ...) _PRINTF_ATTRIBUTE(4, 5)
+                                    const char *exp_fmt, ...)
 {
        struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
                m->private_data, struct pdb_samba_dsdb_state);
@@ -1965,7 +2013,7 @@ static bool pdb_samba_dsdb_search_filter(struct pdb_methods *m,
 
 static bool pdb_samba_dsdb_search_users(struct pdb_methods *m,
                                 struct pdb_search *search,
-                                uint32 acct_flags)
+                                uint32_t acct_flags)
 {
        struct pdb_samba_dsdb_search_state *sstate;
        bool ret;
@@ -2013,8 +2061,16 @@ static bool pdb_samba_dsdb_search_aliases(struct pdb_methods *m,
        return true;
 }
 
-static bool pdb_samba_dsdb_uid_to_sid(struct pdb_methods *m, uid_t uid,
-                              struct dom_sid *sid)
+/* 
+ * Instead of taking a gid or uid, this function takes a pointer to a 
+ * unixid. 
+ *
+ * This acts as an in-out variable so that the idmap functions can correctly
+ * receive ID_TYPE_BOTH, and this function ensures cache details are filled
+ * correctly rather than forcing the cache to store ID_TYPE_UID or ID_TYPE_GID. 
+ */
+static bool pdb_samba_dsdb_id_to_sid(struct pdb_methods *m, struct unixid *id,
+                                    struct dom_sid *sid)
 {
        struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
                m->private_data, struct pdb_samba_dsdb_state);
@@ -2026,8 +2082,7 @@ static bool pdb_samba_dsdb_uid_to_sid(struct pdb_methods *m, uid_t uid,
                return false;
        }
 
-       id_map.xid.id = uid;
-       id_map.xid.type = ID_TYPE_UID;
+       id_map.xid = *id;
        id_maps[0] = &id_map;
        id_maps[1] = NULL;
 
@@ -2036,32 +2091,9 @@ static bool pdb_samba_dsdb_uid_to_sid(struct pdb_methods *m, uid_t uid,
                talloc_free(tmp_ctx);
                return false;
        }
-       *sid = *id_map.sid;
-       talloc_free(tmp_ctx);
-       return true;
-}
-
-static bool pdb_samba_dsdb_gid_to_sid(struct pdb_methods *m, gid_t gid,
-                              struct dom_sid *sid)
-{
-       struct pdb_samba_dsdb_state *state = talloc_get_type_abort(
-               m->private_data, struct pdb_samba_dsdb_state);
-       NTSTATUS status;
-       struct id_map id_map;
-       struct id_map *id_maps[2];
-       TALLOC_CTX *tmp_ctx = talloc_stackframe();
-       if (!tmp_ctx) {
-               return false;
-       }
 
-       id_map.xid.id = gid;
-       id_map.xid.type = ID_TYPE_GID;
-       id_maps[0] = &id_map;
-       id_maps[1] = NULL;
-
-       status = idmap_xids_to_sids(state->idmap_ctx, tmp_ctx, id_maps);
-       if (!NT_STATUS_IS_OK(status)) {
-               return false;
+       if (id_map.xid.type != ID_TYPE_NOT_SPECIFIED) {
+               id->type = id_map.xid.type;
        }
        *sid = *id_map.sid;
        talloc_free(tmp_ctx);
@@ -2103,7 +2135,7 @@ static uint32_t pdb_samba_dsdb_capabilities(struct pdb_methods *m)
        return PDB_CAP_STORE_RIDS | PDB_CAP_ADS;
 }
 
-static bool pdb_samba_dsdb_new_rid(struct pdb_methods *m, uint32 *rid)
+static bool pdb_samba_dsdb_new_rid(struct pdb_methods *m, uint32_t *rid)
 {
        return false;
 }
@@ -2113,14 +2145,729 @@ static bool pdb_samba_dsdb_get_trusteddom_pw(struct pdb_methods *m,
                                      struct dom_sid *sid,
                                      time_t *pass_last_set_time)
 {
-       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[] = {
+               "securityIdentifier",
+               "flatName",
+               "trustPartner",
+               "trustAuthOutgoing",
+               "whenCreated",
+               "msDS-SupportedEncryptionTypes",
+               "trustAttributes",
+               "trustDirection",
+               "trustType",
+               NULL
+       };
+       struct ldb_message *msg;
+       const struct ldb_val *password_val;
+       int trust_direction_flags;
+       int trust_type;
+       int i;
+       DATA_BLOB password_utf16;
+       struct trustAuthInOutBlob password_blob;
+       struct AuthenticationInformationArray *auth_array;
+       char *password_talloc;
+       size_t password_len;
+       enum ndr_err_code ndr_err;
+       NTSTATUS status;
+       const char *netbios_domain = NULL;
+       const struct dom_sid *domain_sid = NULL;
+
+       status = dsdb_trust_search_tdo(state->ldb, domain, NULL,
+                                      attrs, tmp_ctx, &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 - %s.  "
+                         "It may not be a trusted domain.\n", domain,
+                         nt_errstr(status)));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+
+       netbios_domain = ldb_msg_find_attr_as_string(msg, "flatName", NULL);
+       if (netbios_domain == NULL) {
+               DEBUG(2, ("Trusted domain %s has to flatName defined.\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+
+       domain_sid = samdb_result_dom_sid(tmp_ctx, msg, "securityIdentifier");
+       if (domain_sid == NULL) {
+               DEBUG(2, ("Trusted domain %s has no securityIdentifier defined.\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               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.\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+
+       trust_type = ldb_msg_find_attr_as_int(msg, "trustType", 0);
+       if (trust_type == LSA_TRUST_TYPE_MIT) {
+               DEBUG(1, ("Trusted domain %s is is not an AD trust "
+                         "(trustType == LSA_TRUST_TYPE_MIT).\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+
+       password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing");
+       if (password_val == NULL) {
+               DEBUG(2, ("Failed to get trusted domain password for %s, "
+                         "attribute trustAuthOutgoing not returned.\n", domain));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+
+       ndr_err = ndr_pull_struct_blob(password_val, tmp_ctx, &password_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);
+               return false;
+       }
+
+       auth_array = &password_blob.current;
+
+       for (i=0; i < auth_array->count; i++) {
+               if (auth_array->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) {
+                       break;
+               }
+       }
+
+       if (i == auth_array->count) {
+               DEBUG(0, ("Trusted domain %s does not have a "
+                         "clear-text password stored\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+
+       password_utf16 = data_blob_const(auth_array->array[i].AuthInfo.clear.password,
+                                        auth_array->array[i].AuthInfo.clear.size);
+
+       /*
+        * In the future, make this function return a
+        * cli_credentials that can store a MD4 hash with cli_credential_set_nt_hash()
+        * but for now convert to UTF8 and fail if the string can not be converted.
+        *
+        * We can't safely convert the random strings windows uses into
+        * utf8.
+        */
+       if (!convert_string_talloc(tmp_ctx,
+                                  CH_UTF16MUNGED, CH_UTF8,
+                                  password_utf16.data, password_utf16.length,
+                                  (void *)&password_talloc,
+                                  &password_len)) {
+               DEBUG(0, ("FIXME: Could not convert password for trusted domain %s"
+                         " to UTF8. This may be a password set from Windows.\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return false;
+       }
+       *pwd = SMB_STRNDUP(password_talloc, password_len);
+       if (pass_last_set_time) {
+               *pass_last_set_time = nt_time_to_unix(auth_array->array[i].LastUpdateTime);
+       }
+
+       if (sid != NULL) {
+               sid_copy(sid, domain_sid);
+       }
+
+       TALLOC_FREE(tmp_ctx);
+       return true;
+}
+
+static NTSTATUS pdb_samba_dsdb_get_trusteddom_creds(struct pdb_methods *m,
+                                                   const char *domain,
+                                                   TALLOC_CTX *mem_ctx,
+                                                   struct cli_credentials **_creds)
+{
+       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[] = {
+               "securityIdentifier",
+               "flatName",
+               "trustPartner",
+               "trustAuthOutgoing",
+               "whenCreated",
+               "msDS-SupportedEncryptionTypes",
+               "trustAttributes",
+               "trustDirection",
+               "trustType",
+               NULL
+       };
+       struct ldb_message *msg;
+       const struct ldb_val *password_val;
+       int trust_direction_flags;
+       int trust_type;
+       int i;
+       DATA_BLOB password_utf16 = {};
+       struct samr_Password *password_nt = NULL;
+       uint32_t password_version = 0;
+       DATA_BLOB old_password_utf16 = {};
+       struct samr_Password *old_password_nt = NULL;
+       struct trustAuthInOutBlob password_blob;
+       enum ndr_err_code ndr_err;
+       NTSTATUS status;
+       time_t last_set_time = 0;
+       struct cli_credentials *creds = NULL;
+       bool ok;
+       const char *my_netbios_name = NULL;
+       const char *my_netbios_domain = NULL;
+       const char *my_dns_domain = NULL;
+       const char *netbios_domain = NULL;
+       char *account_name = NULL;
+       char *principal_name = NULL;
+       const char *dns_domain = NULL;
+
+       status = dsdb_trust_search_tdo(state->ldb, domain, NULL,
+                                      attrs, tmp_ctx, &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 - %s "
+                         "It may not be a trusted domain.\n", domain,
+                         nt_errstr(status)));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       netbios_domain = ldb_msg_find_attr_as_string(msg, "flatName", NULL);
+       if (netbios_domain == NULL) {
+               DEBUG(2, ("Trusted domain %s has to flatName defined.\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       dns_domain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL);
+
+       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.\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       trust_type = ldb_msg_find_attr_as_int(msg, "trustType", 0);
+       if (trust_type == LSA_TRUST_TYPE_MIT) {
+               DEBUG(1, ("Trusted domain %s is is not an AD trust "
+                         "(trustType == LSA_TRUST_TYPE_MIT).\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing");
+       if (password_val == NULL) {
+               DEBUG(2, ("Failed to get trusted domain password for %s, "
+                         "attribute trustAuthOutgoing not returned.\n", domain));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       ndr_err = ndr_pull_struct_blob(password_val, tmp_ctx, &password_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);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       for (i=0; i < password_blob.current.count; i++) {
+               struct AuthenticationInformation *a =
+                       &password_blob.current.array[i];
+
+               switch (a->AuthType) {
+               case TRUST_AUTH_TYPE_NONE:
+                       break;
+
+               case TRUST_AUTH_TYPE_VERSION:
+                       password_version = a->AuthInfo.version.version;
+                       break;
+
+               case TRUST_AUTH_TYPE_CLEAR:
+                       last_set_time = nt_time_to_unix(a->LastUpdateTime);
+
+                       password_utf16 = data_blob_const(a->AuthInfo.clear.password,
+                                                        a->AuthInfo.clear.size);
+                       password_nt = NULL;
+                       break;
+
+               case TRUST_AUTH_TYPE_NT4OWF:
+                       if (password_utf16.length != 0) {
+                               break;
+                       }
+
+                       last_set_time = nt_time_to_unix(a->LastUpdateTime);
+
+                       password_nt = &a->AuthInfo.nt4owf.password;
+                       break;
+               }
+       }
+
+       for (i=0; i < password_blob.previous.count; i++) {
+               struct AuthenticationInformation *a = &password_blob.previous.array[i];
+
+               switch (a->AuthType) {
+               case TRUST_AUTH_TYPE_NONE:
+                       break;
+
+               case TRUST_AUTH_TYPE_VERSION:
+                       break;
+
+               case TRUST_AUTH_TYPE_CLEAR:
+                       old_password_utf16 = data_blob_const(a->AuthInfo.clear.password,
+                                                        a->AuthInfo.clear.size);
+                       old_password_nt = NULL;
+                       break;
+
+               case TRUST_AUTH_TYPE_NT4OWF:
+                       if (old_password_utf16.length != 0) {
+                               break;
+                       }
+
+                       old_password_nt = &a->AuthInfo.nt4owf.password;
+                       break;
+               }
+       }
+
+       if (password_utf16.length == 0 && password_nt == NULL) {
+               DEBUG(0, ("Trusted domain %s does not have a "
+                         "clear-text nor nt password stored\n",
+                         domain));
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
+       }
+
+       my_netbios_name = lpcfg_netbios_name(state->lp_ctx);
+       my_netbios_domain = lpcfg_workgroup(state->lp_ctx);
+       my_dns_domain = lpcfg_dnsdomain(state->lp_ctx);
+
+       creds = cli_credentials_init(tmp_ctx);
+       if (creds == NULL) {
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ok = cli_credentials_set_workstation(creds, my_netbios_name, CRED_SPECIFIED);
+       if (!ok) {
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       ok = cli_credentials_set_domain(creds, netbios_domain, CRED_SPECIFIED);
+       if (!ok) {
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+       ok = cli_credentials_set_realm(creds, dns_domain, CRED_SPECIFIED);
+       if (!ok) {
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (my_dns_domain != NULL && dns_domain != NULL) {
+               cli_credentials_set_secure_channel_type(creds, SEC_CHAN_DNS_DOMAIN);
+               account_name = talloc_asprintf(tmp_ctx, "%s.", my_dns_domain);
+               if (account_name == NULL) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               principal_name = talloc_asprintf(tmp_ctx, "%s$@%s", my_netbios_domain,
+                                                cli_credentials_get_realm(creds));
+               if (principal_name == NULL) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else {
+               cli_credentials_set_secure_channel_type(creds, SEC_CHAN_DOMAIN);
+               account_name = talloc_asprintf(tmp_ctx, "%s$", my_netbios_domain);
+               if (account_name == NULL) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               principal_name = NULL;
+       }
+
+       ok = cli_credentials_set_username(creds, account_name, CRED_SPECIFIED);
+       if (!ok) {
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (principal_name != NULL) {
+               ok = cli_credentials_set_principal(creds, principal_name,
+                                                  CRED_SPECIFIED);
+               if (!ok) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (old_password_nt != NULL) {
+               ok = cli_credentials_set_old_nt_hash(creds, old_password_nt);
+               if (!ok) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (old_password_utf16.length > 0) {
+               ok = cli_credentials_set_old_utf16_password(creds,
+                                                           &old_password_utf16);
+               if (!ok) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (password_nt != NULL) {
+               ok = cli_credentials_set_nt_hash(creds, password_nt,
+                                                CRED_SPECIFIED);
+               if (!ok) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (password_utf16.length > 0) {
+               ok = cli_credentials_set_utf16_password(creds,
+                                                       &password_utf16,
+                                                       CRED_SPECIFIED);
+               if (!ok) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       cli_credentials_set_password_last_changed_time(creds, last_set_time);
+       cli_credentials_set_kvno(creds, password_version);
+
+       if (password_utf16.length > 0 && dns_domain != NULL) {
+               /*
+                * Force kerberos if this is an active directory domain
+                */
+               cli_credentials_set_kerberos_state(creds,
+                                                  CRED_MUST_USE_KERBEROS);
+       } else  {
+               /*
+                * TODO: we should allow krb5 with the raw nt hash.
+                */
+               cli_credentials_set_kerberos_state(creds,
+                                                  CRED_DONT_USE_KERBEROS);
+       }
+
+       *_creds = talloc_move(mem_ctx, &creds);
+       TALLOC_FREE(tmp_ctx);
+       return NT_STATUS_OK;
 }
 
 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 = dsdb_trust_search_tdo(state->ldb, domain, NULL,
+                                      attrs, tmp_ctx, &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 - %s.  "
+                         "It may not be a trusted domain.\n", domain,
+                         nt_errstr(status)));
+               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,
@@ -2131,11 +2878,89 @@ static bool pdb_samba_dsdb_del_trusteddom_pw(struct pdb_methods *m,
 
 static NTSTATUS pdb_samba_dsdb_enum_trusteddoms(struct pdb_methods *m,
                                         TALLOC_CTX *mem_ctx,
-                                        uint32 *num_domains,
-                                        struct trustdom_info ***domains)
+                                        uint32_t *_num_domains,
+                                        struct trustdom_info ***_domains)
 {
-       *num_domains = 0;
-       *domains = NULL;
+       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[] = {
+               "securityIdentifier",
+               "flatName",
+               "trustDirection",
+               NULL
+       };
+       struct ldb_result *res = NULL;
+       unsigned int i;
+       struct trustdom_info **domains = NULL;
+       NTSTATUS status;
+       uint32_t di = 0;
+
+       *_num_domains = 0;
+       *_domains = NULL;
+
+       status = dsdb_trust_search_tdos(state->ldb, NULL,
+                                       attrs, tmp_ctx, &res);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("dsdb_trust_search_tdos() - %s ", nt_errstr(status));
+               TALLOC_FREE(tmp_ctx);
+               return status;
+       }
+
+       if (res->count == 0) {
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_OK;
+       }
+
+       domains = talloc_zero_array(tmp_ctx, struct trustdom_info *,
+                                   res->count);
+       if (domains == NULL) {
+               TALLOC_FREE(tmp_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i = 0; i < res->count; i++) {
+               struct ldb_message *msg = res->msgs[i];
+               struct trustdom_info *d = NULL;
+               const char *name = NULL;
+               struct dom_sid *sid = NULL;
+               uint32_t direction;
+
+               d = talloc_zero(domains, struct trustdom_info);
+               if (d == NULL) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               name = ldb_msg_find_attr_as_string(msg, "flatName", NULL);
+               if (name == NULL) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               sid = samdb_result_dom_sid(msg, msg, "securityIdentifier");
+               if (sid == NULL) {
+                       continue;
+               }
+
+               direction = ldb_msg_find_attr_as_uint(msg, "trustDirection", 0);
+               if (!(direction & LSA_TRUST_DIRECTION_OUTBOUND)) {
+                       continue;
+               }
+
+               d->name = talloc_strdup(d, name);
+               if (d->name == NULL) {
+                       TALLOC_FREE(tmp_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               d->sid = *sid;
+
+               domains[di++] = d;
+       }
+
+       talloc_realloc(domains, domains, struct trustdom_info *, di);
+       *_domains = talloc_move(mem_ctx, &domains);
+       *_num_domains = di;
+       TALLOC_FREE(tmp_ctx);
        return NT_STATUS_OK;
 }
 
@@ -2144,6 +2969,11 @@ static bool pdb_samba_dsdb_is_responsible_for_wellknown(struct pdb_methods *m)
        return true;
 }
 
+static bool pdb_samba_dsdb_is_responsible_for_everything_else(struct pdb_methods *m)
+{
+       return true;
+}
+
 static void pdb_samba_dsdb_init_methods(struct pdb_methods *m)
 {
        m->name = "samba_dsdb";
@@ -2186,17 +3016,19 @@ static void pdb_samba_dsdb_init_methods(struct pdb_methods *m)
        m->search_users = pdb_samba_dsdb_search_users;
        m->search_groups = pdb_samba_dsdb_search_groups;
        m->search_aliases = pdb_samba_dsdb_search_aliases;
-       m->uid_to_sid = pdb_samba_dsdb_uid_to_sid;
-       m->gid_to_sid = pdb_samba_dsdb_gid_to_sid;
+       m->id_to_sid = pdb_samba_dsdb_id_to_sid;
        m->sid_to_id = pdb_samba_dsdb_sid_to_id;
        m->capabilities = pdb_samba_dsdb_capabilities;
        m->new_rid = pdb_samba_dsdb_new_rid;
        m->get_trusteddom_pw = pdb_samba_dsdb_get_trusteddom_pw;
+       m->get_trusteddom_creds = pdb_samba_dsdb_get_trusteddom_creds;
        m->set_trusteddom_pw = pdb_samba_dsdb_set_trusteddom_pw;
        m->del_trusteddom_pw = pdb_samba_dsdb_del_trusteddom_pw;
        m->enum_trusteddoms = pdb_samba_dsdb_enum_trusteddoms;
        m->is_responsible_for_wellknown =
                                pdb_samba_dsdb_is_responsible_for_wellknown;
+       m->is_responsible_for_everything_else =
+                               pdb_samba_dsdb_is_responsible_for_everything_else;
 }
 
 static void free_private_data(void **vp)
@@ -2210,6 +3042,10 @@ static void free_private_data(void **vp)
 static NTSTATUS pdb_samba_dsdb_init_secrets(struct pdb_methods *m)
 {
        struct pdb_domain_info *dom_info;
+       struct dom_sid stored_sid;
+       struct GUID stored_guid;
+       bool sid_exists_and_matches = false;
+       bool guid_exists_and_matches = false;
        bool ret;
 
        dom_info = pdb_samba_dsdb_get_domain_info(m, m);
@@ -2217,20 +3053,38 @@ static NTSTATUS pdb_samba_dsdb_init_secrets(struct pdb_methods *m)
                return NT_STATUS_UNSUCCESSFUL;
        }
 
-       secrets_clear_domain_protection(dom_info->name);
-       ret = secrets_store_domain_sid(dom_info->name,
-                                      &dom_info->sid);
-       if (!ret) {
-               goto done;
+       ret = secrets_fetch_domain_sid(dom_info->name, &stored_sid);
+       if (ret) {
+               if (dom_sid_equal(&stored_sid, &dom_info->sid)) {
+                       sid_exists_and_matches = true;
+               }
        }
-       ret = secrets_store_domain_guid(dom_info->name,
-                                       &dom_info->guid);
-       if (!ret) {
-               goto done;
+
+       if (sid_exists_and_matches == false) {
+               secrets_clear_domain_protection(dom_info->name);
+               ret = secrets_store_domain_sid(dom_info->name,
+                                              &dom_info->sid);
+               ret &= secrets_mark_domain_protected(dom_info->name);
+               if (!ret) {
+                       goto done;
+               }
        }
-       ret = secrets_mark_domain_protected(dom_info->name);
-       if (!ret) {
-               goto done;
+
+       ret = secrets_fetch_domain_guid(dom_info->name, &stored_guid);
+       if (ret) {
+               if (GUID_equal(&stored_guid, &dom_info->guid)) {
+                       guid_exists_and_matches = true;
+               }
+       }
+
+       if (guid_exists_and_matches == false) {
+               secrets_clear_domain_protection(dom_info->name);
+               ret = secrets_store_domain_guid(dom_info->name,
+                                              &dom_info->guid);
+               ret &= secrets_mark_domain_protected(dom_info->name);
+               if (!ret) {
+                       goto done;
+               }
        }
 
 done:
@@ -2247,6 +3101,8 @@ static NTSTATUS pdb_init_samba_dsdb(struct pdb_methods **pdb_method,
        struct pdb_methods *m;
        struct pdb_samba_dsdb_state *state;
        NTSTATUS status;
+       char *errstring = NULL;
+       int ret;
 
        if ( !NT_STATUS_IS_OK(status = make_pdb_method( &m )) ) {
                return status;
@@ -2272,21 +3128,20 @@ static NTSTATUS pdb_init_samba_dsdb(struct pdb_methods **pdb_method,
                goto nomem;
        }
 
-       if (location) {
-               state->ldb = samdb_connect_url(state,
-                                  state->ev,
-                                  state->lp_ctx,
-                                  system_session(state->lp_ctx),
-                                  0, location);
-       } else {
-               state->ldb = samdb_connect(state,
-                                  state->ev,
-                                  state->lp_ctx,
-                                  system_session(state->lp_ctx), 0);
+       if (location == NULL) {
+               location = "sam.ldb";
        }
 
+       ret = samdb_connect_url(state,
+                               state->ev,
+                               state->lp_ctx,
+                               system_session(state->lp_ctx),
+                               0, location,
+                               &state->ldb, &errstring);
+
        if (!state->ldb) {
-               DEBUG(0, ("samdb_connect failed\n"));
+               DEBUG(0, ("samdb_connect failed: %s: %s\n",
+                         errstring, ldb_strerror(ret)));
                status = NT_STATUS_INTERNAL_ERROR;
                goto fail;
        }
@@ -2314,8 +3169,8 @@ fail:
        return status;
 }
 
-NTSTATUS pdb_samba_dsdb_init(void);
-NTSTATUS pdb_samba_dsdb_init(void)
+NTSTATUS pdb_samba_dsdb_init(TALLOC_CTX *);
+NTSTATUS pdb_samba_dsdb_init(TALLOC_CTX *ctx)
 {
        NTSTATUS status = smb_register_passdb(PASSDB_INTERFACE_VERSION, "samba_dsdb",
                                              pdb_init_samba_dsdb);