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
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #define KRB5_PRIVATE 1 /* this file uses PRIVATE interfaces! */
23 #define KRB5_DEPRECATED 1 /* this file uses DEPRECATED interfaces! */
29 #ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
30 #define KRB5_KEY_TYPE(k) ((k)->keytype)
31 #define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
32 #define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
34 #define KRB5_KEY_TYPE(k) ((k)->enctype)
35 #define KRB5_KEY_LENGTH(k) ((k)->length)
36 #define KRB5_KEY_DATA(k) ((k)->contents)
37 #endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
39 #ifndef HAVE_KRB5_SET_REAL_TIME
41 * This function is not in the Heimdal mainline.
43 krb5_error_code
krb5_set_real_time(krb5_context context
, int32_t seconds
, int32_t microseconds
)
48 ret
= krb5_us_timeofday(context
, &sec
, &usec
);
52 context
->kdc_sec_offset
= seconds
- sec
;
53 context
->kdc_usec_offset
= microseconds
- usec
;
59 #if defined(HAVE_KRB5_SET_DEFAULT_IN_TKT_ETYPES) && !defined(HAVE_KRB5_SET_DEFAULT_TGS_KTYPES)
60 krb5_error_code
krb5_set_default_tgs_ktypes(krb5_context ctx
, const krb5_enctype
*enc
)
62 return krb5_set_default_in_tkt_etypes(ctx
, enc
);
66 #if defined(HAVE_ADDR_TYPE_IN_KRB5_ADDRESS)
68 void setup_kaddr( krb5_address
*pkaddr
, struct sockaddr
*paddr
)
70 pkaddr
->addr_type
= KRB5_ADDRESS_INET
;
71 pkaddr
->address
.length
= sizeof(((struct sockaddr_in
*)paddr
)->sin_addr
);
72 pkaddr
->address
.data
= (char *)&(((struct sockaddr_in
*)paddr
)->sin_addr
);
74 #elif defined(HAVE_ADDRTYPE_IN_KRB5_ADDRESS)
76 void setup_kaddr( krb5_address
*pkaddr
, struct sockaddr
*paddr
)
78 pkaddr
->addrtype
= ADDRTYPE_INET
;
79 pkaddr
->length
= sizeof(((struct sockaddr_in
*)paddr
)->sin_addr
);
80 pkaddr
->contents
= (krb5_octet
*)&(((struct sockaddr_in
*)paddr
)->sin_addr
);
83 #error UNKNOWN_ADDRTYPE
86 #if defined(HAVE_KRB5_PRINCIPAL2SALT) && defined(HAVE_KRB5_USE_ENCTYPE) && defined(HAVE_KRB5_STRING_TO_KEY) && defined(HAVE_KRB5_ENCRYPT_BLOCK)
87 int create_kerberos_key_from_string_direct(krb5_context context
,
88 krb5_principal host_princ
,
95 krb5_encrypt_block eblock
;
97 ret
= krb5_principal2salt(context
, host_princ
, &salt
);
99 DEBUG(1,("krb5_principal2salt failed (%s)\n", error_message(ret
)));
102 krb5_use_enctype(context
, &eblock
, enctype
);
103 ret
= krb5_string_to_key(context
, &eblock
, key
, password
, &salt
);
104 SAFE_FREE(salt
.data
);
107 #elif defined(HAVE_KRB5_GET_PW_SALT) && defined(HAVE_KRB5_STRING_TO_KEY_SALT)
108 int create_kerberos_key_from_string_direct(krb5_context context
,
109 krb5_principal host_princ
,
112 krb5_enctype enctype
)
117 ret
= krb5_get_pw_salt(context
, host_princ
, &salt
);
119 DEBUG(1,("krb5_get_pw_salt failed (%s)\n", error_message(ret
)));
123 ret
= krb5_string_to_key_salt(context
, enctype
, password
->data
, salt
, key
);
124 krb5_free_salt(context
, salt
);
128 #error UNKNOWN_CREATE_KEY_FUNCTIONS
131 int create_kerberos_key_from_string(krb5_context context
,
132 krb5_principal host_princ
,
135 krb5_enctype enctype
)
137 krb5_principal salt_princ
= NULL
;
140 * Check if we've determined that the KDC is salting keys for this
141 * principal/enctype in a non-obvious way. If it is, try to match
144 salt_princ
= kerberos_fetch_salt_princ_for_host_princ(context
, host_princ
, enctype
);
145 ret
= create_kerberos_key_from_string_direct(context
, salt_princ
? salt_princ
: host_princ
, password
, key
, enctype
);
147 krb5_free_principal(context
, salt_princ
);
152 #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES)
153 krb5_error_code
get_kerberos_allowed_etypes(krb5_context context
,
154 krb5_enctype
**enctypes
)
156 return krb5_get_permitted_enctypes(context
, enctypes
);
158 #elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES)
159 krb5_error_code
get_kerberos_allowed_etypes(krb5_context context
,
160 krb5_enctype
**enctypes
)
162 return krb5_get_default_in_tkt_etypes(context
, enctypes
);
165 #error UNKNOWN_GET_ENCTYPES_FUNCTIONS
168 void free_kerberos_etypes(krb5_context context
,
169 krb5_enctype
*enctypes
)
171 #if defined(HAVE_KRB5_FREE_KTYPES)
172 krb5_free_ktypes(context
, enctypes
);
180 #if defined(HAVE_KRB5_AUTH_CON_SETKEY) && !defined(HAVE_KRB5_AUTH_CON_SETUSERUSERKEY)
181 krb5_error_code
krb5_auth_con_setuseruserkey(krb5_context context
,
182 krb5_auth_context auth_context
,
183 krb5_keyblock
*keyblock
)
185 return krb5_auth_con_setkey(context
, auth_context
, keyblock
);
189 void get_auth_data_from_tkt(DATA_BLOB
*auth_data
, krb5_ticket
*tkt
)
191 #if defined(HAVE_KRB5_TKT_ENC_PART2)
192 if (tkt
->enc_part2
&& tkt
->enc_part2
->authorization_data
&& tkt
->enc_part2
->authorization_data
[0] && tkt
->enc_part2
->authorization_data
[0]->length
)
193 *auth_data
= data_blob(tkt
->enc_part2
->authorization_data
[0]->contents
,
194 tkt
->enc_part2
->authorization_data
[0]->length
);
196 if (tkt
->ticket
.authorization_data
&& tkt
->ticket
.authorization_data
->len
)
197 *auth_data
= data_blob(tkt
->ticket
.authorization_data
->val
->ad_data
.data
,
198 tkt
->ticket
.authorization_data
->val
->ad_data
.length
);
202 krb5_const_principal
get_principal_from_tkt(krb5_ticket
*tkt
)
204 #if defined(HAVE_KRB5_TKT_ENC_PART2)
205 return tkt
->enc_part2
->client
;
211 #if !defined(HAVE_KRB5_LOCATE_KDC)
212 krb5_error_code
krb5_locate_kdc(krb5_context ctx
, const krb5_data
*realm
, struct sockaddr
**addr_pp
, int *naddrs
, int get_masters
)
214 krb5_krbhst_handle hnd
;
215 krb5_krbhst_info
*hinfo
;
224 rc
= krb5_krbhst_init(ctx
, realm
->data
, KRB5_KRBHST_KDC
, &hnd
);
226 DEBUG(0, ("krb5_locate_kdc: krb5_krbhst_init failed (%s)\n", error_message(rc
)));
230 for ( num_kdcs
= 0; (rc
= krb5_krbhst_next(ctx
, hnd
, &hinfo
) == 0); num_kdcs
++)
233 krb5_krbhst_reset(ctx
, hnd
);
236 DEBUG(0, ("krb5_locate_kdc: zero kdcs found !\n"));
237 krb5_krbhst_free(ctx
, hnd
);
241 sa
= SMB_MALLOC_ARRAY( struct sockaddr
, num_kdcs
);
243 DEBUG(0, ("krb5_locate_kdc: malloc failed\n"));
244 krb5_krbhst_free(ctx
, hnd
);
249 memset(sa
, '\0', sizeof(struct sockaddr
) * num_kdcs
);
251 for (i
= 0; i
< num_kdcs
&& (rc
= krb5_krbhst_next(ctx
, hnd
, &hinfo
) == 0); i
++) {
253 #if defined(HAVE_KRB5_KRBHST_GET_ADDRINFO)
254 rc
= krb5_krbhst_get_addrinfo(ctx
, hinfo
, &ai
);
256 DEBUG(0,("krb5_krbhst_get_addrinfo failed: %s\n", error_message(rc
)));
260 if (hinfo
->ai
&& hinfo
->ai
->ai_family
== AF_INET
)
261 memcpy(&sa
[i
], hinfo
->ai
->ai_addr
, sizeof(struct sockaddr
));
264 krb5_krbhst_free(ctx
, hnd
);
272 #if !defined(HAVE_KRB5_FREE_UNPARSED_NAME)
273 void krb5_free_unparsed_name(krb5_context context
, char *val
)
279 void kerberos_free_data_contents(krb5_context context
, krb5_data
*pdata
)
281 #if defined(HAVE_KRB5_FREE_DATA_CONTENTS)
283 krb5_free_data_contents(context
, pdata
);
286 SAFE_FREE(pdata
->data
);
290 void kerberos_set_creds_enctype(krb5_creds
*pcreds
, int enctype
)
292 #if defined(HAVE_KRB5_KEYBLOCK_IN_CREDS)
293 KRB5_KEY_TYPE((&pcreds
->keyblock
)) = enctype
;
294 #elif defined(HAVE_KRB5_SESSION_IN_CREDS)
295 KRB5_KEY_TYPE((&pcreds
->session
)) = enctype
;
297 #error UNKNOWN_KEYBLOCK_MEMBER_IN_KRB5_CREDS_STRUCT
301 BOOL
kerberos_compatible_enctypes(krb5_context context
,
302 krb5_enctype enctype1
,
303 krb5_enctype enctype2
)
305 #if defined(HAVE_KRB5_C_ENCTYPE_COMPARE)
306 krb5_boolean similar
= 0;
308 krb5_c_enctype_compare(context
, enctype1
, enctype2
, &similar
);
309 return similar
? True
: False
;
310 #elif defined(HAVE_KRB5_ENCTYPES_COMPATIBLE_KEYS)
311 return krb5_enctypes_compatible_keys(context
, enctype1
, enctype2
) ? True
: False
;
315 static BOOL
ads_cleanup_expired_creds(krb5_context context
,
319 krb5_error_code retval
;
321 DEBUG(3, ("Ticket in ccache[%s] expiration %s\n",
322 krb5_cc_default_name(context
),
323 http_timestring(credsp
->times
.endtime
)));
325 /* we will probably need new tickets if the current ones
326 will expire within 10 seconds.
328 if (credsp
->times
.endtime
>= (time(NULL
) + 10))
331 /* heimdal won't remove creds from a file ccache, and
332 perhaps we shouldn't anyway, since internally we
333 use memory ccaches, and a FILE one probably means that
334 we're using creds obtained outside of our exectuable
336 if (StrCaseCmp(krb5_cc_get_type(context
, ccache
), "FILE") == 0) {
337 DEBUG(5, ("ads_cleanup_expired_creds: We do not remove creds from a FILE ccache\n"));
341 retval
= krb5_cc_remove_cred(context
, ccache
, 0, credsp
);
343 DEBUG(1, ("ads_cleanup_expired_creds: krb5_cc_remove_cred failed, err %s\n",
344 error_message(retval
)));
345 /* If we have an error in this, we want to display it,
346 but continue as though we deleted it */
352 we can't use krb5_mk_req because w2k wants the service to be in a particular format
354 static krb5_error_code
ads_krb5_mk_req(krb5_context context
,
355 krb5_auth_context
*auth_context
,
356 const krb5_flags ap_req_options
,
357 const char *principal
,
361 krb5_error_code retval
;
362 krb5_principal server
;
366 BOOL creds_ready
= False
;
368 retval
= krb5_parse_name(context
, principal
, &server
);
370 DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal
));
374 /* obtain ticket & session key */
376 if ((retval
= krb5_copy_principal(context
, server
, &creds
.server
))) {
377 DEBUG(1,("krb5_copy_principal failed (%s)\n",
378 error_message(retval
)));
382 if ((retval
= krb5_cc_get_principal(context
, ccache
, &creds
.client
))) {
383 /* This can commonly fail on smbd startup with no ticket in the cache.
384 * Report at higher level than 1. */
385 DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n",
386 error_message(retval
)));
390 while(!creds_ready
) {
391 if ((retval
= krb5_get_credentials(context
, 0, ccache
,
393 DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n",
394 principal
, error_message(retval
)));
398 /* cope with ticket being in the future due to clock skew */
399 if ((unsigned)credsp
->times
.starttime
> time(NULL
)) {
400 time_t t
= time(NULL
);
401 int time_offset
=(int)((unsigned)credsp
->times
.starttime
-t
);
402 DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset
));
403 krb5_set_real_time(context
, t
+ time_offset
+ 1, 0);
406 if (!ads_cleanup_expired_creds(context
, ccache
, credsp
))
410 DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s) is valid until: (%s - %u)\n",
411 principal
, krb5_cc_default_name(context
),
412 http_timestring((unsigned)credsp
->times
.endtime
),
413 (unsigned)credsp
->times
.endtime
));
416 retval
= krb5_mk_req_extended(context
, auth_context
, ap_req_options
,
417 &in_data
, credsp
, outbuf
);
419 DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n",
420 error_message(retval
)));
423 krb5_free_creds(context
, credsp
);
426 krb5_free_cred_contents(context
, &creds
);
429 krb5_free_principal(context
, server
);
435 get a kerberos5 ticket for the given service
437 int cli_krb5_get_ticket(const char *principal
, time_t time_offset
,
438 DATA_BLOB
*ticket
, DATA_BLOB
*session_key_krb5
)
440 krb5_error_code retval
;
442 krb5_context context
= NULL
;
443 krb5_ccache ccdef
= NULL
;
444 krb5_auth_context auth_context
= NULL
;
445 krb5_enctype enc_types
[] = {
446 #ifdef ENCTYPE_ARCFOUR_HMAC
447 ENCTYPE_ARCFOUR_HMAC
,
453 retval
= krb5_init_context(&context
);
455 DEBUG(1,("cli_krb5_get_ticket: krb5_init_context failed (%s)\n",
456 error_message(retval
)));
460 if (time_offset
!= 0) {
461 krb5_set_real_time(context
, time(NULL
) + time_offset
, 0);
464 if ((retval
= krb5_cc_default(context
, &ccdef
))) {
465 DEBUG(1,("cli_krb5_get_ticket: krb5_cc_default failed (%s)\n",
466 error_message(retval
)));
470 if ((retval
= krb5_set_default_tgs_ktypes(context
, enc_types
))) {
471 DEBUG(1,("cli_krb5_get_ticket: krb5_set_default_tgs_ktypes failed (%s)\n",
472 error_message(retval
)));
476 if ((retval
= ads_krb5_mk_req(context
,
484 get_krb5_smb_session_key(context
, auth_context
, session_key_krb5
, False
);
486 *ticket
= data_blob(packet
.data
, packet
.length
);
488 kerberos_free_data_contents(context
, &packet
);
494 krb5_cc_close(context
, ccdef
);
496 krb5_auth_con_free(context
, auth_context
);
497 krb5_free_context(context
);
503 BOOL
get_krb5_smb_session_key(krb5_context context
, krb5_auth_context auth_context
, DATA_BLOB
*session_key
, BOOL remote
)
510 err
= krb5_auth_con_getremotesubkey(context
, auth_context
, &skey
);
512 err
= krb5_auth_con_getlocalsubkey(context
, auth_context
, &skey
);
513 if (err
== 0 && skey
!= NULL
) {
514 DEBUG(10, ("Got KRB5 session key of length %d\n", KRB5_KEY_LENGTH(skey
)));
515 *session_key
= data_blob(KRB5_KEY_DATA(skey
), KRB5_KEY_LENGTH(skey
));
516 dump_data_pw("KRB5 Session Key:\n", session_key
->data
, session_key
->length
);
520 krb5_free_keyblock(context
, skey
);
522 DEBUG(10, ("KRB5 error getting session key %d\n", err
));
529 #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
530 const krb5_data
*krb5_princ_component(krb5_context context
, krb5_principal principal
, int i
);
532 const krb5_data
*krb5_princ_component(krb5_context context
, krb5_principal principal
, int i
)
534 static krb5_data kdata
;
536 kdata
.data
= (char *)krb5_principal_get_comp_string(context
, principal
, i
);
537 kdata
.length
= strlen(kdata
.data
);
542 krb5_error_code
smb_krb5_kt_free_entry(krb5_context context
, krb5_keytab_entry
*kt_entry
)
544 #if defined(HAVE_KRB5_KT_FREE_ENTRY)
545 return krb5_kt_free_entry(context
, kt_entry
);
546 #elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS)
547 return krb5_free_keytab_entry_contents(context
, kt_entry
);
549 #error UNKNOWN_KT_FREE_FUNCTION
553 #else /* HAVE_KRB5 */
554 /* this saves a few linking headaches */
555 int cli_krb5_get_ticket(const char *principal
, time_t time_offset
,
556 DATA_BLOB
*ticket
, DATA_BLOB
*session_key_krb5
)
558 DEBUG(0,("NO KERBEROS SUPPORT\n"));