adt_tree: change pathtree_add to return WERR instead of bool.
[Samba.git] / source / libads / kerberos.c
blob66f203b12d3d98d75ecf8c68ea7fbcfe778b6b67
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"
26 #ifdef HAVE_KRB5
28 #define DEFAULT_KRB5_PORT 88
30 #define LIBADS_CCACHE_NAME "MEMORY:libads"
33 we use a prompter to avoid a crash bug in the kerberos libs when
34 dealing with empty passwords
35 this prompter is just a string copy ...
37 static krb5_error_code
38 kerb_prompter(krb5_context ctx, void *data,
39 const char *name,
40 const char *banner,
41 int num_prompts,
42 krb5_prompt prompts[])
44 if (num_prompts == 0) return 0;
46 memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
47 if (prompts[0].reply->length > 0) {
48 if (data) {
49 strncpy(prompts[0].reply->data, (const char *)data,
50 prompts[0].reply->length-1);
51 prompts[0].reply->length = strlen(prompts[0].reply->data);
52 } else {
53 prompts[0].reply->length = 0;
56 return 0;
59 static bool smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx,
60 DATA_BLOB *edata_blob,
61 KRB5_EDATA_NTSTATUS *edata)
63 bool ret = False;
64 prs_struct ps;
66 if (!mem_ctx || !edata_blob || !edata)
67 return False;
69 if (!prs_init(&ps, edata_blob->length, mem_ctx, UNMARSHALL))
70 return False;
72 if (!prs_copy_data_in(&ps, (char *)edata_blob->data, edata_blob->length))
73 goto out;
75 prs_set_offset(&ps, 0);
77 if (!prs_ntstatus("ntstatus", &ps, 1, &edata->ntstatus))
78 goto out;
80 if (!prs_uint32("unknown1", &ps, 1, &edata->unknown1))
81 goto out;
83 if (!prs_uint32("unknown2", &ps, 1, &edata->unknown2)) /* only seen 00000001 here */
84 goto out;
86 ret = True;
87 out:
88 prs_mem_free(&ps);
90 return ret;
93 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
94 NTSTATUS *nt_status)
96 DATA_BLOB edata;
97 DATA_BLOB unwrapped_edata;
98 TALLOC_CTX *mem_ctx;
99 KRB5_EDATA_NTSTATUS parsed_edata;
101 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
102 edata = data_blob(error->e_data->data, error->e_data->length);
103 #else
104 edata = data_blob(error->e_data.data, error->e_data.length);
105 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
107 #ifdef DEVELOPER
108 dump_data(10, edata.data, edata.length);
109 #endif /* DEVELOPER */
111 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
112 if (mem_ctx == NULL) {
113 data_blob_free(&edata);
114 return False;
117 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
118 data_blob_free(&edata);
119 TALLOC_FREE(mem_ctx);
120 return False;
123 data_blob_free(&edata);
125 if (!smb_krb5_err_io_nstatus(mem_ctx, &unwrapped_edata, &parsed_edata)) {
126 data_blob_free(&unwrapped_edata);
127 TALLOC_FREE(mem_ctx);
128 return False;
131 data_blob_free(&unwrapped_edata);
133 if (nt_status) {
134 *nt_status = parsed_edata.ntstatus;
137 TALLOC_FREE(mem_ctx);
139 return True;
142 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
143 krb5_get_init_creds_opt *opt,
144 NTSTATUS *nt_status)
146 bool ret = False;
147 krb5_error *error = NULL;
149 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
150 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
151 if (ret) {
152 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
153 error_message(ret)));
154 return False;
156 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
158 if (!error) {
159 DEBUG(1,("no krb5_error\n"));
160 return False;
163 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
164 if (!error->e_data) {
165 #else
166 if (error->e_data.data == NULL) {
167 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
168 DEBUG(1,("no edata in krb5_error\n"));
169 krb5_free_error(ctx, error);
170 return False;
173 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
175 krb5_free_error(ctx, error);
177 return ret;
181 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
182 place in default cache location.
183 remus@snapserver.com
185 int kerberos_kinit_password_ext(const char *principal,
186 const char *password,
187 int time_offset,
188 time_t *expire_time,
189 time_t *renew_till_time,
190 const char *cache_name,
191 bool request_pac,
192 bool add_netbios_addr,
193 time_t renewable_time,
194 NTSTATUS *ntstatus)
196 krb5_context ctx = NULL;
197 krb5_error_code code = 0;
198 krb5_ccache cc = NULL;
199 krb5_principal me = NULL;
200 krb5_creds my_creds;
201 krb5_get_init_creds_opt *opt = NULL;
202 smb_krb5_addresses *addr = NULL;
204 ZERO_STRUCT(my_creds);
206 initialize_krb5_error_table();
207 if ((code = krb5_init_context(&ctx)))
208 goto out;
210 if (time_offset != 0) {
211 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
214 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
215 principal,
216 cache_name ? cache_name: krb5_cc_default_name(ctx),
217 getenv("KRB5_CONFIG")));
219 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
220 goto out;
223 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
224 goto out;
227 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
228 goto out;
231 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
232 krb5_get_init_creds_opt_set_forwardable(opt, True);
233 #if 0
234 /* insane testing */
235 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
236 #endif
238 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
239 if (request_pac) {
240 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
241 goto out;
244 #endif
245 if (add_netbios_addr) {
246 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
247 goto out;
249 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
252 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
253 kerb_prompter, CONST_DISCARD(char *,password),
254 0, NULL, opt))) {
255 goto out;
258 if ((code = krb5_cc_initialize(ctx, cc, me))) {
259 goto out;
262 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
263 goto out;
266 if (expire_time) {
267 *expire_time = (time_t) my_creds.times.endtime;
270 if (renew_till_time) {
271 *renew_till_time = (time_t) my_creds.times.renew_till;
273 out:
274 if (ntstatus) {
276 NTSTATUS status;
278 /* fast path */
279 if (code == 0) {
280 *ntstatus = NT_STATUS_OK;
281 goto cleanup;
284 /* try to get ntstatus code out of krb5_error when we have it
285 * inside the krb5_get_init_creds_opt - gd */
287 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
288 *ntstatus = status;
289 goto cleanup;
292 /* fall back to self-made-mapping */
293 *ntstatus = krb5_to_nt_status(code);
296 cleanup:
297 krb5_free_cred_contents(ctx, &my_creds);
298 if (me) {
299 krb5_free_principal(ctx, me);
301 if (addr) {
302 smb_krb5_free_addresses(ctx, addr);
304 if (opt) {
305 smb_krb5_get_init_creds_opt_free(ctx, opt);
307 if (cc) {
308 krb5_cc_close(ctx, cc);
310 if (ctx) {
311 krb5_free_context(ctx);
313 return code;
318 /* run kinit to setup our ccache */
319 int ads_kinit_password(ADS_STRUCT *ads)
321 char *s;
322 int ret;
323 const char *account_name;
324 fstring acct_name;
326 if ( IS_DC ) {
327 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
328 account_name = lp_workgroup();
329 } else {
330 /* always use the sAMAccountName for security = domain */
331 /* global_myname()$@REA.LM */
332 if ( lp_security() == SEC_DOMAIN ) {
333 fstr_sprintf( acct_name, "%s$", global_myname() );
334 account_name = acct_name;
336 else
337 /* This looks like host/global_myname()@REA.LM */
338 account_name = ads->auth.user_name;
341 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
342 return KRB5_CC_NOMEM;
345 if (!ads->auth.password) {
346 SAFE_FREE(s);
347 return KRB5_LIBOS_CANTREADPWD;
350 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
351 &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable,
352 NULL);
354 if (ret) {
355 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
356 s, error_message(ret)));
358 SAFE_FREE(s);
359 return ret;
362 int ads_kdestroy(const char *cc_name)
364 krb5_error_code code;
365 krb5_context ctx = NULL;
366 krb5_ccache cc = NULL;
368 initialize_krb5_error_table();
369 if ((code = krb5_init_context (&ctx))) {
370 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
371 error_message(code)));
372 return code;
375 if (!cc_name) {
376 if ((code = krb5_cc_default(ctx, &cc))) {
377 krb5_free_context(ctx);
378 return code;
380 } else {
381 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
382 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
383 error_message(code)));
384 krb5_free_context(ctx);
385 return code;
389 if ((code = krb5_cc_destroy (ctx, cc))) {
390 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
391 error_message(code)));
394 krb5_free_context (ctx);
395 return code;
398 /************************************************************************
399 Routine to fetch the salting principal for a service. Active
400 Directory may use a non-obvious principal name to generate the salt
401 when it determines the key to use for encrypting tickets for a service,
402 and hopefully we detected that when we joined the domain.
403 ************************************************************************/
405 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
407 char *key = NULL;
408 char *ret = NULL;
410 if (asprintf(&key, "%s/%s/enctype=%d",
411 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
412 return NULL;
414 ret = (char *)secrets_fetch(key, NULL);
415 SAFE_FREE(key);
416 return ret;
419 /************************************************************************
420 Return the standard DES salt key
421 ************************************************************************/
423 char* kerberos_standard_des_salt( void )
425 fstring salt;
427 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
428 strlower_m( salt );
429 fstrcat( salt, lp_realm() );
431 return SMB_STRDUP( salt );
434 /************************************************************************
435 ************************************************************************/
437 static char* des_salt_key( void )
439 char *key;
441 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
442 lp_realm()) == -1) {
443 return NULL;
446 return key;
449 /************************************************************************
450 ************************************************************************/
452 bool kerberos_secrets_store_des_salt( const char* salt )
454 char* key;
455 bool ret;
457 if ( (key = des_salt_key()) == NULL ) {
458 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
459 return False;
462 if ( !salt ) {
463 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
464 secrets_delete( key );
465 return True;
468 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
470 ret = secrets_store( key, salt, strlen(salt)+1 );
472 SAFE_FREE( key );
474 return ret;
477 /************************************************************************
478 ************************************************************************/
480 char* kerberos_secrets_fetch_des_salt( void )
482 char *salt, *key;
484 if ( (key = des_salt_key()) == NULL ) {
485 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
486 return False;
489 salt = (char*)secrets_fetch( key, NULL );
491 SAFE_FREE( key );
493 return salt;
496 /************************************************************************
497 Routine to get the default realm from the kerberos credentials cache.
498 Caller must free if the return value is not NULL.
499 ************************************************************************/
501 char *kerberos_get_default_realm_from_ccache( void )
503 char *realm = NULL;
504 krb5_context ctx = NULL;
505 krb5_ccache cc = NULL;
506 krb5_principal princ = NULL;
508 initialize_krb5_error_table();
509 if (krb5_init_context(&ctx)) {
510 return NULL;
513 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
514 "Trying to read krb5 cache: %s\n",
515 krb5_cc_default_name(ctx)));
516 if (krb5_cc_default(ctx, &cc)) {
517 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
518 "failed to read default cache\n"));
519 goto out;
521 if (krb5_cc_get_principal(ctx, cc, &princ)) {
522 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
523 "failed to get default principal\n"));
524 goto out;
527 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
528 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
529 #elif defined(HAVE_KRB5_PRINC_REALM)
531 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
532 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
534 #endif
536 out:
538 if (princ) {
539 krb5_free_principal(ctx, princ);
541 if (cc) {
542 krb5_cc_close(ctx, cc);
544 if (ctx) {
545 krb5_free_context(ctx);
548 return realm;
552 /************************************************************************
553 Routine to get the salting principal for this service. This is
554 maintained for backwards compatibilty with releases prior to 3.0.24.
555 Since we store the salting principal string only at join, we may have
556 to look for the older tdb keys. Caller must free if return is not null.
557 ************************************************************************/
559 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
560 krb5_principal host_princ,
561 int enctype)
563 char *unparsed_name = NULL, *salt_princ_s = NULL;
564 krb5_principal ret_princ = NULL;
566 /* lookup new key first */
568 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
570 /* look under the old key. If this fails, just use the standard key */
572 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
573 return (krb5_principal)NULL;
575 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
576 /* fall back to host/machine.realm@REALM */
577 salt_princ_s = kerberos_standard_des_salt();
581 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
582 ret_princ = NULL;
585 SAFE_FREE(unparsed_name);
586 SAFE_FREE(salt_princ_s);
588 return ret_princ;
591 /************************************************************************
592 Routine to set the salting principal for this service. Active
593 Directory may use a non-obvious principal name to generate the salt
594 when it determines the key to use for encrypting tickets for a service,
595 and hopefully we detected that when we joined the domain.
596 Setting principal to NULL deletes this entry.
597 ************************************************************************/
599 bool kerberos_secrets_store_salting_principal(const char *service,
600 int enctype,
601 const char *principal)
603 char *key = NULL;
604 bool ret = False;
605 krb5_context context = NULL;
606 krb5_principal princ = NULL;
607 char *princ_s = NULL;
608 char *unparsed_name = NULL;
609 krb5_error_code code;
611 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
612 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
613 error_message(code)));
614 return False;
616 if (strchr_m(service, '@')) {
617 if (asprintf(&princ_s, "%s", service) == -1) {
618 goto out;
620 } else {
621 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
622 goto out;
626 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
627 goto out;
630 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
631 goto out;
634 if (asprintf(&key, "%s/%s/enctype=%d",
635 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
636 == -1) {
637 goto out;
640 if ((principal != NULL) && (strlen(principal) > 0)) {
641 ret = secrets_store(key, principal, strlen(principal) + 1);
642 } else {
643 ret = secrets_delete(key);
646 out:
648 SAFE_FREE(key);
649 SAFE_FREE(princ_s);
650 SAFE_FREE(unparsed_name);
652 if (context) {
653 krb5_free_context(context);
656 return ret;
660 /************************************************************************
661 ************************************************************************/
663 int kerberos_kinit_password(const char *principal,
664 const char *password,
665 int time_offset,
666 const char *cache_name)
668 return kerberos_kinit_password_ext(principal,
669 password,
670 time_offset,
673 cache_name,
674 False,
675 False,
677 NULL);
680 /************************************************************************
681 ************************************************************************/
683 static char *print_kdc_line(char *mem_ctx,
684 const char *prev_line,
685 const struct sockaddr_storage *pss)
687 char *kdc_str = NULL;
689 if (pss->ss_family == AF_INET) {
690 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
691 prev_line,
692 print_canonical_sockaddr(mem_ctx, pss));
693 } else {
694 char addr[INET6_ADDRSTRLEN];
695 uint16_t port = get_sockaddr_port(pss);
697 if (port != 0 && port != DEFAULT_KRB5_PORT) {
698 /* Currently for IPv6 we can't specify a non-default
699 krb5 port with an address, as this requires a ':'.
700 Resolve to a name. */
701 char hostname[MAX_DNS_NAME_LENGTH];
702 int ret = sys_getnameinfo((const struct sockaddr *)pss,
703 sizeof(*pss),
704 hostname, sizeof(hostname),
705 NULL, 0,
706 NI_NAMEREQD);
707 if (ret) {
708 DEBUG(0,("print_kdc_line: can't resolve name "
709 "for kdc with non-default port %s. "
710 "Error %s\n.",
711 print_canonical_sockaddr(mem_ctx, pss),
712 gai_strerror(ret)));
714 /* Success, use host:port */
715 kdc_str = talloc_asprintf(mem_ctx,
716 "%s\tkdc = %s:%u\n",
717 prev_line,
718 hostname,
719 (unsigned int)port);
720 } else {
721 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
722 prev_line,
723 print_sockaddr(addr,
724 sizeof(addr),
725 pss));
728 return kdc_str;
731 /************************************************************************
732 Create a string list of available kdc's, possibly searching by sitename.
733 Does DNS queries.
734 ************************************************************************/
736 static char *get_kdc_ip_string(char *mem_ctx,
737 const char *realm,
738 const char *sitename,
739 struct sockaddr_storage *pss)
741 int i;
742 struct ip_service *ip_srv_site = NULL;
743 struct ip_service *ip_srv_nonsite = NULL;
744 int count_site = 0;
745 int count_nonsite;
746 char *kdc_str = print_kdc_line(mem_ctx, "", pss);
748 if (kdc_str == NULL) {
749 return NULL;
752 /* Get the KDC's only in this site. */
754 if (sitename) {
756 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
758 for (i = 0; i < count_site; i++) {
759 if (addr_equal(&ip_srv_site[i].ss, pss)) {
760 continue;
762 /* Append to the string - inefficient
763 * but not done often. */
764 kdc_str = print_kdc_line(mem_ctx,
765 kdc_str,
766 &ip_srv_site[i].ss);
767 if (!kdc_str) {
768 SAFE_FREE(ip_srv_site);
769 return NULL;
774 /* Get all KDC's. */
776 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
778 for (i = 0; i < count_nonsite; i++) {
779 int j;
781 if (addr_equal(&ip_srv_nonsite[i].ss, pss)) {
782 continue;
785 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
786 for (j = 0; j < count_site; j++) {
787 if (addr_equal(&ip_srv_nonsite[i].ss,
788 &ip_srv_site[j].ss)) {
789 break;
791 /* As the lists are sorted we can break early if nonsite > site. */
792 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
793 break;
796 if (j != i) {
797 continue;
800 /* Append to the string - inefficient but not done often. */
801 kdc_str = print_kdc_line(mem_ctx,
802 kdc_str,
803 &ip_srv_nonsite[i].ss);
804 if (!kdc_str) {
805 SAFE_FREE(ip_srv_site);
806 SAFE_FREE(ip_srv_nonsite);
807 return NULL;
812 SAFE_FREE(ip_srv_site);
813 SAFE_FREE(ip_srv_nonsite);
815 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
816 kdc_str ));
818 return kdc_str;
821 /************************************************************************
822 Create a specific krb5.conf file in the private directory pointing
823 at a specific kdc for a realm. Keyed off domain name. Sets
824 KRB5_CONFIG environment variable to point to this file. Must be
825 run as root or will fail (which is a good thing :-).
826 ************************************************************************/
828 bool create_local_private_krb5_conf_for_domain(const char *realm,
829 const char *domain,
830 const char *sitename,
831 struct sockaddr_storage *pss)
833 char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
834 char *tmpname = NULL;
835 char *fname = NULL;
836 char *file_contents = NULL;
837 char *kdc_ip_string = NULL;
838 size_t flen = 0;
839 ssize_t ret;
840 int fd;
841 char *realm_upper = NULL;
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 TALLOC_FREE(dname);
851 return False;
854 tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
855 if (!tmpname) {
856 TALLOC_FREE(dname);
857 return False;
860 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
861 if (!fname) {
862 TALLOC_FREE(dname);
863 return False;
866 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
867 fname, realm, domain ));
869 realm_upper = talloc_strdup(fname, realm);
870 strupper_m(realm_upper);
872 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
873 if (!kdc_ip_string) {
874 TALLOC_FREE(dname);
875 return False;
878 file_contents = talloc_asprintf(fname,
879 "[libdefaults]\n\tdefault_realm = %s\n"
880 "default_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
881 "default_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
882 "preferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
883 "[realms]\n\t%s = {\n"
884 "\t%s\t}\n",
885 realm_upper, realm_upper, kdc_ip_string);
887 if (!file_contents) {
888 TALLOC_FREE(dname);
889 return False;
892 flen = strlen(file_contents);
894 fd = smb_mkstemp(tmpname);
895 if (fd == -1) {
896 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
897 " for file %s. Errno %s\n",
898 tmpname, strerror(errno) ));
899 TALLOC_FREE(dname);
900 return false;
903 if (fchmod(fd, 0644)==-1) {
904 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
905 " Errno %s\n",
906 tmpname, strerror(errno) ));
907 unlink(tmpname);
908 close(fd);
909 TALLOC_FREE(dname);
910 return False;
913 ret = write(fd, file_contents, flen);
914 if (flen != ret) {
915 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
916 " returned %d (should be %u). Errno %s\n",
917 (int)ret, (unsigned int)flen, strerror(errno) ));
918 unlink(tmpname);
919 close(fd);
920 TALLOC_FREE(dname);
921 return False;
923 if (close(fd)==-1) {
924 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
925 " Errno %s\n", strerror(errno) ));
926 unlink(tmpname);
927 TALLOC_FREE(dname);
928 return False;
931 if (rename(tmpname, fname) == -1) {
932 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
933 "of %s to %s failed. Errno %s\n",
934 tmpname, fname, strerror(errno) ));
935 unlink(tmpname);
936 TALLOC_FREE(dname);
937 return False;
940 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
941 "file %s with realm %s KDC list = %s\n",
942 fname, realm_upper, kdc_ip_string));
944 /* Set the environment variable to this file. */
945 setenv("KRB5_CONFIG", fname, 1);
947 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
949 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
950 /* Insanity, sheer insanity..... */
952 if (strequal(realm, lp_realm())) {
953 char linkpath[PATH_MAX+1];
954 int lret;
956 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
957 if (lret != -1) {
958 linkpath[lret] = '\0';
961 if (lret != -1 || strcmp(linkpath, fname) == 0) {
962 /* Symlink already exists. */
963 TALLOC_FREE(dname);
964 return True;
967 /* Try and replace with a symlink. */
968 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
969 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
970 if (errno != EEXIST) {
971 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
972 "of %s to %s failed. Errno %s\n",
973 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
974 TALLOC_FREE(dname);
975 return True; /* Not a fatal error. */
978 /* Yes, this is a race conditon... too bad. */
979 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
980 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
981 "of %s to %s failed. Errno %s\n",
982 SYSTEM_KRB5_CONF_PATH, newpath,
983 strerror(errno) ));
984 TALLOC_FREE(dname);
985 return True; /* Not a fatal error. */
988 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
989 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
990 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
991 fname, strerror(errno) ));
992 TALLOC_FREE(dname);
993 return True; /* Not a fatal error. */
997 #endif
999 TALLOC_FREE(dname);
1001 return True;
1003 #endif