s3-kerberos: only use krb5 headers where required.
[Samba.git] / source / libads / kerberos.c
blobf7091022e6955c3997c172440d4aa80361bf1dbc
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((char *)prompts[0].reply->data, (const char *)data,
51 prompts[0].reply->length-1);
52 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
53 } else {
54 prompts[0].reply->length = 0;
57 return 0;
60 static bool smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx,
61 DATA_BLOB *edata_blob,
62 KRB5_EDATA_NTSTATUS *edata)
64 bool ret = False;
65 prs_struct ps;
67 if (!mem_ctx || !edata_blob || !edata)
68 return False;
70 if (!prs_init(&ps, edata_blob->length, mem_ctx, UNMARSHALL))
71 return False;
73 if (!prs_copy_data_in(&ps, (char *)edata_blob->data, edata_blob->length))
74 goto out;
76 prs_set_offset(&ps, 0);
78 if (!prs_ntstatus("ntstatus", &ps, 1, &edata->ntstatus))
79 goto out;
81 if (!prs_uint32("unknown1", &ps, 1, &edata->unknown1))
82 goto out;
84 if (!prs_uint32("unknown2", &ps, 1, &edata->unknown2)) /* only seen 00000001 here */
85 goto out;
87 ret = True;
88 out:
89 prs_mem_free(&ps);
91 return ret;
94 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
95 NTSTATUS *nt_status)
97 DATA_BLOB edata;
98 DATA_BLOB unwrapped_edata;
99 TALLOC_CTX *mem_ctx;
100 KRB5_EDATA_NTSTATUS parsed_edata;
102 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
103 edata = data_blob(error->e_data->data, error->e_data->length);
104 #else
105 edata = data_blob(error->e_data.data, error->e_data.length);
106 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
108 #ifdef DEVELOPER
109 dump_data(10, edata.data, edata.length);
110 #endif /* DEVELOPER */
112 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
113 if (mem_ctx == NULL) {
114 data_blob_free(&edata);
115 return False;
118 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
119 data_blob_free(&edata);
120 TALLOC_FREE(mem_ctx);
121 return False;
124 data_blob_free(&edata);
126 if (!smb_krb5_err_io_nstatus(mem_ctx, &unwrapped_edata, &parsed_edata)) {
127 data_blob_free(&unwrapped_edata);
128 TALLOC_FREE(mem_ctx);
129 return False;
132 data_blob_free(&unwrapped_edata);
134 if (nt_status) {
135 *nt_status = parsed_edata.ntstatus;
138 TALLOC_FREE(mem_ctx);
140 return True;
143 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
144 krb5_get_init_creds_opt *opt,
145 NTSTATUS *nt_status)
147 bool ret = False;
148 krb5_error *error = NULL;
150 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
151 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
152 if (ret) {
153 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
154 error_message(ret)));
155 return False;
157 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
159 if (!error) {
160 DEBUG(1,("no krb5_error\n"));
161 return False;
164 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
165 if (!error->e_data) {
166 #else
167 if (error->e_data.data == NULL) {
168 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
169 DEBUG(1,("no edata in krb5_error\n"));
170 krb5_free_error(ctx, error);
171 return False;
174 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
176 krb5_free_error(ctx, error);
178 return ret;
182 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
183 place in default cache location.
184 remus@snapserver.com
186 int kerberos_kinit_password_ext(const char *principal,
187 const char *password,
188 int time_offset,
189 time_t *expire_time,
190 time_t *renew_till_time,
191 const char *cache_name,
192 bool request_pac,
193 bool add_netbios_addr,
194 time_t renewable_time,
195 NTSTATUS *ntstatus)
197 krb5_context ctx = NULL;
198 krb5_error_code code = 0;
199 krb5_ccache cc = NULL;
200 krb5_principal me = NULL;
201 krb5_creds my_creds;
202 krb5_get_init_creds_opt *opt = NULL;
203 smb_krb5_addresses *addr = NULL;
205 ZERO_STRUCT(my_creds);
207 initialize_krb5_error_table();
208 if ((code = krb5_init_context(&ctx)))
209 goto out;
211 if (time_offset != 0) {
212 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
215 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
216 principal,
217 cache_name ? cache_name: krb5_cc_default_name(ctx),
218 getenv("KRB5_CONFIG")));
220 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
221 goto out;
224 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
225 goto out;
228 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
229 goto out;
232 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
233 krb5_get_init_creds_opt_set_forwardable(opt, True);
234 #if 0
235 /* insane testing */
236 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
237 #endif
239 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
240 if (request_pac) {
241 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
242 goto out;
245 #endif
246 if (add_netbios_addr) {
247 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
248 goto out;
250 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
253 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
254 kerb_prompter, CONST_DISCARD(char *,password),
255 0, NULL, opt))) {
256 goto out;
259 if ((code = krb5_cc_initialize(ctx, cc, me))) {
260 goto out;
263 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
264 goto out;
267 if (expire_time) {
268 *expire_time = (time_t) my_creds.times.endtime;
271 if (renew_till_time) {
272 *renew_till_time = (time_t) my_creds.times.renew_till;
274 out:
275 if (ntstatus) {
277 NTSTATUS status;
279 /* fast path */
280 if (code == 0) {
281 *ntstatus = NT_STATUS_OK;
282 goto cleanup;
285 /* try to get ntstatus code out of krb5_error when we have it
286 * inside the krb5_get_init_creds_opt - gd */
288 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
289 *ntstatus = status;
290 goto cleanup;
293 /* fall back to self-made-mapping */
294 *ntstatus = krb5_to_nt_status(code);
297 cleanup:
298 krb5_free_cred_contents(ctx, &my_creds);
299 if (me) {
300 krb5_free_principal(ctx, me);
302 if (addr) {
303 smb_krb5_free_addresses(ctx, addr);
305 if (opt) {
306 smb_krb5_get_init_creds_opt_free(ctx, opt);
308 if (cc) {
309 krb5_cc_close(ctx, cc);
311 if (ctx) {
312 krb5_free_context(ctx);
314 return code;
319 /* run kinit to setup our ccache */
320 int ads_kinit_password(ADS_STRUCT *ads)
322 char *s;
323 int ret;
324 const char *account_name;
325 fstring acct_name;
327 if (ads->auth.flags & ADS_AUTH_USER_CREDS) {
328 account_name = ads->auth.user_name;
329 goto got_accountname;
332 if ( IS_DC ) {
333 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
334 account_name = lp_workgroup();
335 } else {
336 /* always use the sAMAccountName for security = domain */
337 /* global_myname()$@REA.LM */
338 if ( lp_security() == SEC_DOMAIN ) {
339 fstr_sprintf( acct_name, "%s$", global_myname() );
340 account_name = acct_name;
342 else
343 /* This looks like host/global_myname()@REA.LM */
344 account_name = ads->auth.user_name;
347 got_accountname:
348 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
349 return KRB5_CC_NOMEM;
352 if (!ads->auth.password) {
353 SAFE_FREE(s);
354 return KRB5_LIBOS_CANTREADPWD;
357 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
358 &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable,
359 NULL);
361 if (ret) {
362 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
363 s, error_message(ret)));
365 SAFE_FREE(s);
366 return ret;
369 int ads_kdestroy(const char *cc_name)
371 krb5_error_code code;
372 krb5_context ctx = NULL;
373 krb5_ccache cc = NULL;
375 initialize_krb5_error_table();
376 if ((code = krb5_init_context (&ctx))) {
377 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
378 error_message(code)));
379 return code;
382 if (!cc_name) {
383 if ((code = krb5_cc_default(ctx, &cc))) {
384 krb5_free_context(ctx);
385 return code;
387 } else {
388 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
389 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
390 error_message(code)));
391 krb5_free_context(ctx);
392 return code;
396 if ((code = krb5_cc_destroy (ctx, cc))) {
397 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
398 error_message(code)));
401 krb5_free_context (ctx);
402 return code;
405 /************************************************************************
406 Routine to fetch the salting principal for a service. Active
407 Directory may use a non-obvious principal name to generate the salt
408 when it determines the key to use for encrypting tickets for a service,
409 and hopefully we detected that when we joined the domain.
410 ************************************************************************/
412 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
414 char *key = NULL;
415 char *ret = NULL;
417 if (asprintf(&key, "%s/%s/enctype=%d",
418 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
419 return NULL;
421 ret = (char *)secrets_fetch(key, NULL);
422 SAFE_FREE(key);
423 return ret;
426 /************************************************************************
427 Return the standard DES salt key
428 ************************************************************************/
430 char* kerberos_standard_des_salt( void )
432 fstring salt;
434 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
435 strlower_m( salt );
436 fstrcat( salt, lp_realm() );
438 return SMB_STRDUP( salt );
441 /************************************************************************
442 ************************************************************************/
444 static char* des_salt_key( void )
446 char *key;
448 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
449 lp_realm()) == -1) {
450 return NULL;
453 return key;
456 /************************************************************************
457 ************************************************************************/
459 bool kerberos_secrets_store_des_salt( const char* salt )
461 char* key;
462 bool ret;
464 if ( (key = des_salt_key()) == NULL ) {
465 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
466 return False;
469 if ( !salt ) {
470 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
471 secrets_delete( key );
472 return True;
475 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
477 ret = secrets_store( key, salt, strlen(salt)+1 );
479 SAFE_FREE( key );
481 return ret;
484 /************************************************************************
485 ************************************************************************/
487 char* kerberos_secrets_fetch_des_salt( void )
489 char *salt, *key;
491 if ( (key = des_salt_key()) == NULL ) {
492 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
493 return False;
496 salt = (char*)secrets_fetch( key, NULL );
498 SAFE_FREE( key );
500 return salt;
503 /************************************************************************
504 Routine to get the default realm from the kerberos credentials cache.
505 Caller must free if the return value is not NULL.
506 ************************************************************************/
508 char *kerberos_get_default_realm_from_ccache( void )
510 char *realm = NULL;
511 krb5_context ctx = NULL;
512 krb5_ccache cc = NULL;
513 krb5_principal princ = NULL;
515 initialize_krb5_error_table();
516 if (krb5_init_context(&ctx)) {
517 return NULL;
520 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
521 "Trying to read krb5 cache: %s\n",
522 krb5_cc_default_name(ctx)));
523 if (krb5_cc_default(ctx, &cc)) {
524 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
525 "failed to read default cache\n"));
526 goto out;
528 if (krb5_cc_get_principal(ctx, cc, &princ)) {
529 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
530 "failed to get default principal\n"));
531 goto out;
534 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
535 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
536 #elif defined(HAVE_KRB5_PRINC_REALM)
538 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
539 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
541 #endif
543 out:
545 if (ctx) {
546 if (princ) {
547 krb5_free_principal(ctx, princ);
549 if (cc) {
550 krb5_cc_close(ctx, cc);
552 krb5_free_context(ctx);
555 return realm;
559 /************************************************************************
560 Routine to get the salting principal for this service. This is
561 maintained for backwards compatibilty with releases prior to 3.0.24.
562 Since we store the salting principal string only at join, we may have
563 to look for the older tdb keys. Caller must free if return is not null.
564 ************************************************************************/
566 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
567 krb5_principal host_princ,
568 int enctype)
570 char *unparsed_name = NULL, *salt_princ_s = NULL;
571 krb5_principal ret_princ = NULL;
573 /* lookup new key first */
575 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
577 /* look under the old key. If this fails, just use the standard key */
579 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
580 return (krb5_principal)NULL;
582 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
583 /* fall back to host/machine.realm@REALM */
584 salt_princ_s = kerberos_standard_des_salt();
588 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
589 ret_princ = NULL;
592 SAFE_FREE(unparsed_name);
593 SAFE_FREE(salt_princ_s);
595 return ret_princ;
598 /************************************************************************
599 Routine to set the salting principal for this service. Active
600 Directory may use a non-obvious principal name to generate the salt
601 when it determines the key to use for encrypting tickets for a service,
602 and hopefully we detected that when we joined the domain.
603 Setting principal to NULL deletes this entry.
604 ************************************************************************/
606 bool kerberos_secrets_store_salting_principal(const char *service,
607 int enctype,
608 const char *principal)
610 char *key = NULL;
611 bool ret = False;
612 krb5_context context = NULL;
613 krb5_principal princ = NULL;
614 char *princ_s = NULL;
615 char *unparsed_name = NULL;
616 krb5_error_code code;
618 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
619 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
620 error_message(code)));
621 return False;
623 if (strchr_m(service, '@')) {
624 if (asprintf(&princ_s, "%s", service) == -1) {
625 goto out;
627 } else {
628 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
629 goto out;
633 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
634 goto out;
637 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
638 goto out;
641 if (asprintf(&key, "%s/%s/enctype=%d",
642 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
643 == -1) {
644 goto out;
647 if ((principal != NULL) && (strlen(principal) > 0)) {
648 ret = secrets_store(key, principal, strlen(principal) + 1);
649 } else {
650 ret = secrets_delete(key);
653 out:
655 SAFE_FREE(key);
656 SAFE_FREE(princ_s);
657 SAFE_FREE(unparsed_name);
659 if (princ) {
660 krb5_free_principal(context, princ);
663 if (context) {
664 krb5_free_context(context);
667 return ret;
671 /************************************************************************
672 ************************************************************************/
674 int kerberos_kinit_password(const char *principal,
675 const char *password,
676 int time_offset,
677 const char *cache_name)
679 return kerberos_kinit_password_ext(principal,
680 password,
681 time_offset,
684 cache_name,
685 False,
686 False,
688 NULL);
691 /************************************************************************
692 ************************************************************************/
694 static char *print_kdc_line(char *mem_ctx,
695 const char *prev_line,
696 const struct sockaddr_storage *pss)
698 char *kdc_str = NULL;
700 if (pss->ss_family == AF_INET) {
701 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
702 prev_line,
703 print_canonical_sockaddr(mem_ctx, pss));
704 } else {
705 char addr[INET6_ADDRSTRLEN];
706 uint16_t port = get_sockaddr_port(pss);
708 if (port != 0 && port != DEFAULT_KRB5_PORT) {
709 /* Currently for IPv6 we can't specify a non-default
710 krb5 port with an address, as this requires a ':'.
711 Resolve to a name. */
712 char hostname[MAX_DNS_NAME_LENGTH];
713 int ret = sys_getnameinfo((const struct sockaddr *)pss,
714 sizeof(*pss),
715 hostname, sizeof(hostname),
716 NULL, 0,
717 NI_NAMEREQD);
718 if (ret) {
719 DEBUG(0,("print_kdc_line: can't resolve name "
720 "for kdc with non-default port %s. "
721 "Error %s\n.",
722 print_canonical_sockaddr(mem_ctx, pss),
723 gai_strerror(ret)));
725 /* Success, use host:port */
726 kdc_str = talloc_asprintf(mem_ctx,
727 "%s\tkdc = %s:%u\n",
728 prev_line,
729 hostname,
730 (unsigned int)port);
731 } else {
732 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
733 prev_line,
734 print_sockaddr(addr,
735 sizeof(addr),
736 pss));
739 return kdc_str;
742 /************************************************************************
743 Create a string list of available kdc's, possibly searching by sitename.
744 Does DNS queries.
746 If "sitename" is given, the DC's in that site are listed first.
748 ************************************************************************/
750 static char *get_kdc_ip_string(char *mem_ctx,
751 const char *realm,
752 const char *sitename,
753 struct sockaddr_storage *pss)
755 int i;
756 struct ip_service *ip_srv_site = NULL;
757 struct ip_service *ip_srv_nonsite = NULL;
758 int count_site = 0;
759 int count_nonsite;
760 char *kdc_str = print_kdc_line(mem_ctx, "", pss);
762 if (kdc_str == NULL) {
763 return NULL;
767 * First get the KDC's only in this site, the rest will be
768 * appended later
771 if (sitename) {
773 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
775 for (i = 0; i < count_site; i++) {
776 if (sockaddr_equal(&ip_srv_site[i].ss, pss)) {
777 continue;
779 /* Append to the string - inefficient
780 * but not done often. */
781 kdc_str = print_kdc_line(mem_ctx,
782 kdc_str,
783 &ip_srv_site[i].ss);
784 if (!kdc_str) {
785 SAFE_FREE(ip_srv_site);
786 return NULL;
791 /* Get all KDC's. */
793 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
795 for (i = 0; i < count_nonsite; i++) {
796 int j;
798 if (sockaddr_equal(&ip_srv_nonsite[i].ss, pss)) {
799 continue;
802 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
803 for (j = 0; j < count_site; j++) {
804 if (sockaddr_equal(&ip_srv_nonsite[i].ss,
805 &ip_srv_site[j].ss)) {
806 break;
808 /* As the lists are sorted we can break early if nonsite > site. */
809 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
810 break;
813 if (j != i) {
814 continue;
817 /* Append to the string - inefficient but not done often. */
818 kdc_str = print_kdc_line(mem_ctx,
819 kdc_str,
820 &ip_srv_nonsite[i].ss);
821 if (!kdc_str) {
822 SAFE_FREE(ip_srv_site);
823 SAFE_FREE(ip_srv_nonsite);
824 return NULL;
829 SAFE_FREE(ip_srv_site);
830 SAFE_FREE(ip_srv_nonsite);
832 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
833 kdc_str ));
835 return kdc_str;
838 /************************************************************************
839 Create a specific krb5.conf file in the private directory pointing
840 at a specific kdc for a realm. Keyed off domain name. Sets
841 KRB5_CONFIG environment variable to point to this file. Must be
842 run as root or will fail (which is a good thing :-).
843 ************************************************************************/
845 bool create_local_private_krb5_conf_for_domain(const char *realm,
846 const char *domain,
847 const char *sitename,
848 struct sockaddr_storage *pss)
850 char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
851 char *tmpname = NULL;
852 char *fname = NULL;
853 char *file_contents = NULL;
854 char *kdc_ip_string = NULL;
855 size_t flen = 0;
856 ssize_t ret;
857 int fd;
858 char *realm_upper = NULL;
860 if (!dname) {
861 return False;
863 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
864 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
865 "failed to create directory %s. Error was %s\n",
866 dname, strerror(errno) ));
867 TALLOC_FREE(dname);
868 return False;
871 tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
872 if (!tmpname) {
873 TALLOC_FREE(dname);
874 return False;
877 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
878 if (!fname) {
879 TALLOC_FREE(dname);
880 return False;
883 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
884 fname, realm, domain ));
886 realm_upper = talloc_strdup(fname, realm);
887 strupper_m(realm_upper);
889 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
890 if (!kdc_ip_string) {
891 TALLOC_FREE(dname);
892 return False;
895 file_contents = talloc_asprintf(fname,
896 "[libdefaults]\n\tdefault_realm = %s\n"
897 "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
898 "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
899 "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
900 "[realms]\n\t%s = {\n"
901 "\t%s\t}\n",
902 realm_upper, realm_upper, kdc_ip_string);
904 if (!file_contents) {
905 TALLOC_FREE(dname);
906 return False;
909 flen = strlen(file_contents);
911 fd = smb_mkstemp(tmpname);
912 if (fd == -1) {
913 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
914 " for file %s. Errno %s\n",
915 tmpname, strerror(errno) ));
916 TALLOC_FREE(dname);
917 return false;
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 TALLOC_FREE(dname);
927 return False;
930 ret = write(fd, file_contents, flen);
931 if (flen != ret) {
932 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
933 " returned %d (should be %u). Errno %s\n",
934 (int)ret, (unsigned int)flen, strerror(errno) ));
935 unlink(tmpname);
936 close(fd);
937 TALLOC_FREE(dname);
938 return False;
940 if (close(fd)==-1) {
941 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
942 " Errno %s\n", strerror(errno) ));
943 unlink(tmpname);
944 TALLOC_FREE(dname);
945 return False;
948 if (rename(tmpname, fname) == -1) {
949 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
950 "of %s to %s failed. Errno %s\n",
951 tmpname, fname, strerror(errno) ));
952 unlink(tmpname);
953 TALLOC_FREE(dname);
954 return False;
957 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
958 "file %s with realm %s KDC list = %s\n",
959 fname, realm_upper, kdc_ip_string));
961 /* Set the environment variable to this file. */
962 setenv("KRB5_CONFIG", fname, 1);
964 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
966 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
967 /* Insanity, sheer insanity..... */
969 if (strequal(realm, lp_realm())) {
970 char linkpath[PATH_MAX+1];
971 int lret;
973 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
974 if (lret != -1) {
975 linkpath[lret] = '\0';
978 if (lret != -1 || strcmp(linkpath, fname) == 0) {
979 /* Symlink already exists. */
980 TALLOC_FREE(dname);
981 return True;
984 /* Try and replace with a symlink. */
985 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
986 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
987 if (errno != EEXIST) {
988 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
989 "of %s to %s failed. Errno %s\n",
990 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
991 TALLOC_FREE(dname);
992 return True; /* Not a fatal error. */
995 /* Yes, this is a race conditon... too bad. */
996 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
997 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
998 "of %s to %s failed. Errno %s\n",
999 SYSTEM_KRB5_CONF_PATH, newpath,
1000 strerror(errno) ));
1001 TALLOC_FREE(dname);
1002 return True; /* Not a fatal error. */
1005 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1006 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1007 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1008 fname, strerror(errno) ));
1009 TALLOC_FREE(dname);
1010 return True; /* Not a fatal error. */
1014 #endif
1016 TALLOC_FREE(dname);
1018 return True;
1020 #endif