script: sign library releases with Samba Library Distribution Key
[Samba.git] / source3 / libads / kerberos.c
blob31af49685a8ae6b7b4f389f1f61dd6a6a8964040
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"
26 #include "../librpc/gen_ndr/ndr_misc.h"
27 #include "libads/kerberos_proto.h"
28 #include "secrets.h"
30 #ifdef HAVE_KRB5
32 #define DEFAULT_KRB5_PORT 88
34 #define LIBADS_CCACHE_NAME "MEMORY:libads"
37 we use a prompter to avoid a crash bug in the kerberos libs when
38 dealing with empty passwords
39 this prompter is just a string copy ...
41 static krb5_error_code
42 kerb_prompter(krb5_context ctx, void *data,
43 const char *name,
44 const char *banner,
45 int num_prompts,
46 krb5_prompt prompts[])
48 if (num_prompts == 0) return 0;
50 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
51 if (prompts[0].reply->length > 0) {
52 if (data) {
53 strncpy((char *)prompts[0].reply->data, (const char *)data,
54 prompts[0].reply->length-1);
55 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
56 } else {
57 prompts[0].reply->length = 0;
60 return 0;
63 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
64 NTSTATUS *nt_status)
66 DATA_BLOB edata;
67 DATA_BLOB unwrapped_edata;
68 TALLOC_CTX *mem_ctx;
69 struct KRB5_EDATA_NTSTATUS parsed_edata;
70 enum ndr_err_code ndr_err;
72 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
73 edata = data_blob(error->e_data->data, error->e_data->length);
74 #else
75 edata = data_blob(error->e_data.data, error->e_data.length);
76 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
78 #ifdef DEVELOPER
79 dump_data(10, edata.data, edata.length);
80 #endif /* DEVELOPER */
82 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
83 if (mem_ctx == NULL) {
84 data_blob_free(&edata);
85 return False;
88 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
89 data_blob_free(&edata);
90 TALLOC_FREE(mem_ctx);
91 return False;
94 data_blob_free(&edata);
96 ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx,
97 &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
98 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
99 data_blob_free(&unwrapped_edata);
100 TALLOC_FREE(mem_ctx);
101 return False;
104 data_blob_free(&unwrapped_edata);
106 if (nt_status) {
107 *nt_status = parsed_edata.ntstatus;
110 TALLOC_FREE(mem_ctx);
112 return True;
115 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
116 krb5_get_init_creds_opt *opt,
117 NTSTATUS *nt_status)
119 bool ret = False;
120 krb5_error *error = NULL;
122 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
123 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
124 if (ret) {
125 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
126 error_message(ret)));
127 return False;
129 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
131 if (!error) {
132 DEBUG(1,("no krb5_error\n"));
133 return False;
136 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
137 if (!error->e_data) {
138 #else
139 if (error->e_data.data == NULL) {
140 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
141 DEBUG(1,("no edata in krb5_error\n"));
142 krb5_free_error(ctx, error);
143 return False;
146 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
148 krb5_free_error(ctx, error);
150 return ret;
154 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
155 place in default cache location.
156 remus@snapserver.com
158 int kerberos_kinit_password_ext(const char *principal,
159 const char *password,
160 int time_offset,
161 time_t *expire_time,
162 time_t *renew_till_time,
163 const char *cache_name,
164 bool request_pac,
165 bool add_netbios_addr,
166 time_t renewable_time,
167 NTSTATUS *ntstatus)
169 krb5_context ctx = NULL;
170 krb5_error_code code = 0;
171 krb5_ccache cc = NULL;
172 krb5_principal me = NULL;
173 krb5_creds my_creds;
174 krb5_get_init_creds_opt *opt = NULL;
175 smb_krb5_addresses *addr = NULL;
177 ZERO_STRUCT(my_creds);
179 initialize_krb5_error_table();
180 if ((code = krb5_init_context(&ctx)))
181 goto out;
183 if (time_offset != 0) {
184 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
187 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
188 principal,
189 cache_name ? cache_name: krb5_cc_default_name(ctx),
190 getenv("KRB5_CONFIG")));
192 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
193 goto out;
196 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
197 goto out;
200 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
201 goto out;
204 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
205 krb5_get_init_creds_opt_set_forwardable(opt, True);
206 #if 0
207 /* insane testing */
208 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
209 #endif
211 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
212 if (request_pac) {
213 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
214 goto out;
217 #endif
218 if (add_netbios_addr) {
219 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
220 goto out;
222 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
225 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
226 kerb_prompter, CONST_DISCARD(char *,password),
227 0, NULL, opt))) {
228 goto out;
231 if ((code = krb5_cc_initialize(ctx, cc, me))) {
232 goto out;
235 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
236 goto out;
239 if (expire_time) {
240 *expire_time = (time_t) my_creds.times.endtime;
243 if (renew_till_time) {
244 *renew_till_time = (time_t) my_creds.times.renew_till;
246 out:
247 if (ntstatus) {
249 NTSTATUS status;
251 /* fast path */
252 if (code == 0) {
253 *ntstatus = NT_STATUS_OK;
254 goto cleanup;
257 /* try to get ntstatus code out of krb5_error when we have it
258 * inside the krb5_get_init_creds_opt - gd */
260 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
261 *ntstatus = status;
262 goto cleanup;
265 /* fall back to self-made-mapping */
266 *ntstatus = krb5_to_nt_status(code);
269 cleanup:
270 krb5_free_cred_contents(ctx, &my_creds);
271 if (me) {
272 krb5_free_principal(ctx, me);
274 if (addr) {
275 smb_krb5_free_addresses(ctx, addr);
277 if (opt) {
278 smb_krb5_get_init_creds_opt_free(ctx, opt);
280 if (cc) {
281 krb5_cc_close(ctx, cc);
283 if (ctx) {
284 krb5_free_context(ctx);
286 return code;
289 int ads_kdestroy(const char *cc_name)
291 krb5_error_code code;
292 krb5_context ctx = NULL;
293 krb5_ccache cc = NULL;
295 initialize_krb5_error_table();
296 if ((code = krb5_init_context (&ctx))) {
297 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
298 error_message(code)));
299 return code;
302 if (!cc_name) {
303 if ((code = krb5_cc_default(ctx, &cc))) {
304 krb5_free_context(ctx);
305 return code;
307 } else {
308 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
309 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
310 error_message(code)));
311 krb5_free_context(ctx);
312 return code;
316 if ((code = krb5_cc_destroy (ctx, cc))) {
317 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
318 error_message(code)));
321 krb5_free_context (ctx);
322 return code;
325 /************************************************************************
326 Routine to fetch the salting principal for a service. Active
327 Directory may use a non-obvious principal name to generate the salt
328 when it determines the key to use for encrypting tickets for a service,
329 and hopefully we detected that when we joined the domain.
330 ************************************************************************/
332 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
334 char *key = NULL;
335 char *ret = NULL;
337 if (asprintf(&key, "%s/%s/enctype=%d",
338 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
339 return NULL;
341 ret = (char *)secrets_fetch(key, NULL);
342 SAFE_FREE(key);
343 return ret;
346 /************************************************************************
347 Return the standard DES salt key
348 ************************************************************************/
350 char* kerberos_standard_des_salt( void )
352 fstring salt;
354 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
355 strlower_m( salt );
356 fstrcat( salt, lp_realm() );
358 return SMB_STRDUP( salt );
361 /************************************************************************
362 ************************************************************************/
364 static char* des_salt_key( void )
366 char *key;
368 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
369 lp_realm()) == -1) {
370 return NULL;
373 return key;
376 /************************************************************************
377 ************************************************************************/
379 bool kerberos_secrets_store_des_salt( const char* salt )
381 char* key;
382 bool ret;
384 if ( (key = des_salt_key()) == NULL ) {
385 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
386 return False;
389 if ( !salt ) {
390 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
391 secrets_delete( key );
392 return True;
395 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
397 ret = secrets_store( key, salt, strlen(salt)+1 );
399 SAFE_FREE( key );
401 return ret;
404 /************************************************************************
405 ************************************************************************/
407 char* kerberos_secrets_fetch_des_salt( void )
409 char *salt, *key;
411 if ( (key = des_salt_key()) == NULL ) {
412 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
413 return False;
416 salt = (char*)secrets_fetch( key, NULL );
418 SAFE_FREE( key );
420 return salt;
423 /************************************************************************
424 Routine to get the default realm from the kerberos credentials cache.
425 Caller must free if the return value is not NULL.
426 ************************************************************************/
428 char *kerberos_get_default_realm_from_ccache( void )
430 char *realm = NULL;
431 krb5_context ctx = NULL;
432 krb5_ccache cc = NULL;
433 krb5_principal princ = NULL;
435 initialize_krb5_error_table();
436 if (krb5_init_context(&ctx)) {
437 return NULL;
440 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
441 "Trying to read krb5 cache: %s\n",
442 krb5_cc_default_name(ctx)));
443 if (krb5_cc_default(ctx, &cc)) {
444 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
445 "failed to read default cache\n"));
446 goto out;
448 if (krb5_cc_get_principal(ctx, cc, &princ)) {
449 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
450 "failed to get default principal\n"));
451 goto out;
454 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
455 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
456 #elif defined(HAVE_KRB5_PRINC_REALM)
458 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
459 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
461 #endif
463 out:
465 if (ctx) {
466 if (princ) {
467 krb5_free_principal(ctx, princ);
469 if (cc) {
470 krb5_cc_close(ctx, cc);
472 krb5_free_context(ctx);
475 return realm;
478 /************************************************************************
479 Routine to get the realm from a given DNS name. Returns malloc'ed memory.
480 Caller must free() if the return value is not NULL.
481 ************************************************************************/
483 char *kerberos_get_realm_from_hostname(const char *hostname)
485 #if defined(HAVE_KRB5_GET_HOST_REALM) && defined(HAVE_KRB5_FREE_HOST_REALM)
486 #if defined(HAVE_KRB5_REALM_TYPE)
487 /* Heimdal. */
488 krb5_realm *realm_list = NULL;
489 #else
490 /* MIT */
491 char **realm_list = NULL;
492 #endif
493 char *realm = NULL;
494 krb5_error_code kerr;
495 krb5_context ctx = NULL;
497 initialize_krb5_error_table();
498 if (krb5_init_context(&ctx)) {
499 return NULL;
502 kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
503 if (kerr != 0) {
504 DEBUG(3,("kerberos_get_realm_from_hostname %s: "
505 "failed %s\n",
506 hostname ? hostname : "(NULL)",
507 error_message(kerr) ));
508 goto out;
511 if (realm_list && realm_list[0]) {
512 realm = SMB_STRDUP(realm_list[0]);
515 out:
517 if (ctx) {
518 if (realm_list) {
519 krb5_free_host_realm(ctx, realm_list);
520 realm_list = NULL;
522 krb5_free_context(ctx);
523 ctx = NULL;
525 return realm;
526 #else
527 return NULL;
528 #endif
531 /************************************************************************
532 Routine to get the salting principal for this service. This is
533 maintained for backwards compatibilty with releases prior to 3.0.24.
534 Since we store the salting principal string only at join, we may have
535 to look for the older tdb keys. Caller must free if return is not null.
536 ************************************************************************/
538 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
539 krb5_principal host_princ,
540 int enctype)
542 char *unparsed_name = NULL, *salt_princ_s = NULL;
543 krb5_principal ret_princ = NULL;
545 /* lookup new key first */
547 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
549 /* look under the old key. If this fails, just use the standard key */
551 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
552 return (krb5_principal)NULL;
554 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
555 /* fall back to host/machine.realm@REALM */
556 salt_princ_s = kerberos_standard_des_salt();
560 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
561 ret_princ = NULL;
564 TALLOC_FREE(unparsed_name);
565 SAFE_FREE(salt_princ_s);
567 return ret_princ;
570 /************************************************************************
571 Routine to set the salting principal for this service. Active
572 Directory may use a non-obvious principal name to generate the salt
573 when it determines the key to use for encrypting tickets for a service,
574 and hopefully we detected that when we joined the domain.
575 Setting principal to NULL deletes this entry.
576 ************************************************************************/
578 bool kerberos_secrets_store_salting_principal(const char *service,
579 int enctype,
580 const char *principal)
582 char *key = NULL;
583 bool ret = False;
584 krb5_context context = NULL;
585 krb5_principal princ = NULL;
586 char *princ_s = NULL;
587 char *unparsed_name = NULL;
588 krb5_error_code code;
590 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
591 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
592 error_message(code)));
593 return False;
595 if (strchr_m(service, '@')) {
596 if (asprintf(&princ_s, "%s", service) == -1) {
597 goto out;
599 } else {
600 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
601 goto out;
605 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
606 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;
673 if (pss->ss_family == AF_INET) {
674 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
675 prev_line,
676 print_canonical_sockaddr(mem_ctx, pss));
677 } else {
678 char addr[INET6_ADDRSTRLEN];
679 uint16_t port = get_sockaddr_port(pss);
681 DEBUG(10,("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
682 kdc_name, port));
684 if (port != 0 && port != DEFAULT_KRB5_PORT) {
685 /* Currently for IPv6 we can't specify a non-default
686 krb5 port with an address, as this requires a ':'.
687 Resolve to a name. */
688 char hostname[MAX_DNS_NAME_LENGTH];
689 int ret = sys_getnameinfo((const struct sockaddr *)pss,
690 sizeof(*pss),
691 hostname, sizeof(hostname),
692 NULL, 0,
693 NI_NAMEREQD);
694 if (ret) {
695 DEBUG(0,("print_kdc_line: can't resolve name "
696 "for kdc with non-default port %s. "
697 "Error %s\n.",
698 print_canonical_sockaddr(mem_ctx, pss),
699 gai_strerror(ret)));
700 return NULL;
702 /* Success, use host:port */
703 kdc_str = talloc_asprintf(mem_ctx,
704 "%s\tkdc = %s:%u\n",
705 prev_line,
706 hostname,
707 (unsigned int)port);
708 } else {
710 /* no krb5 lib currently supports "kdc = ipv6 address"
711 * at all, so just fill in just the kdc_name if we have
712 * it and let the krb5 lib figure out the appropriate
713 * ipv6 address - gd */
715 if (kdc_name) {
716 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
717 prev_line, kdc_name);
718 } else {
719 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
720 prev_line,
721 print_sockaddr(addr,
722 sizeof(addr),
723 pss));
727 return kdc_str;
730 /************************************************************************
731 Create a string list of available kdc's, possibly searching by sitename.
732 Does DNS queries.
734 If "sitename" is given, the DC's in that site are listed first.
736 ************************************************************************/
738 static char *get_kdc_ip_string(char *mem_ctx,
739 const char *realm,
740 const char *sitename,
741 struct sockaddr_storage *pss,
742 const char *kdc_name)
744 int i;
745 struct ip_service *ip_srv_site = NULL;
746 struct ip_service *ip_srv_nonsite = NULL;
747 int count_site = 0;
748 int count_nonsite;
749 char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
751 if (kdc_str == NULL) {
752 return NULL;
756 * First get the KDC's only in this site, the rest will be
757 * appended later
760 if (sitename) {
762 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
764 for (i = 0; i < count_site; i++) {
765 if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss,
766 (struct sockaddr *)pss)) {
767 continue;
769 /* Append to the string - inefficient
770 * but not done often. */
771 kdc_str = print_kdc_line(mem_ctx,
772 kdc_str,
773 &ip_srv_site[i].ss,
774 NULL);
775 if (!kdc_str) {
776 SAFE_FREE(ip_srv_site);
777 return NULL;
782 /* Get all KDC's. */
784 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
786 for (i = 0; i < count_nonsite; i++) {
787 int j;
789 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) {
790 continue;
793 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
794 for (j = 0; j < count_site; j++) {
795 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss,
796 (struct sockaddr *)&ip_srv_site[j].ss)) {
797 break;
799 /* As the lists are sorted we can break early if nonsite > site. */
800 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
801 break;
804 if (j != i) {
805 continue;
808 /* Append to the string - inefficient but not done often. */
809 kdc_str = print_kdc_line(mem_ctx,
810 kdc_str,
811 &ip_srv_nonsite[i].ss,
812 NULL);
813 if (!kdc_str) {
814 SAFE_FREE(ip_srv_site);
815 SAFE_FREE(ip_srv_nonsite);
816 return NULL;
821 SAFE_FREE(ip_srv_site);
822 SAFE_FREE(ip_srv_nonsite);
824 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
825 kdc_str ));
827 return kdc_str;
830 /************************************************************************
831 Create a specific krb5.conf file in the private directory pointing
832 at a specific kdc for a realm. Keyed off domain name. Sets
833 KRB5_CONFIG environment variable to point to this file. Must be
834 run as root or will fail (which is a good thing :-).
835 ************************************************************************/
837 bool create_local_private_krb5_conf_for_domain(const char *realm,
838 const char *domain,
839 const char *sitename,
840 struct sockaddr_storage *pss,
841 const char *kdc_name)
843 char *dname;
844 char *tmpname = NULL;
845 char *fname = NULL;
846 char *file_contents = NULL;
847 char *kdc_ip_string = NULL;
848 size_t flen = 0;
849 ssize_t ret;
850 int fd;
851 char *realm_upper = NULL;
852 bool result = false;
854 if (!lp_create_krb5_conf()) {
855 return false;
858 dname = lock_path("smb_krb5");
859 if (!dname) {
860 return false;
862 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
863 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
864 "failed to create directory %s. Error was %s\n",
865 dname, strerror(errno) ));
866 goto done;
869 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
870 if (!tmpname) {
871 goto done;
874 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
875 if (!fname) {
876 goto done;
879 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
880 fname, realm, domain ));
882 realm_upper = talloc_strdup(fname, realm);
883 strupper_m(realm_upper);
885 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
886 if (!kdc_ip_string) {
887 goto done;
890 file_contents = talloc_asprintf(fname,
891 "[libdefaults]\n\tdefault_realm = %s\n"
892 "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
893 "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
894 "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
895 "[realms]\n\t%s = {\n"
896 "\t%s\t}\n",
897 realm_upper, realm_upper, kdc_ip_string);
899 if (!file_contents) {
900 goto done;
903 flen = strlen(file_contents);
905 fd = mkstemp(tmpname);
906 if (fd == -1) {
907 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
908 " for file %s. Errno %s\n",
909 tmpname, strerror(errno) ));
910 goto done;
913 if (fchmod(fd, 0644)==-1) {
914 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
915 " Errno %s\n",
916 tmpname, strerror(errno) ));
917 unlink(tmpname);
918 close(fd);
919 goto done;
922 ret = write(fd, file_contents, flen);
923 if (flen != ret) {
924 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
925 " returned %d (should be %u). Errno %s\n",
926 (int)ret, (unsigned int)flen, strerror(errno) ));
927 unlink(tmpname);
928 close(fd);
929 goto done;
931 if (close(fd)==-1) {
932 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
933 " Errno %s\n", strerror(errno) ));
934 unlink(tmpname);
935 goto done;
938 if (rename(tmpname, fname) == -1) {
939 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
940 "of %s to %s failed. Errno %s\n",
941 tmpname, fname, strerror(errno) ));
942 unlink(tmpname);
943 goto done;
946 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
947 "file %s with realm %s KDC list = %s\n",
948 fname, realm_upper, kdc_ip_string));
950 /* Set the environment variable to this file. */
951 setenv("KRB5_CONFIG", fname, 1);
953 result = true;
955 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
957 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
958 /* Insanity, sheer insanity..... */
960 if (strequal(realm, lp_realm())) {
961 char linkpath[PATH_MAX+1];
962 int lret;
964 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
965 if (lret != -1) {
966 linkpath[lret] = '\0';
969 if (lret != -1 || strcmp(linkpath, fname) == 0) {
970 /* Symlink already exists. */
971 goto done;
974 /* Try and replace with a symlink. */
975 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
976 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
977 if (errno != EEXIST) {
978 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
979 "of %s to %s failed. Errno %s\n",
980 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
981 goto done; /* Not a fatal error. */
984 /* Yes, this is a race conditon... too bad. */
985 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
986 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
987 "of %s to %s failed. Errno %s\n",
988 SYSTEM_KRB5_CONF_PATH, newpath,
989 strerror(errno) ));
990 goto done; /* Not a fatal error. */
993 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
994 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
995 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
996 fname, strerror(errno) ));
997 goto done; /* Not a fatal error. */
1001 #endif
1003 done:
1004 TALLOC_FREE(tmpname);
1005 TALLOC_FREE(dname);
1007 return result;
1009 #endif