Revert "s3:messages: allow messaging_dgm_ref() to use wrapper tevent_context"
[samba.git] / source3 / libads / kerberos.c
1 /* 
2    Unix SMB/CIFS implementation.
3    kerberos utility library
4    Copyright (C) Andrew Tridgell 2001
5    Copyright (C) Remus Koos 2001
6    Copyright (C) Nalin Dahyabhai <nalin@redhat.com> 2004.
7    Copyright (C) Jeremy Allison 2004.
8    Copyright (C) Gerald Carter 2006.
9
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 3 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 */
23
24 #include "includes.h"
25 #include "libsmb/namequery.h"
26 #include "system/filesys.h"
27 #include "smb_krb5.h"
28 #include "../librpc/gen_ndr/ndr_misc.h"
29 #include "libads/kerberos_proto.h"
30 #include "libads/cldap.h"
31 #include "secrets.h"
32 #include "../lib/tsocket/tsocket.h"
33 #include "lib/util/asn1.h"
34 #include "krb5_errs.h"
35
36 #ifdef HAVE_KRB5
37
38 #define LIBADS_CCACHE_NAME "MEMORY:libads"
39
40 /*
41   we use a prompter to avoid a crash bug in the kerberos libs when 
42   dealing with empty passwords
43   this prompter is just a string copy ...
44 */
45 static krb5_error_code 
46 kerb_prompter(krb5_context ctx, void *data,
47                const char *name,
48                const char *banner,
49                int num_prompts,
50                krb5_prompt prompts[])
51 {
52         if (num_prompts == 0) return 0;
53         if (num_prompts == 2) {
54                 /*
55                  * only heimdal has a prompt type and we need to deal with it here to
56                  * avoid loops.
57                  *
58                  * removing the prompter completely is not an option as at least these
59                  * versions would crash: heimdal-1.0.2 and heimdal-1.1. Later heimdal
60                  * version have looping detection and return with a proper error code.
61                  */
62
63 #if defined(HAVE_KRB5_PROMPT_TYPE) /* Heimdal */
64                  if (prompts[0].type == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
65                      prompts[1].type == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
66                         /*
67                          * We don't want to change passwords here. We're
68                          * called from heimal when the KDC returns
69                          * KRB5KDC_ERR_KEY_EXPIRED, but at this point we don't
70                          * have the chance to ask the user for a new
71                          * password. If we return 0 (i.e. success), we will be
72                          * spinning in the endless for-loop in
73                          * change_password() in
74                          * source4/heimdal/lib/krb5/init_creds_pw.c:526ff
75                          */
76                         return KRB5KDC_ERR_KEY_EXPIRED;
77                 }
78 #elif defined(HAVE_KRB5_GET_PROMPT_TYPES) /* MIT */
79                 krb5_prompt_type *prompt_types = NULL;
80
81                 prompt_types = krb5_get_prompt_types(ctx);
82                 if (prompt_types != NULL) {
83                         if (prompt_types[0] == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
84                             prompt_types[1] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
85                                 return KRB5KDC_ERR_KEY_EXP;
86                         }
87                 }
88 #endif
89         }
90
91         memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
92         if (prompts[0].reply->length > 0) {
93                 if (data) {
94                         strncpy((char *)prompts[0].reply->data, (const char *)data,
95                                 prompts[0].reply->length-1);
96                         prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
97                 } else {
98                         prompts[0].reply->length = 0;
99                 }
100         }
101         return 0;
102 }
103
104 /*
105   simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
106   place in default cache location.
107   remus@snapserver.com
108 */
109 int kerberos_kinit_password_ext(const char *principal,
110                                 const char *password,
111                                 int time_offset,
112                                 time_t *expire_time,
113                                 time_t *renew_till_time,
114                                 const char *cache_name,
115                                 bool request_pac,
116                                 bool add_netbios_addr,
117                                 time_t renewable_time,
118                                 NTSTATUS *ntstatus)
119 {
120         krb5_context ctx = NULL;
121         krb5_error_code code = 0;
122         krb5_ccache cc = NULL;
123         krb5_principal me = NULL;
124         krb5_principal canon_princ = NULL;
125         krb5_creds my_creds;
126         krb5_get_init_creds_opt *opt = NULL;
127         smb_krb5_addresses *addr = NULL;
128
129         ZERO_STRUCT(my_creds);
130
131         code = smb_krb5_init_context_common(&ctx);
132         if (code != 0) {
133                 DBG_ERR("kerberos init context failed (%s)\n",
134                         error_message(code));
135                 return code;
136         }
137
138         if (time_offset != 0) {
139                 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
140         }
141
142         DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
143                         principal,
144                         cache_name ? cache_name: krb5_cc_default_name(ctx),
145                         getenv("KRB5_CONFIG")));
146
147         if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
148                 goto out;
149         }
150
151         if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
152                 goto out;
153         }
154
155         if ((code = krb5_get_init_creds_opt_alloc(ctx, &opt))) {
156                 goto out;
157         }
158
159         krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
160         krb5_get_init_creds_opt_set_forwardable(opt, True);
161
162         /* Turn on canonicalization for lower case realm support */
163 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
164         krb5_get_init_creds_opt_set_canonicalize(opt, true);
165 #endif /* MIT */
166 #if 0
167         /* insane testing */
168         krb5_get_init_creds_opt_set_tkt_life(opt, 60);
169 #endif
170
171 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
172         if (request_pac) {
173                 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
174                         goto out;
175                 }
176         }
177 #endif
178         if (add_netbios_addr) {
179                 if ((code = smb_krb5_gen_netbios_krb5_address(&addr,
180                                                         lp_netbios_name()))) {
181                         goto out;
182                 }
183                 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
184         }
185
186         if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password), 
187                                                  kerb_prompter, discard_const_p(char, password),
188                                                  0, NULL, opt))) {
189                 goto out;
190         }
191
192         canon_princ = me;
193 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
194         canon_princ = my_creds.client;
195 #endif /* MIT */
196
197         if ((code = krb5_cc_initialize(ctx, cc, canon_princ))) {
198                 goto out;
199         }
200
201         if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
202                 goto out;
203         }
204
205         if (expire_time) {
206                 *expire_time = (time_t) my_creds.times.endtime;
207         }
208
209         if (renew_till_time) {
210                 *renew_till_time = (time_t) my_creds.times.renew_till;
211         }
212  out:
213         if (ntstatus) {
214                 /* fast path */
215                 if (code == 0) {
216                         *ntstatus = NT_STATUS_OK;
217                         goto cleanup;
218                 }
219
220                 /* fall back to self-made-mapping */
221                 *ntstatus = krb5_to_nt_status(code);
222         }
223
224  cleanup:
225         krb5_free_cred_contents(ctx, &my_creds);
226         if (me) {
227                 krb5_free_principal(ctx, me);
228         }
229         if (addr) {
230                 smb_krb5_free_addresses(ctx, addr);
231         }
232         if (opt) {
233                 krb5_get_init_creds_opt_free(ctx, opt);
234         }
235         if (cc) {
236                 krb5_cc_close(ctx, cc);
237         }
238         if (ctx) {
239                 krb5_free_context(ctx);
240         }
241         return code;
242 }
243
244 int ads_kdestroy(const char *cc_name)
245 {
246         krb5_error_code code;
247         krb5_context ctx = NULL;
248         krb5_ccache cc = NULL;
249
250         code = smb_krb5_init_context_common(&ctx);
251         if (code != 0) {
252                 DBG_ERR("kerberos init context failed (%s)\n",
253                         error_message(code));
254                 return code;
255         }
256
257         if (!cc_name) {
258                 if ((code = krb5_cc_default(ctx, &cc))) {
259                         krb5_free_context(ctx);
260                         return code;
261                 }
262         } else {
263                 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
264                         DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
265                                   error_message(code)));
266                         krb5_free_context(ctx);
267                         return code;
268                 }
269         }
270
271         if ((code = krb5_cc_destroy (ctx, cc))) {
272                 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", 
273                         error_message(code)));
274         }
275
276         krb5_free_context (ctx);
277         return code;
278 }
279
280 int create_kerberos_key_from_string(krb5_context context,
281                                         krb5_principal host_princ,
282                                         krb5_principal salt_princ,
283                                         krb5_data *password,
284                                         krb5_keyblock *key,
285                                         krb5_enctype enctype,
286                                         bool no_salt)
287 {
288         int ret;
289         /*
290          * Check if we've determined that the KDC is salting keys for this
291          * principal/enctype in a non-obvious way.  If it is, try to match
292          * its behavior.
293          */
294         if (no_salt) {
295                 KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
296                 if (!KRB5_KEY_DATA(key)) {
297                         return ENOMEM;
298                 }
299                 memcpy(KRB5_KEY_DATA(key), password->data, password->length);
300                 KRB5_KEY_LENGTH(key) = password->length;
301                 KRB5_KEY_TYPE(key) = enctype;
302                 return 0;
303         }
304         ret = smb_krb5_create_key_from_string(context,
305                                               salt_princ ? salt_princ : host_princ,
306                                               NULL,
307                                               password,
308                                               enctype,
309                                               key);
310         return ret;
311 }
312
313 /************************************************************************
314 ************************************************************************/
315
316 int kerberos_kinit_password(const char *principal,
317                             const char *password,
318                             int time_offset,
319                             const char *cache_name)
320 {
321         return kerberos_kinit_password_ext(principal, 
322                                            password, 
323                                            time_offset, 
324                                            0, 
325                                            0,
326                                            cache_name,
327                                            False,
328                                            False,
329                                            0,
330                                            NULL);
331 }
332
333 /************************************************************************
334 ************************************************************************/
335
336 /************************************************************************
337  Create a string list of available kdc's, possibly searching by sitename.
338  Does DNS queries.
339
340  If "sitename" is given, the DC's in that site are listed first.
341
342 ************************************************************************/
343
344 static void add_sockaddr_unique(struct sockaddr_storage *addrs, size_t *num_addrs,
345                                 const struct sockaddr_storage *addr)
346 {
347         size_t i;
348
349         for (i=0; i<*num_addrs; i++) {
350                 if (sockaddr_equal((const struct sockaddr *)&addrs[i],
351                                    (const struct sockaddr *)addr)) {
352                         return;
353                 }
354         }
355         addrs[i] = *addr;
356         *num_addrs += 1;
357 }
358
359 /* print_canonical_sockaddr prints an ipv6 addr in the form of
360 * [ipv6.addr]. This string, when put in a generated krb5.conf file is not
361 * always properly dealt with by some older krb5 libraries. Adding the hard-coded
362 * portnumber workarounds the issue. - gd */
363
364 static char *print_canonical_sockaddr_with_port(TALLOC_CTX *mem_ctx,
365                                                 const struct sockaddr_storage *pss)
366 {
367         char *str = NULL;
368
369         str = print_canonical_sockaddr(mem_ctx, pss);
370         if (str == NULL) {
371                 return NULL;
372         }
373
374         if (pss->ss_family != AF_INET6) {
375                 return str;
376         }
377
378 #if defined(HAVE_IPV6)
379         str = talloc_asprintf_append(str, ":88");
380 #endif
381         return str;
382 }
383
384 static char *get_kdc_ip_string(char *mem_ctx,
385                 const char *realm,
386                 const char *sitename,
387                 const struct sockaddr_storage *pss)
388 {
389         TALLOC_CTX *frame = talloc_stackframe();
390         size_t i;
391         struct ip_service *ip_srv_site = NULL;
392         struct ip_service *ip_srv_nonsite = NULL;
393         int count_site = 0;
394         int count_nonsite;
395         size_t num_dcs;
396         struct sockaddr_storage *dc_addrs;
397         struct tsocket_address **dc_addrs2 = NULL;
398         const struct tsocket_address * const *dc_addrs3 = NULL;
399         char *result = NULL;
400         struct netlogon_samlogon_response **responses = NULL;
401         NTSTATUS status;
402         char *kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n", "",
403                                         print_canonical_sockaddr_with_port(mem_ctx, pss));
404
405         if (kdc_str == NULL) {
406                 TALLOC_FREE(frame);
407                 return NULL;
408         }
409
410         /*
411          * First get the KDC's only in this site, the rest will be
412          * appended later
413          */
414
415         if (sitename) {
416                 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
417                 DEBUG(10, ("got %d addresses from site %s search\n", count_site,
418                            sitename));
419         }
420
421         /* Get all KDC's. */
422
423         get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
424         DEBUG(10, ("got %d addresses from site-less search\n", count_nonsite));
425
426         dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
427                                 count_site + count_nonsite);
428         if (dc_addrs == NULL) {
429                 goto out;
430         }
431
432         num_dcs = 0;
433
434         for (i = 0; i < count_site; i++) {
435                 if (!sockaddr_equal(
436                         (const struct sockaddr *)pss,
437                         (const struct sockaddr *)&ip_srv_site[i].ss)) {
438                         add_sockaddr_unique(dc_addrs, &num_dcs,
439                                             &ip_srv_site[i].ss);
440                 }
441         }
442
443         for (i = 0; i < count_nonsite; i++) {
444                 if (!sockaddr_equal(
445                         (const struct sockaddr *)pss,
446                         (const struct sockaddr *)&ip_srv_nonsite[i].ss)) {
447                         add_sockaddr_unique(dc_addrs, &num_dcs,
448                                             &ip_srv_nonsite[i].ss);
449                 }
450         }
451
452         dc_addrs2 = talloc_zero_array(talloc_tos(),
453                                       struct tsocket_address *,
454                                       num_dcs);
455
456         DBG_DEBUG("%zu additional KDCs to test\n", num_dcs);
457         if (num_dcs == 0) {
458                 goto out;
459         }
460         if (dc_addrs2 == NULL) {
461                 goto out;
462         }
463
464         for (i=0; i<num_dcs; i++) {
465                 char addr[INET6_ADDRSTRLEN];
466                 int ret;
467
468                 print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
469
470                 ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
471                                                         addr, LDAP_PORT,
472                                                         &dc_addrs2[i]);
473                 if (ret != 0) {
474                         status = map_nt_error_from_unix(errno);
475                         DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
476                                  addr, nt_errstr(status)));
477                         goto out;
478                 }
479         }
480
481         dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
482
483         status = cldap_multi_netlogon(talloc_tos(),
484                         dc_addrs3, num_dcs,
485                         realm, lp_netbios_name(),
486                         NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
487                         MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
488         TALLOC_FREE(dc_addrs2);
489         dc_addrs3 = NULL;
490
491         if (!NT_STATUS_IS_OK(status)) {
492                 DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
493                           "%s\n", nt_errstr(status)));
494                 goto out;
495         }
496
497         for (i=0; i<num_dcs; i++) {
498                 char *new_kdc_str;
499
500                 if (responses[i] == NULL) {
501                         continue;
502                 }
503
504                 /* Append to the string - inefficient but not done often. */
505                 new_kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n",
506                                               kdc_str,
507                                               print_canonical_sockaddr_with_port(mem_ctx, &dc_addrs[i]));
508                 if (new_kdc_str == NULL) {
509                         goto out;
510                 }
511                 TALLOC_FREE(kdc_str);
512                 kdc_str = new_kdc_str;
513         }
514
515 out:
516         DEBUG(10, ("get_kdc_ip_string: Returning %s\n", kdc_str));
517
518         result = kdc_str;
519         SAFE_FREE(ip_srv_site);
520         SAFE_FREE(ip_srv_nonsite);
521         TALLOC_FREE(frame);
522         return result;
523 }
524
525 /************************************************************************
526  Create  a specific krb5.conf file in the private directory pointing
527  at a specific kdc for a realm. Keyed off domain name. Sets
528  KRB5_CONFIG environment variable to point to this file. Must be
529  run as root or will fail (which is a good thing :-).
530 ************************************************************************/
531
532 #if !defined(SAMBA4_USES_HEIMDAL) /* MIT version */
533 static char *get_enctypes(TALLOC_CTX *mem_ctx)
534 {
535         char *aes_enctypes = NULL;
536         const char *legacy_enctypes = "";
537         char *enctypes = NULL;
538
539         aes_enctypes = talloc_strdup(mem_ctx, "");
540         if (aes_enctypes == NULL) {
541                 goto done;
542         }
543
544         if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
545             lp_kerberos_encryption_types() == KERBEROS_ETYPES_STRONG) {
546 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
547                 aes_enctypes = talloc_asprintf_append(
548                     aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
549                 if (aes_enctypes == NULL) {
550                         goto done;
551                 }
552 #endif
553 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
554                 aes_enctypes = talloc_asprintf_append(
555                     aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
556                 if (aes_enctypes == NULL) {
557                         goto done;
558                 }
559 #endif
560         }
561
562         if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
563             lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY) {
564                 legacy_enctypes = "RC4-HMAC DES-CBC-CRC DES-CBC-MD5";
565         }
566
567         enctypes =
568             talloc_asprintf(mem_ctx, "\tdefault_tgs_enctypes = %s %s\n"
569                                      "\tdefault_tkt_enctypes = %s %s\n"
570                                      "\tpreferred_enctypes = %s %s\n",
571                             aes_enctypes, legacy_enctypes, aes_enctypes,
572                             legacy_enctypes, aes_enctypes, legacy_enctypes);
573 done:
574         TALLOC_FREE(aes_enctypes);
575         return enctypes;
576 }
577 #else /* Heimdal version */
578 static char *get_enctypes(TALLOC_CTX *mem_ctx)
579 {
580         const char *aes_enctypes = "";
581         const char *legacy_enctypes = "";
582         char *enctypes = NULL;
583
584         if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
585             lp_kerberos_encryption_types() == KERBEROS_ETYPES_STRONG) {
586                 aes_enctypes =
587                     "aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96";
588         }
589
590         if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
591             lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY) {
592                 legacy_enctypes = "arcfour-hmac-md5 des-cbc-crc des-cbc-md5";
593         }
594
595         enctypes = talloc_asprintf(mem_ctx, "\tdefault_etypes = %s %s\n",
596                                    aes_enctypes, legacy_enctypes);
597
598         return enctypes;
599 }
600 #endif
601
602 bool create_local_private_krb5_conf_for_domain(const char *realm,
603                                                 const char *domain,
604                                                 const char *sitename,
605                                                 const struct sockaddr_storage *pss)
606 {
607         char *dname;
608         char *tmpname = NULL;
609         char *fname = NULL;
610         char *file_contents = NULL;
611         char *kdc_ip_string = NULL;
612         size_t flen = 0;
613         ssize_t ret;
614         int fd;
615         char *realm_upper = NULL;
616         bool result = false;
617         char *enctypes = NULL;
618         const char *include_system_krb5 = "";
619         mode_t mask;
620
621         if (!lp_create_krb5_conf()) {
622                 return false;
623         }
624
625         if (realm == NULL) {
626                 DEBUG(0, ("No realm has been specified! Do you really want to "
627                           "join an Active Directory server?\n"));
628                 return false;
629         }
630
631         if (domain == NULL || pss == NULL) {
632                 return false;
633         }
634
635         dname = lock_path(talloc_tos(), "smb_krb5");
636         if (!dname) {
637                 return false;
638         }
639         if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
640                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
641                         "failed to create directory %s. Error was %s\n",
642                         dname, strerror(errno) ));
643                 goto done;
644         }
645
646         tmpname = lock_path(talloc_tos(), "smb_tmp_krb5.XXXXXX");
647         if (!tmpname) {
648                 goto done;
649         }
650
651         fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
652         if (!fname) {
653                 goto done;
654         }
655
656         DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
657                 fname, realm, domain ));
658
659         realm_upper = talloc_strdup(fname, realm);
660         if (!strupper_m(realm_upper)) {
661                 goto done;
662         }
663
664         kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
665         if (!kdc_ip_string) {
666                 goto done;
667         }
668
669         enctypes = get_enctypes(fname);
670         if (enctypes == NULL) {
671                 goto done;
672         }
673
674 #if !defined(SAMBA4_USES_HEIMDAL)
675         if (lp_include_system_krb5_conf()) {
676                 include_system_krb5 = "include /etc/krb5.conf";
677         }
678 #endif
679
680         file_contents =
681             talloc_asprintf(fname,
682                             "[libdefaults]\n\tdefault_realm = %s\n"
683                             "%s"
684                             "\tdns_lookup_realm = false\n\n"
685                             "[realms]\n\t%s = {\n"
686                             "%s\t}\n"
687                             "%s\n",
688                             realm_upper,
689                             enctypes,
690                             realm_upper,
691                             kdc_ip_string,
692                             include_system_krb5);
693
694         if (!file_contents) {
695                 goto done;
696         }
697
698         flen = strlen(file_contents);
699
700         mask = umask(S_IRWXO | S_IRWXG);
701         fd = mkstemp(tmpname);
702         umask(mask);
703         if (fd == -1) {
704                 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
705                         " for file %s. Errno %s\n",
706                         tmpname, strerror(errno) ));
707                 goto done;
708         }
709
710         if (fchmod(fd, 0644)==-1) {
711                 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
712                         " Errno %s\n",
713                         tmpname, strerror(errno) ));
714                 unlink(tmpname);
715                 close(fd);
716                 goto done;
717         }
718
719         ret = write(fd, file_contents, flen);
720         if (flen != ret) {
721                 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
722                         " returned %d (should be %u). Errno %s\n",
723                         (int)ret, (unsigned int)flen, strerror(errno) ));
724                 unlink(tmpname);
725                 close(fd);
726                 goto done;
727         }
728         if (close(fd)==-1) {
729                 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
730                         " Errno %s\n", strerror(errno) ));
731                 unlink(tmpname);
732                 goto done;
733         }
734
735         if (rename(tmpname, fname) == -1) {
736                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
737                         "of %s to %s failed. Errno %s\n",
738                         tmpname, fname, strerror(errno) ));
739                 unlink(tmpname);
740                 goto done;
741         }
742
743         DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
744                 "file %s with realm %s KDC list = %s\n",
745                 fname, realm_upper, kdc_ip_string));
746
747         /* Set the environment variable to this file. */
748         setenv("KRB5_CONFIG", fname, 1);
749
750         result = true;
751
752 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
753
754 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
755         /* Insanity, sheer insanity..... */
756
757         if (strequal(realm, lp_realm())) {
758                 SMB_STRUCT_STAT sbuf;
759
760                 if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
761                         if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
762                                 int lret;
763                                 size_t alloc_size = sbuf.st_ex_size + 1;
764                                 char *linkpath = talloc_array(talloc_tos(), char,
765                                                 alloc_size);
766                                 if (!linkpath) {
767                                         goto done;
768                                 }
769                                 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
770                                                 alloc_size - 1);
771                                 if (lret == -1) {
772                                         TALLOC_FREE(linkpath);
773                                         goto done;
774                                 }
775                                 linkpath[lret] = '\0';
776
777                                 if (strcmp(linkpath, fname) == 0) {
778                                         /* Symlink already exists. */
779                                         TALLOC_FREE(linkpath);
780                                         goto done;
781                                 }
782                                 TALLOC_FREE(linkpath);
783                         }
784                 }
785
786                 /* Try and replace with a symlink. */
787                 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
788                         const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
789                         if (errno != EEXIST) {
790                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
791                                         "of %s to %s failed. Errno %s\n",
792                                         fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
793                                 goto done; /* Not a fatal error. */
794                         }
795
796                         /* Yes, this is a race conditon... too bad. */
797                         if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
798                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
799                                         "of %s to %s failed. Errno %s\n",
800                                         SYSTEM_KRB5_CONF_PATH, newpath,
801                                         strerror(errno) ));
802                                 goto done; /* Not a fatal error. */
803                         }
804
805                         if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
806                                 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
807                                         "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
808                                         fname, strerror(errno) ));
809                                 goto done; /* Not a fatal error. */
810                         }
811                 }
812         }
813 #endif
814
815 done:
816         TALLOC_FREE(tmpname);
817         TALLOC_FREE(dname);
818
819         return result;
820 }
821 #endif