Coverage Report

Created: 2025-07-23 07:04

/src/samba/source4/dsdb/samdb/cracknames.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   crachnames implementation for the drsuapi pipe
5
   DsCrackNames()
6
7
   Copyright (C) Stefan Metzmacher 2004
8
   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
9
   Copyright (C) Matthieu Patou <mat@matws.net> 2012
10
   Copyright (C) Catalyst .Net Ltd 2017
11
12
   This program is free software; you can redistribute it and/or modify
13
   it under the terms of the GNU General Public License as published by
14
   the Free Software Foundation; either version 3 of the License, or
15
   (at your option) any later version.
16
17
   This program is distributed in the hope that it will be useful,
18
   but WITHOUT ANY WARRANTY; without even the implied warranty of
19
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
   GNU General Public License for more details.
21
22
   You should have received a copy of the GNU General Public License
23
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
24
*/
25
26
#include "includes.h"
27
#include "librpc/gen_ndr/drsuapi.h"
28
#include "lib/events/events.h"
29
#include <ldb.h>
30
#include <ldb_errors.h>
31
#include "auth/kerberos/kerberos.h"
32
#include "libcli/ldap/ldap_ndr.h"
33
#include "libcli/security/security.h"
34
#include "auth/auth.h"
35
#include "../lib/util/util_ldb.h"
36
#include "dsdb/samdb/samdb.h"
37
#include "dsdb/common/util.h"
38
#include "param/param.h"
39
40
#undef strcasecmp
41
42
static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
43
           struct smb_krb5_context *smb_krb5_context,
44
           uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
45
           enum drsuapi_DsNameFormat format_desired,
46
           struct ldb_dn *name_dn, const char *name,
47
           const char *domain_filter, const char *result_filter,
48
           struct drsuapi_DsNameInfo1 *info1, int scope, struct ldb_dn *search_dn);
49
static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
50
          enum drsuapi_DsNameFormat format_offered,
51
          enum drsuapi_DsNameFormat format_desired,
52
          struct ldb_dn *name_dn, const char *name,
53
          struct drsuapi_DsNameInfo1 *info1);
54
55
static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context,
56
          const char *name,
57
          struct drsuapi_DsNameInfo1 *info1)
58
0
{
59
0
  krb5_error_code ret;
60
0
  krb5_principal principal;
61
  /* perhaps it's a principal with a realm, so return the right 'domain only' response */
62
0
  ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
63
0
            KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
64
0
  if (ret) {
65
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
66
0
    return WERR_OK;
67
0
  }
68
69
0
  info1->dns_domain_name = smb_krb5_principal_get_realm(
70
0
    mem_ctx, smb_krb5_context->krb5_context, principal);
71
0
  krb5_free_principal(smb_krb5_context->krb5_context, principal);
72
73
0
  W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
74
75
0
  info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
76
0
  return WERR_OK;
77
0
}
78
79
static enum drsuapi_DsNameStatus LDB_lookup_spn_alias(struct ldb_context *ldb_ctx,
80
                  TALLOC_CTX *mem_ctx,
81
                  const char *alias_from,
82
                  char **alias_to)
83
0
{
84
  /*
85
   * Some of the logic of this function is mirrored in find_spn_alias()
86
   * in source4/dsdb/samdb/ldb_modules/samldb.c. If you change this to
87
   * not return the first matched alias, you will need to rethink that
88
   * function too.
89
   */
90
0
  unsigned int i;
91
0
  int ret;
92
0
  struct ldb_result *res;
93
0
  struct ldb_message_element *spnmappings;
94
0
  TALLOC_CTX *tmp_ctx;
95
0
  struct ldb_dn *service_dn;
96
0
  char *service_dn_str;
97
98
0
  const char *directory_attrs[] = {
99
0
    "sPNMappings",
100
0
    NULL
101
0
  };
102
103
0
  tmp_ctx = talloc_new(mem_ctx);
104
0
  if (!tmp_ctx) {
105
0
    return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
106
0
  }
107
108
0
  service_dn = ldb_dn_new(tmp_ctx, ldb_ctx, "CN=Directory Service,CN=Windows NT,CN=Services");
109
0
  if ( ! ldb_dn_add_base(service_dn, ldb_get_config_basedn(ldb_ctx))) {
110
0
    talloc_free(tmp_ctx);
111
0
    return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
112
0
  }
113
0
  service_dn_str = ldb_dn_alloc_linearized(tmp_ctx, service_dn);
114
0
  if ( ! service_dn_str) {
115
0
    talloc_free(tmp_ctx);
116
0
    return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
117
0
  }
118
119
0
  ret = ldb_search(ldb_ctx, tmp_ctx, &res, service_dn, LDB_SCOPE_BASE,
120
0
       directory_attrs, "(objectClass=nTDSService)");
121
122
0
  if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_OBJECT) {
123
0
    DEBUG(1, ("ldb_search: dn: %s not found: %s\n", service_dn_str, ldb_errstring(ldb_ctx)));
124
0
    talloc_free(tmp_ctx);
125
0
    return DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
126
0
  } else if (ret == LDB_ERR_NO_SUCH_OBJECT) {
127
0
    DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str));
128
0
    talloc_free(tmp_ctx);
129
0
    return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
130
0
  } else if (res->count != 1) {
131
0
    DEBUG(1, ("ldb_search: dn: %s not found\n", service_dn_str));
132
0
    talloc_free(tmp_ctx);
133
0
    return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
134
0
  }
135
136
0
  spnmappings = ldb_msg_find_element(res->msgs[0], "sPNMappings");
137
0
  if (!spnmappings || spnmappings->num_values == 0) {
138
0
    DEBUG(1, ("ldb_search: dn: %s no sPNMappings attribute\n", service_dn_str));
139
0
    talloc_free(tmp_ctx);
140
0
    return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
141
0
  }
142
143
0
  for (i = 0; i < spnmappings->num_values; i++) {
144
0
    char *mapping, *p, *str;
145
0
    mapping = talloc_strdup(tmp_ctx,
146
0
          (const char *)spnmappings->values[i].data);
147
0
    if (!mapping) {
148
0
      DEBUG(1, ("LDB_lookup_spn_alias: ldb_search: dn: %s did not have an sPNMapping\n", service_dn_str));
149
0
      talloc_free(tmp_ctx);
150
0
      return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
151
0
    }
152
153
    /* C string manipulation sucks */
154
155
0
    p = strchr(mapping, '=');
156
0
    if (!p) {
157
0
      DEBUG(1, ("ldb_search: dn: %s sPNMapping malformed: %s\n",
158
0
          service_dn_str, mapping));
159
0
      talloc_free(tmp_ctx);
160
0
      return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
161
0
    }
162
0
    p[0] = '\0';
163
0
    p++;
164
0
    do {
165
0
      str = p;
166
0
      p = strchr(p, ',');
167
0
      if (p) {
168
0
        p[0] = '\0';
169
0
        p++;
170
0
      }
171
0
      if (strcasecmp(str, alias_from) == 0) {
172
0
        *alias_to = mapping;
173
0
        talloc_steal(mem_ctx, mapping);
174
0
        talloc_free(tmp_ctx);
175
0
        return DRSUAPI_DS_NAME_STATUS_OK;
176
0
      }
177
0
    } while (p);
178
0
  }
179
0
  DEBUG(4, ("LDB_lookup_spn_alias: no alias for service %s applicable\n", alias_from));
180
0
  talloc_free(tmp_ctx);
181
0
  return DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
182
0
}
183
184
/* When cracking a ServicePrincipalName, many services may be served
185
 * by the host/ servicePrincipalName.  The incoming query is for cifs/
186
 * but we translate it here, and search on host/.  This is done after
187
 * the cifs/ entry has been searched for, making this a fallback */
188
189
static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
190
          struct smb_krb5_context *smb_krb5_context,
191
          uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
192
          enum drsuapi_DsNameFormat format_desired,
193
          const char *name, struct drsuapi_DsNameInfo1 *info1)
194
0
{
195
0
  WERROR wret;
196
0
  krb5_error_code ret;
197
0
  krb5_principal principal;
198
0
  krb5_data component;
199
0
  const char *service, *dns_name;
200
0
  char *new_service;
201
0
  char *new_princ;
202
0
  enum drsuapi_DsNameStatus namestatus;
203
204
  /* parse principal */
205
0
  ret = krb5_parse_name_flags(smb_krb5_context->krb5_context,
206
0
            name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
207
0
  if (ret) {
208
0
    DEBUG(2, ("Could not parse principal: %s: %s\n",
209
0
        name, smb_get_krb5_error_message(smb_krb5_context->krb5_context,
210
0
                 ret, mem_ctx)));
211
0
    return WERR_NOT_ENOUGH_MEMORY;
212
0
  }
213
214
  /* grab cifs/, http/ etc */
215
216
0
  ret = smb_krb5_princ_component(smb_krb5_context->krb5_context,
217
0
               principal, 0, &component);
218
0
  if (ret) {
219
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
220
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
221
0
    return WERR_OK;
222
0
  }
223
0
  service = (const char *)component.data;
224
0
  ret = smb_krb5_princ_component(smb_krb5_context->krb5_context,
225
0
               principal, 1, &component);
226
0
  if (ret) {
227
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
228
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
229
0
    return WERR_OK;
230
0
  }
231
0
  dns_name = (const char *)component.data;
232
233
  /* MAP it */
234
0
  namestatus = LDB_lookup_spn_alias(sam_ctx, mem_ctx,
235
0
            service, &new_service);
236
237
0
  if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) {
238
0
    wret = WERR_OK;
239
0
    info1->status   = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
240
0
    info1->dns_domain_name  = talloc_strdup(mem_ctx, dns_name);
241
0
    if (!info1->dns_domain_name) {
242
0
      wret = WERR_NOT_ENOUGH_MEMORY;
243
0
    }
244
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
245
0
    return wret;
246
0
  } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) {
247
0
    info1->status = namestatus;
248
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
249
0
    return WERR_OK;
250
0
  }
251
252
  /* reform principal */
253
0
  new_princ = talloc_asprintf(mem_ctx, "%s/%s", new_service, dns_name);
254
0
  if (!new_princ) {
255
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
256
0
    return WERR_NOT_ENOUGH_MEMORY;
257
0
  }
258
259
0
  wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired,
260
0
          new_princ, info1);
261
0
  talloc_free(new_princ);
262
0
  if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) {
263
0
    info1->status   = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
264
0
    info1->dns_domain_name  = talloc_strdup(mem_ctx, dns_name);
265
0
    if (!info1->dns_domain_name) {
266
0
      wret = WERR_NOT_ENOUGH_MEMORY;
267
0
    }
268
0
  }
269
0
  krb5_free_principal(smb_krb5_context->krb5_context, principal);
270
0
  return wret;
271
0
}
272
273
/* Subcase of CrackNames, for the userPrincipalName */
274
275
static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
276
           struct smb_krb5_context *smb_krb5_context,
277
           uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
278
           enum drsuapi_DsNameFormat format_desired,
279
           const char *name, struct drsuapi_DsNameInfo1 *info1)
280
0
{
281
0
  int ldb_ret;
282
0
  WERROR status;
283
0
  const char *domain_filter = NULL;
284
0
  const char *result_filter = NULL;
285
0
  krb5_error_code ret;
286
0
  krb5_principal principal;
287
0
  char *realm;
288
0
  char *realm_encoded = NULL;
289
0
  char *unparsed_name_short;
290
0
  const char *unparsed_name_short_encoded = NULL;
291
0
  const char *domain_attrs[] = { NULL };
292
0
  struct ldb_result *domain_res = NULL;
293
294
  /* Prevent recursion */
295
0
  if (!name) {
296
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
297
0
    return WERR_OK;
298
0
  }
299
300
0
  ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
301
0
            KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal);
302
0
  if (ret) {
303
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
304
0
    return WERR_OK;
305
0
  }
306
307
0
  realm = smb_krb5_principal_get_realm(
308
0
    mem_ctx, smb_krb5_context->krb5_context, principal);
309
0
  if (realm == NULL) {
310
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
311
0
    return WERR_NOT_ENOUGH_MEMORY;
312
0
  }
313
314
0
  realm_encoded = ldb_binary_encode_string(mem_ctx, realm);
315
0
  if (realm_encoded == NULL) {
316
0
    TALLOC_FREE(realm);
317
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
318
0
    return WERR_NOT_ENOUGH_MEMORY;
319
0
  }
320
321
0
  ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
322
0
           samdb_partitions_dn(sam_ctx, mem_ctx),
323
0
           LDB_SCOPE_ONELEVEL,
324
0
           domain_attrs,
325
0
           "(&(objectClass=crossRef)(|(dnsRoot=%s)(netbiosName=%s))"
326
0
           "(systemFlags:"LDB_OID_COMPARATOR_AND":=%u))",
327
0
           realm_encoded,
328
0
           realm_encoded,
329
0
           SYSTEM_FLAG_CR_NTDS_DOMAIN);
330
0
  TALLOC_FREE(realm_encoded);
331
0
  TALLOC_FREE(realm);
332
333
0
  if (ldb_ret != LDB_SUCCESS) {
334
0
    DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
335
0
    info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
336
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
337
0
    return WERR_OK;
338
0
  }
339
340
0
  switch (domain_res->count) {
341
0
  case 1:
342
0
    break;
343
0
  case 0:
344
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
345
0
    return dns_domain_from_principal(mem_ctx, smb_krb5_context,
346
0
             name, info1);
347
0
  default:
348
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
349
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
350
0
    return WERR_OK;
351
0
  }
352
353
  /*
354
   * The important thing here is that a samAccountName may have
355
   * a space in it, and this must not be kerberos escaped to
356
   * match this filter, so we specify
357
   * KRB5_PRINCIPAL_UNPARSE_DISPLAY
358
   */
359
0
  ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
360
0
              KRB5_PRINCIPAL_UNPARSE_NO_REALM |
361
0
              KRB5_PRINCIPAL_UNPARSE_DISPLAY,
362
0
              &unparsed_name_short);
363
0
  krb5_free_principal(smb_krb5_context->krb5_context, principal);
364
365
0
  if (ret) {
366
0
    free(unparsed_name_short);
367
0
    return WERR_NOT_ENOUGH_MEMORY;
368
0
  }
369
370
0
  unparsed_name_short_encoded = ldb_binary_encode_string(mem_ctx, unparsed_name_short);
371
0
  if (unparsed_name_short_encoded == NULL) {
372
0
    free(unparsed_name_short);
373
0
    return WERR_NOT_ENOUGH_MEMORY;
374
0
  }
375
376
  /* This may need to be extended for more userPrincipalName variations */
377
0
  result_filter = talloc_asprintf(mem_ctx, "(&(samAccountName=%s)(objectClass=user))",
378
0
          unparsed_name_short_encoded);
379
380
0
  domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
381
382
0
  if (!result_filter || !domain_filter) {
383
0
    free(unparsed_name_short);
384
0
    return WERR_NOT_ENOUGH_MEMORY;
385
0
  }
386
0
  status = DsCrackNameOneFilter(sam_ctx, mem_ctx,
387
0
              smb_krb5_context,
388
0
              format_flags, format_offered, format_desired,
389
0
              NULL, unparsed_name_short, domain_filter, result_filter,
390
0
              info1, LDB_SCOPE_SUBTREE, NULL);
391
0
  free(unparsed_name_short);
392
393
0
  return status;
394
0
}
395
396
/*
397
 * This function will workout the filtering parameter in order to be able to do
398
 * the adapted search when the incoming format is format_functional.
399
 * This boils down to defining the search_dn (passed as pointer to ldb_dn *) and the
400
 * ldap filter request.
401
 * Main input parameters are:
402
 * * name, which is the portion of the functional name after the
403
 * first '/'.
404
 * * domain_filter, which is a ldap search filter used to find the NC DN given the
405
 * function name to crack.
406
 */
407
static WERROR get_format_functional_filtering_param(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
408
      char *name, struct drsuapi_DsNameInfo1 *info1,
409
      struct ldb_dn **psearch_dn, const char *domain_filter, const char **presult_filter)
410
0
{
411
0
  struct ldb_result *domain_res = NULL;
412
0
  const char * const domain_attrs[] = {"ncName", NULL};
413
0
  struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
414
0
  int ldb_ret;
415
0
  char *account,  *s, *result_filter = NULL;
416
0
  struct ldb_dn *search_dn = NULL;
417
418
0
  *psearch_dn = NULL;
419
0
  *presult_filter = NULL;
420
421
0
  ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
422
0
        partitions_basedn,
423
0
        LDB_SCOPE_ONELEVEL,
424
0
        domain_attrs,
425
0
        "%s", domain_filter);
426
427
0
  if (ldb_ret != LDB_SUCCESS) {
428
0
    DEBUG(2, ("DsCrackNameOne domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
429
0
    info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
430
0
    return WERR_FOOBAR;
431
0
  }
432
433
0
  if (domain_res->count == 1) {
434
0
    struct ldb_dn *tmp_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
435
0
    const char * const name_attrs[] = {"name", NULL};
436
437
0
    account = name;
438
0
    s = strchr(account, '/');
439
0
    talloc_free(domain_res);
440
0
    while(s) {
441
0
      s[0] = '\0';
442
0
      s++;
443
444
0
      ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
445
0
            tmp_dn,
446
0
            LDB_SCOPE_ONELEVEL,
447
0
            name_attrs,
448
0
            "name=%s", account);
449
450
0
      if (ldb_ret != LDB_SUCCESS) {
451
0
        DEBUG(2, ("DsCrackNameOne domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
452
0
        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
453
0
        return WERR_OK;
454
0
      }
455
0
      talloc_free(tmp_dn);
456
0
      switch (domain_res->count) {
457
0
      case 1:
458
0
        break;
459
0
      case 0:
460
0
        talloc_free(domain_res);
461
0
        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
462
0
        return WERR_OK;
463
0
      default:
464
0
        talloc_free(domain_res);
465
0
        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
466
0
        return WERR_OK;
467
0
      }
468
469
0
      tmp_dn = talloc_steal(mem_ctx, domain_res->msgs[0]->dn);
470
0
      talloc_free(domain_res);
471
0
      search_dn = tmp_dn;
472
0
      account = s;
473
0
      s = strchr(account, '/');
474
0
    }
475
0
    account = ldb_binary_encode_string(mem_ctx, account);
476
0
    W_ERROR_HAVE_NO_MEMORY(account);
477
0
    result_filter = talloc_asprintf(mem_ctx, "(name=%s)",
478
0
            account);
479
0
    W_ERROR_HAVE_NO_MEMORY(result_filter);
480
0
  }
481
0
  *psearch_dn = search_dn;
482
0
  *presult_filter = result_filter;
483
0
  return WERR_OK;
484
0
}
485
486
/* Crack a single 'name', from format_offered into format_desired, returning the result in info1 */
487
488
WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
489
        uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
490
        enum drsuapi_DsNameFormat format_desired,
491
        const char *name, struct drsuapi_DsNameInfo1 *info1)
492
0
{
493
0
  krb5_error_code ret;
494
0
  const char *domain_filter = NULL;
495
0
  const char *result_filter = NULL;
496
0
  struct ldb_dn *name_dn = NULL;
497
0
  struct ldb_dn *search_dn = NULL;
498
499
0
  struct smb_krb5_context *smb_krb5_context = NULL;
500
0
  int scope = LDB_SCOPE_SUBTREE;
501
502
0
  info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
503
0
  info1->dns_domain_name = NULL;
504
0
  info1->result_name = NULL;
505
506
0
  if (!name) {
507
0
    return WERR_INVALID_PARAMETER;
508
0
  }
509
510
  /* TODO: - fill the correct names in all cases!
511
   *       - handle format_flags
512
   */
513
0
  if (format_desired == DRSUAPI_DS_NAME_FORMAT_UNKNOWN) {
514
0
    return WERR_OK;
515
0
  }
516
  /* here we need to set the domain_filter and/or the result_filter */
517
0
  switch (format_offered) {
518
0
  case DRSUAPI_DS_NAME_FORMAT_UNKNOWN:
519
0
  {
520
0
    unsigned int i;
521
0
    enum drsuapi_DsNameFormat formats[] = {
522
0
      DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
523
0
      DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL,
524
0
      DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY,
525
0
      DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
526
0
      DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY,
527
0
      DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX
528
0
    };
529
0
    WERROR werr;
530
0
    for (i=0; i < ARRAY_SIZE(formats); i++) {
531
0
      werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1);
532
0
      if (!W_ERROR_IS_OK(werr)) {
533
0
        return werr;
534
0
      }
535
0
      if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND &&
536
0
          (formats[i] != DRSUAPI_DS_NAME_FORMAT_CANONICAL ||
537
0
           info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR))
538
0
      {
539
0
        return werr;
540
0
      }
541
0
    }
542
0
    return werr;
543
0
  }
544
545
0
  case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
546
0
  case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
547
0
  {
548
0
    char *str, *s, *account;
549
0
    const char *str_encoded = NULL;
550
0
    scope = LDB_SCOPE_ONELEVEL;
551
552
0
    if (strlen(name) == 0) {
553
0
      info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
554
0
      return WERR_OK;
555
0
    }
556
557
0
    str = talloc_strdup(mem_ctx, name);
558
0
    W_ERROR_HAVE_NO_MEMORY(str);
559
560
0
    if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) {
561
      /* Look backwards for the \n, and replace it with / */
562
0
      s = strrchr(str, '\n');
563
0
      if (!s) {
564
0
        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
565
0
        return WERR_OK;
566
0
      }
567
0
      s[0] = '/';
568
0
    }
569
570
0
    s = strchr(str, '/');
571
0
    if (!s) {
572
      /* there must be at least one / */
573
0
      info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
574
0
      return WERR_OK;
575
0
    }
576
577
0
    s[0] = '\0';
578
0
    s++;
579
580
0
    str_encoded = ldb_binary_encode_string(mem_ctx, str);
581
0
    if (str_encoded == NULL) {
582
0
      return WERR_NOT_ENOUGH_MEMORY;
583
0
    }
584
585
0
    domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(dnsRoot=%s)(systemFlags:%s:=%u))",
586
0
            str_encoded,
587
0
            LDB_OID_COMPARATOR_AND,
588
0
            SYSTEM_FLAG_CR_NTDS_DOMAIN);
589
0
    W_ERROR_HAVE_NO_MEMORY(domain_filter);
590
591
    /* There may not be anything after the domain component (search for the domain itself) */
592
0
    account = s;
593
0
    if (account && *account) {
594
0
      WERROR werr = get_format_functional_filtering_param(sam_ctx,
595
0
                    mem_ctx,
596
0
                    account,
597
0
                    info1,
598
0
                    &search_dn,
599
0
                    domain_filter,
600
0
                    &result_filter);
601
0
      if (!W_ERROR_IS_OK(werr)) {
602
0
        return werr;
603
0
      }
604
0
      if (info1->status != DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR)
605
0
        return WERR_OK;
606
0
    }
607
0
    break;
608
0
  }
609
0
  case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
610
0
    char *p;
611
0
    char *domain;
612
0
    char *domain_encoded = NULL;
613
0
    const char *account = NULL;
614
615
0
    domain = talloc_strdup(mem_ctx, name);
616
0
    W_ERROR_HAVE_NO_MEMORY(domain);
617
618
0
    p = strchr(domain, '\\');
619
0
    if (!p) {
620
      /* invalid input format */
621
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
622
0
      return WERR_OK;
623
0
    }
624
0
    p[0] = '\0';
625
626
0
    if (p[1]) {
627
0
      account = &p[1];
628
0
    }
629
630
0
    domain_encoded = ldb_binary_encode_string(mem_ctx, domain);
631
0
    if (domain_encoded == NULL) {
632
0
      return WERR_NOT_ENOUGH_MEMORY;
633
0
    }
634
635
0
    domain_filter = talloc_asprintf(mem_ctx,
636
0
            "(&(objectClass=crossRef)(netbiosName=%s)(systemFlags:%s:=%u))",
637
0
            domain_encoded,
638
0
            LDB_OID_COMPARATOR_AND,
639
0
            SYSTEM_FLAG_CR_NTDS_DOMAIN);
640
0
    W_ERROR_HAVE_NO_MEMORY(domain_filter);
641
0
    if (account) {
642
0
      const char *account_encoded = NULL;
643
644
0
      account_encoded = ldb_binary_encode_string(mem_ctx, account);
645
0
      if (account_encoded == NULL) {
646
0
        return WERR_NOT_ENOUGH_MEMORY;
647
0
      }
648
649
0
      result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)",
650
0
              account_encoded);
651
0
      W_ERROR_HAVE_NO_MEMORY(result_filter);
652
0
    }
653
654
0
    talloc_free(domain);
655
0
    break;
656
0
  }
657
658
    /* A LDAP DN as a string */
659
0
  case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
660
0
    domain_filter = NULL;
661
0
    name_dn = ldb_dn_new(mem_ctx, sam_ctx, name);
662
0
    if (! ldb_dn_validate(name_dn)) {
663
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
664
0
      return WERR_OK;
665
0
    }
666
0
    break;
667
0
  }
668
669
    /* A GUID as a string */
670
0
  case DRSUAPI_DS_NAME_FORMAT_GUID: {
671
0
    struct GUID guid;
672
0
    char *ldap_guid;
673
0
    NTSTATUS nt_status;
674
0
    domain_filter = NULL;
675
676
0
    nt_status = GUID_from_string(name, &guid);
677
0
    if (!NT_STATUS_IS_OK(nt_status)) {
678
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
679
0
      return WERR_OK;
680
0
    }
681
682
0
    ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid);
683
0
    if (!ldap_guid) {
684
0
      return WERR_NOT_ENOUGH_MEMORY;
685
0
    }
686
0
    result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)",
687
0
            ldap_guid);
688
0
    W_ERROR_HAVE_NO_MEMORY(result_filter);
689
0
    break;
690
0
  }
691
0
  case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
692
0
    const char *name_encoded = NULL;
693
694
0
    domain_filter = NULL;
695
696
0
    name_encoded = ldb_binary_encode_string(mem_ctx, name);
697
0
    if (name_encoded == NULL) {
698
0
      return WERR_NOT_ENOUGH_MEMORY;
699
0
    }
700
701
0
    result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))",
702
0
            name_encoded,
703
0
            name_encoded);
704
0
    W_ERROR_HAVE_NO_MEMORY(result_filter);
705
0
    break;
706
0
  }
707
708
    /* A S-1234-5678 style string */
709
0
  case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
710
0
    struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name);
711
0
    char *ldap_sid;
712
713
0
    domain_filter = NULL;
714
0
    if (!sid) {
715
0
      info1->dns_domain_name = NULL;
716
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
717
0
      return WERR_OK;
718
0
    }
719
0
    ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx,
720
0
               sid);
721
0
    if (!ldap_sid) {
722
0
      return WERR_NOT_ENOUGH_MEMORY;
723
0
    }
724
0
    result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)",
725
0
            ldap_sid);
726
0
    W_ERROR_HAVE_NO_MEMORY(result_filter);
727
0
    break;
728
0
  }
729
0
  case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
730
0
    krb5_principal principal;
731
0
    char *unparsed_name;
732
0
    const char *unparsed_name_encoded = NULL;
733
734
0
    ret = smb_krb5_init_context(mem_ctx,
735
0
              (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
736
0
              &smb_krb5_context);
737
738
0
    if (ret) {
739
0
      return WERR_NOT_ENOUGH_MEMORY;
740
0
    }
741
742
    /* Ensure we reject complete junk first */
743
0
    ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
744
0
    if (ret) {
745
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
746
0
      return WERR_OK;
747
0
    }
748
749
0
    domain_filter = NULL;
750
751
    /*
752
     * By getting the unparsed name here, we ensure the
753
     * escaping is removed correctly (and trust the client
754
     * less).  The important thing here is that a
755
     * userPrincipalName may have a space in it, and this
756
     * must not be kerberos escaped to match this filter,
757
     * so we specify KRB5_PRINCIPAL_UNPARSE_DISPLAY
758
     */
759
0
    ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context,
760
0
                principal,
761
0
                KRB5_PRINCIPAL_UNPARSE_DISPLAY,
762
0
                &unparsed_name);
763
0
    if (ret) {
764
0
      krb5_free_principal(smb_krb5_context->krb5_context, principal);
765
0
      return WERR_NOT_ENOUGH_MEMORY;
766
0
    }
767
768
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
769
770
    /* The ldb_binary_encode_string() here avoids LDAP filter injection attacks */
771
0
    unparsed_name_encoded = ldb_binary_encode_string(mem_ctx, unparsed_name);
772
0
    if (unparsed_name_encoded == NULL) {
773
0
      return WERR_NOT_ENOUGH_MEMORY;
774
0
    }
775
776
0
    result_filter = talloc_asprintf(mem_ctx, "(&(userPrincipalName=%s)(objectClass=user))",
777
0
            unparsed_name_encoded);
778
779
0
    free(unparsed_name);
780
0
    W_ERROR_HAVE_NO_MEMORY(result_filter);
781
0
    break;
782
0
  }
783
0
  case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
784
0
    krb5_principal principal;
785
0
    char *unparsed_name_short;
786
0
    const char *unparsed_name_short_encoded = NULL;
787
0
    bool principal_is_host = false;
788
789
0
    ret = smb_krb5_init_context(mem_ctx,
790
0
              (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"),
791
0
              &smb_krb5_context);
792
793
0
    if (ret) {
794
0
      return WERR_NOT_ENOUGH_MEMORY;
795
0
    }
796
797
0
    ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal);
798
0
    if (ret == 0 &&
799
0
        krb5_princ_size(smb_krb5_context->krb5_context,
800
0
              principal) < 2) {
801
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
802
0
      krb5_free_principal(smb_krb5_context->krb5_context, principal);
803
0
      return WERR_OK;
804
0
    } else if (ret == 0) {
805
0
      krb5_free_principal(smb_krb5_context->krb5_context, principal);
806
0
    }
807
0
    ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name,
808
0
              KRB5_PRINCIPAL_PARSE_NO_REALM, &principal);
809
0
    if (ret) {
810
0
      return dns_domain_from_principal(mem_ctx, smb_krb5_context,
811
0
               name, info1);
812
0
    }
813
814
0
    domain_filter = NULL;
815
816
0
    ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal,
817
0
                KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short);
818
0
    if (ret) {
819
0
      krb5_free_principal(smb_krb5_context->krb5_context, principal);
820
0
      return WERR_NOT_ENOUGH_MEMORY;
821
0
    }
822
823
0
    unparsed_name_short_encoded = ldb_binary_encode_string(mem_ctx, unparsed_name_short);
824
0
    if (unparsed_name_short_encoded == NULL) {
825
0
      krb5_free_principal(smb_krb5_context->krb5_context, principal);
826
0
      free(unparsed_name_short);
827
0
      return WERR_NOT_ENOUGH_MEMORY;
828
0
    }
829
830
0
    if ((krb5_princ_size(smb_krb5_context->krb5_context, principal) == 2)) {
831
0
      krb5_data component;
832
833
0
      ret = smb_krb5_princ_component(smb_krb5_context->krb5_context,
834
0
                   principal, 0, &component);
835
0
      if (ret) {
836
0
        krb5_free_principal(smb_krb5_context->krb5_context, principal);
837
0
        free(unparsed_name_short);
838
0
        return WERR_INTERNAL_ERROR;
839
0
      }
840
841
0
      principal_is_host = strcasecmp(component.data, "host") == 0;
842
0
    }
843
844
0
    if (principal_is_host) {
845
      /* the 'cn' attribute is just the leading part of the name */
846
0
      krb5_data component;
847
0
      char *computer_name;
848
0
      const char *computer_name_encoded = NULL;
849
0
      ret = smb_krb5_princ_component(
850
0
        smb_krb5_context->krb5_context,
851
0
        principal, 1, &component);
852
0
      if (ret) {
853
0
        krb5_free_principal(smb_krb5_context->krb5_context, principal);
854
0
        free(unparsed_name_short);
855
0
        return WERR_INTERNAL_ERROR;
856
0
      }
857
0
      computer_name = talloc_strndup(mem_ctx, (char *)component.data,
858
0
              strcspn((char *)component.data, "."));
859
0
      if (computer_name == NULL) {
860
0
        krb5_free_principal(smb_krb5_context->krb5_context, principal);
861
0
        free(unparsed_name_short);
862
0
        return WERR_NOT_ENOUGH_MEMORY;
863
0
      }
864
865
0
      computer_name_encoded = ldb_binary_encode_string(mem_ctx, computer_name);
866
0
      if (computer_name_encoded == NULL) {
867
0
        krb5_free_principal(smb_krb5_context->krb5_context, principal);
868
0
        free(unparsed_name_short);
869
0
        return WERR_NOT_ENOUGH_MEMORY;
870
0
      }
871
872
0
      result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))",
873
0
              unparsed_name_short_encoded,
874
0
              computer_name_encoded);
875
0
    } else {
876
0
      result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))",
877
0
              unparsed_name_short_encoded);
878
0
    }
879
0
    krb5_free_principal(smb_krb5_context->krb5_context, principal);
880
0
    free(unparsed_name_short);
881
0
    W_ERROR_HAVE_NO_MEMORY(result_filter);
882
883
0
    break;
884
0
  }
885
0
  default: {
886
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
887
0
    return WERR_OK;
888
0
  }
889
0
  }
890
891
0
  if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) {
892
0
    return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired,
893
0
             name_dn, name, info1);
894
0
  }
895
896
0
  return DsCrackNameOneFilter(sam_ctx, mem_ctx,
897
0
            smb_krb5_context,
898
0
            format_flags, format_offered, format_desired,
899
0
            name_dn, name,
900
0
            domain_filter, result_filter,
901
0
            info1, scope, search_dn);
902
0
}
903
904
/* Subcase of CrackNames.  It is possible to translate a LDAP-style DN
905
 * (FQDN_1779) into a canonical name without actually searching the
906
 * database */
907
908
static WERROR DsCrackNameOneSyntactical(TALLOC_CTX *mem_ctx,
909
          enum drsuapi_DsNameFormat format_offered,
910
          enum drsuapi_DsNameFormat format_desired,
911
          struct ldb_dn *name_dn, const char *name,
912
          struct drsuapi_DsNameInfo1 *info1)
913
0
{
914
0
  char *cracked;
915
0
  if (format_offered != DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
916
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
917
0
    return WERR_OK;
918
0
  }
919
920
0
  switch (format_desired) {
921
0
  case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
922
0
    cracked = ldb_dn_canonical_string(mem_ctx, name_dn);
923
0
    break;
924
0
  case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
925
0
    cracked = ldb_dn_canonical_ex_string(mem_ctx, name_dn);
926
0
    break;
927
0
  default:
928
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NO_SYNTACTICAL_MAPPING;
929
0
    return WERR_OK;
930
0
  }
931
0
  info1->status = DRSUAPI_DS_NAME_STATUS_OK;
932
0
  info1->result_name  = cracked;
933
0
  if (!cracked) {
934
0
    return WERR_NOT_ENOUGH_MEMORY;
935
0
  }
936
937
0
  return WERR_OK;
938
0
}
939
940
/* Given a filter for the domain, and one for the result, perform the
941
 * ldb search. The format offered and desired flags change the
942
 * behaviours, including what attributes to return.
943
 *
944
 * The smb_krb5_context is required because we use the krb5 libs for principal parsing
945
 */
946
947
static WERROR DsCrackNameOneFilter(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
948
           struct smb_krb5_context *smb_krb5_context,
949
           uint32_t format_flags, enum drsuapi_DsNameFormat format_offered,
950
           enum drsuapi_DsNameFormat format_desired,
951
           struct ldb_dn *name_dn, const char *name,
952
           const char *domain_filter, const char *result_filter,
953
           struct drsuapi_DsNameInfo1 *info1,
954
           int scope, struct ldb_dn *search_dn)
955
0
{
956
0
  int ldb_ret;
957
0
  struct ldb_result *domain_res = NULL;
958
0
  const char * const *domain_attrs;
959
0
  const char * const *result_attrs;
960
0
  struct ldb_message **result_res = NULL;
961
0
  struct ldb_message *result = NULL;
962
0
  int i;
963
0
  char *p;
964
0
  struct ldb_dn *partitions_basedn = samdb_partitions_dn(sam_ctx, mem_ctx);
965
966
0
  const char * const _domain_attrs_1779[] = { "ncName", "dnsRoot", NULL};
967
0
  const char * const _result_attrs_null[] = { NULL };
968
969
0
  const char * const _domain_attrs_canonical[] = { "ncName", "dnsRoot", NULL};
970
0
  const char * const _result_attrs_canonical[] = { "canonicalName", NULL };
971
972
0
  const char * const _domain_attrs_nt4[] = { "ncName", "dnsRoot", "nETBIOSName", NULL};
973
0
  const char * const _result_attrs_nt4[] = { "sAMAccountName", "objectSid", "objectClass", NULL};
974
975
0
  const char * const _domain_attrs_guid[] = { "ncName", "dnsRoot", NULL};
976
0
  const char * const _result_attrs_guid[] = { "objectGUID", NULL};
977
978
0
  const char * const _domain_attrs_upn[] = { "ncName", "dnsRoot", NULL};
979
0
  const char * const _result_attrs_upn[] = { "userPrincipalName", NULL};
980
981
0
  const char * const _domain_attrs_spn[] = { "ncName", "dnsRoot", NULL};
982
0
  const char * const _result_attrs_spn[] = { "servicePrincipalName", NULL};
983
984
0
  const char * const _domain_attrs_display[] = { "ncName", "dnsRoot", NULL};
985
0
  const char * const _result_attrs_display[] = { "displayName", "sAMAccountName", NULL};
986
987
0
  const char * const _domain_attrs_sid[] = { "ncName", "dnsRoot", NULL};
988
0
  const char * const _result_attrs_sid[] = { "objectSid", NULL};
989
990
0
  const char * const _domain_attrs_none[] = { "ncName", "dnsRoot" , NULL};
991
0
  const char * const _result_attrs_none[] = { NULL};
992
993
  /* here we need to set the attrs lists for domain and result lookups */
994
0
  switch (format_desired) {
995
0
  case DRSUAPI_DS_NAME_FORMAT_FQDN_1779:
996
0
  case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
997
0
    domain_attrs = _domain_attrs_1779;
998
0
    result_attrs = _result_attrs_null;
999
0
    break;
1000
0
  case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
1001
0
    domain_attrs = _domain_attrs_canonical;
1002
0
    result_attrs = _result_attrs_canonical;
1003
0
    break;
1004
0
  case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT:
1005
0
    domain_attrs = _domain_attrs_nt4;
1006
0
    result_attrs = _result_attrs_nt4;
1007
0
    break;
1008
0
  case DRSUAPI_DS_NAME_FORMAT_GUID:
1009
0
    domain_attrs = _domain_attrs_guid;
1010
0
    result_attrs = _result_attrs_guid;
1011
0
    break;
1012
0
  case DRSUAPI_DS_NAME_FORMAT_DISPLAY:
1013
0
    domain_attrs = _domain_attrs_display;
1014
0
    result_attrs = _result_attrs_display;
1015
0
    break;
1016
0
  case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
1017
0
    domain_attrs = _domain_attrs_upn;
1018
0
    result_attrs = _result_attrs_upn;
1019
0
    break;
1020
0
  case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
1021
0
    domain_attrs = _domain_attrs_spn;
1022
0
    result_attrs = _result_attrs_spn;
1023
0
    break;
1024
0
  case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY:
1025
0
    domain_attrs = _domain_attrs_sid;
1026
0
    result_attrs = _result_attrs_sid;
1027
0
    break;
1028
0
  default:
1029
0
    domain_attrs = _domain_attrs_none;
1030
0
    result_attrs = _result_attrs_none;
1031
0
    break;
1032
0
  }
1033
1034
0
  if (domain_filter) {
1035
    /* if we have a domain_filter look it up and set the result_basedn and the dns_domain_name */
1036
0
    ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1037
0
               partitions_basedn,
1038
0
               LDB_SCOPE_ONELEVEL,
1039
0
               domain_attrs,
1040
0
               "%s", domain_filter);
1041
1042
0
    if (ldb_ret != LDB_SUCCESS) {
1043
0
      DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
1044
0
      info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1045
0
      return WERR_OK;
1046
0
    }
1047
1048
0
    switch (domain_res->count) {
1049
0
    case 1:
1050
0
      break;
1051
0
    case 0:
1052
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1053
0
      return WERR_OK;
1054
0
    default:
1055
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1056
0
      return WERR_OK;
1057
0
    }
1058
1059
0
    info1->dns_domain_name  = ldb_msg_find_attr_as_string(domain_res->msgs[0], "dnsRoot", NULL);
1060
0
    W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
1061
0
    info1->status   = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY;
1062
0
  } else {
1063
0
    info1->dns_domain_name  = NULL;
1064
0
    info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1065
0
  }
1066
1067
0
  if (result_filter) {
1068
0
    int ret;
1069
0
    struct ldb_result *res;
1070
0
    uint32_t dsdb_flags = 0;
1071
0
    struct ldb_dn *real_search_dn = NULL;
1072
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1073
1074
    /*
1075
     * From 4.1.4.2.11 of MS-DRSR
1076
     * if DS_NAME_FLAG_GCVERIFY in flags then
1077
     * rt := select all O from all
1078
     * where attrValue in GetAttrVals(O, att, false)
1079
     * else
1080
     * rt := select all O from subtree DefaultNC()
1081
     * where attrValue in GetAttrVals(O, att, false)
1082
     * endif
1083
     * return rt
1084
     */
1085
0
    if (format_flags & DRSUAPI_DS_NAME_FLAG_GCVERIFY ||
1086
0
        format_offered == DRSUAPI_DS_NAME_FORMAT_GUID)
1087
0
    {
1088
0
      dsdb_flags = DSDB_SEARCH_SEARCH_ALL_PARTITIONS;
1089
0
    } else if (domain_res) {
1090
0
      if (!search_dn) {
1091
0
        struct ldb_dn *tmp_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
1092
0
        real_search_dn = tmp_dn;
1093
0
      } else {
1094
0
        real_search_dn = search_dn;
1095
0
      }
1096
0
    } else {
1097
0
      real_search_dn = ldb_get_default_basedn(sam_ctx);
1098
0
    }
1099
0
    if (format_offered == DRSUAPI_DS_NAME_FORMAT_GUID){
1100
0
       dsdb_flags |= DSDB_SEARCH_SHOW_RECYCLED;
1101
0
    }
1102
    /* search with the 'phantom root' flag */
1103
0
    ret = dsdb_search(sam_ctx, mem_ctx, &res,
1104
0
          real_search_dn,
1105
0
          scope,
1106
0
          result_attrs,
1107
0
          dsdb_flags,
1108
0
          "%s", result_filter);
1109
0
    if (ret != LDB_SUCCESS) {
1110
0
      DEBUG(2, ("DsCrackNameOneFilter search from '%s' with flags 0x%08x failed: %s\n",
1111
0
          ldb_dn_get_linearized(real_search_dn),
1112
0
          dsdb_flags,
1113
0
          ldb_errstring(sam_ctx)));
1114
0
      info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1115
0
      return WERR_OK;
1116
0
    }
1117
1118
0
    ldb_ret = res->count;
1119
0
    result_res = res->msgs;
1120
0
  } else if (format_offered == DRSUAPI_DS_NAME_FORMAT_FQDN_1779) {
1121
0
    ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
1122
0
            result_attrs);
1123
0
  } else if (domain_res) {
1124
0
    name_dn = samdb_result_dn(sam_ctx, mem_ctx, domain_res->msgs[0], "ncName", NULL);
1125
0
    ldb_ret = gendb_search_dn(sam_ctx, mem_ctx, name_dn, &result_res,
1126
0
            result_attrs);
1127
0
  } else {
1128
    /* Can't happen */
1129
0
    DEBUG(0, ("LOGIC ERROR: DsCrackNameOneFilter domain ref search not available: This can't happen...\n"));
1130
0
    info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1131
0
    return WERR_OK;
1132
0
  }
1133
1134
0
  switch (ldb_ret) {
1135
0
  case 1:
1136
0
    result = result_res[0];
1137
0
    break;
1138
0
  case 0:
1139
0
    switch (format_offered) {
1140
0
    case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL:
1141
0
      return DsCrackNameSPNAlias(sam_ctx, mem_ctx,
1142
0
               smb_krb5_context,
1143
0
               format_flags, format_offered, format_desired,
1144
0
               name, info1);
1145
1146
0
    case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL:
1147
0
      return DsCrackNameUPN(sam_ctx, mem_ctx, smb_krb5_context,
1148
0
                format_flags, format_offered, format_desired,
1149
0
                name, info1);
1150
0
    default:
1151
0
      break;
1152
0
    }
1153
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1154
0
    return WERR_OK;
1155
0
  case -1:
1156
0
    DEBUG(2, ("DsCrackNameOneFilter result search failed: %s\n", ldb_errstring(sam_ctx)));
1157
0
    info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1158
0
    return WERR_OK;
1159
0
  default:
1160
0
    switch (format_offered) {
1161
0
    case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
1162
0
    case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
1163
0
    {
1164
0
      const char *canonical_name = NULL; /* Not required, but we get warnings... */
1165
      /* We may need to manually filter further */
1166
0
      for (i = 0; i < ldb_ret; i++) {
1167
0
        switch (format_offered) {
1168
0
        case DRSUAPI_DS_NAME_FORMAT_CANONICAL:
1169
0
          canonical_name = ldb_dn_canonical_string(mem_ctx, result_res[i]->dn);
1170
0
          break;
1171
0
        case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX:
1172
0
          canonical_name = ldb_dn_canonical_ex_string(mem_ctx, result_res[i]->dn);
1173
0
          break;
1174
0
        default:
1175
0
          break;
1176
0
        }
1177
0
        if (strcasecmp_m(canonical_name, name) == 0) {
1178
0
          result = result_res[i];
1179
0
          break;
1180
0
        }
1181
0
      }
1182
0
      if (!result) {
1183
0
        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1184
0
        return WERR_OK;
1185
0
      }
1186
0
    }
1187
0
    FALL_THROUGH;
1188
0
    default:
1189
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1190
0
      return WERR_OK;
1191
0
    }
1192
0
  }
1193
1194
0
  info1->dns_domain_name = ldb_dn_canonical_string(mem_ctx, result->dn);
1195
0
  W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name);
1196
0
  p = strchr(info1->dns_domain_name, '/');
1197
0
  if (p) {
1198
0
    p[0] = '\0';
1199
0
  }
1200
1201
  /* here we can use result and domain_res[0] */
1202
0
  switch (format_desired) {
1203
0
  case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: {
1204
0
    info1->result_name  = ldb_dn_alloc_linearized(mem_ctx, result->dn);
1205
0
    W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1206
1207
0
    info1->status   = DRSUAPI_DS_NAME_STATUS_OK;
1208
0
    return WERR_OK;
1209
0
  }
1210
0
  case DRSUAPI_DS_NAME_FORMAT_CANONICAL: {
1211
0
    info1->result_name  = ldb_msg_find_attr_as_string(result, "canonicalName", NULL);
1212
0
    info1->status   = DRSUAPI_DS_NAME_STATUS_OK;
1213
0
    return WERR_OK;
1214
0
  }
1215
0
  case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: {
1216
    /* Not in the virtual ldb attribute */
1217
0
    return DsCrackNameOneSyntactical(mem_ctx,
1218
0
             DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1219
0
             DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX,
1220
0
             result->dn, name, info1);
1221
0
  }
1222
0
  case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: {
1223
1224
0
    const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
1225
0
    const char *_acc = "", *_dom = "";
1226
0
    if (sid == NULL) {
1227
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1228
0
      return WERR_OK;
1229
0
    }
1230
1231
0
    if (samdb_find_attribute(sam_ctx, result, "objectClass",
1232
0
           "domain")) {
1233
      /* This can also find a DomainDNSZones entry,
1234
       * but it won't have the SID we just
1235
       * checked.  */
1236
0
      ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1237
0
                 partitions_basedn,
1238
0
                 LDB_SCOPE_ONELEVEL,
1239
0
                 domain_attrs,
1240
0
                 "(ncName=%s)", ldb_dn_get_linearized(result->dn));
1241
1242
0
      if (ldb_ret != LDB_SUCCESS) {
1243
0
        DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
1244
0
        info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1245
0
        return WERR_OK;
1246
0
      }
1247
1248
0
      switch (domain_res->count) {
1249
0
      case 1:
1250
0
        break;
1251
0
      case 0:
1252
0
        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1253
0
        return WERR_OK;
1254
0
      default:
1255
0
        info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1256
0
        return WERR_OK;
1257
0
      }
1258
0
      _dom = ldb_msg_find_attr_as_string(domain_res->msgs[0], "nETBIOSName", NULL);
1259
0
      W_ERROR_HAVE_NO_MEMORY(_dom);
1260
0
    } else {
1261
0
      _acc = ldb_msg_find_attr_as_string(result, "sAMAccountName", NULL);
1262
0
      if (!_acc) {
1263
0
        info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1264
0
        return WERR_OK;
1265
0
      }
1266
0
      if (dom_sid_in_domain(&global_sid_Builtin, sid)) {
1267
0
        _dom = "BUILTIN";
1268
0
      } else {
1269
0
        const char *attrs[] = { NULL };
1270
0
        struct ldb_result *domain_res2;
1271
0
        struct dom_sid *dom_sid = dom_sid_dup(mem_ctx, sid);
1272
0
        if (!dom_sid) {
1273
0
          return WERR_OK;
1274
0
        }
1275
0
        dom_sid->num_auths--;
1276
0
        ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res,
1277
0
                   NULL,
1278
0
                   LDB_SCOPE_BASE,
1279
0
                   attrs,
1280
0
                   "(&(objectSid=%s)(objectClass=domain))",
1281
0
                   ldap_encode_ndr_dom_sid(mem_ctx, dom_sid));
1282
1283
0
        if (ldb_ret != LDB_SUCCESS) {
1284
0
          DEBUG(2, ("DsCrackNameOneFilter domain search failed: %s\n", ldb_errstring(sam_ctx)));
1285
0
          info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1286
0
          return WERR_OK;
1287
0
        }
1288
1289
0
        switch (domain_res->count) {
1290
0
        case 1:
1291
0
          break;
1292
0
        case 0:
1293
0
          info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1294
0
          return WERR_OK;
1295
0
        default:
1296
0
          info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1297
0
          return WERR_OK;
1298
0
        }
1299
1300
0
        ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res2,
1301
0
                   partitions_basedn,
1302
0
                   LDB_SCOPE_ONELEVEL,
1303
0
                   domain_attrs,
1304
0
                   "(ncName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn));
1305
1306
0
        if (ldb_ret != LDB_SUCCESS) {
1307
0
          DEBUG(2, ("DsCrackNameOneFilter domain ref search failed: %s\n", ldb_errstring(sam_ctx)));
1308
0
          info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1309
0
          return WERR_OK;
1310
0
        }
1311
1312
0
        switch (domain_res2->count) {
1313
0
        case 1:
1314
0
          break;
1315
0
        case 0:
1316
0
          info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1317
0
          return WERR_OK;
1318
0
        default:
1319
0
          info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1320
0
          return WERR_OK;
1321
0
        }
1322
0
        _dom = ldb_msg_find_attr_as_string(domain_res2->msgs[0], "nETBIOSName", NULL);
1323
0
        W_ERROR_HAVE_NO_MEMORY(_dom);
1324
0
      }
1325
0
    }
1326
1327
0
    info1->result_name  = talloc_asprintf(mem_ctx, "%s\\%s", _dom, _acc);
1328
0
    W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1329
1330
0
    info1->status   = DRSUAPI_DS_NAME_STATUS_OK;
1331
0
    return WERR_OK;
1332
0
  }
1333
0
  case DRSUAPI_DS_NAME_FORMAT_GUID: {
1334
0
    struct GUID guid;
1335
1336
0
    guid = samdb_result_guid(result, "objectGUID");
1337
1338
0
    info1->result_name  = GUID_string2(mem_ctx, &guid);
1339
0
    W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1340
1341
0
    info1->status   = DRSUAPI_DS_NAME_STATUS_OK;
1342
0
    return WERR_OK;
1343
0
  }
1344
0
  case DRSUAPI_DS_NAME_FORMAT_DISPLAY: {
1345
0
    info1->result_name  = ldb_msg_find_attr_as_string(result, "displayName", NULL);
1346
0
    if (!info1->result_name) {
1347
0
      info1->result_name  = ldb_msg_find_attr_as_string(result, "sAMAccountName", NULL);
1348
0
    }
1349
0
    if (!info1->result_name) {
1350
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1351
0
    } else {
1352
0
      info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1353
0
    }
1354
0
    return WERR_OK;
1355
0
  }
1356
0
  case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: {
1357
0
    struct ldb_message_element *el
1358
0
      = ldb_msg_find_element(result,
1359
0
                 "servicePrincipalName");
1360
0
    if (el == NULL) {
1361
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1362
0
      return WERR_OK;
1363
0
    } else if (el->num_values > 1) {
1364
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE;
1365
0
      return WERR_OK;
1366
0
    }
1367
1368
0
    info1->result_name = ldb_msg_find_attr_as_string(result, "servicePrincipalName", NULL);
1369
0
    if (!info1->result_name) {
1370
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1371
0
    } else {
1372
0
      info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1373
0
    }
1374
0
    return WERR_OK;
1375
0
  }
1376
0
  case DRSUAPI_DS_NAME_FORMAT_DNS_DOMAIN: {
1377
0
    info1->dns_domain_name = NULL;
1378
0
    info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR;
1379
0
    return WERR_OK;
1380
0
  }
1381
0
  case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: {
1382
0
    const struct dom_sid *sid = samdb_result_dom_sid(mem_ctx, result, "objectSid");
1383
1384
0
    if (sid == NULL) {
1385
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1386
0
      return WERR_OK;
1387
0
    }
1388
1389
0
    info1->result_name = dom_sid_string(mem_ctx, sid);
1390
0
    W_ERROR_HAVE_NO_MEMORY(info1->result_name);
1391
1392
0
    info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1393
0
    return WERR_OK;
1394
0
  }
1395
0
  case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: {
1396
0
    info1->result_name = ldb_msg_find_attr_as_string(result, "userPrincipalName", NULL);
1397
0
    if (!info1->result_name) {
1398
0
      info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1399
0
    } else {
1400
0
      info1->status = DRSUAPI_DS_NAME_STATUS_OK;
1401
0
    }
1402
0
    return WERR_OK;
1403
0
  }
1404
0
  default:
1405
0
    info1->status = DRSUAPI_DS_NAME_STATUS_NO_MAPPING;
1406
0
    return WERR_OK;
1407
0
  }
1408
0
}
1409
1410
/* Given a user Principal Name (such as foo@bar.com),
1411
 * return the user and domain DNs.  This is used in the KDC to then
1412
 * return the Keys and evaluate policy */
1413
1414
NTSTATUS crack_user_principal_name(struct ldb_context *sam_ctx,
1415
           TALLOC_CTX *mem_ctx,
1416
           const char *user_principal_name,
1417
           struct ldb_dn **user_dn,
1418
           struct ldb_dn **domain_dn)
1419
0
{
1420
0
  WERROR werr;
1421
0
  struct drsuapi_DsNameInfo1 info1;
1422
0
  werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1423
0
          DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL,
1424
0
          DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1425
0
          user_principal_name,
1426
0
          &info1);
1427
0
  if (!W_ERROR_IS_OK(werr)) {
1428
0
    return werror_to_ntstatus(werr);
1429
0
  }
1430
0
  switch (info1.status) {
1431
0
  case DRSUAPI_DS_NAME_STATUS_OK:
1432
0
    break;
1433
0
  case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1434
0
  case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1435
0
  case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1436
0
    return NT_STATUS_NO_SUCH_USER;
1437
0
  case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1438
0
  default:
1439
0
    return NT_STATUS_UNSUCCESSFUL;
1440
0
  }
1441
1442
0
  *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1443
1444
0
  if (domain_dn) {
1445
0
    werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1446
0
            DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1447
0
            DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1448
0
            talloc_asprintf(mem_ctx, "%s/",
1449
0
                info1.dns_domain_name),
1450
0
            &info1);
1451
0
    if (!W_ERROR_IS_OK(werr)) {
1452
0
      return werror_to_ntstatus(werr);
1453
0
    }
1454
0
    switch (info1.status) {
1455
0
    case DRSUAPI_DS_NAME_STATUS_OK:
1456
0
      break;
1457
0
    case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1458
0
    case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1459
0
    case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1460
0
      return NT_STATUS_NO_SUCH_USER;
1461
0
    case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1462
0
    default:
1463
0
      return NT_STATUS_UNSUCCESSFUL;
1464
0
    }
1465
1466
0
    *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1467
0
  }
1468
1469
0
  return NT_STATUS_OK;
1470
0
}
1471
1472
/* Given a Service Principal Name (such as host/foo.bar.com@BAR.COM),
1473
 * return the user and domain DNs.  This is used in the KDC to then
1474
 * return the Keys and evaluate policy */
1475
1476
NTSTATUS crack_service_principal_name(struct ldb_context *sam_ctx,
1477
              TALLOC_CTX *mem_ctx,
1478
              const char *service_principal_name,
1479
              struct ldb_dn **user_dn,
1480
              struct ldb_dn **domain_dn)
1481
0
{
1482
0
  WERROR werr;
1483
0
  struct drsuapi_DsNameInfo1 info1;
1484
0
  werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1485
0
          DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL,
1486
0
          DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1487
0
          service_principal_name,
1488
0
          &info1);
1489
0
  if (!W_ERROR_IS_OK(werr)) {
1490
0
    return werror_to_ntstatus(werr);
1491
0
  }
1492
0
  switch (info1.status) {
1493
0
  case DRSUAPI_DS_NAME_STATUS_OK:
1494
0
    break;
1495
0
  case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1496
0
  case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1497
0
  case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1498
0
    return NT_STATUS_NO_SUCH_USER;
1499
0
  case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1500
0
  default:
1501
0
    return NT_STATUS_UNSUCCESSFUL;
1502
0
  }
1503
1504
0
  *user_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1505
1506
0
  if (domain_dn) {
1507
0
    werr = DsCrackNameOneName(sam_ctx, mem_ctx, 0,
1508
0
            DRSUAPI_DS_NAME_FORMAT_CANONICAL,
1509
0
            DRSUAPI_DS_NAME_FORMAT_FQDN_1779,
1510
0
            talloc_asprintf(mem_ctx, "%s/",
1511
0
                info1.dns_domain_name),
1512
0
            &info1);
1513
0
    if (!W_ERROR_IS_OK(werr)) {
1514
0
      return werror_to_ntstatus(werr);
1515
0
    }
1516
0
    switch (info1.status) {
1517
0
    case DRSUAPI_DS_NAME_STATUS_OK:
1518
0
      break;
1519
0
    case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1520
0
    case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1521
0
    case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1522
0
      return NT_STATUS_NO_SUCH_USER;
1523
0
    case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1524
0
    default:
1525
0
      return NT_STATUS_UNSUCCESSFUL;
1526
0
    }
1527
1528
0
    *domain_dn = ldb_dn_new(mem_ctx, sam_ctx, info1.result_name);
1529
0
  }
1530
1531
0
  return NT_STATUS_OK;
1532
0
}
1533
1534
NTSTATUS crack_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1535
        struct ldb_context *ldb,
1536
        enum drsuapi_DsNameFormat format_offered,
1537
        const char *name,
1538
        const char **nt4_domain, const char **nt4_account)
1539
0
{
1540
0
  WERROR werr;
1541
0
  struct drsuapi_DsNameInfo1 info1;
1542
0
  char *p;
1543
1544
  /* Handle anonymous bind */
1545
0
  if (!name || !*name) {
1546
0
    *nt4_domain = "";
1547
0
    *nt4_account = "";
1548
0
    return NT_STATUS_OK;
1549
0
  }
1550
1551
0
  werr = DsCrackNameOneName(ldb, mem_ctx, 0,
1552
0
          format_offered,
1553
0
          DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT,
1554
0
          name,
1555
0
          &info1);
1556
0
  if (!W_ERROR_IS_OK(werr)) {
1557
0
    return werror_to_ntstatus(werr);
1558
0
  }
1559
0
  switch (info1.status) {
1560
0
  case DRSUAPI_DS_NAME_STATUS_OK:
1561
0
    break;
1562
0
  case DRSUAPI_DS_NAME_STATUS_NOT_FOUND:
1563
0
  case DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY:
1564
0
  case DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE:
1565
0
    return NT_STATUS_NO_SUCH_USER;
1566
0
  case DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR:
1567
0
  default:
1568
0
    return NT_STATUS_UNSUCCESSFUL;
1569
0
  }
1570
1571
0
  *nt4_domain = talloc_strdup(mem_ctx, info1.result_name);
1572
0
  if (*nt4_domain == NULL) {
1573
0
    return NT_STATUS_NO_MEMORY;
1574
0
  }
1575
1576
0
  p = strchr(*nt4_domain, '\\');
1577
0
  if (!p) {
1578
0
    return NT_STATUS_INVALID_PARAMETER;
1579
0
  }
1580
0
  p[0] = '\0';
1581
1582
0
  *nt4_account = talloc_strdup(mem_ctx, &p[1]);
1583
0
  if (*nt4_account == NULL) {
1584
0
    return NT_STATUS_NO_MEMORY;
1585
0
  }
1586
1587
0
  return NT_STATUS_OK;
1588
0
}
1589
1590
NTSTATUS crack_auto_name_to_nt4_name(TALLOC_CTX *mem_ctx,
1591
             struct ldb_context *ldb,
1592
             const char *name,
1593
             const char **nt4_domain,
1594
             const char **nt4_account)
1595
0
{
1596
0
  enum drsuapi_DsNameFormat format_offered = DRSUAPI_DS_NAME_FORMAT_UNKNOWN;
1597
1598
  /* Handle anonymous bind */
1599
0
  if (!name || !*name) {
1600
0
    *nt4_domain = "";
1601
0
    *nt4_account = "";
1602
0
    return NT_STATUS_OK;
1603
0
  }
1604
1605
  /*
1606
   * Here we only consider a subset of the possible name forms listed in
1607
   * [MS-ADTS] 5.1.1.1.1, and we don't retry with a different name form if
1608
   * the first attempt fails.
1609
   */
1610
1611
0
  if (strchr_m(name, '=')) {
1612
0
    format_offered = DRSUAPI_DS_NAME_FORMAT_FQDN_1779;
1613
0
  } else if (strchr_m(name, '@')) {
1614
0
    format_offered = DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL;
1615
0
  } else if (strchr_m(name, '\\')) {
1616
0
    format_offered = DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT;
1617
0
  } else if (strchr_m(name, '\n')) {
1618
0
    format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX;
1619
0
  } else if (strchr_m(name, '/')) {
1620
0
    format_offered = DRSUAPI_DS_NAME_FORMAT_CANONICAL;
1621
0
  } else if ((name[0] == 'S' || name[0] == 's') && name[1] == '-') {
1622
0
    format_offered = DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY;
1623
0
  } else {
1624
0
    return NT_STATUS_NO_SUCH_USER;
1625
0
  }
1626
1627
0
  return crack_name_to_nt4_name(mem_ctx, ldb, format_offered, name, nt4_domain, nt4_account);
1628
0
}
1629
1630
1631
WERROR dcesrv_drsuapi_ListRoles(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1632
        const struct drsuapi_DsNameRequest1 *req1,
1633
        struct drsuapi_DsNameCtr1 **ctr1)
1634
0
{
1635
0
  struct drsuapi_DsNameInfo1 *names;
1636
0
  uint32_t i;
1637
0
  uint32_t count = 5;/*number of fsmo role owners we are going to return*/
1638
1639
0
  *ctr1 = talloc(mem_ctx, struct drsuapi_DsNameCtr1);
1640
0
  W_ERROR_HAVE_NO_MEMORY(*ctr1);
1641
0
  names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
1642
0
  W_ERROR_HAVE_NO_MEMORY(names);
1643
1644
0
  for (i = 0; i < count; i++) {
1645
0
    WERROR werr;
1646
0
    struct ldb_dn *role_owner_dn, *fsmo_role_dn, *server_dn;
1647
0
    werr = dsdb_get_fsmo_role_info(mem_ctx, sam_ctx, i,
1648
0
                 &fsmo_role_dn, &role_owner_dn);
1649
0
    if(!W_ERROR_IS_OK(werr)) {
1650
0
      return werr;
1651
0
    }
1652
0
    server_dn = ldb_dn_copy(mem_ctx, role_owner_dn);
1653
0
    ldb_dn_remove_child_components(server_dn, 1);
1654
0
    names[i].status = DRSUAPI_DS_NAME_STATUS_OK;
1655
0
    names[i].dns_domain_name = samdb_dn_to_dnshostname(sam_ctx, mem_ctx,
1656
0
                   server_dn);
1657
0
    if(!names[i].dns_domain_name) {
1658
0
      DEBUG(4, ("list_roles: Failed to find dNSHostName for server %s\n",
1659
0
          ldb_dn_get_linearized(server_dn)));
1660
0
    }
1661
0
    names[i].result_name = talloc_strdup(mem_ctx, ldb_dn_get_linearized(role_owner_dn));
1662
0
  }
1663
1664
0
  (*ctr1)->count = count;
1665
0
  (*ctr1)->array = names;
1666
1667
0
  return WERR_OK;
1668
0
}
1669
1670
WERROR dcesrv_drsuapi_CrackNamesByNameFormat(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1671
               const struct drsuapi_DsNameRequest1 *req1,
1672
               struct drsuapi_DsNameCtr1 **ctr1)
1673
0
{
1674
0
  struct drsuapi_DsNameInfo1 *names;
1675
0
  uint32_t i, count;
1676
0
  WERROR status;
1677
1678
0
  *ctr1 = talloc_zero(mem_ctx, struct drsuapi_DsNameCtr1);
1679
0
  W_ERROR_HAVE_NO_MEMORY(*ctr1);
1680
1681
0
  count = req1->count;
1682
0
  names = talloc_array(mem_ctx, struct drsuapi_DsNameInfo1, count);
1683
0
  W_ERROR_HAVE_NO_MEMORY(names);
1684
1685
0
  for (i=0; i < count; i++) {
1686
0
    status = DsCrackNameOneName(sam_ctx, mem_ctx,
1687
0
              req1->format_flags,
1688
0
              req1->format_offered,
1689
0
              req1->format_desired,
1690
0
              req1->names[i].str,
1691
0
              &names[i]);
1692
0
    if (!W_ERROR_IS_OK(status)) {
1693
0
      return status;
1694
0
    }
1695
0
  }
1696
1697
0
  (*ctr1)->count = count;
1698
0
  (*ctr1)->array = names;
1699
1700
0
  return WERR_OK;
1701
0
}
1702
1703
WERROR dcesrv_drsuapi_ListInfoServer(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx,
1704
             const struct drsuapi_DsNameRequest1 *req1,
1705
             struct drsuapi_DsNameCtr1 **_ctr1)
1706
0
{
1707
0
  struct drsuapi_DsNameInfo1 *names;
1708
0
  struct ldb_result *res;
1709
0
  struct ldb_dn *server_dn, *dn;
1710
0
  struct drsuapi_DsNameCtr1 *ctr1;
1711
0
  int ret, i;
1712
0
  const char *str;
1713
0
  const char *attrs[] = {
1714
0
    "dNSHostName",
1715
0
    "serverReference",
1716
0
    NULL
1717
0
  };
1718
1719
0
  *_ctr1 = NULL;
1720
1721
0
  ctr1 = talloc_zero(mem_ctx, struct drsuapi_DsNameCtr1);
1722
0
  W_ERROR_HAVE_NO_MEMORY(ctr1);
1723
1724
  /*
1725
   * No magic value here, we have to return 3 entries according to the
1726
   * MS-DRSR.pdf
1727
   */
1728
0
  ctr1->count = 3;
1729
0
  names = talloc_zero_array(ctr1, struct drsuapi_DsNameInfo1,
1730
0
          ctr1->count);
1731
0
  W_ERROR_HAVE_NO_MEMORY(names);
1732
0
  ctr1->array = names;
1733
1734
0
  for (i=0; i < ctr1->count; i++) {
1735
0
    names[i].status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND;
1736
0
  }
1737
0
  *_ctr1 = ctr1;
1738
1739
0
  if (req1->count != 1) {
1740
0
    DEBUG(1, ("Expected a count of 1 for the ListInfoServer crackname \n"));
1741
0
    return WERR_OK;
1742
0
  }
1743
1744
0
  if (req1->names[0].str == NULL) {
1745
0
    return WERR_OK;
1746
0
  }
1747
1748
0
  server_dn = ldb_dn_new(mem_ctx, sam_ctx, req1->names[0].str);
1749
0
  W_ERROR_HAVE_NO_MEMORY(server_dn);
1750
1751
0
  ret = ldb_search(sam_ctx, mem_ctx, &res, server_dn, LDB_SCOPE_ONELEVEL,
1752
0
       NULL, "(objectClass=nTDSDSA)");
1753
1754
0
  if (ret != LDB_SUCCESS) {
1755
0
    DEBUG(1, ("Search for objectClass=nTDSDSA "
1756
0
        "returned less than 1 objects\n"));
1757
0
    return WERR_OK;
1758
0
  }
1759
1760
0
  if (res->count != 1) {
1761
0
    DEBUG(1, ("Search for objectClass=nTDSDSA "
1762
0
        "returned less than 1 objects\n"));
1763
0
    return WERR_OK;
1764
0
  }
1765
1766
0
  if (res->msgs[0]->dn) {
1767
0
    names[0].result_name = ldb_dn_alloc_linearized(names, res->msgs[0]->dn);
1768
0
    W_ERROR_HAVE_NO_MEMORY(names[0].result_name);
1769
0
    names[0].status = DRSUAPI_DS_NAME_STATUS_OK;
1770
0
  }
1771
1772
0
  talloc_free(res);
1773
1774
0
  ret = ldb_search(sam_ctx, mem_ctx, &res, server_dn, LDB_SCOPE_BASE,
1775
0
       attrs, "(objectClass=*)");
1776
0
  if (ret != LDB_SUCCESS) {
1777
0
    DEBUG(1, ("Search for objectClass=* on dn %s"
1778
0
        "returned %s\n", req1->names[0].str,
1779
0
        ldb_strerror(ret)));
1780
0
    return WERR_OK;
1781
0
  }
1782
1783
0
  if (res->count != 1) {
1784
0
    DEBUG(1, ("Search for objectClass=* on dn %s"
1785
0
        "returned less than 1 objects\n", req1->names[0].str));
1786
0
    return WERR_OK;
1787
0
  }
1788
1789
0
  str = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL);
1790
0
  if (str != NULL) {
1791
0
    names[1].result_name = talloc_strdup(names, str);
1792
0
    W_ERROR_HAVE_NO_MEMORY(names[1].result_name);
1793
0
    names[1].status = DRSUAPI_DS_NAME_STATUS_OK;
1794
0
  }
1795
1796
0
  dn = ldb_msg_find_attr_as_dn(sam_ctx, mem_ctx, res->msgs[0], "serverReference");
1797
0
  if (dn != NULL) {
1798
0
    names[2].result_name = ldb_dn_alloc_linearized(names, dn);
1799
0
    W_ERROR_HAVE_NO_MEMORY(names[2].result_name);
1800
0
    names[2].status = DRSUAPI_DS_NAME_STATUS_OK;
1801
0
  }
1802
1803
0
  talloc_free(dn);
1804
0
  talloc_free(res);
1805
1806
0
  return WERR_OK;
1807
0
}