1/*
2   Unix SMB/CIFS implementation.
3   simple kerberos5 routines for active directory
4   Copyright (C) Andrew Tridgell 2001
5   Copyright (C) Luke Howard 2002-2003
6   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
7   Copyright (C) Guenther Deschner 2005
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22*/
23
24#define KRB5_PRIVATE    1       /* this file uses PRIVATE interfaces! */
25#define KRB5_DEPRECATED 1       /* this file uses DEPRECATED interfaces! */
26
27#include "includes.h"
28
29#ifdef HAVE_KRB5
30
31#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
32#define KRB5_KEY_TYPE(k)	((k)->keytype)
33#define KRB5_KEY_LENGTH(k)	((k)->keyvalue.length)
34#define KRB5_KEY_DATA(k)	((k)->keyvalue.data)
35#else
36#define	KRB5_KEY_TYPE(k)	((k)->enctype)
37#define KRB5_KEY_LENGTH(k)	((k)->length)
38#define KRB5_KEY_DATA(k)	((k)->contents)
39#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
40
41/**************************************************************
42 Wrappers around kerberos string functions that convert from
43 utf8 -> unix charset and vica versa.
44**************************************************************/
45
46/**************************************************************
47 krb5_parse_name that takes a UNIX charset.
48**************************************************************/
49
50 krb5_error_code smb_krb5_parse_name(krb5_context context,
51				const char *name, /* in unix charset */
52				krb5_principal *principal)
53{
54	krb5_error_code ret;
55	char *utf8_name;
56
57	if (push_utf8_allocate(&utf8_name, name) == (size_t)-1) {
58		return ENOMEM;
59	}
60
61	ret = krb5_parse_name(context, utf8_name, principal);
62	SAFE_FREE(utf8_name);
63	return ret;
64}
65
66#ifdef HAVE_KRB5_PARSE_NAME_NOREALM
67/**************************************************************
68 krb5_parse_name_norealm that takes a UNIX charset.
69**************************************************************/
70
71static krb5_error_code smb_krb5_parse_name_norealm_conv(krb5_context context,
72				const char *name, /* in unix charset */
73				krb5_principal *principal)
74{
75	krb5_error_code ret;
76	char *utf8_name;
77
78	*principal = NULL;
79	if (push_utf8_allocate(&utf8_name, name) == (size_t)-1) {
80		return ENOMEM;
81	}
82
83	ret = krb5_parse_name_norealm(context, utf8_name, principal);
84	SAFE_FREE(utf8_name);
85	return ret;
86}
87#endif
88
89/**************************************************************
90 krb5_parse_name that returns a UNIX charset name. Must
91 be freed with normal free() call.
92**************************************************************/
93
94 krb5_error_code smb_krb5_unparse_name(krb5_context context,
95					krb5_const_principal principal,
96					char **unix_name)
97{
98	krb5_error_code ret;
99	char *utf8_name;
100
101	*unix_name = NULL;
102	ret = krb5_unparse_name(context, principal, &utf8_name);
103	if (ret) {
104		return ret;
105	}
106
107	if (pull_utf8_allocate(unix_name, utf8_name)==-1) {
108		krb5_free_unparsed_name(context, utf8_name);
109		return ENOMEM;
110	}
111	krb5_free_unparsed_name(context, utf8_name);
112	return 0;
113}
114
115#ifndef HAVE_KRB5_SET_REAL_TIME
116/*
117 * This function is not in the Heimdal mainline.
118 */
119 krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds)
120{
121	krb5_error_code ret;
122	int32_t sec, usec;
123
124	ret = krb5_us_timeofday(context, &sec, &usec);
125	if (ret)
126		return ret;
127
128	context->kdc_sec_offset = seconds - sec;
129	context->kdc_usec_offset = microseconds - usec;
130
131	return 0;
132}
133#endif
134
135#if !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
136
137#if defined(HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES)
138
139/* With MIT kerberos, we should use krb5_set_default_tgs_enctypes in preference
140 * to krb5_set_default_tgs_ktypes. See
141 *         http://lists.samba.org/archive/samba-technical/2006-July/048271.html
142 *
143 * If the MIT libraries are not exporting internal symbols, we will end up in
144 * this branch, which is correct. Otherwise we will continue to use the
145 * internal symbol
146 */
147 krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
148{
149    return krb5_set_default_tgs_enctypes(ctx, enc);
150}
151
152#elif defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES)
153
154/* Heimdal */
155 krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
156{
157	return krb5_set_default_in_tkt_etypes(ctx, enc);
158}
159
160#endif /* HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES */
161
162#endif /* HAVE_KRB5_SET_DEFAULT_TGS_KTYPES */
163
164#if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
165/* HEIMDAL */
166 void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
167{
168	pkaddr->addr_type = KRB5_ADDRESS_INET;
169	pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
170	pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
171}
172#elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
173/* MIT */
174 void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr)
175{
176	pkaddr->addrtype = ADDRTYPE_INET;
177	pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
178	pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
179}
180#else
181#error UNKNOWN_ADDRTYPE
182#endif
183
184#if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
185 int create_kerberos_key_from_string_direct(krb5_context context,
186					krb5_principal host_princ,
187					krb5_data *password,
188					krb5_keyblock *key,
189					krb5_enctype enctype)
190{
191	int ret;
192	krb5_data salt;
193	krb5_encrypt_block eblock;
194
195	ret = krb5_principal2salt(context, host_princ, &salt);
196	if (ret) {
197		DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
198		return ret;
199	}
200	krb5_use_enctype(context, &eblock, enctype);
201	ret = krb5_string_to_key(context, &eblock, key, password, &salt);
202	SAFE_FREE(salt.data);
203	return ret;
204}
205#elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
206 int create_kerberos_key_from_string_direct(krb5_context context,
207					krb5_principal host_princ,
208					krb5_data *password,
209					krb5_keyblock *key,
210					krb5_enctype enctype)
211{
212	int ret;
213	krb5_salt salt;
214
215	ret = krb5_get_pw_salt(context, host_princ, &salt);
216	if (ret) {
217		DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
218		return ret;
219	}
220
221	ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, salt, key);
222	krb5_free_salt(context, salt);
223	return ret;
224}
225#else
226#error UNKNOWN_CREATE_KEY_FUNCTIONS
227#endif
228
229 int create_kerberos_key_from_string(krb5_context context,
230					krb5_principal host_princ,
231					krb5_data *password,
232					krb5_keyblock *key,
233					krb5_enctype enctype)
234{
235	krb5_principal salt_princ = NULL;
236	int ret;
237	/*
238	 * Check if we've determined that the KDC is salting keys for this
239	 * principal/enctype in a non-obvious way.  If it is, try to match
240	 * its behavior.
241	 */
242	salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
243	ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype);
244	if (salt_princ) {
245		krb5_free_principal(context, salt_princ);
246	}
247	return ret;
248}
249
250#if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
251 krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
252					    krb5_enctype **enctypes)
253{
254	return krb5_get_permitted_enctypes(context, enctypes);
255}
256#elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
257 krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
258					    krb5_enctype **enctypes)
259{
260	return krb5_get_default_in_tkt_etypes(context, enctypes);
261}
262#else
263#error UNKNOWN_GET_ENCTYPES_FUNCTIONS
264#endif
265
266#if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
267 krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
268					krb5_auth_context auth_context,
269					krb5_keyblock *keyblock)
270{
271	return krb5_auth_con_setkey(context, auth_context, keyblock);
272}
273#endif
274
275BOOL unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_pac_data)
276{
277	DATA_BLOB pac_contents;
278	ASN1_DATA data;
279	int data_type;
280
281	if (!auth_data->length) {
282		return False;
283	}
284
285	asn1_load(&data, *auth_data);
286	asn1_start_tag(&data, ASN1_SEQUENCE(0));
287	asn1_start_tag(&data, ASN1_SEQUENCE(0));
288	asn1_start_tag(&data, ASN1_CONTEXT(0));
289	asn1_read_Integer(&data, &data_type);
290
291	if (data_type != KRB5_AUTHDATA_WIN2K_PAC ) {
292		DEBUG(10,("authorization data is not a Windows PAC (type: %d)\n", data_type));
293		asn1_free(&data);
294		return False;
295	}
296
297	asn1_end_tag(&data);
298	asn1_start_tag(&data, ASN1_CONTEXT(1));
299	asn1_read_OctetString(&data, &pac_contents);
300	asn1_end_tag(&data);
301	asn1_end_tag(&data);
302	asn1_end_tag(&data);
303	asn1_free(&data);
304
305	*unwrapped_pac_data = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length);
306
307	data_blob_free(&pac_contents);
308
309	return True;
310}
311
312 BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt)
313{
314	DATA_BLOB auth_data_wrapped;
315	BOOL got_auth_data_pac = False;
316	int i;
317
318#if defined(HAVE_KRB5_TKT_ENC_PART2)
319	if (tkt->enc_part2 && tkt->enc_part2->authorization_data &&
320	    tkt->enc_part2->authorization_data[0] &&
321	    tkt->enc_part2->authorization_data[0]->length)
322	{
323		for (i = 0; tkt->enc_part2->authorization_data[i] != NULL; i++) {
324
325			if (tkt->enc_part2->authorization_data[i]->ad_type !=
326			    KRB5_AUTHDATA_IF_RELEVANT) {
327				DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n",
328					tkt->enc_part2->authorization_data[i]->ad_type));
329				continue;
330			}
331
332			auth_data_wrapped = data_blob(tkt->enc_part2->authorization_data[i]->contents,
333						      tkt->enc_part2->authorization_data[i]->length);
334
335			/* check if it is a PAC */
336			got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data);
337			data_blob_free(&auth_data_wrapped);
338
339			if (!got_auth_data_pac) {
340				continue;
341			}
342		}
343
344		return got_auth_data_pac;
345	}
346
347#else
348	if (tkt->ticket.authorization_data &&
349	    tkt->ticket.authorization_data->len)
350	{
351		for (i = 0; i < tkt->ticket.authorization_data->len; i++) {
352
353			if (tkt->ticket.authorization_data->val[i].ad_type !=
354			    KRB5_AUTHDATA_IF_RELEVANT) {
355				DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n",
356					tkt->ticket.authorization_data->val[i].ad_type));
357				continue;
358			}
359
360			auth_data_wrapped = data_blob(tkt->ticket.authorization_data->val[i].ad_data.data,
361						      tkt->ticket.authorization_data->val[i].ad_data.length);
362
363			/* check if it is a PAC */
364			got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data);
365			data_blob_free(&auth_data_wrapped);
366
367			if (!got_auth_data_pac) {
368				continue;
369			}
370		}
371
372		return got_auth_data_pac;
373	}
374#endif
375	return False;
376}
377
378 krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt)
379{
380#if defined(HAVE_KRB5_TKT_ENC_PART2)
381	return tkt->enc_part2->client;
382#else
383	return tkt->client;
384#endif
385}
386
387#if !defined(HAVE_KRB5_LOCATE_KDC)
388
389/* krb5_locate_kdc is an internal MIT symbol. MIT are not yet willing to commit
390 * to a public interface for this functionality, so we have to be able to live
391 * without it if the MIT libraries are hiding their internal symbols.
392 */
393
394#if defined(KRB5_KRBHST_INIT)
395/* Heimdal */
396 krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters)
397{
398	krb5_krbhst_handle hnd;
399	krb5_krbhst_info *hinfo;
400	krb5_error_code rc;
401	int num_kdcs, i;
402	struct sockaddr *sa;
403	struct addrinfo *ai;
404
405	*addr_pp = NULL;
406	*naddrs = 0;
407
408	rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd);
409	if (rc) {
410		DEBUG(0, ("smb_krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc)));
411		return rc;
412	}
413
414	for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++)
415		;
416
417	krb5_krbhst_reset(ctx, hnd);
418
419	if (!num_kdcs) {
420		DEBUG(0, ("smb_krb5_locate_kdc: zero kdcs found !\n"));
421		krb5_krbhst_free(ctx, hnd);
422		return -1;
423	}
424
425	sa = SMB_MALLOC_ARRAY( struct sockaddr, num_kdcs );
426	if (!sa) {
427		DEBUG(0, ("smb_krb5_locate_kdc: malloc failed\n"));
428		krb5_krbhst_free(ctx, hnd);
429		naddrs = 0;
430		return -1;
431	}
432
433	memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs );
434
435	for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
436
437#if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO)
438		rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai);
439		if (rc) {
440			DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc)));
441			continue;
442		}
443#endif
444		if (hinfo->ai && hinfo->ai->ai_family == AF_INET)
445			memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr));
446	}
447
448	krb5_krbhst_free(ctx, hnd);
449
450	*naddrs = num_kdcs;
451	*addr_pp = sa;
452	return 0;
453}
454
455#else /* ! defined(KRB5_KRBHST_INIT) */
456
457 krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm,
458		struct sockaddr **addr_pp, int *naddrs, int get_masters)
459{
460	DEBUG(0, ("unable to explicitly locate the KDC on this platform\n"));
461	return KRB5_KDC_UNREACH;
462}
463
464#endif /* KRB5_KRBHST_INIT */
465
466#else /* ! HAVE_KRB5_LOCATE_KDC */
467
468 krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm,
469		struct sockaddr **addr_pp, int *naddrs, int get_masters)
470{
471	return krb5_locate_kdc(ctx, realm, addr_pp, naddrs, get_masters);
472}
473
474#endif /* HAVE_KRB5_LOCATE_KDC */
475
476#if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
477 void krb5_free_unparsed_name(krb5_context context, char *val)
478{
479	SAFE_FREE(val);
480}
481#endif
482
483 void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
484{
485#if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
486	if (pdata->data) {
487		krb5_free_data_contents(context, pdata);
488	}
489#else
490	SAFE_FREE(pdata->data);
491#endif
492}
493
494 void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
495{
496#if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
497	KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
498#elif defined(HAVE_KRB5_SESSION_IN_CREDS)
499	KRB5_KEY_TYPE((&pcreds->session)) = enctype;
500#else
501#error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
502#endif
503}
504
505 BOOL kerberos_compatible_enctypes(krb5_context context,
506				  krb5_enctype enctype1,
507				  krb5_enctype enctype2)
508{
509#if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
510	krb5_boolean similar = 0;
511
512	krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
513	return similar ? True : False;
514#elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
515	return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
516#endif
517}
518
519static BOOL ads_cleanup_expired_creds(krb5_context context,
520				      krb5_ccache  ccache,
521				      krb5_creds  *credsp)
522{
523	krb5_error_code retval;
524	const char *cc_type = krb5_cc_get_type(context, ccache);
525
526	DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
527		  cc_type, krb5_cc_get_name(context, ccache),
528		  http_timestring(credsp->times.endtime)));
529
530	/* we will probably need new tickets if the current ones
531	   will expire within 10 seconds.
532	*/
533	if (credsp->times.endtime >= (time(NULL) + 10))
534		return False;
535
536	/* heimdal won't remove creds from a file ccache, and
537	   perhaps we shouldn't anyway, since internally we
538	   use memory ccaches, and a FILE one probably means that
539	   we're using creds obtained outside of our exectuable
540	*/
541	if (strequal(cc_type, "FILE")) {
542		DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
543		return False;
544	}
545
546	retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
547	if (retval) {
548		DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
549			  error_message(retval)));
550		/* If we have an error in this, we want to display it,
551		   but continue as though we deleted it */
552	}
553	return True;
554}
555
556/*
557  we can't use krb5_mk_req because w2k wants the service to be in a particular format
558*/
559static krb5_error_code ads_krb5_mk_req(krb5_context context,
560				       krb5_auth_context *auth_context,
561				       const krb5_flags ap_req_options,
562				       const char *principal,
563				       krb5_ccache ccache,
564				       krb5_data *outbuf,
565				       time_t *expire_time)
566{
567	krb5_error_code 	  retval;
568	krb5_principal	  server;
569	krb5_creds 		* credsp;
570	krb5_creds 		  creds;
571	krb5_data in_data;
572	BOOL creds_ready = False;
573	int i = 0, maxtries = 3;
574
575	retval = smb_krb5_parse_name(context, principal, &server);
576	if (retval) {
577		DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
578		return retval;
579	}
580
581	/* obtain ticket & session key */
582	ZERO_STRUCT(creds);
583	if ((retval = krb5_copy_principal(context, server, &creds.server))) {
584		DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
585			 error_message(retval)));
586		goto cleanup_princ;
587	}
588
589	if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
590		/* This can commonly fail on smbd startup with no ticket in the cache.
591		 * Report at higher level than 1. */
592		DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
593			 error_message(retval)));
594		goto cleanup_creds;
595	}
596
597	while (!creds_ready && (i < maxtries)) {
598
599		if ((retval = krb5_get_credentials(context, 0, ccache,
600						   &creds, &credsp))) {
601			DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n",
602				 principal, error_message(retval)));
603			goto cleanup_creds;
604		}
605
606		/* cope with ticket being in the future due to clock skew */
607		if ((unsigned)credsp->times.starttime > time(NULL)) {
608			time_t t = time(NULL);
609			int time_offset =(int)((unsigned)credsp->times.starttime-t);
610			DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
611			krb5_set_real_time(context, t + time_offset + 1, 0);
612		}
613
614		if (!ads_cleanup_expired_creds(context, ccache, credsp)) {
615			creds_ready = True;
616		}
617
618		i++;
619	}
620
621	DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
622		  principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache),
623		  http_timestring((unsigned)credsp->times.endtime),
624		  (unsigned)credsp->times.endtime));
625
626	if (expire_time) {
627		*expire_time = (time_t)credsp->times.endtime;
628	}
629
630	in_data.length = 0;
631	retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
632				      &in_data, credsp, outbuf);
633	if (retval) {
634		DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n",
635			 error_message(retval)));
636	}
637
638	krb5_free_creds(context, credsp);
639
640cleanup_creds:
641	krb5_free_cred_contents(context, &creds);
642
643cleanup_princ:
644	krb5_free_principal(context, server);
645
646	return retval;
647}
648
649/*
650  get a kerberos5 ticket for the given service
651*/
652int cli_krb5_get_ticket(const char *principal, time_t time_offset,
653			DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
654			uint32 extra_ap_opts, const char *ccname,
655			time_t *tgs_expire)
656
657{
658	krb5_error_code retval;
659	krb5_data packet;
660	krb5_context context = NULL;
661	krb5_ccache ccdef = NULL;
662	krb5_auth_context auth_context = NULL;
663	krb5_enctype enc_types[] = {
664#ifdef ENCTYPE_ARCFOUR_HMAC
665		ENCTYPE_ARCFOUR_HMAC,
666#endif
667		ENCTYPE_DES_CBC_MD5,
668		ENCTYPE_DES_CBC_CRC,
669		ENCTYPE_NULL};
670
671	initialize_krb5_error_table();
672	retval = krb5_init_context(&context);
673	if (retval) {
674		DEBUG(1,("cli_krb5_get_ticket: krb5_init_context failed (%s)\n",
675			 error_message(retval)));
676		goto failed;
677	}
678
679	if (time_offset != 0) {
680		krb5_set_real_time(context, time(NULL) + time_offset, 0);
681	}
682
683	if ((retval = krb5_cc_resolve(context, ccname ?
684			ccname : krb5_cc_default_name(context), &ccdef))) {
685		DEBUG(1,("cli_krb5_get_ticket: krb5_cc_default failed (%s)\n",
686			 error_message(retval)));
687		goto failed;
688	}
689
690	if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
691		DEBUG(1,("cli_krb5_get_ticket: krb5_set_default_tgs_ktypes failed (%s)\n",
692			 error_message(retval)));
693		goto failed;
694	}
695
696	if ((retval = ads_krb5_mk_req(context,
697					&auth_context,
698					AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
699					principal,
700					ccdef, &packet,
701					tgs_expire))) {
702		goto failed;
703	}
704
705	get_krb5_smb_session_key(context, auth_context, session_key_krb5, False);
706
707	*ticket = data_blob(packet.data, packet.length);
708
709 	kerberos_free_data_contents(context, &packet);
710
711failed:
712
713	if ( context ) {
714		if (ccdef)
715			krb5_cc_close(context, ccdef);
716		if (auth_context)
717			krb5_auth_con_free(context, auth_context);
718		krb5_free_context(context);
719	}
720
721	return retval;
722}
723
724 BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote)
725 {
726	krb5_keyblock *skey;
727	krb5_error_code err;
728	BOOL ret = False;
729
730	if (remote)
731		err = krb5_auth_con_getremotesubkey(context, auth_context, &skey);
732	else
733		err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey);
734	if (err == 0 && skey != NULL) {
735		DEBUG(10, ("Got KRB5 session key of length %d\n",  (int)KRB5_KEY_LENGTH(skey)));
736		*session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey));
737		dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length);
738
739		ret = True;
740
741		krb5_free_keyblock(context, skey);
742	} else {
743		DEBUG(10, ("KRB5 error getting session key %d\n", err));
744	}
745
746	return ret;
747 }
748
749
750#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
751 const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
752
753 const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
754{
755	static krb5_data kdata;
756
757	kdata.data = (char *)krb5_principal_get_comp_string(context, principal, i);
758	kdata.length = strlen((const char *)kdata.data);
759	return &kdata;
760}
761#endif
762
763 krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry)
764{
765#if defined(HAVE_KRB5_KT_FREE_ENTRY)
766	return krb5_kt_free_entry(context, kt_entry);
767#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
768	return krb5_free_keytab_entry_contents(context, kt_entry);
769#else
770#error UNKNOWN_KT_FREE_FUNCTION
771#endif
772}
773
774 void smb_krb5_checksum_from_pac_sig(krb5_checksum *cksum,
775				    PAC_SIGNATURE_DATA *sig)
776{
777#ifdef HAVE_CHECKSUM_IN_KRB5_CHECKSUM
778	cksum->cksumtype	= (krb5_cksumtype)sig->type;
779	cksum->checksum.length	= sig->signature.buf_len;
780	cksum->checksum.data	= sig->signature.buffer;
781#else
782	cksum->checksum_type	= (krb5_cksumtype)sig->type;
783	cksum->length		= sig->signature.buf_len;
784	cksum->contents		= sig->signature.buffer;
785#endif
786}
787
788 krb5_error_code smb_krb5_verify_checksum(krb5_context context,
789					 krb5_keyblock *keyblock,
790					 krb5_keyusage usage,
791					 krb5_checksum *cksum,
792					 uint8 *data,
793					 size_t length)
794{
795	krb5_error_code ret;
796
797	/* verify the checksum */
798
799	/* welcome to the wonderful world of samba's kerberos abstraction layer:
800	 *
801	 * function			heimdal 0.6.1rc3	heimdal 0.7	MIT krb 1.4.2
802	 * -----------------------------------------------------------------------------
803	 * krb5_c_verify_checksum	-			works		works
804	 * krb5_verify_checksum		works (6 args)		works (6 args)	broken (7 args)
805	 */
806
807#if defined(HAVE_KRB5_C_VERIFY_CHECKSUM)
808	{
809		krb5_boolean checksum_valid = False;
810		krb5_data input;
811
812		input.data = (char *)data;
813		input.length = length;
814
815		ret = krb5_c_verify_checksum(context,
816					     keyblock,
817					     usage,
818					     &input,
819					     cksum,
820					     &checksum_valid);
821		if (ret) {
822			DEBUG(3,("smb_krb5_verify_checksum: krb5_c_verify_checksum() failed: %s\n",
823				error_message(ret)));
824			return ret;
825		}
826
827		if (!checksum_valid)
828			ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
829	}
830
831#elif KRB5_VERIFY_CHECKSUM_ARGS == 6 && defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CRYPTO) && defined(HAVE_KRB5_CRYPTO_DESTROY)
832
833	/* Warning: MIT's krb5_verify_checksum cannot be used as it will use a key
834	 * without enctype and it ignores any key_usage types - Guenther */
835
836	{
837
838		krb5_crypto crypto;
839		ret = krb5_crypto_init(context,
840				       keyblock,
841				       0,
842				       &crypto);
843		if (ret) {
844			DEBUG(0,("smb_krb5_verify_checksum: krb5_crypto_init() failed: %s\n",
845				error_message(ret)));
846			return ret;
847		}
848
849		ret = krb5_verify_checksum(context,
850					   crypto,
851					   usage,
852					   data,
853					   length,
854					   cksum);
855
856		krb5_crypto_destroy(context, crypto);
857	}
858
859#else
860#error UNKNOWN_KRB5_VERIFY_CHECKSUM_FUNCTION
861#endif
862
863	return ret;
864}
865
866 time_t get_authtime_from_tkt(krb5_ticket *tkt)
867{
868#if defined(HAVE_KRB5_TKT_ENC_PART2)
869	return tkt->enc_part2->times.authtime;
870#else
871	return tkt->ticket.authtime;
872#endif
873}
874
875#ifdef HAVE_KRB5_DECODE_AP_REQ	/* Heimdal */
876static int get_kvno_from_ap_req(krb5_ap_req *ap_req)
877{
878#ifdef HAVE_TICKET_POINTER_IN_KRB5_AP_REQ /* MIT */
879	if (ap_req->ticket->enc_part.kvno)
880		return ap_req->ticket->enc_part.kvno;
881#else /* Heimdal */
882	if (ap_req->ticket.enc_part.kvno)
883		return *ap_req->ticket.enc_part.kvno;
884#endif
885	return 0;
886}
887
888static krb5_enctype get_enctype_from_ap_req(krb5_ap_req *ap_req)
889{
890#ifdef HAVE_ETYPE_IN_ENCRYPTEDDATA /* Heimdal */
891	return ap_req->ticket.enc_part.etype;
892#else /* MIT */
893	return ap_req->ticket->enc_part.enctype;
894#endif
895}
896#endif	/* HAVE_KRB5_DECODE_AP_REQ */
897
898static krb5_error_code
899get_key_from_keytab(krb5_context context,
900		    krb5_const_principal server,
901		    krb5_enctype enctype,
902		    krb5_kvno kvno,
903		    krb5_keyblock **out_key)
904{
905	krb5_keytab_entry entry;
906	krb5_error_code ret;
907	krb5_keytab keytab;
908	char *name = NULL;
909
910	/* We have to open a new keytab handle here, as MIT does
911	   an implicit open/getnext/close on krb5_kt_get_entry. We
912	   may be in the middle of a keytab enumeration when this is
913	   called. JRA. */
914
915	ret = krb5_kt_default(context, &keytab);
916	if (ret) {
917		DEBUG(0,("get_key_from_keytab: failed to open keytab: %s\n", error_message(ret)));
918		return ret;
919	}
920
921	if ( DEBUGLEVEL >= 10 ) {
922		if (smb_krb5_unparse_name(context, server, &name) == 0) {
923			DEBUG(10,("get_key_from_keytab: will look for kvno %d, enctype %d and name: %s\n",
924				kvno, enctype, name));
925			SAFE_FREE(name);
926		}
927	}
928
929	ret = krb5_kt_get_entry(context,
930				keytab,
931				server,
932				kvno,
933				enctype,
934				&entry);
935
936	if (ret) {
937		DEBUG(0,("get_key_from_keytab: failed to retrieve key: %s\n", error_message(ret)));
938		goto out;
939	}
940
941#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK /* Heimdal */
942	ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
943#elif defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) /* MIT */
944	ret = krb5_copy_keyblock(context, &entry.key, out_key);
945#else
946#error UNKNOWN_KRB5_KEYTAB_ENTRY_FORMAT
947#endif
948
949	if (ret) {
950		DEBUG(0,("get_key_from_keytab: failed to copy key: %s\n", error_message(ret)));
951		goto out;
952	}
953
954	smb_krb5_kt_free_entry(context, &entry);
955
956out:
957	krb5_kt_close(context, keytab);
958	return ret;
959}
960
961/* Prototypes */
962
963 krb5_error_code smb_krb5_get_keyinfo_from_ap_req(krb5_context context,
964						 const krb5_data *inbuf,
965						 krb5_kvno *kvno,
966						 krb5_enctype *enctype)
967{
968#ifdef HAVE_KRB5_DECODE_AP_REQ /* Heimdal */
969	{
970		krb5_error_code ret;
971		krb5_ap_req ap_req;
972
973		ret = krb5_decode_ap_req(context, inbuf, &ap_req);
974		if (ret)
975			return ret;
976
977		*kvno = get_kvno_from_ap_req(&ap_req);
978		*enctype = get_enctype_from_ap_req(&ap_req);
979
980 		free_AP_REQ(&ap_req);
981 		return 0;
982	}
983#endif
984
985 	/* Possibly not an appropriate error code. */
986 	return KRB5KDC_ERR_BADOPTION;
987}
988
989 krb5_error_code krb5_rd_req_return_keyblock_from_keytab(krb5_context context,
990							krb5_auth_context *auth_context,
991							const krb5_data *inbuf,
992							krb5_const_principal server,
993							krb5_keytab keytab,
994							krb5_flags *ap_req_options,
995							krb5_ticket **ticket,
996							krb5_keyblock **keyblock)
997{
998	krb5_error_code ret;
999	krb5_kvno kvno;
1000	krb5_enctype enctype;
1001	krb5_keyblock *local_keyblock;
1002
1003	ret = krb5_rd_req(context,
1004			  auth_context,
1005			  inbuf,
1006			  server,
1007			  keytab,
1008			  ap_req_options,
1009			  ticket);
1010	if (ret) {
1011		return ret;
1012	}
1013
1014#ifdef KRB5_TICKET_HAS_KEYINFO
1015	enctype = (*ticket)->enc_part.enctype;
1016	kvno = (*ticket)->enc_part.kvno;
1017#else
1018	ret = smb_krb5_get_keyinfo_from_ap_req(context, inbuf, &kvno, &enctype);
1019	if (ret) {
1020		return ret;
1021	}
1022#endif
1023
1024	ret = get_key_from_keytab(context,
1025				  server,
1026				  enctype,
1027				  kvno,
1028				  &local_keyblock);
1029	if (ret) {
1030		DEBUG(0,("krb5_rd_req_return_keyblock_from_keytab: failed to call get_key_from_keytab\n"));
1031		goto out;
1032	}
1033
1034out:
1035	if (ret && local_keyblock != NULL) {
1036	        krb5_free_keyblock(context, local_keyblock);
1037	} else {
1038		*keyblock = local_keyblock;
1039	}
1040
1041	return ret;
1042}
1043
1044 krb5_error_code smb_krb5_parse_name_norealm(krb5_context context,
1045					    const char *name,
1046					    krb5_principal *principal)
1047{
1048#ifdef HAVE_KRB5_PARSE_NAME_NOREALM
1049	return smb_krb5_parse_name_norealm_conv(context, name, principal);
1050#endif
1051
1052	/* we are cheating here because parse_name will in fact set the realm.
1053	 * We don't care as the only caller of smb_krb5_parse_name_norealm
1054	 * ignores the realm anyway when calling
1055	 * smb_krb5_principal_compare_any_realm later - Guenther */
1056
1057	return smb_krb5_parse_name(context, name, principal);
1058}
1059
1060 BOOL smb_krb5_principal_compare_any_realm(krb5_context context,
1061					  krb5_const_principal princ1,
1062					  krb5_const_principal princ2)
1063{
1064#ifdef HAVE_KRB5_PRINCIPAL_COMPARE_ANY_REALM
1065
1066	return krb5_principal_compare_any_realm(context, princ1, princ2);
1067
1068/* krb5_princ_size is a macro in MIT */
1069#elif defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
1070
1071	int i, len1, len2;
1072	const krb5_data *p1, *p2;
1073
1074	len1 = krb5_princ_size(context, princ1);
1075	len2 = krb5_princ_size(context, princ2);
1076
1077	if (len1 != len2)
1078		return False;
1079
1080	for (i = 0; i < len1; i++) {
1081
1082		p1 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ1), i);
1083		p2 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ2), i);
1084
1085		if (p1->length != p2->length ||	memcmp(p1->data, p2->data, p1->length))
1086			return False;
1087	}
1088
1089	return True;
1090#else
1091#error NO_SUITABLE_PRINCIPAL_COMPARE_FUNCTION
1092#endif
1093}
1094
1095 krb5_error_code smb_krb5_renew_ticket(const char *ccache_string,	/* FILE:/tmp/krb5cc_0 */
1096				       const char *client_string,	/* gd@BER.SUSE.DE */
1097				       const char *service_string,	/* krbtgt/BER.SUSE.DE@BER.SUSE.DE */
1098				       time_t *expire_time)
1099{
1100	krb5_error_code ret;
1101	krb5_context context = NULL;
1102	krb5_ccache ccache = NULL;
1103	krb5_principal client = NULL;
1104
1105	initialize_krb5_error_table();
1106	ret = krb5_init_context(&context);
1107	if (ret) {
1108		goto done;
1109	}
1110
1111	if (!ccache_string) {
1112		ccache_string = krb5_cc_default_name(context);
1113	}
1114
1115	DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
1116
1117	/* FIXME: we should not fall back to defaults */
1118	ret = krb5_cc_resolve(context, CONST_DISCARD(char *, ccache_string), &ccache);
1119	if (ret) {
1120		goto done;
1121	}
1122
1123#ifdef HAVE_KRB5_GET_RENEWED_CREDS	/* MIT */
1124	{
1125		krb5_creds creds;
1126
1127		if (client_string) {
1128			ret = smb_krb5_parse_name(context, client_string, &client);
1129			if (ret) {
1130				goto done;
1131			}
1132		} else {
1133			ret = krb5_cc_get_principal(context, ccache, &client);
1134			if (ret) {
1135				goto done;
1136			}
1137		}
1138
1139		ret = krb5_get_renewed_creds(context, &creds, client, ccache, CONST_DISCARD(char *, service_string));
1140		if (ret) {
1141			DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
1142			goto done;
1143		}
1144
1145		/* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
1146		ret = krb5_cc_initialize(context, ccache, client);
1147		if (ret) {
1148			goto done;
1149		}
1150
1151		ret = krb5_cc_store_cred(context, ccache, &creds);
1152
1153		if (expire_time) {
1154			*expire_time = (time_t) creds.times.endtime;
1155		}
1156
1157		krb5_free_cred_contents(context, &creds);
1158	}
1159#elif defined(HAVE_KRB5_GET_KDC_CRED)	/* Heimdal */
1160	{
1161		krb5_kdc_flags flags;
1162		krb5_creds creds_in;
1163		krb5_realm *client_realm;
1164		krb5_creds *creds;
1165
1166		memset(&creds_in, 0, sizeof(creds_in));
1167
1168		if (client_string) {
1169			ret = smb_krb5_parse_name(context, client_string, &creds_in.client);
1170			if (ret) {
1171				goto done;
1172			}
1173		} else {
1174			ret = krb5_cc_get_principal(context, ccache, &creds_in.client);
1175			if (ret) {
1176				goto done;
1177			}
1178		}
1179
1180		if (service_string) {
1181			ret = smb_krb5_parse_name(context, service_string, &creds_in.server);
1182			if (ret) {
1183				goto done;
1184			}
1185		} else {
1186			/* build tgt service by default */
1187			client_realm = krb5_princ_realm(context, creds_in.client);
1188			if (!client_realm) {
1189				ret = ENOMEM;
1190				goto done;
1191			}
1192			ret = krb5_make_principal(context, &creds_in.server, *client_realm, KRB5_TGS_NAME, *client_realm, NULL);
1193			if (ret) {
1194				goto done;
1195			}
1196		}
1197
1198		flags.i = 0;
1199		flags.b.renewable = flags.b.renew = True;
1200
1201		ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &creds_in, &creds);
1202		if (ret) {
1203			DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
1204			goto done;
1205		}
1206
1207		/* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
1208		ret = krb5_cc_initialize(context, ccache, creds_in.client);
1209		if (ret) {
1210			goto done;
1211		}
1212
1213		ret = krb5_cc_store_cred(context, ccache, creds);
1214
1215		if (expire_time) {
1216			*expire_time = (time_t) creds->times.endtime;
1217		}
1218
1219		krb5_free_cred_contents(context, &creds_in);
1220		krb5_free_creds(context, creds);
1221	}
1222#else
1223#error No suitable krb5 ticket renew function available
1224#endif
1225
1226
1227done:
1228	if (client) {
1229		krb5_free_principal(context, client);
1230	}
1231	if (context) {
1232		krb5_free_context(context);
1233	}
1234	if (ccache) {
1235		krb5_cc_close(context, ccache);
1236	}
1237
1238	return ret;
1239
1240}
1241
1242 krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
1243{
1244	krb5_error_code ret = 0;
1245	if (addr == NULL) {
1246		return ret;
1247	}
1248#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1249	krb5_free_addresses(context, addr->addrs);
1250#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1251	ret = krb5_free_addresses(context, addr->addrs);
1252	SAFE_FREE(addr->addrs);
1253#endif
1254	SAFE_FREE(addr);
1255	addr = NULL;
1256	return ret;
1257}
1258
1259 krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr)
1260{
1261	krb5_error_code ret = 0;
1262	nstring buf;
1263#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1264	krb5_address **addrs = NULL;
1265#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1266	krb5_addresses *addrs = NULL;
1267#endif
1268
1269	*kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
1270	if (*kerb_addr == NULL) {
1271		return ENOMEM;
1272	}
1273
1274	put_name(buf, global_myname(), ' ', 0x20);
1275
1276#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1277	{
1278		int num_addr = 2;
1279
1280		addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
1281		if (addrs == NULL) {
1282			SAFE_FREE(kerb_addr);
1283			return ENOMEM;
1284		}
1285
1286		memset(addrs, 0, sizeof(krb5_address *) * num_addr);
1287
1288		addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1289		if (addrs[0] == NULL) {
1290			SAFE_FREE(addrs);
1291			SAFE_FREE(kerb_addr);
1292			return ENOMEM;
1293		}
1294
1295		addrs[0]->magic = KV5M_ADDRESS;
1296		addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
1297		addrs[0]->length = MAX_NETBIOSNAME_LEN;
1298		addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
1299		if (addrs[0]->contents == NULL) {
1300			SAFE_FREE(addrs[0]);
1301			SAFE_FREE(addrs);
1302			SAFE_FREE(kerb_addr);
1303			return ENOMEM;
1304		}
1305
1306		memcpy(addrs[0]->contents, buf, addrs[0]->length);
1307
1308		addrs[1] = NULL;
1309	}
1310#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1311	{
1312		addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
1313		if (addrs == NULL) {
1314			SAFE_FREE(kerb_addr);
1315			return ENOMEM;
1316		}
1317
1318		memset(addrs, 0, sizeof(krb5_addresses));
1319
1320		addrs->len = 1;
1321		addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1322		if (addrs->val == NULL) {
1323			SAFE_FREE(addrs);
1324			SAFE_FREE(kerb_addr);
1325			return ENOMEM;
1326		}
1327
1328		addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
1329		addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
1330		addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
1331		if (addrs->val[0].address.data == NULL) {
1332			SAFE_FREE(addrs->val);
1333			SAFE_FREE(addrs);
1334			SAFE_FREE(kerb_addr);
1335			return ENOMEM;
1336		}
1337
1338		memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
1339	}
1340#else
1341#error UNKNOWN_KRB5_ADDRESS_FORMAT
1342#endif
1343	(*kerb_addr)->addrs = addrs;
1344
1345	return ret;
1346}
1347
1348 void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
1349{
1350#ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
1351	krb5_free_error_contents(context, krberror);
1352#else /* MIT */
1353	krb5_free_error(context, krberror);
1354#endif
1355}
1356
1357 krb5_error_code handle_krberror_packet(krb5_context context,
1358					krb5_data *packet)
1359{
1360	krb5_error_code ret;
1361	BOOL got_error_code = False;
1362
1363	DEBUG(10,("handle_krberror_packet: got error packet\n"));
1364
1365#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
1366	{
1367		krb5_error krberror;
1368
1369		if ((ret = krb5_rd_error(context, packet, &krberror))) {
1370			DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
1371				error_message(ret)));
1372			return ret;
1373		}
1374
1375		if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
1376			ret = (krb5_error_code) krberror.error_code;
1377			got_error_code = True;
1378		}
1379
1380		smb_krb5_free_error(context, &krberror);
1381	}
1382#else /* MIT */
1383	{
1384		krb5_error *krberror;
1385
1386		if ((ret = krb5_rd_error(context, packet, &krberror))) {
1387			DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n",
1388				error_message(ret)));
1389			return ret;
1390		}
1391
1392		if (krberror->e_data.data == NULL) {
1393			ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
1394			got_error_code = True;
1395		}
1396		smb_krb5_free_error(context, krberror);
1397	}
1398#endif
1399	if (got_error_code) {
1400		DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n",
1401			error_message(ret), ret));
1402	}
1403	return ret;
1404}
1405
1406 krb5_error_code smb_krb5_get_init_creds_opt_alloc(krb5_context context,
1407					    krb5_get_init_creds_opt **opt)
1408{
1409#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
1410	/* Heimdal or modern MIT version */
1411	return krb5_get_init_creds_opt_alloc(context, opt);
1412#else
1413	/* Historical MIT version */
1414	krb5_get_init_creds_opt *my_opt;
1415
1416	*opt = NULL;
1417
1418	if ((my_opt = SMB_MALLOC(sizeof(krb5_get_init_creds_opt))) == NULL) {
1419		return ENOMEM;
1420	}
1421
1422	krb5_get_init_creds_opt_init(my_opt);
1423
1424	*opt =  my_opt;
1425	return 0;
1426#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC  */
1427}
1428
1429 void smb_krb5_get_init_creds_opt_free(krb5_context context,
1430				krb5_get_init_creds_opt *opt)
1431{
1432#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE
1433
1434#ifdef KRB5_CREDS_OPT_FREE_REQUIRES_CONTEXT
1435	/* Modern MIT version */
1436	krb5_get_init_creds_opt_free(context, opt);
1437#else
1438	/* Heimdal version */
1439	krb5_get_init_creds_opt_free(opt);
1440#endif
1441
1442#else /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */
1443	/* Historical MIT version */
1444	SAFE_FREE(opt);
1445	opt = NULL;
1446#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */
1447}
1448
1449 krb5_error_code smb_krb5_mk_error(krb5_context context,
1450				krb5_error_code error_code,
1451				const krb5_principal server,
1452				krb5_data *reply)
1453{
1454#ifdef HAVE_SHORT_KRB5_MK_ERROR_INTERFACE /* MIT */
1455	/*
1456	 * The MIT interface is *terrible*.
1457	 * We have to construct this ourselves...
1458	 */
1459	krb5_error e;
1460
1461	memset(&e, 0, sizeof(e));
1462	krb5_us_timeofday(context, &e.stime, &e.susec);
1463	e.server = server;
1464#if defined(krb5_err_base)
1465	e.error = error_code - krb5_err_base;
1466#elif defined(ERROR_TABLE_BASE_krb5)
1467	e.error = error_code - ERROR_TABLE_BASE_krb5;
1468#else
1469	e.error = error_code; /* Almost certainly wrong, but what can we do... ? */
1470#endif
1471
1472	return krb5_mk_error(context, &e, reply);
1473#else /* Heimdal. */
1474	return krb5_mk_error(context,
1475				error_code,
1476				NULL,
1477				NULL, /* e_data */
1478				NULL,
1479				server,
1480				NULL,
1481				NULL,
1482				reply);
1483#endif
1484}
1485
1486#else /* HAVE_KRB5 */
1487 /* this saves a few linking headaches */
1488 int cli_krb5_get_ticket(const char *principal, time_t time_offset,
1489			DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
1490			const char *ccname, time_t *tgs_expire)
1491{
1492	 DEBUG(0,("NO KERBEROS SUPPORT\n"));
1493	 return 1;
1494}
1495
1496#endif
1497