Make sure to set umask() before calling mkstemp().
[Samba/gbeck.git] / source3 / libads / kerberos.c
blob96d194dc3119a68dc0152d0a7097f66cf2b8f342
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 lp_netbios_name()))) {
224 goto out;
226 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
229 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password),
230 kerb_prompter, discard_const_p(char, password),
231 0, NULL, opt))) {
232 goto out;
235 if ((code = krb5_cc_initialize(ctx, cc, me))) {
236 goto out;
239 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
240 goto out;
243 if (expire_time) {
244 *expire_time = (time_t) my_creds.times.endtime;
247 if (renew_till_time) {
248 *renew_till_time = (time_t) my_creds.times.renew_till;
250 out:
251 if (ntstatus) {
253 NTSTATUS status;
255 /* fast path */
256 if (code == 0) {
257 *ntstatus = NT_STATUS_OK;
258 goto cleanup;
261 /* try to get ntstatus code out of krb5_error when we have it
262 * inside the krb5_get_init_creds_opt - gd */
264 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
265 *ntstatus = status;
266 goto cleanup;
269 /* fall back to self-made-mapping */
270 *ntstatus = krb5_to_nt_status(code);
273 cleanup:
274 krb5_free_cred_contents(ctx, &my_creds);
275 if (me) {
276 krb5_free_principal(ctx, me);
278 if (addr) {
279 smb_krb5_free_addresses(ctx, addr);
281 if (opt) {
282 smb_krb5_get_init_creds_opt_free(ctx, opt);
284 if (cc) {
285 krb5_cc_close(ctx, cc);
287 if (ctx) {
288 krb5_free_context(ctx);
290 return code;
293 int ads_kdestroy(const char *cc_name)
295 krb5_error_code code;
296 krb5_context ctx = NULL;
297 krb5_ccache cc = NULL;
299 initialize_krb5_error_table();
300 if ((code = krb5_init_context (&ctx))) {
301 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
302 error_message(code)));
303 return code;
306 if (!cc_name) {
307 if ((code = krb5_cc_default(ctx, &cc))) {
308 krb5_free_context(ctx);
309 return code;
311 } else {
312 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
313 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
314 error_message(code)));
315 krb5_free_context(ctx);
316 return code;
320 if ((code = krb5_cc_destroy (ctx, cc))) {
321 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
322 error_message(code)));
325 krb5_free_context (ctx);
326 return code;
329 /************************************************************************
330 Routine to fetch the salting principal for a service. Active
331 Directory may use a non-obvious principal name to generate the salt
332 when it determines the key to use for encrypting tickets for a service,
333 and hopefully we detected that when we joined the domain.
334 ************************************************************************/
336 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
338 char *key = NULL;
339 char *ret = NULL;
341 if (asprintf(&key, "%s/%s/enctype=%d",
342 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
343 return NULL;
345 ret = (char *)secrets_fetch(key, NULL);
346 SAFE_FREE(key);
347 return ret;
350 /************************************************************************
351 Return the standard DES salt key
352 ************************************************************************/
354 char* kerberos_standard_des_salt( void )
356 fstring salt;
358 fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
359 (void)strlower_m( salt );
360 fstrcat( salt, lp_realm() );
362 return SMB_STRDUP( salt );
365 /************************************************************************
366 ************************************************************************/
368 static char* des_salt_key( void )
370 char *key;
372 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
373 lp_realm()) == -1) {
374 return NULL;
377 return key;
380 /************************************************************************
381 ************************************************************************/
383 bool kerberos_secrets_store_des_salt( const char* salt )
385 char* key;
386 bool ret;
388 if ( (key = des_salt_key()) == NULL ) {
389 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
390 return False;
393 if ( !salt ) {
394 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
395 secrets_delete( key );
396 return True;
399 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
401 ret = secrets_store( key, salt, strlen(salt)+1 );
403 SAFE_FREE( key );
405 return ret;
408 /************************************************************************
409 ************************************************************************/
411 static
412 char* kerberos_secrets_fetch_des_salt( void )
414 char *salt, *key;
416 if ( (key = des_salt_key()) == NULL ) {
417 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
418 return NULL;
421 salt = (char*)secrets_fetch( key, NULL );
423 SAFE_FREE( key );
425 return salt;
428 /************************************************************************
429 Routine to get the salting principal for this service. This is
430 maintained for backwards compatibilty with releases prior to 3.0.24.
431 Since we store the salting principal string only at join, we may have
432 to look for the older tdb keys. Caller must free if return is not null.
433 ************************************************************************/
435 static
436 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
437 krb5_principal host_princ,
438 int enctype)
440 char *unparsed_name = NULL, *salt_princ_s = NULL;
441 krb5_principal ret_princ = NULL;
443 /* lookup new key first */
445 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
447 /* look under the old key. If this fails, just use the standard key */
449 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
450 return (krb5_principal)NULL;
452 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
453 /* fall back to host/machine.realm@REALM */
454 salt_princ_s = kerberos_standard_des_salt();
458 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
459 ret_princ = NULL;
462 TALLOC_FREE(unparsed_name);
463 SAFE_FREE(salt_princ_s);
465 return ret_princ;
468 int create_kerberos_key_from_string(krb5_context context,
469 krb5_principal host_princ,
470 krb5_data *password,
471 krb5_keyblock *key,
472 krb5_enctype enctype,
473 bool no_salt)
475 krb5_principal salt_princ = NULL;
476 int ret;
478 * Check if we've determined that the KDC is salting keys for this
479 * principal/enctype in a non-obvious way. If it is, try to match
480 * its behavior.
482 if (no_salt) {
483 KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
484 if (!KRB5_KEY_DATA(key)) {
485 return ENOMEM;
487 memcpy(KRB5_KEY_DATA(key), password->data, password->length);
488 KRB5_KEY_LENGTH(key) = password->length;
489 KRB5_KEY_TYPE(key) = enctype;
490 return 0;
492 salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
493 ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype);
494 if (salt_princ) {
495 krb5_free_principal(context, salt_princ);
497 return ret;
500 /************************************************************************
501 Routine to set the salting principal for this service. Active
502 Directory may use a non-obvious principal name to generate the salt
503 when it determines the key to use for encrypting tickets for a service,
504 and hopefully we detected that when we joined the domain.
505 Setting principal to NULL deletes this entry.
506 ************************************************************************/
508 bool kerberos_secrets_store_salting_principal(const char *service,
509 int enctype,
510 const char *principal)
512 char *key = NULL;
513 bool ret = False;
514 krb5_context context = NULL;
515 krb5_principal princ = NULL;
516 char *princ_s = NULL;
517 char *unparsed_name = NULL;
518 krb5_error_code code;
520 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
521 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
522 error_message(code)));
523 return False;
525 if (strchr_m(service, '@')) {
526 if (asprintf(&princ_s, "%s", service) == -1) {
527 goto out;
529 } else {
530 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
531 goto out;
535 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
536 goto out;
538 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
539 goto out;
542 if (asprintf(&key, "%s/%s/enctype=%d",
543 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
544 == -1) {
545 goto out;
548 if ((principal != NULL) && (strlen(principal) > 0)) {
549 ret = secrets_store(key, principal, strlen(principal) + 1);
550 } else {
551 ret = secrets_delete(key);
554 out:
556 SAFE_FREE(key);
557 SAFE_FREE(princ_s);
558 TALLOC_FREE(unparsed_name);
560 if (princ) {
561 krb5_free_principal(context, princ);
564 if (context) {
565 krb5_free_context(context);
568 return ret;
572 /************************************************************************
573 ************************************************************************/
575 int kerberos_kinit_password(const char *principal,
576 const char *password,
577 int time_offset,
578 const char *cache_name)
580 return kerberos_kinit_password_ext(principal,
581 password,
582 time_offset,
585 cache_name,
586 False,
587 False,
589 NULL);
592 /************************************************************************
593 ************************************************************************/
595 static char *print_kdc_line(char *mem_ctx,
596 const char *prev_line,
597 const struct sockaddr_storage *pss,
598 const char *kdc_name)
600 char addr[INET6_ADDRSTRLEN];
601 uint16_t port = get_sockaddr_port(pss);
603 if (pss->ss_family == AF_INET) {
604 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
605 prev_line,
606 print_canonical_sockaddr(mem_ctx, pss));
610 * IPv6 starts here
613 DEBUG(10, ("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
614 kdc_name, port));
616 if (port != 0 && port != DEFAULT_KRB5_PORT) {
617 /* Currently for IPv6 we can't specify a non-default
618 krb5 port with an address, as this requires a ':'.
619 Resolve to a name. */
620 char hostname[MAX_DNS_NAME_LENGTH];
621 int ret = sys_getnameinfo((const struct sockaddr *)pss,
622 sizeof(*pss),
623 hostname, sizeof(hostname),
624 NULL, 0,
625 NI_NAMEREQD);
626 if (ret) {
627 DEBUG(0,("print_kdc_line: can't resolve name "
628 "for kdc with non-default port %s. "
629 "Error %s\n.",
630 print_canonical_sockaddr(mem_ctx, pss),
631 gai_strerror(ret)));
632 return NULL;
634 /* Success, use host:port */
635 return talloc_asprintf(mem_ctx,
636 "%s\tkdc = %s:%u\n",
637 prev_line,
638 hostname,
639 (unsigned int)port);
642 /* no krb5 lib currently supports "kdc = ipv6 address"
643 * at all, so just fill in just the kdc_name if we have
644 * it and let the krb5 lib figure out the appropriate
645 * ipv6 address - gd */
647 if (kdc_name) {
648 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
649 prev_line, kdc_name);
652 return talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
653 prev_line,
654 print_sockaddr(addr,
655 sizeof(addr),
656 pss));
659 /************************************************************************
660 Create a string list of available kdc's, possibly searching by sitename.
661 Does DNS queries.
663 If "sitename" is given, the DC's in that site are listed first.
665 ************************************************************************/
667 static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
668 const struct sockaddr_storage *addr)
670 int i;
672 for (i=0; i<*num_addrs; i++) {
673 if (sockaddr_equal((const struct sockaddr *)&addrs[i],
674 (const struct sockaddr *)addr)) {
675 return;
678 addrs[i] = *addr;
679 *num_addrs += 1;
682 static char *get_kdc_ip_string(char *mem_ctx,
683 const char *realm,
684 const char *sitename,
685 const struct sockaddr_storage *pss,
686 const char *kdc_name)
688 TALLOC_CTX *frame = talloc_stackframe();
689 int i;
690 struct ip_service *ip_srv_site = NULL;
691 struct ip_service *ip_srv_nonsite = NULL;
692 int count_site = 0;
693 int count_nonsite;
694 int num_dcs;
695 struct sockaddr_storage *dc_addrs;
696 struct tsocket_address **dc_addrs2 = NULL;
697 const struct tsocket_address * const *dc_addrs3 = NULL;
698 char *result = NULL;
699 struct netlogon_samlogon_response **responses = NULL;
700 NTSTATUS status;
701 char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
703 if (kdc_str == NULL) {
704 TALLOC_FREE(frame);
705 return NULL;
709 * First get the KDC's only in this site, the rest will be
710 * appended later
713 if (sitename) {
714 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
717 /* Get all KDC's. */
719 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
721 dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
722 1 + count_site + count_nonsite);
723 if (dc_addrs == NULL) {
724 goto fail;
727 dc_addrs[0] = *pss;
728 num_dcs = 1;
730 for (i=0; i<count_site; i++) {
731 add_sockaddr_unique(dc_addrs, &num_dcs, &ip_srv_site[i].ss);
734 for (i=0; i<count_nonsite; i++) {
735 add_sockaddr_unique(dc_addrs, &num_dcs, &ip_srv_nonsite[i].ss);
738 dc_addrs2 = talloc_zero_array(talloc_tos(),
739 struct tsocket_address *,
740 num_dcs);
741 if (dc_addrs2 == NULL) {
742 goto fail;
745 for (i=0; i<num_dcs; i++) {
746 char addr[INET6_ADDRSTRLEN];
747 int ret;
749 print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
751 ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
752 addr, LDAP_PORT,
753 &dc_addrs2[i]);
754 if (ret != 0) {
755 status = map_nt_error_from_unix(errno);
756 DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
757 addr, nt_errstr(status)));
758 goto fail;
762 dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
764 status = cldap_multi_netlogon(talloc_tos(),
765 dc_addrs3, num_dcs,
766 realm, lp_netbios_name(),
767 NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
768 MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
769 TALLOC_FREE(dc_addrs2);
770 dc_addrs3 = NULL;
772 if (!NT_STATUS_IS_OK(status)) {
773 DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
774 "%s\n", nt_errstr(status)));
775 goto fail;
778 kdc_str = talloc_strdup(mem_ctx, "");
779 if (kdc_str == NULL) {
780 goto fail;
783 for (i=0; i<num_dcs; i++) {
784 char *new_kdc_str;
786 if (responses[i] == NULL) {
787 continue;
790 /* Append to the string - inefficient but not done often. */
791 new_kdc_str = print_kdc_line(mem_ctx, kdc_str,
792 &dc_addrs[i],
793 kdc_name);
794 if (new_kdc_str == NULL) {
795 goto fail;
797 TALLOC_FREE(kdc_str);
798 kdc_str = new_kdc_str;
801 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
802 kdc_str ));
804 result = kdc_str;
805 fail:
806 SAFE_FREE(ip_srv_site);
807 SAFE_FREE(ip_srv_nonsite);
808 TALLOC_FREE(frame);
809 return result;
812 /************************************************************************
813 Create a specific krb5.conf file in the private directory pointing
814 at a specific kdc for a realm. Keyed off domain name. Sets
815 KRB5_CONFIG environment variable to point to this file. Must be
816 run as root or will fail (which is a good thing :-).
817 ************************************************************************/
819 bool create_local_private_krb5_conf_for_domain(const char *realm,
820 const char *domain,
821 const char *sitename,
822 const struct sockaddr_storage *pss,
823 const char *kdc_name)
825 char *dname;
826 char *tmpname = NULL;
827 char *fname = NULL;
828 char *file_contents = NULL;
829 char *kdc_ip_string = NULL;
830 size_t flen = 0;
831 ssize_t ret;
832 int fd;
833 char *realm_upper = NULL;
834 bool result = false;
835 char *aes_enctypes = NULL;
836 mode_t mask;
838 if (!lp_create_krb5_conf()) {
839 return false;
842 dname = lock_path("smb_krb5");
843 if (!dname) {
844 return false;
846 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
847 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
848 "failed to create directory %s. Error was %s\n",
849 dname, strerror(errno) ));
850 goto done;
853 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
854 if (!tmpname) {
855 goto done;
858 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
859 if (!fname) {
860 goto done;
863 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
864 fname, realm, domain ));
866 realm_upper = talloc_strdup(fname, realm);
867 if (!strupper_m(realm_upper)) {
868 goto done;
871 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
872 if (!kdc_ip_string) {
873 goto done;
876 aes_enctypes = talloc_strdup(fname, "");
877 if (aes_enctypes == NULL) {
878 goto done;
881 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
882 aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
883 if (aes_enctypes == NULL) {
884 goto done;
886 #endif
887 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
888 aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
889 if (aes_enctypes == NULL) {
890 goto done;
892 #endif
894 file_contents = talloc_asprintf(fname,
895 "[libdefaults]\n\tdefault_realm = %s\n"
896 "\tdefault_tgs_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
897 "\tdefault_tkt_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
898 "\tpreferred_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
899 "[realms]\n\t%s = {\n"
900 "\t%s\t}\n",
901 realm_upper, aes_enctypes, aes_enctypes, aes_enctypes,
902 realm_upper, kdc_ip_string);
904 if (!file_contents) {
905 goto done;
908 flen = strlen(file_contents);
910 mask = umask(S_IRWXO | S_IRWXG);
911 fd = mkstemp(tmpname);
912 umask(mask);
913 if (fd == -1) {
914 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
915 " for file %s. Errno %s\n",
916 tmpname, strerror(errno) ));
917 goto done;
920 if (fchmod(fd, 0644)==-1) {
921 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
922 " Errno %s\n",
923 tmpname, strerror(errno) ));
924 unlink(tmpname);
925 close(fd);
926 goto done;
929 ret = write(fd, file_contents, flen);
930 if (flen != ret) {
931 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
932 " returned %d (should be %u). Errno %s\n",
933 (int)ret, (unsigned int)flen, strerror(errno) ));
934 unlink(tmpname);
935 close(fd);
936 goto done;
938 if (close(fd)==-1) {
939 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
940 " Errno %s\n", strerror(errno) ));
941 unlink(tmpname);
942 goto done;
945 if (rename(tmpname, fname) == -1) {
946 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
947 "of %s to %s failed. Errno %s\n",
948 tmpname, fname, strerror(errno) ));
949 unlink(tmpname);
950 goto done;
953 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
954 "file %s with realm %s KDC list = %s\n",
955 fname, realm_upper, kdc_ip_string));
957 /* Set the environment variable to this file. */
958 setenv("KRB5_CONFIG", fname, 1);
960 result = true;
962 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
964 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
965 /* Insanity, sheer insanity..... */
967 if (strequal(realm, lp_realm())) {
968 SMB_STRUCT_STAT sbuf;
970 if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
971 if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
972 int lret;
973 size_t alloc_size = sbuf.st_ex_size + 1;
974 char *linkpath = talloc_array(talloc_tos(), char,
975 alloc_size);
976 if (!linkpath) {
977 goto done;
979 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
980 alloc_size - 1);
981 if (lret == -1) {
982 TALLOC_FREE(linkpath);
983 goto done;
985 linkpath[lret] = '\0';
987 if (strcmp(linkpath, fname) == 0) {
988 /* Symlink already exists. */
989 TALLOC_FREE(linkpath);
990 goto done;
992 TALLOC_FREE(linkpath);
996 /* Try and replace with a symlink. */
997 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
998 const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
999 if (errno != EEXIST) {
1000 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
1001 "of %s to %s failed. Errno %s\n",
1002 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
1003 goto done; /* Not a fatal error. */
1006 /* Yes, this is a race conditon... too bad. */
1007 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
1008 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
1009 "of %s to %s failed. Errno %s\n",
1010 SYSTEM_KRB5_CONF_PATH, newpath,
1011 strerror(errno) ));
1012 goto done; /* Not a fatal error. */
1015 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1016 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1017 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1018 fname, strerror(errno) ));
1019 goto done; /* Not a fatal error. */
1023 #endif
1025 done:
1026 TALLOC_FREE(tmpname);
1027 TALLOC_FREE(dname);
1029 return result;
1031 #endif