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