2 Unix SMB/CIFS implementation.
3 kerberos utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Luke Howard 2003
7 Copyright (C) Guenther Deschner 2003
8 Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "system/kerberos.h"
27 #include "libcli/auth/kerberos.h"
32 static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data)
35 DATA_BLOB pac_contents = data_blob(NULL, 0);
36 struct asn1_data data;
38 if (!auth_data->length) {
39 return data_blob(NULL, 0);
42 asn1_load(&data, *auth_data);
43 asn1_start_tag(&data, ASN1_SEQUENCE(0));
44 asn1_start_tag(&data, ASN1_SEQUENCE(0));
45 asn1_start_tag(&data, ASN1_CONTEXT(0));
46 asn1_read_Integer(&data, &data_type);
48 asn1_start_tag(&data, ASN1_CONTEXT(1));
49 asn1_read_OctetString(&data, &pac_contents);
55 out = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length);
57 data_blob_free(&pac_contents);
62 /**********************************************************************************
63 Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so
64 it's more like what microsoft does... see comment in utils/net_ads.c in the
65 ads_keytab_add_entry function for details.
66 ***********************************************************************************/
68 static krb5_error_code ads_keytab_verify_ticket(krb5_context context, krb5_auth_context auth_context,
69 const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt,
70 krb5_keyblock *keyblock)
72 krb5_error_code ret = 0;
74 krb5_keytab keytab = NULL;
75 krb5_kt_cursor cursor;
76 krb5_keytab_entry kt_entry;
77 char *princ_name = NULL;
79 ZERO_STRUCT(kt_entry);
82 ZERO_STRUCTP(keyblock);
84 ret = krb5_kt_default(context, &keytab);
86 DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n", error_message(ret)));
90 ret = krb5_kt_start_seq_get(context, keytab, &cursor);
92 DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", error_message(ret)));
96 while (!(ret = krb5_kt_next_entry(context, keytab, &kt_entry, &cursor))) {
97 ret = krb5_unparse_name(context, kt_entry.principal, &princ_name);
99 DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", error_message(ret)));
102 DEBUG(10, ("Checking principal: %s\n", princ_name));
103 /* Look for a CIFS ticket */
104 if (!strncasecmp(princ_name, "cifs/", 5) ||
105 !strncasecmp(princ_name, "host/", 5) ||
106 !strncasecmp(princ_name, "ldap/", 5)) {
107 #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
108 krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.keyblock);
110 krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.key);
113 p_packet->length = ticket->length;
114 p_packet->data = (krb5_pointer)ticket->data;
116 ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt);
118 unsigned int keytype;
119 krb5_free_unparsed_name(context, princ_name);
121 #ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
122 keytype = (unsigned int) kt_entry.keyblock.keytype;
123 copy_EncryptionKey(&kt_entry.keyblock, keyblock);
125 keytype = (unsigned int) kt_entry.key.enctype;
126 /* TODO: copy the keyblock on MIT krb5*/
128 DEBUG(10,("ads_keytab_verify_ticket: enc type [%u] decrypted message !\n",
134 krb5_free_unparsed_name(context, princ_name);
137 if (ret && ret != KRB5_KT_END) {
138 /* This failed because something went wrong, not because the keytab file was empty. */
139 DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_next_entry failed (%s)\n", error_message(ret)));
140 } else if (ret == KRB5_KT_END) {
141 DEBUG(10, ("ads_keytab_verify_ticket: no keytab entry found: %s\n", error_message(ret)));
143 DEBUG(10, ("ads_keytab_verify_ticket: keytab entry found: %s\n", princ_name));
148 krb5_free_unparsed_name(context, princ_name);
151 krb5_kt_cursor zero_csr;
152 ZERO_STRUCT(zero_csr);
153 if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
154 krb5_kt_end_seq_get(context, keytab, &cursor);
158 krb5_kt_close(context, keytab);
164 /**********************************************************************************
165 Try to verify a ticket using the secrets.tdb.
166 ***********************************************************************************/
168 static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context,
169 krb5_principal host_princ,
170 const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt,
171 krb5_keyblock *keyblock)
173 krb5_error_code ret = 0;
174 char *password_s = NULL;
176 krb5_enctype *enctypes = NULL;
179 ZERO_STRUCTP(keyblock);
181 if (!secrets_init()) {
182 DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n"));
186 password_s = secrets_fetch_machine_password(lp_workgroup());
188 DEBUG(1,("ads_secrets_verify_ticket: failed to fetch machine password\n"));
192 password.data = password_s;
193 password.length = strlen(password_s);
195 /* CIFS doesn't use addresses in tickets. This would break NAT. JRA */
197 if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) {
198 DEBUG(1,("ads_secrets_verify_ticket: krb5_get_permitted_enctypes failed (%s)\n",
199 error_message(ret)));
203 p_packet->length = ticket->length;
204 p_packet->data = (krb5_pointer)ticket->data;
206 ret = KRB5_BAD_ENCTYPE;
207 /* We need to setup a auth context with each possible encoding type in turn. */
208 for (i=0;enctypes[i];i++) {
209 krb5_error_code our_ret;
210 our_ret = create_kerberos_key_from_string(context, host_princ, &password, keyblock, enctypes[i]);
216 krb5_auth_con_setuseruserkey(context, auth_context, keyblock);
218 our_ret = krb5_rd_req(context, &auth_context, p_packet,
222 DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n",
223 (unsigned int)enctypes[i] ));
228 krb5_free_keyblock_contents(context, keyblock);
230 DEBUG((our_ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
231 ("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n",
232 (unsigned int)enctypes[i], error_message(our_ret)));
234 if (our_ret != KRB5_BAD_ENCTYPE) {
241 free_kerberos_etypes(context, enctypes);
242 SAFE_FREE(password_s);
247 /**********************************************************************************
248 Verify an incoming ticket and parse out the principal name and
249 authorization_data if available.
250 ***********************************************************************************/
252 NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
253 krb5_context context,
254 krb5_auth_context auth_context,
255 const char *realm, const DATA_BLOB *ticket,
256 char **principal, DATA_BLOB *auth_data,
258 krb5_keyblock *keyblock)
260 NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
262 krb5_ticket *tkt = NULL;
263 krb5_rcache rcache = NULL;
266 krb5_principal host_princ = NULL;
267 char *host_princ_s = NULL;
268 BOOL got_replay_mutex = False;
272 char *malloc_principal;
275 ZERO_STRUCTP(auth_data);
276 ZERO_STRUCTP(ap_rep);
278 /* This whole process is far more complex than I would
279 like. We have to go through all this to allow us to store
280 the secret internally, instead of using /etc/krb5.keytab */
282 myname = name_to_fqdn(mem_ctx, lp_netbios_name());
284 asprintf(&host_princ_s, "host/%s@%s", myname, lp_realm());
285 ret = krb5_parse_name(context, host_princ_s, &host_princ);
287 DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n",
288 host_princ_s, error_message(ret)));
293 /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5
294 * code surrounding the replay cache... */
296 if (!grab_server_mutex("replay cache mutex")) {
297 DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n"));
301 got_replay_mutex = True;
304 * JRA. We must set the rcache here. This will prevent replay attacks.
307 ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache);
309 DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret)));
313 ret = krb5_auth_con_setrcache(context, auth_context, rcache);
315 DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret)));
319 ret = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt, keyblock);
321 DEBUG(10, ("ads_secrets_verify_ticket: using host principal: [%s]\n", host_princ_s));
322 ret = ads_secrets_verify_ticket(context, auth_context, host_princ,
323 ticket, &packet, &tkt, keyblock);
326 release_server_mutex();
327 got_replay_mutex = False;
330 DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n",
331 error_message(ret)));
335 ret = krb5_mk_rep(context, auth_context, &packet);
337 DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n",
338 error_message(ret)));
342 *ap_rep = data_blob_talloc(mem_ctx, packet.data, packet.length);
343 SAFE_FREE(packet.data);
347 file_save("/tmp/ticket.dat", ticket->data, ticket->length);
350 *auth_data = get_auth_data_from_tkt(mem_ctx, tkt);
352 *auth_data = unwrap_pac(mem_ctx, auth_data);
355 if (tkt->enc_part2) {
356 file_save("/tmp/authdata.dat",
357 tkt->enc_part2->authorization_data[0]->contents,
358 tkt->enc_part2->authorization_data[0]->length);
362 if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt),
363 &malloc_principal))) {
364 DEBUG(3,("ads_verify_ticket: krb5_unparse_name failed (%s)\n",
365 error_message(ret)));
366 sret = NT_STATUS_LOGON_FAILURE;
370 *principal = talloc_strdup(mem_ctx, malloc_principal);
371 SAFE_FREE(malloc_principal);
373 DEBUG(3,("ads_verify_ticket: talloc_strdup() failed\n"));
374 sret = NT_STATUS_NO_MEMORY;
382 if (got_replay_mutex) {
383 release_server_mutex();
386 if (!NT_STATUS_IS_OK(sret)) {
387 data_blob_free(auth_data);
390 if (!NT_STATUS_IS_OK(sret)) {
391 data_blob_free(ap_rep);
395 krb5_free_principal(context, host_princ);
399 krb5_free_ticket(context, tkt);
402 SAFE_FREE(host_princ_s);
407 #endif /* HAVE_KRB5 */