r22666: Expand kerberos_kinit_password_ext() to return NTSTATUS codes and make
[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 2 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, NULL, 0, NULL, opt))) {
252                 goto out;
253         }
254
255         if ((code = krb5_cc_initialize(ctx, cc, me))) {
256                 goto out;
257         }
258         
259         if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
260                 goto out;
261         }
262
263         if (expire_time) {
264                 *expire_time = (time_t) my_creds.times.endtime;
265         }
266
267         if (renew_till_time) {
268                 *renew_till_time = (time_t) my_creds.times.renew_till;
269         }
270  out:
271         if (ntstatus) {
272
273                 NTSTATUS status;
274
275                 /* fast path */
276                 if (code == 0) {
277                         *ntstatus = NT_STATUS_OK;
278                         goto cleanup;
279                 }
280
281                 /* try to get ntstatus code out of krb5_error when we have it
282                  * inside the krb5_get_init_creds_opt - gd */
283
284                 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
285                         *ntstatus = status;
286                         goto cleanup;
287                 }
288
289                 /* fall back to self-made-mapping */
290                 *ntstatus = krb5_to_nt_status(code);
291         }
292
293  cleanup:
294         krb5_free_cred_contents(ctx, &my_creds);
295         if (me) {
296                 krb5_free_principal(ctx, me);
297         }
298         if (addr) {
299                 smb_krb5_free_addresses(ctx, addr);
300         }
301         if (opt) {
302                 smb_krb5_get_init_creds_opt_free(ctx, opt);
303         }
304         if (cc) {
305                 krb5_cc_close(ctx, cc);
306         }
307         if (ctx) {
308                 krb5_free_context(ctx);
309         }
310         return code;
311 }
312
313
314
315 /* run kinit to setup our ccache */
316 int ads_kinit_password(ADS_STRUCT *ads)
317 {
318         char *s;
319         int ret;
320         const char *account_name;
321         fstring acct_name;
322
323         if ( IS_DC ) {
324                 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
325                 account_name = lp_workgroup();
326         } else {
327                 /* always use the sAMAccountName for security = domain */
328                 /* global_myname()$@REA.LM */
329                 if ( lp_security() == SEC_DOMAIN ) {
330                         fstr_sprintf( acct_name, "%s$", global_myname() );
331                         account_name = acct_name;
332                 }
333                 else 
334                         /* This looks like host/global_myname()@REA.LM */
335                         account_name = ads->auth.user_name;
336         }
337
338         if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
339                 return KRB5_CC_NOMEM;
340         }
341
342         if (!ads->auth.password) {
343                 SAFE_FREE(s);
344                 return KRB5_LIBOS_CANTREADPWD;
345         }
346         
347         ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
348                         &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable, 
349                         NULL);
350
351         if (ret) {
352                 DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 
353                          s, error_message(ret)));
354         }
355         SAFE_FREE(s);
356         return ret;
357 }
358
359 int ads_kdestroy(const char *cc_name)
360 {
361         krb5_error_code code;
362         krb5_context ctx = NULL;
363         krb5_ccache cc = NULL;
364
365         initialize_krb5_error_table();
366         if ((code = krb5_init_context (&ctx))) {
367                 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", 
368                         error_message(code)));
369                 return code;
370         }
371   
372         if (!cc_name) {
373                 if ((code = krb5_cc_default(ctx, &cc))) {
374                         krb5_free_context(ctx);
375                         return code;
376                 }
377         } else {
378                 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
379                         DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
380                                   error_message(code)));
381                         krb5_free_context(ctx);
382                         return code;
383                 }
384         }
385
386         if ((code = krb5_cc_destroy (ctx, cc))) {
387                 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 
388                         error_message(code)));
389         }
390
391         krb5_free_context (ctx);
392         return code;
393 }
394
395 /************************************************************************
396  Routine to fetch the salting principal for a service.  Active
397  Directory may use a non-obvious principal name to generate the salt
398  when it determines the key to use for encrypting tickets for a service,
399  and hopefully we detected that when we joined the domain.
400  ************************************************************************/
401
402 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
403 {
404         char *key = NULL;
405         char *ret = NULL;
406
407         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
408         if (!key) {
409                 return NULL;
410         }
411         ret = (char *)secrets_fetch(key, NULL);
412         SAFE_FREE(key);
413         return ret;
414 }
415
416 /************************************************************************
417  Return the standard DES salt key
418 ************************************************************************/
419
420 char* kerberos_standard_des_salt( void )
421 {
422         fstring salt;
423
424         fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
425         strlower_m( salt );
426         fstrcat( salt, lp_realm() );
427
428         return SMB_STRDUP( salt );
429 }
430
431 /************************************************************************
432 ************************************************************************/
433
434 static char* des_salt_key( void )
435 {
436         char *key;
437
438         asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
439
440         return key;
441 }
442
443 /************************************************************************
444 ************************************************************************/
445
446 BOOL kerberos_secrets_store_des_salt( const char* salt )
447 {
448         char* key;
449         BOOL ret;
450
451         if ( (key = des_salt_key()) == NULL ) {
452                 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
453                 return False;
454         }
455
456         if ( !salt ) {
457                 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
458                 secrets_delete( key );
459                 return True;
460         }
461
462         DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
463
464         ret = secrets_store( key, salt, strlen(salt)+1 );
465
466         SAFE_FREE( key );
467
468         return ret;
469 }
470
471 /************************************************************************
472 ************************************************************************/
473
474 char* kerberos_secrets_fetch_des_salt( void )
475 {
476         char *salt, *key;
477
478         if ( (key = des_salt_key()) == NULL ) {
479                 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
480                 return False;
481         }
482
483         salt = (char*)secrets_fetch( key, NULL );
484
485         SAFE_FREE( key );
486
487         return salt;
488 }
489
490
491 /************************************************************************
492  Routine to get the salting principal for this service.  This is 
493  maintained for backwards compatibilty with releases prior to 3.0.24.
494  Since we store the salting principal string only at join, we may have 
495  to look for the older tdb keys.  Caller must free if return is not null.
496  ************************************************************************/
497
498 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
499                                                         krb5_principal host_princ,
500                                                         int enctype)
501 {
502         char *unparsed_name = NULL, *salt_princ_s = NULL;
503         krb5_principal ret_princ = NULL;
504         
505         /* lookup new key first */
506
507         if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
508         
509                 /* look under the old key.  If this fails, just use the standard key */
510
511                 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
512                         return (krb5_principal)NULL;
513                 }
514                 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
515                         /* fall back to host/machine.realm@REALM */
516                         salt_princ_s = kerberos_standard_des_salt();
517                 }
518         }
519
520         if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
521                 ret_princ = NULL;
522         }
523         
524         SAFE_FREE(unparsed_name);
525         SAFE_FREE(salt_princ_s);
526         
527         return ret_princ;
528 }
529
530 /************************************************************************
531  Routine to set the salting principal for this service.  Active
532  Directory may use a non-obvious principal name to generate the salt
533  when it determines the key to use for encrypting tickets for a service,
534  and hopefully we detected that when we joined the domain.
535  Setting principal to NULL deletes this entry.
536  ************************************************************************/
537
538 BOOL kerberos_secrets_store_salting_principal(const char *service,
539                                               int enctype,
540                                               const char *principal)
541 {
542         char *key = NULL;
543         BOOL ret = False;
544         krb5_context context = NULL;
545         krb5_principal princ = NULL;
546         char *princ_s = NULL;
547         char *unparsed_name = NULL;
548
549         krb5_init_context(&context);
550         if (!context) {
551                 return False;
552         }
553         if (strchr_m(service, '@')) {
554                 asprintf(&princ_s, "%s", service);
555         } else {
556                 asprintf(&princ_s, "%s@%s", service, lp_realm());
557         }
558
559         if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
560                 goto out;
561                 
562         }
563         if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
564                 goto out;
565         }
566
567         asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
568         if (!key)  {
569                 goto out;
570         }
571
572         if ((principal != NULL) && (strlen(principal) > 0)) {
573                 ret = secrets_store(key, principal, strlen(principal) + 1);
574         } else {
575                 ret = secrets_delete(key);
576         }
577
578  out:
579
580         SAFE_FREE(key);
581         SAFE_FREE(princ_s);
582         SAFE_FREE(unparsed_name);
583
584         if (context) {
585                 krb5_free_context(context);
586         }
587
588         return ret;
589 }
590
591
592 /************************************************************************
593 ************************************************************************/
594
595 int kerberos_kinit_password(const char *principal,
596                             const char *password,
597                             int time_offset,
598                             const char *cache_name)
599 {
600         return kerberos_kinit_password_ext(principal, 
601                                            password, 
602                                            time_offset, 
603                                            0, 
604                                            0,
605                                            cache_name,
606                                            False,
607                                            False,
608                                            0,
609                                            NULL);
610 }
611
612 /************************************************************************
613  Create a string list of available kdc's, possibly searching by sitename.
614  Does DNS queries.
615 ************************************************************************/
616
617 static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
618 {
619         struct ip_service *ip_srv_site;
620         struct ip_service *ip_srv_nonsite;
621         int count_site, count_nonsite, i;
622         char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
623                                         inet_ntoa(primary_ip));
624
625         if (kdc_str == NULL) {
626                 return NULL;
627         }
628
629         /* Get the KDC's only in this site. */
630
631         if (sitename) {
632
633                 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
634
635                 for (i = 0; i < count_site; i++) {
636                         if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
637                                 continue;
638                         }
639                         /* Append to the string - inefficient but not done often. */
640                         kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
641                                 kdc_str, inet_ntoa(ip_srv_site[i].ip));
642                         if (!kdc_str) {
643                                 SAFE_FREE(ip_srv_site);
644                                 return NULL;
645                         }
646                 }
647         }
648
649         /* Get all KDC's. */
650
651         get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
652
653         for (i = 0; i < count_nonsite; i++) {
654                 int j;
655
656                 if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
657                         continue;
658                 }
659
660                 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
661                 for (j = 0; j < count_site; j++) {
662                         if (ip_equal(ip_srv_nonsite[i].ip, ip_srv_site[j].ip)) {
663                                 break;
664                         }
665                         /* As the lists are sorted we can break early if nonsite > site. */
666                         if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
667                                 break;
668                         }
669                 }
670                 if (j != i) {
671                         continue;
672                 }
673
674                 /* Append to the string - inefficient but not done often. */
675                 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
676                         kdc_str, inet_ntoa(ip_srv_nonsite[i].ip));
677                 if (!kdc_str) {
678                         SAFE_FREE(ip_srv_site);
679                         SAFE_FREE(ip_srv_nonsite);
680                         return NULL;
681                 }
682         }
683
684
685         SAFE_FREE(ip_srv_site);
686         SAFE_FREE(ip_srv_nonsite);
687
688         DEBUG(10,("get_kdc_ip_string: Returning %s\n",
689                 kdc_str ));
690
691         return kdc_str;
692 }
693
694 /************************************************************************
695  Create  a specific krb5.conf file in the private directory pointing
696  at a specific kdc for a realm. Keyed off domain name. Sets
697  KRB5_CONFIG environment variable to point to this file. Must be
698  run as root or will fail (which is a good thing :-).
699 ************************************************************************/
700
701 BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
702                                         const char *sitename, struct in_addr ip)
703 {
704         char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
705         char *tmpname = NULL;
706         char *fname = NULL;
707         char *file_contents = NULL;
708         char *kdc_ip_string = NULL;
709         size_t flen = 0;
710         ssize_t ret;
711         int fd;
712         char *realm_upper = NULL;
713
714         if (!dname) {
715                 return False;
716         }
717         if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
718                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
719                         "failed to create directory %s. Error was %s\n",
720                         dname, strerror(errno) ));
721                 TALLOC_FREE(dname);
722                 return False;
723         }
724
725         tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
726         if (!tmpname) {
727                 TALLOC_FREE(dname);
728                 return False;
729         }
730
731         fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
732         if (!fname) {
733                 TALLOC_FREE(dname);
734                 return False;
735         }
736
737         DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
738                 fname, realm, domain ));
739
740         realm_upper = talloc_strdup(fname, realm);
741         strupper_m(realm_upper);
742
743         kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
744         if (!kdc_ip_string) {
745                 TALLOC_FREE(dname);
746                 return False;
747         }
748                 
749         file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
750                                 "[realms]\n\t%s = {\n"
751                                 "\t%s\t}\n",
752                                 realm_upper, realm_upper, kdc_ip_string);
753
754         if (!file_contents) {
755                 TALLOC_FREE(dname);
756                 return False;
757         }
758
759         flen = strlen(file_contents);
760
761         fd = smb_mkstemp(tmpname);
762         if (fd == -1) {
763                 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
764                         " for file %s. Errno %s\n",
765                         tmpname, strerror(errno) ));
766         }
767
768         if (fchmod(fd, 0644)==-1) {
769                 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
770                         " Errno %s\n",
771                         tmpname, strerror(errno) ));
772                 unlink(tmpname);
773                 close(fd);
774                 TALLOC_FREE(dname);
775                 return False;
776         }
777
778         ret = write(fd, file_contents, flen);
779         if (flen != ret) {
780                 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
781                         " returned %d (should be %u). Errno %s\n",
782                         (int)ret, (unsigned int)flen, strerror(errno) ));
783                 unlink(tmpname);
784                 close(fd);
785                 TALLOC_FREE(dname);
786                 return False;
787         }
788         if (close(fd)==-1) {
789                 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
790                         " Errno %s\n", strerror(errno) ));
791                 unlink(tmpname);
792                 TALLOC_FREE(dname);
793                 return False;
794         }
795
796         if (rename(tmpname, fname) == -1) {
797                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
798                         "of %s to %s failed. Errno %s\n",
799                         tmpname, fname, strerror(errno) ));
800                 unlink(tmpname);
801                 TALLOC_FREE(dname);
802                 return False;
803         }
804
805         DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
806                 "file %s with realm %s KDC = %s\n",
807                 fname, realm_upper, inet_ntoa(ip) ));
808
809         /* Set the environment variable to this file. */
810         setenv("KRB5_CONFIG", fname, 1);
811
812 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
813
814 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
815         /* Insanity, sheer insanity..... */
816
817         if (strequal(realm, lp_realm())) {
818                 pstring linkpath;
819                 int lret;
820
821                 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
822                 linkpath[sizeof(pstring)-1] = '\0';
823
824                 if (lret == 0 || strcmp(linkpath, fname) == 0) {
825                         /* Symlink already exists. */
826                         TALLOC_FREE(dname);
827                         return True;
828                 }
829
830                 /* Try and replace with a symlink. */
831                 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
832                         if (errno != EEXIST) {
833                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
834                                         "of %s to %s failed. Errno %s\n",
835                                         fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
836                                 TALLOC_FREE(dname);
837                                 return True; /* Not a fatal error. */
838                         }
839
840                         pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
841                         pstrcat(linkpath, ".saved");
842
843                         /* Yes, this is a race conditon... too bad. */
844                         if (rename(SYSTEM_KRB5_CONF_PATH, linkpath) == -1) {
845                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
846                                         "of %s to %s failed. Errno %s\n",
847                                         SYSTEM_KRB5_CONF_PATH, linkpath,
848                                         strerror(errno) ));
849                                 TALLOC_FREE(dname);
850                                 return True; /* Not a fatal error. */
851                         }
852
853                         if (symlink(fname, "/etc/krb5.conf") == -1) {
854                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
855                                         "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
856                                         fname, strerror(errno) ));
857                                 TALLOC_FREE(dname);
858                                 return True; /* Not a fatal error. */
859                         }
860                 }
861         }
862 #endif
863
864         TALLOC_FREE(dname);
865
866         return True;
867 }
868 #endif