clikrb5: Move pure krb wrapper functions from libads to clikrb5.
[samba.git] / source3 / libads / kerberos.c
1 /* 
2    Unix SMB/CIFS implementation.
3    kerberos utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Nalin Dahyabhai <nalin@redhat.com> 2004.
7    Copyright (C) Jeremy Allison 2004.
8    Copyright (C) Gerald Carter 2006.
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 "system/filesys.h"
26 #include "smb_krb5.h"
27 #include "../librpc/gen_ndr/ndr_misc.h"
28 #include "libads/kerberos_proto.h"
29 #include "libads/cldap.h"
30 #include "secrets.h"
31 #include "../lib/tsocket/tsocket.h"
32
33 #ifdef HAVE_KRB5
34
35 #define DEFAULT_KRB5_PORT 88
36
37 #define LIBADS_CCACHE_NAME "MEMORY:libads"
38
39 /*
40   we use a prompter to avoid a crash bug in the kerberos libs when 
41   dealing with empty passwords
42   this prompter is just a string copy ...
43 */
44 static krb5_error_code 
45 kerb_prompter(krb5_context ctx, void *data,
46                const char *name,
47                const char *banner,
48                int num_prompts,
49                krb5_prompt prompts[])
50 {
51         if (num_prompts == 0) return 0;
52
53         memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
54         if (prompts[0].reply->length > 0) {
55                 if (data) {
56                         strncpy((char *)prompts[0].reply->data, (const char *)data,
57                                 prompts[0].reply->length-1);
58                         prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
59                 } else {
60                         prompts[0].reply->length = 0;
61                 }
62         }
63         return 0;
64 }
65
66  static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
67                                                    NTSTATUS *nt_status)
68 {
69         DATA_BLOB edata;
70         DATA_BLOB unwrapped_edata;
71         TALLOC_CTX *mem_ctx;
72         struct KRB5_EDATA_NTSTATUS parsed_edata;
73         enum ndr_err_code ndr_err;
74
75 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
76         edata = data_blob(error->e_data->data, error->e_data->length);
77 #else
78         edata = data_blob(error->e_data.data, error->e_data.length);
79 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
80
81 #ifdef DEVELOPER
82         dump_data(10, edata.data, edata.length);
83 #endif /* DEVELOPER */
84
85         mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
86         if (mem_ctx == NULL) {
87                 data_blob_free(&edata);
88                 return False;
89         }
90
91         if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
92                 data_blob_free(&edata);
93                 TALLOC_FREE(mem_ctx);
94                 return False;
95         }
96
97         data_blob_free(&edata);
98
99         ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx, 
100                 &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
101         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
102                 data_blob_free(&unwrapped_edata);
103                 TALLOC_FREE(mem_ctx);
104                 return False;
105         }
106
107         data_blob_free(&unwrapped_edata);
108
109         if (nt_status) {
110                 *nt_status = parsed_edata.ntstatus;
111         }
112
113         TALLOC_FREE(mem_ctx);
114
115         return True;
116 }
117
118  static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx, 
119                                                                   krb5_get_init_creds_opt *opt, 
120                                                                   NTSTATUS *nt_status)
121 {
122         bool ret = False;
123         krb5_error *error = NULL;
124
125 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
126         ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
127         if (ret) {
128                 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n", 
129                         error_message(ret)));
130                 return False;
131         }
132 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
133
134         if (!error) {
135                 DEBUG(1,("no krb5_error\n"));
136                 return False;
137         }
138
139 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
140         if (!error->e_data) {
141 #else
142         if (error->e_data.data == NULL) {
143 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
144                 DEBUG(1,("no edata in krb5_error\n")); 
145                 krb5_free_error(ctx, error);
146                 return False;
147         }
148
149         ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
150
151         krb5_free_error(ctx, error);
152
153         return ret;
154 }
155
156 /*
157   simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
158   place in default cache location.
159   remus@snapserver.com
160 */
161 int kerberos_kinit_password_ext(const char *principal,
162                                 const char *password,
163                                 int time_offset,
164                                 time_t *expire_time,
165                                 time_t *renew_till_time,
166                                 const char *cache_name,
167                                 bool request_pac,
168                                 bool add_netbios_addr,
169                                 time_t renewable_time,
170                                 NTSTATUS *ntstatus)
171 {
172         krb5_context ctx = NULL;
173         krb5_error_code code = 0;
174         krb5_ccache cc = NULL;
175         krb5_principal me = NULL;
176         krb5_creds my_creds;
177         krb5_get_init_creds_opt *opt = NULL;
178         smb_krb5_addresses *addr = NULL;
179
180         ZERO_STRUCT(my_creds);
181
182         initialize_krb5_error_table();
183         if ((code = krb5_init_context(&ctx)))
184                 goto out;
185
186         if (time_offset != 0) {
187                 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
188         }
189
190         DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
191                         principal,
192                         cache_name ? cache_name: krb5_cc_default_name(ctx),
193                         getenv("KRB5_CONFIG")));
194
195         if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
196                 goto out;
197         }
198
199         if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
200                 goto out;
201         }
202
203         if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
204                 goto out;
205         }
206
207         krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
208         krb5_get_init_creds_opt_set_forwardable(opt, True);
209 #if 0
210         /* insane testing */
211         krb5_get_init_creds_opt_set_tkt_life(opt, 60);
212 #endif
213
214 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
215         if (request_pac) {
216                 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
217                         goto out;
218                 }
219         }
220 #endif
221         if (add_netbios_addr) {
222                 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
223                         goto out;
224                 }
225                 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
226         }
227
228         if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password), 
229                                                  kerb_prompter, discard_const_p(char, password),
230                                                  0, NULL, opt))) {
231                 goto out;
232         }
233
234         if ((code = krb5_cc_initialize(ctx, cc, me))) {
235                 goto out;
236         }
237
238         if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
239                 goto out;
240         }
241
242         if (expire_time) {
243                 *expire_time = (time_t) my_creds.times.endtime;
244         }
245
246         if (renew_till_time) {
247                 *renew_till_time = (time_t) my_creds.times.renew_till;
248         }
249  out:
250         if (ntstatus) {
251
252                 NTSTATUS status;
253
254                 /* fast path */
255                 if (code == 0) {
256                         *ntstatus = NT_STATUS_OK;
257                         goto cleanup;
258                 }
259
260                 /* try to get ntstatus code out of krb5_error when we have it
261                  * inside the krb5_get_init_creds_opt - gd */
262
263                 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
264                         *ntstatus = status;
265                         goto cleanup;
266                 }
267
268                 /* fall back to self-made-mapping */
269                 *ntstatus = krb5_to_nt_status(code);
270         }
271
272  cleanup:
273         krb5_free_cred_contents(ctx, &my_creds);
274         if (me) {
275                 krb5_free_principal(ctx, me);
276         }
277         if (addr) {
278                 smb_krb5_free_addresses(ctx, addr);
279         }
280         if (opt) {
281                 smb_krb5_get_init_creds_opt_free(ctx, opt);
282         }
283         if (cc) {
284                 krb5_cc_close(ctx, cc);
285         }
286         if (ctx) {
287                 krb5_free_context(ctx);
288         }
289         return code;
290 }
291
292 int ads_kdestroy(const char *cc_name)
293 {
294         krb5_error_code code;
295         krb5_context ctx = NULL;
296         krb5_ccache cc = NULL;
297
298         initialize_krb5_error_table();
299         if ((code = krb5_init_context (&ctx))) {
300                 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", 
301                         error_message(code)));
302                 return code;
303         }
304
305         if (!cc_name) {
306                 if ((code = krb5_cc_default(ctx, &cc))) {
307                         krb5_free_context(ctx);
308                         return code;
309                 }
310         } else {
311                 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
312                         DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
313                                   error_message(code)));
314                         krb5_free_context(ctx);
315                         return code;
316                 }
317         }
318
319         if ((code = krb5_cc_destroy (ctx, cc))) {
320                 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 
321                         error_message(code)));
322         }
323
324         krb5_free_context (ctx);
325         return code;
326 }
327
328 /************************************************************************
329  Routine to fetch the salting principal for a service.  Active
330  Directory may use a non-obvious principal name to generate the salt
331  when it determines the key to use for encrypting tickets for a service,
332  and hopefully we detected that when we joined the domain.
333  ************************************************************************/
334
335 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
336 {
337         char *key = NULL;
338         char *ret = NULL;
339
340         if (asprintf(&key, "%s/%s/enctype=%d",
341                      SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
342                 return NULL;
343         }
344         ret = (char *)secrets_fetch(key, NULL);
345         SAFE_FREE(key);
346         return ret;
347 }
348
349 /************************************************************************
350  Return the standard DES salt key
351 ************************************************************************/
352
353 char* kerberos_standard_des_salt( void )
354 {
355         fstring salt;
356
357         fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
358         strlower_m( salt );
359         fstrcat( salt, lp_realm() );
360
361         return SMB_STRDUP( salt );
362 }
363
364 /************************************************************************
365 ************************************************************************/
366
367 static char* des_salt_key( void )
368 {
369         char *key;
370
371         if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
372                      lp_realm()) == -1) {
373                 return NULL;
374         }
375
376         return key;
377 }
378
379 /************************************************************************
380 ************************************************************************/
381
382 bool kerberos_secrets_store_des_salt( const char* salt )
383 {
384         char* key;
385         bool ret;
386
387         if ( (key = des_salt_key()) == NULL ) {
388                 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
389                 return False;
390         }
391
392         if ( !salt ) {
393                 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
394                 secrets_delete( key );
395                 return True;
396         }
397
398         DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
399
400         ret = secrets_store( key, salt, strlen(salt)+1 );
401
402         SAFE_FREE( key );
403
404         return ret;
405 }
406
407 /************************************************************************
408 ************************************************************************/
409
410 char* kerberos_secrets_fetch_des_salt( void )
411 {
412         char *salt, *key;
413
414         if ( (key = des_salt_key()) == NULL ) {
415                 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
416                 return NULL;
417         }
418
419         salt = (char*)secrets_fetch( key, NULL );
420
421         SAFE_FREE( key );
422
423         return salt;
424 }
425
426 /************************************************************************
427  Routine to get the salting principal for this service.  This is 
428  maintained for backwards compatibilty with releases prior to 3.0.24.
429  Since we store the salting principal string only at join, we may have 
430  to look for the older tdb keys.  Caller must free if return is not null.
431  ************************************************************************/
432
433 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
434                                                         krb5_principal host_princ,
435                                                         int enctype)
436 {
437         char *unparsed_name = NULL, *salt_princ_s = NULL;
438         krb5_principal ret_princ = NULL;
439
440         /* lookup new key first */
441
442         if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
443
444                 /* look under the old key.  If this fails, just use the standard key */
445
446                 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
447                         return (krb5_principal)NULL;
448                 }
449                 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
450                         /* fall back to host/machine.realm@REALM */
451                         salt_princ_s = kerberos_standard_des_salt();
452                 }
453         }
454
455         if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
456                 ret_princ = NULL;
457         }
458
459         TALLOC_FREE(unparsed_name);
460         SAFE_FREE(salt_princ_s);
461
462         return ret_princ;
463 }
464
465 /************************************************************************
466  Routine to set the salting principal for this service.  Active
467  Directory may use a non-obvious principal name to generate the salt
468  when it determines the key to use for encrypting tickets for a service,
469  and hopefully we detected that when we joined the domain.
470  Setting principal to NULL deletes this entry.
471  ************************************************************************/
472
473 bool kerberos_secrets_store_salting_principal(const char *service,
474                                               int enctype,
475                                               const char *principal)
476 {
477         char *key = NULL;
478         bool ret = False;
479         krb5_context context = NULL;
480         krb5_principal princ = NULL;
481         char *princ_s = NULL;
482         char *unparsed_name = NULL;
483         krb5_error_code code;
484
485         if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
486                 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
487                           error_message(code)));
488                 return False;
489         }
490         if (strchr_m(service, '@')) {
491                 if (asprintf(&princ_s, "%s", service) == -1) {
492                         goto out;
493                 }
494         } else {
495                 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
496                         goto out;
497                 }
498         }
499
500         if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
501                 goto out;
502         }
503         if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
504                 goto out;
505         }
506
507         if (asprintf(&key, "%s/%s/enctype=%d",
508                      SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
509             == -1) {
510                 goto out;
511         }
512
513         if ((principal != NULL) && (strlen(principal) > 0)) {
514                 ret = secrets_store(key, principal, strlen(principal) + 1);
515         } else {
516                 ret = secrets_delete(key);
517         }
518
519  out:
520
521         SAFE_FREE(key);
522         SAFE_FREE(princ_s);
523         TALLOC_FREE(unparsed_name);
524
525         if (princ) {
526                 krb5_free_principal(context, princ);
527         }
528
529         if (context) {
530                 krb5_free_context(context);
531         }
532
533         return ret;
534 }
535
536
537 /************************************************************************
538 ************************************************************************/
539
540 int kerberos_kinit_password(const char *principal,
541                             const char *password,
542                             int time_offset,
543                             const char *cache_name)
544 {
545         return kerberos_kinit_password_ext(principal, 
546                                            password, 
547                                            time_offset, 
548                                            0, 
549                                            0,
550                                            cache_name,
551                                            False,
552                                            False,
553                                            0,
554                                            NULL);
555 }
556
557 /************************************************************************
558 ************************************************************************/
559
560 static char *print_kdc_line(char *mem_ctx,
561                         const char *prev_line,
562                         const struct sockaddr_storage *pss,
563                         const char *kdc_name)
564 {
565         char addr[INET6_ADDRSTRLEN];
566         uint16_t port = get_sockaddr_port(pss);
567
568         if (pss->ss_family == AF_INET) {
569                 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
570                                        prev_line,
571                                        print_canonical_sockaddr(mem_ctx, pss));
572         }
573
574         /*
575          * IPv6 starts here
576          */
577
578         DEBUG(10, ("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
579                    kdc_name, port));
580
581         if (port != 0 && port != DEFAULT_KRB5_PORT) {
582                 /* Currently for IPv6 we can't specify a non-default
583                    krb5 port with an address, as this requires a ':'.
584                    Resolve to a name. */
585                 char hostname[MAX_DNS_NAME_LENGTH];
586                 int ret = sys_getnameinfo((const struct sockaddr *)pss,
587                                           sizeof(*pss),
588                                           hostname, sizeof(hostname),
589                                           NULL, 0,
590                                           NI_NAMEREQD);
591                 if (ret) {
592                         DEBUG(0,("print_kdc_line: can't resolve name "
593                                  "for kdc with non-default port %s. "
594                                  "Error %s\n.",
595                                  print_canonical_sockaddr(mem_ctx, pss),
596                                  gai_strerror(ret)));
597                         return NULL;
598                 }
599                 /* Success, use host:port */
600                 return talloc_asprintf(mem_ctx,
601                                        "%s\tkdc = %s:%u\n",
602                                        prev_line,
603                                        hostname,
604                                        (unsigned int)port);
605         }
606
607         /* no krb5 lib currently supports "kdc = ipv6 address"
608          * at all, so just fill in just the kdc_name if we have
609          * it and let the krb5 lib figure out the appropriate
610          * ipv6 address - gd */
611
612         if (kdc_name) {
613                 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
614                                        prev_line, kdc_name);
615         }
616
617         return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
618                                prev_line,
619                                print_sockaddr(addr,
620                                               sizeof(addr),
621                                               pss));
622 }
623
624 /************************************************************************
625  Create a string list of available kdc's, possibly searching by sitename.
626  Does DNS queries.
627
628  If "sitename" is given, the DC's in that site are listed first.
629
630 ************************************************************************/
631
632 static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
633                                 const struct sockaddr_storage *addr)
634 {
635         int i;
636
637         for (i=0; i<*num_addrs; i++) {
638                 if (sockaddr_equal((const struct sockaddr *)&addrs[i],
639                                    (const struct sockaddr *)addr)) {
640                         return;
641                 }
642         }
643         addrs[i] = *addr;
644         *num_addrs += 1;
645 }
646
647 static char *get_kdc_ip_string(char *mem_ctx,
648                 const char *realm,
649                 const char *sitename,
650                 const struct sockaddr_storage *pss,
651                 const char *kdc_name)
652 {
653         TALLOC_CTX *frame = talloc_stackframe();
654         int i;
655         struct ip_service *ip_srv_site = NULL;
656         struct ip_service *ip_srv_nonsite = NULL;
657         int count_site = 0;
658         int count_nonsite;
659         int num_dcs;
660         struct sockaddr_storage *dc_addrs;
661         struct tsocket_address **dc_addrs2 = NULL;
662         const struct tsocket_address * const *dc_addrs3 = NULL;
663         char *result = NULL;
664         struct netlogon_samlogon_response **responses = NULL;
665         NTSTATUS status;
666         char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
667
668         if (kdc_str == NULL) {
669                 return NULL;
670         }
671
672         /*
673          * First get the KDC's only in this site, the rest will be
674          * appended later
675          */
676
677         if (sitename) {
678                 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
679         }
680
681         /* Get all KDC's. */
682
683         get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
684
685         dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
686                                 1 + count_site + count_nonsite);
687         if (dc_addrs == NULL) {
688                 goto fail;
689         }
690
691         dc_addrs[0] = *pss;
692         num_dcs = 1;
693
694         for (i=0; i<count_site; i++) {
695                 add_sockaddr_unique(dc_addrs, &num_dcs, &ip_srv_site[i].ss);
696         }
697
698         for (i=0; i<count_nonsite; i++) {
699                 add_sockaddr_unique(dc_addrs, &num_dcs, &ip_srv_nonsite[i].ss);
700         }
701
702         dc_addrs2 = talloc_zero_array(talloc_tos(),
703                                       struct tsocket_address *,
704                                       num_dcs);
705         if (dc_addrs2 == NULL) {
706                 goto fail;
707         }
708
709         for (i=0; i<num_dcs; i++) {
710                 char addr[INET6_ADDRSTRLEN];
711                 int ret;
712
713                 print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
714
715                 ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
716                                                         addr, LDAP_PORT,
717                                                         &dc_addrs2[i]);
718                 if (ret != 0) {
719                         status = map_nt_error_from_unix(errno);
720                         DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
721                                  addr, nt_errstr(status)));
722                         goto fail;
723                 }
724         }
725
726         dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
727
728         status = cldap_multi_netlogon(talloc_tos(),
729                         dc_addrs3, num_dcs,
730                         realm, lp_netbios_name(),
731                         NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
732                         MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
733         TALLOC_FREE(dc_addrs2);
734         dc_addrs3 = NULL;
735
736         if (!NT_STATUS_IS_OK(status)) {
737                 DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
738                           "%s\n", nt_errstr(status)));
739                 goto fail;
740         }
741
742         kdc_str = talloc_strdup(mem_ctx, "");
743         if (kdc_str == NULL) {
744                 goto fail;
745         }
746
747         for (i=0; i<num_dcs; i++) {
748                 char *new_kdc_str;
749
750                 if (responses[i] == NULL) {
751                         continue;
752                 }
753
754                 /* Append to the string - inefficient but not done often. */
755                 new_kdc_str = print_kdc_line(mem_ctx, kdc_str,
756                                              &dc_addrs[i],
757                                              kdc_name);
758                 if (new_kdc_str == NULL) {
759                         goto fail;
760                 }
761                 TALLOC_FREE(kdc_str);
762                 kdc_str = new_kdc_str;
763         }
764
765         DEBUG(10,("get_kdc_ip_string: Returning %s\n",
766                 kdc_str ));
767
768         result = kdc_str;
769 fail:
770         SAFE_FREE(ip_srv_site);
771         SAFE_FREE(ip_srv_nonsite);
772         TALLOC_FREE(frame);
773         return result;
774 }
775
776 /************************************************************************
777  Create  a specific krb5.conf file in the private directory pointing
778  at a specific kdc for a realm. Keyed off domain name. Sets
779  KRB5_CONFIG environment variable to point to this file. Must be
780  run as root or will fail (which is a good thing :-).
781 ************************************************************************/
782
783 bool create_local_private_krb5_conf_for_domain(const char *realm,
784                                                 const char *domain,
785                                                 const char *sitename,
786                                                 const struct sockaddr_storage *pss,
787                                                 const char *kdc_name)
788 {
789         char *dname;
790         char *tmpname = NULL;
791         char *fname = NULL;
792         char *file_contents = NULL;
793         char *kdc_ip_string = NULL;
794         size_t flen = 0;
795         ssize_t ret;
796         int fd;
797         char *realm_upper = NULL;
798         bool result = false;
799
800         if (!lp_create_krb5_conf()) {
801                 return false;
802         }
803
804         dname = lock_path("smb_krb5");
805         if (!dname) {
806                 return false;
807         }
808         if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
809                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
810                         "failed to create directory %s. Error was %s\n",
811                         dname, strerror(errno) ));
812                 goto done;
813         }
814
815         tmpname = lock_path("smb_tmp_krb5.XXXXXX");
816         if (!tmpname) {
817                 goto done;
818         }
819
820         fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
821         if (!fname) {
822                 goto done;
823         }
824
825         DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
826                 fname, realm, domain ));
827
828         realm_upper = talloc_strdup(fname, realm);
829         strupper_m(realm_upper);
830
831         kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
832         if (!kdc_ip_string) {
833                 goto done;
834         }
835
836         file_contents = talloc_asprintf(fname,
837                                         "[libdefaults]\n\tdefault_realm = %s\n"
838                                         "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
839                                         "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
840                                         "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
841                                         "[realms]\n\t%s = {\n"
842                                         "\t%s\t}\n",
843                                         realm_upper, realm_upper, kdc_ip_string);
844
845         if (!file_contents) {
846                 goto done;
847         }
848
849         flen = strlen(file_contents);
850
851         fd = mkstemp(tmpname);
852         if (fd == -1) {
853                 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
854                         " for file %s. Errno %s\n",
855                         tmpname, strerror(errno) ));
856                 goto done;
857         }
858
859         if (fchmod(fd, 0644)==-1) {
860                 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
861                         " Errno %s\n",
862                         tmpname, strerror(errno) ));
863                 unlink(tmpname);
864                 close(fd);
865                 goto done;
866         }
867
868         ret = write(fd, file_contents, flen);
869         if (flen != ret) {
870                 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
871                         " returned %d (should be %u). Errno %s\n",
872                         (int)ret, (unsigned int)flen, strerror(errno) ));
873                 unlink(tmpname);
874                 close(fd);
875                 goto done;
876         }
877         if (close(fd)==-1) {
878                 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
879                         " Errno %s\n", strerror(errno) ));
880                 unlink(tmpname);
881                 goto done;
882         }
883
884         if (rename(tmpname, fname) == -1) {
885                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
886                         "of %s to %s failed. Errno %s\n",
887                         tmpname, fname, strerror(errno) ));
888                 unlink(tmpname);
889                 goto done;
890         }
891
892         DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
893                 "file %s with realm %s KDC list = %s\n",
894                 fname, realm_upper, kdc_ip_string));
895
896         /* Set the environment variable to this file. */
897         setenv("KRB5_CONFIG", fname, 1);
898
899         result = true;
900
901 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
902
903 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
904         /* Insanity, sheer insanity..... */
905
906         if (strequal(realm, lp_realm())) {
907                 SMB_STRUCT_STAT sbuf;
908
909                 if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
910                         if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
911                                 int lret;
912                                 size_t alloc_size = sbuf.st_ex_size + 1;
913                                 char *linkpath = talloc_array(talloc_tos(), char,
914                                                 alloc_size);
915                                 if (!linkpath) {
916                                         goto done;
917                                 }
918                                 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
919                                                 alloc_size - 1);
920                                 if (lret == -1) {
921                                         TALLOC_FREE(linkpath);
922                                         goto done;
923                                 }
924                                 linkpath[lret] = '\0';
925
926                                 if (strcmp(linkpath, fname) == 0) {
927                                         /* Symlink already exists. */
928                                         TALLOC_FREE(linkpath);
929                                         goto done;
930                                 }
931                                 TALLOC_FREE(linkpath);
932                         }
933                 }
934
935                 /* Try and replace with a symlink. */
936                 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
937                         const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
938                         if (errno != EEXIST) {
939                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
940                                         "of %s to %s failed. Errno %s\n",
941                                         fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
942                                 goto done; /* Not a fatal error. */
943                         }
944
945                         /* Yes, this is a race conditon... too bad. */
946                         if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
947                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
948                                         "of %s to %s failed. Errno %s\n",
949                                         SYSTEM_KRB5_CONF_PATH, newpath,
950                                         strerror(errno) ));
951                                 goto done; /* Not a fatal error. */
952                         }
953
954                         if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
955                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
956                                         "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
957                                         fname, strerror(errno) ));
958                                 goto done; /* Not a fatal error. */
959                         }
960                 }
961         }
962 #endif
963
964 done:
965         TALLOC_FREE(tmpname);
966         TALLOC_FREE(dname);
967
968         return result;
969 }
970 #endif