s3: libsmb: Correctly do lifecycle management on cli->smb1.tcon and cli->smb2.tcon.
[Samba.git] / source3 / libads / kerberos.c
blob13c48ca40236b21aa86bea8966e073faf4336890
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.
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.
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.
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/>.
24 #include "includes.h"
25 #include "system/filesys.h"
26 #include "smb_krb5.h"
27 #include "../librpc/gen_ndr/ndr_misc.h"
28 #include "libads/kerberos_proto.h"
29 #include "libads/cldap.h"
30 #include "secrets.h"
31 #include "../lib/tsocket/tsocket.h"
32 #include "lib/util/asn1.h"
34 #ifdef HAVE_KRB5
36 #define LIBADS_CCACHE_NAME "MEMORY:libads"
39 we use a prompter to avoid a crash bug in the kerberos libs when
40 dealing with empty passwords
41 this prompter is just a string copy ...
43 static krb5_error_code
44 kerb_prompter(krb5_context ctx, void *data,
45 const char *name,
46 const char *banner,
47 int num_prompts,
48 krb5_prompt prompts[])
50 if (num_prompts == 0) return 0;
51 if (num_prompts == 2) {
53 * only heimdal has a prompt type and we need to deal with it here to
54 * avoid loops.
56 * removing the prompter completely is not an option as at least these
57 * versions would crash: heimdal-1.0.2 and heimdal-1.1. Later heimdal
58 * version have looping detection and return with a proper error code.
61 #if HAVE_KRB5_PROMPT_TYPE /* Heimdal */
62 if (prompts[0].type == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
63 prompts[1].type == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
65 * We don't want to change passwords here. We're
66 * called from heimal when the KDC returns
67 * KRB5KDC_ERR_KEY_EXPIRED, but at this point we don't
68 * have the chance to ask the user for a new
69 * password. If we return 0 (i.e. success), we will be
70 * spinning in the endless for-loop in
71 * change_password() in
72 * source4/heimdal/lib/krb5/init_creds_pw.c:526ff
74 return KRB5KDC_ERR_KEY_EXPIRED;
76 #elif defined(HAVE_KRB5_GET_PROMPT_TYPES) /* MIT */
77 krb5_prompt_type *prompt_types = NULL;
79 prompt_types = krb5_get_prompt_types(ctx);
80 if (prompt_types != NULL) {
81 if (prompt_types[0] == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
82 prompt_types[1] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
83 return KRB5KDC_ERR_KEY_EXP;
86 #endif
89 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
90 if (prompts[0].reply->length > 0) {
91 if (data) {
92 strncpy((char *)prompts[0].reply->data, (const char *)data,
93 prompts[0].reply->length-1);
94 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
95 } else {
96 prompts[0].reply->length = 0;
99 return 0;
103 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
104 place in default cache location.
105 remus@snapserver.com
107 int kerberos_kinit_password_ext(const char *principal,
108 const char *password,
109 int time_offset,
110 time_t *expire_time,
111 time_t *renew_till_time,
112 const char *cache_name,
113 bool request_pac,
114 bool add_netbios_addr,
115 time_t renewable_time,
116 NTSTATUS *ntstatus)
118 krb5_context ctx = NULL;
119 krb5_error_code code = 0;
120 krb5_ccache cc = NULL;
121 krb5_principal me = NULL;
122 krb5_principal canon_princ = NULL;
123 krb5_creds my_creds;
124 krb5_get_init_creds_opt *opt = NULL;
125 smb_krb5_addresses *addr = NULL;
127 ZERO_STRUCT(my_creds);
129 initialize_krb5_error_table();
130 if ((code = krb5_init_context(&ctx)))
131 goto out;
133 if (time_offset != 0) {
134 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
137 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
138 principal,
139 cache_name ? cache_name: krb5_cc_default_name(ctx),
140 getenv("KRB5_CONFIG")));
142 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
143 goto out;
146 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
147 goto out;
150 if ((code = krb5_get_init_creds_opt_alloc(ctx, &opt))) {
151 goto out;
154 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
155 krb5_get_init_creds_opt_set_forwardable(opt, True);
157 /* Turn on canonicalization for lower case realm support */
158 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
159 krb5_get_init_creds_opt_set_canonicalize(opt, true);
160 #endif /* MIT */
161 #if 0
162 /* insane testing */
163 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
164 #endif
166 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
167 if (request_pac) {
168 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
169 goto out;
172 #endif
173 if (add_netbios_addr) {
174 if ((code = smb_krb5_gen_netbios_krb5_address(&addr,
175 lp_netbios_name()))) {
176 goto out;
178 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
181 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password),
182 kerb_prompter, discard_const_p(char, password),
183 0, NULL, opt))) {
184 goto out;
187 canon_princ = me;
188 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
189 canon_princ = my_creds.client;
190 #endif /* MIT */
192 if ((code = krb5_cc_initialize(ctx, cc, canon_princ))) {
193 goto out;
196 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
197 goto out;
200 if (expire_time) {
201 *expire_time = (time_t) my_creds.times.endtime;
204 if (renew_till_time) {
205 *renew_till_time = (time_t) my_creds.times.renew_till;
207 out:
208 if (ntstatus) {
209 /* fast path */
210 if (code == 0) {
211 *ntstatus = NT_STATUS_OK;
212 goto cleanup;
215 /* fall back to self-made-mapping */
216 *ntstatus = krb5_to_nt_status(code);
219 cleanup:
220 krb5_free_cred_contents(ctx, &my_creds);
221 if (me) {
222 krb5_free_principal(ctx, me);
224 if (addr) {
225 smb_krb5_free_addresses(ctx, addr);
227 if (opt) {
228 krb5_get_init_creds_opt_free(ctx, opt);
230 if (cc) {
231 krb5_cc_close(ctx, cc);
233 if (ctx) {
234 krb5_free_context(ctx);
236 return code;
239 int ads_kdestroy(const char *cc_name)
241 krb5_error_code code;
242 krb5_context ctx = NULL;
243 krb5_ccache cc = NULL;
245 initialize_krb5_error_table();
246 if ((code = krb5_init_context (&ctx))) {
247 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
248 error_message(code)));
249 return code;
252 if (!cc_name) {
253 if ((code = krb5_cc_default(ctx, &cc))) {
254 krb5_free_context(ctx);
255 return code;
257 } else {
258 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
259 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
260 error_message(code)));
261 krb5_free_context(ctx);
262 return code;
266 if ((code = krb5_cc_destroy (ctx, cc))) {
267 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
268 error_message(code)));
271 krb5_free_context (ctx);
272 return code;
275 /************************************************************************
276 Routine to fetch the salting principal for a service. Active
277 Directory may use a non-obvious principal name to generate the salt
278 when it determines the key to use for encrypting tickets for a service,
279 and hopefully we detected that when we joined the domain.
280 ************************************************************************/
282 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
284 char *key = NULL;
285 char *ret = NULL;
287 if (asprintf(&key, "%s/%s/enctype=%d",
288 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
289 return NULL;
291 ret = (char *)secrets_fetch(key, NULL);
292 SAFE_FREE(key);
293 return ret;
296 /************************************************************************
297 Return the standard DES salt key
298 ************************************************************************/
300 char* kerberos_standard_des_salt( void )
302 fstring salt;
304 fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
305 (void)strlower_m( salt );
306 fstrcat( salt, lp_realm() );
308 return SMB_STRDUP( salt );
311 /************************************************************************
312 ************************************************************************/
314 static char* des_salt_key( void )
316 char *key;
318 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
319 lp_realm()) == -1) {
320 return NULL;
323 return key;
326 /************************************************************************
327 ************************************************************************/
329 bool kerberos_secrets_store_des_salt( const char* salt )
331 char* key;
332 bool ret;
334 if ( (key = des_salt_key()) == NULL ) {
335 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
336 return False;
339 if ( !salt ) {
340 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
341 secrets_delete( key );
342 return True;
345 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
347 ret = secrets_store( key, salt, strlen(salt)+1 );
349 SAFE_FREE( key );
351 return ret;
354 /************************************************************************
355 ************************************************************************/
357 static
358 char* kerberos_secrets_fetch_des_salt( void )
360 char *salt, *key;
362 if ( (key = des_salt_key()) == NULL ) {
363 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
364 return NULL;
367 salt = (char*)secrets_fetch( key, NULL );
369 SAFE_FREE( key );
371 return salt;
374 /************************************************************************
375 Routine to get the salting principal for this service. This is
376 maintained for backwards compatibilty with releases prior to 3.0.24.
377 Since we store the salting principal string only at join, we may have
378 to look for the older tdb keys. Caller must free if return is not null.
379 ************************************************************************/
381 char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
382 const char *host_princ_s,
383 int enctype)
385 char *salt_princ_s;
386 /* lookup new key first */
388 salt_princ_s = kerberos_secrets_fetch_des_salt();
389 if (salt_princ_s == NULL) {
391 /* look under the old key. If this fails, just use the standard key */
392 salt_princ_s = kerberos_secrets_fetch_salting_principal(host_princ_s,
393 enctype);
394 if (salt_princ_s == NULL) {
395 /* fall back to host/machine.realm@REALM */
396 salt_princ_s = kerberos_standard_des_salt();
400 return salt_princ_s;
403 int create_kerberos_key_from_string(krb5_context context,
404 krb5_principal host_princ,
405 krb5_principal salt_princ,
406 krb5_data *password,
407 krb5_keyblock *key,
408 krb5_enctype enctype,
409 bool no_salt)
411 int ret;
413 * Check if we've determined that the KDC is salting keys for this
414 * principal/enctype in a non-obvious way. If it is, try to match
415 * its behavior.
417 if (no_salt) {
418 KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
419 if (!KRB5_KEY_DATA(key)) {
420 return ENOMEM;
422 memcpy(KRB5_KEY_DATA(key), password->data, password->length);
423 KRB5_KEY_LENGTH(key) = password->length;
424 KRB5_KEY_TYPE(key) = enctype;
425 return 0;
427 ret = smb_krb5_create_key_from_string(context,
428 salt_princ ? salt_princ : host_princ,
429 NULL,
430 password,
431 enctype,
432 key);
433 return ret;
436 /************************************************************************
437 Routine to set the salting principal for this service. Active
438 Directory may use a non-obvious principal name to generate the salt
439 when it determines the key to use for encrypting tickets for a service,
440 and hopefully we detected that when we joined the domain.
441 Setting principal to NULL deletes this entry.
442 ************************************************************************/
444 bool kerberos_secrets_store_salting_principal(const char *service,
445 int enctype,
446 const char *principal)
448 char *key = NULL;
449 bool ret = False;
450 krb5_context context = NULL;
451 krb5_principal princ = NULL;
452 char *princ_s = NULL;
453 char *unparsed_name = NULL;
454 krb5_error_code code;
456 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
457 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
458 error_message(code)));
459 return False;
461 if (strchr_m(service, '@')) {
462 if (asprintf(&princ_s, "%s", service) == -1) {
463 goto out;
465 } else {
466 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
467 goto out;
471 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
472 goto out;
474 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
475 goto out;
478 if (asprintf(&key, "%s/%s/enctype=%d",
479 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
480 == -1) {
481 goto out;
484 if ((principal != NULL) && (strlen(principal) > 0)) {
485 ret = secrets_store(key, principal, strlen(principal) + 1);
486 } else {
487 ret = secrets_delete(key);
490 out:
492 SAFE_FREE(key);
493 SAFE_FREE(princ_s);
494 TALLOC_FREE(unparsed_name);
496 if (princ) {
497 krb5_free_principal(context, princ);
500 if (context) {
501 krb5_free_context(context);
504 return ret;
508 /************************************************************************
509 ************************************************************************/
511 int kerberos_kinit_password(const char *principal,
512 const char *password,
513 int time_offset,
514 const char *cache_name)
516 return kerberos_kinit_password_ext(principal,
517 password,
518 time_offset,
521 cache_name,
522 False,
523 False,
525 NULL);
528 /************************************************************************
529 ************************************************************************/
531 /************************************************************************
532 Create a string list of available kdc's, possibly searching by sitename.
533 Does DNS queries.
535 If "sitename" is given, the DC's in that site are listed first.
537 ************************************************************************/
539 static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
540 const struct sockaddr_storage *addr)
542 int i;
544 for (i=0; i<*num_addrs; i++) {
545 if (sockaddr_equal((const struct sockaddr *)&addrs[i],
546 (const struct sockaddr *)addr)) {
547 return;
550 addrs[i] = *addr;
551 *num_addrs += 1;
554 /* print_canonical_sockaddr prints an ipv6 addr in the form of
555 * [ipv6.addr]. This string, when put in a generated krb5.conf file is not
556 * always properly dealt with by some older krb5 libraries. Adding the hard-coded
557 * portnumber workarounds the issue. - gd */
559 static char *print_canonical_sockaddr_with_port(TALLOC_CTX *mem_ctx,
560 const struct sockaddr_storage *pss)
562 char *str = NULL;
564 str = print_canonical_sockaddr(mem_ctx, pss);
565 if (str == NULL) {
566 return NULL;
569 if (pss->ss_family != AF_INET6) {
570 return str;
573 #if defined(HAVE_IPV6)
574 str = talloc_asprintf_append(str, ":88");
575 #endif
576 return str;
579 static char *get_kdc_ip_string(char *mem_ctx,
580 const char *realm,
581 const char *sitename,
582 const struct sockaddr_storage *pss)
584 TALLOC_CTX *frame = talloc_stackframe();
585 int i;
586 struct ip_service *ip_srv_site = NULL;
587 struct ip_service *ip_srv_nonsite = NULL;
588 int count_site = 0;
589 int count_nonsite;
590 int num_dcs;
591 struct sockaddr_storage *dc_addrs;
592 struct tsocket_address **dc_addrs2 = NULL;
593 const struct tsocket_address * const *dc_addrs3 = NULL;
594 char *result = NULL;
595 struct netlogon_samlogon_response **responses = NULL;
596 NTSTATUS status;
597 char *kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n", "",
598 print_canonical_sockaddr_with_port(mem_ctx, pss));
600 if (kdc_str == NULL) {
601 TALLOC_FREE(frame);
602 return NULL;
606 * First get the KDC's only in this site, the rest will be
607 * appended later
610 if (sitename) {
611 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
612 DEBUG(10, ("got %d addresses from site %s search\n", count_site,
613 sitename));
616 /* Get all KDC's. */
618 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
619 DEBUG(10, ("got %d addresses from site-less search\n", count_nonsite));
621 dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
622 count_site + count_nonsite);
623 if (dc_addrs == NULL) {
624 goto out;
627 num_dcs = 0;
629 for (i = 0; i < count_site; i++) {
630 if (!sockaddr_equal(
631 (const struct sockaddr *)pss,
632 (const struct sockaddr *)&ip_srv_site[i].ss)) {
633 add_sockaddr_unique(dc_addrs, &num_dcs,
634 &ip_srv_site[i].ss);
638 for (i = 0; i < count_nonsite; i++) {
639 if (!sockaddr_equal(
640 (const struct sockaddr *)pss,
641 (const struct sockaddr *)&ip_srv_nonsite[i].ss)) {
642 add_sockaddr_unique(dc_addrs, &num_dcs,
643 &ip_srv_nonsite[i].ss);
647 dc_addrs2 = talloc_zero_array(talloc_tos(),
648 struct tsocket_address *,
649 num_dcs);
651 DEBUG(10, ("%d additional KDCs to test\n", num_dcs));
652 if (num_dcs == 0) {
653 goto out;
655 if (dc_addrs2 == NULL) {
656 goto out;
659 for (i=0; i<num_dcs; i++) {
660 char addr[INET6_ADDRSTRLEN];
661 int ret;
663 print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
665 ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
666 addr, LDAP_PORT,
667 &dc_addrs2[i]);
668 if (ret != 0) {
669 status = map_nt_error_from_unix(errno);
670 DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
671 addr, nt_errstr(status)));
672 goto out;
676 dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
678 status = cldap_multi_netlogon(talloc_tos(),
679 dc_addrs3, num_dcs,
680 realm, lp_netbios_name(),
681 NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
682 MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
683 TALLOC_FREE(dc_addrs2);
684 dc_addrs3 = NULL;
686 if (!NT_STATUS_IS_OK(status)) {
687 DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
688 "%s\n", nt_errstr(status)));
689 goto out;
692 for (i=0; i<num_dcs; i++) {
693 char *new_kdc_str;
695 if (responses[i] == NULL) {
696 continue;
699 /* Append to the string - inefficient but not done often. */
700 new_kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n",
701 kdc_str,
702 print_canonical_sockaddr_with_port(mem_ctx, &dc_addrs[i]));
703 if (new_kdc_str == NULL) {
704 goto out;
706 TALLOC_FREE(kdc_str);
707 kdc_str = new_kdc_str;
710 out:
711 DEBUG(10, ("get_kdc_ip_string: Returning %s\n", kdc_str));
713 result = kdc_str;
714 SAFE_FREE(ip_srv_site);
715 SAFE_FREE(ip_srv_nonsite);
716 TALLOC_FREE(frame);
717 return result;
720 /************************************************************************
721 Create a specific krb5.conf file in the private directory pointing
722 at a specific kdc for a realm. Keyed off domain name. Sets
723 KRB5_CONFIG environment variable to point to this file. Must be
724 run as root or will fail (which is a good thing :-).
725 ************************************************************************/
727 #if !defined(SAMBA4_USES_HEIMDAL) /* MIT version */
728 static char *get_enctypes(TALLOC_CTX *mem_ctx)
730 char *aes_enctypes = NULL;
731 const char *legacy_enctypes = "";
732 char *enctypes = NULL;
734 aes_enctypes = talloc_strdup(mem_ctx, "");
735 if (aes_enctypes == NULL) {
736 goto done;
739 if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
740 lp_kerberos_encryption_types() == KERBEROS_ETYPES_STRONG) {
741 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
742 aes_enctypes = talloc_asprintf_append(
743 aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
744 if (aes_enctypes == NULL) {
745 goto done;
747 #endif
748 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
749 aes_enctypes = talloc_asprintf_append(
750 aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
751 if (aes_enctypes == NULL) {
752 goto done;
754 #endif
757 if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
758 lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY) {
759 legacy_enctypes = "RC4-HMAC DES-CBC-CRC DES-CBC-MD5";
762 enctypes =
763 talloc_asprintf(mem_ctx, "\tdefault_tgs_enctypes = %s %s\n"
764 "\tdefault_tkt_enctypes = %s %s\n"
765 "\tpreferred_enctypes = %s %s\n",
766 aes_enctypes, legacy_enctypes, aes_enctypes,
767 legacy_enctypes, aes_enctypes, legacy_enctypes);
768 done:
769 TALLOC_FREE(aes_enctypes);
770 return enctypes;
772 #else /* Heimdal version */
773 static char *get_enctypes(TALLOC_CTX *mem_ctx)
775 const char *aes_enctypes = "";
776 const char *legacy_enctypes = "";
777 char *enctypes = NULL;
779 if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
780 lp_kerberos_encryption_types() == KERBEROS_ETYPES_STRONG) {
781 aes_enctypes =
782 "aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96";
785 if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
786 lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY) {
787 legacy_enctypes = "arcfour-hmac-md5 des-cbc-crc des-cbc-md5";
790 enctypes = talloc_asprintf(mem_ctx, "\tdefault_etypes = %s %s\n",
791 aes_enctypes, legacy_enctypes);
793 return enctypes;
795 #endif
797 bool create_local_private_krb5_conf_for_domain(const char *realm,
798 const char *domain,
799 const char *sitename,
800 const struct sockaddr_storage *pss)
802 char *dname;
803 char *tmpname = NULL;
804 char *fname = NULL;
805 char *file_contents = NULL;
806 char *kdc_ip_string = NULL;
807 size_t flen = 0;
808 ssize_t ret;
809 int fd;
810 char *realm_upper = NULL;
811 bool result = false;
812 char *enctypes = NULL;
813 const char *include_system_krb5 = "";
814 mode_t mask;
816 if (!lp_create_krb5_conf()) {
817 return false;
820 if (realm == NULL) {
821 DEBUG(0, ("No realm has been specified! Do you really want to "
822 "join an Active Directory server?\n"));
823 return false;
826 if (domain == NULL || pss == NULL) {
827 return false;
830 dname = lock_path("smb_krb5");
831 if (!dname) {
832 return false;
834 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
835 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
836 "failed to create directory %s. Error was %s\n",
837 dname, strerror(errno) ));
838 goto done;
841 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
842 if (!tmpname) {
843 goto done;
846 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
847 if (!fname) {
848 goto done;
851 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
852 fname, realm, domain ));
854 realm_upper = talloc_strdup(fname, realm);
855 if (!strupper_m(realm_upper)) {
856 goto done;
859 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
860 if (!kdc_ip_string) {
861 goto done;
864 enctypes = get_enctypes(fname);
865 if (enctypes == NULL) {
866 goto done;
869 #if !defined(SAMBA4_USES_HEIMDAL)
870 if (lp_include_system_krb5_conf()) {
871 include_system_krb5 = "include /etc/krb5.conf";
873 #endif
875 file_contents =
876 talloc_asprintf(fname,
877 "[libdefaults]\n\tdefault_realm = %s\n"
878 "%s"
879 "\tdns_lookup_realm = false\n\n"
880 "[realms]\n\t%s = {\n"
881 "%s\t}\n"
882 "%s\n",
883 realm_upper,
884 enctypes,
885 realm_upper,
886 kdc_ip_string,
887 include_system_krb5);
889 if (!file_contents) {
890 goto done;
893 flen = strlen(file_contents);
895 mask = umask(S_IRWXO | S_IRWXG);
896 fd = mkstemp(tmpname);
897 umask(mask);
898 if (fd == -1) {
899 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
900 " for file %s. Errno %s\n",
901 tmpname, strerror(errno) ));
902 goto done;
905 if (fchmod(fd, 0644)==-1) {
906 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
907 " Errno %s\n",
908 tmpname, strerror(errno) ));
909 unlink(tmpname);
910 close(fd);
911 goto done;
914 ret = write(fd, file_contents, flen);
915 if (flen != ret) {
916 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
917 " returned %d (should be %u). Errno %s\n",
918 (int)ret, (unsigned int)flen, strerror(errno) ));
919 unlink(tmpname);
920 close(fd);
921 goto done;
923 if (close(fd)==-1) {
924 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
925 " Errno %s\n", strerror(errno) ));
926 unlink(tmpname);
927 goto done;
930 if (rename(tmpname, fname) == -1) {
931 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
932 "of %s to %s failed. Errno %s\n",
933 tmpname, fname, strerror(errno) ));
934 unlink(tmpname);
935 goto done;
938 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
939 "file %s with realm %s KDC list = %s\n",
940 fname, realm_upper, kdc_ip_string));
942 /* Set the environment variable to this file. */
943 setenv("KRB5_CONFIG", fname, 1);
945 result = true;
947 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
949 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
950 /* Insanity, sheer insanity..... */
952 if (strequal(realm, lp_realm())) {
953 SMB_STRUCT_STAT sbuf;
955 if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
956 if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
957 int lret;
958 size_t alloc_size = sbuf.st_ex_size + 1;
959 char *linkpath = talloc_array(talloc_tos(), char,
960 alloc_size);
961 if (!linkpath) {
962 goto done;
964 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
965 alloc_size - 1);
966 if (lret == -1) {
967 TALLOC_FREE(linkpath);
968 goto done;
970 linkpath[lret] = '\0';
972 if (strcmp(linkpath, fname) == 0) {
973 /* Symlink already exists. */
974 TALLOC_FREE(linkpath);
975 goto done;
977 TALLOC_FREE(linkpath);
981 /* Try and replace with a symlink. */
982 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
983 const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
984 if (errno != EEXIST) {
985 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
986 "of %s to %s failed. Errno %s\n",
987 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
988 goto done; /* Not a fatal error. */
991 /* Yes, this is a race conditon... too bad. */
992 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
993 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
994 "of %s to %s failed. Errno %s\n",
995 SYSTEM_KRB5_CONF_PATH, newpath,
996 strerror(errno) ));
997 goto done; /* Not a fatal error. */
1000 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1001 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1002 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1003 fname, strerror(errno) ));
1004 goto done; /* Not a fatal error. */
1008 #endif
1010 done:
1011 TALLOC_FREE(tmpname);
1012 TALLOC_FREE(dname);
1014 return result;
1016 #endif