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