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
9 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "system/network.h"
28 #include "system/kerberos.h"
29 #include "auth/kerberos/kerberos.h"
31 #include "lib/ldb/include/ldb.h"
37 DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data)
40 DATA_BLOB pac_contents = data_blob(NULL, 0);
41 struct asn1_data data;
43 if (!auth_data->length) {
44 return data_blob(NULL, 0);
47 asn1_load(&data, *auth_data);
48 asn1_start_tag(&data, ASN1_SEQUENCE(0));
49 asn1_start_tag(&data, ASN1_SEQUENCE(0));
50 asn1_start_tag(&data, ASN1_CONTEXT(0));
51 asn1_read_Integer(&data, &data_type);
53 asn1_start_tag(&data, ASN1_CONTEXT(1));
54 asn1_read_OctetString(&data, &pac_contents);
60 out = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length);
62 data_blob_free(&pac_contents);
67 /**********************************************************************************
68 Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so
69 it's more like what microsoft does... see comment in utils/net_ads.c in the
70 ads_keytab_add_entry function for details.
71 ***********************************************************************************/
73 static krb5_error_code ads_keytab_verify_ticket(TALLOC_CTX *mem_ctx, krb5_context context,
74 krb5_auth_context *auth_context,
76 const krb5_data *p_packet,
77 krb5_flags *ap_req_options,
79 krb5_keyblock **keyblock)
81 krb5_error_code ret = 0;
82 krb5_keytab keytab = NULL;
83 krb5_kt_cursor kt_cursor;
84 krb5_keytab_entry kt_entry;
85 char *valid_princ_formats[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL };
86 char *entry_princ_s = NULL;
87 const char *my_name, *my_fqdn;
89 int number_matched_principals = 0;
90 const char *last_error_message;
92 /* Generate the list of principal names which we expect
93 * clients might want to use for authenticating to the file
94 * service. We allow name$,{host,service}/{name,fqdn,name.REALM}.
95 * (where service is specified by the caller) */
97 my_name = lp_netbios_name();
99 my_fqdn = name_to_fqdn(mem_ctx, my_name);
101 asprintf(&valid_princ_formats[0], "%s$@%s", my_name, lp_realm());
102 asprintf(&valid_princ_formats[1], "host/%s@%s", my_name, lp_realm());
103 asprintf(&valid_princ_formats[2], "host/%s@%s", my_fqdn, lp_realm());
104 asprintf(&valid_princ_formats[3], "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
105 asprintf(&valid_princ_formats[4], "%s/%s@%s", service, my_name, lp_realm());
106 asprintf(&valid_princ_formats[5], "%s/%s@%s", service, my_fqdn, lp_realm());
107 asprintf(&valid_princ_formats[6], "%s/%s.%s@%s", service, my_name, lp_realm(), lp_realm());
109 ZERO_STRUCT(kt_entry);
110 ZERO_STRUCT(kt_cursor);
112 ret = krb5_kt_default(context, &keytab);
114 last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx);
115 DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n",
116 last_error_message));
120 /* Iterate through the keytab. For each key, if the principal
121 * name case-insensitively matches one of the allowed formats,
122 * try verifying the ticket using that principal. */
124 ret = krb5_kt_start_seq_get(context, keytab, &kt_cursor);
125 if (ret == KRB5_KT_END || ret == ENOENT ) {
126 last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx);
128 last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx);
129 DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n",
130 last_error_message));
132 ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; /* Pick an error... */
133 last_error_message = "No principals in Keytab";
134 while (ret && (krb5_kt_next_entry(context, keytab, &kt_entry, &kt_cursor) == 0)) {
135 krb5_error_code upn_ret;
136 upn_ret = krb5_unparse_name(context, kt_entry.principal, &entry_princ_s);
138 last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx);
139 DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n",
140 last_error_message));
144 for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) {
145 if (!strequal(entry_princ_s, valid_princ_formats[i])) {
149 number_matched_principals++;
151 ret = krb5_rd_req_return_keyblock(context, auth_context, p_packet,
152 kt_entry.principal, keytab,
153 ap_req_options, pp_tkt, keyblock);
155 last_error_message = smb_get_krb5_error_message(context, ret, mem_ctx);
156 DEBUG(10, ("ads_keytab_verify_ticket: krb5_rd_req(%s) failed: %s\n",
157 entry_princ_s, last_error_message));
159 DEBUG(3,("ads_keytab_verify_ticket: krb5_rd_req succeeded for principal %s\n",
165 /* Free the name we parsed. */
166 krb5_free_unparsed_name(context, entry_princ_s);
167 entry_princ_s = NULL;
169 /* Free the entry we just read. */
170 smb_krb5_kt_free_entry(context, &kt_entry);
171 ZERO_STRUCT(kt_entry);
173 krb5_kt_end_seq_get(context, keytab, &kt_cursor);
176 ZERO_STRUCT(kt_cursor);
181 if (!number_matched_principals) {
182 DEBUG(3, ("ads_keytab_verify_ticket: no keytab principals matched expected file service name.\n"));
184 DEBUG(3, ("ads_keytab_verify_ticket: krb5_rd_req failed for all %d matched keytab principals\n",
185 number_matched_principals));
187 DEBUG(3, ("ads_keytab_verify_ticket: last error: %s\n", last_error_message));
191 krb5_free_unparsed_name(context, entry_princ_s);
195 krb5_keytab_entry zero_kt_entry;
196 ZERO_STRUCT(zero_kt_entry);
197 if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
198 smb_krb5_kt_free_entry(context, &kt_entry);
203 krb5_kt_cursor zero_csr;
204 ZERO_STRUCT(zero_csr);
205 if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
206 krb5_kt_end_seq_get(context, keytab, &kt_cursor);
211 krb5_kt_close(context, keytab);
217 /**********************************************************************************
218 Verify an incoming ticket and parse out the principal name and
219 authorization_data if available.
220 ***********************************************************************************/
222 NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
223 struct smb_krb5_context *smb_krb5_context,
224 krb5_auth_context *auth_context,
225 const char *realm, const char *service,
226 const DATA_BLOB *enc_ticket,
229 krb5_keyblock **keyblock)
231 krb5_keyblock *local_keyblock;
233 krb5_principal salt_princ;
235 krb5_flags ap_req_options = 0;
237 NTSTATUS creds_nt_status, status;
238 struct cli_credentials *machine_account;
240 machine_account = cli_credentials_init(mem_ctx);
241 cli_credentials_set_conf(machine_account);
242 creds_nt_status = cli_credentials_set_machine_account(machine_account);
244 if (!NT_STATUS_IS_OK(creds_nt_status)) {
245 DEBUG(3, ("Could not obtain machine account credentials from the local database\n"));
246 talloc_free(machine_account);
247 machine_account = NULL;
249 ret = salt_principal_from_credentials(mem_ctx, machine_account,
253 DEBUG(1,("ads_verify_ticket: maksing salt principal failed (%s)\n",
254 error_message(ret)));
255 return NT_STATUS_INTERNAL_ERROR;
259 /* This whole process is far more complex than I would
260 like. We have to go through all this to allow us to store
261 the secret internally, instead of using /etc/krb5.keytab */
264 * TODO: Actually hook in the replay cache in Heimdal, then
265 * re-add calls to setup a replay cache here, in our private
266 * directory. This will eventually prevent replay attacks
269 packet.length = enc_ticket->length;
270 packet.data = (krb5_pointer)enc_ticket->data;
272 ret = ads_keytab_verify_ticket(mem_ctx, smb_krb5_context->krb5_context, auth_context,
273 service, &packet, &ap_req_options, tkt, &local_keyblock);
274 if (ret && machine_account) {
276 krb5_principal server;
277 status = create_memory_keytab(mem_ctx, machine_account, smb_krb5_context,
279 if (!NT_STATUS_IS_OK(status)) {
282 ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context,
285 ret = krb5_rd_req_return_keyblock(smb_krb5_context->krb5_context, auth_context, &packet,
287 keytab, &ap_req_options, tkt,
293 DEBUG(3,("ads_secrets_verify_ticket: failed to decrypt with error %s\n",
294 smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx)));
295 return NT_STATUS_LOGON_FAILURE;
297 *keyblock = local_keyblock;
299 if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
300 krb5_data packet_out;
301 ret = krb5_mk_rep(smb_krb5_context->krb5_context, *auth_context, &packet_out);
303 krb5_free_ticket(smb_krb5_context->krb5_context, *tkt);
305 DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n",
306 smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx)));
307 return NT_STATUS_LOGON_FAILURE;
310 *ap_rep = data_blob_talloc(mem_ctx, packet_out.data, packet_out.length);
311 krb5_free_data_contents(smb_krb5_context->krb5_context, &packet_out);
313 *ap_rep = data_blob(NULL, 0);
319 #endif /* HAVE_KRB5 */