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