ed158ee2d8c0cf2387768720247d947413054f2c
[ira/wip.git] / source3 / libads / authdata.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 #include "librpc/gen_ndr/ndr_krb5pac.h"
27 #include "smb_krb5.h"
28 #include "authdata.h"
29
30 #ifdef HAVE_KRB5
31
32 /****************************************************************
33 ****************************************************************/
34
35 static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx,
36                                           DATA_BLOB pac_data,
37                                           struct PAC_SIGNATURE_DATA *sig,
38                                           krb5_context context,
39                                           krb5_keyblock *keyblock)
40 {
41         krb5_error_code ret;
42         krb5_checksum cksum;
43         krb5_keyusage usage = 0;
44
45         smb_krb5_checksum_from_pac_sig(&cksum, sig);
46
47 #ifdef HAVE_KRB5_KU_OTHER_CKSUM /* Heimdal */
48         usage = KRB5_KU_OTHER_CKSUM;
49 #elif defined(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM) /* MIT */
50         usage = KRB5_KEYUSAGE_APP_DATA_CKSUM;
51 #else
52 #error UNKNOWN_KRB5_KEYUSAGE
53 #endif
54
55         ret = smb_krb5_verify_checksum(context,
56                                        keyblock,
57                                        usage,
58                                        &cksum,
59                                        pac_data.data,
60                                        pac_data.length);
61
62         if (ret) {
63                 DEBUG(2,("check_pac_checksum: PAC Verification failed: %s (%d)\n",
64                         error_message(ret), ret));
65                 return ret;
66         }
67
68         return ret;
69 }
70
71 /****************************************************************
72 ****************************************************************/
73
74  NTSTATUS decode_pac_data(TALLOC_CTX *mem_ctx,
75                          DATA_BLOB *pac_data_blob,
76                          krb5_context context,
77                          krb5_keyblock *service_keyblock,
78                          krb5_const_principal client_principal,
79                          time_t tgs_authtime,
80                          struct PAC_DATA **pac_data_out)
81 {
82         NTSTATUS status;
83         enum ndr_err_code ndr_err;
84         krb5_error_code ret;
85         DATA_BLOB modified_pac_blob;
86
87         NTTIME tgs_authtime_nttime;
88         krb5_principal client_principal_pac = NULL;
89         int i;
90
91         struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL;
92         struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL;
93         struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL;
94         struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL;
95         struct PAC_LOGON_NAME *logon_name = NULL;
96         struct PAC_LOGON_INFO *logon_info = NULL;
97         struct PAC_DATA *pac_data = NULL;
98         struct PAC_DATA_RAW *pac_data_raw = NULL;
99
100         DATA_BLOB *srv_sig_blob = NULL;
101         DATA_BLOB *kdc_sig_blob = NULL;
102
103         *pac_data_out = NULL;
104
105         pac_data = TALLOC_ZERO_P(mem_ctx, struct PAC_DATA);
106         pac_data_raw = TALLOC_ZERO_P(mem_ctx, struct PAC_DATA_RAW);
107         kdc_sig_wipe = TALLOC_ZERO_P(mem_ctx, struct PAC_SIGNATURE_DATA);
108         srv_sig_wipe = TALLOC_ZERO_P(mem_ctx, struct PAC_SIGNATURE_DATA);
109         if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) {
110                 return NT_STATUS_NO_MEMORY;
111         }
112
113         ndr_err = ndr_pull_struct_blob(pac_data_blob, pac_data,
114                         NULL, pac_data,
115                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
116         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
117                 status = ndr_map_error2ntstatus(ndr_err);
118                 DEBUG(0,("can't parse the PAC: %s\n",
119                         nt_errstr(status)));
120                 return status;
121         }
122
123         if (pac_data->num_buffers < 4) {
124                 /* we need logon_ingo, service_key and kdc_key */
125                 DEBUG(0,("less than 4 PAC buffers\n"));
126                 return NT_STATUS_INVALID_PARAMETER;
127         }
128
129         ndr_err = ndr_pull_struct_blob(pac_data_blob, pac_data_raw,
130                                        NULL, pac_data_raw,
131                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW);
132         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
133                 status = ndr_map_error2ntstatus(ndr_err);
134                 DEBUG(0,("can't parse the PAC: %s\n",
135                         nt_errstr(status)));
136                 return status;
137         }
138
139         if (pac_data_raw->num_buffers < 4) {
140                 /* we need logon_ingo, service_key and kdc_key */
141                 DEBUG(0,("less than 4 PAC buffers\n"));
142                 return NT_STATUS_INVALID_PARAMETER;
143         }
144
145         if (pac_data->num_buffers != pac_data_raw->num_buffers) {
146                 /* we need logon_ingo, service_key and kdc_key */
147                 DEBUG(0,("misparse!  PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n",
148                          pac_data->num_buffers, pac_data_raw->num_buffers));
149                 return NT_STATUS_INVALID_PARAMETER;
150         }
151
152         for (i=0; i < pac_data->num_buffers; i++) {
153                 if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) {
154                         DEBUG(0,("misparse!  PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n",
155                                  i, pac_data->buffers[i].type, pac_data->buffers[i].type));
156                         return NT_STATUS_INVALID_PARAMETER;
157                 }
158                 switch (pac_data->buffers[i].type) {
159                         case PAC_TYPE_LOGON_INFO:
160                                 if (!pac_data->buffers[i].info) {
161                                         break;
162                                 }
163                                 logon_info = pac_data->buffers[i].info->logon_info.info;
164                                 break;
165                         case PAC_TYPE_SRV_CHECKSUM:
166                                 if (!pac_data->buffers[i].info) {
167                                         break;
168                                 }
169                                 srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum;
170                                 srv_sig_blob = &pac_data_raw->buffers[i].info->remaining;
171                                 break;
172                         case PAC_TYPE_KDC_CHECKSUM:
173                                 if (!pac_data->buffers[i].info) {
174                                         break;
175                                 }
176                                 kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum;
177                                 kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining;
178                                 break;
179                         case PAC_TYPE_LOGON_NAME:
180                                 logon_name = &pac_data->buffers[i].info->logon_name;
181                                 break;
182                         default:
183                                 break;
184                 }
185         }
186
187         if (!logon_info) {
188                 DEBUG(0,("PAC no logon_info\n"));
189                 return NT_STATUS_INVALID_PARAMETER;
190         }
191
192         if (!logon_name) {
193                 DEBUG(0,("PAC no logon_name\n"));
194                 return NT_STATUS_INVALID_PARAMETER;
195         }
196
197         if (!srv_sig_ptr || !srv_sig_blob) {
198                 DEBUG(0,("PAC no srv_key\n"));
199                 return NT_STATUS_INVALID_PARAMETER;
200         }
201
202         if (!kdc_sig_ptr || !kdc_sig_blob) {
203                 DEBUG(0,("PAC no kdc_key\n"));
204                 return NT_STATUS_INVALID_PARAMETER;
205         }
206
207         /* Find and zero out the signatures, as required by the signing algorithm */
208
209         /* We find the data blobs above, now we parse them to get at the exact portion we should zero */
210         ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe,
211                                        NULL, kdc_sig_wipe,
212                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
213         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
214                 status = ndr_map_error2ntstatus(ndr_err);
215                 DEBUG(0,("can't parse the KDC signature: %s\n",
216                         nt_errstr(status)));
217                 return status;
218         }
219
220         ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe,
221                                        NULL, srv_sig_wipe,
222                                        (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA);
223         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
224                 status = ndr_map_error2ntstatus(ndr_err);
225                 DEBUG(0,("can't parse the SRV signature: %s\n",
226                         nt_errstr(status)));
227                 return status;
228         }
229
230         /* Now zero the decoded structure */
231         memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length);
232         memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length);
233
234         /* and reencode, back into the same place it came from */
235         ndr_err = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw,
236                                        NULL, kdc_sig_wipe,
237                                        (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
238         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
239                 status = ndr_map_error2ntstatus(ndr_err);
240                 DEBUG(0,("can't repack the KDC signature: %s\n",
241                         nt_errstr(status)));
242                 return status;
243         }
244         ndr_err = ndr_push_struct_blob(srv_sig_blob, pac_data_raw,
245                                        NULL, srv_sig_wipe,
246                                        (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA);
247         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
248                 status = ndr_map_error2ntstatus(ndr_err);
249                 DEBUG(0,("can't repack the SRV signature: %s\n",
250                         nt_errstr(status)));
251                 return status;
252         }
253
254         /* push out the whole structure, but now with zero'ed signatures */
255         ndr_err = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw,
256                                        NULL, pac_data_raw,
257                                        (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW);
258         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
259                 status = ndr_map_error2ntstatus(ndr_err);
260                 DEBUG(0,("can't repack the RAW PAC: %s\n",
261                         nt_errstr(status)));
262                 return status;
263         }
264
265         /* verify by service_key */
266         ret = check_pac_checksum(mem_ctx,
267                                  modified_pac_blob, srv_sig_ptr,
268                                  context,
269                                  service_keyblock);
270         if (ret) {
271                 DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n",
272                           error_message(ret)));
273                 return NT_STATUS_ACCESS_DENIED;
274         }
275
276         /* Convert to NT time, so as not to loose accuracy in comparison */
277         unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime);
278
279         if (tgs_authtime_nttime != logon_name->logon_time) {
280                 DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n"));
281                 DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time)));
282                 DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime)));
283                 return NT_STATUS_ACCESS_DENIED;
284         }
285
286         ret = smb_krb5_parse_name_norealm(context, logon_name->account_name,
287                                     &client_principal_pac);
288         if (ret) {
289                 DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n",
290                           logon_name->account_name,
291                           error_message(ret)));
292                 return NT_STATUS_INVALID_PARAMETER;
293         }
294
295         if (!smb_krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) {
296                 DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n",
297                           logon_name->account_name));
298                 krb5_free_principal(context, client_principal_pac);
299                 return NT_STATUS_ACCESS_DENIED;
300         }
301
302         DEBUG(3,("Found account name from PAC: %s [%s]\n",
303                  logon_info->info3.base.account_name.string,
304                  logon_info->info3.base.full_name.string));
305
306         DEBUG(10,("Successfully validated Kerberos PAC\n"));
307
308         if (DEBUGLEVEL >= 10) {
309                 const char *s;
310                 s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_DATA, pac_data);
311                 if (s) {
312                         DEBUGADD(10,("%s\n", s));
313                 }
314         }
315
316         *pac_data_out = pac_data;
317
318         return NT_STATUS_OK;
319 }
320
321 /****************************************************************
322 ****************************************************************/
323
324 struct PAC_LOGON_INFO *get_logon_info_from_pac(struct PAC_DATA *pac_data)
325 {
326         int i;
327
328         for (i=0; i < pac_data->num_buffers; i++) {
329
330                 if (pac_data->buffers[i].type != PAC_TYPE_LOGON_INFO) {
331                         continue;
332                 }
333
334                 return pac_data->buffers[i].info->logon_info.info;
335         }
336
337         return NULL;
338 }
339
340 /****************************************************************
341 ****************************************************************/
342
343 NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx,
344                              const char *name,
345                              const char *pass,
346                              time_t time_offset,
347                              time_t *expire_time,
348                              time_t *renew_till_time,
349                              const char *cache_name,
350                              bool request_pac,
351                              bool add_netbios_addr,
352                              time_t renewable_time,
353                              const char *impersonate_princ_s,
354                              struct PAC_DATA **pac_ret)
355 {
356         krb5_error_code ret;
357         NTSTATUS status = NT_STATUS_INVALID_PARAMETER;
358         DATA_BLOB tkt, ap_rep, sesskey1, sesskey2;
359         struct PAC_DATA *pac_data = NULL;
360         char *client_princ_out = NULL;
361         const char *auth_princ = NULL;
362         const char *local_service = NULL;
363         const char *cc = "MEMORY:kerberos_return_pac";
364
365         ZERO_STRUCT(tkt);
366         ZERO_STRUCT(ap_rep);
367         ZERO_STRUCT(sesskey1);
368         ZERO_STRUCT(sesskey2);
369
370         if (!name || !pass) {
371                 return NT_STATUS_INVALID_PARAMETER;
372         }
373
374         if (cache_name) {
375                 cc = cache_name;
376         }
377
378         if (!strchr_m(name, '@')) {
379                 auth_princ = talloc_asprintf(mem_ctx, "%s@%s", name,
380                         lp_realm());
381         } else {
382                 auth_princ = name;
383         }
384         NT_STATUS_HAVE_NO_MEMORY(auth_princ);
385
386         local_service = talloc_asprintf(mem_ctx, "%s$@%s",
387                                         global_myname(), lp_realm());
388         NT_STATUS_HAVE_NO_MEMORY(local_service);
389
390         ret = kerberos_kinit_password_ext(auth_princ,
391                                           pass,
392                                           time_offset,
393                                           expire_time,
394                                           renew_till_time,
395                                           cc,
396                                           request_pac,
397                                           add_netbios_addr,
398                                           renewable_time,
399                                           &status);
400         if (ret) {
401                 DEBUG(1,("kinit failed for '%s' with: %s (%d)\n",
402                         auth_princ, error_message(ret), ret));
403                 /* status already set */
404                 goto out;
405         }
406
407         DEBUG(10,("got TGT for %s in %s\n", auth_princ, cc));
408         if (expire_time) {
409                 DEBUGADD(10,("\tvalid until: %s (%d)\n",
410                         http_timestring(talloc_tos(), *expire_time),
411                         (int)*expire_time));
412         }
413         if (renew_till_time) {
414                 DEBUGADD(10,("\trenewable till: %s (%d)\n",
415                         http_timestring(talloc_tos(), *renew_till_time),
416                         (int)*renew_till_time));
417         }
418
419         /* we cannot continue with krb5 when UF_DONT_REQUIRE_PREAUTH is set,
420          * in that case fallback to NTLM - gd */
421
422         if (expire_time && renew_till_time &&
423             (*expire_time == 0) && (*renew_till_time == 0)) {
424                 return NT_STATUS_INVALID_LOGON_TYPE;
425         }
426
427         ret = cli_krb5_get_ticket(local_service,
428                                   time_offset,
429                                   &tkt,
430                                   &sesskey1,
431                                   0,
432                                   cc,
433                                   NULL,
434                                   impersonate_princ_s);
435         if (ret) {
436                 DEBUG(1,("failed to get ticket for %s: %s\n",
437                         local_service, error_message(ret)));
438                 if (impersonate_princ_s) {
439                         DEBUGADD(1,("tried S4U2SELF impersonation as: %s\n",
440                                 impersonate_princ_s));
441                 }
442                 status = krb5_to_nt_status(ret);
443                 goto out;
444         }
445         status = ads_verify_ticket(mem_ctx,
446                                    lp_realm(),
447                                    time_offset,
448                                    &tkt,
449                                    &client_princ_out,
450                                    &pac_data,
451                                    &ap_rep,
452                                    &sesskey2,
453                                    False);
454         if (!NT_STATUS_IS_OK(status)) {
455                 DEBUG(1,("ads_verify_ticket failed: %s\n",
456                         nt_errstr(status)));
457                 goto out;
458         }
459
460         if (!pac_data) {
461                 DEBUG(1,("no PAC\n"));
462                 status = NT_STATUS_INVALID_PARAMETER;
463                 goto out;
464         }
465
466         *pac_ret = pac_data;
467
468 out:
469         if (cc != cache_name) {
470                 ads_kdestroy(cc);
471         }
472
473         data_blob_free(&tkt);
474         data_blob_free(&ap_rep);
475         data_blob_free(&sesskey1);
476         data_blob_free(&sesskey2);
477
478         TALLOC_FREE(client_princ_out);
479
480         return status;
481 }
482
483 /****************************************************************
484 ****************************************************************/
485
486 static NTSTATUS kerberos_return_pac_logon_info(TALLOC_CTX *mem_ctx,
487                                                const char *name,
488                                                const char *pass,
489                                                time_t time_offset,
490                                                time_t *expire_time,
491                                                time_t *renew_till_time,
492                                                const char *cache_name,
493                                                bool request_pac,
494                                                bool add_netbios_addr,
495                                                time_t renewable_time,
496                                                const char *impersonate_princ_s,
497                                                struct PAC_LOGON_INFO **logon_info)
498 {
499         NTSTATUS status;
500         struct PAC_DATA *pac_data = NULL;
501         struct PAC_LOGON_INFO *info = NULL;
502
503         status = kerberos_return_pac(mem_ctx,
504                                      name,
505                                      pass,
506                                      time_offset,
507                                      expire_time,
508                                      renew_till_time,
509                                      cache_name,
510                                      request_pac,
511                                      add_netbios_addr,
512                                      renewable_time,
513                                      impersonate_princ_s,
514                                      &pac_data);
515         if (!NT_STATUS_IS_OK(status)) {
516                 return status;
517         }
518
519         if (!pac_data) {
520                 DEBUG(3,("no pac\n"));
521                 return NT_STATUS_INVALID_USER_BUFFER;
522         }
523
524         info = get_logon_info_from_pac(pac_data);
525         if (!info) {
526                 DEBUG(1,("no logon_info\n"));
527                 return NT_STATUS_INVALID_USER_BUFFER;
528         }
529
530         *logon_info = info;
531
532         return NT_STATUS_OK;
533 }
534
535 /****************************************************************
536 ****************************************************************/
537
538 NTSTATUS kerberos_return_info3_from_pac(TALLOC_CTX *mem_ctx,
539                                         const char *name,
540                                         const char *pass,
541                                         time_t time_offset,
542                                         time_t *expire_time,
543                                         time_t *renew_till_time,
544                                         const char *cache_name,
545                                         bool request_pac,
546                                         bool add_netbios_addr,
547                                         time_t renewable_time,
548                                         const char *impersonate_princ_s,
549                                         struct netr_SamInfo3 **info3)
550 {
551         NTSTATUS status;
552         struct PAC_LOGON_INFO *logon_info = NULL;
553
554         status = kerberos_return_pac_logon_info(mem_ctx,
555                                                 name,
556                                                 pass,
557                                                 time_offset,
558                                                 expire_time,
559                                                 renew_till_time,
560                                                 cache_name,
561                                                 request_pac,
562                                                 add_netbios_addr,
563                                                 renewable_time,
564                                                 impersonate_princ_s,
565                                                 &logon_info);
566         if (!NT_STATUS_IS_OK(status)) {
567                 return status;
568         }
569
570         *info3 = &logon_info->info3;
571
572         return NT_STATUS_OK;
573 }
574 #endif