r23779: Change from v2 or later to v3 or later.
[kai/samba-autobuild/.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, write to the Free Software
22    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 */
24
25 #include "includes.h"
26
27 #ifdef HAVE_KRB5
28
29 #define LIBADS_CCACHE_NAME "MEMORY:libads"
30
31 /*
32   we use a prompter to avoid a crash bug in the kerberos libs when 
33   dealing with empty passwords
34   this prompter is just a string copy ...
35 */
36 static krb5_error_code 
37 kerb_prompter(krb5_context ctx, void *data,
38                const char *name,
39                const char *banner,
40                int num_prompts,
41                krb5_prompt prompts[])
42 {
43         if (num_prompts == 0) return 0;
44
45         memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
46         if (prompts[0].reply->length > 0) {
47                 if (data) {
48                         strncpy(prompts[0].reply->data, (const char *)data,
49                                 prompts[0].reply->length-1);
50                         prompts[0].reply->length = strlen(prompts[0].reply->data);
51                 } else {
52                         prompts[0].reply->length = 0;
53                 }
54         }
55         return 0;
56 }
57
58 static BOOL smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx, 
59                                     DATA_BLOB *edata_blob, 
60                                     KRB5_EDATA_NTSTATUS *edata)
61 {
62         BOOL ret = False;
63         prs_struct ps;
64
65         if (!mem_ctx || !edata_blob || !edata) 
66                 return False;
67
68         if (!prs_init(&ps, edata_blob->length, mem_ctx, UNMARSHALL))
69                 return False;
70
71         if (!prs_copy_data_in(&ps, (char *)edata_blob->data, edata_blob->length))
72                 goto out;
73
74         prs_set_offset(&ps, 0);
75
76         if (!prs_ntstatus("ntstatus", &ps, 1, &edata->ntstatus))
77                 goto out;
78
79         if (!prs_uint32("unknown1", &ps, 1, &edata->unknown1))
80                 goto out;
81
82         if (!prs_uint32("unknown2", &ps, 1, &edata->unknown2)) /* only seen 00000001 here */
83                 goto out;
84
85         ret = True;
86  out:
87         prs_mem_free(&ps);
88
89         return ret;
90 }
91
92  static BOOL smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
93                                                    NTSTATUS *nt_status)
94 {
95         DATA_BLOB edata;
96         DATA_BLOB unwrapped_edata;
97         TALLOC_CTX *mem_ctx;
98         KRB5_EDATA_NTSTATUS parsed_edata;
99
100 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
101         edata = data_blob(error->e_data->data, error->e_data->length);
102 #else
103         edata = data_blob(error->e_data.data, error->e_data.length);
104 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
105
106 #ifdef DEVELOPER
107         dump_data(10, edata.data, edata.length);
108 #endif /* DEVELOPER */
109
110         mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
111         if (mem_ctx == NULL) {
112                 data_blob_free(&edata);
113                 return False;
114         }
115
116         if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
117                 data_blob_free(&edata);
118                 TALLOC_FREE(mem_ctx);
119                 return False;
120         }
121
122         data_blob_free(&edata);
123
124         if (!smb_krb5_err_io_nstatus(mem_ctx, &unwrapped_edata, &parsed_edata)) {
125                 data_blob_free(&unwrapped_edata);
126                 TALLOC_FREE(mem_ctx);
127                 return False;
128         }
129
130         data_blob_free(&unwrapped_edata);
131
132         if (nt_status) {
133                 *nt_status = parsed_edata.ntstatus;
134         }
135
136         TALLOC_FREE(mem_ctx);
137
138         return True;
139 }
140
141  static BOOL smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx, 
142                                                                   krb5_get_init_creds_opt *opt, 
143                                                                   NTSTATUS *nt_status)
144 {
145         BOOL ret = False;
146         krb5_error *error = NULL;
147
148 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
149         ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
150         if (ret) {
151                 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n", 
152                         error_message(ret)));
153                 return False;
154         }
155 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
156
157         if (!error) {
158                 DEBUG(1,("no krb5_error\n"));
159                 return False;
160         }
161
162 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
163         if (!error->e_data) {
164 #else
165         if (error->e_data.data == NULL) {
166 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
167                 DEBUG(1,("no edata in krb5_error\n")); 
168                 krb5_free_error(ctx, error);
169                 return False;
170         }
171
172         ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
173
174         krb5_free_error(ctx, error);
175
176         return ret;
177 }
178
179 /*
180   simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
181   place in default cache location.
182   remus@snapserver.com
183 */
184 int kerberos_kinit_password_ext(const char *principal,
185                                 const char *password,
186                                 int time_offset,
187                                 time_t *expire_time,
188                                 time_t *renew_till_time,
189                                 const char *cache_name,
190                                 BOOL request_pac,
191                                 BOOL add_netbios_addr,
192                                 time_t renewable_time,
193                                 NTSTATUS *ntstatus)
194 {
195         krb5_context ctx = NULL;
196         krb5_error_code code = 0;
197         krb5_ccache cc = NULL;
198         krb5_principal me = NULL;
199         krb5_creds my_creds;
200         krb5_get_init_creds_opt *opt = NULL;
201         smb_krb5_addresses *addr = NULL;
202
203         ZERO_STRUCT(my_creds);
204
205         initialize_krb5_error_table();
206         if ((code = krb5_init_context(&ctx)))
207                 goto out;
208
209         if (time_offset != 0) {
210                 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
211         }
212
213         DEBUG(10,("kerberos_kinit_password: using [%s] as ccache and config [%s]\n",
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 /************************************************************************
493  Routine to get the salting principal for this service.  This is 
494  maintained for backwards compatibilty with releases prior to 3.0.24.
495  Since we store the salting principal string only at join, we may have 
496  to look for the older tdb keys.  Caller must free if return is not null.
497  ************************************************************************/
498
499 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
500                                                         krb5_principal host_princ,
501                                                         int enctype)
502 {
503         char *unparsed_name = NULL, *salt_princ_s = NULL;
504         krb5_principal ret_princ = NULL;
505         
506         /* lookup new key first */
507
508         if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
509         
510                 /* look under the old key.  If this fails, just use the standard key */
511
512                 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
513                         return (krb5_principal)NULL;
514                 }
515                 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
516                         /* fall back to host/machine.realm@REALM */
517                         salt_princ_s = kerberos_standard_des_salt();
518                 }
519         }
520
521         if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
522                 ret_princ = NULL;
523         }
524         
525         SAFE_FREE(unparsed_name);
526         SAFE_FREE(salt_princ_s);
527         
528         return ret_princ;
529 }
530
531 /************************************************************************
532  Routine to set the salting principal for this service.  Active
533  Directory may use a non-obvious principal name to generate the salt
534  when it determines the key to use for encrypting tickets for a service,
535  and hopefully we detected that when we joined the domain.
536  Setting principal to NULL deletes this entry.
537  ************************************************************************/
538
539 BOOL kerberos_secrets_store_salting_principal(const char *service,
540                                               int enctype,
541                                               const char *principal)
542 {
543         char *key = NULL;
544         BOOL ret = False;
545         krb5_context context = NULL;
546         krb5_principal princ = NULL;
547         char *princ_s = NULL;
548         char *unparsed_name = NULL;
549
550         krb5_init_context(&context);
551         if (!context) {
552                 return False;
553         }
554         if (strchr_m(service, '@')) {
555                 asprintf(&princ_s, "%s", service);
556         } else {
557                 asprintf(&princ_s, "%s@%s", service, lp_realm());
558         }
559
560         if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
561                 goto out;
562                 
563         }
564         if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
565                 goto out;
566         }
567
568         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
569         if (!key)  {
570                 goto out;
571         }
572
573         if ((principal != NULL) && (strlen(principal) > 0)) {
574                 ret = secrets_store(key, principal, strlen(principal) + 1);
575         } else {
576                 ret = secrets_delete(key);
577         }
578
579  out:
580
581         SAFE_FREE(key);
582         SAFE_FREE(princ_s);
583         SAFE_FREE(unparsed_name);
584
585         if (context) {
586                 krb5_free_context(context);
587         }
588
589         return ret;
590 }
591
592
593 /************************************************************************
594 ************************************************************************/
595
596 int kerberos_kinit_password(const char *principal,
597                             const char *password,
598                             int time_offset,
599                             const char *cache_name)
600 {
601         return kerberos_kinit_password_ext(principal, 
602                                            password, 
603                                            time_offset, 
604                                            0, 
605                                            0,
606                                            cache_name,
607                                            False,
608                                            False,
609                                            0,
610                                            NULL);
611 }
612
613 /************************************************************************
614  Create a string list of available kdc's, possibly searching by sitename.
615  Does DNS queries.
616 ************************************************************************/
617
618 static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
619 {
620         struct ip_service *ip_srv_site;
621         struct ip_service *ip_srv_nonsite;
622         int count_site, count_nonsite, i;
623         char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
624                                         inet_ntoa(primary_ip));
625
626         if (kdc_str == NULL) {
627                 return NULL;
628         }
629
630         /* Get the KDC's only in this site. */
631
632         if (sitename) {
633
634                 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
635
636                 for (i = 0; i < count_site; i++) {
637                         if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
638                                 continue;
639                         }
640                         /* Append to the string - inefficient but not done often. */
641                         kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
642                                 kdc_str, inet_ntoa(ip_srv_site[i].ip));
643                         if (!kdc_str) {
644                                 SAFE_FREE(ip_srv_site);
645                                 return NULL;
646                         }
647                 }
648         }
649
650         /* Get all KDC's. */
651
652         get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
653
654         for (i = 0; i < count_nonsite; i++) {
655                 int j;
656
657                 if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
658                         continue;
659                 }
660
661                 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
662                 for (j = 0; j < count_site; j++) {
663                         if (ip_equal(ip_srv_nonsite[i].ip, ip_srv_site[j].ip)) {
664                                 break;
665                         }
666                         /* As the lists are sorted we can break early if nonsite > site. */
667                         if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
668                                 break;
669                         }
670                 }
671                 if (j != i) {
672                         continue;
673                 }
674
675                 /* Append to the string - inefficient but not done often. */
676                 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
677                         kdc_str, inet_ntoa(ip_srv_nonsite[i].ip));
678                 if (!kdc_str) {
679                         SAFE_FREE(ip_srv_site);
680                         SAFE_FREE(ip_srv_nonsite);
681                         return NULL;
682                 }
683         }
684
685
686         SAFE_FREE(ip_srv_site);
687         SAFE_FREE(ip_srv_nonsite);
688
689         DEBUG(10,("get_kdc_ip_string: Returning %s\n",
690                 kdc_str ));
691
692         return kdc_str;
693 }
694
695 /************************************************************************
696  Create  a specific krb5.conf file in the private directory pointing
697  at a specific kdc for a realm. Keyed off domain name. Sets
698  KRB5_CONFIG environment variable to point to this file. Must be
699  run as root or will fail (which is a good thing :-).
700 ************************************************************************/
701
702 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
703                                         const char *sitename, struct in_addr ip)
704 {
705         char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
706         char *tmpname = NULL;
707         char *fname = NULL;
708         char *file_contents = NULL;
709         char *kdc_ip_string = NULL;
710         size_t flen = 0;
711         ssize_t ret;
712         int fd;
713         char *realm_upper = NULL;
714
715         if (!dname) {
716                 return False;
717         }
718         if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
719                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
720                         "failed to create directory %s. Error was %s\n",
721                         dname, strerror(errno) ));
722                 TALLOC_FREE(dname);
723                 return False;
724         }
725
726         tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
727         if (!tmpname) {
728                 TALLOC_FREE(dname);
729                 return False;
730         }
731
732         fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
733         if (!fname) {
734                 TALLOC_FREE(dname);
735                 return False;
736         }
737
738         DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
739                 fname, realm, domain ));
740
741         realm_upper = talloc_strdup(fname, realm);
742         strupper_m(realm_upper);
743
744         kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
745         if (!kdc_ip_string) {
746                 TALLOC_FREE(dname);
747                 return False;
748         }
749                 
750         file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
751                                 "[realms]\n\t%s = {\n"
752                                 "\t%s\t}\n",
753                                 realm_upper, realm_upper, kdc_ip_string);
754
755         if (!file_contents) {
756                 TALLOC_FREE(dname);
757                 return False;
758         }
759
760         flen = strlen(file_contents);
761
762         fd = smb_mkstemp(tmpname);
763         if (fd == -1) {
764                 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
765                         " for file %s. Errno %s\n",
766                         tmpname, strerror(errno) ));
767         }
768
769         if (fchmod(fd, 0644)==-1) {
770                 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
771                         " Errno %s\n",
772                         tmpname, strerror(errno) ));
773                 unlink(tmpname);
774                 close(fd);
775                 TALLOC_FREE(dname);
776                 return False;
777         }
778
779         ret = write(fd, file_contents, flen);
780         if (flen != ret) {
781                 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
782                         " returned %d (should be %u). Errno %s\n",
783                         (int)ret, (unsigned int)flen, strerror(errno) ));
784                 unlink(tmpname);
785                 close(fd);
786                 TALLOC_FREE(dname);
787                 return False;
788         }
789         if (close(fd)==-1) {
790                 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
791                         " Errno %s\n", strerror(errno) ));
792                 unlink(tmpname);
793                 TALLOC_FREE(dname);
794                 return False;
795         }
796
797         if (rename(tmpname, fname) == -1) {
798                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
799                         "of %s to %s failed. Errno %s\n",
800                         tmpname, fname, strerror(errno) ));
801                 unlink(tmpname);
802                 TALLOC_FREE(dname);
803                 return False;
804         }
805
806         DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
807                 "file %s with realm %s KDC = %s\n",
808                 fname, realm_upper, inet_ntoa(ip) ));
809
810         /* Set the environment variable to this file. */
811         setenv("KRB5_CONFIG", fname, 1);
812
813 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
814
815 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
816         /* Insanity, sheer insanity..... */
817
818         if (strequal(realm, lp_realm())) {
819                 pstring linkpath;
820                 int lret;
821
822                 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
823                 linkpath[sizeof(pstring)-1] = '\0';
824
825                 if (lret == 0 || strcmp(linkpath, fname) == 0) {
826                         /* Symlink already exists. */
827                         TALLOC_FREE(dname);
828                         return True;
829                 }
830
831                 /* Try and replace with a symlink. */
832                 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
833                         if (errno != EEXIST) {
834                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
835                                         "of %s to %s failed. Errno %s\n",
836                                         fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
837                                 TALLOC_FREE(dname);
838                                 return True; /* Not a fatal error. */
839                         }
840
841                         pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
842                         pstrcat(linkpath, ".saved");
843
844                         /* Yes, this is a race conditon... too bad. */
845                         if (rename(SYSTEM_KRB5_CONF_PATH, linkpath) == -1) {
846                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
847                                         "of %s to %s failed. Errno %s\n",
848                                         SYSTEM_KRB5_CONF_PATH, linkpath,
849                                         strerror(errno) ));
850                                 TALLOC_FREE(dname);
851                                 return True; /* Not a fatal error. */
852                         }
853
854                         if (symlink(fname, "/etc/krb5.conf") == -1) {
855                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
856                                         "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
857                                         fname, strerror(errno) ));
858                                 TALLOC_FREE(dname);
859                                 return True; /* Not a fatal error. */
860                         }
861                 }
862         }
863 #endif
864
865         TALLOC_FREE(dname);
866
867         return True;
868 }
869 #endif