From db4b95827e4e6d13577513946bff4f956c849756 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sun, 23 Oct 2005 22:20:42 +0000 Subject: r11270: Move the core CrackNames code from rpc_server/drsuapi to dsdb/samdb. I'm sure this will not be the final resting place, but it will do for now. Use the cracknames code in auth/ for creating a server_info given a principal name only (should avoid assumtions about spliting a user@realm principal). Andrew Bartlett (This used to be commit c9d5d8e45dd7b7c99b6cf35b087bc18012f31222) --- source4/dsdb/config.mk | 1 + source4/dsdb/samdb/cracknames.c | 772 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 773 insertions(+) create mode 100644 source4/dsdb/samdb/cracknames.c (limited to 'source4/dsdb') diff --git a/source4/dsdb/config.mk b/source4/dsdb/config.mk index 59178c3cda..065eb65afa 100644 --- a/source4/dsdb/config.mk +++ b/source4/dsdb/config.mk @@ -48,6 +48,7 @@ INIT_OBJ_FILES = \ samdb/samdb.o ADD_OBJ_FILES = \ samdb/samdb_privilege.o \ + samdb/cracknames.o \ common/flag_mapping.o REQUIRED_SUBSYSTEMS = \ DCERPC_COMMON diff --git a/source4/dsdb/samdb/cracknames.c b/source4/dsdb/samdb/cracknames.c new file mode 100644 index 0000000000..6b04d05ad8 --- /dev/null +++ b/source4/dsdb/samdb/cracknames.c @@ -0,0 +1,772 @@ +/* + Unix SMB/CIFS implementation. + + endpoint server for the drsuapi pipe + DsCrackNames() + + Copyright (C) Stefan Metzmacher 2004 + Copyright (C) Andrew Bartlett 2004-2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "librpc/gen_ndr/ndr_drsuapi.h" +#include "rpc_server/dcerpc_server.h" +#include "rpc_server/common/common.h" +#include "rpc_server/drsuapi/dcesrv_drsuapi.h" +#include "lib/ldb/include/ldb.h" +#include "system/kerberos.h" +#include "auth/kerberos/kerberos.h" + +static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, + const struct ldb_dn *name_dn, const char *name, + const char *domain_filter, const char *result_filter, + struct drsuapi_DsNameInfo1 *info1); +static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx, + uint32_t format_offered, uint32_t format_desired, + const struct ldb_dn *name_dn, const char *name, + struct drsuapi_DsNameInfo1 *info1); + +static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx, + TALLOC_CTX *mem_ctx, + const char *alias_from, + char **alias_to) +{ + int i; + int count; + struct ldb_message **msg; + struct ldb_message_element *spnmappings; + struct ldb_dn *service_dn = ldb_dn_string_compose(mem_ctx, samdb_base_dn(mem_ctx), + "CN=Directory Service,CN=Windows NT" + ",CN=Services,CN=Configuration"); + char *service_dn_str = ldb_dn_linearize(mem_ctx, service_dn); + const char *directory_attrs[] = { + "sPNMappings", + NULL + }; + + count = ldb_search(ldb_ctx, service_dn, LDB_SCOPE_BASE, "(objectClass=nTDSService)", + directory_attrs, &msg); + talloc_steal(mem_ctx, msg); + + if (count < 1) { + DEBUG(1, ("ldb_search: dn: %s not found: %d", service_dn_str, count)); + return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + } else if (count > 1) { + DEBUG(1, ("ldb_search: dn: %s found %d times!", service_dn_str, count)); + return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + } + + spnmappings = ldb_msg_find_element(msg[0], "sPNMappings"); + if (!spnmappings || spnmappings->num_values == 0) { + DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str)); + return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + } + + for (i = 0; i < spnmappings->num_values; i++) { + char *mapping, *p, *str; + mapping = talloc_strdup(mem_ctx, + (const char *)spnmappings->values[i].data); + if (!mapping) { + DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str)); + return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + } + + /* C string manipulation sucks */ + + p = strchr(mapping, '='); + if (!p) { + DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n", + service_dn_str, mapping)); + return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + } + p[0] = '\0'; + p++; + do { + str = p; + p = strchr(p, ','); + if (p) { + p[0] = '\0'; + p++; + } + if (strcasecmp(str, alias_from) == 0) { + *alias_to = mapping; + return DRSUAPI_DS_NAME_STATUS_OK; + } + } while (p); + } + DEBUG(1, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from)); + return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; +} + +static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, + const char *name, struct drsuapi_DsNameInfo1 *info1) +{ + WERROR wret; + krb5_error_code ret; + krb5_principal principal; + const char *service; + char *new_service; + char *new_princ; + enum drsuapi_DsNameStatus namestatus; + + /* parse principal */ + ret = krb5_parse_name_norealm(smb_krb5_context->krb5_context, + name, &principal); + if (ret) { + DEBUG(2, ("Could not parse principal: %s: %s", + name, smb_get_krb5_error_message(smb_krb5_context->krb5_context, + ret, mem_ctx))); + return WERR_NOMEM; + } + + /* grab cifs/, http/ etc */ + + /* This is checked for in callers, but be safe */ + if (principal->name.name_string.len < 2) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + service = principal->name.name_string.val[0]; + + /* MAP it */ + namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context, + sam_ctx, mem_ctx, + service, &new_service); + + if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) { + info1->status = namestatus; + return WERR_OK; + } + + if (ret != 0) { + info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; + return WERR_OK; + } + + /* ooh, very nasty playing around in the Principal... */ + free(principal->name.name_string.val[0]); + principal->name.name_string.val[0] = strdup(new_service); + if (!principal->name.name_string.val[0]) { + krb5_free_principal(smb_krb5_context->krb5_context, principal); + return WERR_NOMEM; + } + + /* reform principal */ + ret = krb5_unparse_name_norealm(smb_krb5_context->krb5_context, principal, &new_princ); + + krb5_free_principal(smb_krb5_context->krb5_context, principal); + + if (ret) { + return WERR_NOMEM; + } + + wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired, + new_princ, info1); + free(new_princ); + return wret; +} + +static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, + const char *name, struct drsuapi_DsNameInfo1 *info1) +{ + WERROR status; + const char *domain_filter = NULL; + const char *result_filter = NULL; + krb5_error_code ret; + krb5_principal principal; + char **realm; + char *unparsed_name_short; + + /* Prevent recursion */ + if (!name) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + + ret = krb5_parse_name_mustrealm(smb_krb5_context->krb5_context, name, &principal); + if (ret) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + + domain_filter = NULL; + realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal); + domain_filter = talloc_asprintf(mem_ctx, + "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", + *realm, *realm); + ret = krb5_unparse_name_norealm(smb_krb5_context->krb5_context, principal, &unparsed_name_short); + krb5_free_principal(smb_krb5_context->krb5_context, principal); + + if (ret) { + free(unparsed_name_short); + return WERR_NOMEM; + } + + /* This may need to be extended for more userPrincipalName variations */ + result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", + unparsed_name_short); + if (!result_filter || !domain_filter) { + free(unparsed_name_short); + return WERR_NOMEM; + } + status = DsCrackNameOneFilter(sam_ctx, mem_ctx, + smb_krb5_context, + format_flags, format_offered, format_desired, + NULL, unparsed_name_short, domain_filter, result_filter, + info1); + free(unparsed_name_short); + return status; +} + +WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, + uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, + const char *name, struct drsuapi_DsNameInfo1 *info1) +{ + krb5_error_code ret; + const char *domain_filter = NULL; + const char *result_filter = NULL; + struct ldb_dn *name_dn = NULL; + + struct smb_krb5_context *smb_krb5_context; + ret = smb_krb5_init_context(mem_ctx, &smb_krb5_context); + + if (ret) { + return WERR_NOMEM; + } + + info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; + info1->dns_domain_name = NULL; + info1->result_name = NULL; + + if (!name) { + return WERR_INVALID_PARAM; + } + + /* TODO: - fill the correct names in all cases! + * - handle format_flags + */ + + /* here we need to set the domain_filter and/or the result_filter */ + switch (format_offered) { + case DRSUAPI_DS_NAME_FORMAT_CANONICAL: { + char *str; + + str = talloc_strdup(mem_ctx, name); + WERR_TALLOC_CHECK(str); + + if (strlen(str) == 0 || str[strlen(str)-1] != '/') { + info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; + return WERR_OK; + } + + str[strlen(str)-1] = '\0'; + + domain_filter = talloc_asprintf(mem_ctx, + "(&(&(&(dnsRoot=%s)(objectclass=crossRef)))(nETBIOSName=*)(ncName=*))", + str); + WERR_TALLOC_CHECK(domain_filter); + + break; + } + case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: { + char *p; + char *domain; + const char *account = NULL; + + domain = talloc_strdup(mem_ctx, name); + WERR_TALLOC_CHECK(domain); + + p = strchr(domain, '\\'); + if (!p) { + /* invalid input format */ + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + p[0] = '\0'; + + if (p[1]) { + account = &p[1]; + } + + domain_filter = talloc_asprintf(mem_ctx, + "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", + domain); + WERR_TALLOC_CHECK(domain_filter); + if (account) { + result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)", + account); + WERR_TALLOC_CHECK(result_filter); + } + + talloc_free(domain); + break; + } + case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: { + name_dn = ldb_dn_explode(mem_ctx, name); + domain_filter = NULL; + if (!name_dn) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + break; + } + case DRSUAPI_DS_NAME_FORMAT_GUID: { + struct GUID guid; + char *ldap_guid; + NTSTATUS nt_status; + domain_filter = NULL; + + nt_status = GUID_from_string(name, &guid); + if (!NT_STATUS_IS_OK(nt_status)) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + + ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid); + if (!ldap_guid) { + return WERR_NOMEM; + } + result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)", + ldap_guid); + WERR_TALLOC_CHECK(result_filter); + break; + } + case DRSUAPI_DS_NAME_FORMAT_DISPLAY: { + domain_filter = NULL; + + result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))", + name, name); + WERR_TALLOC_CHECK(result_filter); + break; + } + + case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: { + struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name); + char *ldap_sid; + + domain_filter = NULL; + if (!sid) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx, + sid); + if (!ldap_sid) { + return WERR_NOMEM; + } + result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", + ldap_sid); + WERR_TALLOC_CHECK(result_filter); + break; + } + case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: { + krb5_principal principal; + char *unparsed_name; + ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal); + if (ret) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + + domain_filter = NULL; + + ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name); + if (ret) { + krb5_free_principal(smb_krb5_context->krb5_context, principal); + return WERR_NOMEM; + } + + krb5_free_principal(smb_krb5_context->krb5_context, principal); + result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))", + unparsed_name); + + free(unparsed_name); + WERR_TALLOC_CHECK(result_filter); + break; + } + case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: { + krb5_principal principal; + char *unparsed_name_short; + ret = krb5_parse_name_norealm(smb_krb5_context->krb5_context, name, &principal); + if (ret || (principal->name.name_string.len < 2)) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + + domain_filter = NULL; + + ret = krb5_unparse_name_norealm(smb_krb5_context->krb5_context, principal, &unparsed_name_short); + krb5_free_principal(smb_krb5_context->krb5_context, principal); + if (ret) { + return WERR_NOMEM; + } + + result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(servicePrincipalName=%s))", + unparsed_name_short); + free(unparsed_name_short); + WERR_TALLOC_CHECK(result_filter); + + break; + } + default: { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + + } + + if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) { + return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired, + name_dn, name, info1); + } + + return DsCrackNameOneFilter(sam_ctx, mem_ctx, + smb_krb5_context, + format_flags, format_offered, format_desired, + name_dn, name, + domain_filter, result_filter, + info1); +} + +static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx, + uint32_t format_offered, uint32_t format_desired, + const struct ldb_dn *name_dn, const char *name, + struct drsuapi_DsNameInfo1 *info1) +{ + char *cracked; + if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) { + info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING; + return WERR_OK; + } + + switch (format_desired) { + case DRSUAPI_DS_NAME_FORMAT_CANONICAL: + cracked = ldb_dn_canonical_string(mem_ctx, name_dn); + break; + case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: + cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn); + break; + default: + info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING; + return WERR_OK; + } + info1->status = DRSUAPI_DS_NAME_STATUS_OK; + info1->result_name = cracked; + if (!cracked) { + return WERR_NOMEM; + } + + return WERR_OK; + +} + +static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, + const struct ldb_dn *name_dn, const char *name, + const char *domain_filter, const char *result_filter, + struct drsuapi_DsNameInfo1 *info1) +{ + int ldb_ret; + struct ldb_message **domain_res = NULL; + const char * const *domain_attrs; + const char * const *result_attrs; + struct ldb_message **result_res = NULL; + const struct ldb_dn *result_basedn; + + /* here we need to set the attrs lists for domain and result lookups */ + switch (format_desired) { + case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: + case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: { + const char * const _domain_attrs[] = { "ncName", "dnsRoot", NULL}; + const char * const _result_attrs[] = { NULL}; + + domain_attrs = _domain_attrs; + result_attrs = _result_attrs; + break; + } + case DRSUAPI_DS_NAME_FORMAT_CANONICAL: { + const char * const _domain_attrs[] = { "ncName", "dnsRoot", NULL}; + const char * const _result_attrs[] = { "canonicalName", NULL }; + + domain_attrs = _domain_attrs; + result_attrs = _result_attrs; + break; + } + case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: { + const char * const _domain_attrs[] = { "ncName", "dnsRoot", "nETBIOSName", NULL}; + const char * const _result_attrs[] = { "sAMAccountName", "objectSid", NULL}; + + domain_attrs = _domain_attrs; + result_attrs = _result_attrs; + break; + } + case DRSUAPI_DS_NAME_FORMAT_GUID: { + const char * const _domain_attrs[] = { "ncName", "dnsRoot", NULL}; + const char * const _result_attrs[] = { "objectGUID", NULL}; + + domain_attrs = _domain_attrs; + result_attrs = _result_attrs; + break; + } + case DRSUAPI_DS_NAME_FORMAT_DISPLAY: { + const char * const _domain_attrs[] = { "ncName", "dnsRoot", NULL}; + const char * const _result_attrs[] = { "displayName", "samAccountName", NULL}; + + domain_attrs = _domain_attrs; + result_attrs = _result_attrs; + break; + } + default: + return WERR_OK; + } + + if (domain_filter) { + /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */ + ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, domain_attrs, + "%s", domain_filter); + } else { + ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, domain_attrs, + "(ncName=%s)", ldb_dn_linearize(mem_ctx, samdb_base_dn(mem_ctx))); + } + + switch (ldb_ret) { + case 1: + break; + case 0: + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + case -1: + info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; + return WERR_OK; + default: + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; + return WERR_OK; + } + + info1->dns_domain_name = samdb_result_string(domain_res[0], "dnsRoot", NULL); + WERR_TALLOC_CHECK(info1->dns_domain_name); + info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; + + if (result_filter) { + result_basedn = samdb_result_dn(mem_ctx, domain_res[0], "ncName", NULL); + + ldb_ret = gendb_search(sam_ctx, mem_ctx, result_basedn, &result_res, + result_attrs, "%s", result_filter); + } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) { + ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res, + result_attrs); + } else { + name_dn = samdb_result_dn(mem_ctx, domain_res[0], "ncName", NULL); + ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res, + result_attrs); + } + + switch (ldb_ret) { + case 1: + break; + case 0: + switch (format_offered) { + case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: + return DsCrackNameSPNAlias(sam_ctx, mem_ctx, + smb_krb5_context, + format_flags, format_offered, format_desired, + name, info1); + + case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: + return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context, + format_flags, format_offered, format_desired, + name, info1); + } + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + case -1: + info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; + return WERR_OK; + default: + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; + return WERR_OK; + } + + /* here we can use result_res[0] and domain_res[0] */ + switch (format_desired) { + case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: { + info1->result_name = ldb_dn_linearize(mem_ctx, result_res[0]->dn); + WERR_TALLOC_CHECK(info1->result_name); + + info1->status = DRSUAPI_DS_NAME_STATUS_OK; + return WERR_OK; + } + case DRSUAPI_DS_NAME_FORMAT_CANONICAL: { + info1->result_name = samdb_result_string(result_res[0], "canonicalName", NULL); + info1->status = DRSUAPI_DS_NAME_STATUS_OK; + return WERR_OK; + } + case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: { + /* Not in the virtual ldb attribute */ + return DsCrackNameOneSyntactical(mem_ctx, + DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, + result_res[0]->dn, name, info1); + } + case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: { + const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result_res[0], "objectSid"); + const char *_acc = "", *_dom = ""; + + if ((sid->num_auths < 4) || (sid->num_auths > 5)) { + info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING; + return WERR_OK; + } + + if (sid->num_auths == 4) { + ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, domain_attrs, + "(ncName=%s)", ldb_dn_linearize(mem_ctx, result_res[0]->dn)); + if (ldb_ret != 1) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + _dom = samdb_result_string(domain_res[0], "nETBIOSName", NULL); + WERR_TALLOC_CHECK(_dom); + + } else if (sid->num_auths == 5) { + const char *attrs[] = { NULL }; + struct ldb_message **domain_res2; + struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid); + if (!dom_sid) { + return WERR_OK; + } + dom_sid->num_auths--; + ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res, attrs, + "(objectSid=%s)", ldap_encode_ndr_dom_sid(mem_ctx, dom_sid)); + if (ldb_ret != 1) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + ldb_ret = gendb_search(sam_ctx, mem_ctx, NULL, &domain_res2, domain_attrs, + "(ncName=%s)", ldb_dn_linearize(mem_ctx, domain_res[0]->dn)); + if (ldb_ret != 1) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + return WERR_OK; + } + + _dom = samdb_result_string(domain_res2[0], "nETBIOSName", NULL); + WERR_TALLOC_CHECK(_dom); + + _acc = samdb_result_string(result_res[0], "sAMAccountName", NULL); + WERR_TALLOC_CHECK(_acc); + } + + info1->result_name = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc); + WERR_TALLOC_CHECK(info1->result_name); + + info1->status = DRSUAPI_DS_NAME_STATUS_OK; + return WERR_OK; + } + case DRSUAPI_DS_NAME_FORMAT_GUID: { + struct GUID guid; + + guid = samdb_result_guid(result_res[0], "objectGUID"); + + info1->result_name = GUID_string2(mem_ctx, &guid); + WERR_TALLOC_CHECK(info1->result_name); + + info1->status = DRSUAPI_DS_NAME_STATUS_OK; + return WERR_OK; + } + case DRSUAPI_DS_NAME_FORMAT_DISPLAY: { + info1->result_name = samdb_result_string(result_res[0], "displayName", NULL); + if (!info1->result_name) { + info1->result_name = samdb_result_string(result_res[0], "sAMAccountName", NULL); + } + if (!info1->result_name) { + info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; + } else { + info1->status = DRSUAPI_DS_NAME_STATUS_OK; + } + return WERR_OK; + } + default: + return WERR_OK; + } + + return WERR_INVALID_PARAM; +} + +NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx, + TALLOC_CTX *mem_ctx, + const char *user_principal_name, + struct ldb_dn **user_dn, + struct ldb_dn **domain_dn) +{ + WERROR werr; + struct drsuapi_DsNameInfo1 info1; + werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0, + DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, + DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + user_principal_name, + &info1); + if (!W_ERROR_IS_OK(werr)) { + return werror_to_ntstatus(werr); + } + switch (info1.status) { + case DRSUAPI_DS_NAME_STATUS_OK: + break; + case DRSUAPI_DS_NAME_STATUS_NOT_FOUND: + case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY: + case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE: + return NT_STATUS_NO_SUCH_USER; + case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR: + default: + return NT_STATUS_UNSUCCESSFUL; + } + + *user_dn = ldb_dn_explode(mem_ctx, info1.result_name); + + if (domain_dn) { + werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0, + DRSUAPI_DS_NAME_FORMAT_CANONICAL, + DRSUAPI_DS_NAME_FORMAT_FQDN_1779, + talloc_asprintf(mem_ctx, "%s/", + info1.dns_domain_name), + &info1); + if (!W_ERROR_IS_OK(werr)) { + return werror_to_ntstatus(werr); + } + switch (info1.status) { + case DRSUAPI_DS_NAME_STATUS_OK: + break; + case DRSUAPI_DS_NAME_STATUS_NOT_FOUND: + case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY: + case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE: + return NT_STATUS_NO_SUCH_USER; + case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR: + default: + return NT_STATUS_UNSUCCESSFUL; + } + + *domain_dn = ldb_dn_explode(mem_ctx, info1.result_name); + } + + return NT_STATUS_OK; + +} -- cgit