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