11bfb4d069d659dea30a552997b633b905aa5b9b
[ira/wip.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         if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
609                 goto out;
610         }
611
612         if (asprintf(&key, "%s/%s/enctype=%d",
613                      SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
614             == -1) {
615                 goto out;
616         }
617
618         if ((principal != NULL) && (strlen(principal) > 0)) {
619                 ret = secrets_store(key, principal, strlen(principal) + 1);
620         } else {
621                 ret = secrets_delete(key);
622         }
623
624  out:
625
626         SAFE_FREE(key);
627         SAFE_FREE(princ_s);
628         TALLOC_FREE(unparsed_name);
629
630         if (princ) {
631                 krb5_free_principal(context, princ);
632         }
633
634         if (context) {
635                 krb5_free_context(context);
636         }
637
638         return ret;
639 }
640
641
642 /************************************************************************
643 ************************************************************************/
644
645 int kerberos_kinit_password(const char *principal,
646                             const char *password,
647                             int time_offset,
648                             const char *cache_name)
649 {
650         return kerberos_kinit_password_ext(principal, 
651                                            password, 
652                                            time_offset, 
653                                            0, 
654                                            0,
655                                            cache_name,
656                                            False,
657                                            False,
658                                            0,
659                                            NULL);
660 }
661
662 /************************************************************************
663 ************************************************************************/
664
665 static char *print_kdc_line(char *mem_ctx,
666                         const char *prev_line,
667                         const struct sockaddr_storage *pss,
668                         const char *kdc_name)
669 {
670         char *kdc_str = NULL;
671
672         if (pss->ss_family == AF_INET) {
673                 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
674                                         prev_line,
675                                         print_canonical_sockaddr(mem_ctx, pss));
676         } else {
677                 char addr[INET6_ADDRSTRLEN];
678                 uint16_t port = get_sockaddr_port(pss);
679
680                 DEBUG(10,("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
681                         kdc_name, port));
682
683                 if (port != 0 && port != DEFAULT_KRB5_PORT) {
684                         /* Currently for IPv6 we can't specify a non-default
685                            krb5 port with an address, as this requires a ':'.
686                            Resolve to a name. */
687                         char hostname[MAX_DNS_NAME_LENGTH];
688                         int ret = sys_getnameinfo((const struct sockaddr *)pss,
689                                         sizeof(*pss),
690                                         hostname, sizeof(hostname),
691                                         NULL, 0,
692                                         NI_NAMEREQD);
693                         if (ret) {
694                                 DEBUG(0,("print_kdc_line: can't resolve name "
695                                         "for kdc with non-default port %s. "
696                                         "Error %s\n.",
697                                         print_canonical_sockaddr(mem_ctx, pss),
698                                         gai_strerror(ret)));
699                                 return NULL;
700                         }
701                         /* Success, use host:port */
702                         kdc_str = talloc_asprintf(mem_ctx,
703                                         "%s\tkdc = %s:%u\n",
704                                         prev_line,
705                                         hostname,
706                                         (unsigned int)port);
707                 } else {
708
709                         /* no krb5 lib currently supports "kdc = ipv6 address"
710                          * at all, so just fill in just the kdc_name if we have
711                          * it and let the krb5 lib figure out the appropriate
712                          * ipv6 address - gd */
713
714                         if (kdc_name) {
715                                 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
716                                                 prev_line, kdc_name);
717                         } else {
718                                 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
719                                                 prev_line,
720                                                 print_sockaddr(addr,
721                                                         sizeof(addr),
722                                                         pss));
723                         }
724                 }
725         }
726         return kdc_str;
727 }
728
729 /************************************************************************
730  Create a string list of available kdc's, possibly searching by sitename.
731  Does DNS queries.
732
733  If "sitename" is given, the DC's in that site are listed first.
734
735 ************************************************************************/
736
737 static char *get_kdc_ip_string(char *mem_ctx,
738                 const char *realm,
739                 const char *sitename,
740                 struct sockaddr_storage *pss,
741                 const char *kdc_name)
742 {
743         int i;
744         struct ip_service *ip_srv_site = NULL;
745         struct ip_service *ip_srv_nonsite = NULL;
746         int count_site = 0;
747         int count_nonsite;
748         char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
749
750         if (kdc_str == NULL) {
751                 return NULL;
752         }
753
754         /*
755          * First get the KDC's only in this site, the rest will be
756          * appended later
757          */
758
759         if (sitename) {
760
761                 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
762
763                 for (i = 0; i < count_site; i++) {
764                         if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss,
765                                                    (struct sockaddr *)pss)) {
766                                 continue;
767                         }
768                         /* Append to the string - inefficient
769                          * but not done often. */
770                         kdc_str = print_kdc_line(mem_ctx,
771                                                 kdc_str,
772                                                 &ip_srv_site[i].ss,
773                                                 NULL);
774                         if (!kdc_str) {
775                                 SAFE_FREE(ip_srv_site);
776                                 return NULL;
777                         }
778                 }
779         }
780
781         /* Get all KDC's. */
782
783         get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
784
785         for (i = 0; i < count_nonsite; i++) {
786                 int j;
787
788                 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) {
789                         continue;
790                 }
791
792                 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
793                 for (j = 0; j < count_site; j++) {
794                         if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss,
795                                                 (struct sockaddr *)&ip_srv_site[j].ss)) {
796                                 break;
797                         }
798                         /* As the lists are sorted we can break early if nonsite > site. */
799                         if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
800                                 break;
801                         }
802                 }
803                 if (j != i) {
804                         continue;
805                 }
806
807                 /* Append to the string - inefficient but not done often. */
808                 kdc_str = print_kdc_line(mem_ctx,
809                                 kdc_str,
810                                 &ip_srv_nonsite[i].ss,
811                                 NULL);
812                 if (!kdc_str) {
813                         SAFE_FREE(ip_srv_site);
814                         SAFE_FREE(ip_srv_nonsite);
815                         return NULL;
816                 }
817         }
818
819
820         SAFE_FREE(ip_srv_site);
821         SAFE_FREE(ip_srv_nonsite);
822
823         DEBUG(10,("get_kdc_ip_string: Returning %s\n",
824                 kdc_str ));
825
826         return kdc_str;
827 }
828
829 /************************************************************************
830  Create  a specific krb5.conf file in the private directory pointing
831  at a specific kdc for a realm. Keyed off domain name. Sets
832  KRB5_CONFIG environment variable to point to this file. Must be
833  run as root or will fail (which is a good thing :-).
834 ************************************************************************/
835
836 bool create_local_private_krb5_conf_for_domain(const char *realm,
837                                                 const char *domain,
838                                                 const char *sitename,
839                                                 struct sockaddr_storage *pss,
840                                                 const char *kdc_name)
841 {
842         char *dname;
843         char *tmpname = NULL;
844         char *fname = NULL;
845         char *file_contents = NULL;
846         char *kdc_ip_string = NULL;
847         size_t flen = 0;
848         ssize_t ret;
849         int fd;
850         char *realm_upper = NULL;
851         bool result = false;
852
853         if (!lp_create_krb5_conf()) {
854                 return false;
855         }
856
857         dname = lock_path("smb_krb5");
858         if (!dname) {
859                 return false;
860         }
861         if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
862                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
863                         "failed to create directory %s. Error was %s\n",
864                         dname, strerror(errno) ));
865                 goto done;
866         }
867
868         tmpname = lock_path("smb_tmp_krb5.XXXXXX");
869         if (!tmpname) {
870                 goto done;
871         }
872
873         fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
874         if (!fname) {
875                 goto done;
876         }
877
878         DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
879                 fname, realm, domain ));
880
881         realm_upper = talloc_strdup(fname, realm);
882         strupper_m(realm_upper);
883
884         kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
885         if (!kdc_ip_string) {
886                 goto done;
887         }
888
889         file_contents = talloc_asprintf(fname,
890                                         "[libdefaults]\n\tdefault_realm = %s\n"
891                                         "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
892                                         "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
893                                         "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
894                                         "[realms]\n\t%s = {\n"
895                                         "\t%s\t}\n",
896                                         realm_upper, realm_upper, kdc_ip_string);
897
898         if (!file_contents) {
899                 goto done;
900         }
901
902         flen = strlen(file_contents);
903
904         fd = mkstemp(tmpname);
905         if (fd == -1) {
906                 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
907                         " for file %s. Errno %s\n",
908                         tmpname, strerror(errno) ));
909                 goto done;
910         }
911
912         if (fchmod(fd, 0644)==-1) {
913                 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
914                         " Errno %s\n",
915                         tmpname, strerror(errno) ));
916                 unlink(tmpname);
917                 close(fd);
918                 goto done;
919         }
920
921         ret = write(fd, file_contents, flen);
922         if (flen != ret) {
923                 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
924                         " returned %d (should be %u). Errno %s\n",
925                         (int)ret, (unsigned int)flen, strerror(errno) ));
926                 unlink(tmpname);
927                 close(fd);
928                 goto done;
929         }
930         if (close(fd)==-1) {
931                 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
932                         " Errno %s\n", strerror(errno) ));
933                 unlink(tmpname);
934                 goto done;
935         }
936
937         if (rename(tmpname, fname) == -1) {
938                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
939                         "of %s to %s failed. Errno %s\n",
940                         tmpname, fname, strerror(errno) ));
941                 unlink(tmpname);
942                 goto done;
943         }
944
945         DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
946                 "file %s with realm %s KDC list = %s\n",
947                 fname, realm_upper, kdc_ip_string));
948
949         /* Set the environment variable to this file. */
950         setenv("KRB5_CONFIG", fname, 1);
951
952         result = true;
953
954 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
955
956 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
957         /* Insanity, sheer insanity..... */
958
959         if (strequal(realm, lp_realm())) {
960                 char linkpath[PATH_MAX+1];
961                 int lret;
962
963                 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
964                 if (lret != -1) {
965                         linkpath[lret] = '\0';
966                 }
967
968                 if (lret != -1 || strcmp(linkpath, fname) == 0) {
969                         /* Symlink already exists. */
970                         goto done;
971                 }
972
973                 /* Try and replace with a symlink. */
974                 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
975                         const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
976                         if (errno != EEXIST) {
977                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
978                                         "of %s to %s failed. Errno %s\n",
979                                         fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
980                                 goto done; /* Not a fatal error. */
981                         }
982
983                         /* Yes, this is a race conditon... too bad. */
984                         if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
985                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
986                                         "of %s to %s failed. Errno %s\n",
987                                         SYSTEM_KRB5_CONF_PATH, newpath,
988                                         strerror(errno) ));
989                                 goto done; /* Not a fatal error. */
990                         }
991
992                         if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
993                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
994                                         "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
995                                         fname, strerror(errno) ));
996                                 goto done; /* Not a fatal error. */
997                         }
998                 }
999         }
1000 #endif
1001
1002 done:
1003         TALLOC_FREE(tmpname);
1004         TALLOC_FREE(dname);
1005
1006         return result;
1007 }
1008 #endif