s3-kerberos: temporary fix for ipv6 in print_kdc_line().
[Samba.git] / source3 / libads / kerberos.c
blobaa3a76bf2ac1504bf3318caf4b7fa6e7a91f5cac
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 "smb_krb5.h"
27 #ifdef HAVE_KRB5
29 #define DEFAULT_KRB5_PORT 88
31 #define LIBADS_CCACHE_NAME "MEMORY:libads"
34 we use a prompter to avoid a crash bug in the kerberos libs when
35 dealing with empty passwords
36 this prompter is just a string copy ...
38 static krb5_error_code
39 kerb_prompter(krb5_context ctx, void *data,
40 const char *name,
41 const char *banner,
42 int num_prompts,
43 krb5_prompt prompts[])
45 if (num_prompts == 0) return 0;
47 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
48 if (prompts[0].reply->length > 0) {
49 if (data) {
50 strncpy(prompts[0].reply->data, (const char *)data,
51 prompts[0].reply->length-1);
52 prompts[0].reply->length = strlen(prompts[0].reply->data);
53 } else {
54 prompts[0].reply->length = 0;
57 return 0;
60 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
61 NTSTATUS *nt_status)
63 DATA_BLOB edata;
64 DATA_BLOB unwrapped_edata;
65 TALLOC_CTX *mem_ctx;
66 struct KRB5_EDATA_NTSTATUS parsed_edata;
67 enum ndr_err_code ndr_err;
69 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
70 edata = data_blob(error->e_data->data, error->e_data->length);
71 #else
72 edata = data_blob(error->e_data.data, error->e_data.length);
73 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
75 #ifdef DEVELOPER
76 dump_data(10, edata.data, edata.length);
77 #endif /* DEVELOPER */
79 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
80 if (mem_ctx == NULL) {
81 data_blob_free(&edata);
82 return False;
85 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
86 data_blob_free(&edata);
87 TALLOC_FREE(mem_ctx);
88 return False;
91 data_blob_free(&edata);
93 ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx, NULL,
94 &parsed_edata,
95 (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
96 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
97 data_blob_free(&unwrapped_edata);
98 TALLOC_FREE(mem_ctx);
99 return False;
102 data_blob_free(&unwrapped_edata);
104 if (nt_status) {
105 *nt_status = parsed_edata.ntstatus;
108 TALLOC_FREE(mem_ctx);
110 return True;
113 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
114 krb5_get_init_creds_opt *opt,
115 NTSTATUS *nt_status)
117 bool ret = False;
118 krb5_error *error = NULL;
120 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
121 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
122 if (ret) {
123 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
124 error_message(ret)));
125 return False;
127 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
129 if (!error) {
130 DEBUG(1,("no krb5_error\n"));
131 return False;
134 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
135 if (!error->e_data) {
136 #else
137 if (error->e_data.data == NULL) {
138 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
139 DEBUG(1,("no edata in krb5_error\n"));
140 krb5_free_error(ctx, error);
141 return False;
144 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
146 krb5_free_error(ctx, error);
148 return ret;
152 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
153 place in default cache location.
154 remus@snapserver.com
156 int kerberos_kinit_password_ext(const char *principal,
157 const char *password,
158 int time_offset,
159 time_t *expire_time,
160 time_t *renew_till_time,
161 const char *cache_name,
162 bool request_pac,
163 bool add_netbios_addr,
164 time_t renewable_time,
165 NTSTATUS *ntstatus)
167 krb5_context ctx = NULL;
168 krb5_error_code code = 0;
169 krb5_ccache cc = NULL;
170 krb5_principal me = NULL;
171 krb5_creds my_creds;
172 krb5_get_init_creds_opt *opt = NULL;
173 smb_krb5_addresses *addr = NULL;
175 ZERO_STRUCT(my_creds);
177 initialize_krb5_error_table();
178 if ((code = krb5_init_context(&ctx)))
179 goto out;
181 if (time_offset != 0) {
182 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
185 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
186 principal,
187 cache_name ? cache_name: krb5_cc_default_name(ctx),
188 getenv("KRB5_CONFIG")));
190 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
191 goto out;
194 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
195 goto out;
198 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
199 goto out;
202 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
203 krb5_get_init_creds_opt_set_forwardable(opt, True);
204 #if 0
205 /* insane testing */
206 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
207 #endif
209 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
210 if (request_pac) {
211 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
212 goto out;
215 #endif
216 if (add_netbios_addr) {
217 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
218 goto out;
220 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
223 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
224 kerb_prompter, CONST_DISCARD(char *,password),
225 0, NULL, opt))) {
226 goto out;
229 if ((code = krb5_cc_initialize(ctx, cc, me))) {
230 goto out;
233 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
234 goto out;
237 if (expire_time) {
238 *expire_time = (time_t) my_creds.times.endtime;
241 if (renew_till_time) {
242 *renew_till_time = (time_t) my_creds.times.renew_till;
244 out:
245 if (ntstatus) {
247 NTSTATUS status;
249 /* fast path */
250 if (code == 0) {
251 *ntstatus = NT_STATUS_OK;
252 goto cleanup;
255 /* try to get ntstatus code out of krb5_error when we have it
256 * inside the krb5_get_init_creds_opt - gd */
258 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
259 *ntstatus = status;
260 goto cleanup;
263 /* fall back to self-made-mapping */
264 *ntstatus = krb5_to_nt_status(code);
267 cleanup:
268 krb5_free_cred_contents(ctx, &my_creds);
269 if (me) {
270 krb5_free_principal(ctx, me);
272 if (addr) {
273 smb_krb5_free_addresses(ctx, addr);
275 if (opt) {
276 smb_krb5_get_init_creds_opt_free(ctx, opt);
278 if (cc) {
279 krb5_cc_close(ctx, cc);
281 if (ctx) {
282 krb5_free_context(ctx);
284 return code;
289 /* run kinit to setup our ccache */
290 int ads_kinit_password(ADS_STRUCT *ads)
292 char *s;
293 int ret;
294 const char *account_name;
295 fstring acct_name;
297 if (ads->auth.flags & ADS_AUTH_USER_CREDS) {
298 account_name = ads->auth.user_name;
299 goto got_accountname;
302 if ( IS_DC ) {
303 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
304 account_name = lp_workgroup();
305 } else {
306 /* always use the sAMAccountName for security = domain */
307 /* global_myname()$@REA.LM */
308 if ( lp_security() == SEC_DOMAIN ) {
309 fstr_sprintf( acct_name, "%s$", global_myname() );
310 account_name = acct_name;
312 else
313 /* This looks like host/global_myname()@REA.LM */
314 account_name = ads->auth.user_name;
317 got_accountname:
318 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
319 return KRB5_CC_NOMEM;
322 if (!ads->auth.password) {
323 SAFE_FREE(s);
324 return KRB5_LIBOS_CANTREADPWD;
327 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
328 &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable,
329 NULL);
331 if (ret) {
332 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
333 s, error_message(ret)));
335 SAFE_FREE(s);
336 return ret;
339 int ads_kdestroy(const char *cc_name)
341 krb5_error_code code;
342 krb5_context ctx = NULL;
343 krb5_ccache cc = NULL;
345 initialize_krb5_error_table();
346 if ((code = krb5_init_context (&ctx))) {
347 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
348 error_message(code)));
349 return code;
352 if (!cc_name) {
353 if ((code = krb5_cc_default(ctx, &cc))) {
354 krb5_free_context(ctx);
355 return code;
357 } else {
358 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
359 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
360 error_message(code)));
361 krb5_free_context(ctx);
362 return code;
366 if ((code = krb5_cc_destroy (ctx, cc))) {
367 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
368 error_message(code)));
371 krb5_free_context (ctx);
372 return code;
375 /************************************************************************
376 Routine to fetch the salting principal for a service. Active
377 Directory may use a non-obvious principal name to generate the salt
378 when it determines the key to use for encrypting tickets for a service,
379 and hopefully we detected that when we joined the domain.
380 ************************************************************************/
382 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
384 char *key = NULL;
385 char *ret = NULL;
387 if (asprintf(&key, "%s/%s/enctype=%d",
388 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
389 return NULL;
391 ret = (char *)secrets_fetch(key, NULL);
392 SAFE_FREE(key);
393 return ret;
396 /************************************************************************
397 Return the standard DES salt key
398 ************************************************************************/
400 char* kerberos_standard_des_salt( void )
402 fstring salt;
404 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
405 strlower_m( salt );
406 fstrcat( salt, lp_realm() );
408 return SMB_STRDUP( salt );
411 /************************************************************************
412 ************************************************************************/
414 static char* des_salt_key( void )
416 char *key;
418 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
419 lp_realm()) == -1) {
420 return NULL;
423 return key;
426 /************************************************************************
427 ************************************************************************/
429 bool kerberos_secrets_store_des_salt( const char* salt )
431 char* key;
432 bool ret;
434 if ( (key = des_salt_key()) == NULL ) {
435 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
436 return False;
439 if ( !salt ) {
440 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
441 secrets_delete( key );
442 return True;
445 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
447 ret = secrets_store( key, salt, strlen(salt)+1 );
449 SAFE_FREE( key );
451 return ret;
454 /************************************************************************
455 ************************************************************************/
457 char* kerberos_secrets_fetch_des_salt( void )
459 char *salt, *key;
461 if ( (key = des_salt_key()) == NULL ) {
462 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
463 return False;
466 salt = (char*)secrets_fetch( key, NULL );
468 SAFE_FREE( key );
470 return salt;
473 /************************************************************************
474 Routine to get the default realm from the kerberos credentials cache.
475 Caller must free if the return value is not NULL.
476 ************************************************************************/
478 char *kerberos_get_default_realm_from_ccache( void )
480 char *realm = NULL;
481 krb5_context ctx = NULL;
482 krb5_ccache cc = NULL;
483 krb5_principal princ = NULL;
485 initialize_krb5_error_table();
486 if (krb5_init_context(&ctx)) {
487 return NULL;
490 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
491 "Trying to read krb5 cache: %s\n",
492 krb5_cc_default_name(ctx)));
493 if (krb5_cc_default(ctx, &cc)) {
494 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
495 "failed to read default cache\n"));
496 goto out;
498 if (krb5_cc_get_principal(ctx, cc, &princ)) {
499 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
500 "failed to get default principal\n"));
501 goto out;
504 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
505 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
506 #elif defined(HAVE_KRB5_PRINC_REALM)
508 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
509 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
511 #endif
513 out:
515 if (ctx) {
516 if (princ) {
517 krb5_free_principal(ctx, princ);
519 if (cc) {
520 krb5_cc_close(ctx, cc);
522 krb5_free_context(ctx);
525 return realm;
529 /************************************************************************
530 Routine to get the salting principal for this service. This is
531 maintained for backwards compatibilty with releases prior to 3.0.24.
532 Since we store the salting principal string only at join, we may have
533 to look for the older tdb keys. Caller must free if return is not null.
534 ************************************************************************/
536 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
537 krb5_principal host_princ,
538 int enctype)
540 char *unparsed_name = NULL, *salt_princ_s = NULL;
541 krb5_principal ret_princ = NULL;
543 /* lookup new key first */
545 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
547 /* look under the old key. If this fails, just use the standard key */
549 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
550 return (krb5_principal)NULL;
552 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
553 /* fall back to host/machine.realm@REALM */
554 salt_princ_s = kerberos_standard_des_salt();
558 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
559 ret_princ = NULL;
562 TALLOC_FREE(unparsed_name);
563 SAFE_FREE(salt_princ_s);
565 return ret_princ;
568 /************************************************************************
569 Routine to set the salting principal for this service. Active
570 Directory may use a non-obvious principal name to generate the salt
571 when it determines the key to use for encrypting tickets for a service,
572 and hopefully we detected that when we joined the domain.
573 Setting principal to NULL deletes this entry.
574 ************************************************************************/
576 bool kerberos_secrets_store_salting_principal(const char *service,
577 int enctype,
578 const char *principal)
580 char *key = NULL;
581 bool ret = False;
582 krb5_context context = NULL;
583 krb5_principal princ = NULL;
584 char *princ_s = NULL;
585 char *unparsed_name = NULL;
586 krb5_error_code code;
588 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
589 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
590 error_message(code)));
591 return False;
593 if (strchr_m(service, '@')) {
594 if (asprintf(&princ_s, "%s", service) == -1) {
595 goto out;
597 } else {
598 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
599 goto out;
603 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
604 goto out;
607 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
608 goto out;
611 if (asprintf(&key, "%s/%s/enctype=%d",
612 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
613 == -1) {
614 goto out;
617 if ((principal != NULL) && (strlen(principal) > 0)) {
618 ret = secrets_store(key, principal, strlen(principal) + 1);
619 } else {
620 ret = secrets_delete(key);
623 out:
625 SAFE_FREE(key);
626 SAFE_FREE(princ_s);
627 TALLOC_FREE(unparsed_name);
629 if (princ) {
630 krb5_free_principal(context, princ);
633 if (context) {
634 krb5_free_context(context);
637 return ret;
641 /************************************************************************
642 ************************************************************************/
644 int kerberos_kinit_password(const char *principal,
645 const char *password,
646 int time_offset,
647 const char *cache_name)
649 return kerberos_kinit_password_ext(principal,
650 password,
651 time_offset,
654 cache_name,
655 False,
656 False,
658 NULL);
661 /************************************************************************
662 ************************************************************************/
664 static char *print_kdc_line(char *mem_ctx,
665 const char *prev_line,
666 const struct sockaddr_storage *pss,
667 const char *kdc_name)
669 char *kdc_str = NULL;
671 if (pss->ss_family == AF_INET) {
672 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
673 prev_line,
674 print_canonical_sockaddr(mem_ctx, pss));
675 } else {
676 char addr[INET6_ADDRSTRLEN];
677 uint16_t port = get_sockaddr_port(pss);
679 DEBUG(10,("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
680 kdc_name, port));
682 if (port != 0 && port != DEFAULT_KRB5_PORT) {
683 /* Currently for IPv6 we can't specify a non-default
684 krb5 port with an address, as this requires a ':'.
685 Resolve to a name. */
686 char hostname[MAX_DNS_NAME_LENGTH];
687 int ret = sys_getnameinfo((const struct sockaddr *)pss,
688 sizeof(*pss),
689 hostname, sizeof(hostname),
690 NULL, 0,
691 NI_NAMEREQD);
692 if (ret) {
693 DEBUG(0,("print_kdc_line: can't resolve name "
694 "for kdc with non-default port %s. "
695 "Error %s\n.",
696 print_canonical_sockaddr(mem_ctx, pss),
697 gai_strerror(ret)));
698 return NULL;
700 /* Success, use host:port */
701 kdc_str = talloc_asprintf(mem_ctx,
702 "%s\tkdc = %s:%u\n",
703 prev_line,
704 hostname,
705 (unsigned int)port);
706 } else {
708 /* no krb5 lib currently supports "kdc = ipv6 address"
709 * at all, so just fill in just the kdc_name if we have
710 * it and let the krb5 lib figure out the appropriate
711 * ipv6 address - gd */
713 if (kdc_name) {
714 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
715 prev_line, kdc_name);
716 } else {
717 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
718 prev_line,
719 print_sockaddr(addr,
720 sizeof(addr),
721 pss));
725 return kdc_str;
728 /************************************************************************
729 Create a string list of available kdc's, possibly searching by sitename.
730 Does DNS queries.
732 If "sitename" is given, the DC's in that site are listed first.
734 ************************************************************************/
736 static char *get_kdc_ip_string(char *mem_ctx,
737 const char *realm,
738 const char *sitename,
739 struct sockaddr_storage *pss,
740 const char *kdc_name)
742 int i;
743 struct ip_service *ip_srv_site = NULL;
744 struct ip_service *ip_srv_nonsite = NULL;
745 int count_site = 0;
746 int count_nonsite;
747 char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
749 if (kdc_str == NULL) {
750 return NULL;
754 * First get the KDC's only in this site, the rest will be
755 * appended later
758 if (sitename) {
760 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
762 for (i = 0; i < count_site; i++) {
763 if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss,
764 (struct sockaddr *)pss)) {
765 continue;
767 /* Append to the string - inefficient
768 * but not done often. */
769 kdc_str = print_kdc_line(mem_ctx,
770 kdc_str,
771 &ip_srv_site[i].ss,
772 NULL);
773 if (!kdc_str) {
774 SAFE_FREE(ip_srv_site);
775 return NULL;
780 /* Get all KDC's. */
782 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
784 for (i = 0; i < count_nonsite; i++) {
785 int j;
787 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) {
788 continue;
791 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
792 for (j = 0; j < count_site; j++) {
793 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss,
794 (struct sockaddr *)&ip_srv_site[j].ss)) {
795 break;
797 /* As the lists are sorted we can break early if nonsite > site. */
798 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
799 break;
802 if (j != i) {
803 continue;
806 /* Append to the string - inefficient but not done often. */
807 kdc_str = print_kdc_line(mem_ctx,
808 kdc_str,
809 &ip_srv_nonsite[i].ss,
810 NULL);
811 if (!kdc_str) {
812 SAFE_FREE(ip_srv_site);
813 SAFE_FREE(ip_srv_nonsite);
814 return NULL;
819 SAFE_FREE(ip_srv_site);
820 SAFE_FREE(ip_srv_nonsite);
822 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
823 kdc_str ));
825 return kdc_str;
828 /************************************************************************
829 Create a specific krb5.conf file in the private directory pointing
830 at a specific kdc for a realm. Keyed off domain name. Sets
831 KRB5_CONFIG environment variable to point to this file. Must be
832 run as root or will fail (which is a good thing :-).
833 ************************************************************************/
835 bool create_local_private_krb5_conf_for_domain(const char *realm,
836 const char *domain,
837 const char *sitename,
838 struct sockaddr_storage *pss,
839 const char *kdc_name)
841 char *dname = lock_path("smb_krb5");
842 char *tmpname = NULL;
843 char *fname = NULL;
844 char *file_contents = NULL;
845 char *kdc_ip_string = NULL;
846 size_t flen = 0;
847 ssize_t ret;
848 int fd;
849 char *realm_upper = NULL;
850 bool result = false;
852 if (!dname) {
853 return false;
855 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
856 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
857 "failed to create directory %s. Error was %s\n",
858 dname, strerror(errno) ));
859 goto done;
862 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
863 if (!tmpname) {
864 goto done;
867 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
868 if (!fname) {
869 goto done;
872 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
873 fname, realm, domain ));
875 realm_upper = talloc_strdup(fname, realm);
876 strupper_m(realm_upper);
878 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
879 if (!kdc_ip_string) {
880 goto done;
883 file_contents = talloc_asprintf(fname,
884 "[libdefaults]\n\tdefault_realm = %s\n"
885 "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
886 "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
887 "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
888 "[realms]\n\t%s = {\n"
889 "\t%s\t}\n",
890 realm_upper, realm_upper, kdc_ip_string);
892 if (!file_contents) {
893 goto done;
896 flen = strlen(file_contents);
898 fd = smb_mkstemp(tmpname);
899 if (fd == -1) {
900 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
901 " for file %s. Errno %s\n",
902 tmpname, strerror(errno) ));
903 goto done;
906 if (fchmod(fd, 0644)==-1) {
907 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
908 " Errno %s\n",
909 tmpname, strerror(errno) ));
910 unlink(tmpname);
911 close(fd);
912 goto done;
915 ret = write(fd, file_contents, flen);
916 if (flen != ret) {
917 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
918 " returned %d (should be %u). Errno %s\n",
919 (int)ret, (unsigned int)flen, strerror(errno) ));
920 unlink(tmpname);
921 close(fd);
922 goto done;
924 if (close(fd)==-1) {
925 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
926 " Errno %s\n", strerror(errno) ));
927 unlink(tmpname);
928 goto done;
931 if (rename(tmpname, fname) == -1) {
932 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
933 "of %s to %s failed. Errno %s\n",
934 tmpname, fname, strerror(errno) ));
935 unlink(tmpname);
936 goto done;
939 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
940 "file %s with realm %s KDC list = %s\n",
941 fname, realm_upper, kdc_ip_string));
943 /* Set the environment variable to this file. */
944 setenv("KRB5_CONFIG", fname, 1);
946 result = true;
948 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
950 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
951 /* Insanity, sheer insanity..... */
953 if (strequal(realm, lp_realm())) {
954 char linkpath[PATH_MAX+1];
955 int lret;
957 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
958 if (lret != -1) {
959 linkpath[lret] = '\0';
962 if (lret != -1 || strcmp(linkpath, fname) == 0) {
963 /* Symlink already exists. */
964 goto done;
967 /* Try and replace with a symlink. */
968 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
969 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
970 if (errno != EEXIST) {
971 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
972 "of %s to %s failed. Errno %s\n",
973 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
974 goto done; /* Not a fatal error. */
977 /* Yes, this is a race conditon... too bad. */
978 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
979 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
980 "of %s to %s failed. Errno %s\n",
981 SYSTEM_KRB5_CONF_PATH, newpath,
982 strerror(errno) ));
983 goto done; /* Not a fatal error. */
986 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
987 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
988 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
989 fname, strerror(errno) ));
990 goto done; /* Not a fatal error. */
994 #endif
996 done:
997 TALLOC_FREE(tmpname);
998 TALLOC_FREE(dname);
1000 return result;
1002 #endif