1/* 2 Unix SMB/CIFS implementation. 3 4 endpoint server for the drsuapi pipe 5 DsCrackNames() 6 7 Copyright (C) Stefan Metzmacher 2004 8 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005 9 10 This program is free software; you can redistribute it and/or modify 11 it under the terms of the GNU General Public License as published by 12 the Free Software Foundation; either version 3 of the License, or 13 (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program. If not, see <http://www.gnu.org/licenses/>. 22*/ 23 24#include "includes.h" 25#include "librpc/gen_ndr/drsuapi.h" 26#include "rpc_server/common/common.h" 27#include "lib/events/events.h" 28#include "lib/ldb/include/ldb.h" 29#include "lib/ldb/include/ldb_errors.h" 30#include "system/kerberos.h" 31#include "auth/kerberos/kerberos.h" 32#include "libcli/ldap/ldap_ndr.h" 33#include "libcli/security/security.h" 34#include "librpc/gen_ndr/ndr_misc.h" 35#include "auth/auth.h" 36#include "../lib/util/util_ldb.h" 37#include "dsdb/samdb/samdb.h" 38#include "param/param.h" 39 40static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 41 struct smb_krb5_context *smb_krb5_context, 42 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, 43 struct ldb_dn *name_dn, const char *name, 44 const char *domain_filter, const char *result_filter, 45 struct drsuapi_DsNameInfo1 *info1); 46static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx, 47 uint32_t format_offered, uint32_t format_desired, 48 struct ldb_dn *name_dn, const char *name, 49 struct drsuapi_DsNameInfo1 *info1); 50 51static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, 52 const char *name, 53 struct drsuapi_DsNameInfo1 *info1) 54{ 55 krb5_error_code ret; 56 krb5_principal principal; 57 /* perhaps it's a principal with a realm, so return the right 'domain only' response */ 58 char *realm; 59 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, 60 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal); 61 if (ret) { 62 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 63 return WERR_OK; 64 } 65 66 /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */ 67 realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal); 68 69 info1->dns_domain_name = talloc_strdup(mem_ctx, realm); 70 krb5_free_principal(smb_krb5_context->krb5_context, principal); 71 72 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name); 73 74 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; 75 return WERR_OK; 76} 77 78static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(krb5_context context, struct ldb_context *ldb_ctx, 79 TALLOC_CTX *mem_ctx, 80 const char *alias_from, 81 char **alias_to) 82{ 83 int i; 84 int ret; 85 struct ldb_result *res; 86 struct ldb_message_element *spnmappings; 87 TALLOC_CTX *tmp_ctx; 88 struct ldb_dn *service_dn; 89 char *service_dn_str; 90 91 const char *directory_attrs[] = { 92 "sPNMappings", 93 NULL 94 }; 95 96 tmp_ctx = talloc_new(mem_ctx); 97 if (!tmp_ctx) { 98 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 99 } 100 101 service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services"); 102 if ( ! ldb_dn_add_base(service_dn, samdb_config_dn(ldb_ctx))) { 103 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 104 } 105 service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn); 106 if ( ! service_dn_str) { 107 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 108 } 109 110 ret = ldb_search(ldb_ctx, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE, 111 directory_attrs, "(objectClass=nTDSService)"); 112 113 if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) { 114 DEBUG(1, ("ldb_search: dn: %s not found: %s", service_dn_str, ldb_errstring(ldb_ctx))); 115 return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 116 } else if (ret == LDB_ERR_NO_SUCH_OBJECT) { 117 DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str)); 118 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 119 } else if (res->count != 1) { 120 talloc_free(res); 121 DEBUG(1, ("ldb_search: dn: %s not found", service_dn_str)); 122 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 123 } 124 125 spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings"); 126 if (!spnmappings || spnmappings->num_values == 0) { 127 DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute", service_dn_str)); 128 talloc_free(tmp_ctx); 129 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 130 } 131 132 for (i = 0; i < spnmappings->num_values; i++) { 133 char *mapping, *p, *str; 134 mapping = talloc_strdup(tmp_ctx, 135 (const char *)spnmappings->values[i].data); 136 if (!mapping) { 137 DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str)); 138 talloc_free(tmp_ctx); 139 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 140 } 141 142 /* C string manipulation sucks */ 143 144 p = strchr(mapping, '='); 145 if (!p) { 146 DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n", 147 service_dn_str, mapping)); 148 talloc_free(tmp_ctx); 149 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 150 } 151 p[0] = '\0'; 152 p++; 153 do { 154 str = p; 155 p = strchr(p, ','); 156 if (p) { 157 p[0] = '\0'; 158 p++; 159 } 160 if (strcasecmp(str, alias_from) == 0) { 161 *alias_to = mapping; 162 talloc_steal(mem_ctx, mapping); 163 talloc_free(tmp_ctx); 164 return DRSUAPI_DS_NAME_STATUS_OK; 165 } 166 } while (p); 167 } 168 DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from)); 169 talloc_free(tmp_ctx); 170 return DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 171} 172 173/* When cracking a ServicePrincipalName, many services may be served 174 * by the host/ servicePrincipalName. The incoming query is for cifs/ 175 * but we translate it here, and search on host/. This is done after 176 * the cifs/ entry has been searched for, making this a fallback */ 177 178static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 179 struct smb_krb5_context *smb_krb5_context, 180 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, 181 const char *name, struct drsuapi_DsNameInfo1 *info1) 182{ 183 WERROR wret; 184 krb5_error_code ret; 185 krb5_principal principal; 186 const char *service, *dns_name; 187 char *new_service; 188 char *new_princ; 189 enum drsuapi_DsNameStatus namestatus; 190 191 /* parse principal */ 192 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, 193 name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); 194 if (ret) { 195 DEBUG(2, ("Could not parse principal: %s: %s", 196 name, smb_get_krb5_error_message(smb_krb5_context->krb5_context, 197 ret, mem_ctx))); 198 return WERR_NOMEM; 199 } 200 201 /* grab cifs/, http/ etc */ 202 203 /* This is checked for in callers, but be safe */ 204 if (principal->name.name_string.len < 2) { 205 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 206 return WERR_OK; 207 } 208 service = principal->name.name_string.val[0]; 209 dns_name = principal->name.name_string.val[1]; 210 211 /* MAP it */ 212 namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context, 213 sam_ctx, mem_ctx, 214 service, &new_service); 215 216 if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) { 217 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; 218 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); 219 if (!info1->dns_domain_name) { 220 krb5_free_principal(smb_krb5_context->krb5_context, principal); 221 return WERR_NOMEM; 222 } 223 return WERR_OK; 224 } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) { 225 info1->status = namestatus; 226 krb5_free_principal(smb_krb5_context->krb5_context, principal); 227 return WERR_OK; 228 } 229 230 /* ooh, very nasty playing around in the Principal... */ 231 free(principal->name.name_string.val[0]); 232 principal->name.name_string.val[0] = strdup(new_service); 233 if (!principal->name.name_string.val[0]) { 234 krb5_free_principal(smb_krb5_context->krb5_context, principal); 235 return WERR_NOMEM; 236 } 237 238 /* reform principal */ 239 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, 240 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ); 241 242 if (ret) { 243 krb5_free_principal(smb_krb5_context->krb5_context, principal); 244 return WERR_NOMEM; 245 } 246 247 wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired, 248 new_princ, info1); 249 free(new_princ); 250 if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) { 251 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; 252 info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); 253 if (!info1->dns_domain_name) { 254 wret = WERR_NOMEM; 255 } 256 } 257 krb5_free_principal(smb_krb5_context->krb5_context, principal); 258 return wret; 259} 260 261/* Subcase of CrackNames, for the userPrincipalName */ 262 263static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 264 struct smb_krb5_context *smb_krb5_context, 265 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, 266 const char *name, struct drsuapi_DsNameInfo1 *info1) 267{ 268 int ldb_ret; 269 WERROR status; 270 const char *domain_filter = NULL; 271 const char *result_filter = NULL; 272 krb5_error_code ret; 273 krb5_principal principal; 274 char *realm; 275 char *unparsed_name_short; 276 const char *domain_attrs[] = { NULL }; 277 struct ldb_result *domain_res = NULL; 278 279 /* Prevent recursion */ 280 if (!name) { 281 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 282 return WERR_OK; 283 } 284 285 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, 286 KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal); 287 if (ret) { 288 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 289 return WERR_OK; 290 } 291 292 realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal); 293 294 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res, 295 samdb_partitions_dn(sam_ctx, mem_ctx), 296 LDB_SCOPE_ONELEVEL, 297 domain_attrs, 298 "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", 299 ldb_binary_encode_string(mem_ctx, realm), 300 ldb_binary_encode_string(mem_ctx, realm)); 301 302 if (ldb_ret != LDB_SUCCESS) { 303 DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx))); 304 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 305 return WERR_OK; 306 } 307 308 switch (domain_res->count) { 309 case 1: 310 break; 311 case 0: 312 return dns_domain_from_principal(mem_ctx, smb_krb5_context, 313 name, info1); 314 default: 315 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; 316 return WERR_OK; 317 } 318 319 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, 320 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short); 321 krb5_free_principal(smb_krb5_context->krb5_context, principal); 322 323 if (ret) { 324 free(unparsed_name_short); 325 return WERR_NOMEM; 326 } 327 328 /* This may need to be extended for more userPrincipalName variations */ 329 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", 330 ldb_binary_encode_string(mem_ctx, unparsed_name_short)); 331 332 domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn)); 333 334 if (!result_filter || !domain_filter) { 335 free(unparsed_name_short); 336 return WERR_NOMEM; 337 } 338 status = DsCrackNameOneFilter(sam_ctx, mem_ctx, 339 smb_krb5_context, 340 format_flags, format_offered, format_desired, 341 NULL, unparsed_name_short, domain_filter, result_filter, 342 info1); 343 free(unparsed_name_short); 344 345 return status; 346} 347 348/* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */ 349 350WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 351 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, 352 const char *name, struct drsuapi_DsNameInfo1 *info1) 353{ 354 krb5_error_code ret; 355 const char *domain_filter = NULL; 356 const char *result_filter = NULL; 357 struct ldb_dn *name_dn = NULL; 358 359 struct smb_krb5_context *smb_krb5_context = NULL; 360 361 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 362 info1->dns_domain_name = NULL; 363 info1->result_name = NULL; 364 365 if (!name) { 366 return WERR_INVALID_PARAM; 367 } 368 369 /* TODO: - fill the correct names in all cases! 370 * - handle format_flags 371 */ 372 373 /* here we need to set the domain_filter and/or the result_filter */ 374 switch (format_offered) { 375 case DRSUAPI_DS_NAME_FORMAT_UNKNOWN: 376 { 377 int i; 378 enum drsuapi_DsNameFormat formats[] = { 379 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, 380 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL, 381 DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY, 382 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, 383 DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, 384 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX 385 }; 386 WERROR werr; 387 for (i=0; i < ARRAY_SIZE(formats); i++) { 388 werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1); 389 if (!W_ERROR_IS_OK(werr)) { 390 return werr; 391 } 392 if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND) { 393 return werr; 394 } 395 } 396 return werr; 397 } 398 399 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: 400 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: 401 { 402 char *str, *s, *account; 403 404 if (strlen(name) == 0) { 405 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 406 return WERR_OK; 407 } 408 409 str = talloc_strdup(mem_ctx, name); 410 W_ERROR_HAVE_NO_MEMORY(str); 411 412 if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) { 413 /* Look backwards for the \n, and replace it with / */ 414 s = strrchr(str, '\n'); 415 if (!s) { 416 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 417 return WERR_OK; 418 } 419 s[0] = '/'; 420 } 421 422 s = strchr(str, '/'); 423 if (!s) { 424 /* there must be at least one / */ 425 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 426 return WERR_OK; 427 } 428 429 s[0] = '\0'; 430 s++; 431 432 domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(ncName=%s))", 433 ldb_dn_get_linearized(samdb_dns_domain_to_dn(sam_ctx, mem_ctx, str))); 434 W_ERROR_HAVE_NO_MEMORY(domain_filter); 435 436 /* There may not be anything after the domain component (search for the domain itself) */ 437 if (s[0]) { 438 439 account = strrchr(s, '/'); 440 if (!account) { 441 account = s; 442 } else { 443 account++; 444 } 445 account = ldb_binary_encode_string(mem_ctx, account); 446 W_ERROR_HAVE_NO_MEMORY(account); 447 result_filter = talloc_asprintf(mem_ctx, "(name=%s)", 448 account); 449 W_ERROR_HAVE_NO_MEMORY(result_filter); 450 } 451 break; 452 } 453 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: { 454 char *p; 455 char *domain; 456 const char *account = NULL; 457 458 domain = talloc_strdup(mem_ctx, name); 459 W_ERROR_HAVE_NO_MEMORY(domain); 460 461 p = strchr(domain, '\\'); 462 if (!p) { 463 /* invalid input format */ 464 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 465 return WERR_OK; 466 } 467 p[0] = '\0'; 468 469 if (p[1]) { 470 account = &p[1]; 471 } 472 473 domain_filter = talloc_asprintf(mem_ctx, 474 "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", 475 ldb_binary_encode_string(mem_ctx, domain)); 476 W_ERROR_HAVE_NO_MEMORY(domain_filter); 477 if (account) { 478 result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)", 479 ldb_binary_encode_string(mem_ctx, account)); 480 W_ERROR_HAVE_NO_MEMORY(result_filter); 481 } 482 483 talloc_free(domain); 484 break; 485 } 486 487 /* A LDAP DN as a string */ 488 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: { 489 domain_filter = NULL; 490 name_dn = ldb_dn_new(mem_ctx, sam_ctx, name); 491 if (! ldb_dn_validate(name_dn)) { 492 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 493 return WERR_OK; 494 } 495 break; 496 } 497 498 /* A GUID as a string */ 499 case DRSUAPI_DS_NAME_FORMAT_GUID: { 500 struct GUID guid; 501 char *ldap_guid; 502 NTSTATUS nt_status; 503 domain_filter = NULL; 504 505 nt_status = GUID_from_string(name, &guid); 506 if (!NT_STATUS_IS_OK(nt_status)) { 507 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 508 return WERR_OK; 509 } 510 511 ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid); 512 if (!ldap_guid) { 513 return WERR_NOMEM; 514 } 515 result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)", 516 ldap_guid); 517 W_ERROR_HAVE_NO_MEMORY(result_filter); 518 break; 519 } 520 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: { 521 domain_filter = NULL; 522 523 result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))", 524 ldb_binary_encode_string(mem_ctx, name), 525 ldb_binary_encode_string(mem_ctx, name)); 526 W_ERROR_HAVE_NO_MEMORY(result_filter); 527 break; 528 } 529 530 /* A S-1234-5678 style string */ 531 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: { 532 struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name); 533 char *ldap_sid; 534 535 domain_filter = NULL; 536 if (!sid) { 537 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 538 return WERR_OK; 539 } 540 ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx, 541 sid); 542 if (!ldap_sid) { 543 return WERR_NOMEM; 544 } 545 result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", 546 ldap_sid); 547 W_ERROR_HAVE_NO_MEMORY(result_filter); 548 break; 549 } 550 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: { 551 krb5_principal principal; 552 char *unparsed_name; 553 554 ret = smb_krb5_init_context(mem_ctx, 555 ldb_get_event_context(sam_ctx), 556 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"), 557 &smb_krb5_context); 558 559 if (ret) { 560 return WERR_NOMEM; 561 } 562 563 /* Ensure we reject compleate junk first */ 564 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal); 565 if (ret) { 566 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 567 return WERR_OK; 568 } 569 570 domain_filter = NULL; 571 572 /* By getting the unparsed name here, we ensure the escaping is correct (and trust the client less) */ 573 ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name); 574 if (ret) { 575 krb5_free_principal(smb_krb5_context->krb5_context, principal); 576 return WERR_NOMEM; 577 } 578 579 krb5_free_principal(smb_krb5_context->krb5_context, principal); 580 581 /* The ldb_binary_encode_string() here avoid LDAP filter injection attacks */ 582 result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))", 583 ldb_binary_encode_string(mem_ctx, unparsed_name)); 584 585 free(unparsed_name); 586 W_ERROR_HAVE_NO_MEMORY(result_filter); 587 break; 588 } 589 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: { 590 krb5_principal principal; 591 char *unparsed_name_short; 592 char *service; 593 594 ret = smb_krb5_init_context(mem_ctx, 595 ldb_get_event_context(sam_ctx), 596 (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"), 597 &smb_krb5_context); 598 599 if (ret) { 600 return WERR_NOMEM; 601 } 602 603 ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal); 604 if (ret == 0 && principal->name.name_string.len < 2) { 605 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 606 krb5_free_principal(smb_krb5_context->krb5_context, principal); 607 return WERR_OK; 608 } 609 ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, 610 KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); 611 if (ret) { 612 krb5_free_principal(smb_krb5_context->krb5_context, principal); 613 614 return dns_domain_from_principal(mem_ctx, smb_krb5_context, 615 name, info1); 616 } 617 618 domain_filter = NULL; 619 620 ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, 621 KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short); 622 if (ret) { 623 krb5_free_principal(smb_krb5_context->krb5_context, principal); 624 return WERR_NOMEM; 625 } 626 627 service = principal->name.name_string.val[0]; 628 if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) { 629 /* the 'cn' attribute is just the leading part of the name */ 630 char *computer_name; 631 computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1], 632 strcspn(principal->name.name_string.val[1], ".")); 633 if (computer_name == NULL) { 634 return WERR_NOMEM; 635 } 636 637 result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))", 638 ldb_binary_encode_string(mem_ctx, unparsed_name_short), 639 ldb_binary_encode_string(mem_ctx, computer_name)); 640 } else { 641 result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))", 642 ldb_binary_encode_string(mem_ctx, unparsed_name_short)); 643 } 644 krb5_free_principal(smb_krb5_context->krb5_context, principal); 645 free(unparsed_name_short); 646 W_ERROR_HAVE_NO_MEMORY(result_filter); 647 648 break; 649 } 650 default: { 651 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 652 return WERR_OK; 653 } 654 655 } 656 657 if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) { 658 return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired, 659 name_dn, name, info1); 660 } 661 662 return DsCrackNameOneFilter(sam_ctx, mem_ctx, 663 smb_krb5_context, 664 format_flags, format_offered, format_desired, 665 name_dn, name, 666 domain_filter, result_filter, 667 info1); 668} 669 670/* Subcase of CrackNames. It is possible to translate a LDAP-style DN 671 * (FQDN_1779) into a canoical name without actually searching the 672 * database */ 673 674static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx, 675 uint32_t format_offered, uint32_t format_desired, 676 struct ldb_dn *name_dn, const char *name, 677 struct drsuapi_DsNameInfo1 *info1) 678{ 679 char *cracked; 680 if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) { 681 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING; 682 return WERR_OK; 683 } 684 685 switch (format_desired) { 686 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: 687 cracked = ldb_dn_canonical_string(mem_ctx, name_dn); 688 break; 689 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: 690 cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn); 691 break; 692 default: 693 info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING; 694 return WERR_OK; 695 } 696 info1->status = DRSUAPI_DS_NAME_STATUS_OK; 697 info1->result_name = cracked; 698 if (!cracked) { 699 return WERR_NOMEM; 700 } 701 702 return WERR_OK; 703} 704 705/* Given a filter for the domain, and one for the result, perform the 706 * ldb search. The format offered and desired flags change the 707 * behaviours, including what attributes to return. 708 * 709 * The smb_krb5_context is required because we use the krb5 libs for principal parsing 710 */ 711 712static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, 713 struct smb_krb5_context *smb_krb5_context, 714 uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, 715 struct ldb_dn *name_dn, const char *name, 716 const char *domain_filter, const char *result_filter, 717 struct drsuapi_DsNameInfo1 *info1) 718{ 719 int ldb_ret; 720 struct ldb_result *domain_res = NULL; 721 const char * const *domain_attrs; 722 const char * const *result_attrs; 723 struct ldb_message **result_res = NULL; 724 struct ldb_message *result = NULL; 725 struct ldb_dn *result_basedn = NULL; 726 int i; 727 char *p; 728 struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx); 729 730 const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL}; 731 const char * const _result_attrs_null[] = { NULL }; 732 733 const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL}; 734 const char * const _result_attrs_canonical[] = { "canonicalName", NULL }; 735 736 const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL}; 737 const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL}; 738 739 const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL}; 740 const char * const _result_attrs_guid[] = { "objectGUID", NULL}; 741 742 const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL}; 743 const char * const _result_attrs_display[] = { "displayName", "samAccountName", NULL}; 744 745 const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL}; 746 const char * const _result_attrs_none[] = { NULL}; 747 748 /* here we need to set the attrs lists for domain and result lookups */ 749 switch (format_desired) { 750 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: 751 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: 752 domain_attrs = _domain_attrs_1779; 753 result_attrs = _result_attrs_null; 754 break; 755 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: 756 domain_attrs = _domain_attrs_canonical; 757 result_attrs = _result_attrs_canonical; 758 break; 759 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: 760 domain_attrs = _domain_attrs_nt4; 761 result_attrs = _result_attrs_nt4; 762 break; 763 case DRSUAPI_DS_NAME_FORMAT_GUID: 764 domain_attrs = _domain_attrs_guid; 765 result_attrs = _result_attrs_guid; 766 break; 767 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: 768 domain_attrs = _domain_attrs_display; 769 result_attrs = _result_attrs_display; 770 break; 771 default: 772 domain_attrs = _domain_attrs_none; 773 result_attrs = _result_attrs_none; 774 break; 775 } 776 777 if (domain_filter) { 778 /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */ 779 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res, 780 partitions_basedn, 781 LDB_SCOPE_ONELEVEL, 782 domain_attrs, 783 "%s", domain_filter); 784 785 if (ldb_ret != LDB_SUCCESS) { 786 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx))); 787 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 788 return WERR_OK; 789 } 790 791 switch (domain_res->count) { 792 case 1: 793 break; 794 case 0: 795 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 796 return WERR_OK; 797 default: 798 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; 799 return WERR_OK; 800 } 801 802 info1->dns_domain_name = samdb_result_string(domain_res->msgs[0], "dnsRoot", NULL); 803 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name); 804 info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; 805 } else { 806 info1->dns_domain_name = NULL; 807 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 808 } 809 810 if (result_filter) { 811 int ret; 812 struct ldb_result *res; 813 if (domain_res) { 814 result_basedn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL); 815 816 ret = ldb_search(sam_ctx, mem_ctx, &res, 817 result_basedn, LDB_SCOPE_SUBTREE, 818 result_attrs, "%s", result_filter); 819 if (ret != LDB_SUCCESS) { 820 talloc_free(result_res); 821 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 822 return WERR_OK; 823 } 824 ldb_ret = res->count; 825 result_res = res->msgs; 826 } else { 827 /* search with the 'phantom root' flag */ 828 struct ldb_request *req; 829 830 res = talloc_zero(mem_ctx, struct ldb_result); 831 W_ERROR_HAVE_NO_MEMORY(res); 832 833 ret = ldb_build_search_req(&req, sam_ctx, mem_ctx, 834 ldb_get_root_basedn(sam_ctx), 835 LDB_SCOPE_SUBTREE, 836 result_filter, 837 result_attrs, 838 NULL, 839 res, 840 ldb_search_default_callback, 841 NULL); 842 if (ret == LDB_SUCCESS) { 843 struct ldb_search_options_control *search_options; 844 search_options = talloc(req, struct ldb_search_options_control); 845 W_ERROR_HAVE_NO_MEMORY(search_options); 846 search_options->search_options = LDB_SEARCH_OPTION_PHANTOM_ROOT; 847 848 ret = ldb_request_add_control(req, LDB_CONTROL_SEARCH_OPTIONS_OID, false, search_options); 849 } 850 if (ret != LDB_SUCCESS) { 851 talloc_free(res); 852 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 853 return WERR_OK; 854 } 855 856 ret = ldb_request(sam_ctx, req); 857 858 if (ret == LDB_SUCCESS) { 859 ret = ldb_wait(req->handle, LDB_WAIT_ALL); 860 } 861 862 talloc_free(req); 863 864 if (ret != LDB_SUCCESS) { 865 DEBUG(2, ("DsCrackNameOneFilter phantom root search failed: %s", 866 ldb_errstring(sam_ctx))); 867 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 868 return WERR_OK; 869 } 870 ldb_ret = res->count; 871 result_res = res->msgs; 872 } 873 } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) { 874 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res, 875 result_attrs); 876 } else if (domain_res) { 877 name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL); 878 ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res, 879 result_attrs); 880 } else { 881 /* Can't happen */ 882 DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not availible: This can't happen...")); 883 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 884 return WERR_OK; 885 } 886 887 switch (ldb_ret) { 888 case 1: 889 result = result_res[0]; 890 break; 891 case 0: 892 switch (format_offered) { 893 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: 894 return DsCrackNameSPNAlias(sam_ctx, mem_ctx, 895 smb_krb5_context, 896 format_flags, format_offered, format_desired, 897 name, info1); 898 899 case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: 900 return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context, 901 format_flags, format_offered, format_desired, 902 name, info1); 903 } 904 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 905 return WERR_OK; 906 case -1: 907 DEBUG(2, ("DsCrackNameOneFilter result search failed: %s", ldb_errstring(sam_ctx))); 908 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 909 return WERR_OK; 910 default: 911 switch (format_offered) { 912 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: 913 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: 914 { 915 const char *canonical_name = NULL; /* Not required, but we get warnings... */ 916 /* We may need to manually filter further */ 917 for (i = 0; i < ldb_ret; i++) { 918 switch (format_offered) { 919 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: 920 canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn); 921 break; 922 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: 923 canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn); 924 break; 925 } 926 if (strcasecmp_m(canonical_name, name) == 0) { 927 result = result_res[i]; 928 break; 929 } 930 } 931 if (!result) { 932 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 933 return WERR_OK; 934 } 935 } 936 default: 937 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; 938 return WERR_OK; 939 } 940 } 941 942 info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn); 943 W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name); 944 p = strchr(info1->dns_domain_name, '/'); 945 if (p) { 946 p[0] = '\0'; 947 } 948 949 /* here we can use result and domain_res[0] */ 950 switch (format_desired) { 951 case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: { 952 info1->result_name = ldb_dn_alloc_linearized(mem_ctx, result->dn); 953 W_ERROR_HAVE_NO_MEMORY(info1->result_name); 954 955 info1->status = DRSUAPI_DS_NAME_STATUS_OK; 956 return WERR_OK; 957 } 958 case DRSUAPI_DS_NAME_FORMAT_CANONICAL: { 959 info1->result_name = samdb_result_string(result, "canonicalName", NULL); 960 info1->status = DRSUAPI_DS_NAME_STATUS_OK; 961 return WERR_OK; 962 } 963 case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: { 964 /* Not in the virtual ldb attribute */ 965 return DsCrackNameOneSyntactical(mem_ctx, 966 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, 967 DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX, 968 result->dn, name, info1); 969 } 970 case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: { 971 972 const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid"); 973 const char *_acc = "", *_dom = ""; 974 975 if (samdb_find_attribute(sam_ctx, result, "objectClass", "domain")) { 976 977 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res, 978 partitions_basedn, 979 LDB_SCOPE_ONELEVEL, 980 domain_attrs, 981 "(ncName=%s)", ldb_dn_get_linearized(result->dn)); 982 983 if (ldb_ret != LDB_SUCCESS) { 984 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx))); 985 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 986 return WERR_OK; 987 } 988 989 switch (domain_res->count) { 990 case 1: 991 break; 992 case 0: 993 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 994 return WERR_OK; 995 default: 996 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; 997 return WERR_OK; 998 } 999 _dom = samdb_result_string(domain_res->msgs[0], "nETBIOSName", NULL); 1000 W_ERROR_HAVE_NO_MEMORY(_dom); 1001 } else { 1002 _acc = samdb_result_string(result, "sAMAccountName", NULL); 1003 if (!_acc) { 1004 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING; 1005 return WERR_OK; 1006 } 1007 if (dom_sid_in_domain(dom_sid_parse_talloc(mem_ctx, SID_BUILTIN), sid)) { 1008 _dom = "BUILTIN"; 1009 } else { 1010 const char *attrs[] = { NULL }; 1011 struct ldb_result *domain_res2; 1012 struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid); 1013 if (!dom_sid) { 1014 return WERR_OK; 1015 } 1016 dom_sid->num_auths--; 1017 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res, 1018 NULL, 1019 LDB_SCOPE_BASE, 1020 attrs, 1021 "(&(objectSid=%s)(objectClass=domain))", 1022 ldap_encode_ndr_dom_sid(mem_ctx, dom_sid)); 1023 1024 if (ldb_ret != LDB_SUCCESS) { 1025 DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s", ldb_errstring(sam_ctx))); 1026 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 1027 return WERR_OK; 1028 } 1029 1030 switch (domain_res->count) { 1031 case 1: 1032 break; 1033 case 0: 1034 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 1035 return WERR_OK; 1036 default: 1037 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; 1038 return WERR_OK; 1039 } 1040 1041 ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res2, 1042 partitions_basedn, 1043 LDB_SCOPE_ONELEVEL, 1044 domain_attrs, 1045 "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn)); 1046 1047 if (ldb_ret != LDB_SUCCESS) { 1048 DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s", ldb_errstring(sam_ctx))); 1049 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 1050 return WERR_OK; 1051 } 1052 1053 switch (domain_res2->count) { 1054 case 1: 1055 break; 1056 case 0: 1057 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 1058 return WERR_OK; 1059 default: 1060 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; 1061 return WERR_OK; 1062 } 1063 _dom = samdb_result_string(domain_res2->msgs[0], "nETBIOSName", NULL); 1064 W_ERROR_HAVE_NO_MEMORY(_dom); 1065 } 1066 } 1067 1068 info1->result_name = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc); 1069 W_ERROR_HAVE_NO_MEMORY(info1->result_name); 1070 1071 info1->status = DRSUAPI_DS_NAME_STATUS_OK; 1072 return WERR_OK; 1073 } 1074 case DRSUAPI_DS_NAME_FORMAT_GUID: { 1075 struct GUID guid; 1076 1077 guid = samdb_result_guid(result, "objectGUID"); 1078 1079 info1->result_name = GUID_string2(mem_ctx, &guid); 1080 W_ERROR_HAVE_NO_MEMORY(info1->result_name); 1081 1082 info1->status = DRSUAPI_DS_NAME_STATUS_OK; 1083 return WERR_OK; 1084 } 1085 case DRSUAPI_DS_NAME_FORMAT_DISPLAY: { 1086 info1->result_name = samdb_result_string(result, "displayName", NULL); 1087 if (!info1->result_name) { 1088 info1->result_name = samdb_result_string(result, "sAMAccountName", NULL); 1089 } 1090 if (!info1->result_name) { 1091 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; 1092 } else { 1093 info1->status = DRSUAPI_DS_NAME_STATUS_OK; 1094 } 1095 return WERR_OK; 1096 } 1097 case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: { 1098 info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; 1099 return WERR_OK; 1100 } 1101 case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: 1102 case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: { 1103 info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; 1104 return WERR_OK; 1105 } 1106 default: 1107 info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING; 1108 return WERR_OK; 1109 } 1110} 1111 1112/* Given a user Principal Name (such as foo@bar.com), 1113 * return the user and domain DNs. This is used in the KDC to then 1114 * return the Keys and evaluate policy */ 1115 1116NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx, 1117 TALLOC_CTX *mem_ctx, 1118 const char *user_principal_name, 1119 struct ldb_dn **user_dn, 1120 struct ldb_dn **domain_dn) 1121{ 1122 WERROR werr; 1123 struct drsuapi_DsNameInfo1 info1; 1124 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0, 1125 DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, 1126 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, 1127 user_principal_name, 1128 &info1); 1129 if (!W_ERROR_IS_OK(werr)) { 1130 return werror_to_ntstatus(werr); 1131 } 1132 switch (info1.status) { 1133 case DRSUAPI_DS_NAME_STATUS_OK: 1134 break; 1135 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND: 1136 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY: 1137 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE: 1138 return NT_STATUS_NO_SUCH_USER; 1139 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR: 1140 default: 1141 return NT_STATUS_UNSUCCESSFUL; 1142 } 1143 1144 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name); 1145 1146 if (domain_dn) { 1147 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0, 1148 DRSUAPI_DS_NAME_FORMAT_CANONICAL, 1149 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, 1150 talloc_asprintf(mem_ctx, "%s/", 1151 info1.dns_domain_name), 1152 &info1); 1153 if (!W_ERROR_IS_OK(werr)) { 1154 return werror_to_ntstatus(werr); 1155 } 1156 switch (info1.status) { 1157 case DRSUAPI_DS_NAME_STATUS_OK: 1158 break; 1159 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND: 1160 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY: 1161 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE: 1162 return NT_STATUS_NO_SUCH_USER; 1163 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR: 1164 default: 1165 return NT_STATUS_UNSUCCESSFUL; 1166 } 1167 1168 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name); 1169 } 1170 1171 return NT_STATUS_OK; 1172} 1173 1174/* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM), 1175 * return the user and domain DNs. This is used in the KDC to then 1176 * return the Keys and evaluate policy */ 1177 1178NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx, 1179 TALLOC_CTX *mem_ctx, 1180 const char *service_principal_name, 1181 struct ldb_dn **user_dn, 1182 struct ldb_dn **domain_dn) 1183{ 1184 WERROR werr; 1185 struct drsuapi_DsNameInfo1 info1; 1186 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0, 1187 DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, 1188 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, 1189 service_principal_name, 1190 &info1); 1191 if (!W_ERROR_IS_OK(werr)) { 1192 return werror_to_ntstatus(werr); 1193 } 1194 switch (info1.status) { 1195 case DRSUAPI_DS_NAME_STATUS_OK: 1196 break; 1197 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND: 1198 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY: 1199 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE: 1200 return NT_STATUS_NO_SUCH_USER; 1201 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR: 1202 default: 1203 return NT_STATUS_UNSUCCESSFUL; 1204 } 1205 1206 *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name); 1207 1208 if (domain_dn) { 1209 werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0, 1210 DRSUAPI_DS_NAME_FORMAT_CANONICAL, 1211 DRSUAPI_DS_NAME_FORMAT_FQDN_1779, 1212 talloc_asprintf(mem_ctx, "%s/", 1213 info1.dns_domain_name), 1214 &info1); 1215 if (!W_ERROR_IS_OK(werr)) { 1216 return werror_to_ntstatus(werr); 1217 } 1218 switch (info1.status) { 1219 case DRSUAPI_DS_NAME_STATUS_OK: 1220 break; 1221 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND: 1222 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY: 1223 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE: 1224 return NT_STATUS_NO_SUCH_USER; 1225 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR: 1226 default: 1227 return NT_STATUS_UNSUCCESSFUL; 1228 } 1229 1230 *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name); 1231 } 1232 1233 return NT_STATUS_OK; 1234} 1235 1236NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx, 1237 struct tevent_context *ev_ctx, 1238 struct loadparm_context *lp_ctx, 1239 uint32_t format_offered, 1240 const char *name, 1241 const char **nt4_domain, const char **nt4_account) 1242{ 1243 WERROR werr; 1244 struct drsuapi_DsNameInfo1 info1; 1245 struct ldb_context *ldb; 1246 char *p; 1247 1248 /* Handle anonymous bind */ 1249 if (!name || !*name) { 1250 *nt4_domain = ""; 1251 *nt4_account = ""; 1252 return NT_STATUS_OK; 1253 } 1254 1255 ldb = samdb_connect(mem_ctx, ev_ctx, lp_ctx, system_session(mem_ctx, lp_ctx)); 1256 if (ldb == NULL) { 1257 return NT_STATUS_INTERNAL_DB_CORRUPTION; 1258 } 1259 1260 werr = DsCrackNameOneName(ldb, mem_ctx, 0, 1261 format_offered, 1262 DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, 1263 name, 1264 &info1); 1265 if (!W_ERROR_IS_OK(werr)) { 1266 return werror_to_ntstatus(werr); 1267 } 1268 switch (info1.status) { 1269 case DRSUAPI_DS_NAME_STATUS_OK: 1270 break; 1271 case DRSUAPI_DS_NAME_STATUS_NOT_FOUND: 1272 case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY: 1273 case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE: 1274 return NT_STATUS_NO_SUCH_USER; 1275 case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR: 1276 default: 1277 return NT_STATUS_UNSUCCESSFUL; 1278 } 1279 1280 *nt4_domain = talloc_strdup(mem_ctx, info1.result_name); 1281 if (*nt4_domain == NULL) { 1282 return NT_STATUS_NO_MEMORY; 1283 } 1284 1285 p = strchr(*nt4_domain, '\\'); 1286 if (!p) { 1287 return NT_STATUS_INVALID_PARAMETER; 1288 } 1289 p[0] = '\0'; 1290 1291 if (p[1]) { 1292 *nt4_account = talloc_strdup(mem_ctx, &p[1]); 1293 if (*nt4_account == NULL) { 1294 return NT_STATUS_NO_MEMORY; 1295 } 1296 } 1297 1298 return NT_STATUS_OK; 1299} 1300 1301NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx, 1302 struct tevent_context *ev_ctx, 1303 struct loadparm_context *lp_ctx, 1304 const char *name, 1305 const char **nt4_domain, 1306 const char **nt4_account) 1307{ 1308 uint32_t format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN; 1309 1310 /* Handle anonymous bind */ 1311 if (!name || !*name) { 1312 *nt4_domain = ""; 1313 *nt4_account = ""; 1314 return NT_STATUS_OK; 1315 } 1316 1317 if (strchr_m(name, '=')) { 1318 format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779; 1319 } else if (strchr_m(name, '@')) { 1320 format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL; 1321 } else if (strchr_m(name, '\\')) { 1322 format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT; 1323 } else if (strchr_m(name, '/')) { 1324 format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL; 1325 } else { 1326 return NT_STATUS_NO_SUCH_USER; 1327 } 1328 1329 return crack_name_to_nt4_name(mem_ctx, ev_ctx, lp_ctx, format_offered, name, nt4_domain, nt4_account); 1330} 1331