krb5_wrap: Fix build error when not using heimdal.
[samba.git] / lib / krb5_wrap / krb5_samba.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 "system/filesys.h"
25 #include "krb5_samba.h"
26 #include "lib/util/asn1.h"
27
28 #ifdef HAVE_COM_ERR_H
29 #include <com_err.h>
30 #endif /* HAVE_COM_ERR_H */
31
32 #ifndef KRB5_AUTHDATA_WIN2K_PAC
33 #define KRB5_AUTHDATA_WIN2K_PAC 128
34 #endif
35
36 #ifndef KRB5_AUTHDATA_IF_RELEVANT
37 #define KRB5_AUTHDATA_IF_RELEVANT 1
38 #endif
39
40 #ifdef HAVE_KRB5
41
42 #define GSSAPI_CHECKSUM      0x8003             /* Checksum type value for Kerberos */
43 #define GSSAPI_BNDLENGTH     16                 /* Bind Length (rfc-1964 pg.3) */
44 #define GSSAPI_CHECKSUM_SIZE (4+GSSAPI_BNDLENGTH+4) /* Length of bind length,
45                                                         bind field, flags field. */
46 #define GSS_C_DELEG_FLAG 1
47
48 /* MIT krb5 1.7beta3 (in Ubuntu Karmic) is missing the prototype,
49    but still has the symbol */
50 #if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE
51 krb5_error_code krb5_auth_con_set_req_cksumtype(
52         krb5_context     context,
53         krb5_auth_context      auth_context,
54         krb5_cksumtype     cksumtype);
55 #endif
56
57 #if !defined(SMB_MALLOC)
58 #undef malloc
59 #define SMB_MALLOC(s) malloc((s))
60 #endif
61
62 #ifndef SMB_STRDUP
63 #define SMB_STRDUP(s) strdup(s)
64 #endif
65
66 #if !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
67
68 #if defined(HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES)
69
70 /* With MIT kerberos, we should use krb5_set_default_tgs_enctypes in preference
71  * to krb5_set_default_tgs_ktypes. See
72  *         http://lists.samba.org/archive/samba-technical/2006-July/048271.html
73  *
74  * If the MIT libraries are not exporting internal symbols, we will end up in
75  * this branch, which is correct. Otherwise we will continue to use the
76  * internal symbol
77  */
78  krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
79 {
80     return krb5_set_default_tgs_enctypes(ctx, enc);
81 }
82
83 #elif defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES)
84
85 /* Heimdal */
86  krb5_error_code krb5_set_default_tgs_ktypes(krb5_context ctx, const krb5_enctype *enc)
87 {
88         return krb5_set_default_in_tkt_etypes(ctx, enc);
89 }
90
91 #endif /* HAVE_KRB5_SET_DEFAULT_TGS_ENCTYPES */
92
93 #endif /* HAVE_KRB5_SET_DEFAULT_TGS_KTYPES */
94
95 #if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
96 /* HEIMDAL */
97  bool setup_kaddr( krb5_address *pkaddr, struct sockaddr_storage *paddr)
98 {
99         memset(pkaddr, '\0', sizeof(krb5_address));
100 #if defined(HAVE_IPV6) && defined(KRB5_ADDRESS_INET6)
101         if (paddr->ss_family == AF_INET6) {
102                 pkaddr->addr_type = KRB5_ADDRESS_INET6;
103                 pkaddr->address.length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
104                 pkaddr->address.data = (char *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
105                 return true;
106         }
107 #endif
108         if (paddr->ss_family == AF_INET) {
109                 pkaddr->addr_type = KRB5_ADDRESS_INET;
110                 pkaddr->address.length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
111                 pkaddr->address.data = (char *)&(((struct sockaddr_in *)paddr)->sin_addr);
112                 return true;
113         }
114         return false;
115 }
116 #elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
117 /* MIT */
118 bool setup_kaddr( krb5_address *pkaddr, struct sockaddr_storage *paddr)
119 {
120         memset(pkaddr, '\0', sizeof(krb5_address));
121 #if defined(HAVE_IPV6) && defined(ADDRTYPE_INET6)
122         if (paddr->ss_family == AF_INET6) {
123                 pkaddr->addrtype = ADDRTYPE_INET6;
124                 pkaddr->length = sizeof(((struct sockaddr_in6 *)paddr)->sin6_addr);
125                 pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in6 *)paddr)->sin6_addr);
126                 return true;
127         }
128 #endif
129         if (paddr->ss_family == AF_INET) {
130                 pkaddr->addrtype = ADDRTYPE_INET;
131                 pkaddr->length = sizeof(((struct sockaddr_in *)paddr)->sin_addr);
132                 pkaddr->contents = (krb5_octet *)&(((struct sockaddr_in *)paddr)->sin_addr);
133                 return true;
134         }
135         return false;
136 }
137 #else
138 #error UNKNOWN_ADDRTYPE
139 #endif
140
141 krb5_error_code smb_krb5_mk_error(krb5_context context,
142                                   krb5_error_code error_code,
143                                   const char *e_text,
144                                   krb5_data *e_data,
145                                   krb5_data *enc_err)
146 {
147         krb5_error_code code = EINVAL;
148 #ifdef SAMBA4_USES_HEIMDAL
149         code = krb5_mk_error(context,
150                              error_code,
151                              e_text,
152                              e_data,
153                              NULL, /* client */
154                              NULL, /* server */
155                              NULL, /* client_time */
156                              NULL, /* client_usec */
157                              enc_err);
158 #else
159         krb5_error dec_err = {
160                 .error = error_code,
161         };
162
163         if (e_text != NULL) {
164                 dec_err.text.length = strlen(e_text);
165                 dec_err.text.data = discard_const_p(char, e_text);
166         }
167         if (e_data != NULL) {
168                 dec_err.e_data = *e_data;
169         }
170
171         code = krb5_mk_error(context,
172                              &dec_err,
173                              enc_err);
174 #endif
175         return code;
176 }
177
178 /**
179 * @brief Create a keyblock based on input parameters
180 *
181 * @param context        The krb5_context
182 * @param host_princ     The krb5_principal to use
183 * @param salt           The optional salt, if omitted, salt is calculated with
184 *                       the provided principal.
185 * @param password       The krb5_data containing the password
186 * @param enctype        The krb5_enctype to use for the keyblock generation
187 * @param key            The returned krb5_keyblock, caller needs to free with
188 *                       krb5_free_keyblock().
189 *
190 * @return krb5_error_code
191 */
192 int smb_krb5_create_key_from_string(krb5_context context,
193                                     krb5_const_principal host_princ,
194                                     krb5_data *salt,
195                                     krb5_data *password,
196                                     krb5_enctype enctype,
197                                     krb5_keyblock *key)
198 {
199         int ret = 0;
200
201         if (host_princ == NULL && salt == NULL) {
202                 return -1;
203         }
204
205 #if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_C_STRING_TO_KEY)
206 {/* MIT */
207         krb5_data _salt;
208
209         if (salt == NULL) {
210                 ret = krb5_principal2salt(context, host_princ, &_salt);
211                 if (ret) {
212                         DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret)));
213                         return ret;
214                 }
215         } else {
216                 _salt = *salt;
217         }
218         ret = krb5_c_string_to_key(context, enctype, password, &_salt, key);
219         if (salt == NULL) {
220                 SAFE_FREE(_salt.data);
221         }
222 }
223 #elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
224 {/* Heimdal */
225         krb5_salt _salt;
226
227         if (salt == NULL) {
228                 ret = krb5_get_pw_salt(context, host_princ, &_salt);
229                 if (ret) {
230                         DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret)));
231                         return ret;
232                 }
233         } else {
234                 _salt.saltvalue = *salt;
235                 _salt.salttype = KRB5_PW_SALT;
236         }
237
238         ret = krb5_string_to_key_salt(context, enctype, (const char *)password->data, _salt, key);
239         if (salt == NULL) {
240                 krb5_free_salt(context, _salt);
241         }
242 }
243 #else
244 #error UNKNOWN_CREATE_KEY_FUNCTIONS
245 #endif
246         return ret;
247 }
248
249 /**
250 * @brief Create a salt for a given principal
251 *
252 * @param context        The initialized krb5_context
253 * @param host_princ     The krb5_principal to create the salt for
254 * @param psalt          A pointer to a krb5_data struct
255 *
256 * caller has to free the contents of psalt with kerberos_free_data_contents
257 * when function has succeeded
258 *
259 * @return krb5_error_code, returns 0 on success, error code otherwise
260 */
261
262 int smb_krb5_get_pw_salt(krb5_context context,
263                          krb5_const_principal host_princ,
264                          krb5_data *psalt)
265 #if defined(HAVE_KRB5_GET_PW_SALT)
266 /* Heimdal */
267 {
268         int ret;
269         krb5_salt salt;
270
271         ret = krb5_get_pw_salt(context, host_princ, &salt);
272         if (ret) {
273                 return ret;
274         }
275
276         psalt->data = salt.saltvalue.data;
277         psalt->length = salt.saltvalue.length;
278
279         return ret;
280 }
281 #elif defined(HAVE_KRB5_PRINCIPAL2SALT)
282 /* MIT */
283 {
284         return krb5_principal2salt(context, host_princ, psalt);
285 }
286 #else
287 #error UNKNOWN_SALT_FUNCTIONS
288 #endif
289
290 #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
291  krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
292                                             krb5_enctype **enctypes)
293 {
294         return krb5_get_permitted_enctypes(context, enctypes);
295 }
296 #elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
297  krb5_error_code get_kerberos_allowed_etypes(krb5_context context,
298                                             krb5_enctype **enctypes)
299 {
300 #ifdef HAVE_KRB5_PDU_NONE_DECL
301         return krb5_get_default_in_tkt_etypes(context, KRB5_PDU_NONE, enctypes);
302 #else
303         return krb5_get_default_in_tkt_etypes(context, enctypes);
304 #endif
305 }
306 #else
307 #error UNKNOWN_GET_ENCTYPES_FUNCTIONS
308 #endif
309
310 #if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
311  krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context,
312                                         krb5_auth_context auth_context,
313                                         krb5_keyblock *keyblock)
314 {
315         return krb5_auth_con_setkey(context, auth_context, keyblock);
316 }
317 #endif
318
319 bool unwrap_edata_ntstatus(TALLOC_CTX *mem_ctx,
320                            DATA_BLOB *edata,
321                            DATA_BLOB *edata_out)
322 {
323         DATA_BLOB edata_contents;
324         ASN1_DATA *data;
325         int edata_type;
326
327         if (!edata->length) {
328                 return false;
329         }
330
331         data = asn1_init(mem_ctx);
332         if (data == NULL) {
333                 return false;
334         }
335
336         if (!asn1_load(data, *edata)) goto err;
337         if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) goto err;
338         if (!asn1_start_tag(data, ASN1_CONTEXT(1))) goto err;
339         if (!asn1_read_Integer(data, &edata_type)) goto err;
340
341         if (edata_type != KRB5_PADATA_PW_SALT) {
342                 DEBUG(0,("edata is not of required type %d but of type %d\n",
343                         KRB5_PADATA_PW_SALT, edata_type));
344                 goto err;
345         }
346
347         if (!asn1_start_tag(data, ASN1_CONTEXT(2))) goto err;
348         if (!asn1_read_OctetString(data, talloc_tos(), &edata_contents)) goto err;
349         if (!asn1_end_tag(data)) goto err;
350         if (!asn1_end_tag(data)) goto err;
351         if (!asn1_end_tag(data)) goto err;
352         asn1_free(data);
353
354         *edata_out = data_blob_talloc(mem_ctx, edata_contents.data, edata_contents.length);
355
356         data_blob_free(&edata_contents);
357
358         return true;
359
360   err:
361
362         asn1_free(data);
363         return false;
364 }
365
366
367 static bool ads_cleanup_expired_creds(krb5_context context,
368                                       krb5_ccache  ccache,
369                                       krb5_creds  *credsp)
370 {
371         krb5_error_code retval;
372         const char *cc_type = krb5_cc_get_type(context, ccache);
373
374         DEBUG(3, ("ads_cleanup_expired_creds: Ticket in ccache[%s:%s] expiration %s\n",
375                   cc_type, krb5_cc_get_name(context, ccache),
376                   http_timestring(talloc_tos(), credsp->times.endtime)));
377
378         /* we will probably need new tickets if the current ones
379            will expire within 10 seconds.
380         */
381         if (credsp->times.endtime >= (time(NULL) + 10))
382                 return false;
383
384         /* heimdal won't remove creds from a file ccache, and
385            perhaps we shouldn't anyway, since internally we
386            use memory ccaches, and a FILE one probably means that
387            we're using creds obtained outside of our exectuable
388         */
389         if (strequal(cc_type, "FILE")) {
390                 DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a %s ccache\n", cc_type));
391                 return false;
392         }
393
394         retval = krb5_cc_remove_cred(context, ccache, 0, credsp);
395         if (retval) {
396                 DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
397                           error_message(retval)));
398                 /* If we have an error in this, we want to display it,
399                    but continue as though we deleted it */
400         }
401         return true;
402 }
403
404 /* Allocate and setup the auth context into the state we need. */
405
406 static krb5_error_code setup_auth_context(krb5_context context,
407                         krb5_auth_context *auth_context)
408 {
409         krb5_error_code retval;
410
411         retval = krb5_auth_con_init(context, auth_context );
412         if (retval) {
413                 DEBUG(1,("krb5_auth_con_init failed (%s)\n",
414                         error_message(retval)));
415                 return retval;
416         }
417
418         /* Ensure this is an addressless ticket. */
419         retval = krb5_auth_con_setaddrs(context, *auth_context, NULL, NULL);
420         if (retval) {
421                 DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n",
422                         error_message(retval)));
423         }
424
425         return retval;
426 }
427
428 #if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
429 static krb5_error_code create_gss_checksum(krb5_data *in_data, /* [inout] */
430                                                 uint32_t gss_flags)
431 {
432         unsigned int orig_length = in_data->length;
433         unsigned int base_cksum_size = GSSAPI_CHECKSUM_SIZE;
434         char *gss_cksum = NULL;
435
436         if (orig_length) {
437                 /* Extra length field for delgated ticket. */
438                 base_cksum_size += 4;
439         }
440
441         if ((unsigned int)base_cksum_size + orig_length <
442                         (unsigned int)base_cksum_size) {
443                 return EINVAL;
444         }
445
446         gss_cksum = (char *)SMB_MALLOC(base_cksum_size + orig_length);
447         if (gss_cksum == NULL) {
448                 return ENOMEM;
449         }
450
451         memset(gss_cksum, '\0', base_cksum_size + orig_length);
452         SIVAL(gss_cksum, 0, GSSAPI_BNDLENGTH);
453
454         /*
455          * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes.
456          * This matches the behavior of heimdal and mit.
457          *
458          * And it is needed to work against some closed source
459          * SMB servers.
460          *
461          * See bug #7883
462          */
463         memset(&gss_cksum[4], 0x00, GSSAPI_BNDLENGTH);
464
465         SIVAL(gss_cksum, 20, gss_flags);
466
467         if (orig_length) {
468                 SSVAL(gss_cksum, 24, 1); /* The Delegation Option identifier */
469                 SSVAL(gss_cksum, 26, orig_length);
470                 /* Copy the kerberos KRB_CRED data */
471                 memcpy(gss_cksum + 28, in_data->data, orig_length);
472                 free(in_data->data);
473                 in_data->data = NULL;
474                 in_data->length = 0;
475         }
476         in_data->data = gss_cksum;
477         in_data->length = base_cksum_size + orig_length;
478         return 0;
479 }
480 #endif
481
482 /**************************************************************
483  krb5_parse_name that takes a UNIX charset.
484 **************************************************************/
485
486 krb5_error_code smb_krb5_parse_name(krb5_context context,
487                                 const char *name, /* in unix charset */
488                                 krb5_principal *principal)
489 {
490         krb5_error_code ret;
491         char *utf8_name;
492         size_t converted_size;
493         TALLOC_CTX *frame = talloc_stackframe();
494
495         if (!push_utf8_talloc(frame, &utf8_name, name, &converted_size)) {
496                 talloc_free(frame);
497                 return ENOMEM;
498         }
499
500         ret = krb5_parse_name(context, utf8_name, principal);
501         TALLOC_FREE(frame);
502         return ret;
503 }
504
505 #if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
506 void krb5_free_unparsed_name(krb5_context context, char *val)
507 {
508         SAFE_FREE(val);
509 }
510 #endif
511
512 /**************************************************************
513  krb5_parse_name that returns a UNIX charset name. Must
514  be freed with talloc_free() call.
515 **************************************************************/
516
517 krb5_error_code smb_krb5_unparse_name(TALLOC_CTX *mem_ctx,
518                                       krb5_context context,
519                                       krb5_const_principal principal,
520                                       char **unix_name)
521 {
522         krb5_error_code ret;
523         char *utf8_name;
524         size_t converted_size;
525
526         *unix_name = NULL;
527         ret = krb5_unparse_name(context, principal, &utf8_name);
528         if (ret) {
529                 return ret;
530         }
531
532         if (!pull_utf8_talloc(mem_ctx, unix_name, utf8_name, &converted_size)) {
533                 krb5_free_unparsed_name(context, utf8_name);
534                 return ENOMEM;
535         }
536         krb5_free_unparsed_name(context, utf8_name);
537         return 0;
538 }
539
540 krb5_error_code smb_krb5_parse_name_norealm(krb5_context context, 
541                                             const char *name, 
542                                             krb5_principal *principal)
543 {
544         /* we are cheating here because parse_name will in fact set the realm.
545          * We don't care as the only caller of smb_krb5_parse_name_norealm
546          * ignores the realm anyway when calling
547          * smb_krb5_principal_compare_any_realm later - Guenther */
548
549         return smb_krb5_parse_name(context, name, principal);
550 }
551
552 bool smb_krb5_principal_compare_any_realm(krb5_context context, 
553                                           krb5_const_principal princ1, 
554                                           krb5_const_principal princ2)
555 {
556         return krb5_principal_compare_any_realm(context, princ1, princ2);
557 }
558
559 /*
560   we can't use krb5_mk_req because w2k wants the service to be in a particular format
561 */
562 static krb5_error_code ads_krb5_mk_req(krb5_context context,
563                                        krb5_auth_context *auth_context,
564                                        const krb5_flags ap_req_options,
565                                        const char *principal,
566                                        krb5_ccache ccache,
567                                        krb5_data *outbuf,
568                                        time_t *expire_time,
569                                        const char *impersonate_princ_s)
570 {
571         krb5_error_code           retval;
572         krb5_principal    server;
573         krb5_principal impersonate_princ = NULL;
574         krb5_creds              * credsp;
575         krb5_creds                creds;
576         krb5_data in_data;
577         bool creds_ready = false;
578         int i = 0, maxtries = 3;
579
580         ZERO_STRUCT(in_data);
581
582         retval = smb_krb5_parse_name(context, principal, &server);
583         if (retval) {
584                 DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal));
585                 return retval;
586         }
587
588         if (impersonate_princ_s) {
589                 retval = smb_krb5_parse_name(context, impersonate_princ_s,
590                                              &impersonate_princ);
591                 if (retval) {
592                         DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", impersonate_princ_s));
593                         goto cleanup_princ;
594                 }
595         }
596
597         /* obtain ticket & session key */
598         ZERO_STRUCT(creds);
599         if ((retval = krb5_copy_principal(context, server, &creds.server))) {
600                 DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n",
601                          error_message(retval)));
602                 goto cleanup_princ;
603         }
604
605         if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) {
606                 /* This can commonly fail on smbd startup with no ticket in the cache.
607                  * Report at higher level than 1. */
608                 DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
609                          error_message(retval)));
610                 goto cleanup_creds;
611         }
612
613         while (!creds_ready && (i < maxtries)) {
614
615                 if ((retval = smb_krb5_get_credentials(context, ccache,
616                                                        creds.client,
617                                                        creds.server,
618                                                        impersonate_princ,
619                                                        &credsp))) {
620                         DEBUG(1,("ads_krb5_mk_req: smb_krb5_get_credentials failed for %s (%s)\n",
621                                 principal, error_message(retval)));
622                         goto cleanup_creds;
623                 }
624
625                 /* cope with ticket being in the future due to clock skew */
626                 if ((unsigned)credsp->times.starttime > time(NULL)) {
627                         time_t t = time(NULL);
628                         int time_offset =(int)((unsigned)credsp->times.starttime-t);
629                         DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset));
630                         krb5_set_real_time(context, t + time_offset + 1, 0);
631                 }
632
633                 if (!ads_cleanup_expired_creds(context, ccache, credsp)) {
634                         creds_ready = true;
635                 }
636
637                 i++;
638         }
639
640         DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n",
641                   principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache),
642                   http_timestring(talloc_tos(), (unsigned)credsp->times.endtime), 
643                   (unsigned)credsp->times.endtime));
644
645         if (expire_time) {
646                 *expire_time = (time_t)credsp->times.endtime;
647         }
648
649         /* Allocate the auth_context. */
650         retval = setup_auth_context(context, auth_context);
651         if (retval) {
652                 DEBUG(1,("setup_auth_context failed (%s)\n",
653                         error_message(retval)));
654                 goto cleanup_creds;
655         }
656
657 #if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
658         {
659                 uint32_t gss_flags = 0;
660
661                 if( credsp->ticket_flags & TKT_FLG_OK_AS_DELEGATE ) {
662                         /* Fetch a forwarded TGT from the KDC so that we can hand off a 2nd ticket
663                          as part of the kerberos exchange. */
664
665                         DEBUG( 3, ("ads_krb5_mk_req: server marked as OK to delegate to, building forwardable TGT\n")  );
666
667                         retval = krb5_auth_con_setuseruserkey(context,
668                                         *auth_context,
669                                         &credsp->keyblock );
670                         if (retval) {
671                                 DEBUG(1,("krb5_auth_con_setuseruserkey failed (%s)\n",
672                                         error_message(retval)));
673                                 goto cleanup_creds;
674                         }
675
676                         /* Must use a subkey for forwarded tickets. */
677                         retval = krb5_auth_con_setflags(context,
678                                 *auth_context,
679                                 KRB5_AUTH_CONTEXT_USE_SUBKEY);
680                         if (retval) {
681                                 DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
682                                         error_message(retval)));
683                                 goto cleanup_creds;
684                         }
685
686                         retval = krb5_fwd_tgt_creds(context,/* Krb5 context [in] */
687                                 *auth_context,  /* Authentication context [in] */
688                                 discard_const_p(char, KRB5_TGS_NAME),  /* Ticket service name ("krbtgt") [in] */
689                                 credsp->client, /* Client principal for the tgt [in] */
690                                 credsp->server, /* Server principal for the tgt [in] */
691                                 ccache,         /* Credential cache to use for storage [in] */
692                                 1,              /* Turn on for "Forwardable ticket" [in] */
693                                 &in_data );     /* Resulting response [out] */
694
695                         if (retval) {
696                                 DEBUG( 3, ("krb5_fwd_tgt_creds failed (%s)\n",
697                                            error_message( retval ) ) );
698
699                                 /*
700                                  * This is not fatal. Delete the *auth_context and continue
701                                  * with krb5_mk_req_extended to get a non-forwardable ticket.
702                                  */
703
704                                 if (in_data.data) {
705                                         free( in_data.data );
706                                         in_data.data = NULL;
707                                         in_data.length = 0;
708                                 }
709                                 krb5_auth_con_free(context, *auth_context);
710                                 *auth_context = NULL;
711                                 retval = setup_auth_context(context, auth_context);
712                                 if (retval) {
713                                         DEBUG(1,("setup_auth_context failed (%s)\n",
714                                                 error_message(retval)));
715                                         goto cleanup_creds;
716                                 }
717                         } else {
718                                 /* We got a delegated ticket. */
719                                 gss_flags |= GSS_C_DELEG_FLAG;
720                         }
721                 }
722
723                 /* Frees and reallocates in_data into a GSS checksum blob. */
724                 retval = create_gss_checksum(&in_data, gss_flags);
725                 if (retval) {
726                         goto cleanup_data;
727                 }
728
729                 /* We always want GSS-checksum types. */
730                 retval = krb5_auth_con_set_req_cksumtype(context, *auth_context, GSSAPI_CHECKSUM );
731                 if (retval) {
732                         DEBUG(1,("krb5_auth_con_set_req_cksumtype failed (%s)\n",
733                                 error_message(retval)));
734                         goto cleanup_data;
735                 }
736         }
737 #endif
738
739         retval = krb5_mk_req_extended(context, auth_context, ap_req_options,
740                                       &in_data, credsp, outbuf);
741         if (retval) {
742                 DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", 
743                          error_message(retval)));
744         }
745
746 #if defined(TKT_FLG_OK_AS_DELEGATE ) && defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY) && defined(KRB5_AUTH_CONTEXT_USE_SUBKEY) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE)
747 cleanup_data:
748 #endif
749
750         if (in_data.data) {
751                 free( in_data.data );
752                 in_data.length = 0;
753         }
754
755         krb5_free_creds(context, credsp);
756
757 cleanup_creds:
758         krb5_free_cred_contents(context, &creds);
759
760 cleanup_princ:
761         krb5_free_principal(context, server);
762         if (impersonate_princ) {
763                 krb5_free_principal(context, impersonate_princ);
764         }
765
766         return retval;
767 }
768
769 void kerberos_free_data_contents(krb5_context context, krb5_data *pdata)
770 {
771 #if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
772         if (pdata->data) {
773                 krb5_free_data_contents(context, pdata);
774         }
775 #elif defined(HAVE_KRB5_DATA_FREE)
776         krb5_data_free(context, pdata);
777 #else
778         SAFE_FREE(pdata->data);
779 #endif
780 }
781
782 /*
783  * @brief copy a buffer into a krb5_data struct
784  *
785  * @param[in] p                 The krb5_data
786  * @param[in] data              The data to copy
787  * @param[in] length            The length of the data to copy
788  * @return krb5_error_code
789  *
790  * Caller has to free krb5_data with kerberos_free_data_contents().
791  */
792
793 krb5_error_code krb5_copy_data_contents(krb5_data *p,
794                                         const void *data,
795                                         size_t len)
796 {
797 #if defined(HAVE_KRB5_DATA_COPY)
798         return krb5_data_copy(p, data, len);
799 #else
800         if (len) {
801                 p->data = malloc(len);
802                 if (p->data == NULL) {
803                         return ENOMEM;
804                 }
805                 memmove(p->data, data, len);
806         } else {
807                 p->data = NULL;
808         }
809         p->length = len;
810         p->magic = KV5M_DATA;
811         return 0;
812 #endif
813 }
814
815 /*
816   get a kerberos5 ticket for the given service
817 */
818 int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
819                         const char *principal, time_t time_offset,
820                         DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
821                         uint32_t extra_ap_opts, const char *ccname,
822                         time_t *tgs_expire,
823                         const char *impersonate_princ_s)
824
825 {
826         krb5_error_code retval;
827         krb5_data packet;
828         krb5_context context = NULL;
829         krb5_ccache ccdef = NULL;
830         krb5_auth_context auth_context = NULL;
831         krb5_enctype enc_types[] = {
832 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
833                 ENCTYPE_AES256_CTS_HMAC_SHA1_96,
834 #endif
835 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
836                 ENCTYPE_AES128_CTS_HMAC_SHA1_96,
837 #endif
838                 ENCTYPE_ARCFOUR_HMAC,
839                 ENCTYPE_DES_CBC_MD5,
840                 ENCTYPE_DES_CBC_CRC,
841                 ENCTYPE_NULL};
842
843         initialize_krb5_error_table();
844         retval = krb5_init_context(&context);
845         if (retval) {
846                 DEBUG(1, ("krb5_init_context failed (%s)\n",
847                          error_message(retval)));
848                 goto failed;
849         }
850
851         if (time_offset != 0) {
852                 krb5_set_real_time(context, time(NULL) + time_offset, 0);
853         }
854
855         if ((retval = krb5_cc_resolve(context, ccname ?
856                         ccname : krb5_cc_default_name(context), &ccdef))) {
857                 DEBUG(1, ("krb5_cc_default failed (%s)\n",
858                          error_message(retval)));
859                 goto failed;
860         }
861
862         if ((retval = krb5_set_default_tgs_ktypes(context, enc_types))) {
863                 DEBUG(1, ("krb5_set_default_tgs_ktypes failed (%s)\n",
864                          error_message(retval)));
865                 goto failed;
866         }
867
868         retval = ads_krb5_mk_req(context, &auth_context,
869                                 AP_OPTS_USE_SUBKEY | (krb5_flags)extra_ap_opts,
870                                 principal, ccdef, &packet,
871                                 tgs_expire, impersonate_princ_s);
872         if (retval) {
873                 goto failed;
874         }
875
876         get_krb5_smb_session_key(mem_ctx, context, auth_context,
877                                  session_key_krb5, false);
878
879         *ticket = data_blob_talloc(mem_ctx, packet.data, packet.length);
880
881         kerberos_free_data_contents(context, &packet);
882
883 failed:
884
885         if (context) {
886                 if (ccdef)
887                         krb5_cc_close(context, ccdef);
888                 if (auth_context)
889                         krb5_auth_con_free(context, auth_context);
890                 krb5_free_context(context);
891         }
892
893         return retval;
894 }
895
896 bool get_krb5_smb_session_key(TALLOC_CTX *mem_ctx,
897                               krb5_context context,
898                               krb5_auth_context auth_context,
899                               DATA_BLOB *session_key, bool remote)
900 {
901         krb5_keyblock *skey = NULL;
902         krb5_error_code err = 0;
903         bool ret = false;
904
905         if (remote) {
906 #ifdef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
907                 err = krb5_auth_con_getrecvsubkey(context,
908                                                   auth_context,
909                                                   &skey);
910 #else /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
911                 err = krb5_auth_con_getremotesubkey(context,
912                                                     auth_context, &skey);
913 #endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
914         } else {
915 #ifdef HAVE_KRB5_AUTH_CON_GETSENDSUBKEY
916                 err = krb5_auth_con_getsendsubkey(context,
917                                                   auth_context,
918                                                   &skey);
919 #else /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
920                 err = krb5_auth_con_getlocalsubkey(context,
921                                                    auth_context, &skey);
922 #endif /* HAVE_KRB5_AUTH_CON_GETSENDSUBKEY */
923         }
924
925         if (err || skey == NULL) {
926                 DEBUG(10, ("KRB5 error getting session key %d\n", err));
927                 goto done;
928         }
929
930         DEBUG(10, ("Got KRB5 session key of length %d\n",
931                    (int)KRB5_KEY_LENGTH(skey)));
932
933         *session_key = data_blob_talloc(mem_ctx,
934                                          KRB5_KEY_DATA(skey),
935                                          KRB5_KEY_LENGTH(skey));
936         dump_data_pw("KRB5 Session Key:\n",
937                      session_key->data,
938                      session_key->length);
939
940         ret = true;
941
942 done:
943         if (skey) {
944                 krb5_free_keyblock(context, skey);
945         }
946
947         return ret;
948 }
949
950
951 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
952  const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
953
954  const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i )
955 {
956         static krb5_data kdata;
957
958         kdata.data = discard_const_p(char, krb5_principal_get_comp_string(context, principal, i));
959         kdata.length = strlen((const char *)kdata.data);
960         return &kdata;
961 }
962 #endif
963
964 /*
965  * @brief Get talloced string component of a principal
966  *
967  * @param[in] mem_ctx           The TALLOC_CTX
968  * @param[in] context           The krb5_context
969  * @param[in] principal         The principal
970  * @param[in] component         The component
971  * @return string component
972  *
973  * Caller must talloc_free if the return value is not NULL.
974  *
975  */
976
977 /* caller has to free returned string with talloc_free() */
978 char *smb_krb5_principal_get_comp_string(TALLOC_CTX *mem_ctx,
979                                          krb5_context context,
980                                          krb5_const_principal principal,
981                                          unsigned int component)
982 {
983 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
984         return talloc_strdup(mem_ctx, krb5_principal_get_comp_string(context, principal, component));
985 #else
986         krb5_data *data;
987
988         if (component >= krb5_princ_size(context, principal)) {
989                 return NULL;
990         }
991
992         data = krb5_princ_component(context, principal, component);
993         if (data == NULL) {
994                 return NULL;
995         }
996
997         return talloc_strndup(mem_ctx, data->data, data->length);
998 #endif
999 }
1000
1001 /* Prototypes */
1002
1003  krb5_error_code smb_krb5_renew_ticket(const char *ccache_string,       /* FILE:/tmp/krb5cc_0 */
1004                                        const char *client_string,       /* gd@BER.SUSE.DE */
1005                                        const char *service_string,      /* krbtgt/BER.SUSE.DE@BER.SUSE.DE */
1006                                        time_t *expire_time)
1007 {
1008         krb5_error_code ret;
1009         krb5_context context = NULL;
1010         krb5_ccache ccache = NULL;
1011         krb5_principal client = NULL;
1012         krb5_creds creds, creds_in;
1013
1014         ZERO_STRUCT(creds);
1015         ZERO_STRUCT(creds_in);
1016
1017         initialize_krb5_error_table();
1018         ret = krb5_init_context(&context);
1019         if (ret) {
1020                 goto done;
1021         }
1022
1023         if (!ccache_string) {
1024                 ccache_string = krb5_cc_default_name(context);
1025         }
1026
1027         if (!ccache_string) {
1028                 ret = EINVAL;
1029                 goto done;
1030         }
1031
1032         DEBUG(10,("smb_krb5_renew_ticket: using %s as ccache\n", ccache_string));
1033
1034         /* FIXME: we should not fall back to defaults */
1035         ret = krb5_cc_resolve(context, discard_const_p(char, ccache_string), &ccache);
1036         if (ret) {
1037                 goto done;
1038         }
1039
1040         if (client_string) {
1041                 ret = smb_krb5_parse_name(context, client_string, &client);
1042                 if (ret) {
1043                         goto done;
1044                 }
1045         } else {
1046                 ret = krb5_cc_get_principal(context, ccache, &client);
1047                 if (ret) {
1048                         goto done;
1049                 }
1050         }
1051
1052         ret = krb5_get_renewed_creds(context, &creds, client, ccache, discard_const_p(char, service_string));
1053         if (ret) {
1054                 DEBUG(10,("smb_krb5_renew_ticket: krb5_get_kdc_cred failed: %s\n", error_message(ret)));
1055                 goto done;
1056         }
1057
1058         /* hm, doesn't that create a new one if the old one wasn't there? - Guenther */
1059         ret = krb5_cc_initialize(context, ccache, client);
1060         if (ret) {
1061                 goto done;
1062         }
1063
1064         ret = krb5_cc_store_cred(context, ccache, &creds);
1065
1066         if (expire_time) {
1067                 *expire_time = (time_t) creds.times.endtime;
1068         }
1069
1070 done:
1071         krb5_free_cred_contents(context, &creds_in);
1072         krb5_free_cred_contents(context, &creds);
1073
1074         if (client) {
1075                 krb5_free_principal(context, client);
1076         }
1077         if (ccache) {
1078                 krb5_cc_close(context, ccache);
1079         }
1080         if (context) {
1081                 krb5_free_context(context);
1082         }
1083
1084         return ret;
1085 }
1086
1087  krb5_error_code smb_krb5_free_addresses(krb5_context context, smb_krb5_addresses *addr)
1088 {
1089         krb5_error_code ret = 0;
1090         if (addr == NULL) {
1091                 return ret;
1092         }
1093 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1094         krb5_free_addresses(context, addr->addrs);
1095 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1096         ret = krb5_free_addresses(context, addr->addrs);
1097         SAFE_FREE(addr->addrs);
1098 #endif
1099         SAFE_FREE(addr);
1100         addr = NULL;
1101         return ret;
1102 }
1103
1104 #define MAX_NETBIOSNAME_LEN 16
1105  krb5_error_code smb_krb5_gen_netbios_krb5_address(smb_krb5_addresses **kerb_addr,
1106                                                    const char *netbios_name)
1107 {
1108         krb5_error_code ret = 0;
1109         char buf[MAX_NETBIOSNAME_LEN];
1110         int len;
1111 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1112         krb5_address **addrs = NULL;
1113 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1114         krb5_addresses *addrs = NULL;
1115 #endif
1116
1117         *kerb_addr = (smb_krb5_addresses *)SMB_MALLOC(sizeof(smb_krb5_addresses));
1118         if (*kerb_addr == NULL) {
1119                 return ENOMEM;
1120         }
1121
1122         /* temporarily duplicate put_name() code here to avoid dependency
1123          * issues for a 5 lines function */
1124         len = strlen(netbios_name);
1125         memcpy(buf, netbios_name,
1126                 (len < MAX_NETBIOSNAME_LEN) ? len : MAX_NETBIOSNAME_LEN - 1);
1127         if (len < MAX_NETBIOSNAME_LEN - 1) {
1128                 memset(buf + len, ' ', MAX_NETBIOSNAME_LEN - 1 - len);
1129         }
1130         buf[MAX_NETBIOSNAME_LEN - 1] = 0x20;
1131
1132 #if defined(HAVE_MAGIC_IN_KRB5_ADDRESS) && defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS) /* MIT */
1133         {
1134                 int num_addr = 2;
1135
1136                 addrs = (krb5_address **)SMB_MALLOC(sizeof(krb5_address *) * num_addr);
1137                 if (addrs == NULL) {
1138                         SAFE_FREE(*kerb_addr);
1139                         return ENOMEM;
1140                 }
1141
1142                 memset(addrs, 0, sizeof(krb5_address *) * num_addr);
1143
1144                 addrs[0] = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1145                 if (addrs[0] == NULL) {
1146                         SAFE_FREE(addrs);
1147                         SAFE_FREE(*kerb_addr);
1148                         return ENOMEM;
1149                 }
1150
1151                 addrs[0]->magic = KV5M_ADDRESS;
1152                 addrs[0]->addrtype = KRB5_ADDR_NETBIOS;
1153                 addrs[0]->length = MAX_NETBIOSNAME_LEN;
1154                 addrs[0]->contents = (unsigned char *)SMB_MALLOC(addrs[0]->length);
1155                 if (addrs[0]->contents == NULL) {
1156                         SAFE_FREE(addrs[0]);
1157                         SAFE_FREE(addrs);
1158                         SAFE_FREE(*kerb_addr);
1159                         return ENOMEM;
1160                 }
1161
1162                 memcpy(addrs[0]->contents, buf, addrs[0]->length);
1163
1164                 addrs[1] = NULL;
1165         }
1166 #elif defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS) /* Heimdal */
1167         {
1168                 addrs = (krb5_addresses *)SMB_MALLOC(sizeof(krb5_addresses));
1169                 if (addrs == NULL) {
1170                         SAFE_FREE(*kerb_addr);
1171                         return ENOMEM;
1172                 }
1173
1174                 memset(addrs, 0, sizeof(krb5_addresses));
1175
1176                 addrs->len = 1;
1177                 addrs->val = (krb5_address *)SMB_MALLOC(sizeof(krb5_address));
1178                 if (addrs->val == NULL) {
1179                         SAFE_FREE(addrs);
1180                         SAFE_FREE(kerb_addr);
1181                         return ENOMEM;
1182                 }
1183
1184                 addrs->val[0].addr_type = KRB5_ADDR_NETBIOS;
1185                 addrs->val[0].address.length = MAX_NETBIOSNAME_LEN;
1186                 addrs->val[0].address.data = (unsigned char *)SMB_MALLOC(addrs->val[0].address.length);
1187                 if (addrs->val[0].address.data == NULL) {
1188                         SAFE_FREE(addrs->val);
1189                         SAFE_FREE(addrs);
1190                         SAFE_FREE(*kerb_addr);
1191                         return ENOMEM;
1192                 }
1193
1194                 memcpy(addrs->val[0].address.data, buf, addrs->val[0].address.length);
1195         }
1196 #else
1197 #error UNKNOWN_KRB5_ADDRESS_FORMAT
1198 #endif
1199         (*kerb_addr)->addrs = addrs;
1200
1201         return ret;
1202 }
1203
1204  void smb_krb5_free_error(krb5_context context, krb5_error *krberror)
1205 {
1206 #ifdef HAVE_KRB5_FREE_ERROR_CONTENTS /* Heimdal */
1207         krb5_free_error_contents(context, krberror);
1208 #else /* MIT */
1209         krb5_free_error(context, krberror);
1210 #endif
1211 }
1212
1213  krb5_error_code handle_krberror_packet(krb5_context context,
1214                                         krb5_data *packet)
1215 {
1216         krb5_error_code ret;
1217         bool got_error_code = false;
1218
1219         DEBUG(10,("handle_krberror_packet: got error packet\n"));
1220
1221 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR /* Heimdal */
1222         {
1223                 krb5_error krberror;
1224
1225                 if ((ret = krb5_rd_error(context, packet, &krberror))) {
1226                         DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
1227                                 error_message(ret)));
1228                         return ret;
1229                 }
1230
1231                 if (krberror.e_data == NULL || krberror.e_data->data == NULL) {
1232                         ret = (krb5_error_code) krberror.error_code;
1233                         got_error_code = true;
1234                 }
1235
1236                 smb_krb5_free_error(context, &krberror);
1237         }
1238 #else /* MIT */
1239         {
1240                 krb5_error *krberror;
1241
1242                 if ((ret = krb5_rd_error(context, packet, &krberror))) {
1243                         DEBUG(10,("handle_krberror_packet: krb5_rd_error failed with: %s\n", 
1244                                 error_message(ret)));
1245                         return ret;
1246                 }
1247
1248                 if (krberror->e_data.data == NULL) {
1249 #if defined(ERROR_TABLE_BASE_krb5)
1250                         ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
1251 #else
1252                         ret = (krb5_error_code)krberror->error;
1253 #endif
1254                         got_error_code = true;
1255                 }
1256                 smb_krb5_free_error(context, krberror);
1257         }
1258 #endif
1259         if (got_error_code) {
1260                 DEBUG(5,("handle_krberror_packet: got KERBERR from kpasswd: %s (%d)\n", 
1261                         error_message(ret), ret));
1262         }
1263         return ret;
1264 }
1265
1266 krb5_error_code smb_krb5_get_init_creds_opt_alloc(krb5_context context,
1267                                             krb5_get_init_creds_opt **opt)
1268 {
1269         /* Heimdal or modern MIT version */
1270         return krb5_get_init_creds_opt_alloc(context, opt);
1271 }
1272
1273 void smb_krb5_get_init_creds_opt_free(krb5_context context,
1274                                 krb5_get_init_creds_opt *opt)
1275 {
1276         /* Modern MIT or Heimdal version */
1277         krb5_get_init_creds_opt_free(context, opt);
1278 }
1279
1280 krb5_enctype smb_get_enctype_from_kt_entry(krb5_keytab_entry *kt_entry)
1281 {
1282         return KRB5_KEY_TYPE(KRB5_KT_KEY(kt_entry));
1283 }
1284
1285 krb5_error_code smb_krb5_kt_free_entry(krb5_context context,
1286                                         krb5_keytab_entry *kt_entry)
1287 {
1288 /* Try krb5_free_keytab_entry_contents first, since
1289  * MIT Kerberos >= 1.7 has both krb5_free_keytab_entry_contents and
1290  * krb5_kt_free_entry but only has a prototype for the first, while the
1291  * second is considered private.
1292  */
1293 #if defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
1294         return krb5_free_keytab_entry_contents(context, kt_entry);
1295 #elif defined(HAVE_KRB5_KT_FREE_ENTRY)
1296         return krb5_kt_free_entry(context, kt_entry);
1297 #else
1298 #error UNKNOWN_KT_FREE_FUNCTION
1299 #endif
1300 }
1301
1302
1303 /* caller needs to free etype_s */
1304 krb5_error_code smb_krb5_enctype_to_string(krb5_context context,
1305                                            krb5_enctype enctype,
1306                                            char **etype_s)
1307 {
1308 #ifdef HAVE_KRB5_ENCTYPE_TO_STRING_WITH_KRB5_CONTEXT_ARG
1309         return krb5_enctype_to_string(context, enctype, etype_s); /* Heimdal */
1310 #elif defined(HAVE_KRB5_ENCTYPE_TO_STRING_WITH_SIZE_T_ARG)
1311         char buf[256];
1312         krb5_error_code ret = krb5_enctype_to_string(enctype, buf, 256); /* MIT */
1313         if (ret) {
1314                 return ret;
1315         }
1316         *etype_s = SMB_STRDUP(buf);
1317         if (!*etype_s) {
1318                 return ENOMEM;
1319         }
1320         return ret;
1321 #else
1322 #error UNKNOWN_KRB5_ENCTYPE_TO_STRING_FUNCTION
1323 #endif
1324 }
1325
1326 /**********************************************************************
1327  * Open a krb5 keytab with flags, handles readonly or readwrite access and
1328  * allows one to process non-default keytab names.
1329  * @param context krb5_context
1330  * @param keytab_name_req string
1331  * @param write_access bool if writable keytab is required
1332  * @param krb5_keytab pointer to krb5_keytab (close with krb5_kt_close())
1333  * @return krb5_error_code
1334 **********************************************************************/
1335
1336 /* This MAX_NAME_LEN is a constant defined in krb5.h */
1337 #ifndef MAX_KEYTAB_NAME_LEN
1338 #define MAX_KEYTAB_NAME_LEN 1100
1339 #endif
1340
1341 krb5_error_code smb_krb5_open_keytab_relative(krb5_context context,
1342                                               const char *keytab_name_req,
1343                                               bool write_access,
1344                                               krb5_keytab *keytab)
1345 {
1346         krb5_error_code ret = 0;
1347         TALLOC_CTX *mem_ctx;
1348         char keytab_string[MAX_KEYTAB_NAME_LEN];
1349         char *kt_str = NULL;
1350         bool found_valid_name = false;
1351         const char *pragma = "FILE";
1352         const char *tmp = NULL;
1353
1354         if (!write_access && !keytab_name_req) {
1355                 /* caller just wants to read the default keytab readonly, so be it */
1356                 return krb5_kt_default(context, keytab);
1357         }
1358
1359         mem_ctx = talloc_init("smb_krb5_open_keytab");
1360         if (!mem_ctx) {
1361                 return ENOMEM;
1362         }
1363
1364 #ifdef HAVE_WRFILE_KEYTAB
1365         if (write_access) {
1366                 pragma = "WRFILE";
1367         }
1368 #endif
1369
1370         if (keytab_name_req) {
1371
1372                 if (strlen(keytab_name_req) > MAX_KEYTAB_NAME_LEN) {
1373                         ret = KRB5_CONFIG_NOTENUFSPACE;
1374                         goto out;
1375                 }
1376
1377                 if ((strncmp(keytab_name_req, "WRFILE:/", 8) == 0) ||
1378                     (strncmp(keytab_name_req, "FILE:/", 6) == 0)) {
1379                         tmp = keytab_name_req;
1380                         goto resolve;
1381                 }
1382
1383                 tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, keytab_name_req);
1384                 if (!tmp) {
1385                         ret = ENOMEM;
1386                         goto out;
1387                 }
1388
1389                 goto resolve;
1390         }
1391
1392         /* we need to handle more complex keytab_strings, like:
1393          * "ANY:FILE:/etc/krb5.keytab,krb4:/etc/srvtab" */
1394
1395         ret = krb5_kt_default_name(context, &keytab_string[0], MAX_KEYTAB_NAME_LEN - 2);
1396         if (ret) {
1397                 goto out;
1398         }
1399
1400         DEBUG(10,("smb_krb5_open_keytab: krb5_kt_default_name returned %s\n", keytab_string));
1401
1402         tmp = talloc_strdup(mem_ctx, keytab_string);
1403         if (!tmp) {
1404                 ret = ENOMEM;
1405                 goto out;
1406         }
1407
1408         if (strncmp(tmp, "ANY:", 4) == 0) {
1409                 tmp += 4;
1410         }
1411
1412         memset(&keytab_string, '\0', sizeof(keytab_string));
1413
1414         while (next_token_talloc(mem_ctx, &tmp, &kt_str, ",")) {
1415                 if (strncmp(kt_str, "WRFILE:", 7) == 0) {
1416                         found_valid_name = true;
1417                         tmp = kt_str;
1418                         tmp += 7;
1419                 }
1420
1421                 if (strncmp(kt_str, "FILE:", 5) == 0) {
1422                         found_valid_name = true;
1423                         tmp = kt_str;
1424                         tmp += 5;
1425                 }
1426
1427                 if (tmp[0] == '/') {
1428                         /* Treat as a FILE: keytab definition. */
1429                         found_valid_name = true;
1430                 }
1431
1432                 if (found_valid_name) {
1433                         if (tmp[0] != '/') {
1434                                 ret = KRB5_KT_BADNAME;
1435                                 goto out;
1436                         }
1437
1438                         tmp = talloc_asprintf(mem_ctx, "%s:%s", pragma, tmp);
1439                         if (!tmp) {
1440                                 ret = ENOMEM;
1441                                 goto out;
1442                         }
1443                         break;
1444                 }
1445         }
1446
1447         if (!found_valid_name) {
1448                 ret = KRB5_KT_UNKNOWN_TYPE;
1449                 goto out;
1450         }
1451
1452  resolve:
1453         DEBUG(10,("smb_krb5_open_keytab: resolving: %s\n", tmp));
1454         ret = krb5_kt_resolve(context, tmp, keytab);
1455
1456  out:
1457         TALLOC_FREE(mem_ctx);
1458         return ret;
1459 }
1460
1461 krb5_error_code smb_krb5_open_keytab(krb5_context context,
1462                                      const char *keytab_name_req,
1463                                      bool write_access,
1464                                      krb5_keytab *keytab)
1465 {
1466         if (keytab_name_req != NULL) {
1467                 if (keytab_name_req[0] != '/') {
1468                         return KRB5_KT_BADNAME;
1469                 }
1470         }
1471
1472         return smb_krb5_open_keytab_relative(context,
1473                                              keytab_name_req,
1474                                              write_access,
1475                                              keytab);
1476 }
1477
1478 krb5_error_code smb_krb5_keytab_name(TALLOC_CTX *mem_ctx,
1479                                      krb5_context context,
1480                                      krb5_keytab keytab,
1481                                      const char **keytab_name)
1482 {
1483         char keytab_string[MAX_KEYTAB_NAME_LEN];
1484         krb5_error_code ret = 0;
1485
1486         ret = krb5_kt_get_name(context, keytab,
1487                                keytab_string, MAX_KEYTAB_NAME_LEN - 2);
1488         if (ret) {
1489                 return ret;
1490         }
1491
1492         *keytab_name = talloc_strdup(mem_ctx, keytab_string);
1493         if (!*keytab_name) {
1494                 return ENOMEM;
1495         }
1496
1497         return ret;
1498 }
1499
1500 /**
1501  * @brief Seek and delete old entries in a keytab based on the passed
1502  *        principal.
1503  *
1504  * @param[in]  context       The KRB5 context to use.
1505  *
1506  * @param[in]  keytab        The keytab to operate on.
1507  *
1508  * @param[in]  kvno          The kvnco to use.
1509  *
1510  * @param[in]  princ_s       The principal as a string to search for.
1511  *
1512  * @param[in]  princ         The principal as a krb5_principal to search for.
1513  *
1514  * @param[in]  flush         Weather to flush the complete keytab.
1515  *
1516  * @param[in]  keep_old_entries Keep the entry with the previous kvno.
1517  *
1518  * @retval 0 on Sucess
1519  *
1520  * @return An appropriate KRB5 error code.
1521  */
1522 krb5_error_code smb_krb5_kt_seek_and_delete_old_entries(krb5_context context,
1523                                                         krb5_keytab keytab,
1524                                                         krb5_kvno kvno,
1525                                                         krb5_enctype enctype,
1526                                                         const char *princ_s,
1527                                                         krb5_principal princ,
1528                                                         bool flush,
1529                                                         bool keep_old_entries)
1530 {
1531         krb5_error_code ret;
1532         krb5_kt_cursor cursor;
1533         krb5_kt_cursor zero_csr;
1534         krb5_keytab_entry kt_entry;
1535         krb5_keytab_entry zero_kt_entry;
1536         char *ktprinc = NULL;
1537         krb5_kvno old_kvno = kvno - 1;
1538         TALLOC_CTX *tmp_ctx;
1539
1540         ZERO_STRUCT(cursor);
1541         ZERO_STRUCT(zero_csr);
1542         ZERO_STRUCT(kt_entry);
1543         ZERO_STRUCT(zero_kt_entry);
1544
1545         ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1546         if (ret == KRB5_KT_END || ret == ENOENT ) {
1547                 /* no entries */
1548                 return 0;
1549         }
1550
1551         tmp_ctx = talloc_new(NULL);
1552         if (tmp_ctx == NULL) {
1553                 return ENOMEM;
1554         }
1555
1556         DEBUG(3, (__location__ ": Will try to delete old keytab entries\n"));
1557         while (!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
1558                 bool name_ok = false;
1559                 krb5_enctype kt_entry_enctype =
1560                         smb_get_enctype_from_kt_entry(&kt_entry);
1561
1562                 if (!flush && (princ_s != NULL)) {
1563                         ret = smb_krb5_unparse_name(tmp_ctx, context,
1564                                                     kt_entry.principal,
1565                                                     &ktprinc);
1566                         if (ret) {
1567                                 DEBUG(1, (__location__
1568                                           ": smb_krb5_unparse_name failed "
1569                                           "(%s)\n", error_message(ret)));
1570                                 goto out;
1571                         }
1572
1573 #ifdef HAVE_KRB5_KT_COMPARE
1574                         name_ok = krb5_kt_compare(context, &kt_entry,
1575                                                   princ, 0, 0);
1576 #else
1577                         name_ok = (strcmp(ktprinc, princ_s) == 0);
1578 #endif
1579
1580                         if (!name_ok) {
1581                                 DEBUG(10, (__location__ ": ignoring keytab "
1582                                            "entry principal %s, kvno = %d\n",
1583                                            ktprinc, kt_entry.vno));
1584
1585                                 /* Not a match,
1586                                  * just free this entry and continue. */
1587                                 ret = smb_krb5_kt_free_entry(context,
1588                                                              &kt_entry);
1589                                 ZERO_STRUCT(kt_entry);
1590                                 if (ret) {
1591                                         DEBUG(1, (__location__
1592                                                   ": smb_krb5_kt_free_entry "
1593                                                   "failed (%s)\n",
1594                                                   error_message(ret)));
1595                                         goto out;
1596                                 }
1597
1598                                 TALLOC_FREE(ktprinc);
1599                                 continue;
1600                         }
1601
1602                         TALLOC_FREE(ktprinc);
1603                 }
1604
1605                 /*------------------------------------------------------------
1606                  * Save the entries with kvno - 1. This is what microsoft does
1607                  * to allow people with existing sessions that have kvno - 1
1608                  * to still work. Otherwise, when the password for the machine
1609                  * changes, all kerberizied sessions will 'break' until either
1610                  * the client reboots or the client's session key expires and
1611                  * they get a new session ticket with the new kvno.
1612                  * Some keytab files only store the kvno in 8bits, limit
1613                  * the compare accordingly.
1614                  */
1615
1616                 if (!flush && ((kt_entry.vno & 0xff) == (old_kvno & 0xff))) {
1617                         DEBUG(5, (__location__ ": Saving previous (kvno %d) "
1618                                   "entry for principal: %s.\n",
1619                                   old_kvno, princ_s));
1620                         continue;
1621                 }
1622
1623                 if (keep_old_entries) {
1624                         DEBUG(5, (__location__ ": Saving old (kvno %d) "
1625                                   "entry for principal: %s.\n",
1626                                   kvno, princ_s));
1627                         continue;
1628                 }
1629
1630                 if (!flush &&
1631                     (kt_entry.vno == kvno) &&
1632                     (kt_entry_enctype != enctype))
1633                 {
1634                         DEBUG(5, (__location__ ": Saving entry with kvno [%d] "
1635                                   "enctype [%d] for principal: %s.\n",
1636                                   kvno, kt_entry_enctype, princ_s));
1637                         continue;
1638                 }
1639
1640                 DEBUG(5, (__location__ ": Found old entry for principal: %s "
1641                           "(kvno %d) - trying to remove it.\n",
1642                           princ_s, kt_entry.vno));
1643
1644                 ret = krb5_kt_end_seq_get(context, keytab, &cursor);
1645                 ZERO_STRUCT(cursor);
1646                 if (ret) {
1647                         DEBUG(1, (__location__ ": krb5_kt_end_seq_get() "
1648                                   "failed (%s)\n", error_message(ret)));
1649                         goto out;
1650                 }
1651                 ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
1652                 if (ret) {
1653                         DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
1654                                   "failed (%s)\n", error_message(ret)));
1655                         goto out;
1656                 }
1657
1658                 DEBUG(5, (__location__ ": removed old entry for principal: "
1659                           "%s (kvno %d).\n", princ_s, kt_entry.vno));
1660
1661                 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
1662                 if (ret) {
1663                         DEBUG(1, (__location__ ": krb5_kt_start_seq() failed "
1664                                   "(%s)\n", error_message(ret)));
1665                         goto out;
1666                 }
1667                 ret = smb_krb5_kt_free_entry(context, &kt_entry);
1668                 ZERO_STRUCT(kt_entry);
1669                 if (ret) {
1670                         DEBUG(1, (__location__ ": krb5_kt_remove_entry() "
1671                                   "failed (%s)\n", error_message(ret)));
1672                         goto out;
1673                 }
1674         }
1675
1676 out:
1677         talloc_free(tmp_ctx);
1678         if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
1679                 smb_krb5_kt_free_entry(context, &kt_entry);
1680         }
1681         if (memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) {
1682                 krb5_kt_end_seq_get(context, keytab, &cursor);
1683         }
1684         return ret;
1685 }
1686
1687 /**
1688  * @brief Add a keytab entry for the given principal
1689  *
1690  * @param[in]  context       The krb5 context to use.
1691  *
1692  * @param[in]  keytab        The keytab to add the entry to.
1693  *
1694  * @param[in]  kvno          The kvno to use.
1695  *
1696  * @param[in]  princ_s       The principal as a string.
1697  *
1698  * @param[in]  salt_principal The salt principal to salt the password with.
1699  *                            Only needed for keys which support salting.
1700  *                            If no salt is used set no_salt to false and
1701  *                            pass NULL here.
1702  *
1703  * @param[in]  enctype        The encryption type of the keytab entry.
1704  *
1705  * @param[in]  password       The password of the keytab entry.
1706  *
1707  * @param[in]  no_salt        If the password should not be salted. Normally
1708  *                            this is only set to false for encryption types
1709  *                            which do not support salting like RC4.
1710  *
1711  * @param[in]  keep_old_entries Wether to keep or delte old keytab entries.
1712  *
1713  * @retval 0 on Success
1714  *
1715  * @return A corresponding KRB5 error code.
1716  *
1717  * @see smb_krb5_open_keytab()
1718  */
1719 krb5_error_code smb_krb5_kt_add_entry(krb5_context context,
1720                                       krb5_keytab keytab,
1721                                       krb5_kvno kvno,
1722                                       const char *princ_s,
1723                                       const char *salt_principal,
1724                                       krb5_enctype enctype,
1725                                       krb5_data *password,
1726                                       bool no_salt,
1727                                       bool keep_old_entries)
1728 {
1729         krb5_error_code ret;
1730         krb5_keytab_entry kt_entry;
1731         krb5_principal princ = NULL;
1732         krb5_keyblock *keyp;
1733
1734         ZERO_STRUCT(kt_entry);
1735
1736         ret = smb_krb5_parse_name(context, princ_s, &princ);
1737         if (ret) {
1738                 DEBUG(1, (__location__ ": smb_krb5_parse_name(%s) "
1739                           "failed (%s)\n", princ_s, error_message(ret)));
1740                 goto out;
1741         }
1742
1743         /* Seek and delete old keytab entries */
1744         ret = smb_krb5_kt_seek_and_delete_old_entries(context,
1745                                                       keytab,
1746                                                       kvno,
1747                                                       enctype,
1748                                                       princ_s,
1749                                                       princ,
1750                                                       false,
1751                                                       keep_old_entries);
1752         if (ret) {
1753                 goto out;
1754         }
1755
1756         /* If we get here, we have deleted all the old entries with kvno's
1757          * not equal to the current kvno-1. */
1758
1759         keyp = KRB5_KT_KEY(&kt_entry);
1760
1761         if (no_salt) {
1762                 KRB5_KEY_DATA(keyp) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
1763                 if (KRB5_KEY_DATA(keyp) == NULL) {
1764                         ret = ENOMEM;
1765                         goto out;
1766                 }
1767                 memcpy(KRB5_KEY_DATA(keyp), password->data, password->length);
1768                 KRB5_KEY_LENGTH(keyp) = password->length;
1769                 KRB5_KEY_TYPE(keyp) = enctype;
1770         } else {
1771                 krb5_principal salt_princ = NULL;
1772
1773                 /* Now add keytab entries for all encryption types */
1774                 ret = smb_krb5_parse_name(context, salt_principal, &salt_princ);
1775                 if (ret) {
1776                         DBG_WARNING("krb5_parse_name(%s) failed (%s)\n",
1777                                     salt_principal, error_message(ret));
1778                         goto out;
1779                 }
1780
1781                 ret = smb_krb5_create_key_from_string(context,
1782                                                       salt_princ,
1783                                                       NULL,
1784                                                       password,
1785                                                       enctype,
1786                                                       keyp);
1787                 krb5_free_principal(context, salt_princ);
1788                 if (ret != 0) {
1789                         goto out;
1790                 }
1791         }
1792
1793         kt_entry.principal = princ;
1794         kt_entry.vno       = kvno;
1795
1796         DEBUG(3, (__location__ ": adding keytab entry for (%s) with "
1797                   "encryption type (%d) and version (%d)\n",
1798                   princ_s, enctype, kt_entry.vno));
1799         ret = krb5_kt_add_entry(context, keytab, &kt_entry);
1800         krb5_free_keyblock_contents(context, keyp);
1801         ZERO_STRUCT(kt_entry);
1802         if (ret) {
1803                 DEBUG(1, (__location__ ": adding entry to keytab "
1804                           "failed (%s)\n", error_message(ret)));
1805                 goto out;
1806         }
1807
1808 out:
1809         if (princ) {
1810                 krb5_free_principal(context, princ);
1811         }
1812
1813         return ret;
1814 }
1815
1816 #if defined(HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE) && \
1817     defined(HAVE_KRB5_GET_CREDS_OPT_ALLOC) && \
1818     defined(HAVE_KRB5_GET_CREDS)
1819 static krb5_error_code smb_krb5_get_credentials_for_user_opt(krb5_context context,
1820                                                              krb5_ccache ccache,
1821                                                              krb5_principal me,
1822                                                              krb5_principal server,
1823                                                              krb5_principal impersonate_princ,
1824                                                              krb5_creds **out_creds)
1825 {
1826         krb5_error_code ret;
1827         krb5_get_creds_opt opt;
1828
1829         ret = krb5_get_creds_opt_alloc(context, &opt);
1830         if (ret) {
1831                 goto done;
1832         }
1833         krb5_get_creds_opt_add_options(context, opt, KRB5_GC_FORWARDABLE);
1834
1835         if (impersonate_princ) {
1836                 ret = krb5_get_creds_opt_set_impersonate(context, opt,
1837                                                          impersonate_princ);
1838                 if (ret) {
1839                         goto done;
1840                 }
1841         }
1842
1843         ret = krb5_get_creds(context, opt, ccache, server, out_creds);
1844         if (ret) {
1845                 goto done;
1846         }
1847
1848  done:
1849         if (opt) {
1850                 krb5_get_creds_opt_free(context, opt);
1851         }
1852         return ret;
1853 }
1854 #endif /* HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE */
1855
1856 #ifdef HAVE_KRB5_GET_CREDENTIALS_FOR_USER
1857
1858 #if !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER
1859 krb5_error_code KRB5_CALLCONV
1860 krb5_get_credentials_for_user(krb5_context context, krb5_flags options,
1861                               krb5_ccache ccache, krb5_creds *in_creds,
1862                               krb5_data *subject_cert,
1863                               krb5_creds **out_creds);
1864 #endif /* !HAVE_DECL_KRB5_GET_CREDENTIALS_FOR_USER */
1865
1866 static krb5_error_code smb_krb5_get_credentials_for_user(krb5_context context,
1867                                                          krb5_ccache ccache,
1868                                                          krb5_principal me,
1869                                                          krb5_principal server,
1870                                                          krb5_principal impersonate_princ,
1871                                                          krb5_creds **out_creds)
1872 {
1873         krb5_error_code ret;
1874         krb5_creds in_creds;
1875
1876         ZERO_STRUCT(in_creds);
1877
1878         if (impersonate_princ) {
1879
1880                 in_creds.server = me;
1881                 in_creds.client = impersonate_princ;
1882
1883                 ret = krb5_get_credentials_for_user(context,
1884                                                     0, /* krb5_flags options */
1885                                                     ccache,
1886                                                     &in_creds,
1887                                                     NULL, /* krb5_data *subject_cert */
1888                                                     out_creds);
1889         } else {
1890                 in_creds.client = me;
1891                 in_creds.server = server;
1892
1893                 ret = krb5_get_credentials(context, 0, ccache,
1894                                            &in_creds, out_creds);
1895         }
1896
1897         return ret;
1898 }
1899 #endif /* HAVE_KRB5_GET_CREDENTIALS_FOR_USER */
1900
1901 /*
1902  * smb_krb5_get_credentials
1903  *
1904  * @brief Get krb5 credentials for a server
1905  *
1906  * @param[in] context           An initialized krb5_context
1907  * @param[in] ccache            An initialized krb5_ccache
1908  * @param[in] me                The krb5_principal of the caller
1909  * @param[in] server            The krb5_principal of the requested service
1910  * @param[in] impersonate_princ The krb5_principal of a user to impersonate as (optional)
1911  * @param[out] out_creds        The returned krb5_creds structure
1912  * @return krb5_error_code
1913  *
1914  */
1915 krb5_error_code smb_krb5_get_credentials(krb5_context context,
1916                                          krb5_ccache ccache,
1917                                          krb5_principal me,
1918                                          krb5_principal server,
1919                                          krb5_principal impersonate_princ,
1920                                          krb5_creds **out_creds)
1921 {
1922         krb5_error_code ret;
1923         krb5_creds *creds = NULL;
1924
1925         if (out_creds != NULL) {
1926                 *out_creds = NULL;
1927         }
1928
1929         if (impersonate_princ) {
1930 #ifdef HAVE_KRB5_GET_CREDS_OPT_SET_IMPERSONATE /* Heimdal */
1931                 ret = smb_krb5_get_credentials_for_user_opt(context, ccache, me, server, impersonate_princ, &creds);
1932 #elif defined(HAVE_KRB5_GET_CREDENTIALS_FOR_USER) /* MIT */
1933                 ret = smb_krb5_get_credentials_for_user(context, ccache, me, server, impersonate_princ, &creds);
1934 #else
1935                 ret = ENOTSUP;
1936 #endif
1937         } else {
1938                 krb5_creds in_creds;
1939
1940                 ZERO_STRUCT(in_creds);
1941
1942                 in_creds.client = me;
1943                 in_creds.server = server;
1944
1945                 ret = krb5_get_credentials(context, 0, ccache,
1946                                            &in_creds, &creds);
1947         }
1948         if (ret) {
1949                 goto done;
1950         }
1951
1952         if (out_creds) {
1953                 *out_creds = creds;
1954         }
1955
1956  done:
1957         if (creds && ret) {
1958                 krb5_free_creds(context, creds);
1959         }
1960
1961         return ret;
1962 }
1963
1964 krb5_error_code smb_krb5_keyblock_init_contents(krb5_context context,
1965                                                 krb5_enctype enctype,
1966                                                 const void *data,
1967                                                 size_t length,
1968                                                 krb5_keyblock *key)
1969 {
1970 #if defined(HAVE_KRB5_KEYBLOCK_INIT)
1971         return krb5_keyblock_init(context, enctype, data, length, key);
1972 #else
1973         memset(key, 0, sizeof(krb5_keyblock));
1974         KRB5_KEY_DATA(key) = SMB_MALLOC(length);
1975         if (NULL == KRB5_KEY_DATA(key)) {
1976                 return ENOMEM;
1977         }
1978         memcpy(KRB5_KEY_DATA(key), data, length);
1979         KRB5_KEY_LENGTH(key) = length;
1980         KRB5_KEY_TYPE(key) = enctype;
1981         return 0;
1982 #endif
1983 }
1984
1985 /*
1986   simulate a kinit, putting the tgt in the given credentials cache.
1987   Orignally by remus@snapserver.com
1988
1989   This version is built to use a keyblock, rather than needing the
1990   original password.
1991
1992   The impersonate_principal is the principal if NULL, or the principal
1993   to impersonate
1994
1995   The target_service defaults to the krbtgt if NULL, but could be
1996    kpasswd/realm or the local service (if we are doing s4u2self)
1997 */
1998 krb5_error_code kerberos_kinit_keyblock_cc(krb5_context ctx, krb5_ccache cc,
1999                                            krb5_principal principal,
2000                                            krb5_keyblock *keyblock,
2001                                            const char *target_service,
2002                                            krb5_get_init_creds_opt *krb_options,
2003                                            time_t *expire_time,
2004                                            time_t *kdc_time)
2005 {
2006         krb5_error_code code = 0;
2007         krb5_creds my_creds;
2008
2009 #if defined(HAVE_KRB5_GET_INIT_CREDS_KEYBLOCK)
2010         code = krb5_get_init_creds_keyblock(ctx, &my_creds, principal,
2011                                             keyblock, 0, target_service,
2012                                             krb_options);
2013 #elif defined(HAVE_KRB5_GET_INIT_CREDS_KEYTAB)
2014 {
2015 #define SMB_CREDS_KEYTAB "MEMORY:tmp_smb_creds_XXXXXX"
2016         char tmp_name[sizeof(SMB_CREDS_KEYTAB)];
2017         krb5_keytab_entry entry;
2018         krb5_keytab keytab;
2019         mode_t mask;
2020
2021         memset(&entry, 0, sizeof(entry));
2022         entry.principal = principal;
2023         *(KRB5_KT_KEY(&entry)) = *keyblock;
2024
2025         memcpy(tmp_name, SMB_CREDS_KEYTAB, sizeof(SMB_CREDS_KEYTAB));
2026         mask = umask(S_IRWXO | S_IRWXG);
2027         mktemp(tmp_name);
2028         umask(mask);
2029         if (tmp_name[0] == 0) {
2030                 return KRB5_KT_BADNAME;
2031         }
2032         code = krb5_kt_resolve(ctx, tmp_name, &keytab);
2033         if (code) {
2034                 return code;
2035         }
2036
2037         code = krb5_kt_add_entry(ctx, keytab, &entry);
2038         if (code) {
2039                 (void)krb5_kt_close(ctx, keytab);
2040                 goto done;
2041         }
2042
2043         code = krb5_get_init_creds_keytab(ctx, &my_creds, principal,
2044                                           keytab, 0, target_service,
2045                                           krb_options);
2046         (void)krb5_kt_close(ctx, keytab);
2047 }
2048 #else
2049 #error krb5_get_init_creds_keyblock not available!
2050 #endif
2051         if (code) {
2052                 return code;
2053         }
2054
2055 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
2056         /*
2057          * We need to store the principal as returned from the KDC to the
2058          * credentials cache. If we don't do that the KRB5 library is not
2059          * able to find the tickets it is looking for
2060          */
2061         principal = my_creds.client;
2062 #endif
2063         code = krb5_cc_initialize(ctx, cc, principal);
2064         if (code) {
2065                 goto done;
2066         }
2067
2068         code = krb5_cc_store_cred(ctx, cc, &my_creds);
2069         if (code) {
2070                 goto done;
2071         }
2072
2073         if (expire_time) {
2074                 *expire_time = (time_t) my_creds.times.endtime;
2075         }
2076
2077         if (kdc_time) {
2078                 *kdc_time = (time_t) my_creds.times.starttime;
2079         }
2080
2081         code = 0;
2082 done:
2083         krb5_free_cred_contents(ctx, &my_creds);
2084         return code;
2085 }
2086
2087 krb5_error_code kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc,
2088                                            krb5_principal principal,
2089                                            const char *password,
2090                                            const char *target_service,
2091                                            krb5_get_init_creds_opt *krb_options,
2092                                            time_t *expire_time,
2093                                            time_t *kdc_time)
2094 {
2095         krb5_error_code code = 0;
2096         krb5_creds my_creds;
2097
2098         code = krb5_get_init_creds_password(ctx, &my_creds, principal,
2099                                             password, NULL, NULL, 0,
2100                                             target_service, krb_options);
2101         if (code) {
2102                 return code;
2103         }
2104
2105 #ifndef SAMBA4_USES_HEIMDAL /* MIT */
2106         /*
2107          * We need to store the principal as returned from the KDC to the
2108          * credentials cache. If we don't do that the KRB5 library is not
2109          * able to find the tickets it is looking for
2110          */
2111         principal = my_creds.client;
2112 #endif
2113         code = krb5_cc_initialize(ctx, cc, principal);
2114         if (code) {
2115                 goto done;
2116         }
2117
2118         code = krb5_cc_store_cred(ctx, cc, &my_creds);
2119         if (code) {
2120                 goto done;
2121         }
2122
2123         if (expire_time) {
2124                 *expire_time = (time_t) my_creds.times.endtime;
2125         }
2126
2127         if (kdc_time) {
2128                 *kdc_time = (time_t) my_creds.times.starttime;
2129         }
2130
2131         code = 0;
2132 done:
2133         krb5_free_cred_contents(ctx, &my_creds);
2134         return code;
2135 }
2136
2137 #ifdef SAMBA4_USES_HEIMDAL
2138 /*
2139   simulate a kinit, putting the tgt in the given credentials cache.
2140   Orignally by remus@snapserver.com
2141
2142   The impersonate_principal is the principal
2143
2144   The self_service, should be the local service (for S4U2Self if
2145   impersonate_principal is given).
2146
2147   The target_service defaults to the krbtgt if NULL, but could be
2148   kpasswd/realm or a remote service (for S4U2Proxy)
2149
2150 */
2151 krb5_error_code kerberos_kinit_s4u2_cc(krb5_context ctx,
2152                                         krb5_ccache store_cc,
2153                                         krb5_principal init_principal,
2154                                         const char *init_password,
2155                                         krb5_principal impersonate_principal,
2156                                         const char *self_service,
2157                                         const char *target_service,
2158                                         krb5_get_init_creds_opt *krb_options,
2159                                         time_t *expire_time,
2160                                         time_t *kdc_time)
2161 {
2162         krb5_error_code code = 0;
2163         krb5_get_creds_opt options;
2164         krb5_principal store_principal;
2165         krb5_creds store_creds;
2166         krb5_creds *s4u2self_creds;
2167         Ticket s4u2self_ticket;
2168         size_t s4u2self_ticketlen;
2169         krb5_creds *s4u2proxy_creds;
2170         krb5_principal self_princ;
2171         bool s4u2proxy;
2172         krb5_principal target_princ;
2173         krb5_ccache tmp_cc;
2174         const char *self_realm;
2175         krb5_principal blacklist_principal = NULL;
2176         krb5_principal whitelist_principal = NULL;
2177
2178         code = krb5_get_init_creds_password(ctx, &store_creds,
2179                                             init_principal,
2180                                             init_password,
2181                                             NULL, NULL,
2182                                             0,
2183                                             NULL,
2184                                             krb_options);
2185         if (code != 0) {
2186                 return code;
2187         }
2188
2189         store_principal = init_principal;
2190
2191         /*
2192          * We are trying S4U2Self now:
2193          *
2194          * As we do not want to expose our TGT in the
2195          * krb5_ccache, which is also holds the impersonated creds.
2196          *
2197          * Some low level krb5/gssapi function might use the TGT
2198          * identity and let the client act as our machine account.
2199          *
2200          * We need to avoid that and use a temporary krb5_ccache
2201          * in order to pass our TGT to the krb5_get_creds() function.
2202          */
2203         code = krb5_cc_new_unique(ctx, NULL, NULL, &tmp_cc);
2204         if (code != 0) {
2205                 krb5_free_cred_contents(ctx, &store_creds);
2206                 return code;
2207         }
2208
2209         code = krb5_cc_initialize(ctx, tmp_cc, store_creds.client);
2210         if (code != 0) {
2211                 krb5_cc_destroy(ctx, tmp_cc);
2212                 krb5_free_cred_contents(ctx, &store_creds);
2213                 return code;
2214         }
2215
2216         code = krb5_cc_store_cred(ctx, tmp_cc, &store_creds);
2217         if (code != 0) {
2218                 krb5_free_cred_contents(ctx, &store_creds);
2219                 krb5_cc_destroy(ctx, tmp_cc);
2220                 return code;
2221         }
2222
2223         /*
2224          * we need to remember the client principal of our
2225          * TGT and make sure the KDC does not return this
2226          * in the impersonated tickets. This can happen
2227          * if the KDC does not support S4U2Self and S4U2Proxy.
2228          */
2229         blacklist_principal = store_creds.client;
2230         store_creds.client = NULL;
2231         krb5_free_cred_contents(ctx, &store_creds);
2232
2233         /*
2234          * Check if we also need S4U2Proxy or if S4U2Self is
2235          * enough in order to get a ticket for the target.
2236          */
2237         if (target_service == NULL) {
2238                 s4u2proxy = false;
2239         } else if (strcmp(target_service, self_service) == 0) {
2240                 s4u2proxy = false;
2241         } else {
2242                 s4u2proxy = true;
2243         }
2244
2245         /*
2246          * For S4U2Self we need our own service principal,
2247          * which belongs to our own realm (available on
2248          * our client principal).
2249          */
2250         self_realm = krb5_principal_get_realm(ctx, init_principal);
2251
2252         code = krb5_parse_name(ctx, self_service, &self_princ);
2253         if (code != 0) {
2254                 krb5_free_principal(ctx, blacklist_principal);
2255                 krb5_cc_destroy(ctx, tmp_cc);
2256                 return code;
2257         }
2258
2259         code = krb5_principal_set_realm(ctx, self_princ, self_realm);
2260         if (code != 0) {
2261                 krb5_free_principal(ctx, blacklist_principal);
2262                 krb5_free_principal(ctx, self_princ);
2263                 krb5_cc_destroy(ctx, tmp_cc);
2264                 return code;
2265         }
2266
2267         code = krb5_get_creds_opt_alloc(ctx, &options);
2268         if (code != 0) {
2269                 krb5_free_principal(ctx, blacklist_principal);
2270                 krb5_free_principal(ctx, self_princ);
2271                 krb5_cc_destroy(ctx, tmp_cc);
2272                 return code;
2273         }
2274
2275         if (s4u2proxy) {
2276                 /*
2277                  * If we want S4U2Proxy, we need the forwardable flag
2278                  * on the S4U2Self ticket.
2279                  */
2280                 krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2281         }
2282
2283         code = krb5_get_creds_opt_set_impersonate(ctx, options,
2284                                                   impersonate_principal);
2285         if (code != 0) {
2286                 krb5_get_creds_opt_free(ctx, options);
2287                 krb5_free_principal(ctx, blacklist_principal);
2288                 krb5_free_principal(ctx, self_princ);
2289                 krb5_cc_destroy(ctx, tmp_cc);
2290                 return code;
2291         }
2292
2293         code = krb5_get_creds(ctx, options, tmp_cc,
2294                               self_princ, &s4u2self_creds);
2295         krb5_get_creds_opt_free(ctx, options);
2296         krb5_free_principal(ctx, self_princ);
2297         if (code != 0) {
2298                 krb5_free_principal(ctx, blacklist_principal);
2299                 krb5_cc_destroy(ctx, tmp_cc);
2300                 return code;
2301         }
2302
2303         if (!s4u2proxy) {
2304                 krb5_cc_destroy(ctx, tmp_cc);
2305
2306                 /*
2307                  * Now make sure we store the impersonated principal
2308                  * and creds instead of the TGT related stuff
2309                  * in the krb5_ccache of the caller.
2310                  */
2311                 code = krb5_copy_creds_contents(ctx, s4u2self_creds,
2312                                                 &store_creds);
2313                 krb5_free_creds(ctx, s4u2self_creds);
2314                 if (code != 0) {
2315                         return code;
2316                 }
2317
2318                 /*
2319                  * It's important to store the principal the KDC
2320                  * returned, as otherwise the caller would not find
2321                  * the S4U2Self ticket in the krb5_ccache lookup.
2322                  */
2323                 store_principal = store_creds.client;
2324                 goto store;
2325         }
2326
2327         /*
2328          * We are trying S4U2Proxy:
2329          *
2330          * We need the ticket from the S4U2Self step
2331          * and our TGT in order to get the delegated ticket.
2332          */
2333         code = decode_Ticket((const uint8_t *)s4u2self_creds->ticket.data,
2334                              s4u2self_creds->ticket.length,
2335                              &s4u2self_ticket,
2336                              &s4u2self_ticketlen);
2337         if (code != 0) {
2338                 krb5_free_creds(ctx, s4u2self_creds);
2339                 krb5_free_principal(ctx, blacklist_principal);
2340                 krb5_cc_destroy(ctx, tmp_cc);
2341                 return code;
2342         }
2343
2344         /*
2345          * we need to remember the client principal of the
2346          * S4U2Self stage and as it needs to match the one we
2347          * will get for the S4U2Proxy stage. We need this
2348          * in order to detect KDCs which does not support S4U2Proxy.
2349          */
2350         whitelist_principal = s4u2self_creds->client;
2351         s4u2self_creds->client = NULL;
2352         krb5_free_creds(ctx, s4u2self_creds);
2353
2354         /*
2355          * For S4U2Proxy we also got a target service principal,
2356          * which also belongs to our own realm (available on
2357          * our client principal).
2358          */
2359         code = krb5_parse_name(ctx, target_service, &target_princ);
2360         if (code != 0) {
2361                 free_Ticket(&s4u2self_ticket);
2362                 krb5_free_principal(ctx, whitelist_principal);
2363                 krb5_free_principal(ctx, blacklist_principal);
2364                 krb5_cc_destroy(ctx, tmp_cc);
2365                 return code;
2366         }
2367
2368         code = krb5_principal_set_realm(ctx, target_princ, self_realm);
2369         if (code != 0) {
2370                 free_Ticket(&s4u2self_ticket);
2371                 krb5_free_principal(ctx, target_princ);
2372                 krb5_free_principal(ctx, whitelist_principal);
2373                 krb5_free_principal(ctx, blacklist_principal);
2374                 krb5_cc_destroy(ctx, tmp_cc);
2375                 return code;
2376         }
2377
2378         code = krb5_get_creds_opt_alloc(ctx, &options);
2379         if (code != 0) {
2380                 free_Ticket(&s4u2self_ticket);
2381                 krb5_free_principal(ctx, target_princ);
2382                 krb5_free_principal(ctx, whitelist_principal);
2383                 krb5_free_principal(ctx, blacklist_principal);
2384                 krb5_cc_destroy(ctx, tmp_cc);
2385                 return code;
2386         }
2387
2388         krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_FORWARDABLE);
2389         krb5_get_creds_opt_set_options(ctx, options, KRB5_GC_CONSTRAINED_DELEGATION);
2390
2391         code = krb5_get_creds_opt_set_ticket(ctx, options, &s4u2self_ticket);
2392         free_Ticket(&s4u2self_ticket);
2393         if (code != 0) {
2394                 krb5_get_creds_opt_free(ctx, options);
2395                 krb5_free_principal(ctx, target_princ);
2396                 krb5_free_principal(ctx, whitelist_principal);
2397                 krb5_free_principal(ctx, blacklist_principal);
2398                 krb5_cc_destroy(ctx, tmp_cc);
2399                 return code;
2400         }
2401
2402         code = krb5_get_creds(ctx, options, tmp_cc,
2403                               target_princ, &s4u2proxy_creds);
2404         krb5_get_creds_opt_free(ctx, options);
2405         krb5_free_principal(ctx, target_princ);
2406         krb5_cc_destroy(ctx, tmp_cc);
2407         if (code != 0) {
2408                 krb5_free_principal(ctx, whitelist_principal);
2409                 krb5_free_principal(ctx, blacklist_principal);
2410                 return code;
2411         }
2412
2413         /*
2414          * Now make sure we store the impersonated principal
2415          * and creds instead of the TGT related stuff
2416          * in the krb5_ccache of the caller.
2417          */
2418         code = krb5_copy_creds_contents(ctx, s4u2proxy_creds,
2419                                         &store_creds);
2420         krb5_free_creds(ctx, s4u2proxy_creds);
2421         if (code != 0) {
2422                 krb5_free_principal(ctx, whitelist_principal);
2423                 krb5_free_principal(ctx, blacklist_principal);
2424                 return code;
2425         }
2426
2427         /*
2428          * It's important to store the principal the KDC
2429          * returned, as otherwise the caller would not find
2430          * the S4U2Self ticket in the krb5_ccache lookup.
2431          */
2432         store_principal = store_creds.client;
2433
2434  store:
2435         if (blacklist_principal &&
2436             krb5_principal_compare(ctx, store_creds.client, blacklist_principal)) {
2437                 char *sp = NULL;
2438                 char *ip = NULL;
2439
2440                 code = krb5_unparse_name(ctx, blacklist_principal, &sp);
2441                 if (code != 0) {
2442                         sp = NULL;
2443                 }
2444                 code = krb5_unparse_name(ctx, impersonate_principal, &ip);
2445                 if (code != 0) {
2446                         ip = NULL;
2447                 }
2448                 DEBUG(1, ("kerberos_kinit_password_cc: "
2449                           "KDC returned self principal[%s] while impersonating [%s]\n",
2450                           sp?sp:"<no memory>",
2451                           ip?ip:"<no memory>"));
2452
2453                 SAFE_FREE(sp);
2454                 SAFE_FREE(ip);
2455
2456                 krb5_free_principal(ctx, whitelist_principal);
2457                 krb5_free_principal(ctx, blacklist_principal);
2458                 krb5_free_cred_contents(ctx, &store_creds);
2459                 return KRB5_FWD_BAD_PRINCIPAL;
2460         }
2461         if (blacklist_principal) {
2462                 krb5_free_principal(ctx, blacklist_principal);
2463         }
2464
2465         if (whitelist_principal &&
2466             !krb5_principal_compare(ctx, store_creds.client, whitelist_principal)) {
2467                 char *sp = NULL;
2468                 char *ep = NULL;
2469
2470                 code = krb5_unparse_name(ctx, store_creds.client, &sp);
2471                 if (code != 0) {
2472                         sp = NULL;
2473                 }
2474                 code = krb5_unparse_name(ctx, whitelist_principal, &ep);
2475                 if (code != 0) {
2476                         ep = NULL;
2477                 }
2478                 DEBUG(1, ("kerberos_kinit_password_cc: "
2479                           "KDC returned wrong principal[%s] we expected [%s]\n",
2480                           sp?sp:"<no memory>",
2481                           ep?ep:"<no memory>"));
2482
2483                 SAFE_FREE(sp);
2484                 SAFE_FREE(ep);
2485
2486                 krb5_free_principal(ctx, whitelist_principal);
2487                 krb5_free_cred_contents(ctx, &store_creds);
2488                 return KRB5_FWD_BAD_PRINCIPAL;
2489         }
2490         if (whitelist_principal) {
2491                 krb5_free_principal(ctx, whitelist_principal);
2492         }
2493
2494         code = krb5_cc_initialize(ctx, store_cc, store_principal);
2495         if (code != 0) {
2496                 krb5_free_cred_contents(ctx, &store_creds);
2497                 return code;
2498         }
2499
2500         code = krb5_cc_store_cred(ctx, store_cc, &store_creds);
2501         if (code != 0) {
2502                 krb5_free_cred_contents(ctx, &store_creds);
2503                 return code;
2504         }
2505
2506         if (expire_time) {
2507                 *expire_time = (time_t) store_creds.times.endtime;
2508         }
2509
2510         if (kdc_time) {
2511                 *kdc_time = (time_t) store_creds.times.starttime;
2512         }
2513
2514         krb5_free_cred_contents(ctx, &store_creds);
2515
2516         return 0;
2517 }
2518 #endif
2519
2520 #if !defined(HAVE_KRB5_MAKE_PRINCIPAL) && defined(HAVE_KRB5_BUILD_PRINCIPAL_ALLOC_VA)
2521 krb5_error_code smb_krb5_make_principal(krb5_context context,
2522                                         krb5_principal *principal,
2523                                         const char *_realm, ...)
2524 {
2525         krb5_error_code code;
2526         bool free_realm;
2527         char *realm;
2528         va_list ap;
2529
2530         if (_realm) {
2531                 realm = discard_const_p(char, _realm);
2532                 free_realm = false;
2533         } else {
2534                 code = krb5_get_default_realm(context, &realm);
2535                 if (code) {
2536                         return code;
2537                 }
2538                 free_realm = true;
2539         }
2540
2541         va_start(ap, _realm);
2542         code = krb5_build_principal_alloc_va(context, principal,
2543                                              strlen(realm), realm,
2544                                              ap);
2545         va_end(ap);
2546
2547         if (free_realm) {
2548                 krb5_free_default_realm(context, realm);
2549         }
2550
2551         return code;
2552 }
2553 #endif
2554
2555 #if !defined(HAVE_KRB5_CC_GET_LIFETIME) && defined(HAVE_KRB5_CC_RETRIEVE_CRED)
2556 /**
2557  * @brief Get the lifetime of the initial ticket in the cache.
2558  *
2559  * @param[in]  context  The kerberos context.
2560  *
2561  * @param[in]  id       The credential cache to get the ticket lifetime.
2562  *
2563  * @param[out] t        A pointer to a time value to store the lifetime.
2564  *
2565  * @return              0 on success, a krb5_error_code on error.
2566  */
2567 krb5_error_code smb_krb5_cc_get_lifetime(krb5_context context,
2568                                          krb5_ccache id,
2569                                          time_t *t)
2570 {
2571         krb5_cc_cursor cursor;
2572         krb5_error_code kerr;
2573         krb5_creds cred;
2574         krb5_timestamp now;
2575
2576         *t = 0;
2577
2578         kerr = krb5_timeofday(context, &now);
2579         if (kerr) {
2580                 return kerr;
2581         }
2582
2583         kerr = krb5_cc_start_seq_get(context, id, &cursor);
2584         if (kerr) {
2585                 return kerr;
2586         }
2587
2588         while ((kerr = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
2589 #ifndef HAVE_FLAGS_IN_KRB5_CREDS
2590                 if (cred.ticket_flags & TKT_FLG_INITIAL) {
2591 #else
2592                 if (cred.flags.b.initial) {
2593 #endif
2594                         if (now < cred.times.endtime) {
2595                                 *t = (time_t) (cred.times.endtime - now);
2596                         }
2597                         krb5_free_cred_contents(context, &cred);
2598                         break;
2599                 }
2600                 krb5_free_cred_contents(context, &cred);
2601         }
2602
2603         krb5_cc_end_seq_get(context, id, &cursor);
2604
2605         return kerr;
2606 }
2607 #endif /* HAVE_KRB5_CC_GET_LIFETIME */
2608
2609 #if !defined(HAVE_KRB5_FREE_CHECKSUM_CONTENTS) && defined(HAVE_FREE_CHECKSUM)
2610 void smb_krb5_free_checksum_contents(krb5_context ctx, krb5_checksum *cksum)
2611 {
2612         free_Checksum(cksum);
2613 }
2614 #endif
2615
2616 krb5_error_code smb_krb5_make_pac_checksum(TALLOC_CTX *mem_ctx,
2617                                            DATA_BLOB *pac_data,
2618                                            krb5_context context,
2619                                            const krb5_keyblock *keyblock,
2620                                            uint32_t *sig_type,
2621                                            DATA_BLOB *sig_blob)
2622 {
2623         krb5_error_code ret;
2624         krb5_checksum cksum;
2625 #if defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CREATE_CHECKSUM)
2626         krb5_crypto crypto;
2627
2628
2629         ret = krb5_crypto_init(context,
2630                                keyblock,
2631                                0,
2632                                &crypto);
2633         if (ret) {
2634                 DEBUG(0,("krb5_crypto_init() failed: %s\n",
2635                           smb_get_krb5_error_message(context, ret, mem_ctx)));
2636                 return ret;
2637         }
2638         ret = krb5_create_checksum(context,
2639                                    crypto,
2640                                    KRB5_KU_OTHER_CKSUM,
2641                                    0,
2642                                    pac_data->data,
2643                                    pac_data->length,
2644                                    &cksum);
2645         if (ret) {
2646                 DEBUG(2, ("PAC Verification failed: %s\n",
2647                           smb_get_krb5_error_message(context, ret, mem_ctx)));
2648         }
2649
2650         krb5_crypto_destroy(context, crypto);
2651
2652         if (ret) {
2653                 return ret;
2654         }
2655
2656         *sig_type = cksum.cksumtype;
2657         *sig_blob = data_blob_talloc(mem_ctx,
2658                                         cksum.checksum.data,
2659                                         cksum.checksum.length);
2660 #elif defined(HAVE_KRB5_C_MAKE_CHECKSUM)
2661         krb5_data input;
2662
2663         input.data = (char *)pac_data->data;
2664         input.length = pac_data->length;
2665
2666         ret = krb5_c_make_checksum(context,
2667                                    0,
2668                                    keyblock,
2669                                    KRB5_KEYUSAGE_APP_DATA_CKSUM,
2670                                    &input,
2671                                    &cksum);
2672         if (ret) {
2673                 DEBUG(2, ("PAC Verification failed: %s\n",
2674                           smb_get_krb5_error_message(context, ret, mem_ctx)));
2675                 return ret;
2676         }
2677
2678         *sig_type = cksum.checksum_type;
2679         *sig_blob = data_blob_talloc(mem_ctx,
2680                                         cksum.contents,
2681                                         cksum.length);
2682
2683 #else
2684 #error krb5_create_checksum or krb5_c_make_checksum not available
2685 #endif /* HAVE_KRB5_C_MAKE_CHECKSUM */
2686         smb_krb5_free_checksum_contents(context, &cksum);
2687
2688         return 0;
2689 }
2690
2691
2692 /*
2693  * smb_krb5_principal_get_realm
2694  *
2695  * @brief Get realm of a principal
2696  *
2697  * @param[in] context           The krb5_context
2698  * @param[in] principal         The principal
2699  * @return pointer to the realm
2700  *
2701  * Caller must free if the return value is not NULL.
2702  *
2703  */
2704
2705 char *smb_krb5_principal_get_realm(krb5_context context,
2706                                    krb5_const_principal principal)
2707 {
2708 #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM /* Heimdal */
2709         return strdup(discard_const_p(char, krb5_principal_get_realm(context, principal)));
2710 #elif defined(krb5_princ_realm) /* MIT */
2711         krb5_data *realm;
2712         realm = discard_const_p(krb5_data,
2713                                 krb5_princ_realm(context, principal));
2714         return strndup(realm->data, realm->length);
2715 #else
2716 #error UNKNOWN_GET_PRINC_REALM_FUNCTIONS
2717 #endif
2718 }
2719
2720 /*
2721  * smb_krb5_principal_set_realm
2722  *
2723  * @brief Get realm of a principal
2724  *
2725  * @param[in] context           The krb5_context
2726  * @param[in] principal         The principal
2727  * @param[in] realm             The realm
2728  * @return                      0 on success, a krb5_error_code on error.
2729  *
2730  */
2731
2732 krb5_error_code smb_krb5_principal_set_realm(krb5_context context,
2733                                              krb5_principal principal,
2734                                              const char *realm)
2735 {
2736 #ifdef HAVE_KRB5_PRINCIPAL_SET_REALM /* Heimdal */
2737         return krb5_principal_set_realm(context, principal, realm);
2738 #elif defined(krb5_princ_realm) && defined(krb5_princ_set_realm) /* MIT */
2739         krb5_error_code ret;
2740         krb5_data data;
2741         krb5_data *old_data;
2742
2743         old_data = krb5_princ_realm(context, principal);
2744
2745         ret = krb5_copy_data_contents(&data,
2746                                       realm,
2747                                       strlen(realm));
2748         if (ret) {
2749                 return ret;
2750         }
2751
2752         /* free realm before setting */
2753         free(old_data->data);
2754
2755         krb5_princ_set_realm(context, principal, &data);
2756
2757         return ret;
2758 #else
2759 #error UNKNOWN_PRINC_SET_REALM_FUNCTION
2760 #endif
2761 }
2762
2763
2764 /************************************************************************
2765  Routine to get the default realm from the kerberos credentials cache.
2766  Caller must free if the return value is not NULL.
2767 ************************************************************************/
2768
2769 static char *smb_krb5_get_default_realm_from_ccache(TALLOC_CTX *mem_ctx)
2770 {
2771         char *realm = NULL;
2772         krb5_context ctx = NULL;
2773         krb5_ccache cc = NULL;
2774         krb5_principal princ = NULL;
2775
2776         initialize_krb5_error_table();
2777         if (krb5_init_context(&ctx)) {
2778                 return NULL;
2779         }
2780
2781         DEBUG(5,("kerberos_get_default_realm_from_ccache: "
2782                 "Trying to read krb5 cache: %s\n",
2783                 krb5_cc_default_name(ctx)));
2784         if (krb5_cc_default(ctx, &cc)) {
2785                 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
2786                         "failed to read default cache\n"));
2787                 goto out;
2788         }
2789         if (krb5_cc_get_principal(ctx, cc, &princ)) {
2790                 DEBUG(5,("kerberos_get_default_realm_from_ccache: "
2791                         "failed to get default principal\n"));
2792                 goto out;
2793         }
2794
2795 #if defined(HAVE_KRB5_PRINCIPAL_GET_REALM)
2796         realm = talloc_strdup(mem_ctx, krb5_principal_get_realm(ctx, princ));
2797 #elif defined(HAVE_KRB5_PRINC_REALM)
2798         {
2799                 krb5_data *realm_data = krb5_princ_realm(ctx, princ);
2800                 realm = talloc_strndup(mem_ctx, realm_data->data, realm_data->length);
2801         }
2802 #endif
2803
2804   out:
2805
2806         if (ctx) {
2807                 if (princ) {
2808                         krb5_free_principal(ctx, princ);
2809                 }
2810                 if (cc) {
2811                         krb5_cc_close(ctx, cc);
2812                 }
2813                 krb5_free_context(ctx);
2814         }
2815
2816         return realm;
2817 }
2818
2819 /************************************************************************
2820  Routine to get the realm from a given DNS name.
2821 ************************************************************************/
2822
2823 static char *smb_krb5_get_realm_from_hostname(TALLOC_CTX *mem_ctx,
2824                                                 const char *hostname)
2825 {
2826 #if defined(HAVE_KRB5_REALM_TYPE)
2827         /* Heimdal. */
2828         krb5_realm *realm_list = NULL;
2829 #else
2830         /* MIT */
2831         char **realm_list = NULL;
2832 #endif
2833         char *realm = NULL;
2834         krb5_error_code kerr;
2835         krb5_context ctx = NULL;
2836
2837         initialize_krb5_error_table();
2838         if (krb5_init_context(&ctx)) {
2839                 return NULL;
2840         }
2841
2842         kerr = krb5_get_host_realm(ctx, hostname, &realm_list);
2843         if (kerr != 0) {
2844                 DEBUG(3,("kerberos_get_realm_from_hostname %s: "
2845                         "failed %s\n",
2846                         hostname ? hostname : "(NULL)",
2847                         error_message(kerr) ));
2848                 goto out;
2849         }
2850
2851         if (realm_list && realm_list[0]) {
2852                 realm = talloc_strdup(mem_ctx, realm_list[0]);
2853         }
2854
2855   out:
2856
2857         if (ctx) {
2858                 if (realm_list) {
2859                         krb5_free_host_realm(ctx, realm_list);
2860                         realm_list = NULL;
2861                 }
2862                 krb5_free_context(ctx);
2863                 ctx = NULL;
2864         }
2865         return realm;
2866 }
2867
2868 char *kerberos_get_principal_from_service_hostname(TALLOC_CTX *mem_ctx,
2869                                                    const char *service,
2870                                                    const char *remote_name,
2871                                                    const char *default_realm)
2872 {
2873         char *realm = NULL;
2874         char *host = NULL;
2875         char *principal;
2876         host = strchr_m(remote_name, '.');
2877         if (host) {
2878                 /* DNS name. */
2879                 realm = smb_krb5_get_realm_from_hostname(talloc_tos(),
2880                                                          remote_name);
2881         } else {
2882                 /* NetBIOS name - use our realm. */
2883                 realm = smb_krb5_get_default_realm_from_ccache(talloc_tos());
2884         }
2885
2886         if (realm == NULL || *realm == '\0') {
2887                 realm = talloc_strdup(talloc_tos(), default_realm);
2888                 if (!realm) {
2889                         return NULL;
2890                 }
2891                 DEBUG(3,("kerberos_get_principal_from_service_hostname: "
2892                          "cannot get realm from, "
2893                          "desthost %s or default ccache. Using default "
2894                          "smb.conf realm %s\n",
2895                          remote_name,
2896                          realm));
2897         }
2898
2899         principal = talloc_asprintf(mem_ctx,
2900                                     "%s/%s@%s",
2901                                     service, remote_name,
2902                                     realm);
2903         TALLOC_FREE(realm);
2904         return principal;
2905 }
2906
2907 char *smb_get_krb5_error_message(krb5_context context,
2908                                  krb5_error_code code,
2909                                  TALLOC_CTX *mem_ctx)
2910 {
2911         char *ret;
2912
2913 #if defined(HAVE_KRB5_GET_ERROR_MESSAGE) && defined(HAVE_KRB5_FREE_ERROR_MESSAGE)
2914         const char *context_error = krb5_get_error_message(context, code);
2915         if (context_error) {
2916                 ret = talloc_asprintf(mem_ctx, "%s: %s",
2917                                         error_message(code), context_error);
2918                 krb5_free_error_message(context, context_error);
2919                 return ret;
2920         }
2921 #endif
2922         ret = talloc_strdup(mem_ctx, error_message(code));
2923         return ret;
2924 }
2925
2926
2927 /**
2928 * @brief Return the kerberos library setting for "libdefaults:allow_weak_crypto"
2929 *
2930 * @param context        The krb5_context
2931 *
2932 * @return krb5_boolean
2933 *
2934 * Function returns true if weak crypto is allowd, false if not
2935 */
2936
2937 krb5_boolean smb_krb5_get_allowed_weak_crypto(krb5_context context)
2938 #if defined(HAVE_KRB5_CONFIG_GET_BOOL_DEFAULT)
2939 {
2940         return krb5_config_get_bool_default(context,
2941                                             NULL,
2942                                             FALSE,
2943                                             "libdefaults",
2944                                             "allow_weak_crypto",
2945                                             NULL);
2946 }
2947 #elif defined(HAVE_PROFILE_H) && defined(HAVE_KRB5_GET_PROFILE)
2948 {
2949 #include <profile.h>
2950         krb5_error_code ret;
2951         krb5_boolean ret_default = false;
2952         profile_t profile;
2953         int ret_profile;
2954
2955         ret = krb5_get_profile(context,
2956                                &profile);
2957         if (ret) {
2958                 return ret_default;
2959         }
2960
2961         ret = profile_get_boolean(profile,
2962                                   "libdefaults",
2963                                   "allow_weak_crypto",
2964                                   NULL, /* subsubname */
2965                                   ret_default, /* def_val */
2966                                   &ret_profile /* *ret_default */);
2967         if (ret) {
2968                 return ret_default;
2969         }
2970
2971         profile_release(profile);
2972
2973         return ret_profile;
2974 }
2975 #else
2976 #error UNKNOWN_KRB5_CONFIG_ROUTINES
2977 #endif
2978
2979 /**
2980 * @brief Return the type of a krb5_principal
2981 *
2982 * @param context        The krb5_context
2983 * @param principal      The const krb5_principal
2984 *
2985 * @return integer type of the principal
2986 */
2987 int smb_krb5_principal_get_type(krb5_context context,
2988                                 krb5_const_principal principal)
2989 {
2990 #ifdef HAVE_KRB5_PRINCIPAL_GET_TYPE /* Heimdal */
2991         return krb5_principal_get_type(context, principal);
2992 #elif defined(krb5_princ_type) /* MIT */
2993         return krb5_princ_type(context, principal);
2994 #else
2995 #error  UNKNOWN_PRINC_GET_TYPE_FUNCTION
2996 #endif
2997 }
2998
2999 /**
3000 * @brief Set the type of a krb5_principal
3001 *
3002 * @param context        The krb5_context
3003 * @param principal      The const krb5_principal
3004 * @param type           The principal type
3005 *
3006 */
3007 void smb_krb5_principal_set_type(krb5_context context,
3008                                  krb5_principal principal,
3009                                  int type)
3010 {
3011 #ifdef HAVE_KRB5_PRINCIPAL_SET_TYPE /* Heimdal */
3012         krb5_principal_set_type(context, principal, type);
3013 #elif defined(krb5_princ_type) /* MIT */
3014         krb5_princ_type(context, principal) = type;
3015 #else
3016 #error  UNKNOWN_PRINC_SET_TYPE_FUNCTION
3017 #endif
3018 }
3019
3020 /**
3021 * @brief Generate a krb5 warning, forwarding to com_err
3022 *
3023 * @param context        The krb5_context
3024 * @param fmt            The message format
3025 * @param ...            The message arguments
3026 *
3027 * @return
3028 */
3029 #if !defined(HAVE_KRB5_WARNX)
3030 krb5_error_code krb5_warnx(krb5_context context, const char *fmt, ...)
3031 {
3032         va_list args;
3033
3034         va_start(args, fmt);
3035         com_err_va("kdb_samba", errno, fmt, args);
3036         va_end(args);
3037
3038         return 0;
3039 }
3040 #endif
3041
3042 #else /* HAVE_KRB5 */
3043  /* this saves a few linking headaches */
3044  int cli_krb5_get_ticket(TALLOC_CTX *mem_ctx,
3045                         const char *principal, time_t time_offset,
3046                         DATA_BLOB *ticket, DATA_BLOB *session_key_krb5,
3047                         uint32_t extra_ap_opts,
3048                         const char *ccname, time_t *tgs_expire,
3049                         const char *impersonate_princ_s)
3050 {
3051          DEBUG(0,("NO KERBEROS SUPPORT\n"));
3052          return 1;
3053 }
3054
3055 #endif /* HAVE_KRB5 */