+ 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;