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.
9
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.
14
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.
19
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/>.
22*/
23
24#include "includes.h"
25#include "smb_krb5.h"
26
27#ifdef HAVE_KRB5
28
29#define DEFAULT_KRB5_PORT 88
30
31#define LIBADS_CCACHE_NAME "MEMORY:libads"
32
33/*
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 ...
37*/
38static krb5_error_code
39kerb_prompter(krb5_context ctx, void *data,
40	       const char *name,
41	       const char *banner,
42	       int num_prompts,
43	       krb5_prompt prompts[])
44{
45	if (num_prompts == 0) return 0;
46
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;
55		}
56	}
57	return 0;
58}
59
60 static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
61						   NTSTATUS *nt_status)
62{
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;
68
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 */
74
75#ifdef DEVELOPER
76	dump_data(10, edata.data, edata.length);
77#endif /* DEVELOPER */
78
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;
83	}
84
85	if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
86		data_blob_free(&edata);
87		TALLOC_FREE(mem_ctx);
88		return False;
89	}
90
91	data_blob_free(&edata);
92
93	ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx, NULL,
94			&parsed_edata,
95			(ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
96	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
97		data_blob_free(&unwrapped_edata);
98		TALLOC_FREE(mem_ctx);
99		return False;
100	}
101
102	data_blob_free(&unwrapped_edata);
103
104	if (nt_status) {
105		*nt_status = parsed_edata.ntstatus;
106	}
107
108	TALLOC_FREE(mem_ctx);
109
110	return True;
111}
112
113 static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx,
114 								  krb5_get_init_creds_opt *opt,
115								  NTSTATUS *nt_status)
116{
117	bool ret = False;
118	krb5_error *error = NULL;
119
120#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
121	ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
122	if (ret) {
123		DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n",
124			error_message(ret)));
125		return False;
126	}
127#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
128
129	if (!error) {
130		DEBUG(1,("no krb5_error\n"));
131		return False;
132	}
133
134#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
135	if (!error->e_data) {
136#else
137	if (error->e_data.data == NULL) {
138#endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
139		DEBUG(1,("no edata in krb5_error\n"));
140		krb5_free_error(ctx, error);
141		return False;
142	}
143
144	ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
145
146	krb5_free_error(ctx, error);
147
148	return ret;
149}
150
151/*
152  simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
153  place in default cache location.
154  remus@snapserver.com
155*/
156int kerberos_kinit_password_ext(const char *principal,
157				const char *password,
158				int time_offset,
159				time_t *expire_time,
160				time_t *renew_till_time,
161				const char *cache_name,
162				bool request_pac,
163				bool add_netbios_addr,
164				time_t renewable_time,
165				NTSTATUS *ntstatus)
166{
167	krb5_context ctx = NULL;
168	krb5_error_code code = 0;
169	krb5_ccache cc = NULL;
170	krb5_principal me = NULL;
171	krb5_creds my_creds;
172	krb5_get_init_creds_opt *opt = NULL;
173	smb_krb5_addresses *addr = NULL;
174
175	ZERO_STRUCT(my_creds);
176
177	initialize_krb5_error_table();
178	if ((code = krb5_init_context(&ctx)))
179		goto out;
180
181	if (time_offset != 0) {
182		krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
183	}
184
185	DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
186			principal,
187			cache_name ? cache_name: krb5_cc_default_name(ctx),
188			getenv("KRB5_CONFIG")));
189
190	if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
191		goto out;
192	}
193
194	if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
195		goto out;
196	}
197
198	if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
199		goto out;
200	}
201
202	krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
203	krb5_get_init_creds_opt_set_forwardable(opt, True);
204#if 0
205	/* insane testing */
206	krb5_get_init_creds_opt_set_tkt_life(opt, 60);
207#endif
208
209#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_PAC_REQUEST
210	if (request_pac) {
211		if ((code = krb5_get_init_creds_opt_set_pac_request(ctx, opt, (krb5_boolean)request_pac))) {
212			goto out;
213		}
214	}
215#endif
216	if (add_netbios_addr) {
217		if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
218			goto out;
219		}
220		krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
221	}
222
223	if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password),
224						 kerb_prompter, CONST_DISCARD(char *,password),
225						 0, NULL, opt))) {
226		goto out;
227	}
228
229	if ((code = krb5_cc_initialize(ctx, cc, me))) {
230		goto out;
231	}
232
233	if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
234		goto out;
235	}
236
237	if (expire_time) {
238		*expire_time = (time_t) my_creds.times.endtime;
239	}
240
241	if (renew_till_time) {
242		*renew_till_time = (time_t) my_creds.times.renew_till;
243	}
244 out:
245	if (ntstatus) {
246
247		NTSTATUS status;
248
249		/* fast path */
250		if (code == 0) {
251			*ntstatus = NT_STATUS_OK;
252			goto cleanup;
253		}
254
255		/* try to get ntstatus code out of krb5_error when we have it
256		 * inside the krb5_get_init_creds_opt - gd */
257
258		if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
259			*ntstatus = status;
260			goto cleanup;
261		}
262
263		/* fall back to self-made-mapping */
264		*ntstatus = krb5_to_nt_status(code);
265	}
266
267 cleanup:
268	krb5_free_cred_contents(ctx, &my_creds);
269	if (me) {
270		krb5_free_principal(ctx, me);
271	}
272	if (addr) {
273		smb_krb5_free_addresses(ctx, addr);
274	}
275 	if (opt) {
276		smb_krb5_get_init_creds_opt_free(ctx, opt);
277	}
278	if (cc) {
279		krb5_cc_close(ctx, cc);
280	}
281	if (ctx) {
282		krb5_free_context(ctx);
283	}
284	return code;
285}
286
287
288
289/* run kinit to setup our ccache */
290int ads_kinit_password(ADS_STRUCT *ads)
291{
292	char *s;
293	int ret;
294	const char *account_name;
295	fstring acct_name;
296
297	if (ads->auth.flags & ADS_AUTH_USER_CREDS) {
298		account_name = ads->auth.user_name;
299		goto got_accountname;
300	}
301
302	if ( IS_DC ) {
303		/* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
304		account_name = lp_workgroup();
305	} else {
306		/* always use the sAMAccountName for security = domain */
307		/* global_myname()$@REA.LM */
308		if ( lp_security() == SEC_DOMAIN ) {
309			fstr_sprintf( acct_name, "%s$", global_myname() );
310			account_name = acct_name;
311		}
312		else
313			/* This looks like host/global_myname()@REA.LM */
314			account_name = ads->auth.user_name;
315	}
316
317 got_accountname:
318	if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
319		return KRB5_CC_NOMEM;
320	}
321
322	if (!ads->auth.password) {
323		SAFE_FREE(s);
324		return KRB5_LIBOS_CANTREADPWD;
325	}
326
327	ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
328			&ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable,
329			NULL);
330
331	if (ret) {
332		DEBUG(0,("kerberos_kinit_password %s failed: %s\n",
333			 s, error_message(ret)));
334	}
335	SAFE_FREE(s);
336	return ret;
337}
338
339int ads_kdestroy(const char *cc_name)
340{
341	krb5_error_code code;
342	krb5_context ctx = NULL;
343	krb5_ccache cc = NULL;
344
345	initialize_krb5_error_table();
346	if ((code = krb5_init_context (&ctx))) {
347		DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n",
348			error_message(code)));
349		return code;
350	}
351
352	if (!cc_name) {
353		if ((code = krb5_cc_default(ctx, &cc))) {
354			krb5_free_context(ctx);
355			return code;
356		}
357	} else {
358		if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) {
359			DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n",
360				  error_message(code)));
361			krb5_free_context(ctx);
362			return code;
363		}
364	}
365
366	if ((code = krb5_cc_destroy (ctx, cc))) {
367		DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n",
368			error_message(code)));
369	}
370
371	krb5_free_context (ctx);
372	return code;
373}
374
375/************************************************************************
376 Routine to fetch the salting principal for a service.  Active
377 Directory may use a non-obvious principal name to generate the salt
378 when it determines the key to use for encrypting tickets for a service,
379 and hopefully we detected that when we joined the domain.
380 ************************************************************************/
381
382static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
383{
384	char *key = NULL;
385	char *ret = NULL;
386
387	if (asprintf(&key, "%s/%s/enctype=%d",
388		     SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
389		return NULL;
390	}
391	ret = (char *)secrets_fetch(key, NULL);
392	SAFE_FREE(key);
393	return ret;
394}
395
396/************************************************************************
397 Return the standard DES salt key
398************************************************************************/
399
400char* kerberos_standard_des_salt( void )
401{
402	fstring salt;
403
404	fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
405	strlower_m( salt );
406	fstrcat( salt, lp_realm() );
407
408	return SMB_STRDUP( salt );
409}
410
411/************************************************************************
412************************************************************************/
413
414static char* des_salt_key( void )
415{
416	char *key;
417
418	if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
419		     lp_realm()) == -1) {
420		return NULL;
421	}
422
423	return key;
424}
425
426/************************************************************************
427************************************************************************/
428
429bool kerberos_secrets_store_des_salt( const char* salt )
430{
431	char* key;
432	bool ret;
433
434	if ( (key = des_salt_key()) == NULL ) {
435		DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
436		return False;
437	}
438
439	if ( !salt ) {
440		DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
441		secrets_delete( key );
442		return True;
443	}
444
445	DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
446
447	ret = secrets_store( key, salt, strlen(salt)+1 );
448
449	SAFE_FREE( key );
450
451	return ret;
452}
453
454/************************************************************************
455************************************************************************/
456
457char* kerberos_secrets_fetch_des_salt( void )
458{
459	char *salt, *key;
460
461	if ( (key = des_salt_key()) == NULL ) {
462		DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
463		return False;
464	}
465
466	salt = (char*)secrets_fetch( key, NULL );
467
468	SAFE_FREE( key );
469
470	return salt;
471}
472
473/************************************************************************
474 Routine to get the default realm from the kerberos credentials cache.
475 Caller must free if the return value is not NULL.
476************************************************************************/
477
478char *kerberos_get_default_realm_from_ccache( void )
479{
480	char *realm = NULL;
481	krb5_context ctx = NULL;
482	krb5_ccache cc = NULL;
483	krb5_principal princ = NULL;
484
485	initialize_krb5_error_table();
486	if (krb5_init_context(&ctx)) {
487		return NULL;
488	}
489
490	DEBUG(5,("kerberos_get_default_realm_from_ccache: "
491		"Trying to read krb5 cache: %s\n",
492		krb5_cc_default_name(ctx)));
493	if (krb5_cc_default(ctx, &cc)) {
494		DEBUG(0,("kerberos_get_default_realm_from_ccache: "
495			"failed to read default cache\n"));
496		goto out;
497	}
498	if (krb5_cc_get_principal(ctx, cc, &princ)) {
499		DEBUG(0,("kerberos_get_default_realm_from_ccache: "
500			"failed to get default principal\n"));
501		goto out;
502	}
503
504#if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
505	realm = SMB_STRDUP(krb5_principal_get_realm(ctx, princ));
506#elif defined(HAVE_KRB5_PRINC_REALM)
507	{
508		krb5_data *realm_data = krb5_princ_realm(ctx, princ);
509		realm = SMB_STRNDUP(realm_data->data, realm_data->length);
510	}
511#endif
512
513  out:
514
515	if (ctx) {
516		if (princ) {
517			krb5_free_principal(ctx, princ);
518		}
519		if (cc) {
520			krb5_cc_close(ctx, cc);
521		}
522		krb5_free_context(ctx);
523	}
524
525	return realm;
526}
527
528/************************************************************************
529 Routine to get the realm from a given DNS name. Returns malloc'ed memory.
530 Caller must free() if the return value is not NULL.
531************************************************************************/
532
533char *kerberos_get_realm_from_hostname(const char *hostname)
534{
535#if defined(HAVE_KRB5_GET_HOST_REALM) && defined(HAVE_KRB5_FREE_HOST_REALM)
536#if defined(HAVE_KRB5_REALM_TYPE)
537	/* Heimdal. */
538	krb5_realm *realm_list = NULL;
539#else
540	/* MIT */
541	char **realm_list = NULL;
542#endif
543	char *realm = NULL;
544	krb5_error_code kerr;
545	krb5_context ctx = NULL;
546
547	initialize_krb5_error_table();
548	if (krb5_init_context(&ctx)) {
549		return NULL;
550	}
551
552	kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
553	if (kerr != 0) {
554		DEBUG(3,("kerberos_get_realm_from_hostname %s: "
555			"failed %s\n",
556			hostname ? hostname : "(NULL)",
557			error_message(kerr) ));
558		goto out;
559	}
560
561	if (realm_list && realm_list[0]) {
562		realm = SMB_STRDUP(realm_list[0]);
563	}
564
565  out:
566
567	if (ctx) {
568		if (realm_list) {
569			krb5_free_host_realm(ctx, realm_list);
570			realm_list = NULL;
571		}
572		krb5_free_context(ctx);
573		ctx = NULL;
574	}
575	return realm;
576#else
577	return NULL;
578#endif
579}
580
581/************************************************************************
582 Routine to get the salting principal for this service.  This is
583 maintained for backwards compatibilty with releases prior to 3.0.24.
584 Since we store the salting principal string only at join, we may have
585 to look for the older tdb keys.  Caller must free if return is not null.
586 ************************************************************************/
587
588krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
589							krb5_principal host_princ,
590							int enctype)
591{
592	char *unparsed_name = NULL, *salt_princ_s = NULL;
593	krb5_principal ret_princ = NULL;
594
595	/* lookup new key first */
596
597	if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
598
599		/* look under the old key.  If this fails, just use the standard key */
600
601		if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
602			return (krb5_principal)NULL;
603		}
604		if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
605			/* fall back to host/machine.realm@REALM */
606			salt_princ_s = kerberos_standard_des_salt();
607		}
608	}
609
610	if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
611		ret_princ = NULL;
612	}
613
614	TALLOC_FREE(unparsed_name);
615	SAFE_FREE(salt_princ_s);
616
617	return ret_princ;
618}
619
620/************************************************************************
621 Routine to set the salting principal for this service.  Active
622 Directory may use a non-obvious principal name to generate the salt
623 when it determines the key to use for encrypting tickets for a service,
624 and hopefully we detected that when we joined the domain.
625 Setting principal to NULL deletes this entry.
626 ************************************************************************/
627
628bool kerberos_secrets_store_salting_principal(const char *service,
629					      int enctype,
630					      const char *principal)
631{
632	char *key = NULL;
633	bool ret = False;
634	krb5_context context = NULL;
635	krb5_principal princ = NULL;
636	char *princ_s = NULL;
637	char *unparsed_name = NULL;
638	krb5_error_code code;
639
640	if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
641		DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
642			  error_message(code)));
643		return False;
644	}
645	if (strchr_m(service, '@')) {
646		if (asprintf(&princ_s, "%s", service) == -1) {
647			goto out;
648		}
649	} else {
650		if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
651			goto out;
652		}
653	}
654
655	if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
656		goto out;
657
658	}
659	if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
660		goto out;
661	}
662
663	if (asprintf(&key, "%s/%s/enctype=%d",
664		     SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
665	    == -1) {
666		goto out;
667	}
668
669	if ((principal != NULL) && (strlen(principal) > 0)) {
670		ret = secrets_store(key, principal, strlen(principal) + 1);
671	} else {
672		ret = secrets_delete(key);
673	}
674
675 out:
676
677	SAFE_FREE(key);
678	SAFE_FREE(princ_s);
679	TALLOC_FREE(unparsed_name);
680
681	if (princ) {
682		krb5_free_principal(context, princ);
683	}
684
685	if (context) {
686		krb5_free_context(context);
687	}
688
689	return ret;
690}
691
692
693/************************************************************************
694************************************************************************/
695
696int kerberos_kinit_password(const char *principal,
697			    const char *password,
698			    int time_offset,
699			    const char *cache_name)
700{
701	return kerberos_kinit_password_ext(principal,
702					   password,
703					   time_offset,
704					   0,
705					   0,
706					   cache_name,
707					   False,
708					   False,
709					   0,
710					   NULL);
711}
712
713/************************************************************************
714************************************************************************/
715
716static char *print_kdc_line(char *mem_ctx,
717			const char *prev_line,
718			const struct sockaddr_storage *pss,
719			const char *kdc_name)
720{
721	char *kdc_str = NULL;
722
723	if (pss->ss_family == AF_INET) {
724		kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
725					prev_line,
726                                        print_canonical_sockaddr(mem_ctx, pss));
727	} else {
728		char addr[INET6_ADDRSTRLEN];
729		uint16_t port = get_sockaddr_port(pss);
730
731		DEBUG(10,("print_kdc_line: IPv6 case for kdc_name: %s, port: %d\n",
732			kdc_name, port));
733
734		if (port != 0 && port != DEFAULT_KRB5_PORT) {
735			/* Currently for IPv6 we can't specify a non-default
736			   krb5 port with an address, as this requires a ':'.
737			   Resolve to a name. */
738			char hostname[MAX_DNS_NAME_LENGTH];
739			int ret = sys_getnameinfo((const struct sockaddr *)pss,
740					sizeof(*pss),
741					hostname, sizeof(hostname),
742					NULL, 0,
743					NI_NAMEREQD);
744			if (ret) {
745				DEBUG(0,("print_kdc_line: can't resolve name "
746					"for kdc with non-default port %s. "
747					"Error %s\n.",
748					print_canonical_sockaddr(mem_ctx, pss),
749					gai_strerror(ret)));
750				return NULL;
751			}
752			/* Success, use host:port */
753			kdc_str = talloc_asprintf(mem_ctx,
754					"%s\tkdc = %s:%u\n",
755					prev_line,
756					hostname,
757					(unsigned int)port);
758		} else {
759
760			/* no krb5 lib currently supports "kdc = ipv6 address"
761			 * at all, so just fill in just the kdc_name if we have
762			 * it and let the krb5 lib figure out the appropriate
763			 * ipv6 address - gd */
764
765			if (kdc_name) {
766				kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
767						prev_line, kdc_name);
768			} else {
769				kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
770						prev_line,
771						print_sockaddr(addr,
772							sizeof(addr),
773							pss));
774			}
775		}
776	}
777	return kdc_str;
778}
779
780/************************************************************************
781 Create a string list of available kdc's, possibly searching by sitename.
782 Does DNS queries.
783
784 If "sitename" is given, the DC's in that site are listed first.
785
786************************************************************************/
787
788static char *get_kdc_ip_string(char *mem_ctx,
789		const char *realm,
790		const char *sitename,
791		struct sockaddr_storage *pss,
792		const char *kdc_name)
793{
794	int i;
795	struct ip_service *ip_srv_site = NULL;
796	struct ip_service *ip_srv_nonsite = NULL;
797	int count_site = 0;
798	int count_nonsite;
799	char *kdc_str = print_kdc_line(mem_ctx, "", pss, kdc_name);
800
801	if (kdc_str == NULL) {
802		return NULL;
803	}
804
805	/*
806	 * First get the KDC's only in this site, the rest will be
807	 * appended later
808	 */
809
810	if (sitename) {
811
812		get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
813
814		for (i = 0; i < count_site; i++) {
815			if (sockaddr_equal((struct sockaddr *)&ip_srv_site[i].ss,
816						   (struct sockaddr *)pss)) {
817				continue;
818			}
819			/* Append to the string - inefficient
820			 * but not done often. */
821			kdc_str = print_kdc_line(mem_ctx,
822						kdc_str,
823						&ip_srv_site[i].ss,
824						NULL);
825			if (!kdc_str) {
826				SAFE_FREE(ip_srv_site);
827				return NULL;
828			}
829		}
830	}
831
832	/* Get all KDC's. */
833
834	get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
835
836	for (i = 0; i < count_nonsite; i++) {
837		int j;
838
839		if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss, (struct sockaddr *)pss)) {
840			continue;
841		}
842
843		/* Ensure this isn't an IP already seen (YUK! this is n*n....) */
844		for (j = 0; j < count_site; j++) {
845			if (sockaddr_equal((struct sockaddr *)&ip_srv_nonsite[i].ss,
846						(struct sockaddr *)&ip_srv_site[j].ss)) {
847				break;
848			}
849			/* As the lists are sorted we can break early if nonsite > site. */
850			if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
851				break;
852			}
853		}
854		if (j != i) {
855			continue;
856		}
857
858		/* Append to the string - inefficient but not done often. */
859		kdc_str = print_kdc_line(mem_ctx,
860				kdc_str,
861				&ip_srv_nonsite[i].ss,
862				NULL);
863		if (!kdc_str) {
864			SAFE_FREE(ip_srv_site);
865			SAFE_FREE(ip_srv_nonsite);
866			return NULL;
867		}
868	}
869
870
871	SAFE_FREE(ip_srv_site);
872	SAFE_FREE(ip_srv_nonsite);
873
874	DEBUG(10,("get_kdc_ip_string: Returning %s\n",
875		kdc_str ));
876
877	return kdc_str;
878}
879
880/************************************************************************
881 Create  a specific krb5.conf file in the private directory pointing
882 at a specific kdc for a realm. Keyed off domain name. Sets
883 KRB5_CONFIG environment variable to point to this file. Must be
884 run as root or will fail (which is a good thing :-).
885************************************************************************/
886
887bool create_local_private_krb5_conf_for_domain(const char *realm,
888						const char *domain,
889						const char *sitename,
890						struct sockaddr_storage *pss,
891						const char *kdc_name)
892{
893	char *dname;
894	char *tmpname = NULL;
895	char *fname = NULL;
896	char *file_contents = NULL;
897	char *kdc_ip_string = NULL;
898	size_t flen = 0;
899	ssize_t ret;
900	int fd;
901	char *realm_upper = NULL;
902	bool result = false;
903
904	if (!lp_create_krb5_conf()) {
905		return false;
906	}
907
908	dname = lock_path("smb_krb5");
909	if (!dname) {
910		return false;
911	}
912	if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
913		DEBUG(0,("create_local_private_krb5_conf_for_domain: "
914			"failed to create directory %s. Error was %s\n",
915			dname, strerror(errno) ));
916		goto done;
917	}
918
919	tmpname = lock_path("smb_tmp_krb5.XXXXXX");
920	if (!tmpname) {
921		goto done;
922	}
923
924	fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
925	if (!fname) {
926		goto done;
927	}
928
929	DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
930		fname, realm, domain ));
931
932	realm_upper = talloc_strdup(fname, realm);
933	strupper_m(realm_upper);
934
935	kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss, kdc_name);
936	if (!kdc_ip_string) {
937		goto done;
938	}
939
940	file_contents = talloc_asprintf(fname,
941					"[libdefaults]\n\tdefault_realm = %s\n"
942					"\tdefault_tgs_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
943					"\tdefault_tkt_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
944					"\tpreferred_enctypes = RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
945					"[realms]\n\t%s = {\n"
946					"\t%s\t}\n",
947					realm_upper, realm_upper, kdc_ip_string);
948
949	if (!file_contents) {
950		goto done;
951	}
952
953	flen = strlen(file_contents);
954
955	fd = mkstemp(tmpname);
956	if (fd == -1) {
957		DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
958			" for file %s. Errno %s\n",
959			tmpname, strerror(errno) ));
960		goto done;
961	}
962
963	if (fchmod(fd, 0644)==-1) {
964		DEBUG(0,("create_local_private_krb5_conf_for_domain: fchmod failed for %s."
965			" Errno %s\n",
966			tmpname, strerror(errno) ));
967		unlink(tmpname);
968		close(fd);
969		goto done;
970	}
971
972	ret = write(fd, file_contents, flen);
973	if (flen != ret) {
974		DEBUG(0,("create_local_private_krb5_conf_for_domain: write failed,"
975			" returned %d (should be %u). Errno %s\n",
976			(int)ret, (unsigned int)flen, strerror(errno) ));
977		unlink(tmpname);
978		close(fd);
979		goto done;
980	}
981	if (close(fd)==-1) {
982		DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
983			" Errno %s\n", strerror(errno) ));
984		unlink(tmpname);
985		goto done;
986	}
987
988	if (rename(tmpname, fname) == -1) {
989		DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
990			"of %s to %s failed. Errno %s\n",
991			tmpname, fname, strerror(errno) ));
992		unlink(tmpname);
993		goto done;
994	}
995
996	DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
997		"file %s with realm %s KDC list = %s\n",
998		fname, realm_upper, kdc_ip_string));
999
1000	/* Set the environment variable to this file. */
1001	setenv("KRB5_CONFIG", fname, 1);
1002
1003	result = true;
1004
1005#if defined(OVERWRITE_SYSTEM_KRB5_CONF)
1006
1007#define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
1008	/* Insanity, sheer insanity..... */
1009
1010	if (strequal(realm, lp_realm())) {
1011		char linkpath[PATH_MAX+1];
1012		int lret;
1013
1014		lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
1015		if (lret != -1) {
1016			linkpath[lret] = '\0';
1017		}
1018
1019		if (lret != -1 || strcmp(linkpath, fname) == 0) {
1020			/* Symlink already exists. */
1021			goto done;
1022		}
1023
1024		/* Try and replace with a symlink. */
1025		if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1026			const char *newpath = SYSTEM_KRB5_CONF_PATH ## ".saved";
1027			if (errno != EEXIST) {
1028				DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
1029					"of %s to %s failed. Errno %s\n",
1030					fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
1031				goto done; /* Not a fatal error. */
1032			}
1033
1034			/* Yes, this is a race conditon... too bad. */
1035			if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
1036				DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
1037					"of %s to %s failed. Errno %s\n",
1038					SYSTEM_KRB5_CONF_PATH, newpath,
1039					strerror(errno) ));
1040				goto done; /* Not a fatal error. */
1041			}
1042
1043			if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
1044				DEBUG(0,("create_local_private_krb5_conf_for_domain: "
1045					"forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
1046					fname, strerror(errno) ));
1047				goto done; /* Not a fatal error. */
1048			}
1049		}
1050	}
1051#endif
1052
1053done:
1054	TALLOC_FREE(tmpname);
1055	TALLOC_FREE(dname);
1056
1057	return result;
1058}
1059#endif
1060