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