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