s3-kerberos: avoid entering a password change dialogue also when using MIT.
[Samba.git] / source3 / libads / kerberos.c
blob4774a9fc726e009fe4c40928143a7977df63ddf1
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"
33 #ifdef HAVE_KRB5
35 #define LIBADS_CCACHE_NAME "MEMORY:libads"
38 we use a prompter to avoid a crash bug in the kerberos libs when
39 dealing with empty passwords
40 this prompter is just a string copy ...
42 static krb5_error_code
43 kerb_prompter(krb5_context ctx, void *data,
44 const char *name,
45 const char *banner,
46 int num_prompts,
47 krb5_prompt prompts[])
49 if (num_prompts == 0) return 0;
50 if (num_prompts == 2) {
52 * only heimdal has a prompt type and we need to deal with it here to
53 * avoid loops.
55 * removing the prompter completely is not an option as at least these
56 * versions would crash: heimdal-1.0.2 and heimdal-1.1. Later heimdal
57 * version have looping detection and return with a proper error code.
60 #if HAVE_KRB5_PROMPT_TYPE /* Heimdal */
61 if (prompts[0].type == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
62 prompts[1].type == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
64 * We don't want to change passwords here. We're
65 * called from heimal when the KDC returns
66 * KRB5KDC_ERR_KEY_EXPIRED, but at this point we don't
67 * have the chance to ask the user for a new
68 * password. If we return 0 (i.e. success), we will be
69 * spinning in the endless for-loop in
70 * change_password() in
71 * source4/heimdal/lib/krb5/init_creds_pw.c:526ff
73 return KRB5KDC_ERR_KEY_EXPIRED;
75 #elif defined(HAVE_KRB5_GET_PROMPT_TYPES) /* MIT */
76 krb5_prompt_type *prompt_types = NULL;
78 prompt_types = krb5_get_prompt_types(ctx);
79 if (prompt_types != NULL) {
80 if (prompt_types[0] == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
81 prompt_types[1] == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
82 return KRB5KDC_ERR_KEY_EXP;
85 #endif
88 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
89 if (prompts[0].reply->length > 0) {
90 if (data) {
91 strncpy((char *)prompts[0].reply->data, (const char *)data,
92 prompts[0].reply->length-1);
93 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
94 } else {
95 prompts[0].reply->length = 0;
98 return 0;
101 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
102 NTSTATUS *nt_status)
104 DATA_BLOB edata;
105 DATA_BLOB unwrapped_edata;
106 TALLOC_CTX *mem_ctx;
107 struct KRB5_EDATA_NTSTATUS parsed_edata;
108 enum ndr_err_code ndr_err;
110 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
111 edata = data_blob(error->e_data->data, error->e_data->length);
112 #else
113 edata = data_blob(error->e_data.data, error->e_data.length);
114 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
116 #ifdef DEVELOPER
117 dump_data(10, edata.data, edata.length);
118 #endif /* DEVELOPER */
120 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
121 if (mem_ctx == NULL) {
122 data_blob_free(&edata);
123 return False;
126 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
127 data_blob_free(&edata);
128 TALLOC_FREE(mem_ctx);
129 return False;
132 data_blob_free(&edata);
134 ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx,
135 &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
136 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
137 data_blob_free(&unwrapped_edata);
138 TALLOC_FREE(mem_ctx);
139 return False;
142 data_blob_free(&unwrapped_edata);
144 if (nt_status) {
145 *nt_status = parsed_edata.ntstatus;
148 TALLOC_FREE(mem_ctx);
150 return True;
153 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
154 krb5_get_init_creds_opt *opt,
155 NTSTATUS *nt_status)
157 bool ret = False;
158 krb5_error *error = NULL;
160 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
161 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
162 if (ret) {
163 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
164 error_message(ret)));
165 return False;
167 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
169 if (!error) {
170 DEBUG(1,("no krb5_error\n"));
171 return False;
174 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
175 if (!error->e_data) {
176 #else
177 if (error->e_data.data == NULL) {
178 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
179 DEBUG(1,("no edata in krb5_error\n"));
180 krb5_free_error(ctx, error);
181 return False;
184 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
186 krb5_free_error(ctx, error);
188 return ret;
192 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
193 place in default cache location.
194 remus@snapserver.com
196 int kerberos_kinit_password_ext(const char *principal,
197 const char *password,
198 int time_offset,
199 time_t *expire_time,
200 time_t *renew_till_time,
201 const char *cache_name,
202 bool request_pac,
203 bool add_netbios_addr,
204 time_t renewable_time,
205 NTSTATUS *ntstatus)
207 krb5_context ctx = NULL;
208 krb5_error_code code = 0;
209 krb5_ccache cc = NULL;
210 krb5_principal me = NULL;
211 krb5_creds my_creds;
212 krb5_get_init_creds_opt *opt = NULL;
213 smb_krb5_addresses *addr = NULL;
215 ZERO_STRUCT(my_creds);
217 initialize_krb5_error_table();
218 if ((code = krb5_init_context(&ctx)))
219 goto out;
221 if (time_offset != 0) {
222 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
225 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
226 principal,
227 cache_name ? cache_name: krb5_cc_default_name(ctx),
228 getenv("KRB5_CONFIG")));
230 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
231 goto out;
234 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
235 goto out;
238 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
239 goto out;
242 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
243 krb5_get_init_creds_opt_set_forwardable(opt, True);
244 #if 0
245 /* insane testing */
246 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
247 #endif
249 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
250 if (request_pac) {
251 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
252 goto out;
255 #endif
256 if (add_netbios_addr) {
257 if ((code = smb_krb5_gen_netbios_krb5_address(&addr,
258 lp_netbios_name()))) {
259 goto out;
261 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
264 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password),
265 kerb_prompter, discard_const_p(char, password),
266 0, NULL, opt))) {
267 goto out;
270 if ((code = krb5_cc_initialize(ctx, cc, me))) {
271 goto out;
274 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
275 goto out;
278 if (expire_time) {
279 *expire_time = (time_t) my_creds.times.endtime;
282 if (renew_till_time) {
283 *renew_till_time = (time_t) my_creds.times.renew_till;
285 out:
286 if (ntstatus) {
288 NTSTATUS status;
290 /* fast path */
291 if (code == 0) {
292 *ntstatus = NT_STATUS_OK;
293 goto cleanup;
296 /* try to get ntstatus code out of krb5_error when we have it
297 * inside the krb5_get_init_creds_opt - gd */
299 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
300 *ntstatus = status;
301 goto cleanup;
304 /* fall back to self-made-mapping */
305 *ntstatus = krb5_to_nt_status(code);
308 cleanup:
309 krb5_free_cred_contents(ctx, &my_creds);
310 if (me) {
311 krb5_free_principal(ctx, me);
313 if (addr) {
314 smb_krb5_free_addresses(ctx, addr);
316 if (opt) {
317 smb_krb5_get_init_creds_opt_free(ctx, opt);
319 if (cc) {
320 krb5_cc_close(ctx, cc);
322 if (ctx) {
323 krb5_free_context(ctx);
325 return code;
328 int ads_kdestroy(const char *cc_name)
330 krb5_error_code code;
331 krb5_context ctx = NULL;
332 krb5_ccache cc = NULL;
334 initialize_krb5_error_table();
335 if ((code = krb5_init_context (&ctx))) {
336 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
337 error_message(code)));
338 return code;
341 if (!cc_name) {
342 if ((code = krb5_cc_default(ctx, &cc))) {
343 krb5_free_context(ctx);
344 return code;
346 } else {
347 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
348 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
349 error_message(code)));
350 krb5_free_context(ctx);
351 return code;
355 if ((code = krb5_cc_destroy (ctx, cc))) {
356 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
357 error_message(code)));
360 krb5_free_context (ctx);
361 return code;
364 /************************************************************************
365 Routine to fetch the salting principal for a service. Active
366 Directory may use a non-obvious principal name to generate the salt
367 when it determines the key to use for encrypting tickets for a service,
368 and hopefully we detected that when we joined the domain.
369 ************************************************************************/
371 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
373 char *key = NULL;
374 char *ret = NULL;
376 if (asprintf(&key, "%s/%s/enctype=%d",
377 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
378 return NULL;
380 ret = (char *)secrets_fetch(key, NULL);
381 SAFE_FREE(key);
382 return ret;
385 /************************************************************************
386 Return the standard DES salt key
387 ************************************************************************/
389 char* kerberos_standard_des_salt( void )
391 fstring salt;
393 fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
394 (void)strlower_m( salt );
395 fstrcat( salt, lp_realm() );
397 return SMB_STRDUP( salt );
400 /************************************************************************
401 ************************************************************************/
403 static char* des_salt_key( void )
405 char *key;
407 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
408 lp_realm()) == -1) {
409 return NULL;
412 return key;
415 /************************************************************************
416 ************************************************************************/
418 bool kerberos_secrets_store_des_salt( const char* salt )
420 char* key;
421 bool ret;
423 if ( (key = des_salt_key()) == NULL ) {
424 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
425 return False;
428 if ( !salt ) {
429 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
430 secrets_delete( key );
431 return True;
434 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
436 ret = secrets_store( key, salt, strlen(salt)+1 );
438 SAFE_FREE( key );
440 return ret;
443 /************************************************************************
444 ************************************************************************/
446 static
447 char* kerberos_secrets_fetch_des_salt( void )
449 char *salt, *key;
451 if ( (key = des_salt_key()) == NULL ) {
452 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
453 return NULL;
456 salt = (char*)secrets_fetch( key, NULL );
458 SAFE_FREE( key );
460 return salt;
463 /************************************************************************
464 Routine to get the salting principal for this service. This is
465 maintained for backwards compatibilty with releases prior to 3.0.24.
466 Since we store the salting principal string only at join, we may have
467 to look for the older tdb keys. Caller must free if return is not null.
468 ************************************************************************/
470 static
471 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
472 krb5_principal host_princ,
473 int enctype)
475 char *unparsed_name = NULL, *salt_princ_s = NULL;
476 krb5_principal ret_princ = NULL;
478 /* lookup new key first */
480 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
482 /* look under the old key. If this fails, just use the standard key */
484 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
485 return (krb5_principal)NULL;
487 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
488 /* fall back to host/machine.realm@REALM */
489 salt_princ_s = kerberos_standard_des_salt();
493 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
494 ret_princ = NULL;
497 TALLOC_FREE(unparsed_name);
498 SAFE_FREE(salt_princ_s);
500 return ret_princ;
503 int create_kerberos_key_from_string(krb5_context context,
504 krb5_principal host_princ,
505 krb5_data *password,
506 krb5_keyblock *key,
507 krb5_enctype enctype,
508 bool no_salt)
510 krb5_principal salt_princ = NULL;
511 int ret;
513 * Check if we've determined that the KDC is salting keys for this
514 * principal/enctype in a non-obvious way. If it is, try to match
515 * its behavior.
517 if (no_salt) {
518 KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
519 if (!KRB5_KEY_DATA(key)) {
520 return ENOMEM;
522 memcpy(KRB5_KEY_DATA(key), password->data, password->length);
523 KRB5_KEY_LENGTH(key) = password->length;
524 KRB5_KEY_TYPE(key) = enctype;
525 return 0;
527 salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
528 ret = smb_krb5_create_key_from_string(context,
529 salt_princ ? salt_princ : host_princ,
530 NULL,
531 password,
532 enctype,
533 key);
534 if (salt_princ) {
535 krb5_free_principal(context, salt_princ);
537 return ret;
540 /************************************************************************
541 Routine to set the salting principal for this service. Active
542 Directory may use a non-obvious principal name to generate the salt
543 when it determines the key to use for encrypting tickets for a service,
544 and hopefully we detected that when we joined the domain.
545 Setting principal to NULL deletes this entry.
546 ************************************************************************/
548 bool kerberos_secrets_store_salting_principal(const char *service,
549 int enctype,
550 const char *principal)
552 char *key = NULL;
553 bool ret = False;
554 krb5_context context = NULL;
555 krb5_principal princ = NULL;
556 char *princ_s = NULL;
557 char *unparsed_name = NULL;
558 krb5_error_code code;
560 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
561 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
562 error_message(code)));
563 return False;
565 if (strchr_m(service, '@')) {
566 if (asprintf(&princ_s, "%s", service) == -1) {
567 goto out;
569 } else {
570 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
571 goto out;
575 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
576 goto out;
578 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
579 goto out;
582 if (asprintf(&key, "%s/%s/enctype=%d",
583 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
584 == -1) {
585 goto out;
588 if ((principal != NULL) && (strlen(principal) > 0)) {
589 ret = secrets_store(key, principal, strlen(principal) + 1);
590 } else {
591 ret = secrets_delete(key);
594 out:
596 SAFE_FREE(key);
597 SAFE_FREE(princ_s);
598 TALLOC_FREE(unparsed_name);
600 if (princ) {
601 krb5_free_principal(context, princ);
604 if (context) {
605 krb5_free_context(context);
608 return ret;
612 /************************************************************************
613 ************************************************************************/
615 int kerberos_kinit_password(const char *principal,
616 const char *password,
617 int time_offset,
618 const char *cache_name)
620 return kerberos_kinit_password_ext(principal,
621 password,
622 time_offset,
625 cache_name,
626 False,
627 False,
629 NULL);
632 /************************************************************************
633 ************************************************************************/
635 /************************************************************************
636 Create a string list of available kdc's, possibly searching by sitename.
637 Does DNS queries.
639 If "sitename" is given, the DC's in that site are listed first.
641 ************************************************************************/
643 static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
644 const struct sockaddr_storage *addr)
646 int i;
648 for (i=0; i<*num_addrs; i++) {
649 if (sockaddr_equal((const struct sockaddr *)&addrs[i],
650 (const struct sockaddr *)addr)) {
651 return;
654 addrs[i] = *addr;
655 *num_addrs += 1;
658 /* print_canonical_sockaddr prints an ipv6 addr in the form of
659 * [ipv6.addr]. This string, when put in a generated krb5.conf file is not
660 * always properly dealt with by some older krb5 libraries. Adding the hard-coded
661 * portnumber workarounds the issue. - gd */
663 static char *print_canonical_sockaddr_with_port(TALLOC_CTX *mem_ctx,
664 const struct sockaddr_storage *pss)
666 char *str = NULL;
668 str = print_canonical_sockaddr(mem_ctx, pss);
669 if (str == NULL) {
670 return NULL;
673 if (pss->ss_family != AF_INET6) {
674 return str;
677 #if defined(HAVE_IPV6)
678 str = talloc_asprintf_append(str, ":88");
679 #endif
680 return str;
683 static char *get_kdc_ip_string(char *mem_ctx,
684 const char *realm,
685 const char *sitename,
686 const struct sockaddr_storage *pss)
688 TALLOC_CTX *frame = talloc_stackframe();
689 int i;
690 struct ip_service *ip_srv_site = NULL;
691 struct ip_service *ip_srv_nonsite = NULL;
692 int count_site = 0;
693 int count_nonsite;
694 int num_dcs;
695 struct sockaddr_storage *dc_addrs;
696 struct tsocket_address **dc_addrs2 = NULL;
697 const struct tsocket_address * const *dc_addrs3 = NULL;
698 char *result = NULL;
699 struct netlogon_samlogon_response **responses = NULL;
700 NTSTATUS status;
701 char *kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n", "",
702 print_canonical_sockaddr_with_port(mem_ctx, pss));
704 if (kdc_str == NULL) {
705 TALLOC_FREE(frame);
706 return NULL;
710 * First get the KDC's only in this site, the rest will be
711 * appended later
714 if (sitename) {
715 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
716 DEBUG(10, ("got %d addresses from site %s search\n", count_site,
717 sitename));
720 /* Get all KDC's. */
722 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
723 DEBUG(10, ("got %d addresses from site-less search\n", count_nonsite));
725 dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
726 count_site + count_nonsite);
727 if (dc_addrs == NULL) {
728 goto out;
731 num_dcs = 0;
733 for (i = 0; i < count_site; i++) {
734 if (!sockaddr_equal(
735 (const struct sockaddr *)pss,
736 (const struct sockaddr *)&ip_srv_site[i].ss)) {
737 add_sockaddr_unique(dc_addrs, &num_dcs,
738 &ip_srv_site[i].ss);
742 for (i = 0; i < count_nonsite; i++) {
743 if (!sockaddr_equal(
744 (const struct sockaddr *)pss,
745 (const struct sockaddr *)&ip_srv_nonsite[i].ss)) {
746 add_sockaddr_unique(dc_addrs, &num_dcs,
747 &ip_srv_nonsite[i].ss);
751 dc_addrs2 = talloc_zero_array(talloc_tos(),
752 struct tsocket_address *,
753 num_dcs);
755 DEBUG(10, ("%d additional KDCs to test\n", num_dcs));
756 if (num_dcs == 0) {
757 goto out;
759 if (dc_addrs2 == NULL) {
760 goto out;
763 for (i=0; i<num_dcs; i++) {
764 char addr[INET6_ADDRSTRLEN];
765 int ret;
767 print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
769 ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
770 addr, LDAP_PORT,
771 &dc_addrs2[i]);
772 if (ret != 0) {
773 status = map_nt_error_from_unix(errno);
774 DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
775 addr, nt_errstr(status)));
776 goto out;
780 dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
782 status = cldap_multi_netlogon(talloc_tos(),
783 dc_addrs3, num_dcs,
784 realm, lp_netbios_name(),
785 NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
786 MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
787 TALLOC_FREE(dc_addrs2);
788 dc_addrs3 = NULL;
790 if (!NT_STATUS_IS_OK(status)) {
791 DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
792 "%s\n", nt_errstr(status)));
793 goto out;
796 for (i=0; i<num_dcs; i++) {
797 char *new_kdc_str;
799 if (responses[i] == NULL) {
800 continue;
803 /* Append to the string - inefficient but not done often. */
804 new_kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n",
805 kdc_str,
806 print_canonical_sockaddr_with_port(mem_ctx, &dc_addrs[i]));
807 if (new_kdc_str == NULL) {
808 goto out;
810 TALLOC_FREE(kdc_str);
811 kdc_str = new_kdc_str;
814 out:
815 DEBUG(10, ("get_kdc_ip_string: Returning %s\n", kdc_str));
817 result = kdc_str;
818 SAFE_FREE(ip_srv_site);
819 SAFE_FREE(ip_srv_nonsite);
820 TALLOC_FREE(frame);
821 return result;
824 /************************************************************************
825 Create a specific krb5.conf file in the private directory pointing
826 at a specific kdc for a realm. Keyed off domain name. Sets
827 KRB5_CONFIG environment variable to point to this file. Must be
828 run as root or will fail (which is a good thing :-).
829 ************************************************************************/
831 bool create_local_private_krb5_conf_for_domain(const char *realm,
832 const char *domain,
833 const char *sitename,
834 const struct sockaddr_storage *pss)
836 char *dname;
837 char *tmpname = NULL;
838 char *fname = NULL;
839 char *file_contents = NULL;
840 char *kdc_ip_string = NULL;
841 size_t flen = 0;
842 ssize_t ret;
843 int fd;
844 char *realm_upper = NULL;
845 bool result = false;
846 char *aes_enctypes = NULL;
847 mode_t mask;
849 if (!lp_create_krb5_conf()) {
850 return false;
853 if (realm == NULL) {
854 DEBUG(0, ("No realm has been specified! Do you really want to "
855 "join an Active Directory server?\n"));
856 return false;
859 if (domain == NULL || pss == NULL) {
860 return false;
863 dname = lock_path("smb_krb5");
864 if (!dname) {
865 return false;
867 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
868 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
869 "failed to create directory %s. Error was %s\n",
870 dname, strerror(errno) ));
871 goto done;
874 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
875 if (!tmpname) {
876 goto done;
879 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
880 if (!fname) {
881 goto done;
884 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
885 fname, realm, domain ));
887 realm_upper = talloc_strdup(fname, realm);
888 if (!strupper_m(realm_upper)) {
889 goto done;
892 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
893 if (!kdc_ip_string) {
894 goto done;
897 aes_enctypes = talloc_strdup(fname, "");
898 if (aes_enctypes == NULL) {
899 goto done;
902 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
903 aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
904 if (aes_enctypes == NULL) {
905 goto done;
907 #endif
908 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
909 aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
910 if (aes_enctypes == NULL) {
911 goto done;
913 #endif
915 file_contents = talloc_asprintf(fname,
916 "[libdefaults]\n\tdefault_realm = %s\n"
917 "\tdefault_tgs_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
918 "\tdefault_tkt_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
919 "\tpreferred_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
920 "\tdns_lookup_realm = false\n\n"
921 "[realms]\n\t%s = {\n"
922 "%s\t}\n",
923 realm_upper, aes_enctypes, aes_enctypes, aes_enctypes,
924 realm_upper, kdc_ip_string);
926 if (!file_contents) {
927 goto done;
930 flen = strlen(file_contents);
932 mask = umask(S_IRWXO | S_IRWXG);
933 fd = mkstemp(tmpname);
934 umask(mask);
935 if (fd == -1) {
936 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
937 " for file %s. Errno %s\n",
938 tmpname, strerror(errno) ));
939 goto done;
942 if (fchmod(fd, 0644)==-1) {
943 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
944 " Errno %s\n",
945 tmpname, strerror(errno) ));
946 unlink(tmpname);
947 close(fd);
948 goto done;
951 ret = write(fd, file_contents, flen);
952 if (flen != ret) {
953 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
954 " returned %d (should be %u). Errno %s\n",
955 (int)ret, (unsigned int)flen, strerror(errno) ));
956 unlink(tmpname);
957 close(fd);
958 goto done;
960 if (close(fd)==-1) {
961 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
962 " Errno %s\n", strerror(errno) ));
963 unlink(tmpname);
964 goto done;
967 if (rename(tmpname, fname) == -1) {
968 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
969 "of %s to %s failed. Errno %s\n",
970 tmpname, fname, strerror(errno) ));
971 unlink(tmpname);
972 goto done;
975 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
976 "file %s with realm %s KDC list = %s\n",
977 fname, realm_upper, kdc_ip_string));
979 /* Set the environment variable to this file. */
980 setenv("KRB5_CONFIG", fname, 1);
982 result = true;
984 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
986 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
987 /* Insanity, sheer insanity..... */
989 if (strequal(realm, lp_realm())) {
990 SMB_STRUCT_STAT sbuf;
992 if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
993 if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
994 int lret;
995 size_t alloc_size = sbuf.st_ex_size + 1;
996 char *linkpath = talloc_array(talloc_tos(), char,
997 alloc_size);
998 if (!linkpath) {
999 goto done;
1001 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
1002 alloc_size - 1);
1003 if (lret == -1) {
1004 TALLOC_FREE(linkpath);
1005 goto done;
1007 linkpath[lret] = '\0';
1009 if (strcmp(linkpath, fname) == 0) {
1010 /* Symlink already exists. */
1011 TALLOC_FREE(linkpath);
1012 goto done;
1014 TALLOC_FREE(linkpath);
1018 /* Try and replace with a symlink. */
1019 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1020 const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
1021 if (errno != EEXIST) {
1022 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
1023 "of %s to %s failed. Errno %s\n",
1024 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
1025 goto done; /* Not a fatal error. */
1028 /* Yes, this is a race conditon... too bad. */
1029 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
1030 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
1031 "of %s to %s failed. Errno %s\n",
1032 SYSTEM_KRB5_CONF_PATH, newpath,
1033 strerror(errno) ));
1034 goto done; /* Not a fatal error. */
1037 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1038 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1039 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1040 fname, strerror(errno) ));
1041 goto done; /* Not a fatal error. */
1045 #endif
1047 done:
1048 TALLOC_FREE(tmpname);
1049 TALLOC_FREE(dname);
1051 return result;
1053 #endif