s4:rpc_server: remove unused dcesrv_connection_context->private_date
[samba.git] / auth / kerberos / kerberos_pac.c
1 /*
2    Unix SMB/CIFS implementation.
3    kerberos authorization data (PAC) utility library
4    Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
5    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
6    Copyright (C) Andrew Tridgell 2001
7    Copyright (C) Luke Howard 2002-2003
8    Copyright (C) Stefan Metzmacher 2004-2005
9    Copyright (C) Guenther Deschner 2005,2007,2008
10
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 3 of the License, or
14    (at your option) any later version.
15
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.
20
21    You should have received a copy of the GNU General Public License
22    along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 #include "includes.h"
26
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_AUTH
29
30 #ifdef HAVE_KRB5
31
32 #include "librpc/gen_ndr/ndr_krb5pac.h"
33 #include "auth/kerberos/pac_utils.h"
34
35 krb5_error_code check_pac_checksum(DATA_BLOB pac_data,
36                                           struct PAC_SIGNATURE_DATA *sig,
37                                           krb5_context context,
38                                           const krb5_keyblock *keyblock)
39 {
40         krb5_error_code ret;
41         krb5_checksum cksum;
42         krb5_keyusage usage = 0;
43         krb5_boolean checksum_valid = false;
44         krb5_data input;
45
46         switch (sig->type) {
47         case CKSUMTYPE_HMAC_MD5:
48                 /* ignores the key type */
49                 break;
50         case CKSUMTYPE_HMAC_SHA1_96_AES_256:
51                 if (KRB5_KEY_TYPE(keyblock) != ENCTYPE_AES256_CTS_HMAC_SHA1_96) {
52                         return EINVAL;
53                 }
54                 /* ok */
55                 break;
56         case CKSUMTYPE_HMAC_SHA1_96_AES_128:
57                 if (KRB5_KEY_TYPE(keyblock) != ENCTYPE_AES128_CTS_HMAC_SHA1_96) {
58                         return EINVAL;
59                 }
60                 /* ok */
61                 break;
62         default:
63                 DEBUG(2,("check_pac_checksum: Checksum Type %d is not supported\n",
64                         (int)sig->type));
65                 return EINVAL;
66         }
67
68 #ifdef HAVE_CHECKSUM_IN_KRB5_CHECKSUM /* Heimdal */
69         cksum.cksumtype = (krb5_cksumtype)sig->type;
70         cksum.checksum.length   = sig->signature.length;
71         cksum.checksum.data     = sig->signature.data;
72 #else /* MIT */
73         cksum.checksum_type     = (krb5_cksumtype)sig->type;
74         cksum.length            = sig->signature.length;
75         cksum.contents          = sig->signature.data;
76 #endif
77
78 #ifdef HAVE_KRB5_KU_OTHER_CKSUM /* Heimdal */
79         usage = KRB5_KU_OTHER_CKSUM;
80 #elif defined(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM) /* MIT */
81         usage = KRB5_KEYUSAGE_APP_DATA_CKSUM;
82 #else
83 #error UNKNOWN_KRB5_KEYUSAGE
84 #endif
85
86         input.data = (char *)pac_data.data;
87         input.length = pac_data.length;
88
89         ret = krb5_c_verify_checksum(context,
90                                      keyblock,
91                                      usage,
92                                      &input,
93                                      &cksum,
94                                      &checksum_valid);
95         if (!checksum_valid) {
96                 ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
97         }
98         if (ret){
99                 DEBUG(2,("check_pac_checksum: PAC Verification failed: %s (%d)\n",
100                         error_message(ret), ret));
101                 return ret;
102         }
103
104         return ret;
105 }
106
107 /**
108 * @brief Decode a blob containing a NDR encoded PAC structure
109 *
110 * @param mem_ctx          - The memory context
111 * @param pac_data_blob    - The data blob containing the NDR encoded data
112 * @param context          - The Kerberos Context
113 * @param service_keyblock - The Service Key used to verify the checksum
114 * @param client_principal - The client principal
115 * @param tgs_authtime     - The ticket timestamp
116 * @param pac_data_out     - [out] The decoded PAC
117 *
118 * @return - A NTSTATUS error code
119 */
120 NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
121                              DATA_BLOB pac_data_blob,
122                              krb5_context context,
123                              const krb5_keyblock *krbtgt_keyblock,
124                              const krb5_keyblock *service_keyblock,
125                              krb5_const_principal client_principal,
126                              time_t tgs_authtime,
127                              struct PAC_DATA **pac_data_out)
128 {
129         NTSTATUS status;
130         enum ndr_err_code ndr_err;
131         krb5_error_code ret;
132         DATA_BLOB modified_pac_blob;
133
134         NTTIME tgs_authtime_nttime;
135         int i;
136
137         struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
138         struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
139         struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
140         struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
141         struct PAC_LOGON_NAME *logon_name = NULL;
142         struct PAC_LOGON_INFO *logon_info = NULL;
143         struct PAC_DATA *pac_data = NULL;
144         struct PAC_DATA_RAW *pac_data_raw = NULL;
145
146         DATA_BLOB *srv_sig_blob = NULL;
147         DATA_BLOB *kdc_sig_blob = NULL;
148
149         bool bool_ret;
150
151         TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
152         if (!tmp_ctx) {
153                 return NT_STATUS_NO_MEMORY;
154         }
155
156         if (pac_data_out) {
157                 *pac_data_out = NULL;
158         }
159
160         pac_data = talloc(tmp_ctx, struct PAC_DATA);
161         pac_data_raw = talloc(tmp_ctx, struct PAC_DATA_RAW);
162         kdc_sig_wipe = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA);
163         srv_sig_wipe = talloc(tmp_ctx, struct PAC_SIGNATURE_DATA);
164         if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
165                 talloc_free(tmp_ctx);
166                 return NT_STATUS_NO_MEMORY;
167         }
168
169         ndr_err = ndr_pull_struct_blob(&pac_data_blob, pac_data, pac_data,
170                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
171         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
172                 status = ndr_map_error2ntstatus(ndr_err);
173                 DEBUG(0,("can't parse the PAC: %s\n",
174                         nt_errstr(status)));
175                 talloc_free(tmp_ctx);
176                 return status;
177         }
178
179         if (pac_data->num_buffers < 4) {
180                 /* we need logon_ingo, service_key and kdc_key */
181                 DEBUG(0,("less than 4 PAC buffers\n"));
182                 talloc_free(tmp_ctx);
183                 return NT_STATUS_INVALID_PARAMETER;
184         }
185
186         ndr_err = ndr_pull_struct_blob(
187                                 &pac_data_blob, pac_data_raw, pac_data_raw,
188                                 (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
189         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
190                 status = ndr_map_error2ntstatus(ndr_err);
191                 DEBUG(0,("can't parse the PAC: %s\n",
192                         nt_errstr(status)));
193                 talloc_free(tmp_ctx);
194                 return status;
195         }
196
197         if (pac_data_raw->num_buffers < 4) {
198                 /* we need logon_ingo, service_key and kdc_key */
199                 DEBUG(0,("less than 4 PAC buffers\n"));
200                 talloc_free(tmp_ctx);
201                 return NT_STATUS_INVALID_PARAMETER;
202         }
203
204         if (pac_data->num_buffers != pac_data_raw->num_buffers) {
205                 /* we need logon_ingo, service_key and kdc_key */
206                 DEBUG(0, ("misparse! PAC_DATA has %d buffers while "
207                           "PAC_DATA_RAW has %d\n", pac_data->num_buffers,
208                           pac_data_raw->num_buffers));
209                 talloc_free(tmp_ctx);
210                 return NT_STATUS_INVALID_PARAMETER;
211         }
212
213         for (i=0; i < pac_data->num_buffers; i++) {
214                 struct PAC_BUFFER *data_buf = &pac_data->buffers[i];
215                 struct PAC_BUFFER_RAW *raw_buf = &pac_data_raw->buffers[i];
216
217                 if (data_buf->type != raw_buf->type) {
218                         DEBUG(0, ("misparse! PAC_DATA buffer %d has type "
219                                   "%d while PAC_DATA_RAW has %d\n", i,
220                                   data_buf->type, raw_buf->type));
221                         talloc_free(tmp_ctx);
222                         return NT_STATUS_INVALID_PARAMETER;
223                 }
224                 switch (data_buf->type) {
225                 case PAC_TYPE_LOGON_INFO:
226                         if (!data_buf->info) {
227                                 break;
228                         }
229                         logon_info = data_buf->info->logon_info.info;
230                         break;
231                 case PAC_TYPE_SRV_CHECKSUM:
232                         if (!data_buf->info) {
233                                 break;
234                         }
235                         srv_sig_ptr = &data_buf->info->srv_cksum;
236                         srv_sig_blob = &raw_buf->info->remaining;
237                         break;
238                 case PAC_TYPE_KDC_CHECKSUM:
239                         if (!data_buf->info) {
240                                 break;
241                         }
242                         kdc_sig_ptr = &data_buf->info->kdc_cksum;
243                         kdc_sig_blob = &raw_buf->info->remaining;
244                         break;
245                 case PAC_TYPE_LOGON_NAME:
246                         logon_name = &data_buf->info->logon_name;
247                         break;
248                 default:
249                         break;
250                 }
251         }
252
253         if (!logon_info) {
254                 DEBUG(0,("PAC no logon_info\n"));
255                 talloc_free(tmp_ctx);
256                 return NT_STATUS_INVALID_PARAMETER;
257         }
258
259         if (!logon_name) {
260                 DEBUG(0,("PAC no logon_name\n"));
261                 talloc_free(tmp_ctx);
262                 return NT_STATUS_INVALID_PARAMETER;
263         }
264
265         if (!srv_sig_ptr || !srv_sig_blob) {
266                 DEBUG(0,("PAC no srv_key\n"));
267                 talloc_free(tmp_ctx);
268                 return NT_STATUS_INVALID_PARAMETER;
269         }
270
271         if (!kdc_sig_ptr || !kdc_sig_blob) {
272                 DEBUG(0,("PAC no kdc_key\n"));
273                 talloc_free(tmp_ctx);
274                 return NT_STATUS_INVALID_PARAMETER;
275         }
276
277         /* Find and zero out the signatures,
278          * as required by the signing algorithm */
279
280         /* We find the data blobs above,
281          * now we parse them to get at the exact portion we should zero */
282         ndr_err = ndr_pull_struct_blob(
283                         kdc_sig_blob, kdc_sig_wipe, kdc_sig_wipe,
284                         (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
285         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
286                 status = ndr_map_error2ntstatus(ndr_err);
287                 DEBUG(0,("can't parse the KDC signature: %s\n",
288                         nt_errstr(status)));
289                 talloc_free(tmp_ctx);
290                 return status;
291         }
292
293         ndr_err = ndr_pull_struct_blob(
294                         srv_sig_blob, srv_sig_wipe, srv_sig_wipe,
295                         (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
296         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
297                 status = ndr_map_error2ntstatus(ndr_err);
298                 DEBUG(0,("can't parse the SRV signature: %s\n",
299                         nt_errstr(status)));
300                 talloc_free(tmp_ctx);
301                 return status;
302         }
303
304         /* Now zero the decoded structure */
305         memset(kdc_sig_wipe->signature.data,
306                 '\0', kdc_sig_wipe->signature.length);
307         memset(srv_sig_wipe->signature.data,
308                 '\0', srv_sig_wipe->signature.length);
309
310         /* and reencode, back into the same place it came from */
311         ndr_err = ndr_push_struct_blob(
312                         kdc_sig_blob, pac_data_raw, kdc_sig_wipe,
313                         (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
314         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
315                 status = ndr_map_error2ntstatus(ndr_err);
316                 DEBUG(0,("can't repack the KDC signature: %s\n",
317                         nt_errstr(status)));
318                 talloc_free(tmp_ctx);
319                 return status;
320         }
321         ndr_err = ndr_push_struct_blob(
322                         srv_sig_blob, pac_data_raw, srv_sig_wipe,
323                         (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
324         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
325                 status = ndr_map_error2ntstatus(ndr_err);
326                 DEBUG(0,("can't repack the SRV signature: %s\n",
327                         nt_errstr(status)));
328                 talloc_free(tmp_ctx);
329                 return status;
330         }
331
332         /* push out the whole structure, but now with zero'ed signatures */
333         ndr_err = ndr_push_struct_blob(
334                         &modified_pac_blob, pac_data_raw, pac_data_raw,
335                         (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
336         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
337                 status = ndr_map_error2ntstatus(ndr_err);
338                 DEBUG(0,("can't repack the RAW PAC: %s\n",
339                         nt_errstr(status)));
340                 talloc_free(tmp_ctx);
341                 return status;
342         }
343
344         if (service_keyblock) {
345                 /* verify by service_key */
346                 ret = check_pac_checksum(modified_pac_blob, srv_sig_ptr,
347                                          context,
348                                          service_keyblock);
349                 if (ret) {
350                         DEBUG(5, ("PAC Decode: Failed to verify the service "
351                                   "signature: %s\n", error_message(ret)));
352                         return NT_STATUS_ACCESS_DENIED;
353                 }
354
355                 if (krbtgt_keyblock) {
356                         /* verify the service key checksum by krbtgt_key */
357                         ret = check_pac_checksum(srv_sig_ptr->signature, kdc_sig_ptr,
358                                                  context, krbtgt_keyblock);
359                         if (ret) {
360                                 DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n",
361                                           smb_get_krb5_error_message(context, ret, tmp_ctx)));
362                                 talloc_free(tmp_ctx);
363                                 return NT_STATUS_ACCESS_DENIED;
364                         }
365                 }
366         }
367
368         if (tgs_authtime) {
369                 /* Convert to NT time, so as not to loose accuracy in comparison */
370                 unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
371
372                 if (tgs_authtime_nttime != logon_name->logon_time) {
373                         DEBUG(2, ("PAC Decode: "
374                                   "Logon time mismatch between ticket and PAC!\n"));
375                         DEBUG(2, ("PAC Decode: PAC: %s\n",
376                                   nt_time_string(tmp_ctx, logon_name->logon_time)));
377                         DEBUG(2, ("PAC Decode: Ticket: %s\n",
378                                   nt_time_string(tmp_ctx, tgs_authtime_nttime)));
379                         talloc_free(tmp_ctx);
380                         return NT_STATUS_ACCESS_DENIED;
381                 }
382         }
383
384         if (client_principal) {
385                 char *client_principal_string;
386                 ret = krb5_unparse_name_flags(context, client_principal,
387                                               KRB5_PRINCIPAL_UNPARSE_NO_REALM|KRB5_PRINCIPAL_UNPARSE_DISPLAY,
388                                               &client_principal_string);
389                 if (ret) {
390                         DEBUG(2, ("Could not unparse name from ticket to match with name from PAC: [%s]:%s\n",
391                                   logon_name->account_name, error_message(ret)));
392                         talloc_free(tmp_ctx);
393                         return NT_STATUS_INVALID_PARAMETER;
394                 }
395
396                 bool_ret = strcmp(client_principal_string, logon_name->account_name) == 0;
397
398                 if (!bool_ret) {
399                         DEBUG(2, ("Name in PAC [%s] does not match principal name "
400                                   "in ticket [%s]\n",
401                                   logon_name->account_name,
402                                   client_principal_string));
403                         SAFE_FREE(client_principal_string);
404                         talloc_free(tmp_ctx);
405                         return NT_STATUS_ACCESS_DENIED;
406                 }
407                 SAFE_FREE(client_principal_string);
408
409         }
410
411         DEBUG(3,("Found account name from PAC: %s [%s]\n",
412                  logon_info->info3.base.account_name.string,
413                  logon_info->info3.base.full_name.string));
414
415         DEBUG(10,("Successfully validated Kerberos PAC\n"));
416
417         if (DEBUGLEVEL >= 10) {
418                 const char *s;
419                 s = NDR_PRINT_STRUCT_STRING(tmp_ctx, PAC_DATA, pac_data);
420                 if (s) {
421                         DEBUGADD(10,("%s\n", s));
422                 }
423         }
424
425         if (pac_data_out) {
426                 *pac_data_out = talloc_steal(mem_ctx, pac_data);
427         }
428
429         return NT_STATUS_OK;
430 }
431
432 NTSTATUS kerberos_pac_logon_info(TALLOC_CTX *mem_ctx,
433                                  DATA_BLOB blob,
434                                  krb5_context context,
435                                  const krb5_keyblock *krbtgt_keyblock,
436                                  const krb5_keyblock *service_keyblock,
437                                  krb5_const_principal client_principal,
438                                  time_t tgs_authtime,
439                                  struct PAC_LOGON_INFO **logon_info)
440 {
441         NTSTATUS nt_status;
442         struct PAC_DATA *pac_data;
443         int i;
444         nt_status = kerberos_decode_pac(mem_ctx,
445                                         blob,
446                                         context,
447                                         krbtgt_keyblock,
448                                         service_keyblock,
449                                         client_principal,
450                                         tgs_authtime,
451                                         &pac_data);
452         if (!NT_STATUS_IS_OK(nt_status)) {
453                 return nt_status;
454         }
455
456         *logon_info = NULL;
457         for (i=0; i < pac_data->num_buffers; i++) {
458                 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
459                         continue;
460                 }
461                 *logon_info = pac_data->buffers[i].info->logon_info.info;
462         }
463         if (!*logon_info) {
464                 return NT_STATUS_INVALID_PARAMETER;
465         }
466         return NT_STATUS_OK;
467 }
468
469 #endif