s3-libads: Pass down the salt principal in smb_krb5_kt_add_entry()
[Samba.git] / source3 / libads / kerberos.c
blob53407fa1b5488599bd2ac4c46b4eb2eb6dc50e24
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 char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
471 const char *host_princ_s,
472 int enctype)
474 char *salt_princ_s;
475 /* lookup new key first */
477 salt_princ_s = kerberos_secrets_fetch_des_salt();
478 if (salt_princ_s == NULL) {
480 /* look under the old key. If this fails, just use the standard key */
481 salt_princ_s = kerberos_secrets_fetch_salting_principal(host_princ_s,
482 enctype);
483 if (salt_princ_s == NULL) {
484 /* fall back to host/machine.realm@REALM */
485 salt_princ_s = kerberos_standard_des_salt();
489 return salt_princ_s;
492 int create_kerberos_key_from_string(krb5_context context,
493 krb5_principal host_princ,
494 krb5_principal salt_princ,
495 krb5_data *password,
496 krb5_keyblock *key,
497 krb5_enctype enctype,
498 bool no_salt)
500 int ret;
502 * Check if we've determined that the KDC is salting keys for this
503 * principal/enctype in a non-obvious way. If it is, try to match
504 * its behavior.
506 if (no_salt) {
507 KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
508 if (!KRB5_KEY_DATA(key)) {
509 return ENOMEM;
511 memcpy(KRB5_KEY_DATA(key), password->data, password->length);
512 KRB5_KEY_LENGTH(key) = password->length;
513 KRB5_KEY_TYPE(key) = enctype;
514 return 0;
516 ret = smb_krb5_create_key_from_string(context,
517 salt_princ ? salt_princ : host_princ,
518 NULL,
519 password,
520 enctype,
521 key);
522 return ret;
525 /************************************************************************
526 Routine to set the salting principal for this service. Active
527 Directory may use a non-obvious principal name to generate the salt
528 when it determines the key to use for encrypting tickets for a service,
529 and hopefully we detected that when we joined the domain.
530 Setting principal to NULL deletes this entry.
531 ************************************************************************/
533 bool kerberos_secrets_store_salting_principal(const char *service,
534 int enctype,
535 const char *principal)
537 char *key = NULL;
538 bool ret = False;
539 krb5_context context = NULL;
540 krb5_principal princ = NULL;
541 char *princ_s = NULL;
542 char *unparsed_name = NULL;
543 krb5_error_code code;
545 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
546 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
547 error_message(code)));
548 return False;
550 if (strchr_m(service, '@')) {
551 if (asprintf(&princ_s, "%s", service) == -1) {
552 goto out;
554 } else {
555 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
556 goto out;
560 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
561 goto out;
563 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
564 goto out;
567 if (asprintf(&key, "%s/%s/enctype=%d",
568 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
569 == -1) {
570 goto out;
573 if ((principal != NULL) && (strlen(principal) > 0)) {
574 ret = secrets_store(key, principal, strlen(principal) + 1);
575 } else {
576 ret = secrets_delete(key);
579 out:
581 SAFE_FREE(key);
582 SAFE_FREE(princ_s);
583 TALLOC_FREE(unparsed_name);
585 if (princ) {
586 krb5_free_principal(context, princ);
589 if (context) {
590 krb5_free_context(context);
593 return ret;
597 /************************************************************************
598 ************************************************************************/
600 int kerberos_kinit_password(const char *principal,
601 const char *password,
602 int time_offset,
603 const char *cache_name)
605 return kerberos_kinit_password_ext(principal,
606 password,
607 time_offset,
610 cache_name,
611 False,
612 False,
614 NULL);
617 /************************************************************************
618 ************************************************************************/
620 /************************************************************************
621 Create a string list of available kdc's, possibly searching by sitename.
622 Does DNS queries.
624 If "sitename" is given, the DC's in that site are listed first.
626 ************************************************************************/
628 static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
629 const struct sockaddr_storage *addr)
631 int i;
633 for (i=0; i<*num_addrs; i++) {
634 if (sockaddr_equal((const struct sockaddr *)&addrs[i],
635 (const struct sockaddr *)addr)) {
636 return;
639 addrs[i] = *addr;
640 *num_addrs += 1;
643 /* print_canonical_sockaddr prints an ipv6 addr in the form of
644 * [ipv6.addr]. This string, when put in a generated krb5.conf file is not
645 * always properly dealt with by some older krb5 libraries. Adding the hard-coded
646 * portnumber workarounds the issue. - gd */
648 static char *print_canonical_sockaddr_with_port(TALLOC_CTX *mem_ctx,
649 const struct sockaddr_storage *pss)
651 char *str = NULL;
653 str = print_canonical_sockaddr(mem_ctx, pss);
654 if (str == NULL) {
655 return NULL;
658 if (pss->ss_family != AF_INET6) {
659 return str;
662 #if defined(HAVE_IPV6)
663 str = talloc_asprintf_append(str, ":88");
664 #endif
665 return str;
668 static char *get_kdc_ip_string(char *mem_ctx,
669 const char *realm,
670 const char *sitename,
671 const struct sockaddr_storage *pss)
673 TALLOC_CTX *frame = talloc_stackframe();
674 int i;
675 struct ip_service *ip_srv_site = NULL;
676 struct ip_service *ip_srv_nonsite = NULL;
677 int count_site = 0;
678 int count_nonsite;
679 int num_dcs;
680 struct sockaddr_storage *dc_addrs;
681 struct tsocket_address **dc_addrs2 = NULL;
682 const struct tsocket_address * const *dc_addrs3 = NULL;
683 char *result = NULL;
684 struct netlogon_samlogon_response **responses = NULL;
685 NTSTATUS status;
686 char *kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n", "",
687 print_canonical_sockaddr_with_port(mem_ctx, pss));
689 if (kdc_str == NULL) {
690 TALLOC_FREE(frame);
691 return NULL;
695 * First get the KDC's only in this site, the rest will be
696 * appended later
699 if (sitename) {
700 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
701 DEBUG(10, ("got %d addresses from site %s search\n", count_site,
702 sitename));
705 /* Get all KDC's. */
707 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
708 DEBUG(10, ("got %d addresses from site-less search\n", count_nonsite));
710 dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
711 count_site + count_nonsite);
712 if (dc_addrs == NULL) {
713 goto out;
716 num_dcs = 0;
718 for (i = 0; i < count_site; i++) {
719 if (!sockaddr_equal(
720 (const struct sockaddr *)pss,
721 (const struct sockaddr *)&ip_srv_site[i].ss)) {
722 add_sockaddr_unique(dc_addrs, &num_dcs,
723 &ip_srv_site[i].ss);
727 for (i = 0; i < count_nonsite; i++) {
728 if (!sockaddr_equal(
729 (const struct sockaddr *)pss,
730 (const struct sockaddr *)&ip_srv_nonsite[i].ss)) {
731 add_sockaddr_unique(dc_addrs, &num_dcs,
732 &ip_srv_nonsite[i].ss);
736 dc_addrs2 = talloc_zero_array(talloc_tos(),
737 struct tsocket_address *,
738 num_dcs);
740 DEBUG(10, ("%d additional KDCs to test\n", num_dcs));
741 if (num_dcs == 0) {
742 goto out;
744 if (dc_addrs2 == NULL) {
745 goto out;
748 for (i=0; i<num_dcs; i++) {
749 char addr[INET6_ADDRSTRLEN];
750 int ret;
752 print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
754 ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
755 addr, LDAP_PORT,
756 &dc_addrs2[i]);
757 if (ret != 0) {
758 status = map_nt_error_from_unix(errno);
759 DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
760 addr, nt_errstr(status)));
761 goto out;
765 dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
767 status = cldap_multi_netlogon(talloc_tos(),
768 dc_addrs3, num_dcs,
769 realm, lp_netbios_name(),
770 NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
771 MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
772 TALLOC_FREE(dc_addrs2);
773 dc_addrs3 = NULL;
775 if (!NT_STATUS_IS_OK(status)) {
776 DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
777 "%s\n", nt_errstr(status)));
778 goto out;
781 for (i=0; i<num_dcs; i++) {
782 char *new_kdc_str;
784 if (responses[i] == NULL) {
785 continue;
788 /* Append to the string - inefficient but not done often. */
789 new_kdc_str = talloc_asprintf(mem_ctx, "%s\t\tkdc = %s\n",
790 kdc_str,
791 print_canonical_sockaddr_with_port(mem_ctx, &dc_addrs[i]));
792 if (new_kdc_str == NULL) {
793 goto out;
795 TALLOC_FREE(kdc_str);
796 kdc_str = new_kdc_str;
799 out:
800 DEBUG(10, ("get_kdc_ip_string: Returning %s\n", kdc_str));
802 result = kdc_str;
803 SAFE_FREE(ip_srv_site);
804 SAFE_FREE(ip_srv_nonsite);
805 TALLOC_FREE(frame);
806 return result;
809 /************************************************************************
810 Create a specific krb5.conf file in the private directory pointing
811 at a specific kdc for a realm. Keyed off domain name. Sets
812 KRB5_CONFIG environment variable to point to this file. Must be
813 run as root or will fail (which is a good thing :-).
814 ************************************************************************/
816 bool create_local_private_krb5_conf_for_domain(const char *realm,
817 const char *domain,
818 const char *sitename,
819 const struct sockaddr_storage *pss)
821 char *dname;
822 char *tmpname = NULL;
823 char *fname = NULL;
824 char *file_contents = NULL;
825 char *kdc_ip_string = NULL;
826 size_t flen = 0;
827 ssize_t ret;
828 int fd;
829 char *realm_upper = NULL;
830 bool result = false;
831 char *aes_enctypes = NULL;
832 mode_t mask;
834 if (!lp_create_krb5_conf()) {
835 return false;
838 if (realm == NULL) {
839 DEBUG(0, ("No realm has been specified! Do you really want to "
840 "join an Active Directory server?\n"));
841 return false;
844 if (domain == NULL || pss == NULL) {
845 return false;
848 dname = lock_path("smb_krb5");
849 if (!dname) {
850 return false;
852 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
853 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
854 "failed to create directory %s. Error was %s\n",
855 dname, strerror(errno) ));
856 goto done;
859 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
860 if (!tmpname) {
861 goto done;
864 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
865 if (!fname) {
866 goto done;
869 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
870 fname, realm, domain ));
872 realm_upper = talloc_strdup(fname, realm);
873 if (!strupper_m(realm_upper)) {
874 goto done;
877 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
878 if (!kdc_ip_string) {
879 goto done;
882 aes_enctypes = talloc_strdup(fname, "");
883 if (aes_enctypes == NULL) {
884 goto done;
887 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
888 aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
889 if (aes_enctypes == NULL) {
890 goto done;
892 #endif
893 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
894 aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
895 if (aes_enctypes == NULL) {
896 goto done;
898 #endif
900 file_contents = talloc_asprintf(fname,
901 "[libdefaults]\n\tdefault_realm = %s\n"
902 "\tdefault_tgs_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
903 "\tdefault_tkt_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
904 "\tpreferred_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
905 "\tdns_lookup_realm = false\n\n"
906 "[realms]\n\t%s = {\n"
907 "%s\t}\n",
908 realm_upper, aes_enctypes, aes_enctypes, aes_enctypes,
909 realm_upper, kdc_ip_string);
911 if (!file_contents) {
912 goto done;
915 flen = strlen(file_contents);
917 mask = umask(S_IRWXO | S_IRWXG);
918 fd = mkstemp(tmpname);
919 umask(mask);
920 if (fd == -1) {
921 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
922 " for file %s. Errno %s\n",
923 tmpname, strerror(errno) ));
924 goto done;
927 if (fchmod(fd, 0644)==-1) {
928 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
929 " Errno %s\n",
930 tmpname, strerror(errno) ));
931 unlink(tmpname);
932 close(fd);
933 goto done;
936 ret = write(fd, file_contents, flen);
937 if (flen != ret) {
938 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
939 " returned %d (should be %u). Errno %s\n",
940 (int)ret, (unsigned int)flen, strerror(errno) ));
941 unlink(tmpname);
942 close(fd);
943 goto done;
945 if (close(fd)==-1) {
946 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
947 " Errno %s\n", strerror(errno) ));
948 unlink(tmpname);
949 goto done;
952 if (rename(tmpname, fname) == -1) {
953 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
954 "of %s to %s failed. Errno %s\n",
955 tmpname, fname, strerror(errno) ));
956 unlink(tmpname);
957 goto done;
960 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
961 "file %s with realm %s KDC list = %s\n",
962 fname, realm_upper, kdc_ip_string));
964 /* Set the environment variable to this file. */
965 setenv("KRB5_CONFIG", fname, 1);
967 result = true;
969 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
971 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
972 /* Insanity, sheer insanity..... */
974 if (strequal(realm, lp_realm())) {
975 SMB_STRUCT_STAT sbuf;
977 if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
978 if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
979 int lret;
980 size_t alloc_size = sbuf.st_ex_size + 1;
981 char *linkpath = talloc_array(talloc_tos(), char,
982 alloc_size);
983 if (!linkpath) {
984 goto done;
986 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
987 alloc_size - 1);
988 if (lret == -1) {
989 TALLOC_FREE(linkpath);
990 goto done;
992 linkpath[lret] = '\0';
994 if (strcmp(linkpath, fname) == 0) {
995 /* Symlink already exists. */
996 TALLOC_FREE(linkpath);
997 goto done;
999 TALLOC_FREE(linkpath);
1003 /* Try and replace with a symlink. */
1004 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1005 const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
1006 if (errno != EEXIST) {
1007 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
1008 "of %s to %s failed. Errno %s\n",
1009 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
1010 goto done; /* Not a fatal error. */
1013 /* Yes, this is a race conditon... too bad. */
1014 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
1015 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
1016 "of %s to %s failed. Errno %s\n",
1017 SYSTEM_KRB5_CONF_PATH, newpath,
1018 strerror(errno) ));
1019 goto done; /* Not a fatal error. */
1022 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1023 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1024 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1025 fname, strerror(errno) ));
1026 goto done; /* Not a fatal error. */
1030 #endif
1032 done:
1033 TALLOC_FREE(tmpname);
1034 TALLOC_FREE(dname);
1036 return result;
1038 #endif