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