s3-waf: ndr string functions moved to top level
[Samba/ekacnet.git] / source3 / libads / kerberos.c
blob9f58ee915ca5ff238adb70557704932f47494060
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_get_ntstatus_from_krb5_error(krb5_error *error,
61 NTSTATUS *nt_status)
63 DATA_BLOB edata;
64 DATA_BLOB unwrapped_edata;
65 TALLOC_CTX *mem_ctx;
66 struct KRB5_EDATA_NTSTATUS parsed_edata;
67 enum ndr_err_code ndr_err;
69 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
70 edata = data_blob(error->e_data->data, error->e_data->length);
71 #else
72 edata = data_blob(error->e_data.data, error->e_data.length);
73 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
75 #ifdef DEVELOPER
76 dump_data(10, edata.data, edata.length);
77 #endif /* DEVELOPER */
79 mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
80 if (mem_ctx == NULL) {
81 data_blob_free(&edata);
82 return False;
85 if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
86 data_blob_free(&edata);
87 TALLOC_FREE(mem_ctx);
88 return False;
91 data_blob_free(&edata);
93 ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx,
94 &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
95 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
96 data_blob_free(&unwrapped_edata);
97 TALLOC_FREE(mem_ctx);
98 return False;
101 data_blob_free(&unwrapped_edata);
103 if (nt_status) {
104 *nt_status = parsed_edata.ntstatus;
107 TALLOC_FREE(mem_ctx);
109 return True;
112 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
113 krb5_get_init_creds_opt *opt,
114 NTSTATUS *nt_status)
116 bool ret = False;
117 krb5_error *error = NULL;
119 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
120 ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
121 if (ret) {
122 DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
123 error_message(ret)));
124 return False;
126 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
128 if (!error) {
129 DEBUG(1,("no krb5_error\n"));
130 return False;
133 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
134 if (!error->e_data) {
135 #else
136 if (error->e_data.data == NULL) {
137 #endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
138 DEBUG(1,("no edata in krb5_error\n"));
139 krb5_free_error(ctx, error);
140 return False;
143 ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
145 krb5_free_error(ctx, error);
147 return ret;
151 simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
152 place in default cache location.
153 remus@snapserver.com
155 int kerberos_kinit_password_ext(const char *principal,
156 const char *password,
157 int time_offset,
158 time_t *expire_time,
159 time_t *renew_till_time,
160 const char *cache_name,
161 bool request_pac,
162 bool add_netbios_addr,
163 time_t renewable_time,
164 NTSTATUS *ntstatus)
166 krb5_context ctx = NULL;
167 krb5_error_code code = 0;
168 krb5_ccache cc = NULL;
169 krb5_principal me = NULL;
170 krb5_creds my_creds;
171 krb5_get_init_creds_opt *opt = NULL;
172 smb_krb5_addresses *addr = NULL;
174 ZERO_STRUCT(my_creds);
176 initialize_krb5_error_table();
177 if ((code = krb5_init_context(&ctx)))
178 goto out;
180 if (time_offset != 0) {
181 krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
184 DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
185 principal,
186 cache_name ? cache_name: krb5_cc_default_name(ctx),
187 getenv("KRB5_CONFIG")));
189 if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
190 goto out;
193 if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
194 goto out;
197 if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
198 goto out;
201 krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
202 krb5_get_init_creds_opt_set_forwardable(opt, True);
203 #if 0
204 /* insane testing */
205 krb5_get_init_creds_opt_set_tkt_life(opt, 60);
206 #endif
208 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
209 if (request_pac) {
210 if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
211 goto out;
214 #endif
215 if (add_netbios_addr) {
216 if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
217 goto out;
219 krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
222 if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
223 kerb_prompter, CONST_DISCARD(char *,password),
224 0, NULL, opt))) {
225 goto out;
228 if ((code = krb5_cc_initialize(ctx, cc, me))) {
229 goto out;
232 if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
233 goto out;
236 if (expire_time) {
237 *expire_time = (time_t) my_creds.times.endtime;
240 if (renew_till_time) {
241 *renew_till_time = (time_t) my_creds.times.renew_till;
243 out:
244 if (ntstatus) {
246 NTSTATUS status;
248 /* fast path */
249 if (code == 0) {
250 *ntstatus = NT_STATUS_OK;
251 goto cleanup;
254 /* try to get ntstatus code out of krb5_error when we have it
255 * inside the krb5_get_init_creds_opt - gd */
257 if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
258 *ntstatus = status;
259 goto cleanup;
262 /* fall back to self-made-mapping */
263 *ntstatus = krb5_to_nt_status(code);
266 cleanup:
267 krb5_free_cred_contents(ctx, &my_creds);
268 if (me) {
269 krb5_free_principal(ctx, me);
271 if (addr) {
272 smb_krb5_free_addresses(ctx, addr);
274 if (opt) {
275 smb_krb5_get_init_creds_opt_free(ctx, opt);
277 if (cc) {
278 krb5_cc_close(ctx, cc);
280 if (ctx) {
281 krb5_free_context(ctx);
283 return code;
288 /* run kinit to setup our ccache */
289 int ads_kinit_password(ADS_STRUCT *ads)
291 char *s;
292 int ret;
293 const char *account_name;
294 fstring acct_name;
296 if (ads->auth.flags & ADS_AUTH_USER_CREDS) {
297 account_name = ads->auth.user_name;
298 goto got_accountname;
301 if ( IS_DC ) {
302 /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
303 account_name = lp_workgroup();
304 } else {
305 /* always use the sAMAccountName for security = domain */
306 /* global_myname()$@REA.LM */
307 if ( lp_security() == SEC_DOMAIN ) {
308 fstr_sprintf( acct_name, "%s$", global_myname() );
309 account_name = acct_name;
311 else
312 /* This looks like host/global_myname()@REA.LM */
313 account_name = ads->auth.user_name;
316 got_accountname:
317 if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
318 return KRB5_CC_NOMEM;
321 if (!ads->auth.password) {
322 SAFE_FREE(s);
323 return KRB5_LIBOS_CANTREADPWD;
326 ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
327 &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable,
328 NULL);
330 if (ret) {
331 DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
332 s, error_message(ret)));
334 SAFE_FREE(s);
335 return ret;
338 int ads_kdestroy(const char *cc_name)
340 krb5_error_code code;
341 krb5_context ctx = NULL;
342 krb5_ccache cc = NULL;
344 initialize_krb5_error_table();
345 if ((code = krb5_init_context (&ctx))) {
346 DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
347 error_message(code)));
348 return code;
351 if (!cc_name) {
352 if ((code = krb5_cc_default(ctx, &cc))) {
353 krb5_free_context(ctx);
354 return code;
356 } else {
357 if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
358 DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
359 error_message(code)));
360 krb5_free_context(ctx);
361 return code;
365 if ((code = krb5_cc_destroy (ctx, cc))) {
366 DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
367 error_message(code)));
370 krb5_free_context (ctx);
371 return code;
374 /************************************************************************
375 Routine to fetch the salting principal for a service. Active
376 Directory may use a non-obvious principal name to generate the salt
377 when it determines the key to use for encrypting tickets for a service,
378 and hopefully we detected that when we joined the domain.
379 ************************************************************************/
381 static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
383 char *key = NULL;
384 char *ret = NULL;
386 if (asprintf(&key, "%s/%s/enctype=%d",
387 SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
388 return NULL;
390 ret = (char *)secrets_fetch(key, NULL);
391 SAFE_FREE(key);
392 return ret;
395 /************************************************************************
396 Return the standard DES salt key
397 ************************************************************************/
399 char* kerberos_standard_des_salt( void )
401 fstring salt;
403 fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
404 strlower_m( salt );
405 fstrcat( salt, lp_realm() );
407 return SMB_STRDUP( salt );
410 /************************************************************************
411 ************************************************************************/
413 static char* des_salt_key( void )
415 char *key;
417 if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
418 lp_realm()) == -1) {
419 return NULL;
422 return key;
425 /************************************************************************
426 ************************************************************************/
428 bool kerberos_secrets_store_des_salt( const char* salt )
430 char* key;
431 bool ret;
433 if ( (key = des_salt_key()) == NULL ) {
434 DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
435 return False;
438 if ( !salt ) {
439 DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
440 secrets_delete( key );
441 return True;
444 DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
446 ret = secrets_store( key, salt, strlen(salt)+1 );
448 SAFE_FREE( key );
450 return ret;
453 /************************************************************************
454 ************************************************************************/
456 char* kerberos_secrets_fetch_des_salt( void )
458 char *salt, *key;
460 if ( (key = des_salt_key()) == NULL ) {
461 DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
462 return False;
465 salt = (char*)secrets_fetch( key, NULL );
467 SAFE_FREE( key );
469 return salt;
472 /************************************************************************
473 Routine to get the default realm from the kerberos credentials cache.
474 Caller must free if the return value is not NULL.
475 ************************************************************************/
477 char *kerberos_get_default_realm_from_ccache( void )
479 char *realm = NULL;
480 krb5_context ctx = NULL;
481 krb5_ccache cc = NULL;
482 krb5_principal princ = NULL;
484 initialize_krb5_error_table();
485 if (krb5_init_context(&ctx)) {
486 return NULL;
489 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
490 "Trying to read krb5 cache: %s\n",
491 krb5_cc_default_name(ctx)));
492 if (krb5_cc_default(ctx, &cc)) {
493 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
494 "failed to read default cache\n"));
495 goto out;
497 if (krb5_cc_get_principal(ctx, cc, &princ)) {
498 DEBUG(0,("kerberos_get_default_realm_from_ccache: "
499 "failed to get default principal\n"));
500 goto out;
503 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
504 realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
505 #elif defined(HAVE_KRB5_PRINC_REALM)
507 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
508 realm = SMB_STRNDUP(realm_data->data, realm_data->length);
510 #endif
512 out:
514 if (ctx) {
515 if (princ) {
516 krb5_free_principal(ctx, princ);
518 if (cc) {
519 krb5_cc_close(ctx, cc);
521 krb5_free_context(ctx);
524 return realm;
527 /************************************************************************
528 Routine to get the realm from a given DNS name. Returns malloc'ed memory.
529 Caller must free() if the return value is not NULL.
530 ************************************************************************/
532 char *kerberos_get_realm_from_hostname(const char *hostname)
534 #if defined(HAVE_KRB5_GET_HOST_REALM) && defined(HAVE_KRB5_FREE_HOST_REALM)
535 #if defined(HAVE_KRB5_REALM_TYPE)
536 /* Heimdal. */
537 krb5_realm *realm_list = NULL;
538 #else
539 /* MIT */
540 char **realm_list = NULL;
541 #endif
542 char *realm = NULL;
543 krb5_error_code kerr;
544 krb5_context ctx = NULL;
546 initialize_krb5_error_table();
547 if (krb5_init_context(&ctx)) {
548 return NULL;
551 kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
552 if (kerr != 0) {
553 DEBUG(3,("kerberos_get_realm_from_hostname %s: "
554 "failed %s\n",
555 hostname ? hostname : "(NULL)",
556 error_message(kerr) ));
557 goto out;
560 if (realm_list && realm_list[0]) {
561 realm = SMB_STRDUP(realm_list[0]);
564 out:
566 if (ctx) {
567 if (realm_list) {
568 krb5_free_host_realm(ctx, realm_list);
569 realm_list = NULL;
571 krb5_free_context(ctx);
572 ctx = NULL;
574 return realm;
575 #else
576 return NULL;
577 #endif
580 /************************************************************************
581 Routine to get the salting principal for this service. This is
582 maintained for backwards compatibilty with releases prior to 3.0.24.
583 Since we store the salting principal string only at join, we may have
584 to look for the older tdb keys. Caller must free if return is not null.
585 ************************************************************************/
587 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
588 krb5_principal host_princ,
589 int enctype)
591 char *unparsed_name = NULL, *salt_princ_s = NULL;
592 krb5_principal ret_princ = NULL;
594 /* lookup new key first */
596 if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
598 /* look under the old key. If this fails, just use the standard key */
600 if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
601 return (krb5_principal)NULL;
603 if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
604 /* fall back to host/machine.realm@REALM */
605 salt_princ_s = kerberos_standard_des_salt();
609 if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
610 ret_princ = NULL;
613 TALLOC_FREE(unparsed_name);
614 SAFE_FREE(salt_princ_s);
616 return ret_princ;
619 /************************************************************************
620 Routine to set the salting principal for this service. Active
621 Directory may use a non-obvious principal name to generate the salt
622 when it determines the key to use for encrypting tickets for a service,
623 and hopefully we detected that when we joined the domain.
624 Setting principal to NULL deletes this entry.
625 ************************************************************************/
627 bool kerberos_secrets_store_salting_principal(const char *service,
628 int enctype,
629 const char *principal)
631 char *key = NULL;
632 bool ret = False;
633 krb5_context context = NULL;
634 krb5_principal princ = NULL;
635 char *princ_s = NULL;
636 char *unparsed_name = NULL;
637 krb5_error_code code;
639 if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
640 DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
641 error_message(code)));
642 return False;
644 if (strchr_m(service, '@')) {
645 if (asprintf(&princ_s, "%s", service) == -1) {
646 goto out;
648 } else {
649 if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
650 goto out;
654 if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
655 goto out;
658 if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
659 goto out;
662 if (asprintf(&key, "%s/%s/enctype=%d",
663 SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
664 == -1) {
665 goto out;
668 if ((principal != NULL) && (strlen(principal) > 0)) {
669 ret = secrets_store(key, principal, strlen(principal) + 1);
670 } else {
671 ret = secrets_delete(key);
674 out:
676 SAFE_FREE(key);
677 SAFE_FREE(princ_s);
678 TALLOC_FREE(unparsed_name);
680 if (princ) {
681 krb5_free_principal(context, princ);
684 if (context) {
685 krb5_free_context(context);
688 return ret;
692 /************************************************************************
693 ************************************************************************/
695 int kerberos_kinit_password(const char *principal,
696 const char *password,
697 int time_offset,
698 const char *cache_name)
700 return kerberos_kinit_password_ext(principal,
701 password,
702 time_offset,
705 cache_name,
706 False,
707 False,
709 NULL);
712 /************************************************************************
713 ************************************************************************/
715 static char *print_kdc_line(char *mem_ctx,
716 const char *prev_line,
717 const struct sockaddr_storage *pss,
718 const char *kdc_name)
720 char *kdc_str = NULL;
722 if (pss->ss_family == AF_INET) {
723 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
724 prev_line,
725 print_canonical_sockaddr(mem_ctx, pss));
726 } else {
727 char addr[INET6_ADDRSTRLEN];
728 uint16_t port = get_sockaddr_port(pss);
730 DEBUG(10,("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
731 kdc_name, port));
733 if (port != 0 && port != DEFAULT_KRB5_PORT) {
734 /* Currently for IPv6 we can't specify a non-default
735 krb5 port with an address, as this requires a ':'.
736 Resolve to a name. */
737 char hostname[MAX_DNS_NAME_LENGTH];
738 int ret = sys_getnameinfo((const struct sockaddr *)pss,
739 sizeof(*pss),
740 hostname, sizeof(hostname),
741 NULL, 0,
742 NI_NAMEREQD);
743 if (ret) {
744 DEBUG(0,("print_kdc_line: can't resolve name "
745 "for kdc with non-default port %s. "
746 "Error %s\n.",
747 print_canonical_sockaddr(mem_ctx, pss),
748 gai_strerror(ret)));
749 return NULL;
751 /* Success, use host:port */
752 kdc_str = talloc_asprintf(mem_ctx,
753 "%s\tkdc = %s:%u\n",
754 prev_line,
755 hostname,
756 (unsigned int)port);
757 } else {
759 /* no krb5 lib currently supports "kdc = ipv6 address"
760 * at all, so just fill in just the kdc_name if we have
761 * it and let the krb5 lib figure out the appropriate
762 * ipv6 address - gd */
764 if (kdc_name) {
765 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
766 prev_line, kdc_name);
767 } else {
768 kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
769 prev_line,
770 print_sockaddr(addr,
771 sizeof(addr),
772 pss));
776 return kdc_str;
779 /************************************************************************
780 Create a string list of available kdc's, possibly searching by sitename.
781 Does DNS queries.
783 If "sitename" is given, the DC's in that site are listed first.
785 ************************************************************************/
787 static char *get_kdc_ip_string(char *mem_ctx,
788 const char *realm,
789 const char *sitename,
790 struct sockaddr_storage *pss,
791 const char *kdc_name)
793 int i;
794 struct ip_service *ip_srv_site = NULL;
795 struct ip_service *ip_srv_nonsite = NULL;
796 int count_site = 0;
797 int count_nonsite;
798 char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
800 if (kdc_str == NULL) {
801 return NULL;
805 * First get the KDC's only in this site, the rest will be
806 * appended later
809 if (sitename) {
811 get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
813 for (i = 0; i < count_site; i++) {
814 if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss,
815 (struct sockaddr *)pss)) {
816 continue;
818 /* Append to the string - inefficient
819 * but not done often. */
820 kdc_str = print_kdc_line(mem_ctx,
821 kdc_str,
822 &ip_srv_site[i].ss,
823 NULL);
824 if (!kdc_str) {
825 SAFE_FREE(ip_srv_site);
826 return NULL;
831 /* Get all KDC's. */
833 get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
835 for (i = 0; i < count_nonsite; i++) {
836 int j;
838 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) {
839 continue;
842 /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
843 for (j = 0; j < count_site; j++) {
844 if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss,
845 (struct sockaddr *)&ip_srv_site[j].ss)) {
846 break;
848 /* As the lists are sorted we can break early if nonsite > site. */
849 if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
850 break;
853 if (j != i) {
854 continue;
857 /* Append to the string - inefficient but not done often. */
858 kdc_str = print_kdc_line(mem_ctx,
859 kdc_str,
860 &ip_srv_nonsite[i].ss,
861 NULL);
862 if (!kdc_str) {
863 SAFE_FREE(ip_srv_site);
864 SAFE_FREE(ip_srv_nonsite);
865 return NULL;
870 SAFE_FREE(ip_srv_site);
871 SAFE_FREE(ip_srv_nonsite);
873 DEBUG(10,("get_kdc_ip_string: Returning %s\n",
874 kdc_str ));
876 return kdc_str;
879 /************************************************************************
880 Create a specific krb5.conf file in the private directory pointing
881 at a specific kdc for a realm. Keyed off domain name. Sets
882 KRB5_CONFIG environment variable to point to this file. Must be
883 run as root or will fail (which is a good thing :-).
884 ************************************************************************/
886 bool create_local_private_krb5_conf_for_domain(const char *realm,
887 const char *domain,
888 const char *sitename,
889 struct sockaddr_storage *pss,
890 const char *kdc_name)
892 char *dname;
893 char *tmpname = NULL;
894 char *fname = NULL;
895 char *file_contents = NULL;
896 char *kdc_ip_string = NULL;
897 size_t flen = 0;
898 ssize_t ret;
899 int fd;
900 char *realm_upper = NULL;
901 bool result = false;
903 if (!lp_create_krb5_conf()) {
904 return false;
907 dname = lock_path("smb_krb5");
908 if (!dname) {
909 return false;
911 if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
912 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
913 "failed to create directory %s. Error was %s\n",
914 dname, strerror(errno) ));
915 goto done;
918 tmpname = lock_path("smb_tmp_krb5.XXXXXX");
919 if (!tmpname) {
920 goto done;
923 fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
924 if (!fname) {
925 goto done;
928 DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
929 fname, realm, domain ));
931 realm_upper = talloc_strdup(fname, realm);
932 strupper_m(realm_upper);
934 kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
935 if (!kdc_ip_string) {
936 goto done;
939 file_contents = talloc_asprintf(fname,
940 "[libdefaults]\n\tdefault_realm = %s\n"
941 "\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
942 "\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
943 "\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
944 "[realms]\n\t%s = {\n"
945 "\t%s\t}\n",
946 realm_upper, realm_upper, kdc_ip_string);
948 if (!file_contents) {
949 goto done;
952 flen = strlen(file_contents);
954 fd = mkstemp(tmpname);
955 if (fd == -1) {
956 DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
957 " for file %s. Errno %s\n",
958 tmpname, strerror(errno) ));
959 goto done;
962 if (fchmod(fd, 0644)==-1) {
963 DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
964 " Errno %s\n",
965 tmpname, strerror(errno) ));
966 unlink(tmpname);
967 close(fd);
968 goto done;
971 ret = write(fd, file_contents, flen);
972 if (flen != ret) {
973 DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
974 " returned %d (should be %u). Errno %s\n",
975 (int)ret, (unsigned int)flen, strerror(errno) ));
976 unlink(tmpname);
977 close(fd);
978 goto done;
980 if (close(fd)==-1) {
981 DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
982 " Errno %s\n", strerror(errno) ));
983 unlink(tmpname);
984 goto done;
987 if (rename(tmpname, fname) == -1) {
988 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
989 "of %s to %s failed. Errno %s\n",
990 tmpname, fname, strerror(errno) ));
991 unlink(tmpname);
992 goto done;
995 DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
996 "file %s with realm %s KDC list = %s\n",
997 fname, realm_upper, kdc_ip_string));
999 /* Set the environment variable to this file. */
1000 setenv("KRB5_CONFIG", fname, 1);
1002 result = true;
1004 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
1006 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
1007 /* Insanity, sheer insanity..... */
1009 if (strequal(realm, lp_realm())) {
1010 char linkpath[PATH_MAX+1];
1011 int lret;
1013 lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
1014 if (lret != -1) {
1015 linkpath[lret] = '\0';
1018 if (lret != -1 || strcmp(linkpath, fname) == 0) {
1019 /* Symlink already exists. */
1020 goto done;
1023 /* Try and replace with a symlink. */
1024 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1025 const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
1026 if (errno != EEXIST) {
1027 DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
1028 "of %s to %s failed. Errno %s\n",
1029 fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
1030 goto done; /* Not a fatal error. */
1033 /* Yes, this is a race conditon... too bad. */
1034 if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
1035 DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
1036 "of %s to %s failed. Errno %s\n",
1037 SYSTEM_KRB5_CONF_PATH, newpath,
1038 strerror(errno) ));
1039 goto done; /* Not a fatal error. */
1042 if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1043 DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1044 "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1045 fname, strerror(errno) ));
1046 goto done; /* Not a fatal error. */
1050 #endif
1052 done:
1053 TALLOC_FREE(tmpname);
1054 TALLOC_FREE(dname);
1056 return result;
1058 #endif