clikrb5: Move pure krb wrapper functions from libads to clikrb5.
[Samba/id10ts.git] / source3 / libads / kerberos.c
blobf1df31ca4fa438236e48cb959fa7d924867c4916
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 DEFAULT_KRB5_PORT 88
37 #define LIBADS_CCACHE_NAME "MEMORY:libads"
40 we use a prompter to avoid a crash bug in the kerberos libs when
41 dealing with empty passwords
42 this prompter is just a string copy ...
44 static krb5_error_code
45 kerb_prompter(krb5_context ctx, void *data,
46 const char *name,
47 const char *banner,
48 int num_prompts,
49 krb5_prompt prompts[])
51 if (num_prompts == 0) return 0;
53 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
54 if (prompts[0].reply->length > 0) {
55 if (data) {
56 strncpy((char *)prompts[0].reply->data, (const char *)data,
57 prompts[0].reply->length-1);
58 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
59 } else {
60 prompts[0].reply->length = 0;
63 return 0;
66 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
67 NTSTATUS *nt_status)
69 DATA_BLOB edata;
70 DATA_BLOB unwrapped_edata;
71 TALLOC_CTX *mem_ctx;
72 struct KRB5_EDATA_NTSTATUS parsed_edata;
73 enum ndr_err_code ndr_err;
75 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
76 edata = data_blob(error->e_data->data, error->e_data->length);
77 #else
78 edata = data_blob(error->e_data.data, error->e_data.length);
79 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
81 #ifdef DEVELOPER
82 dump_data(10, edata.data, edata.length);
83 #endif /* DEVELOPER */
85 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
86 if (mem_ctx == NULL) {
87 data_blob_free(&edata);
88 return False;
91 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
92 data_blob_free(&edata);
93 TALLOC_FREE(mem_ctx);
94 return False;
97 data_blob_free(&edata);
99 ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx,
100 &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
101 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
102 data_blob_free(&unwrapped_edata);
103 TALLOC_FREE(mem_ctx);
104 return False;
107 data_blob_free(&unwrapped_edata);
109 if (nt_status) {
110 *nt_status = parsed_edata.ntstatus;
113 TALLOC_FREE(mem_ctx);
115 return True;
118 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
119 krb5_get_init_creds_opt *opt,
120 NTSTATUS *nt_status)
122 bool ret = False;
123 krb5_error *error = NULL;
125 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
126 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
127 if (ret) {
128 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
129 error_message(ret)));
130 return False;
132 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
134 if (!error) {
135 DEBUG(1,("no krb5_error\n"));
136 return False;
139 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
140 if (!error->e_data) {
141 #else
142 if (error->e_data.data == NULL) {
143 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
144 DEBUG(1,("no edata in krb5_error\n"));
145 krb5_free_error(ctx, error);
146 return False;
149 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
151 krb5_free_error(ctx, error);
153 return ret;
157 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
158 place in default cache location.
159 remus@snapserver.com
161 int kerberos_kinit_password_ext(const char *principal,
162 const char *password,
163 int time_offset,
164 time_t *expire_time,
165 time_t *renew_till_time,
166 const char *cache_name,
167 bool request_pac,
168 bool add_netbios_addr,
169 time_t renewable_time,
170 NTSTATUS *ntstatus)
172 krb5_context ctx = NULL;
173 krb5_error_code code = 0;
174 krb5_ccache cc = NULL;
175 krb5_principal me = NULL;
176 krb5_creds my_creds;
177 krb5_get_init_creds_opt *opt = NULL;
178 smb_krb5_addresses *addr = NULL;
180 ZERO_STRUCT(my_creds);
182 initialize_krb5_error_table();
183 if ((code = krb5_init_context(&ctx)))
184 goto out;
186 if (time_offset != 0) {
187 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
190 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
191 principal,
192 cache_name ? cache_name: krb5_cc_default_name(ctx),
193 getenv("KRB5_CONFIG")));
195 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
196 goto out;
199 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
200 goto out;
203 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
204 goto out;
207 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
208 krb5_get_init_creds_opt_set_forwardable(opt, True);
209 #if 0
210 /* insane testing */
211 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
212 #endif
214 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
215 if (request_pac) {
216 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
217 goto out;
220 #endif
221 if (add_netbios_addr) {
222 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
223 goto out;
225 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
228 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password),
229 kerb_prompter, discard_const_p(char, password),
230 0, NULL, opt))) {
231 goto out;
234 if ((code = krb5_cc_initialize(ctx, cc, me))) {
235 goto out;
238 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
239 goto out;
242 if (expire_time) {
243 *expire_time = (time_t) my_creds.times.endtime;
246 if (renew_till_time) {
247 *renew_till_time = (time_t) my_creds.times.renew_till;
249 out:
250 if (ntstatus) {
252 NTSTATUS status;
254 /* fast path */
255 if (code == 0) {
256 *ntstatus = NT_STATUS_OK;
257 goto cleanup;
260 /* try to get ntstatus code out of krb5_error when we have it
261 * inside the krb5_get_init_creds_opt - gd */
263 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
264 *ntstatus = status;
265 goto cleanup;
268 /* fall back to self-made-mapping */
269 *ntstatus = krb5_to_nt_status(code);
272 cleanup:
273 krb5_free_cred_contents(ctx, &my_creds);
274 if (me) {
275 krb5_free_principal(ctx, me);
277 if (addr) {
278 smb_krb5_free_addresses(ctx, addr);
280 if (opt) {
281 smb_krb5_get_init_creds_opt_free(ctx, opt);
283 if (cc) {
284 krb5_cc_close(ctx, cc);
286 if (ctx) {
287 krb5_free_context(ctx);
289 return code;
292 int ads_kdestroy(const char *cc_name)
294 krb5_error_code code;
295 krb5_context ctx = NULL;
296 krb5_ccache cc = NULL;
298 initialize_krb5_error_table();
299 if ((code = krb5_init_context (&ctx))) {
300 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
301 error_message(code)));
302 return code;
305 if (!cc_name) {
306 if ((code = krb5_cc_default(ctx, &cc))) {
307 krb5_free_context(ctx);
308 return code;
310 } else {
311 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
312 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
313 error_message(code)));
314 krb5_free_context(ctx);
315 return code;
319 if ((code = krb5_cc_destroy (ctx, cc))) {
320 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
321 error_message(code)));
324 krb5_free_context (ctx);
325 return code;
328 /************************************************************************
329 Routine to fetch the salting principal for a service. Active
330 Directory may use a non-obvious principal name to generate the salt
331 when it determines the key to use for encrypting tickets for a service,
332 and hopefully we detected that when we joined the domain.
333 ************************************************************************/
335 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
337 char *key = NULL;
338 char *ret = NULL;
340 if (asprintf(&key, "%s/%s/enctype=%d",
341 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
342 return NULL;
344 ret = (char *)secrets_fetch(key, NULL);
345 SAFE_FREE(key);
346 return ret;
349 /************************************************************************
350 Return the standard DES salt key
351 ************************************************************************/
353 char* kerberos_standard_des_salt( void )
355 fstring salt;
357 fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
358 strlower_m( salt );
359 fstrcat( salt, lp_realm() );
361 return SMB_STRDUP( salt );
364 /************************************************************************
365 ************************************************************************/
367 static char* des_salt_key( void )
369 char *key;
371 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
372 lp_realm()) == -1) {
373 return NULL;
376 return key;
379 /************************************************************************
380 ************************************************************************/
382 bool kerberos_secrets_store_des_salt( const char* salt )
384 char* key;
385 bool ret;
387 if ( (key = des_salt_key()) == NULL ) {
388 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
389 return False;
392 if ( !salt ) {
393 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
394 secrets_delete( key );
395 return True;
398 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
400 ret = secrets_store( key, salt, strlen(salt)+1 );
402 SAFE_FREE( key );
404 return ret;
407 /************************************************************************
408 ************************************************************************/
410 char* kerberos_secrets_fetch_des_salt( void )
412 char *salt, *key;
414 if ( (key = des_salt_key()) == NULL ) {
415 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
416 return NULL;
419 salt = (char*)secrets_fetch( key, NULL );
421 SAFE_FREE( key );
423 return salt;
426 /************************************************************************
427 Routine to get the salting principal for this service. This is
428 maintained for backwards compatibilty with releases prior to 3.0.24.
429 Since we store the salting principal string only at join, we may have
430 to look for the older tdb keys. Caller must free if return is not null.
431 ************************************************************************/
433 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
434 krb5_principal host_princ,
435 int enctype)
437 char *unparsed_name = NULL, *salt_princ_s = NULL;
438 krb5_principal ret_princ = NULL;
440 /* lookup new key first */
442 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
444 /* look under the old key. If this fails, just use the standard key */
446 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
447 return (krb5_principal)NULL;
449 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
450 /* fall back to host/machine.realm@REALM */
451 salt_princ_s = kerberos_standard_des_salt();
455 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
456 ret_princ = NULL;
459 TALLOC_FREE(unparsed_name);
460 SAFE_FREE(salt_princ_s);
462 return ret_princ;
465 /************************************************************************
466 Routine to set the salting principal for this service. Active
467 Directory may use a non-obvious principal name to generate the salt
468 when it determines the key to use for encrypting tickets for a service,
469 and hopefully we detected that when we joined the domain.
470 Setting principal to NULL deletes this entry.
471 ************************************************************************/
473 bool kerberos_secrets_store_salting_principal(const char *service,
474 int enctype,
475 const char *principal)
477 char *key = NULL;
478 bool ret = False;
479 krb5_context context = NULL;
480 krb5_principal princ = NULL;
481 char *princ_s = NULL;
482 char *unparsed_name = NULL;
483 krb5_error_code code;
485 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
486 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
487 error_message(code)));
488 return False;
490 if (strchr_m(service, '@')) {
491 if (asprintf(&princ_s, "%s", service) == -1) {
492 goto out;
494 } else {
495 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
496 goto out;
500 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
501 goto out;
503 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
504 goto out;
507 if (asprintf(&key, "%s/%s/enctype=%d",
508 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
509 == -1) {
510 goto out;
513 if ((principal != NULL) && (strlen(principal) > 0)) {
514 ret = secrets_store(key, principal, strlen(principal) + 1);
515 } else {
516 ret = secrets_delete(key);
519 out:
521 SAFE_FREE(key);
522 SAFE_FREE(princ_s);
523 TALLOC_FREE(unparsed_name);
525 if (princ) {
526 krb5_free_principal(context, princ);
529 if (context) {
530 krb5_free_context(context);
533 return ret;
537 /************************************************************************
538 ************************************************************************/
540 int kerberos_kinit_password(const char *principal,
541 const char *password,
542 int time_offset,
543 const char *cache_name)
545 return kerberos_kinit_password_ext(principal,
546 password,
547 time_offset,
550 cache_name,
551 False,
552 False,
554 NULL);
557 /************************************************************************
558 ************************************************************************/
560 static char *print_kdc_line(char *mem_ctx,
561 const char *prev_line,
562 const struct sockaddr_storage *pss,
563 const char *kdc_name)
565 char addr[INET6_ADDRSTRLEN];
566 uint16_t port = get_sockaddr_port(pss);
568 if (pss->ss_family == AF_INET) {
569 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
570 prev_line,
571 print_canonical_sockaddr(mem_ctx, pss));
575 * IPv6 starts here
578 DEBUG(10, ("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
579 kdc_name, port));
581 if (port != 0 && port != DEFAULT_KRB5_PORT) {
582 /* Currently for IPv6 we can't specify a non-default
583 krb5 port with an address, as this requires a ':'.
584 Resolve to a name. */
585 char hostname[MAX_DNS_NAME_LENGTH];
586 int ret = sys_getnameinfo((const struct sockaddr *)pss,
587 sizeof(*pss),
588 hostname, sizeof(hostname),
589 NULL, 0,
590 NI_NAMEREQD);
591 if (ret) {
592 DEBUG(0,("print_kdc_line: can't resolve name "
593 "for kdc with non-default port %s. "
594 "Error %s\n.",
595 print_canonical_sockaddr(mem_ctx, pss),
596 gai_strerror(ret)));
597 return NULL;
599 /* Success, use host:port */
600 return talloc_asprintf(mem_ctx,
601 "%s\tkdc = %s:%u\n",
602 prev_line,
603 hostname,
604 (unsigned int)port);
607 /* no krb5 lib currently supports "kdc = ipv6 address"
608 * at all, so just fill in just the kdc_name if we have
609 * it and let the krb5 lib figure out the appropriate
610 * ipv6 address - gd */
612 if (kdc_name) {
613 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
614 prev_line, kdc_name);
617 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
618 prev_line,
619 print_sockaddr(addr,
620 sizeof(addr),
621 pss));
624 /************************************************************************
625 Create a string list of available kdc's, possibly searching by sitename.
626 Does DNS queries.
628 If "sitename" is given, the DC's in that site are listed first.
630 ************************************************************************/
632 static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
633 const struct sockaddr_storage *addr)
635 int i;
637 for (i=0; i<*num_addrs; i++) {
638 if (sockaddr_equal((const struct sockaddr *)&addrs[i],
639 (const struct sockaddr *)addr)) {
640 return;
643 addrs[i] = *addr;
644 *num_addrs += 1;
647 static char *get_kdc_ip_string(char *mem_ctx,
648 const char *realm,
649 const char *sitename,
650 const struct sockaddr_storage *pss,
651 const char *kdc_name)
653 TALLOC_CTX *frame = talloc_stackframe();
654 int i;
655 struct ip_service *ip_srv_site = NULL;
656 struct ip_service *ip_srv_nonsite = NULL;
657 int count_site = 0;
658 int count_nonsite;
659 int num_dcs;
660 struct sockaddr_storage *dc_addrs;
661 struct tsocket_address **dc_addrs2 = NULL;
662 const struct tsocket_address * const *dc_addrs3 = NULL;
663 char *result = NULL;
664 struct netlogon_samlogon_response **responses = NULL;
665 NTSTATUS status;
666 char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
668 if (kdc_str == NULL) {
669 return NULL;
673 * First get the KDC's only in this site, the rest will be
674 * appended later
677 if (sitename) {
678 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
681 /* Get all KDC's. */
683 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
685 dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
686 1 + count_site + count_nonsite);
687 if (dc_addrs == NULL) {
688 goto fail;
691 dc_addrs[0] = *pss;
692 num_dcs = 1;
694 for (i=0; i<count_site; i++) {
695 add_sockaddr_unique(dc_addrs, &num_dcs, &ip_srv_site[i].ss);
698 for (i=0; i<count_nonsite; i++) {
699 add_sockaddr_unique(dc_addrs, &num_dcs, &ip_srv_nonsite[i].ss);
702 dc_addrs2 = talloc_zero_array(talloc_tos(),
703 struct tsocket_address *,
704 num_dcs);
705 if (dc_addrs2 == NULL) {
706 goto fail;
709 for (i=0; i<num_dcs; i++) {
710 char addr[INET6_ADDRSTRLEN];
711 int ret;
713 print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
715 ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
716 addr, LDAP_PORT,
717 &dc_addrs2[i]);
718 if (ret != 0) {
719 status = map_nt_error_from_unix(errno);
720 DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
721 addr, nt_errstr(status)));
722 goto fail;
726 dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
728 status = cldap_multi_netlogon(talloc_tos(),
729 dc_addrs3, num_dcs,
730 realm, lp_netbios_name(),
731 NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
732 MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
733 TALLOC_FREE(dc_addrs2);
734 dc_addrs3 = NULL;
736 if (!NT_STATUS_IS_OK(status)) {
737 DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
738 "%s\n", nt_errstr(status)));
739 goto fail;
742 kdc_str = talloc_strdup(mem_ctx, "");
743 if (kdc_str == NULL) {
744 goto fail;
747 for (i=0; i<num_dcs; i++) {
748 char *new_kdc_str;
750 if (responses[i] == NULL) {
751 continue;
754 /* Append to the string - inefficient but not done often. */
755 new_kdc_str = print_kdc_line(mem_ctx, kdc_str,
756 &dc_addrs[i],
757 kdc_name);
758 if (new_kdc_str == NULL) {
759 goto fail;
761 TALLOC_FREE(kdc_str);
762 kdc_str = new_kdc_str;
765 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
766 kdc_str ));
768 result = kdc_str;
769 fail:
770 SAFE_FREE(ip_srv_site);
771 SAFE_FREE(ip_srv_nonsite);
772 TALLOC_FREE(frame);
773 return result;
776 /************************************************************************
777 Create a specific krb5.conf file in the private directory pointing
778 at a specific kdc for a realm. Keyed off domain name. Sets
779 KRB5_CONFIG environment variable to point to this file. Must be
780 run as root or will fail (which is a good thing :-).
781 ************************************************************************/
783 bool create_local_private_krb5_conf_for_domain(const char *realm,
784 const char *domain,
785 const char *sitename,
786 const struct sockaddr_storage *pss,
787 const char *kdc_name)
789 char *dname;
790 char *tmpname = NULL;
791 char *fname = NULL;
792 char *file_contents = NULL;
793 char *kdc_ip_string = NULL;
794 size_t flen = 0;
795 ssize_t ret;
796 int fd;
797 char *realm_upper = NULL;
798 bool result = false;
800 if (!lp_create_krb5_conf()) {
801 return false;
804 dname = lock_path("smb_krb5");
805 if (!dname) {
806 return false;
808 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
809 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
810 "failed to create directory %s. Error was %s\n",
811 dname, strerror(errno) ));
812 goto done;
815 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
816 if (!tmpname) {
817 goto done;
820 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
821 if (!fname) {
822 goto done;
825 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
826 fname, realm, domain ));
828 realm_upper = talloc_strdup(fname, realm);
829 strupper_m(realm_upper);
831 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
832 if (!kdc_ip_string) {
833 goto done;
836 file_contents = talloc_asprintf(fname,
837 "[libdefaults]\n\tdefault_realm = %s\n"
838 "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
839 "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
840 "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
841 "[realms]\n\t%s = {\n"
842 "\t%s\t}\n",
843 realm_upper, realm_upper, kdc_ip_string);
845 if (!file_contents) {
846 goto done;
849 flen = strlen(file_contents);
851 fd = mkstemp(tmpname);
852 if (fd == -1) {
853 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
854 " for file %s. Errno %s\n",
855 tmpname, strerror(errno) ));
856 goto done;
859 if (fchmod(fd, 0644)==-1) {
860 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
861 " Errno %s\n",
862 tmpname, strerror(errno) ));
863 unlink(tmpname);
864 close(fd);
865 goto done;
868 ret = write(fd, file_contents, flen);
869 if (flen != ret) {
870 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
871 " returned %d (should be %u). Errno %s\n",
872 (int)ret, (unsigned int)flen, strerror(errno) ));
873 unlink(tmpname);
874 close(fd);
875 goto done;
877 if (close(fd)==-1) {
878 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
879 " Errno %s\n", strerror(errno) ));
880 unlink(tmpname);
881 goto done;
884 if (rename(tmpname, fname) == -1) {
885 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
886 "of %s to %s failed. Errno %s\n",
887 tmpname, fname, strerror(errno) ));
888 unlink(tmpname);
889 goto done;
892 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
893 "file %s with realm %s KDC list = %s\n",
894 fname, realm_upper, kdc_ip_string));
896 /* Set the environment variable to this file. */
897 setenv("KRB5_CONFIG", fname, 1);
899 result = true;
901 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
903 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
904 /* Insanity, sheer insanity..... */
906 if (strequal(realm, lp_realm())) {
907 SMB_STRUCT_STAT sbuf;
909 if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
910 if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
911 int lret;
912 size_t alloc_size = sbuf.st_ex_size + 1;
913 char *linkpath = talloc_array(talloc_tos(), char,
914 alloc_size);
915 if (!linkpath) {
916 goto done;
918 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
919 alloc_size - 1);
920 if (lret == -1) {
921 TALLOC_FREE(linkpath);
922 goto done;
924 linkpath[lret] = '\0';
926 if (strcmp(linkpath, fname) == 0) {
927 /* Symlink already exists. */
928 TALLOC_FREE(linkpath);
929 goto done;
931 TALLOC_FREE(linkpath);
935 /* Try and replace with a symlink. */
936 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
937 const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
938 if (errno != EEXIST) {
939 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
940 "of %s to %s failed. Errno %s\n",
941 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
942 goto done; /* Not a fatal error. */
945 /* Yes, this is a race conditon... too bad. */
946 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
947 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
948 "of %s to %s failed. Errno %s\n",
949 SYSTEM_KRB5_CONF_PATH, newpath,
950 strerror(errno) ));
951 goto done; /* Not a fatal error. */
954 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
955 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
956 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
957 fname, strerror(errno) ));
958 goto done; /* Not a fatal error. */
962 #endif
964 done:
965 TALLOC_FREE(tmpname);
966 TALLOC_FREE(dname);
968 return result;
970 #endif