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