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