libcli/auth Move krb5 wrapper functions from s3 into common
[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-2009
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 3 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, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "includes.h"
24 #include "smb_krb5.h"
25 #include "../librpc/gen_ndr/krb5pac.h"
26 #include "../lib/util/asn1.h"
27 #include "libsmb/nmblib.h"
28
29 #ifndef KRB5_AUTHDATA_WIN2K_PAC
30 #define KRB5_AUTHDATA_WIN2K_PAC 128
31 #endif
32
33 #ifndef KRB5_AUTHDATA_IF_RELEVANT
34 #define KRB5_AUTHDATA_IF_RELEVANT 1
35 #endif
36
37 #ifdef HAVE_KRB5
38
39 #define GSSAPI_CHECKSUM      0x8003             /* Checksum type value for Kerberos */
40 #define GSSAPI_BNDLENGTH     16                 /* Bind Length (rfc-1964 pg.3) */
41 #define GSSAPI_CHECKSUM_SIZE (4+GSSAPI_BNDLENGTH+4) /* Length of bind length,
42                                                         bind field, flags field. */
43
44 /* MIT krb5 1.7beta3 (in Ubuntu Karmic) is missing the prototype,
45    but still has the symbol */
46 #if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE
47 krb5_error_code krb5_auth_con_set_req_cksumtype(  
48         krb5_context     context,
49         krb5_auth_context      auth_context,  
50         krb5_cksumtype     cksumtype);
51 #endif
52
53 /**************************************************************
54  Wrappers around kerberos string functions that convert from
55  utf8 -> unix charset and vica versa.
56 **************************************************************/
57
58 /**************************************************************
59  krb5_parse_name that takes a UNIX charset.
60 **************************************************************/
61
62  krb5_error_code smb_krb5_parse_name(krb5_context context,
63                                 const char *name, /* in unix charset */
64                                 krb5_principal *principal)
65 {
66         krb5_error_code ret;
67         char *utf8_name;
68         size_t converted_size;
69
70         if (!push_utf8_talloc(talloc_tos(), &utf8_name, name, &converted_size)) {
71                 return ENOMEM;
72         }
73
74         ret = krb5_parse_name(context, utf8_name, principal);
75         TALLOC_FREE(utf8_name);
76         return ret;
77 }
78
79 #ifdef HAVE_KRB5_PARSE_NAME_NOREALM
80 /**************************************************************
81  krb5_parse_name_norealm that takes a UNIX charset.
82 **************************************************************/
83
84 static krb5_error_code smb_krb5_parse_name_norealm_conv(krb5_context context,
85                                 const char *name, /* in unix charset */
86                                 krb5_principal *principal)
87 {
88         krb5_error_code ret;
89         char *utf8_name;
90         size_t converted_size;
91
92         *principal = NULL;
93         if (!push_utf8_talloc(talloc_tos(), &utf8_name, name, &converted_size)) {
94                 return ENOMEM;
95         }
96
97         ret = krb5_parse_name_norealm(context, utf8_name, principal);
98         TALLOC_FREE(utf8_name);
99         return ret;
100 }
101 #endif
102
103 /**************************************************************
104  krb5_parse_name that returns a UNIX charset name. Must
105  be freed with talloc_free() call.
106 **************************************************************/
107
108 krb5_error_code smb_krb5_unparse_name(TALLOC_CTX *mem_ctx,
109                                       krb5_context context,
110                                       krb5_const_principal principal,
111                                       char **unix_name)
112 {
113         krb5_error_code ret;
114         char *utf8_name;
115         size_t converted_size;
116
117         *unix_name = NULL;
118         ret = krb5_unparse_name(context, principal, &utf8_name);
119         if (ret) {
120                 return ret;
121         }
122
123         if (!pull_utf8_talloc(mem_ctx, unix_name, utf8_name, &converted_size)) {
124                 krb5_free_unparsed_name(context, utf8_name);
125                 return ENOMEM;
126         }
127         krb5_free_unparsed_name(context, utf8_name);
128         return 0;
129 }
130
131 #ifndef HAVE_KRB5_SET_REAL_TIME
132 /*
133  * This function is not in the Heimdal mainline.
134  */
135  krb5_error_code krb5_set_real_time(krb5_context context, int32_t seconds, int32_t microseconds)
136 {
137         krb5_error_code ret;
138         int32_t sec, usec;
139
140         ret = krb5_us_timeofday(context, &sec, &usec);
141         if (ret)
142                 return ret;
143
144         context->kdc_sec_offset = seconds - sec;
145         context->kdc_usec_offset = microseconds - usec;
146
147         return 0;
148 }
149 #endif
150
151 #if !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
152
153 #if defined(HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES)
154
155 /* With MIT kerberos, we should use krb5_set_default_tgs_enctypes in preference
156  * to krb5_set_default_tgs_ktypes. See
157  *         http://lists.samba.org/archive/samba-technical/2006-July/048271.html
158  *
159  * If the MIT libraries are not exporting internal symbols, we will end up in
160  * this branch, which is correct. Otherwise we will continue to use the
161  * internal symbol
162  */
163  krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
164 {
165     return krb5_set_default_tgs_enctypes(ctx, enc);
166 }
167
168 #elif defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES)
169
170 /* Heimdal */
171  krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
172 {
173         return krb5_set_default_in_tkt_etypes(ctx, enc);
174 }
175
176 #endif /* HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES */
177
178 #endif /* HAVE_KRB5_SET_DEFAULT_TGS_KTYPES */
179
180 #if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
181 /* HEIMDAL */
182  bool setup_kaddr( krb5_address *pkaddr, struct sockaddr_storage *paddr)
183 {
184         memset(pkaddr, '\0', sizeof(krb5_address));
185 #if defined(HAVE_IPV6) && defined(KRB5_ADDRESS_INET6)
186         if (paddr->ss_family == AF_INET6) {
187                 pkaddr->addr_type = KRB5_ADDRESS_INET6;
188                 pkaddr->address.length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
189                 pkaddr->address.data = (char *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
190                 return true;
191         }
192 #endif
193         if (paddr->ss_family == AF_INET) {
194                 pkaddr->addr_type = KRB5_ADDRESS_INET;
195                 pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
196                 pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
197                 return true;
198         }
199         return false;
200 }
201 #elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
202 /* MIT */
203  bool setup_kaddr( krb5_address *pkaddr, struct sockaddr_storage *paddr)
204 {
205         memset(pkaddr, '\0', sizeof(krb5_address));
206 #if defined(HAVE_IPV6) && defined(ADDRTYPE_INET6)
207         if (paddr->ss_family == AF_INET6) {
208                 pkaddr->addrtype = ADDRTYPE_INET6;
209                 pkaddr->length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
210                 pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
211                 return true;
212         }
213 #endif
214         if (paddr->ss_family == AF_INET) {
215                 pkaddr->addrtype = ADDRTYPE_INET;
216                 pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
217                 pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
218                 return true;
219         }
220         return false;
221 }
222 #else
223 #error UNKNOWN_ADDRTYPE
224 #endif
225
226  int create_kerberos_key_from_string(krb5_context context,
227                                         krb5_principal host_princ,
228                                         krb5_data *password,
229                                         krb5_keyblock *key,
230                                         krb5_enctype enctype,
231                                         bool no_salt)
232 {
233         krb5_principal salt_princ = NULL;
234         int ret;
235         /*
236          * Check if we've determined that the KDC is salting keys for this
237          * principal/enctype in a non-obvious way.  If it is, try to match
238          * its behavior.
239          */
240         if (no_salt) {
241                 KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
242                 if (!KRB5_KEY_DATA(key)) {
243                         return ENOMEM;
244                 }
245                 memcpy(KRB5_KEY_DATA(key), password->data, password->length);
246                 KRB5_KEY_LENGTH(key) = password->length;
247                 KRB5_KEY_TYPE(key) = enctype;
248                 return 0;
249         }
250         salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
251         ret = create_kerberos_key_from_string_direct(context, salt_princ ? salt_princ : host_princ, password, key, enctype);
252         if (salt_princ) {
253                 krb5_free_principal(context, salt_princ);
254         }
255         return ret;
256 }
257
258 #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
259  krb5_error_code get_kerberos_allowed_etypes(krb5_context context, 
260                                             krb5_enctype **enctypes)
261 {
262         return krb5_get_permitted_enctypes(context, enctypes);
263 }
264 #elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
265  krb5_error_code get_kerberos_allowed_etypes(krb5_context context, 
266                                             krb5_enctype **enctypes)
267 {
268         return krb5_get_default_in_tkt_etypes(context, enctypes);
269 }
270 #else
271 #error UNKNOWN_GET_ENCTYPES_FUNCTIONS
272 #endif
273
274 #if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
275  krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
276                                         krb5_auth_context auth_context,
277                                         krb5_keyblock *keyblock)
278 {
279         return krb5_auth_con_setkey(context, auth_context, keyblock);
280 }
281 #endif
282
283 bool unwrap_edata_ntstatus(TALLOC_CTX *mem_ctx, 
284                            DATA_BLOB *edata, 
285                            DATA_BLOB *edata_out)
286 {
287         DATA_BLOB edata_contents;
288         ASN1_DATA *data;
289         int edata_type;
290
291         if (!edata->length) {
292                 return False;
293         }
294
295         data = asn1_init(mem_ctx);
296         if (data == NULL) {
297                 return false;
298         }
299
300         asn1_load(data, *edata);
301         asn1_start_tag(data, ASN1_SEQUENCE(0));
302         asn1_start_tag(data, ASN1_CONTEXT(1));
303         asn1_read_Integer(data, &edata_type);
304
305         if (edata_type != KRB5_PADATA_PW_SALT) {
306                 DEBUG(0,("edata is not of required type %d but of type %d\n", 
307                         KRB5_PADATA_PW_SALT, edata_type));
308                 asn1_free(data);
309                 return False;
310         }
311         
312         asn1_start_tag(data, ASN1_CONTEXT(2));
313         asn1_read_OctetString(data, talloc_tos(), &edata_contents);
314         asn1_end_tag(data);
315         asn1_end_tag(data);
316         asn1_end_tag(data);
317         asn1_free(data);
318
319         *edata_out = data_blob_talloc(mem_ctx, edata_contents.data, edata_contents.length);
320
321         data_blob_free(&edata_contents);
322
323         return True;
324 }
325
326
327 bool unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_pac_data)
328 {
329         DATA_BLOB pac_contents;
330         ASN1_DATA *data;
331         int data_type;
332
333         if (!auth_data->length) {
334                 return False;
335         }
336
337         data = asn1_init(mem_ctx);
338         if (data == NULL) {
339                 return false;
340         }
341
342         asn1_load(data, *auth_data);
343         asn1_start_tag(data, ASN1_SEQUENCE(0));
344         asn1_start_tag(data, ASN1_SEQUENCE(0));
345         asn1_start_tag(data, ASN1_CONTEXT(0));
346         asn1_read_Integer(data, &data_type);
347         
348         if (data_type != KRB5_AUTHDATA_WIN2K_PAC ) {
349                 DEBUG(10,("authorization data is not a Windows PAC (type: %d)\n", data_type));
350                 asn1_free(data);
351                 return False;
352         }
353         
354         asn1_end_tag(data);
355         asn1_start_tag(data, ASN1_CONTEXT(1));
356         asn1_read_OctetString(data, talloc_tos(), &pac_contents);
357         asn1_end_tag(data);
358         asn1_end_tag(data);
359         asn1_end_tag(data);
360         asn1_free(data);
361
362         *unwrapped_pac_data = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length);
363
364         data_blob_free(&pac_contents);
365
366         return True;
367 }
368
369  bool get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt)
370 {
371         DATA_BLOB auth_data_wrapped;
372         bool got_auth_data_pac = False;
373         int i;
374         
375 #if defined(HAVE_KRB5_TKT_ENC_PART2)
376         if (tkt->enc_part2 && tkt->enc_part2->authorization_data && 
377             tkt->enc_part2->authorization_data[0] && 
378             tkt->enc_part2->authorization_data[0]->length)
379         {
380                 for (i = 0; tkt->enc_part2->authorization_data[i] != NULL; i++) {
381                 
382                         if (tkt->enc_part2->authorization_data[i]->ad_type != 
383                             KRB5_AUTHDATA_IF_RELEVANT) {
384                                 DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n", 
385                                         tkt->enc_part2->authorization_data[i]->ad_type));
386                                 continue;
387                         }
388
389                         auth_data_wrapped = data_blob(tkt->enc_part2->authorization_data[i]->contents,
390                                                       tkt->enc_part2->authorization_data[i]->length);
391
392                         /* check if it is a PAC */
393                         got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data);
394                         data_blob_free(&auth_data_wrapped);
395
396                         if (got_auth_data_pac) {
397                                 return true;
398                         }
399                 }
400
401                 return got_auth_data_pac;
402         }
403                 
404 #else
405         if (tkt->ticket.authorization_data && 
406             tkt->ticket.authorization_data->len)
407         {
408                 for (i = 0; i < tkt->ticket.authorization_data->len; i++) {
409                         
410                         if (tkt->ticket.authorization_data->val[i].ad_type != 
411                             KRB5_AUTHDATA_IF_RELEVANT) {
412                                 DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n", 
413                                         tkt->ticket.authorization_data->val[i].ad_type));
414                                 continue;
415                         }
416
417                         auth_data_wrapped = data_blob(tkt->ticket.authorization_data->val[i].ad_data.data,
418                                                       tkt->ticket.authorization_data->val[i].ad_data.length);
419
420                         /* check if it is a PAC */
421                         got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data);
422                         data_blob_free(&auth_data_wrapped);
423
424                         if (got_auth_data_pac) {
425                                 return true;
426                         }
427                 }
428
429                 return got_auth_data_pac;
430         }
431 #endif
432         return False;
433 }
434
435  krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt)
436 {
437 #if defined(HAVE_KRB5_TKT_ENC_PART2)
438         return tkt->enc_part2->client;
439 #else
440         return tkt->client;
441 #endif
442 }
443
444 #if !defined(HAVE_KRB5_LOCATE_KDC)
445
446 /* krb5_locate_kdc is an internal MIT symbol. MIT are not yet willing to commit
447  * to a public interface for this functionality, so we have to be able to live
448  * without it if the MIT libraries are hiding their internal symbols.
449  */
450
451 #if defined(KRB5_KRBHST_INIT)
452 /* Heimdal */
453  krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters)
454 {
455         krb5_krbhst_handle hnd;
456         krb5_krbhst_info *hinfo;
457         krb5_error_code rc;
458         int num_kdcs, i;
459         struct sockaddr *sa;
460         struct addrinfo *ai;
461
462         *addr_pp = NULL;
463         *naddrs = 0;
464
465         rc = krb5_krbhst_init(ctx, realm->data, KRB5_KRBHST_KDC, &hnd);
466         if (rc) {
467                 DEBUG(0, ("smb_krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc)));
468                 return rc;
469         }
470
471         for ( num_kdcs = 0; (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); num_kdcs++)
472                 ;
473
474         krb5_krbhst_reset(ctx, hnd);
475
476         if (!num_kdcs) {
477                 DEBUG(0, ("smb_krb5_locate_kdc: zero kdcs found !\n"));
478                 krb5_krbhst_free(ctx, hnd);
479                 return -1;
480         }
481
482         sa = SMB_MALLOC_ARRAY( struct sockaddr, num_kdcs );
483         if (!sa) {
484                 DEBUG(0, ("smb_krb5_locate_kdc: malloc failed\n"));
485                 krb5_krbhst_free(ctx, hnd);
486                 naddrs = 0;
487                 return -1;
488         }
489
490         memset(sa, '\0', sizeof(struct sockaddr) * num_kdcs );
491
492         for (i = 0; i < num_kdcs && (rc = krb5_krbhst_next(ctx, hnd, &hinfo) == 0); i++) {
493
494 #if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO)
495                 rc = krb5_krbhst_get_addrinfo(ctx, hinfo, &ai);
496                 if (rc) {
497                         DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc)));
498                         continue;
499                 }
500 #endif
501                 if (hinfo->ai && hinfo->ai->ai_family == AF_INET) 
502                         memcpy(&sa[i], hinfo->ai->ai_addr, sizeof(struct sockaddr));
503         }
504
505         krb5_krbhst_free(ctx, hnd);
506
507         *naddrs = num_kdcs;
508         *addr_pp = sa;
509         return 0;
510 }
511
512 #else /* ! defined(KRB5_KRBHST_INIT) */
513
514  krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm,
515                 struct sockaddr **addr_pp, int *naddrs, int get_masters)
516 {
517         DEBUG(0, ("unable to explicitly locate the KDC on this platform\n"));
518         return KRB5_KDC_UNREACH;
519 }
520
521 #endif /* KRB5_KRBHST_INIT */
522
523 #else /* ! HAVE_KRB5_LOCATE_KDC */
524
525  krb5_error_code smb_krb5_locate_kdc(krb5_context ctx, const krb5_data *realm,
526                 struct sockaddr **addr_pp, int *naddrs, int get_masters)
527 {
528         return krb5_locate_kdc(ctx, realm, addr_pp, naddrs, get_masters);
529 }
530
531 #endif /* HAVE_KRB5_LOCATE_KDC */
532
533 #if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
534  void krb5_free_unparsed_name(krb5_context context, char *val)
535 {
536         SAFE_FREE(val);
537 }
538 #endif
539
540  void kerberos_set_creds_enctype(krb5_creds *pcreds, int enctype)
541 {
542 #if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
543         KRB5_KEY_TYPE((&pcreds->keyblock)) = enctype;
544 #elif defined(HAVE_KRB5_SESSION_IN_CREDS)
545         KRB5_KEY_TYPE((&pcreds->session)) = enctype;
546 #else
547 #error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
548 #endif
549 }
550
551  bool kerberos_compatible_enctypes(krb5_context context,
552                                   krb5_enctype enctype1,
553                                   krb5_enctype enctype2)
554 {
555 #if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
556         krb5_boolean similar = 0;
557
558         krb5_c_enctype_compare(context, enctype1, enctype2, &similar);
559         return similar ? True : False;
560 #elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
561         return krb5_enctypes_compatible_keys(context, enctype1, enctype2) ? True : False;
562 #endif
563 }
564
565 static bool ads_cleanup_expired_creds(krb5_context context, 
566                                       krb5_ccache  ccache,
567                                       krb5_creds  *credsp)
568 {
569         krb5_error_code retval;
570         const char *cc_type = krb5_cc_get_type(context, ccache);
571
572         DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
573                   cc_type, krb5_cc_get_name(context, ccache),
574                   http_timestring(talloc_tos(), credsp->times.endtime)));
575
576         /* we will probably need new tickets if the current ones
577            will expire within 10 seconds.
578         */
579         if (credsp->times.endtime >= (time(NULL) + 10))
580                 return False;
581
582         /* heimdal won't remove creds from a file ccache, and 
583            perhaps we shouldn't anyway, since internally we 
584            use memory ccaches, and a FILE one probably means that
585            we're using creds obtained outside of our exectuable
586         */
587         if (strequal(cc_type, "FILE")) {
588                 DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
589                 return False;
590         }
591
592         retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
593         if (retval) {
594                 DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
595                           error_message(retval)));
596                 /* If we have an error in this, we want to display it,
597                    but continue as though we deleted it */
598         }
599         return True;
600 }
601
602 /* Allocate and setup the auth context into the state we need. */
603
604 static krb5_error_code setup_auth_context(krb5_context context,
605                         krb5_auth_context *auth_context)
606 {
607         krb5_error_code retval;
608
609         retval = krb5_auth_con_init(context, auth_context );
610         if (retval) {
611                 DEBUG(1,("krb5_auth_con_init failed (%s)\n",
612                         error_message(retval)));
613                 return retval;
614         }
615
616         /* Ensure this is an addressless ticket. */
617         retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
618         if (retval) {
619                 DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
620                         error_message(retval)));
621         }
622
623         return retval;
624 }
625
626 static krb5_error_code create_gss_checksum(krb5_data *in_data, /* [inout] */
627                                                 uint32_t gss_flags)
628 {
629         unsigned int orig_length = in_data->length;
630         unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
631         char *gss_cksum = NULL;
632
633         if (orig_length) {
634                 /* Extra length field for delgated ticket. */
635                 base_cksum_size += 4;
636         }
637
638         if ((unsigned int)base_cksum_size + orig_length <
639                         (unsigned int)base_cksum_size) {
640                 return EINVAL;
641         }
642
643         gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
644         if (gss_cksum == NULL) {
645                 return ENOMEM;
646         }
647
648         memset(gss_cksum, '\0', base_cksum_size + orig_length);
649         SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
650
651         /*
652          * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
653          * This matches the behavior of heimdal and mit.
654          *
655          * And it is needed to work against some closed source
656          * SMB servers.
657          *
658          * See bug #7883
659          */
660         memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
661
662         SIVAL(gss_cksum, 20, gss_flags);
663
664         if (orig_length) {
665                 SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
666                 SSVAL(gss_cksum, 26, orig_length);
667                 /* Copy the kerberos KRB_CRED data */
668                 memcpy(gss_cksum + 28, in_data->data, orig_length);
669                 free(in_data->data);
670                 in_data->data = NULL;
671                 in_data->length = 0;
672         }
673         in_data->data = gss_cksum;
674         in_data->length = base_cksum_size + orig_length;
675         return 0;
676 }
677
678 /*
679   we can't use krb5_mk_req because w2k wants the service to be in a particular format
680 */
681 static krb5_error_code ads_krb5_mk_req(krb5_context context, 
682                                        krb5_auth_context *auth_context, 
683                                        const krb5_flags ap_req_options,
684                                        const char *principal,
685                                        krb5_ccache ccache, 
686                                        krb5_data *outbuf, 
687                                        time_t *expire_time,
688                                        const char *impersonate_princ_s)
689 {
690         krb5_error_code           retval;
691         krb5_principal    server;
692         krb5_principal impersonate_princ = NULL;
693         krb5_creds              * credsp;
694         krb5_creds                creds;
695         krb5_data in_data;
696         bool creds_ready = False;
697         int i = 0, maxtries = 3;
698         uint32_t gss_flags = 0;
699
700         ZERO_STRUCT(in_data);
701
702         retval = smb_krb5_parse_name(context, principal, &server);
703         if (retval) {
704                 DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
705                 return retval;
706         }
707
708         if (impersonate_princ_s) {
709                 retval = smb_krb5_parse_name(context, impersonate_princ_s,
710                                              &impersonate_princ);
711                 if (retval) {
712                         DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", impersonate_princ_s));
713                         goto cleanup_princ;
714                 }
715         }
716
717         /* obtain ticket & session key */
718         ZERO_STRUCT(creds);
719         if ((retval = krb5_copy_principal(context, server, &creds.server))) {
720                 DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n", 
721                          error_message(retval)));
722                 goto cleanup_princ;
723         }
724         
725         if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
726                 /* This can commonly fail on smbd startup with no ticket in the cache.
727                  * Report at higher level than 1. */
728                 DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
729                          error_message(retval)));
730                 goto cleanup_creds;
731         }
732
733         while (!creds_ready && (i < maxtries)) {
734
735                 if ((retval = smb_krb5_get_credentials(context, ccache,
736                                                        creds.client,
737                                                        creds.server,
738                                                        impersonate_princ,
739                                                        &credsp))) {
740                         DEBUG(1,("ads_krb5_mk_req: smb_krb5_get_credentials failed for %s (%s)\n",
741                                 principal, error_message(retval)));
742                         goto cleanup_creds;
743                 }
744
745                 /* cope with ticket being in the future due to clock skew */
746                 if ((unsigned)credsp->times.starttime > time(NULL)) {
747                         time_t t = time(NULL);
748                         int time_offset =(int)((unsigned)credsp->times.starttime-t);
749                         DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
750                         krb5_set_real_time(context, t + time_offset + 1, 0);
751                 }
752
753                 if (!ads_cleanup_expired_creds(context, ccache, credsp)) {
754                         creds_ready = True;
755                 }
756
757                 i++;
758         }
759
760         DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
761                   principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache),
762                   http_timestring(talloc_tos(), (unsigned)credsp->times.endtime), 
763                   (unsigned)credsp->times.endtime));
764
765         if (expire_time) {
766                 *expire_time = (time_t)credsp->times.endtime;
767         }
768
769         /* Allocate the auth_context. */
770         retval = setup_auth_context(context, auth_context);
771         if (retval) {
772                 DEBUG(1,("setup_auth_context failed (%s)\n",
773                         error_message(retval)));
774                 goto cleanup_creds;
775         }
776
777 #if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_FWD_TGT_CREDS) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
778         if( credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE ) {
779                 /* Fetch a forwarded TGT from the KDC so that we can hand off a 2nd ticket
780                  as part of the kerberos exchange. */
781
782                 DEBUG( 3, ("ads_krb5_mk_req: server marked as OK to delegate to, building forwardable TGT\n")  );
783
784                 retval = krb5_auth_con_setuseruserkey(context,
785                                         *auth_context,
786                                         &credsp->keyblock );
787                 if (retval) {
788                         DEBUG(1,("krb5_auth_con_setuseruserkey failed (%s)\n",
789                                 error_message(retval)));
790                         goto cleanup_creds;
791                 }
792
793                 /* Must use a subkey for forwarded tickets. */
794                 retval = krb5_auth_con_setflags(context,
795                                 *auth_context,
796                                 KRB5_AUTH_CONTEXT_USE_SUBKEY);
797                 if (retval) {
798                         DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
799                                 error_message(retval)));
800                         goto cleanup_creds;
801                 }
802
803                 retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
804                                 *auth_context,  /* Authentication context [in] */
805                                 CONST_DISCARD(char *, KRB5_TGS_NAME),  /* Ticket service name ("krbtgt") [in] */
806                                 credsp->client, /* Client principal for the tgt [in] */
807                                 credsp->server, /* Server principal for the tgt [in] */
808                                 ccache,         /* Credential cache to use for storage [in] */
809                                 1,              /* Turn on for "Forwardable ticket" [in] */
810                                 &in_data );     /* Resulting response [out] */
811
812                 if (retval) {
813                         DEBUG( 3, ("krb5_fwd_tgt_creds failed (%s)\n",
814                                    error_message( retval ) ) );
815
816                         /*
817                          * This is not fatal. Delete the *auth_context and continue
818                          * with krb5_mk_req_extended to get a non-forwardable ticket.
819                          */
820
821                         if (in_data.data) {
822                                 free( in_data.data );
823                                 in_data.data = NULL;
824                                 in_data.length = 0;
825                         }
826                         krb5_auth_con_free(context, *auth_context);
827                         *auth_context = NULL;
828                         retval = setup_auth_context(context, auth_context);
829                         if (retval) {
830                                 DEBUG(1,("setup_auth_context failed (%s)\n",
831                                         error_message(retval)));
832                                 goto cleanup_creds;
833                         }
834                 } else {
835                         /* We got a delegated ticket. */
836                         gss_flags |= GSS_C_DELEG_FLAG;
837                 }
838         }
839
840         /* Frees and reallocates in_data into a GSS checksum blob. */
841         retval = create_gss_checksum(&in_data, gss_flags);
842         if (retval) {
843                 goto cleanup_data;
844         }
845
846         /* We always want GSS-checksum types. */
847         retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
848         if (retval) {
849                 DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
850                         error_message(retval)));
851                 goto cleanup_data;
852         }
853 #endif
854
855         retval = krb5_mk_req_extended(context, auth_context, ap_req_options, 
856                                       &in_data, credsp, outbuf);
857         if (retval) {
858                 DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", 
859                          error_message(retval)));
860         }
861
862 cleanup_data:
863         if (in_data.data) {
864                 free( in_data.data );
865                 in_data.length = 0;
866         }
867
868         krb5_free_creds(context, credsp);
869
870 cleanup_creds:
871         krb5_free_cred_contents(context, &creds);
872
873 cleanup_princ:
874         krb5_free_principal(context, server);
875         if (impersonate_princ) {
876                 krb5_free_principal(context, impersonate_princ);
877         }
878
879         return retval;
880 }
881
882 /*
883   get a kerberos5 ticket for the given service
884 */
885 int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
886                         const char *principal, time_t time_offset,
887                         DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
888                         uint32_t extra_ap_opts, const char *ccname,
889                         time_t *tgs_expire,
890                         const char *impersonate_princ_s)
891
892 {
893         krb5_error_code retval;
894         krb5_data packet;
895         krb5_context context = NULL;
896         krb5_ccache ccdef = NULL;
897         krb5_auth_context auth_context = NULL;
898         krb5_enctype enc_types[] = {
899                 ENCTYPE_ARCFOUR_HMAC,
900                 ENCTYPE_DES_CBC_MD5,
901                 ENCTYPE_DES_CBC_CRC,
902                 ENCTYPE_NULL};
903
904         initialize_krb5_error_table();
905         retval = krb5_init_context(&context);
906         if (retval) {
907                 DEBUG(1, ("krb5_init_context failed (%s)\n",
908                          error_message(retval)));
909                 goto failed;
910         }
911
912         if (time_offset != 0) {
913                 krb5_set_real_time(context, time(NULL) + time_offset, 0);
914         }
915
916         if ((retval = krb5_cc_resolve(context, ccname ?
917                         ccname : krb5_cc_default_name(context), &ccdef))) {
918                 DEBUG(1, ("krb5_cc_default failed (%s)\n",
919                          error_message(retval)));
920                 goto failed;
921         }
922
923         if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
924                 DEBUG(1, ("krb5_set_default_tgs_ktypes failed (%s)\n",
925                          error_message(retval)));
926                 goto failed;
927         }
928
929         retval = ads_krb5_mk_req(context, &auth_context,
930                                 AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
931                                 principal, ccdef, &packet,
932                                 tgs_expire, impersonate_princ_s);
933         if (retval) {
934                 goto failed;
935         }
936
937         get_krb5_smb_session_key(mem_ctx, context, auth_context,
938                                  session_key_krb5, false);
939
940         *ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
941
942         kerberos_free_data_contents(context, &packet);
943
944 failed:
945
946         if (context) {
947                 if (ccdef)
948                         krb5_cc_close(context, ccdef);
949                 if (auth_context)
950                         krb5_auth_con_free(context, auth_context);
951                 krb5_free_context(context);
952         }
953
954         return retval;
955 }
956
957 bool get_krb5_smb_session_key(TALLOC_CTX *mem_ctx,
958                               krb5_context context,
959                               krb5_auth_context auth_context,
960                               DATA_BLOB *session_key, bool remote)
961 {
962         krb5_keyblock *skey = NULL;
963         krb5_error_code err = 0;
964         bool ret = false;
965
966         if (remote) {
967                 err = krb5_auth_con_getremotesubkey(context,
968                                                     auth_context, &skey);
969         } else {
970                 err = krb5_auth_con_getlocalsubkey(context,
971                                                    auth_context, &skey);
972         }
973
974         if (err || skey == NULL) {
975                 DEBUG(10, ("KRB5 error getting session key %d\n", err));
976                 goto done;
977         }
978
979         DEBUG(10, ("Got KRB5 session key of length %d\n",
980                    (int)KRB5_KEY_LENGTH(skey)));
981
982         *session_key = data_blob_talloc(mem_ctx,
983                                          KRB5_KEY_DATA(skey),
984                                          KRB5_KEY_LENGTH(skey));
985         dump_data_pw("KRB5 Session Key:\n",
986                      session_key->data,
987                      session_key->length);
988
989         ret = true;
990
991 done:
992         if (skey) {
993                 krb5_free_keyblock(context, skey);
994         }
995
996         return ret;
997 }
998
999
1000 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
1001  const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
1002
1003  const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
1004 {
1005         static krb5_data kdata;
1006
1007         kdata.data = (char *)krb5_principal_get_comp_string(context, principal, i);
1008         kdata.length = strlen((const char *)kdata.data);
1009         return &kdata;
1010 }
1011 #endif
1012
1013  void smb_krb5_checksum_from_pac_sig(krb5_checksum *cksum,
1014                                      struct PAC_SIGNATURE_DATA *sig)
1015 {
1016 #ifdef HAVE_CHECKSUM_IN_KRB5_CHECKSUM
1017         cksum->cksumtype        = (krb5_cksumtype)sig->type;
1018         cksum->checksum.length  = sig->signature.length;
1019         cksum->checksum.data    = sig->signature.data;
1020 #else
1021         cksum->checksum_type    = (krb5_cksumtype)sig->type;
1022         cksum->length           = sig->signature.length;
1023         cksum->contents         = sig->signature.data;
1024 #endif
1025 }
1026
1027  krb5_error_code smb_krb5_verify_checksum(krb5_context context,
1028                                           const krb5_keyblock *keyblock,
1029                                          krb5_keyusage usage,
1030                                          krb5_checksum *cksum,
1031                                          uint8 *data,
1032                                          size_t length)
1033 {
1034         krb5_error_code ret;
1035
1036         /* verify the checksum */
1037
1038         /* welcome to the wonderful world of samba's kerberos abstraction layer:
1039          * 
1040          * function                     heimdal 0.6.1rc3        heimdal 0.7     MIT krb 1.4.2
1041          * -----------------------------------------------------------------------------
1042          * krb5_c_verify_checksum       -                       works           works
1043          * krb5_verify_checksum         works (6 args)          works (6 args)  broken (7 args) 
1044          */
1045
1046 #if defined(HAVE_KRB5_C_VERIFY_CHECKSUM)
1047         {
1048                 krb5_boolean checksum_valid = False;
1049                 krb5_data input;
1050
1051                 input.data = (char *)data;
1052                 input.length = length;
1053
1054                 ret = krb5_c_verify_checksum(context, 
1055                                              keyblock, 
1056                                              usage,
1057                                              &input, 
1058                                              cksum,
1059                                              &checksum_valid);
1060                 if (ret) {
1061                         DEBUG(3,("smb_krb5_verify_checksum: krb5_c_verify_checksum() failed: %s\n", 
1062                                 error_message(ret)));
1063                         return ret;
1064                 }
1065
1066                 if (!checksum_valid)
1067                         ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
1068         }
1069
1070 #elif KRB5_VERIFY_CHECKSUM_ARGS == 6 && defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CRYPTO) && defined(HAVE_KRB5_CRYPTO_DESTROY)
1071
1072         /* Warning: MIT's krb5_verify_checksum cannot be used as it will use a key
1073          * without enctype and it ignores any key_usage types - Guenther */
1074
1075         {
1076
1077                 krb5_crypto crypto;
1078                 ret = krb5_crypto_init(context,
1079                                        keyblock,
1080                                        0,
1081                                        &crypto);
1082                 if (ret) {
1083                         DEBUG(0,("smb_krb5_verify_checksum: krb5_crypto_init() failed: %s\n", 
1084                                 error_message(ret)));
1085                         return ret;
1086                 }
1087
1088                 ret = krb5_verify_checksum(context,
1089                                            crypto,
1090                                            usage,
1091                                            data,
1092                                            length,
1093                                            cksum);
1094
1095                 krb5_crypto_destroy(context, crypto);
1096         }
1097
1098 #else
1099 #error UNKNOWN_KRB5_VERIFY_CHECKSUM_FUNCTION
1100 #endif
1101
1102         return ret;
1103 }
1104
1105  time_t get_authtime_from_tkt(krb5_ticket *tkt)
1106 {
1107 #if defined(HAVE_KRB5_TKT_ENC_PART2)
1108         return tkt->enc_part2->times.authtime;
1109 #else
1110         return tkt->ticket.authtime;
1111 #endif
1112 }
1113
1114 #ifdef HAVE_KRB5_DECODE_AP_REQ  /* Heimdal */
1115 static int get_kvno_from_ap_req(krb5_ap_req *ap_req)
1116 {
1117 #ifdef HAVE_TICKET_POINTER_IN_KRB5_AP_REQ /* MIT */
1118         if (ap_req->ticket->enc_part.kvno)
1119                 return ap_req->ticket->enc_part.kvno;
1120 #else /* Heimdal */
1121         if (ap_req->ticket.enc_part.kvno) 
1122                 return *ap_req->ticket.enc_part.kvno;
1123 #endif
1124         return 0;
1125 }
1126
1127 static krb5_enctype get_enctype_from_ap_req(krb5_ap_req *ap_req)
1128 {
1129 #ifdef HAVE_ETYPE_IN_ENCRYPTEDDATA /* Heimdal */
1130         return ap_req->ticket.enc_part.etype;
1131 #else /* MIT */
1132         return ap_req->ticket->enc_part.enctype;
1133 #endif
1134 }
1135 #endif  /* HAVE_KRB5_DECODE_AP_REQ */
1136
1137 static krb5_error_code
1138 get_key_from_keytab(krb5_context context,
1139                     krb5_const_principal server,
1140                     krb5_enctype enctype,
1141                     krb5_kvno kvno,
1142                     krb5_keyblock **out_key)
1143 {
1144         krb5_keytab_entry entry;
1145         krb5_error_code ret;
1146         krb5_keytab keytab;
1147         char *name = NULL;
1148         krb5_keyblock *keyp;
1149
1150         /* We have to open a new keytab handle here, as MIT does
1151            an implicit open/getnext/close on krb5_kt_get_entry. We
1152            may be in the middle of a keytab enumeration when this is
1153            called. JRA. */
1154
1155         ret = smb_krb5_open_keytab(context, NULL, False, &keytab);
1156         if (ret) {
1157                 DEBUG(1,("get_key_from_keytab: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
1158                 return ret;
1159         }
1160
1161         if ( DEBUGLEVEL >= 10 ) {
1162                 if (smb_krb5_unparse_name(talloc_tos(), context, server, &name) == 0) {
1163                         DEBUG(10,("get_key_from_keytab: will look for kvno %d, enctype %d and name: %s\n", 
1164                                 kvno, enctype, name));
1165                         TALLOC_FREE(name);
1166                 }
1167         }
1168
1169         ret = krb5_kt_get_entry(context,
1170                                 keytab,
1171                                 server,
1172                                 kvno,
1173                                 enctype,
1174                                 &entry);
1175
1176         if (ret) {
1177                 DEBUG(0,("get_key_from_keytab: failed to retrieve key: %s\n", error_message(ret)));
1178                 goto out;
1179         }
1180
1181         keyp = KRB5_KT_KEY(&entry);
1182
1183         ret = krb5_copy_keyblock(context, keyp, out_key);
1184         if (ret) {
1185                 DEBUG(0,("get_key_from_keytab: failed to copy key: %s\n", error_message(ret)));
1186                 goto out;
1187         }
1188                 
1189         smb_krb5_kt_free_entry(context, &entry);
1190         
1191 out:    
1192         krb5_kt_close(context, keytab);
1193         return ret;
1194 }
1195
1196 /* Prototypes */
1197
1198  krb5_error_code smb_krb5_get_keyinfo_from_ap_req(krb5_context context, 
1199                                                  const krb5_data *inbuf, 
1200                                                  krb5_kvno *kvno, 
1201                                                  krb5_enctype *enctype)
1202 {
1203 #ifdef HAVE_KRB5_DECODE_AP_REQ /* Heimdal */
1204         {
1205                 krb5_error_code ret;
1206                 krb5_ap_req ap_req;
1207                 
1208                 ret = krb5_decode_ap_req(context, inbuf, &ap_req);
1209                 if (ret)
1210                         return ret;
1211
1212                 *kvno = get_kvno_from_ap_req(&ap_req);
1213                 *enctype = get_enctype_from_ap_req(&ap_req);
1214
1215                 free_AP_REQ(&ap_req);
1216                 return 0;
1217         }
1218 #endif
1219
1220         /* Possibly not an appropriate error code. */
1221         return KRB5KDC_ERR_BADOPTION;
1222 }
1223
1224  krb5_error_code krb5_rd_req_return_keyblock_from_keytab(krb5_context context,
1225                                                         krb5_auth_context *auth_context,
1226                                                         const krb5_data *inbuf,
1227                                                         krb5_const_principal server,
1228                                                         krb5_keytab keytab,
1229                                                         krb5_flags *ap_req_options,
1230                                                         krb5_ticket **ticket, 
1231                                                         krb5_keyblock **keyblock)
1232 {
1233         krb5_error_code ret;
1234         krb5_kvno kvno;
1235         krb5_enctype enctype;
1236         krb5_keyblock *local_keyblock;
1237
1238         ret = krb5_rd_req(context, 
1239                           auth_context, 
1240                           inbuf, 
1241                           server, 
1242                           keytab, 
1243                           ap_req_options, 
1244                           ticket);
1245         if (ret) {
1246                 return ret;
1247         }
1248         
1249 #ifdef KRB5_TICKET_HAS_KEYINFO
1250         enctype = (*ticket)->enc_part.enctype;
1251         kvno = (*ticket)->enc_part.kvno;
1252 #else
1253         ret = smb_krb5_get_keyinfo_from_ap_req(context, inbuf, &kvno, &enctype);
1254         if (ret) {
1255                 return ret;
1256         }
1257 #endif
1258
1259         ret = get_key_from_keytab(context, 
1260                                   server,
1261                                   enctype,
1262                                   kvno,
1263                                   &local_keyblock);
1264         if (ret) {
1265                 DEBUG(0,("krb5_rd_req_return_keyblock_from_keytab: failed to call get_key_from_keytab\n"));
1266                 goto out;
1267         }
1268
1269 out:
1270         if (ret && local_keyblock != NULL) {
1271                 krb5_free_keyblock(context, local_keyblock);
1272         } else {
1273                 *keyblock = local_keyblock;
1274         }
1275
1276         return ret;
1277 }
1278
1279  krb5_error_code smb_krb5_parse_name_norealm(krb5_context context, 
1280                                             const char *name, 
1281                                             krb5_principal *principal)
1282 {
1283 #ifdef HAVE_KRB5_PARSE_NAME_NOREALM
1284         return smb_krb5_parse_name_norealm_conv(context, name, principal);
1285 #endif
1286
1287         /* we are cheating here because parse_name will in fact set the realm.
1288          * We don't care as the only caller of smb_krb5_parse_name_norealm
1289          * ignores the realm anyway when calling
1290          * smb_krb5_principal_compare_any_realm later - Guenther */
1291
1292         return smb_krb5_parse_name(context, name, principal);
1293 }
1294
1295  bool smb_krb5_principal_compare_any_realm(krb5_context context, 
1296                                           krb5_const_principal princ1, 
1297                                           krb5_const_principal princ2)
1298 {
1299 #ifdef HAVE_KRB5_PRINCIPAL_COMPARE_ANY_REALM
1300
1301         return krb5_principal_compare_any_realm(context, princ1, princ2);
1302
1303 /* krb5_princ_size is a macro in MIT */
1304 #elif defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size)
1305
1306         int i, len1, len2;
1307         const krb5_data *p1, *p2;
1308
1309         len1 = krb5_princ_size(context, princ1);
1310         len2 = krb5_princ_size(context, princ2);
1311
1312         if (len1 != len2)
1313                 return False;
1314
1315         for (i = 0; i < len1; i++) {
1316
1317                 p1 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ1), i);
1318                 p2 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ2), i);
1319
1320                 if (p1->length != p2->length || memcmp(p1->data, p2->data, p1->length))
1321                         return False;
1322         }
1323
1324         return True;
1325 #else
1326 #error NO_SUITABLE_PRINCIPAL_COMPARE_FUNCTION
1327 #endif
1328 }
1329
1330  krb5_error_code smb_krb5_renew_ticket(const char *ccache_string,       /* FILE:/tmp/krb5cc_0 */
1331                                        const char *client_string,       /* gd@BER.SUSE.DE */
1332                                        const char *service_string,      /* krbtgt/BER.SUSE.DE@BER.SUSE.DE */
1333                                        time_t *expire_time)
1334 {
1335         krb5_error_code ret;
1336         krb5_context context = NULL;
1337         krb5_ccache ccache = NULL;
1338         krb5_principal client = NULL;
1339         krb5_creds creds, creds_in, *creds_out = NULL;
1340
1341         ZERO_STRUCT(creds);
1342         ZERO_STRUCT(creds_in);
1343
1344         initialize_krb5_error_table();
1345         ret = krb5_init_context(&context);
1346         if (ret) {
1347                 goto done;
1348         }
1349
1350         if (!ccache_string) {
1351                 ccache_string = krb5_cc_default_name(context);
1352         }
1353
1354         if (!ccache_string) {
1355                 ret = EINVAL;
1356                 goto done;
1357         }
1358
1359         DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
1360
1361         /* FIXME: we should not fall back to defaults */
1362         ret = krb5_cc_resolve(context, CONST_DISCARD(char *, ccache_string), &ccache);
1363         if (ret) {
1364                 goto done;
1365         }
1366
1367         if (client_string) {
1368                 ret = smb_krb5_parse_name(context, client_string, &client);
1369                 if (ret) {
1370                         goto done;
1371                 }
1372         } else {
1373                 ret = krb5_cc_get_principal(context, ccache, &client);
1374                 if (ret) {
1375                         goto done;
1376                 }
1377         }
1378
1379 #ifdef HAVE_KRB5_GET_RENEWED_CREDS      /* MIT */
1380         {
1381                 ret = krb5_get_renewed_creds(context, &creds, client, ccache, CONST_DISCARD(char *, service_string));
1382                 if (ret) {
1383                         DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
1384                         goto done;
1385                 }
1386         }
1387 #elif defined(HAVE_KRB5_GET_KDC_CRED)   /* Heimdal */
1388         {
1389                 krb5_kdc_flags flags;
1390                 krb5_realm *client_realm = NULL;
1391
1392                 ret = krb5_copy_principal(context, client, &creds_in.client);
1393                 if (ret) {
1394                         goto done;
1395                 }
1396
1397                 if (service_string) {
1398                         ret = smb_krb5_parse_name(context, service_string, &creds_in.server);
1399                         if (ret) { 
1400                                 goto done;
1401                         }
1402                 } else {
1403                         /* build tgt service by default */
1404                         client_realm = krb5_princ_realm(context, creds_in.client);
1405                         if (!client_realm) {
1406                                 ret = ENOMEM;
1407                                 goto done;
1408                         }
1409                         ret = krb5_make_principal(context, &creds_in.server, *client_realm, KRB5_TGS_NAME, *client_realm, NULL);
1410                         if (ret) {
1411                                 goto done;
1412                         }
1413                 }
1414
1415                 flags.i = 0;
1416                 flags.b.renewable = flags.b.renew = True;
1417
1418                 ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &creds_in, &creds_out);
1419                 if (ret) {
1420                         DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
1421                         goto done;
1422                 }
1423
1424                 creds = *creds_out;
1425         }
1426 #else
1427 #error NO_SUITABLE_KRB5_TICKET_RENEW_FUNCTION_AVAILABLE
1428 #endif
1429
1430         /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
1431         ret = krb5_cc_initialize(context, ccache, client);
1432         if (ret) {
1433                 goto done;
1434         }
1435         
1436         ret = krb5_cc_store_cred(context, ccache, &creds);
1437
1438         if (expire_time) {
1439                 *expire_time = (time_t) creds.times.endtime;
1440         }
1441
1442 done:
1443         krb5_free_cred_contents(context, &creds_in);
1444
1445         if (creds_out) {
1446                 krb5_free_creds(context, creds_out);
1447         } else {
1448                 krb5_free_cred_contents(context, &creds);
1449         }
1450
1451         if (client) {
1452                 krb5_free_principal(context, client);
1453         }
1454         if (ccache) {
1455                 krb5_cc_close(context, ccache);
1456         }
1457         if (context) {
1458                 krb5_free_context(context);
1459         }
1460
1461         return ret;
1462 }
1463
1464  krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
1465 {
1466         krb5_error_code ret = 0;
1467         if (addr == NULL) {
1468                 return ret;
1469         }
1470 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1471         krb5_free_addresses(context, addr->addrs);
1472 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1473         ret = krb5_free_addresses(context, addr->addrs);
1474         SAFE_FREE(addr->addrs);
1475 #endif
1476         SAFE_FREE(addr);
1477         addr = NULL;
1478         return ret;
1479 }
1480
1481  krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr)
1482 {
1483         krb5_error_code ret = 0;
1484         nstring buf;
1485 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1486         krb5_address **addrs = NULL;
1487 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1488         krb5_addresses *addrs = NULL;
1489 #endif
1490
1491         *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
1492         if (*kerb_addr == NULL) {
1493                 return ENOMEM;
1494         }
1495
1496         put_name(buf, global_myname(), ' ', 0x20);
1497
1498 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1499         {
1500                 int num_addr = 2;
1501
1502                 addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
1503                 if (addrs == NULL) {
1504                         SAFE_FREE(*kerb_addr);
1505                         return ENOMEM;
1506                 }
1507
1508                 memset(addrs, 0, sizeof(krb5_address *) * num_addr);
1509
1510                 addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1511                 if (addrs[0] == NULL) {
1512                         SAFE_FREE(addrs);
1513                         SAFE_FREE(*kerb_addr);
1514                         return ENOMEM;
1515                 }
1516
1517                 addrs[0]->magic = KV5M_ADDRESS;
1518                 addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
1519                 addrs[0]->length = MAX_NETBIOSNAME_LEN;
1520                 addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
1521                 if (addrs[0]->contents == NULL) {
1522                         SAFE_FREE(addrs[0]);
1523                         SAFE_FREE(addrs);
1524                         SAFE_FREE(*kerb_addr);
1525                         return ENOMEM;
1526                 }
1527
1528                 memcpy(addrs[0]->contents, buf, addrs[0]->length);
1529
1530                 addrs[1] = NULL;
1531         }
1532 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1533         {
1534                 addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
1535                 if (addrs == NULL) {
1536                         SAFE_FREE(*kerb_addr);
1537                         return ENOMEM;
1538                 }
1539
1540                 memset(addrs, 0, sizeof(krb5_addresses));
1541
1542                 addrs->len = 1;
1543                 addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1544                 if (addrs->val == NULL) {
1545                         SAFE_FREE(addrs);
1546                         SAFE_FREE(kerb_addr);
1547                         return ENOMEM;
1548                 }
1549
1550                 addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
1551                 addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
1552                 addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
1553                 if (addrs->val[0].address.data == NULL) {
1554                         SAFE_FREE(addrs->val);
1555                         SAFE_FREE(addrs);
1556                         SAFE_FREE(*kerb_addr);
1557                         return ENOMEM;
1558                 }
1559
1560                 memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
1561         }
1562 #else
1563 #error UNKNOWN_KRB5_ADDRESS_FORMAT
1564 #endif
1565         (*kerb_addr)->addrs = addrs;
1566
1567         return ret;
1568 }
1569
1570  void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
1571 {
1572 #ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
1573         krb5_free_error_contents(context, krberror);
1574 #else /* MIT */
1575         krb5_free_error(context, krberror);
1576 #endif
1577 }
1578
1579  krb5_error_code handle_krberror_packet(krb5_context context,
1580                                         krb5_data *packet)
1581 {
1582         krb5_error_code ret;
1583         bool got_error_code = False;
1584
1585         DEBUG(10,("handle_krberror_packet: got error packet\n"));
1586         
1587 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
1588         {
1589                 krb5_error krberror;
1590
1591                 if ((ret = krb5_rd_error(context, packet, &krberror))) {
1592                         DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
1593                                 error_message(ret)));
1594                         return ret;
1595                 }
1596
1597                 if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
1598                         ret = (krb5_error_code) krberror.error_code;
1599                         got_error_code = True;
1600                 }
1601
1602                 smb_krb5_free_error(context, &krberror);
1603         }
1604 #else /* MIT */
1605         {
1606                 krb5_error *krberror;
1607
1608                 if ((ret = krb5_rd_error(context, packet, &krberror))) {
1609                         DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
1610                                 error_message(ret)));
1611                         return ret;
1612                 }
1613
1614                 if (krberror->e_data.data == NULL) {
1615 #if defined(ERROR_TABLE_BASE_krb5)
1616                         ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
1617 #else
1618                         ret = (krb5_error_code)krberror->error;
1619 #endif
1620                         got_error_code = True;
1621                 }
1622                 smb_krb5_free_error(context, krberror);
1623         }
1624 #endif
1625         if (got_error_code) {
1626                 DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n", 
1627                         error_message(ret), ret));
1628         }
1629         return ret;
1630 }
1631
1632  krb5_error_code smb_krb5_get_init_creds_opt_alloc(krb5_context context,
1633                                             krb5_get_init_creds_opt **opt)
1634 {
1635 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC
1636         /* Heimdal or modern MIT version */
1637         return krb5_get_init_creds_opt_alloc(context, opt);
1638 #else
1639         /* Historical MIT version */
1640         krb5_get_init_creds_opt *my_opt;
1641
1642         *opt = NULL;
1643
1644         if ((my_opt = SMB_MALLOC_P(krb5_get_init_creds_opt)) == NULL) {
1645                 return ENOMEM;
1646         }
1647
1648         krb5_get_init_creds_opt_init(my_opt);
1649
1650         *opt =  my_opt;
1651         return 0;
1652 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_ALLOC  */
1653 }
1654
1655  void smb_krb5_get_init_creds_opt_free(krb5_context context,
1656                                 krb5_get_init_creds_opt *opt)
1657 {
1658 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_FREE
1659
1660 #ifdef KRB5_CREDS_OPT_FREE_REQUIRES_CONTEXT
1661         /* Modern MIT or Heimdal version */
1662         krb5_get_init_creds_opt_free(context, opt);
1663 #else
1664         /* Heimdal version */
1665         krb5_get_init_creds_opt_free(opt);
1666 #endif /* KRB5_CREDS_OPT_FREE_REQUIRES_CONTEXT */
1667
1668 #else /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */
1669         /* Historical MIT version */
1670         SAFE_FREE(opt);
1671         opt = NULL;
1672 #endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_FREE */
1673 }
1674
1675  krb5_enctype smb_get_enctype_from_kt_entry(krb5_keytab_entry *kt_entry)
1676 {
1677         return KRB5_KEY_TYPE(KRB5_KT_KEY(kt_entry));
1678 }
1679
1680
1681 /* caller needs to free etype_s */
1682  krb5_error_code smb_krb5_enctype_to_string(krb5_context context, 
1683                                             krb5_enctype enctype, 
1684                                             char **etype_s)
1685 {
1686 #ifdef HAVE_KRB5_ENCTYPE_TO_STRING_WITH_KRB5_CONTEXT_ARG
1687         return krb5_enctype_to_string(context, enctype, etype_s); /* Heimdal */
1688 #elif defined(HAVE_KRB5_ENCTYPE_TO_STRING_WITH_SIZE_T_ARG)
1689         char buf[256];
1690         krb5_error_code ret = krb5_enctype_to_string(enctype, buf, 256); /* MIT */
1691         if (ret) {
1692                 return ret;
1693         }
1694         *etype_s = SMB_STRDUP(buf);
1695         if (!*etype_s) {
1696                 return ENOMEM;
1697         }
1698         return ret;
1699 #else
1700 #error UNKNOWN_KRB5_ENCTYPE_TO_STRING_FUNCTION
1701 #endif
1702 }
1703
1704  krb5_error_code smb_krb5_mk_error(krb5_context context,
1705                                 krb5_error_code error_code,
1706                                 const krb5_principal server,
1707                                 krb5_data *reply)
1708 {
1709 #ifdef HAVE_SHORT_KRB5_MK_ERROR_INTERFACE /* MIT */
1710         /*
1711          * The MIT interface is *terrible*.
1712          * We have to construct this ourselves...
1713          */
1714         krb5_error e;
1715
1716         memset(&e, 0, sizeof(e));
1717         krb5_us_timeofday(context, &e.stime, &e.susec);
1718         e.server = server;
1719 #if defined(krb5_err_base)
1720         e.error = error_code - krb5_err_base;
1721 #elif defined(ERROR_TABLE_BASE_krb5)
1722         e.error = error_code - ERROR_TABLE_BASE_krb5;
1723 #else
1724         e.error = error_code; /* Almost certainly wrong, but what can we do... ? */
1725 #endif
1726
1727         return krb5_mk_error(context, &e, reply);
1728 #else /* Heimdal. */
1729         return krb5_mk_error(context,
1730                                 error_code,
1731                                 NULL,
1732                                 NULL, /* e_data */
1733                                 NULL,
1734                                 server,
1735                                 NULL,
1736                                 NULL,
1737                                 reply);
1738 #endif
1739 }
1740
1741 /**********************************************************************
1742  * Open a krb5 keytab with flags, handles readonly or readwrite access and
1743  * allows to process non-default keytab names.
1744  * @param context krb5_context 
1745  * @param keytab_name_req string
1746  * @param write_access bool if writable keytab is required
1747  * @param krb5_keytab pointer to krb5_keytab (close with krb5_kt_close())
1748  * @return krb5_error_code
1749 **********************************************************************/
1750
1751 /* This MAX_NAME_LEN is a constant defined in krb5.h */
1752 #ifndef MAX_KEYTAB_NAME_LEN
1753 #define MAX_KEYTAB_NAME_LEN 1100
1754 #endif
1755
1756  krb5_error_code smb_krb5_open_keytab(krb5_context context,
1757                                       const char *keytab_name_req,
1758                                       bool write_access,
1759                                       krb5_keytab *keytab)
1760 {
1761         krb5_error_code ret = 0;
1762         TALLOC_CTX *mem_ctx;
1763         char keytab_string[MAX_KEYTAB_NAME_LEN];
1764         char *kt_str = NULL;
1765         bool found_valid_name = False;
1766         const char *pragma = "FILE";
1767         const char *tmp = NULL;
1768
1769         if (!write_access && !keytab_name_req) {
1770                 /* caller just wants to read the default keytab readonly, so be it */
1771                 return krb5_kt_default(context, keytab);
1772         }
1773
1774         mem_ctx = talloc_init("smb_krb5_open_keytab");
1775         if (!mem_ctx) {
1776                 return ENOMEM;
1777         }
1778
1779 #ifdef HAVE_WRFILE_KEYTAB 
1780         if (write_access) {
1781                 pragma = "WRFILE";
1782         }
1783 #endif
1784
1785         if (keytab_name_req) {
1786
1787                 if (strlen(keytab_name_req) > MAX_KEYTAB_NAME_LEN) {
1788                         ret = KRB5_CONFIG_NOTENUFSPACE;
1789                         goto out;
1790                 }
1791
1792                 if ((strncmp(keytab_name_req, "WRFILE:/", 8) == 0) || 
1793                     (strncmp(keytab_name_req, "FILE:/", 6) == 0)) {
1794                         tmp = keytab_name_req;
1795                         goto resolve;
1796                 }
1797
1798                 if (keytab_name_req[0] != '/') {
1799                         ret = KRB5_KT_BADNAME;
1800                         goto out;
1801                 }
1802
1803                 tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, keytab_name_req);
1804                 if (!tmp) {
1805                         ret = ENOMEM;
1806                         goto out;
1807                 }
1808
1809                 goto resolve;
1810         }
1811
1812         /* we need to handle more complex keytab_strings, like:
1813          * "ANY:FILE:/etc/krb5.keytab,krb4:/etc/srvtab" */
1814
1815         ret = krb5_kt_default_name(context, &keytab_string[0], MAX_KEYTAB_NAME_LEN - 2);
1816         if (ret) {
1817                 goto out;
1818         }
1819
1820         DEBUG(10,("smb_krb5_open_keytab: krb5_kt_default_name returned %s\n", keytab_string));
1821
1822         tmp = talloc_strdup(mem_ctx, keytab_string);
1823         if (!tmp) {
1824                 ret = ENOMEM;
1825                 goto out;
1826         }
1827
1828         if (strncmp(tmp, "ANY:", 4) == 0) {
1829                 tmp += 4;
1830         }
1831
1832         memset(&keytab_string, '\0', sizeof(keytab_string));
1833
1834         while (next_token_talloc(mem_ctx, &tmp, &kt_str, ",")) {
1835                 if (strncmp(kt_str, "WRFILE:", 7) == 0) {
1836                         found_valid_name = True;
1837                         tmp = kt_str;
1838                         tmp += 7;
1839                 }
1840
1841                 if (strncmp(kt_str, "FILE:", 5) == 0) {
1842                         found_valid_name = True;
1843                         tmp = kt_str;
1844                         tmp += 5;
1845                 }
1846
1847                 if (tmp[0] == '/') {
1848                         /* Treat as a FILE: keytab definition. */
1849                         found_valid_name = true;
1850                 }
1851
1852                 if (found_valid_name) {
1853                         if (tmp[0] != '/') {
1854                                 ret = KRB5_KT_BADNAME;
1855                                 goto out;
1856                         }
1857
1858                         tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, tmp);
1859                         if (!tmp) {
1860                                 ret = ENOMEM;
1861                                 goto out;
1862                         }
1863                         break;
1864                 }
1865         }
1866
1867         if (!found_valid_name) {
1868                 ret = KRB5_KT_UNKNOWN_TYPE;
1869                 goto out;
1870         }
1871
1872  resolve:
1873         DEBUG(10,("smb_krb5_open_keytab: resolving: %s\n", tmp));
1874         ret = krb5_kt_resolve(context, tmp, keytab);
1875
1876  out:
1877         TALLOC_FREE(mem_ctx);
1878         return ret;
1879 }
1880
1881 krb5_error_code smb_krb5_keytab_name(TALLOC_CTX *mem_ctx,
1882                                      krb5_context context,
1883                                      krb5_keytab keytab,
1884                                      const char **keytab_name)
1885 {
1886         char keytab_string[MAX_KEYTAB_NAME_LEN];
1887         krb5_error_code ret = 0;
1888
1889         ret = krb5_kt_get_name(context, keytab,
1890                                keytab_string, MAX_KEYTAB_NAME_LEN - 2);
1891         if (ret) {
1892                 return ret;
1893         }
1894
1895         *keytab_name = talloc_strdup(mem_ctx, keytab_string);
1896         if (!*keytab_name) {
1897                 return ENOMEM;
1898         }
1899
1900         return ret;
1901 }
1902
1903 #if defined(HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE) && \
1904     defined(HAVE_KRB5_GET_CREDS_OPT_ALLOC) && \
1905     defined(HAVE_KRB5_GET_CREDS)
1906 static krb5_error_code smb_krb5_get_credentials_for_user_opt(krb5_context context,
1907                                                              krb5_ccache ccache,
1908                                                              krb5_principal me,
1909                                                              krb5_principal server,
1910                                                              krb5_principal impersonate_princ,
1911                                                              krb5_creds **out_creds)
1912 {
1913         krb5_error_code ret;
1914         krb5_get_creds_opt opt;
1915
1916         ret = krb5_get_creds_opt_alloc(context, &opt);
1917         if (ret) {
1918                 goto done;
1919         }
1920         krb5_get_creds_opt_add_options(context, opt, KRB5_GC_FORWARDABLE);
1921
1922         if (impersonate_princ) {
1923                 ret = krb5_get_creds_opt_set_impersonate(context, opt,
1924                                                          impersonate_princ);
1925                 if (ret) {
1926                         goto done;
1927                 }
1928         }
1929
1930         ret = krb5_get_creds(context, opt, ccache, server, out_creds);
1931         if (ret) {
1932                 goto done;
1933         }
1934
1935  done:
1936         if (opt) {
1937                 krb5_get_creds_opt_free(context, opt);
1938         }
1939         return ret;
1940 }
1941 #endif /* HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE */
1942
1943 #ifdef HAVE_KRB5_GET_CREDENTIALS_FOR_USER
1944 static krb5_error_code smb_krb5_get_credentials_for_user(krb5_context context,
1945                                                          krb5_ccache ccache,
1946                                                          krb5_principal me,
1947                                                          krb5_principal server,
1948                                                          krb5_principal impersonate_princ,
1949                                                          krb5_creds **out_creds)
1950 {
1951         krb5_error_code ret;
1952         krb5_creds in_creds;
1953
1954 #if !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER
1955 krb5_error_code KRB5_CALLCONV
1956 krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
1957                               krb5_ccache ccache, krb5_creds *in_creds,
1958                               krb5_data *subject_cert,
1959                               krb5_creds **out_creds);
1960 #endif /* !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER */
1961
1962         ZERO_STRUCT(in_creds);
1963
1964         if (impersonate_princ) {
1965
1966                 in_creds.server = me;
1967                 in_creds.client = impersonate_princ;
1968
1969                 ret = krb5_get_credentials_for_user(context,
1970                                                     0, /* krb5_flags options */
1971                                                     ccache,
1972                                                     &in_creds,
1973                                                     NULL, /* krb5_data *subject_cert */
1974                                                     out_creds);
1975         } else {
1976                 in_creds.client = me;
1977                 in_creds.server = server;
1978
1979                 ret = krb5_get_credentials(context, 0, ccache,
1980                                            &in_creds, out_creds);
1981         }
1982
1983         return ret;
1984 }
1985 #endif /* HAVE_KRB5_GET_CREDENTIALS_FOR_USER */
1986
1987 /*
1988  * smb_krb5_get_credentials
1989  *
1990  * @brief Get krb5 credentials for a server
1991  *
1992  * @param[in] context           An initialized krb5_context
1993  * @param[in] ccache            An initialized krb5_ccache
1994  * @param[in] me                The krb5_principal of the caller
1995  * @param[in] server            The krb5_principal of the requested service
1996  * @param[in] impersonate_princ The krb5_principal of a user to impersonate as (optional)
1997  * @param[out] out_creds        The returned krb5_creds structure
1998  * @return krb5_error_code
1999  *
2000  */
2001 krb5_error_code smb_krb5_get_credentials(krb5_context context,
2002                                          krb5_ccache ccache,
2003                                          krb5_principal me,
2004                                          krb5_principal server,
2005                                          krb5_principal impersonate_princ,
2006                                          krb5_creds **out_creds)
2007 {
2008         krb5_error_code ret;
2009         krb5_creds *creds = NULL;
2010
2011         *out_creds = NULL;
2012
2013         if (impersonate_princ) {
2014 #ifdef HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE /* Heimdal */
2015                 ret = smb_krb5_get_credentials_for_user_opt(context, ccache, me, server, impersonate_princ, &creds);
2016 #elif defined(HAVE_KRB5_GET_CREDENTIALS_FOR_USER) /* MIT */
2017                 ret = smb_krb5_get_credentials_for_user(context, ccache, me, server, impersonate_princ, &creds);
2018 #else
2019                 ret = ENOTSUP;
2020 #endif
2021         } else {
2022                 krb5_creds in_creds;
2023
2024                 ZERO_STRUCT(in_creds);
2025
2026                 in_creds.client = me;
2027                 in_creds.server = server;
2028
2029                 ret = krb5_get_credentials(context, 0, ccache,
2030                                            &in_creds, &creds);
2031         }
2032         if (ret) {
2033                 goto done;
2034         }
2035
2036         ret = krb5_cc_store_cred(context, ccache, creds);
2037         if (ret) {
2038                 goto done;
2039         }
2040
2041         if (out_creds) {
2042                 *out_creds = creds;
2043         }
2044
2045  done:
2046         if (creds && ret) {
2047                 krb5_free_creds(context, creds);
2048         }
2049
2050         return ret;
2051 }
2052
2053 /*
2054  * smb_krb5_get_creds
2055  *
2056  * @brief Get krb5 credentials for a server
2057  *
2058  * @param[in] server_s          The string name of the service
2059  * @param[in] time_offset       The offset to the KDCs time in seconds (optional)
2060  * @param[in] cc                The krb5 credential cache string name (optional)
2061  * @param[in] impersonate_princ_s The string principal name to impersonate (optional)
2062  * @param[out] creds_p          The returned krb5_creds structure
2063  * @return krb5_error_code
2064  *
2065  */
2066 krb5_error_code smb_krb5_get_creds(const char *server_s,
2067                                    time_t time_offset,
2068                                    const char *cc,
2069                                    const char *impersonate_princ_s,
2070                                    krb5_creds **creds_p)
2071 {
2072         krb5_error_code ret;
2073         krb5_context context = NULL;
2074         krb5_principal me = NULL;
2075         krb5_principal server = NULL;
2076         krb5_principal impersonate_princ = NULL;
2077         krb5_creds *creds = NULL;
2078         krb5_ccache ccache = NULL;
2079
2080         *creds_p = NULL;
2081
2082         initialize_krb5_error_table();
2083         ret = krb5_init_context(&context);
2084         if (ret) {
2085                 goto done;
2086         }
2087
2088         if (time_offset != 0) {
2089                 krb5_set_real_time(context, time(NULL) + time_offset, 0);
2090         }
2091
2092         ret = krb5_cc_resolve(context, cc ? cc :
2093                 krb5_cc_default_name(context), &ccache);
2094         if (ret) {
2095                 goto done;
2096         }
2097
2098         ret = krb5_cc_get_principal(context, ccache, &me);
2099         if (ret) {
2100                 goto done;
2101         }
2102
2103         ret = smb_krb5_parse_name(context, server_s, &server);
2104         if (ret) {
2105                 goto done;
2106         }
2107
2108         if (impersonate_princ_s) {
2109                 ret = smb_krb5_parse_name(context, impersonate_princ_s,
2110                                           &impersonate_princ);
2111                 if (ret) {
2112                         goto done;
2113                 }
2114         }
2115
2116         ret = smb_krb5_get_credentials(context, ccache,
2117                                        me, server, impersonate_princ,
2118                                        &creds);
2119         if (ret) {
2120                 goto done;
2121         }
2122
2123         ret = krb5_cc_store_cred(context, ccache, creds);
2124         if (ret) {
2125                 goto done;
2126         }
2127
2128         if (creds_p) {
2129                 *creds_p = creds;
2130         }
2131
2132         DEBUG(1,("smb_krb5_get_creds: got ticket for %s\n",
2133                 server_s));
2134
2135         if (impersonate_princ_s) {
2136                 char *client = NULL;
2137
2138                 ret = smb_krb5_unparse_name(talloc_tos(), context, creds->client, &client);
2139                 if (ret) {
2140                         goto done;
2141                 }
2142                 DEBUGADD(1,("smb_krb5_get_creds: using S4U2SELF impersonation as %s\n",
2143                         client));
2144                 TALLOC_FREE(client);
2145         }
2146
2147  done:
2148         if (!context) {
2149                 return ret;
2150         }
2151
2152         if (creds && ret) {
2153                 krb5_free_creds(context, creds);
2154         }
2155         if (server) {
2156                 krb5_free_principal(context, server);
2157         }
2158         if (me) {
2159                 krb5_free_principal(context, me);
2160         }
2161         if (impersonate_princ) {
2162                 krb5_free_principal(context, impersonate_princ);
2163         }
2164         if (ccache) {
2165                 krb5_cc_close(context, ccache);
2166         }
2167         krb5_free_context(context);
2168
2169         return ret;
2170 }
2171
2172 /*
2173  * smb_krb5_principal_get_realm
2174  *
2175  * @brief Get realm of a principal
2176  *
2177  * @param[in] context           The krb5_context
2178  * @param[in] principal         The principal
2179  * @return pointer to the realm
2180  *
2181  */
2182
2183 char *smb_krb5_principal_get_realm(krb5_context context,
2184                                    krb5_principal principal)
2185 {
2186 #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM /* Heimdal */
2187         return krb5_principal_get_realm(context, principal);
2188 #elif defined(krb5_princ_realm) /* MIT */
2189         krb5_data *realm;
2190         realm = krb5_princ_realm(context, principal);
2191         return (char *)realm->data;
2192 #else
2193         return NULL;
2194 #endif
2195 }
2196
2197 #else /* HAVE_KRB5 */
2198  /* this saves a few linking headaches */
2199  int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
2200                         const char *principal, time_t time_offset,
2201                         DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
2202                         uint32_t extra_ap_opts,
2203                         const char *ccname, time_t *tgs_expire,
2204                         const char *impersonate_princ_s)
2205 {
2206          DEBUG(0,("NO KERBEROS SUPPORT\n"));
2207          return 1;
2208 }
2209
2210 bool unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_pac_data)
2211 {
2212         DEBUG(0,("NO KERBEROS SUPPORT\n"));
2213         return false;
2214 }
2215
2216 #endif /* HAVE_KRB5 */