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