X-Git-Url: http://git.samba.org/samba.git/?p=bbaumbach%2Fsamba-autobuild%2F.git;a=blobdiff_plain;f=source3%2Fpassdb%2Fpdb_samba_dsdb.c;h=d70a867505d564edbdfdd60180fad1d63adeaac8;hp=febf21ceb27ed635b516b7519a4a63f637410689;hb=6f9232e26c8b4d4595c339d95977c9b1ca94a601;hpb=90f9db9c062f2953c19f62dab3fab0b61d8c8e6c diff --git a/source3/passdb/pdb_samba_dsdb.c b/source3/passdb/pdb_samba_dsdb.c index febf21ceb27..d70a867505d 100644 --- a/source3/passdb/pdb_samba_dsdb.c +++ b/source3/passdb/pdb_samba_dsdb.c @@ -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); } @@ -357,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)); @@ -434,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; @@ -464,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; } } @@ -555,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)) { @@ -634,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; @@ -705,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); @@ -859,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; @@ -923,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; @@ -990,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; } @@ -1034,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); @@ -1063,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( @@ -1438,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( @@ -1467,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); @@ -1475,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( @@ -1770,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) { @@ -1796,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; @@ -1873,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); @@ -1972,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; @@ -2020,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); @@ -2033,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; @@ -2043,33 +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)) { - talloc_free(tmp_ctx); - 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); @@ -2111,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; } @@ -2121,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, @@ -2139,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; } @@ -2152,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"; @@ -2194,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) @@ -2218,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); @@ -2225,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: @@ -2255,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; @@ -2280,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; } @@ -2322,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);