source: trunk/samba/source/libsmb/clikrb5.c @ 26

Last change on this file since 26 was 26, checked in by Paul Smedley, 14 years ago

Updated source to 3.0.25rc1

File size: 39.7 KB
Line 
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 *new_start_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 (new_start_time) {
1154                        *new_start_time = (time_t) creds.times.renew_till;
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, client);
1188                        ret = krb5_make_principal(context, &creds_in.server, *client_realm, KRB5_TGS_NAME, *client_realm, NULL);
1189                        if (ret) {
1190                                goto done;
1191                        }
1192                }
1193
1194                flags.i = 0;
1195                flags.b.renewable = flags.b.renew = True;
1196
1197                ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &creds_in, &creds);
1198                if (ret) {
1199                        DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
1200                        goto done;
1201                }
1202               
1203                /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
1204                ret = krb5_cc_initialize(context, ccache, creds_in.client);
1205                if (ret) {
1206                        goto done;
1207                }
1208       
1209                ret = krb5_cc_store_cred(context, ccache, creds);
1210
1211                if (new_start_time) {
1212                        *new_start_time = (time_t) creds->times.renew_till;
1213                }
1214                                               
1215                krb5_free_cred_contents(context, &creds_in);
1216                krb5_free_creds(context, creds);
1217        }
1218#else
1219#error No suitable krb5 ticket renew function available
1220#endif
1221
1222
1223done:
1224        if (client) {
1225                krb5_free_principal(context, client);
1226        }
1227        if (context) {
1228                krb5_free_context(context);
1229        }
1230        if (ccache) {
1231                krb5_cc_close(context, ccache);
1232        }
1233
1234        return ret;
1235   
1236}
1237
1238 krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
1239{
1240        krb5_error_code ret = 0;
1241        if (addr == NULL) {
1242                return ret;
1243        }
1244#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1245        krb5_free_addresses(context, addr->addrs);
1246#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1247        ret = krb5_free_addresses(context, addr->addrs);
1248        SAFE_FREE(addr->addrs);
1249#endif
1250        SAFE_FREE(addr);
1251        addr = NULL;
1252        return ret;
1253}
1254
1255 krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr)
1256{
1257        krb5_error_code ret = 0;
1258        nstring buf;
1259#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1260        krb5_address **addrs = NULL;
1261#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1262        krb5_addresses *addrs = NULL;
1263#endif
1264
1265        *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
1266        if (*kerb_addr == NULL) {
1267                return ENOMEM;
1268        }
1269
1270        put_name(buf, global_myname(), ' ', 0x20);
1271
1272#if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1273        {
1274                int num_addr = 2;
1275
1276                addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
1277                if (addrs == NULL) {
1278                        SAFE_FREE(kerb_addr);
1279                        return ENOMEM;
1280                }
1281
1282                memset(addrs, 0, sizeof(krb5_address *) * num_addr);
1283
1284                addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1285                if (addrs[0] == NULL) {
1286                        SAFE_FREE(addrs);
1287                        SAFE_FREE(kerb_addr);
1288                        return ENOMEM;
1289                }
1290
1291                addrs[0]->magic = KV5M_ADDRESS;
1292                addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
1293                addrs[0]->length = MAX_NETBIOSNAME_LEN;
1294                addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
1295                if (addrs[0]->contents == NULL) {
1296                        SAFE_FREE(addrs[0]);
1297                        SAFE_FREE(addrs);
1298                        SAFE_FREE(kerb_addr);
1299                        return ENOMEM;
1300                }
1301
1302                memcpy(addrs[0]->contents, buf, addrs[0]->length);
1303
1304                addrs[1] = NULL;
1305        }
1306#elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1307        {
1308                addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
1309                if (addrs == NULL) {
1310                        SAFE_FREE(kerb_addr);
1311                        return ENOMEM;
1312                }
1313
1314                memset(addrs, 0, sizeof(krb5_addresses));
1315
1316                addrs->len = 1;
1317                addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1318                if (addrs->val == NULL) {
1319                        SAFE_FREE(addrs);
1320                        SAFE_FREE(kerb_addr);
1321                        return ENOMEM;
1322                }
1323
1324                addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
1325                addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
1326                addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
1327                if (addrs->val[0].address.data == NULL) {
1328                        SAFE_FREE(addrs->val);
1329                        SAFE_FREE(addrs);
1330                        SAFE_FREE(kerb_addr);
1331                        return ENOMEM;
1332                }
1333
1334                memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
1335        }
1336#else
1337#error UNKNOWN_KRB5_ADDRESS_FORMAT
1338#endif
1339        (*kerb_addr)->addrs = addrs;
1340
1341        return ret;
1342}
1343
1344 void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
1345{
1346#ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
1347        krb5_free_error_contents(context, krberror);
1348#else /* MIT */
1349        krb5_free_error(context, krberror);
1350#endif
1351}
1352
1353 krb5_error_code handle_krberror_packet(krb5_context context,
1354                                        krb5_data *packet)
1355{
1356        krb5_error_code ret;
1357        BOOL got_error_code = False;
1358
1359        DEBUG(10,("handle_krberror_packet: got error packet\n"));
1360       
1361#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
1362        {
1363                krb5_error krberror;
1364
1365                if ((ret = krb5_rd_error(context, packet, &krberror))) {
1366                        DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
1367                                error_message(ret)));
1368                        return ret;
1369                }
1370
1371                if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
1372                        ret = (krb5_error_code) krberror.error_code;
1373                        got_error_code = True;
1374                }
1375
1376                smb_krb5_free_error(context, &krberror);
1377        }
1378#else /* MIT */
1379        {
1380                krb5_error *krberror;
1381
1382                if ((ret = krb5_rd_error(context, packet, &krberror))) {
1383                        DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
1384                                error_message(ret)));
1385                        return ret;
1386                }
1387
1388                if (krberror->e_data.data == NULL) {
1389                        ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
1390                        got_error_code = True;
1391                }
1392                smb_krb5_free_error(context, krberror);
1393        }
1394#endif
1395        if (got_error_code) {
1396                DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n", 
1397                        error_message(ret), ret));
1398        }
1399        return ret;
1400}
1401
1402#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
1403 krb5_error_code krb5_get_init_creds_opt_alloc(krb5_context context, krb5_get_init_creds_opt **opt)
1404{
1405        krb5_get_init_creds_opt *my_opt;
1406
1407        *opt = NULL;
1408
1409        if ((my_opt = SMB_MALLOC(sizeof(krb5_get_init_creds_opt))) == NULL) {
1410                return ENOMEM;
1411        }
1412
1413        krb5_get_init_creds_opt_init(my_opt);
1414
1415        *opt =  my_opt;
1416        return 0;
1417}
1418#endif
1419
1420#ifndef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE
1421 void krb5_get_init_creds_opt_free(krb5_get_init_creds_opt *opt)
1422{
1423        SAFE_FREE(opt);
1424        opt = NULL;
1425}
1426#endif
1427
1428 krb5_error_code smb_krb5_mk_error(krb5_context context,
1429                                krb5_error_code error_code,
1430                                const krb5_principal server,
1431                                krb5_data *reply)
1432{
1433#ifdef HAVE_SHORT_KRB5_MK_ERROR_INTERFACE /* MIT */
1434        /*
1435         * The MIT interface is *terrible*.
1436         * We have to construct this ourselves...
1437         */
1438        krb5_error e;
1439
1440        memset(&e, 0, sizeof(e));
1441        krb5_us_timeofday(context, &e.stime, &e.susec);
1442        e.server = server;
1443#if defined(krb5_err_base)
1444        e.error = error_code - krb5_err_base;
1445#elif defined(ERROR_TABLE_BASE_krb5)
1446        e.error = error_code - ERROR_TABLE_BASE_krb5;
1447#else
1448        e.error = error_code; /* Almost certainly wrong, but what can we do... ? */
1449#endif
1450
1451        return krb5_mk_error(context, &e, reply);
1452#else /* Heimdal. */
1453        return krb5_mk_error(context,
1454                                error_code,
1455                                NULL,
1456                                NULL, /* e_data */
1457                                NULL,
1458                                server,
1459                                NULL,
1460                                NULL,
1461                                reply);
1462#endif
1463}
1464
1465#else /* HAVE_KRB5 */
1466 /* this saves a few linking headaches */
1467 int cli_krb5_get_ticket(const char *principal, time_t time_offset, 
1468                        DATA_BLOB *ticket, DATA_BLOB *session_key_krb5, uint32 extra_ap_opts,
1469                        const char *ccname, time_t *tgs_expire) 
1470{
1471         DEBUG(0,("NO KERBEROS SUPPORT\n"));
1472         return 1;
1473}
1474
1475#endif
Note: See TracBrowser for help on using the repository browser.