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