s3: Slightly simplify print_kdc_line()
[Samba/gebeck_regimport.git] / source3 / libads / kerberos.c
blobda86e25dfdabdbc830f9ebff2e5ff13a5ea24120
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 "secrets.h"
31 #ifdef HAVE_KRB5
33 #define DEFAULT_KRB5_PORT 88
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;
51 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
52 if (prompts[0].reply->length > 0) {
53 if (data) {
54 strncpy((char *)prompts[0].reply->data, (const char *)data,
55 prompts[0].reply->length-1);
56 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
57 } else {
58 prompts[0].reply->length = 0;
61 return 0;
64 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
65 NTSTATUS *nt_status)
67 DATA_BLOB edata;
68 DATA_BLOB unwrapped_edata;
69 TALLOC_CTX *mem_ctx;
70 struct KRB5_EDATA_NTSTATUS parsed_edata;
71 enum ndr_err_code ndr_err;
73 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
74 edata = data_blob(error->e_data->data, error->e_data->length);
75 #else
76 edata = data_blob(error->e_data.data, error->e_data.length);
77 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
79 #ifdef DEVELOPER
80 dump_data(10, edata.data, edata.length);
81 #endif /* DEVELOPER */
83 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
84 if (mem_ctx == NULL) {
85 data_blob_free(&edata);
86 return False;
89 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
90 data_blob_free(&edata);
91 TALLOC_FREE(mem_ctx);
92 return False;
95 data_blob_free(&edata);
97 ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx,
98 &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
99 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
100 data_blob_free(&unwrapped_edata);
101 TALLOC_FREE(mem_ctx);
102 return False;
105 data_blob_free(&unwrapped_edata);
107 if (nt_status) {
108 *nt_status = parsed_edata.ntstatus;
111 TALLOC_FREE(mem_ctx);
113 return True;
116 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
117 krb5_get_init_creds_opt *opt,
118 NTSTATUS *nt_status)
120 bool ret = False;
121 krb5_error *error = NULL;
123 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
124 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
125 if (ret) {
126 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
127 error_message(ret)));
128 return False;
130 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
132 if (!error) {
133 DEBUG(1,("no krb5_error\n"));
134 return False;
137 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
138 if (!error->e_data) {
139 #else
140 if (error->e_data.data == NULL) {
141 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
142 DEBUG(1,("no edata in krb5_error\n"));
143 krb5_free_error(ctx, error);
144 return False;
147 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
149 krb5_free_error(ctx, error);
151 return ret;
155 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
156 place in default cache location.
157 remus@snapserver.com
159 int kerberos_kinit_password_ext(const char *principal,
160 const char *password,
161 int time_offset,
162 time_t *expire_time,
163 time_t *renew_till_time,
164 const char *cache_name,
165 bool request_pac,
166 bool add_netbios_addr,
167 time_t renewable_time,
168 NTSTATUS *ntstatus)
170 krb5_context ctx = NULL;
171 krb5_error_code code = 0;
172 krb5_ccache cc = NULL;
173 krb5_principal me = NULL;
174 krb5_creds my_creds;
175 krb5_get_init_creds_opt *opt = NULL;
176 smb_krb5_addresses *addr = NULL;
178 ZERO_STRUCT(my_creds);
180 initialize_krb5_error_table();
181 if ((code = krb5_init_context(&ctx)))
182 goto out;
184 if (time_offset != 0) {
185 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
188 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
189 principal,
190 cache_name ? cache_name: krb5_cc_default_name(ctx),
191 getenv("KRB5_CONFIG")));
193 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
194 goto out;
197 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
198 goto out;
201 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
202 goto out;
205 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
206 krb5_get_init_creds_opt_set_forwardable(opt, True);
207 #if 0
208 /* insane testing */
209 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
210 #endif
212 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
213 if (request_pac) {
214 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
215 goto out;
218 #endif
219 if (add_netbios_addr) {
220 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
221 goto out;
223 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
226 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password),
227 kerb_prompter, discard_const_p(char, password),
228 0, NULL, opt))) {
229 goto out;
232 if ((code = krb5_cc_initialize(ctx, cc, me))) {
233 goto out;
236 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
237 goto out;
240 if (expire_time) {
241 *expire_time = (time_t) my_creds.times.endtime;
244 if (renew_till_time) {
245 *renew_till_time = (time_t) my_creds.times.renew_till;
247 out:
248 if (ntstatus) {
250 NTSTATUS status;
252 /* fast path */
253 if (code == 0) {
254 *ntstatus = NT_STATUS_OK;
255 goto cleanup;
258 /* try to get ntstatus code out of krb5_error when we have it
259 * inside the krb5_get_init_creds_opt - gd */
261 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
262 *ntstatus = status;
263 goto cleanup;
266 /* fall back to self-made-mapping */
267 *ntstatus = krb5_to_nt_status(code);
270 cleanup:
271 krb5_free_cred_contents(ctx, &my_creds);
272 if (me) {
273 krb5_free_principal(ctx, me);
275 if (addr) {
276 smb_krb5_free_addresses(ctx, addr);
278 if (opt) {
279 smb_krb5_get_init_creds_opt_free(ctx, opt);
281 if (cc) {
282 krb5_cc_close(ctx, cc);
284 if (ctx) {
285 krb5_free_context(ctx);
287 return code;
290 int ads_kdestroy(const char *cc_name)
292 krb5_error_code code;
293 krb5_context ctx = NULL;
294 krb5_ccache cc = NULL;
296 initialize_krb5_error_table();
297 if ((code = krb5_init_context (&ctx))) {
298 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
299 error_message(code)));
300 return code;
303 if (!cc_name) {
304 if ((code = krb5_cc_default(ctx, &cc))) {
305 krb5_free_context(ctx);
306 return code;
308 } else {
309 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
310 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
311 error_message(code)));
312 krb5_free_context(ctx);
313 return code;
317 if ((code = krb5_cc_destroy (ctx, cc))) {
318 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
319 error_message(code)));
322 krb5_free_context (ctx);
323 return code;
326 /************************************************************************
327 Routine to fetch the salting principal for a service. Active
328 Directory may use a non-obvious principal name to generate the salt
329 when it determines the key to use for encrypting tickets for a service,
330 and hopefully we detected that when we joined the domain.
331 ************************************************************************/
333 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
335 char *key = NULL;
336 char *ret = NULL;
338 if (asprintf(&key, "%s/%s/enctype=%d",
339 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
340 return NULL;
342 ret = (char *)secrets_fetch(key, NULL);
343 SAFE_FREE(key);
344 return ret;
347 /************************************************************************
348 Return the standard DES salt key
349 ************************************************************************/
351 char* kerberos_standard_des_salt( void )
353 fstring salt;
355 fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
356 strlower_m( salt );
357 fstrcat( salt, lp_realm() );
359 return SMB_STRDUP( salt );
362 /************************************************************************
363 ************************************************************************/
365 static char* des_salt_key( void )
367 char *key;
369 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
370 lp_realm()) == -1) {
371 return NULL;
374 return key;
377 /************************************************************************
378 ************************************************************************/
380 bool kerberos_secrets_store_des_salt( const char* salt )
382 char* key;
383 bool ret;
385 if ( (key = des_salt_key()) == NULL ) {
386 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
387 return False;
390 if ( !salt ) {
391 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
392 secrets_delete( key );
393 return True;
396 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
398 ret = secrets_store( key, salt, strlen(salt)+1 );
400 SAFE_FREE( key );
402 return ret;
405 /************************************************************************
406 ************************************************************************/
408 char* kerberos_secrets_fetch_des_salt( void )
410 char *salt, *key;
412 if ( (key = des_salt_key()) == NULL ) {
413 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
414 return False;
417 salt = (char*)secrets_fetch( key, NULL );
419 SAFE_FREE( key );
421 return salt;
424 /************************************************************************
425 Routine to get the default realm from the kerberos credentials cache.
426 Caller must free if the return value is not NULL.
427 ************************************************************************/
429 char *kerberos_get_default_realm_from_ccache( void )
431 char *realm = NULL;
432 krb5_context ctx = NULL;
433 krb5_ccache cc = NULL;
434 krb5_principal princ = NULL;
436 initialize_krb5_error_table();
437 if (krb5_init_context(&ctx)) {
438 return NULL;
441 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
442 "Trying to read krb5 cache: %s\n",
443 krb5_cc_default_name(ctx)));
444 if (krb5_cc_default(ctx, &cc)) {
445 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
446 "failed to read default cache\n"));
447 goto out;
449 if (krb5_cc_get_principal(ctx, cc, &princ)) {
450 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
451 "failed to get default principal\n"));
452 goto out;
455 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
456 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
457 #elif defined(HAVE_KRB5_PRINC_REALM)
459 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
460 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
462 #endif
464 out:
466 if (ctx) {
467 if (princ) {
468 krb5_free_principal(ctx, princ);
470 if (cc) {
471 krb5_cc_close(ctx, cc);
473 krb5_free_context(ctx);
476 return realm;
479 /************************************************************************
480 Routine to get the realm from a given DNS name. Returns malloc'ed memory.
481 Caller must free() if the return value is not NULL.
482 ************************************************************************/
484 char *kerberos_get_realm_from_hostname(const char *hostname)
486 #if defined(HAVE_KRB5_GET_HOST_REALM) && defined(HAVE_KRB5_FREE_HOST_REALM)
487 #if defined(HAVE_KRB5_REALM_TYPE)
488 /* Heimdal. */
489 krb5_realm *realm_list = NULL;
490 #else
491 /* MIT */
492 char **realm_list = NULL;
493 #endif
494 char *realm = NULL;
495 krb5_error_code kerr;
496 krb5_context ctx = NULL;
498 initialize_krb5_error_table();
499 if (krb5_init_context(&ctx)) {
500 return NULL;
503 kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
504 if (kerr != 0) {
505 DEBUG(3,("kerberos_get_realm_from_hostname %s: "
506 "failed %s\n",
507 hostname ? hostname : "(NULL)",
508 error_message(kerr) ));
509 goto out;
512 if (realm_list && realm_list[0]) {
513 realm = SMB_STRDUP(realm_list[0]);
516 out:
518 if (ctx) {
519 if (realm_list) {
520 krb5_free_host_realm(ctx, realm_list);
521 realm_list = NULL;
523 krb5_free_context(ctx);
524 ctx = NULL;
526 return realm;
527 #else
528 return NULL;
529 #endif
532 /************************************************************************
533 Routine to get the salting principal for this service. This is
534 maintained for backwards compatibilty with releases prior to 3.0.24.
535 Since we store the salting principal string only at join, we may have
536 to look for the older tdb keys. Caller must free if return is not null.
537 ************************************************************************/
539 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
540 krb5_principal host_princ,
541 int enctype)
543 char *unparsed_name = NULL, *salt_princ_s = NULL;
544 krb5_principal ret_princ = NULL;
546 /* lookup new key first */
548 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
550 /* look under the old key. If this fails, just use the standard key */
552 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
553 return (krb5_principal)NULL;
555 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
556 /* fall back to host/machine.realm@REALM */
557 salt_princ_s = kerberos_standard_des_salt();
561 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
562 ret_princ = NULL;
565 TALLOC_FREE(unparsed_name);
566 SAFE_FREE(salt_princ_s);
568 return ret_princ;
571 /************************************************************************
572 Routine to set the salting principal for this service. Active
573 Directory may use a non-obvious principal name to generate the salt
574 when it determines the key to use for encrypting tickets for a service,
575 and hopefully we detected that when we joined the domain.
576 Setting principal to NULL deletes this entry.
577 ************************************************************************/
579 bool kerberos_secrets_store_salting_principal(const char *service,
580 int enctype,
581 const char *principal)
583 char *key = NULL;
584 bool ret = False;
585 krb5_context context = NULL;
586 krb5_principal princ = NULL;
587 char *princ_s = NULL;
588 char *unparsed_name = NULL;
589 krb5_error_code code;
591 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
592 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
593 error_message(code)));
594 return False;
596 if (strchr_m(service, '@')) {
597 if (asprintf(&princ_s, "%s", service) == -1) {
598 goto out;
600 } else {
601 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
602 goto out;
606 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
607 goto out;
609 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
610 goto out;
613 if (asprintf(&key, "%s/%s/enctype=%d",
614 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
615 == -1) {
616 goto out;
619 if ((principal != NULL) && (strlen(principal) > 0)) {
620 ret = secrets_store(key, principal, strlen(principal) + 1);
621 } else {
622 ret = secrets_delete(key);
625 out:
627 SAFE_FREE(key);
628 SAFE_FREE(princ_s);
629 TALLOC_FREE(unparsed_name);
631 if (princ) {
632 krb5_free_principal(context, princ);
635 if (context) {
636 krb5_free_context(context);
639 return ret;
643 /************************************************************************
644 ************************************************************************/
646 int kerberos_kinit_password(const char *principal,
647 const char *password,
648 int time_offset,
649 const char *cache_name)
651 return kerberos_kinit_password_ext(principal,
652 password,
653 time_offset,
656 cache_name,
657 False,
658 False,
660 NULL);
663 /************************************************************************
664 ************************************************************************/
666 static char *print_kdc_line(char *mem_ctx,
667 const char *prev_line,
668 const struct sockaddr_storage *pss,
669 const char *kdc_name)
671 char *kdc_str = NULL;
672 char addr[INET6_ADDRSTRLEN];
673 uint16_t port = get_sockaddr_port(pss);
675 if (pss->ss_family == AF_INET) {
676 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
677 prev_line,
678 print_canonical_sockaddr(mem_ctx, pss));
682 * IPv6 starts here
685 DEBUG(10, ("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
686 kdc_name, port));
688 if (port != 0 && port != DEFAULT_KRB5_PORT) {
689 /* Currently for IPv6 we can't specify a non-default
690 krb5 port with an address, as this requires a ':'.
691 Resolve to a name. */
692 char hostname[MAX_DNS_NAME_LENGTH];
693 int ret = sys_getnameinfo((const struct sockaddr *)pss,
694 sizeof(*pss),
695 hostname, sizeof(hostname),
696 NULL, 0,
697 NI_NAMEREQD);
698 if (ret) {
699 DEBUG(0,("print_kdc_line: can't resolve name "
700 "for kdc with non-default port %s. "
701 "Error %s\n.",
702 print_canonical_sockaddr(mem_ctx, pss),
703 gai_strerror(ret)));
704 return NULL;
706 /* Success, use host:port */
707 kdc_str = talloc_asprintf(mem_ctx,
708 "%s\tkdc = %s:%u\n",
709 prev_line,
710 hostname,
711 (unsigned int)port);
712 } else {
714 /* no krb5 lib currently supports "kdc = ipv6 address"
715 * at all, so just fill in just the kdc_name if we have
716 * it and let the krb5 lib figure out the appropriate
717 * ipv6 address - gd */
719 if (kdc_name) {
720 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
721 prev_line, kdc_name);
722 } else {
723 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
724 prev_line,
725 print_sockaddr(addr,
726 sizeof(addr),
727 pss));
730 return kdc_str;
733 /************************************************************************
734 Create a string list of available kdc's, possibly searching by sitename.
735 Does DNS queries.
737 If "sitename" is given, the DC's in that site are listed first.
739 ************************************************************************/
741 static char *get_kdc_ip_string(char *mem_ctx,
742 const char *realm,
743 const char *sitename,
744 const struct sockaddr_storage *pss,
745 const char *kdc_name)
747 int i;
748 struct ip_service *ip_srv_site = NULL;
749 struct ip_service *ip_srv_nonsite = NULL;
750 int count_site = 0;
751 int count_nonsite;
752 char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
754 if (kdc_str == NULL) {
755 return NULL;
759 * First get the KDC's only in this site, the rest will be
760 * appended later
763 if (sitename) {
765 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
767 for (i = 0; i < count_site; i++) {
768 if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss,
769 (struct sockaddr *)pss)) {
770 continue;
772 /* Append to the string - inefficient
773 * but not done often. */
774 kdc_str = print_kdc_line(mem_ctx,
775 kdc_str,
776 &ip_srv_site[i].ss,
777 NULL);
778 if (!kdc_str) {
779 SAFE_FREE(ip_srv_site);
780 return NULL;
785 /* Get all KDC's. */
787 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
789 for (i = 0; i < count_nonsite; i++) {
790 int j;
792 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) {
793 continue;
796 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
797 for (j = 0; j < count_site; j++) {
798 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss,
799 (struct sockaddr *)&ip_srv_site[j].ss)) {
800 break;
802 /* As the lists are sorted we can break early if nonsite > site. */
803 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
804 break;
807 if (j != i) {
808 continue;
811 /* Append to the string - inefficient but not done often. */
812 kdc_str = print_kdc_line(mem_ctx,
813 kdc_str,
814 &ip_srv_nonsite[i].ss,
815 NULL);
816 if (!kdc_str) {
817 SAFE_FREE(ip_srv_site);
818 SAFE_FREE(ip_srv_nonsite);
819 return NULL;
824 SAFE_FREE(ip_srv_site);
825 SAFE_FREE(ip_srv_nonsite);
827 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
828 kdc_str ));
830 return kdc_str;
833 /************************************************************************
834 Create a specific krb5.conf file in the private directory pointing
835 at a specific kdc for a realm. Keyed off domain name. Sets
836 KRB5_CONFIG environment variable to point to this file. Must be
837 run as root or will fail (which is a good thing :-).
838 ************************************************************************/
840 bool create_local_private_krb5_conf_for_domain(const char *realm,
841 const char *domain,
842 const char *sitename,
843 const struct sockaddr_storage *pss,
844 const char *kdc_name)
846 char *dname;
847 char *tmpname = NULL;
848 char *fname = NULL;
849 char *file_contents = NULL;
850 char *kdc_ip_string = NULL;
851 size_t flen = 0;
852 ssize_t ret;
853 int fd;
854 char *realm_upper = NULL;
855 bool result = false;
857 if (!lp_create_krb5_conf()) {
858 return false;
861 dname = lock_path("smb_krb5");
862 if (!dname) {
863 return false;
865 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
866 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
867 "failed to create directory %s. Error was %s\n",
868 dname, strerror(errno) ));
869 goto done;
872 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
873 if (!tmpname) {
874 goto done;
877 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
878 if (!fname) {
879 goto done;
882 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
883 fname, realm, domain ));
885 realm_upper = talloc_strdup(fname, realm);
886 strupper_m(realm_upper);
888 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
889 if (!kdc_ip_string) {
890 goto done;
893 file_contents = talloc_asprintf(fname,
894 "[libdefaults]\n\tdefault_realm = %s\n"
895 "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
896 "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
897 "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
898 "[realms]\n\t%s = {\n"
899 "\t%s\t}\n",
900 realm_upper, realm_upper, kdc_ip_string);
902 if (!file_contents) {
903 goto done;
906 flen = strlen(file_contents);
908 fd = mkstemp(tmpname);
909 if (fd == -1) {
910 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
911 " for file %s. Errno %s\n",
912 tmpname, strerror(errno) ));
913 goto done;
916 if (fchmod(fd, 0644)==-1) {
917 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
918 " Errno %s\n",
919 tmpname, strerror(errno) ));
920 unlink(tmpname);
921 close(fd);
922 goto done;
925 ret = write(fd, file_contents, flen);
926 if (flen != ret) {
927 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
928 " returned %d (should be %u). Errno %s\n",
929 (int)ret, (unsigned int)flen, strerror(errno) ));
930 unlink(tmpname);
931 close(fd);
932 goto done;
934 if (close(fd)==-1) {
935 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
936 " Errno %s\n", strerror(errno) ));
937 unlink(tmpname);
938 goto done;
941 if (rename(tmpname, fname) == -1) {
942 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
943 "of %s to %s failed. Errno %s\n",
944 tmpname, fname, strerror(errno) ));
945 unlink(tmpname);
946 goto done;
949 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
950 "file %s with realm %s KDC list = %s\n",
951 fname, realm_upper, kdc_ip_string));
953 /* Set the environment variable to this file. */
954 setenv("KRB5_CONFIG", fname, 1);
956 result = true;
958 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
960 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
961 /* Insanity, sheer insanity..... */
963 if (strequal(realm, lp_realm())) {
964 SMB_STRUCT_STAT sbuf;
966 if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
967 if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
968 int lret;
969 size_t alloc_size = sbuf.st_ex_size + 1;
970 char *linkpath = talloc_array(talloc_tos(), char,
971 alloc_size);
972 if (!linkpath) {
973 goto done;
975 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
976 alloc_size - 1);
977 if (lret == -1) {
978 TALLOC_FREE(linkpath);
979 goto done;
981 linkpath[lret] = '\0';
983 if (strcmp(linkpath, fname) == 0) {
984 /* Symlink already exists. */
985 TALLOC_FREE(linkpath);
986 goto done;
988 TALLOC_FREE(linkpath);
992 /* Try and replace with a symlink. */
993 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
994 const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
995 if (errno != EEXIST) {
996 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
997 "of %s to %s failed. Errno %s\n",
998 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
999 goto done; /* Not a fatal error. */
1002 /* Yes, this is a race conditon... too bad. */
1003 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
1004 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
1005 "of %s to %s failed. Errno %s\n",
1006 SYSTEM_KRB5_CONF_PATH, newpath,
1007 strerror(errno) ));
1008 goto done; /* Not a fatal error. */
1011 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1012 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1013 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1014 fname, strerror(errno) ));
1015 goto done; /* Not a fatal error. */
1019 #endif
1021 done:
1022 TALLOC_FREE(tmpname);
1023 TALLOC_FREE(dname);
1025 return result;
1027 #endif