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