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