More clean up, indentation and trailing space removal
[Samba.git] / source3 / libads / kerberos.c
blob31e5af4224f6f3999beb040931c884738f635d08
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 (ads->auth.flags & ADS_AUTH_USER_CREDS) {
327 account_name = ads->auth.user_name;
328 goto got_accountname;
331 if ( IS_DC ) {
332 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
333 account_name = lp_workgroup();
334 } else {
335 /* always use the sAMAccountName for security = domain */
336 /* global_myname()$@REA.LM */
337 if ( lp_security() == SEC_DOMAIN ) {
338 fstr_sprintf( acct_name, "%s$", global_myname() );
339 account_name = acct_name;
341 else
342 /* This looks like host/global_myname()@REA.LM */
343 account_name = ads->auth.user_name;
346 got_accountname:
347 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
348 return KRB5_CC_NOMEM;
351 if (!ads->auth.password) {
352 SAFE_FREE(s);
353 return KRB5_LIBOS_CANTREADPWD;
356 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
357 &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable,
358 NULL);
360 if (ret) {
361 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
362 s, error_message(ret)));
364 SAFE_FREE(s);
365 return ret;
368 int ads_kdestroy(const char *cc_name)
370 krb5_error_code code;
371 krb5_context ctx = NULL;
372 krb5_ccache cc = NULL;
374 initialize_krb5_error_table();
375 if ((code = krb5_init_context (&ctx))) {
376 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
377 error_message(code)));
378 return code;
381 if (!cc_name) {
382 if ((code = krb5_cc_default(ctx, &cc))) {
383 krb5_free_context(ctx);
384 return code;
386 } else {
387 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
388 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
389 error_message(code)));
390 krb5_free_context(ctx);
391 return code;
395 if ((code = krb5_cc_destroy (ctx, cc))) {
396 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
397 error_message(code)));
400 krb5_free_context (ctx);
401 return code;
404 /************************************************************************
405 Routine to fetch the salting principal for a service. Active
406 Directory may use a non-obvious principal name to generate the salt
407 when it determines the key to use for encrypting tickets for a service,
408 and hopefully we detected that when we joined the domain.
409 ************************************************************************/
411 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
413 char *key = NULL;
414 char *ret = NULL;
416 if (asprintf(&key, "%s/%s/enctype=%d",
417 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
418 return NULL;
420 ret = (char *)secrets_fetch(key, NULL);
421 SAFE_FREE(key);
422 return ret;
425 /************************************************************************
426 Return the standard DES salt key
427 ************************************************************************/
429 char* kerberos_standard_des_salt( void )
431 fstring salt;
433 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
434 strlower_m( salt );
435 fstrcat( salt, lp_realm() );
437 return SMB_STRDUP( salt );
440 /************************************************************************
441 ************************************************************************/
443 static char* des_salt_key( void )
445 char *key;
447 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
448 lp_realm()) == -1) {
449 return NULL;
452 return key;
455 /************************************************************************
456 ************************************************************************/
458 bool kerberos_secrets_store_des_salt( const char* salt )
460 char* key;
461 bool ret;
463 if ( (key = des_salt_key()) == NULL ) {
464 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
465 return False;
468 if ( !salt ) {
469 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
470 secrets_delete( key );
471 return True;
474 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
476 ret = secrets_store( key, salt, strlen(salt)+1 );
478 SAFE_FREE( key );
480 return ret;
483 /************************************************************************
484 ************************************************************************/
486 char* kerberos_secrets_fetch_des_salt( void )
488 char *salt, *key;
490 if ( (key = des_salt_key()) == NULL ) {
491 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
492 return False;
495 salt = (char*)secrets_fetch( key, NULL );
497 SAFE_FREE( key );
499 return salt;
502 /************************************************************************
503 Routine to get the default realm from the kerberos credentials cache.
504 Caller must free if the return value is not NULL.
505 ************************************************************************/
507 char *kerberos_get_default_realm_from_ccache( void )
509 char *realm = NULL;
510 krb5_context ctx = NULL;
511 krb5_ccache cc = NULL;
512 krb5_principal princ = NULL;
514 initialize_krb5_error_table();
515 if (krb5_init_context(&ctx)) {
516 return NULL;
519 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
520 "Trying to read krb5 cache: %s\n",
521 krb5_cc_default_name(ctx)));
522 if (krb5_cc_default(ctx, &cc)) {
523 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
524 "failed to read default cache\n"));
525 goto out;
527 if (krb5_cc_get_principal(ctx, cc, &princ)) {
528 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
529 "failed to get default principal\n"));
530 goto out;
533 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
534 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
535 #elif defined(HAVE_KRB5_PRINC_REALM)
537 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
538 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
540 #endif
542 out:
544 if (princ) {
545 krb5_free_principal(ctx, princ);
547 if (cc) {
548 krb5_cc_close(ctx, cc);
550 if (ctx) {
551 krb5_free_context(ctx);
554 return realm;
558 /************************************************************************
559 Routine to get the salting principal for this service. This is
560 maintained for backwards compatibilty with releases prior to 3.0.24.
561 Since we store the salting principal string only at join, we may have
562 to look for the older tdb keys. Caller must free if return is not null.
563 ************************************************************************/
565 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
566 krb5_principal host_princ,
567 int enctype)
569 char *unparsed_name = NULL, *salt_princ_s = NULL;
570 krb5_principal ret_princ = NULL;
572 /* lookup new key first */
574 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
576 /* look under the old key. If this fails, just use the standard key */
578 if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
579 return (krb5_principal)NULL;
581 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
582 /* fall back to host/machine.realm@REALM */
583 salt_princ_s = kerberos_standard_des_salt();
587 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
588 ret_princ = NULL;
591 SAFE_FREE(unparsed_name);
592 SAFE_FREE(salt_princ_s);
594 return ret_princ;
597 /************************************************************************
598 Routine to set the salting principal for this service. Active
599 Directory may use a non-obvious principal name to generate the salt
600 when it determines the key to use for encrypting tickets for a service,
601 and hopefully we detected that when we joined the domain.
602 Setting principal to NULL deletes this entry.
603 ************************************************************************/
605 bool kerberos_secrets_store_salting_principal(const char *service,
606 int enctype,
607 const char *principal)
609 char *key = NULL;
610 bool ret = False;
611 krb5_context context = NULL;
612 krb5_principal princ = NULL;
613 char *princ_s = NULL;
614 char *unparsed_name = NULL;
615 krb5_error_code code;
617 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
618 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
619 error_message(code)));
620 return False;
622 if (strchr_m(service, '@')) {
623 if (asprintf(&princ_s, "%s", service) == -1) {
624 goto out;
626 } else {
627 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
628 goto out;
632 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
633 goto out;
636 if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
637 goto out;
640 if (asprintf(&key, "%s/%s/enctype=%d",
641 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
642 == -1) {
643 goto out;
646 if ((principal != NULL) && (strlen(principal) > 0)) {
647 ret = secrets_store(key, principal, strlen(principal) + 1);
648 } else {
649 ret = secrets_delete(key);
652 out:
654 SAFE_FREE(key);
655 SAFE_FREE(princ_s);
656 SAFE_FREE(unparsed_name);
658 if (princ) {
659 krb5_free_principal(context, princ);
662 if (context) {
663 krb5_free_context(context);
666 return ret;
670 /************************************************************************
671 ************************************************************************/
673 int kerberos_kinit_password(const char *principal,
674 const char *password,
675 int time_offset,
676 const char *cache_name)
678 return kerberos_kinit_password_ext(principal,
679 password,
680 time_offset,
683 cache_name,
684 False,
685 False,
687 NULL);
690 /************************************************************************
691 ************************************************************************/
693 static char *print_kdc_line(char *mem_ctx,
694 const char *prev_line,
695 const struct sockaddr_storage *pss)
697 char *kdc_str = NULL;
699 if (pss->ss_family == AF_INET) {
700 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
701 prev_line,
702 print_canonical_sockaddr(mem_ctx, pss));
703 } else {
704 char addr[INET6_ADDRSTRLEN];
705 uint16_t port = get_sockaddr_port(pss);
707 if (port != 0 && port != DEFAULT_KRB5_PORT) {
708 /* Currently for IPv6 we can't specify a non-default
709 krb5 port with an address, as this requires a ':'.
710 Resolve to a name. */
711 char hostname[MAX_DNS_NAME_LENGTH];
712 int ret = sys_getnameinfo((const struct sockaddr *)pss,
713 sizeof(*pss),
714 hostname, sizeof(hostname),
715 NULL, 0,
716 NI_NAMEREQD);
717 if (ret) {
718 DEBUG(0,("print_kdc_line: can't resolve name "
719 "for kdc with non-default port %s. "
720 "Error %s\n.",
721 print_canonical_sockaddr(mem_ctx, pss),
722 gai_strerror(ret)));
724 /* Success, use host:port */
725 kdc_str = talloc_asprintf(mem_ctx,
726 "%s\tkdc = %s:%u\n",
727 prev_line,
728 hostname,
729 (unsigned int)port);
730 } else {
731 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
732 prev_line,
733 print_sockaddr(addr,
734 sizeof(addr),
735 pss));
738 return kdc_str;
741 /************************************************************************
742 Create a string list of available kdc's, possibly searching by sitename.
743 Does DNS queries.
745 If "sitename" is given, the DC's in that site are listed first.
747 ************************************************************************/
749 static char *get_kdc_ip_string(char *mem_ctx,
750 const char *realm,
751 const char *sitename,
752 struct sockaddr_storage *pss)
754 int i;
755 struct ip_service *ip_srv_site = NULL;
756 struct ip_service *ip_srv_nonsite = NULL;
757 int count_site = 0;
758 int count_nonsite;
759 char *kdc_str = print_kdc_line(mem_ctx, "", pss);
761 if (kdc_str == NULL) {
762 return NULL;
766 * First get the KDC's only in this site, the rest will be
767 * appended later
770 if (sitename) {
772 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
774 for (i = 0; i < count_site; i++) {
775 if (addr_equal(&ip_srv_site[i].ss, pss)) {
776 continue;
778 /* Append to the string - inefficient
779 * but not done often. */
780 kdc_str = print_kdc_line(mem_ctx,
781 kdc_str,
782 &ip_srv_site[i].ss);
783 if (!kdc_str) {
784 SAFE_FREE(ip_srv_site);
785 return NULL;
790 /* Get all KDC's. */
792 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
794 for (i = 0; i < count_nonsite; i++) {
795 int j;
797 if (addr_equal(&ip_srv_nonsite[i].ss, pss)) {
798 continue;
801 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
802 for (j = 0; j < count_site; j++) {
803 if (addr_equal(&ip_srv_nonsite[i].ss,
804 &ip_srv_site[j].ss)) {
805 break;
807 /* As the lists are sorted we can break early if nonsite > site. */
808 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
809 break;
812 if (j != i) {
813 continue;
816 /* Append to the string - inefficient but not done often. */
817 kdc_str = print_kdc_line(mem_ctx,
818 kdc_str,
819 &ip_srv_nonsite[i].ss);
820 if (!kdc_str) {
821 SAFE_FREE(ip_srv_site);
822 SAFE_FREE(ip_srv_nonsite);
823 return NULL;
828 SAFE_FREE(ip_srv_site);
829 SAFE_FREE(ip_srv_nonsite);
831 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
832 kdc_str ));
834 return kdc_str;
837 /************************************************************************
838 Create a specific krb5.conf file in the private directory pointing
839 at a specific kdc for a realm. Keyed off domain name. Sets
840 KRB5_CONFIG environment variable to point to this file. Must be
841 run as root or will fail (which is a good thing :-).
842 ************************************************************************/
844 bool create_local_private_krb5_conf_for_domain(const char *realm,
845 const char *domain,
846 const char *sitename,
847 struct sockaddr_storage *pss)
849 char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
850 char *tmpname = NULL;
851 char *fname = NULL;
852 char *file_contents = NULL;
853 char *kdc_ip_string = NULL;
854 size_t flen = 0;
855 ssize_t ret;
856 int fd;
857 char *realm_upper = NULL;
859 if (!dname) {
860 return False;
862 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
863 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
864 "failed to create directory %s. Error was %s\n",
865 dname, strerror(errno) ));
866 TALLOC_FREE(dname);
867 return False;
870 tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
871 if (!tmpname) {
872 TALLOC_FREE(dname);
873 return False;
876 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
877 if (!fname) {
878 TALLOC_FREE(dname);
879 return False;
882 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
883 fname, realm, domain ));
885 realm_upper = talloc_strdup(fname, realm);
886 strupper_m(realm_upper);
888 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
889 if (!kdc_ip_string) {
890 TALLOC_FREE(dname);
891 return False;
894 file_contents = talloc_asprintf(fname,
895 "[libdefaults]\n\tdefault_realm = %s\n"
896 "default_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
897 "default_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
898 "preferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
899 "[realms]\n\t%s = {\n"
900 "\t%s\t}\n",
901 realm_upper, realm_upper, kdc_ip_string);
903 if (!file_contents) {
904 TALLOC_FREE(dname);
905 return False;
908 flen = strlen(file_contents);
910 fd = smb_mkstemp(tmpname);
911 if (fd == -1) {
912 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
913 " for file %s. Errno %s\n",
914 tmpname, strerror(errno) ));
915 TALLOC_FREE(dname);
916 return false;
919 if (fchmod(fd, 0644)==-1) {
920 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
921 " Errno %s\n",
922 tmpname, strerror(errno) ));
923 unlink(tmpname);
924 close(fd);
925 TALLOC_FREE(dname);
926 return False;
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 TALLOC_FREE(dname);
937 return False;
939 if (close(fd)==-1) {
940 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
941 " Errno %s\n", strerror(errno) ));
942 unlink(tmpname);
943 TALLOC_FREE(dname);
944 return False;
947 if (rename(tmpname, fname) == -1) {
948 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
949 "of %s to %s failed. Errno %s\n",
950 tmpname, fname, strerror(errno) ));
951 unlink(tmpname);
952 TALLOC_FREE(dname);
953 return False;
956 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
957 "file %s with realm %s KDC list = %s\n",
958 fname, realm_upper, kdc_ip_string));
960 /* Set the environment variable to this file. */
961 setenv("KRB5_CONFIG", fname, 1);
963 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
965 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
966 /* Insanity, sheer insanity..... */
968 if (strequal(realm, lp_realm())) {
969 char linkpath[PATH_MAX+1];
970 int lret;
972 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
973 if (lret != -1) {
974 linkpath[lret] = '\0';
977 if (lret != -1 || strcmp(linkpath, fname) == 0) {
978 /* Symlink already exists. */
979 TALLOC_FREE(dname);
980 return True;
983 /* Try and replace with a symlink. */
984 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
985 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
986 if (errno != EEXIST) {
987 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
988 "of %s to %s failed. Errno %s\n",
989 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
990 TALLOC_FREE(dname);
991 return True; /* Not a fatal error. */
994 /* Yes, this is a race conditon... too bad. */
995 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
996 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
997 "of %s to %s failed. Errno %s\n",
998 SYSTEM_KRB5_CONF_PATH, newpath,
999 strerror(errno) ));
1000 TALLOC_FREE(dname);
1001 return True; /* Not a fatal error. */
1004 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1005 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1006 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1007 fname, strerror(errno) ));
1008 TALLOC_FREE(dname);
1009 return True; /* Not a fatal error. */
1013 #endif
1015 TALLOC_FREE(dname);
1017 return True;
1019 #endif