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