📄 cracknames.c
字号:
/* Unix SMB/CIFS implementation. endpoint server for the drsuapi pipe DsCrackNames() Copyright (C) Stefan Metzmacher 2004 Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 3 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, see <http://www.gnu.org/licenses/>.*/#include "includes.h"#include "librpc/gen_ndr/drsuapi.h"#include "rpc_server/common/common.h"#include "lib/ldb/include/ldb.h"#include "lib/ldb/include/ldb_errors.h"#include "system/kerberos.h"#include "auth/kerberos/kerberos.h"#include "libcli/ldap/ldap_ndr.h"#include "libcli/security/security.h"#include "librpc/gen_ndr/ndr_misc.h"#include "auth/auth.h"#include "util/util_ldb.h"#include "dsdb/samdb/samdb.h"#include "param/param.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, 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, struct ldb_dn *name_dn, const char *name, struct drsuapi_DsNameInfo1 *info1);static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, const char *name, struct drsuapi_DsNameInfo1 *info1) { krb5_error_code ret; krb5_principal principal; /* perhaps it's a principal with a realm, so return the right 'domain only' response */ char **realm; ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal); if (ret) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */ realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal); info1->dns_domain_name = talloc_strdup(mem_ctx, *realm); krb5_free_principal(smb_krb5_context->krb5_context, principal); W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name); info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; return WERR_OK;} 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 ret; struct ldb_result *res; struct ldb_message_element *spnmappings; TALLOC_CTX *tmp_ctx; struct ldb_dn *service_dn; char *service_dn_str; const char *directory_attrs[] = { "sPNMappings", NULL }; tmp_ctx = talloc_new(mem_ctx); if (!tmp_ctx) { return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; } service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services"); if ( ! ldb_dn_add_base(service_dn, samdb_config_dn(ldb_ctx))) { return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; } service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn); if ( ! service_dn_str) { return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; } ret = ldb_search(ldb_ctx, service_dn, LDB_SCOPE_BASE, "(objectClass=nTDSService)", directory_attrs, &res); if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx))); return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; } else if (ret == LDB_ERR_NO_SUCH_OBJECT) { DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str)); return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; } else if (res->count != 1) { talloc_free(res); DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str)); return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; } talloc_steal(tmp_ctx, res); spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings"); if (!spnmappings || spnmappings->num_values == 0) { DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str)); talloc_free(tmp_ctx); return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; } for (i = 0; i < spnmappings->num_values; i++) { char *mapping, *p, *str; mapping = talloc_strdup(tmp_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)); talloc_free(tmp_ctx); 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)); talloc_free(tmp_ctx); 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; talloc_steal(mem_ctx, mapping); talloc_free(tmp_ctx); return DRSUAPI_DS_NAME_STATUS_OK; } } while (p); } DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from)); talloc_free(tmp_ctx); return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;}/* When cracking a ServicePrincipalName, many services may be served * by the host/ servicePrincipalName. The incoming query is for cifs/ * but we translate it here, and search on host/. This is done after * the cifs/ entry has been searched for, making this a fallback */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, *dns_name; char *new_service; char *new_princ; enum drsuapi_DsNameStatus namestatus; /* parse principal */ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_NO_REALM, &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]; dns_name = principal->name.name_string.val[1]; /* 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_NOT_FOUND) { info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); if (!info1->dns_domain_name) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } return WERR_OK; } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) { info1->status = namestatus; krb5_free_principal(smb_krb5_context->krb5_context, principal); 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_flags(smb_krb5_context->krb5_context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ); if (ret) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired, new_princ, info1); free(new_princ); if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) { info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); if (!info1->dns_domain_name) { wret = WERR_NOMEM; } } krb5_free_principal(smb_krb5_context->krb5_context, principal); return wret;}/* Subcase of CrackNames, for the userPrincipalName */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){ int ldb_ret; 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; const char *domain_attrs[] = { NULL }; struct ldb_result *domain_res = NULL; /* Prevent recursion */ if (!name) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_MUST_REALM, &principal); if (ret) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } realm = krb5_princ_realm(smb_krb5_context->krb5_context, principal); ldb_ret = ldb_search_exp_fmt(sam_ctx, mem_ctx, &domain_res, samdb_partitions_dn(sam_ctx, mem_ctx), LDB_SCOPE_ONELEVEL, domain_attrs, "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", ldb_binary_encode_string(mem_ctx, *realm), ldb_binary_encode_string(mem_ctx, *realm)); if (ldb_ret != LDB_SUCCESS) { DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx))); info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } switch (domain_res->count) { case 1: break; case 0: return dns_domain_from_principal(mem_ctx, smb_krb5_context, name, info1); default: info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; return WERR_OK; } ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &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))", ldb_binary_encode_string(mem_ctx, unparsed_name_short)); domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn)); 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;}/* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */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, (struct event_context *)ldb_get_opaque(sam_ctx, "EventContext"), (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"), &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: case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: { char *str, *s, *account; if (strlen(name) == 0) { info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } str = talloc_strdup(mem_ctx, name); W_ERROR_HAVE_NO_MEMORY(str); if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) { /* Look backwards for the \n, and replace it with / */ s = strrchr(str, '\n'); if (!s) { info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } s[0] = '/'; } s = strchr(str, '/'); if (!s) { /* there must be at least one / */ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } s[0] = '\0'; s++; domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(ncName=%s))", ldb_dn_get_linearized(samdb_dns_domain_to_dn(sam_ctx, mem_ctx, str))); W_ERROR_HAVE_NO_MEMORY(domain_filter); /* There may not be anything after the domain component (search for the domain itself) */ if (s[0]) { account = strrchr(s, '/'); if (!account) { account = s; } else { account++; } account = ldb_binary_encode_string(mem_ctx, account); W_ERROR_HAVE_NO_MEMORY(account);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -