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