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