s3-secrets: only include secrets.h when needed.
[kai/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 "smb_krb5.h"
26 #include "../librpc/gen_ndr/ndr_misc.h"
27 #include "libads/kerberos_proto.h"
28 #include "secrets.h"
29
30 #ifdef HAVE_KRB5
31
32 #define DEFAULT_KRB5_PORT 88
33
34 #define LIBADS_CCACHE_NAME "MEMORY:libads"
35
36 /*
37   we use a prompter to avoid a crash bug in the kerberos libs when 
38   dealing with empty passwords
39   this prompter is just a string copy ...
40 */
41 static krb5_error_code 
42 kerb_prompter(krb5_context ctx, void *data,
43                const char *name,
44                const char *banner,
45                int num_prompts,
46                krb5_prompt prompts[])
47 {
48         if (num_prompts == 0) return 0;
49
50         memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
51         if (prompts[0].reply->length > 0) {
52                 if (data) {
53                         strncpy((char *)prompts[0].reply->data, (const char *)data,
54                                 prompts[0].reply->length-1);
55                         prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
56                 } else {
57                         prompts[0].reply->length = 0;
58                 }
59         }
60         return 0;
61 }
62
63  static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
64                                                    NTSTATUS *nt_status)
65 {
66         DATA_BLOB edata;
67         DATA_BLOB unwrapped_edata;
68         TALLOC_CTX *mem_ctx;
69         struct KRB5_EDATA_NTSTATUS parsed_edata;
70         enum ndr_err_code ndr_err;
71
72 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
73         edata = data_blob(error->e_data->data, error->e_data->length);
74 #else
75         edata = data_blob(error->e_data.data, error->e_data.length);
76 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
77
78 #ifdef DEVELOPER
79         dump_data(10, edata.data, edata.length);
80 #endif /* DEVELOPER */
81
82         mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
83         if (mem_ctx == NULL) {
84                 data_blob_free(&edata);
85                 return False;
86         }
87
88         if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
89                 data_blob_free(&edata);
90                 TALLOC_FREE(mem_ctx);
91                 return False;
92         }
93
94         data_blob_free(&edata);
95
96         ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx, 
97                 &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
98         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
99                 data_blob_free(&unwrapped_edata);
100                 TALLOC_FREE(mem_ctx);
101                 return False;
102         }
103
104         data_blob_free(&unwrapped_edata);
105
106         if (nt_status) {
107                 *nt_status = parsed_edata.ntstatus;
108         }
109
110         TALLOC_FREE(mem_ctx);
111
112         return True;
113 }
114
115  static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx, 
116                                                                   krb5_get_init_creds_opt *opt, 
117                                                                   NTSTATUS *nt_status)
118 {
119         bool ret = False;
120         krb5_error *error = NULL;
121
122 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
123         ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
124         if (ret) {
125                 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n", 
126                         error_message(ret)));
127                 return False;
128         }
129 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
130
131         if (!error) {
132                 DEBUG(1,("no krb5_error\n"));
133                 return False;
134         }
135
136 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
137         if (!error->e_data) {
138 #else
139         if (error->e_data.data == NULL) {
140 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
141                 DEBUG(1,("no edata in krb5_error\n")); 
142                 krb5_free_error(ctx, error);
143                 return False;
144         }
145
146         ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
147
148         krb5_free_error(ctx, error);
149
150         return ret;
151 }
152
153 /*
154   simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
155   place in default cache location.
156   remus@snapserver.com
157 */
158 int kerberos_kinit_password_ext(const char *principal,
159                                 const char *password,
160                                 int time_offset,
161                                 time_t *expire_time,
162                                 time_t *renew_till_time,
163                                 const char *cache_name,
164                                 bool request_pac,
165                                 bool add_netbios_addr,
166                                 time_t renewable_time,
167                                 NTSTATUS *ntstatus)
168 {
169         krb5_context ctx = NULL;
170         krb5_error_code code = 0;
171         krb5_ccache cc = NULL;
172         krb5_principal me = NULL;
173         krb5_creds my_creds;
174         krb5_get_init_creds_opt *opt = NULL;
175         smb_krb5_addresses *addr = NULL;
176
177         ZERO_STRUCT(my_creds);
178
179         initialize_krb5_error_table();
180         if ((code = krb5_init_context(&ctx)))
181                 goto out;
182
183         if (time_offset != 0) {
184                 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
185         }
186
187         DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
188                         principal,
189                         cache_name ? cache_name: krb5_cc_default_name(ctx),
190                         getenv("KRB5_CONFIG")));
191
192         if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
193                 goto out;
194         }
195         
196         if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
197                 goto out;
198         }
199
200         if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
201                 goto out;
202         }
203
204         krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
205         krb5_get_init_creds_opt_set_forwardable(opt, True);
206 #if 0
207         /* insane testing */
208         krb5_get_init_creds_opt_set_tkt_life(opt, 60);
209 #endif
210
211 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
212         if (request_pac) {
213                 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
214                         goto out;
215                 }
216         }
217 #endif
218         if (add_netbios_addr) {
219                 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
220                         goto out;
221                 }
222                 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
223         }
224
225         if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password), 
226                                                  kerb_prompter, CONST_DISCARD(char *,password),
227                                                  0, NULL, opt))) {
228                 goto out;
229         }
230
231         if ((code = krb5_cc_initialize(ctx, cc, me))) {
232                 goto out;
233         }
234         
235         if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
236                 goto out;
237         }
238
239         if (expire_time) {
240                 *expire_time = (time_t) my_creds.times.endtime;
241         }
242
243         if (renew_till_time) {
244                 *renew_till_time = (time_t) my_creds.times.renew_till;
245         }
246  out:
247         if (ntstatus) {
248
249                 NTSTATUS status;
250
251                 /* fast path */
252                 if (code == 0) {
253                         *ntstatus = NT_STATUS_OK;
254                         goto cleanup;
255                 }
256
257                 /* try to get ntstatus code out of krb5_error when we have it
258                  * inside the krb5_get_init_creds_opt - gd */
259
260                 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
261                         *ntstatus = status;
262                         goto cleanup;
263                 }
264
265                 /* fall back to self-made-mapping */
266                 *ntstatus = krb5_to_nt_status(code);
267         }
268
269  cleanup:
270         krb5_free_cred_contents(ctx, &my_creds);
271         if (me) {
272                 krb5_free_principal(ctx, me);
273         }
274         if (addr) {
275                 smb_krb5_free_addresses(ctx, addr);
276         }
277         if (opt) {
278                 smb_krb5_get_init_creds_opt_free(ctx, opt);
279         }
280         if (cc) {
281                 krb5_cc_close(ctx, cc);
282         }
283         if (ctx) {
284                 krb5_free_context(ctx);
285         }
286         return code;
287 }
288
289 int ads_kdestroy(const char *cc_name)
290 {
291         krb5_error_code code;
292         krb5_context ctx = NULL;
293         krb5_ccache cc = NULL;
294
295         initialize_krb5_error_table();
296         if ((code = krb5_init_context (&ctx))) {
297                 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", 
298                         error_message(code)));
299                 return code;
300         }
301   
302         if (!cc_name) {
303                 if ((code = krb5_cc_default(ctx, &cc))) {
304                         krb5_free_context(ctx);
305                         return code;
306                 }
307         } else {
308                 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
309                         DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
310                                   error_message(code)));
311                         krb5_free_context(ctx);
312                         return code;
313                 }
314         }
315
316         if ((code = krb5_cc_destroy (ctx, cc))) {
317                 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 
318                         error_message(code)));
319         }
320
321         krb5_free_context (ctx);
322         return code;
323 }
324
325 /************************************************************************
326  Routine to fetch the salting principal for a service.  Active
327  Directory may use a non-obvious principal name to generate the salt
328  when it determines the key to use for encrypting tickets for a service,
329  and hopefully we detected that when we joined the domain.
330  ************************************************************************/
331
332 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
333 {
334         char *key = NULL;
335         char *ret = NULL;
336
337         if (asprintf(&key, "%s/%s/enctype=%d",
338                      SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
339                 return NULL;
340         }
341         ret = (char *)secrets_fetch(key, NULL);
342         SAFE_FREE(key);
343         return ret;
344 }
345
346 /************************************************************************
347  Return the standard DES salt key
348 ************************************************************************/
349
350 char* kerberos_standard_des_salt( void )
351 {
352         fstring salt;
353
354         fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
355         strlower_m( salt );
356         fstrcat( salt, lp_realm() );
357
358         return SMB_STRDUP( salt );
359 }
360
361 /************************************************************************
362 ************************************************************************/
363
364 static char* des_salt_key( void )
365 {
366         char *key;
367
368         if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
369                      lp_realm()) == -1) {
370                 return NULL;
371         }
372
373         return key;
374 }
375
376 /************************************************************************
377 ************************************************************************/
378
379 bool kerberos_secrets_store_des_salt( const char* salt )
380 {
381         char* key;
382         bool ret;
383
384         if ( (key = des_salt_key()) == NULL ) {
385                 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
386                 return False;
387         }
388
389         if ( !salt ) {
390                 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
391                 secrets_delete( key );
392                 return True;
393         }
394
395         DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
396
397         ret = secrets_store( key, salt, strlen(salt)+1 );
398
399         SAFE_FREE( key );
400
401         return ret;
402 }
403
404 /************************************************************************
405 ************************************************************************/
406
407 char* kerberos_secrets_fetch_des_salt( void )
408 {
409         char *salt, *key;
410
411         if ( (key = des_salt_key()) == NULL ) {
412                 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
413                 return False;
414         }
415
416         salt = (char*)secrets_fetch( key, NULL );
417
418         SAFE_FREE( key );
419
420         return salt;
421 }
422
423 /************************************************************************
424  Routine to get the default realm from the kerberos credentials cache.
425  Caller must free if the return value is not NULL.
426 ************************************************************************/
427
428 char *kerberos_get_default_realm_from_ccache( void )
429 {
430         char *realm = NULL;
431         krb5_context ctx = NULL;
432         krb5_ccache cc = NULL;
433         krb5_principal princ = NULL;
434
435         initialize_krb5_error_table();
436         if (krb5_init_context(&ctx)) {
437                 return NULL;
438         }
439
440         DEBUG(5,("kerberos_get_default_realm_from_ccache: "
441                 "Trying to read krb5 cache: %s\n",
442                 krb5_cc_default_name(ctx)));
443         if (krb5_cc_default(ctx, &cc)) {
444                 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
445                         "failed to read default cache\n"));
446                 goto out;
447         }
448         if (krb5_cc_get_principal(ctx, cc, &princ)) {
449                 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
450                         "failed to get default principal\n"));
451                 goto out;
452         }
453
454 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
455         realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
456 #elif defined(HAVE_KRB5_PRINC_REALM)
457         {
458                 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
459                 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
460         }
461 #endif
462
463   out:
464
465         if (ctx) {
466                 if (princ) {
467                         krb5_free_principal(ctx, princ);
468                 }
469                 if (cc) {
470                         krb5_cc_close(ctx, cc);
471                 }
472                 krb5_free_context(ctx);
473         }
474
475         return realm;
476 }
477
478 /************************************************************************
479  Routine to get the realm from a given DNS name. Returns malloc'ed memory.
480  Caller must free() if the return value is not NULL.
481 ************************************************************************/
482
483 char *kerberos_get_realm_from_hostname(const char *hostname)
484 {
485 #if defined(HAVE_KRB5_GET_HOST_REALM) && defined(HAVE_KRB5_FREE_HOST_REALM)
486 #if defined(HAVE_KRB5_REALM_TYPE)
487         /* Heimdal. */
488         krb5_realm *realm_list = NULL;
489 #else
490         /* MIT */
491         char **realm_list = NULL;
492 #endif
493         char *realm = NULL;
494         krb5_error_code kerr;
495         krb5_context ctx = NULL;
496
497         initialize_krb5_error_table();
498         if (krb5_init_context(&ctx)) {
499                 return NULL;
500         }
501
502         kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
503         if (kerr != 0) {
504                 DEBUG(3,("kerberos_get_realm_from_hostname %s: "
505                         "failed %s\n",
506                         hostname ? hostname : "(NULL)",
507                         error_message(kerr) ));
508                 goto out;
509         }
510
511         if (realm_list && realm_list[0]) {
512                 realm = SMB_STRDUP(realm_list[0]);
513         }
514
515   out:
516
517         if (ctx) {
518                 if (realm_list) {
519                         krb5_free_host_realm(ctx, realm_list);
520                         realm_list = NULL;
521                 }
522                 krb5_free_context(ctx);
523                 ctx = NULL;
524         }
525         return realm;
526 #else
527         return NULL;
528 #endif
529 }
530
531 /************************************************************************
532  Routine to get the salting principal for this service.  This is 
533  maintained for backwards compatibilty with releases prior to 3.0.24.
534  Since we store the salting principal string only at join, we may have 
535  to look for the older tdb keys.  Caller must free if return is not null.
536  ************************************************************************/
537
538 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
539                                                         krb5_principal host_princ,
540                                                         int enctype)
541 {
542         char *unparsed_name = NULL, *salt_princ_s = NULL;
543         krb5_principal ret_princ = NULL;
544         
545         /* lookup new key first */
546
547         if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
548         
549                 /* look under the old key.  If this fails, just use the standard key */
550
551                 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
552                         return (krb5_principal)NULL;
553                 }
554                 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
555                         /* fall back to host/machine.realm@REALM */
556                         salt_princ_s = kerberos_standard_des_salt();
557                 }
558         }
559
560         if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
561                 ret_princ = NULL;
562         }
563         
564         TALLOC_FREE(unparsed_name);
565         SAFE_FREE(salt_princ_s);
566         
567         return ret_princ;
568 }
569
570 /************************************************************************
571  Routine to set the salting principal for this service.  Active
572  Directory may use a non-obvious principal name to generate the salt
573  when it determines the key to use for encrypting tickets for a service,
574  and hopefully we detected that when we joined the domain.
575  Setting principal to NULL deletes this entry.
576  ************************************************************************/
577
578 bool kerberos_secrets_store_salting_principal(const char *service,
579                                               int enctype,
580                                               const char *principal)
581 {
582         char *key = NULL;
583         bool ret = False;
584         krb5_context context = NULL;
585         krb5_principal princ = NULL;
586         char *princ_s = NULL;
587         char *unparsed_name = NULL;
588         krb5_error_code code;
589
590         if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
591                 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
592                           error_message(code)));
593                 return False;
594         }
595         if (strchr_m(service, '@')) {
596                 if (asprintf(&princ_s, "%s", service) == -1) {
597                         goto out;
598                 }
599         } else {
600                 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
601                         goto out;
602                 }
603         }
604
605         if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
606                 goto out;
607                 
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                 char linkpath[PATH_MAX+1];
962                 int lret;
963
964                 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
965                 if (lret != -1) {
966                         linkpath[lret] = '\0';
967                 }
968
969                 if (lret != -1 || strcmp(linkpath, fname) == 0) {
970                         /* Symlink already exists. */
971                         goto done;
972                 }
973
974                 /* Try and replace with a symlink. */
975                 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
976                         const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
977                         if (errno != EEXIST) {
978                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
979                                         "of %s to %s failed. Errno %s\n",
980                                         fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
981                                 goto done; /* Not a fatal error. */
982                         }
983
984                         /* Yes, this is a race conditon... too bad. */
985                         if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
986                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
987                                         "of %s to %s failed. Errno %s\n",
988                                         SYSTEM_KRB5_CONF_PATH, newpath,
989                                         strerror(errno) ));
990                                 goto done; /* Not a fatal error. */
991                         }
992
993                         if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
994                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
995                                         "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
996                                         fname, strerror(errno) ));
997                                 goto done; /* Not a fatal error. */
998                         }
999                 }
1000         }
1001 #endif
1002
1003 done:
1004         TALLOC_FREE(tmpname);
1005         TALLOC_FREE(dname);
1006
1007         return result;
1008 }
1009 #endif