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