Stop using SALT_TYPE_AFS_LENGTH
[krb5.git] / src / lib / krb5 / krb / pac.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/pac.c */
3 /*
4  * Copyright 2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26
27 #include "k5-int.h"
28 #include "authdata.h"
29
30 /* draft-brezak-win2k-krb-authz-00 */
31
32 /*
33  * Add a buffer to the provided PAC and update header.
34  */
35 krb5_error_code
36 k5_pac_add_buffer(krb5_context context,
37                   krb5_pac pac,
38                   krb5_ui_4 type,
39                   const krb5_data *data,
40                   krb5_boolean zerofill,
41                   krb5_data *out_data)
42 {
43     PACTYPE *header;
44     size_t header_len, i, pad = 0;
45     char *pac_data;
46
47     assert((data->data == NULL) == zerofill);
48
49     /* Check there isn't already a buffer of this type */
50     if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
51         return EEXIST;
52     }
53
54     header = (PACTYPE *)realloc(pac->pac,
55                                 sizeof(PACTYPE) +
56                                 (pac->pac->cBuffers * sizeof(PAC_INFO_BUFFER)));
57     if (header == NULL) {
58         return ENOMEM;
59     }
60     pac->pac = header;
61
62     header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
63
64     if (data->length % PAC_ALIGNMENT)
65         pad = PAC_ALIGNMENT - (data->length % PAC_ALIGNMENT);
66
67     pac_data = realloc(pac->data.data,
68                        pac->data.length + PAC_INFO_BUFFER_LENGTH + data->length + pad);
69     if (pac_data == NULL) {
70         return ENOMEM;
71     }
72     pac->data.data = pac_data;
73
74     /* Update offsets of existing buffers */
75     for (i = 0; i < pac->pac->cBuffers; i++)
76         pac->pac->Buffers[i].Offset += PAC_INFO_BUFFER_LENGTH;
77
78     /* Make room for new PAC_INFO_BUFFER */
79     memmove(pac->data.data + header_len + PAC_INFO_BUFFER_LENGTH,
80             pac->data.data + header_len,
81             pac->data.length - header_len);
82     memset(pac->data.data + header_len, 0, PAC_INFO_BUFFER_LENGTH);
83
84     /* Initialise new PAC_INFO_BUFFER */
85     pac->pac->Buffers[i].ulType = type;
86     pac->pac->Buffers[i].cbBufferSize = data->length;
87     pac->pac->Buffers[i].Offset = pac->data.length + PAC_INFO_BUFFER_LENGTH;
88     assert((pac->pac->Buffers[i].Offset % PAC_ALIGNMENT) == 0);
89
90     /* Copy in new PAC data and zero padding bytes */
91     if (zerofill)
92         memset(pac->data.data + pac->pac->Buffers[i].Offset, 0, data->length);
93     else
94         memcpy(pac->data.data + pac->pac->Buffers[i].Offset, data->data, data->length);
95
96     memset(pac->data.data + pac->pac->Buffers[i].Offset + data->length, 0, pad);
97
98     pac->pac->cBuffers++;
99     pac->data.length += PAC_INFO_BUFFER_LENGTH + data->length + pad;
100
101     if (out_data != NULL) {
102         out_data->data = pac->data.data + pac->pac->Buffers[i].Offset;
103         out_data->length = data->length;
104     }
105
106     pac->verified = FALSE;
107
108     return 0;
109 }
110
111 krb5_error_code KRB5_CALLCONV
112 krb5_pac_add_buffer(krb5_context context,
113                     krb5_pac pac,
114                     krb5_ui_4 type,
115                     const krb5_data *data)
116 {
117     return k5_pac_add_buffer(context, pac, type, data, FALSE, NULL);
118 }
119
120 /*
121  * Free a PAC
122  */
123 void KRB5_CALLCONV
124 krb5_pac_free(krb5_context context,
125               krb5_pac pac)
126 {
127     if (pac != NULL) {
128         if (pac->data.data != NULL) {
129             memset(pac->data.data, 0, pac->data.length);
130             free(pac->data.data);
131         }
132         if (pac->pac != NULL)
133             free(pac->pac);
134         memset(pac, 0, sizeof(*pac));
135         free(pac);
136     }
137 }
138
139 krb5_error_code
140 k5_pac_locate_buffer(krb5_context context,
141                      const krb5_pac pac,
142                      krb5_ui_4 type,
143                      krb5_data *data)
144 {
145     PAC_INFO_BUFFER *buffer = NULL;
146     size_t i;
147
148     if (pac == NULL)
149         return EINVAL;
150
151     for (i = 0; i < pac->pac->cBuffers; i++) {
152         if (pac->pac->Buffers[i].ulType == type) {
153             if (buffer == NULL)
154                 buffer = &pac->pac->Buffers[i];
155             else
156                 return EINVAL;
157         }
158     }
159
160     if (buffer == NULL)
161         return ENOENT;
162
163     assert(buffer->Offset + buffer->cbBufferSize <= pac->data.length);
164
165     if (data != NULL) {
166         data->length = buffer->cbBufferSize;
167         data->data = pac->data.data + buffer->Offset;
168     }
169
170     return 0;
171 }
172
173 /*
174  * Find a buffer and copy data into output
175  */
176 krb5_error_code KRB5_CALLCONV
177 krb5_pac_get_buffer(krb5_context context,
178                     krb5_pac pac,
179                     krb5_ui_4 type,
180                     krb5_data *data)
181 {
182     krb5_data d;
183     krb5_error_code ret;
184
185     ret = k5_pac_locate_buffer(context, pac, type, &d);
186     if (ret != 0)
187         return ret;
188
189     data->data = malloc(d.length);
190     if (data->data == NULL)
191         return ENOMEM;
192
193     data->length = d.length;
194     memcpy(data->data, d.data, d.length);
195
196     return 0;
197 }
198
199 /*
200  * Return an array of the types of data in the PAC
201  */
202 krb5_error_code KRB5_CALLCONV
203 krb5_pac_get_types(krb5_context context,
204                    krb5_pac pac,
205                    size_t *len,
206                    krb5_ui_4 **types)
207 {
208     size_t i;
209
210     *types = (krb5_ui_4 *)malloc(pac->pac->cBuffers * sizeof(krb5_ui_4));
211     if (*types == NULL)
212         return ENOMEM;
213
214     *len = pac->pac->cBuffers;
215
216     for (i = 0; i < pac->pac->cBuffers; i++)
217         (*types)[i] = pac->pac->Buffers[i].ulType;
218
219     return 0;
220 }
221
222 /*
223  * Initialize PAC
224  */
225 krb5_error_code KRB5_CALLCONV
226 krb5_pac_init(krb5_context context,
227               krb5_pac *ppac)
228 {
229     krb5_pac pac;
230
231     pac = (krb5_pac)malloc(sizeof(*pac));
232     if (pac == NULL)
233         return ENOMEM;
234
235     pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
236     if (pac->pac == NULL) {
237         free(pac);
238         return ENOMEM;
239     }
240
241     pac->pac->cBuffers = 0;
242     pac->pac->Version = 0;
243
244     pac->data.length = PACTYPE_LENGTH;
245     pac->data.data = calloc(1, pac->data.length);
246     if (pac->data.data == NULL) {
247         krb5_pac_free(context, pac);
248         return ENOMEM;
249     }
250
251     pac->verified = FALSE;
252
253     *ppac = pac;
254
255     return 0;
256 }
257
258 static krb5_error_code
259 k5_pac_copy(krb5_context context,
260             krb5_pac src,
261             krb5_pac *dst)
262 {
263     size_t header_len;
264     krb5_ui_4 cbuffers;
265     krb5_error_code code;
266     krb5_pac pac;
267
268     cbuffers = src->pac->cBuffers;
269     if (cbuffers != 0)
270         cbuffers--;
271
272     header_len = sizeof(PACTYPE) + cbuffers * sizeof(PAC_INFO_BUFFER);
273
274     pac = (krb5_pac)malloc(sizeof(*pac));
275     if (pac == NULL)
276         return ENOMEM;
277
278     pac->pac = (PACTYPE *)malloc(header_len);
279     if (pac->pac == NULL) {
280         free(pac);
281         return ENOMEM;
282     }
283
284     memcpy(pac->pac, src->pac, header_len);
285
286     code = krb5int_copy_data_contents(context, &src->data, &pac->data);
287     if (code != 0) {
288         free(pac->pac);
289         free(pac);
290         return ENOMEM;
291     }
292
293     pac->verified = src->verified;
294     *dst = pac;
295
296     return 0;
297 }
298
299 /*
300  * Parse the supplied data into the PAC allocated by this function
301  */
302 krb5_error_code KRB5_CALLCONV
303 krb5_pac_parse(krb5_context context,
304                const void *ptr,
305                size_t len,
306                krb5_pac *ppac)
307 {
308     krb5_error_code ret;
309     size_t i;
310     const unsigned char *p = (const unsigned char *)ptr;
311     krb5_pac pac;
312     size_t header_len;
313     krb5_ui_4 cbuffers, version;
314
315     *ppac = NULL;
316
317     if (len < PACTYPE_LENGTH)
318         return ERANGE;
319
320     cbuffers = load_32_le(p);
321     p += 4;
322     version = load_32_le(p);
323     p += 4;
324
325     if (version != 0)
326         return EINVAL;
327
328     header_len = PACTYPE_LENGTH + (cbuffers * PAC_INFO_BUFFER_LENGTH);
329     if (len < header_len)
330         return ERANGE;
331
332     ret = krb5_pac_init(context, &pac);
333     if (ret != 0)
334         return ret;
335
336     pac->pac = (PACTYPE *)realloc(pac->pac,
337                                   sizeof(PACTYPE) + ((cbuffers - 1) * sizeof(PAC_INFO_BUFFER)));
338     if (pac->pac == NULL) {
339         krb5_pac_free(context, pac);
340         return ENOMEM;
341     }
342
343     pac->pac->cBuffers = cbuffers;
344     pac->pac->Version = version;
345
346     for (i = 0; i < pac->pac->cBuffers; i++) {
347         PAC_INFO_BUFFER *buffer = &pac->pac->Buffers[i];
348
349         buffer->ulType = load_32_le(p);
350         p += 4;
351         buffer->cbBufferSize = load_32_le(p);
352         p += 4;
353         buffer->Offset = load_64_le(p);
354         p += 8;
355
356         if (buffer->Offset % PAC_ALIGNMENT) {
357             krb5_pac_free(context, pac);
358             return EINVAL;
359         }
360         if (buffer->Offset < header_len ||
361             buffer->Offset + buffer->cbBufferSize > len) {
362             krb5_pac_free(context, pac);
363             return ERANGE;
364         }
365     }
366
367     pac->data.data = realloc(pac->data.data, len);
368     if (pac->data.data == NULL) {
369         krb5_pac_free(context, pac);
370         return ENOMEM;
371     }
372     memcpy(pac->data.data, ptr, len);
373
374     pac->data.length = len;
375
376     *ppac = pac;
377
378     return 0;
379 }
380
381 static krb5_error_code
382 k5_time_to_seconds_since_1970(krb5_int64 ntTime,
383                               krb5_timestamp *elapsedSeconds)
384 {
385     krb5_ui_8 abstime;
386
387     ntTime /= 10000000;
388
389     abstime = ntTime > 0 ? ntTime - NT_TIME_EPOCH : -ntTime;
390
391     if (abstime > KRB5_INT32_MAX)
392         return ERANGE;
393
394     *elapsedSeconds = abstime;
395
396     return 0;
397 }
398
399 krb5_error_code
400 k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds,
401                               krb5_ui_8 *ntTime)
402 {
403     *ntTime = elapsedSeconds;
404
405     if (elapsedSeconds > 0)
406         *ntTime += NT_TIME_EPOCH;
407
408     *ntTime *= 10000000;
409
410     return 0;
411 }
412
413 krb5_error_code
414 k5_pac_validate_client(krb5_context context,
415                        const krb5_pac pac,
416                        krb5_timestamp authtime,
417                        krb5_const_principal principal)
418 {
419     krb5_error_code ret;
420     krb5_data client_info;
421     char *pac_princname;
422     unsigned char *p;
423     krb5_timestamp pac_authtime;
424     krb5_ui_2 pac_princname_length;
425     krb5_int64 pac_nt_authtime;
426     krb5_principal pac_principal;
427
428     ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_CLIENT_INFO,
429                                &client_info);
430     if (ret != 0)
431         return ret;
432
433     if (client_info.length < PAC_CLIENT_INFO_LENGTH)
434         return ERANGE;
435
436     p = (unsigned char *)client_info.data;
437     pac_nt_authtime = load_64_le(p);
438     p += 8;
439     pac_princname_length = load_16_le(p);
440     p += 2;
441
442     ret = k5_time_to_seconds_since_1970(pac_nt_authtime, &pac_authtime);
443     if (ret != 0)
444         return ret;
445
446     if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
447         pac_princname_length % 2)
448         return ERANGE;
449
450     ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2,
451                                     &pac_princname, NULL);
452     if (ret != 0)
453         return ret;
454
455     ret = krb5_parse_name_flags(context, pac_princname,
456                                 KRB5_PRINCIPAL_PARSE_NO_REALM, &pac_principal);
457     if (ret != 0) {
458         free(pac_princname);
459         return ret;
460     }
461
462     free(pac_princname);
463
464     if (pac_authtime != authtime ||
465         !krb5_principal_compare_flags(context,
466                                       pac_principal,
467                                       principal,
468                                       KRB5_PRINCIPAL_COMPARE_IGNORE_REALM))
469         ret = KRB5KRB_AP_WRONG_PRINC;
470
471     krb5_free_principal(context, pac_principal);
472
473     return ret;
474 }
475
476 static krb5_error_code
477 k5_pac_zero_signature(krb5_context context,
478                       const krb5_pac pac,
479                       krb5_ui_4 type,
480                       krb5_data *data)
481 {
482     PAC_INFO_BUFFER *buffer = NULL;
483     size_t i;
484
485     assert(type == KRB5_PAC_SERVER_CHECKSUM ||
486            type == KRB5_PAC_PRIVSVR_CHECKSUM);
487     assert(data->length >= pac->data.length);
488
489     for (i = 0; i < pac->pac->cBuffers; i++) {
490         if (pac->pac->Buffers[i].ulType == type) {
491             buffer = &pac->pac->Buffers[i];
492             break;
493         }
494     }
495
496     if (buffer == NULL)
497         return ENOENT;
498
499     if (buffer->Offset + buffer->cbBufferSize > pac->data.length)
500         return ERANGE;
501
502     if (buffer->cbBufferSize < PAC_SIGNATURE_DATA_LENGTH)
503         return KRB5_BAD_MSIZE;
504
505     /* Zero out the data portion of the checksum only */
506     memset(data->data + buffer->Offset + PAC_SIGNATURE_DATA_LENGTH,
507            0,
508            buffer->cbBufferSize - PAC_SIGNATURE_DATA_LENGTH);
509
510     return 0;
511 }
512
513 static krb5_error_code
514 k5_pac_verify_server_checksum(krb5_context context,
515                               const krb5_pac pac,
516                               const krb5_keyblock *server)
517 {
518     krb5_error_code ret;
519     krb5_data pac_data; /* PAC with zeroed checksums */
520     krb5_checksum checksum;
521     krb5_data checksum_data;
522     krb5_boolean valid;
523     krb5_octet *p;
524
525     ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
526                                &checksum_data);
527     if (ret != 0)
528         return ret;
529
530     if (checksum_data.length < PAC_SIGNATURE_DATA_LENGTH)
531         return KRB5_BAD_MSIZE;
532
533     p = (krb5_octet *)checksum_data.data;
534     checksum.checksum_type = load_32_le(p);
535     checksum.length = checksum_data.length - PAC_SIGNATURE_DATA_LENGTH;
536     checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
537     if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
538         return KRB5KRB_AP_ERR_INAPP_CKSUM;
539
540     pac_data.length = pac->data.length;
541     pac_data.data = malloc(pac->data.length);
542     if (pac_data.data == NULL)
543         return ENOMEM;
544
545     memcpy(pac_data.data, pac->data.data, pac->data.length);
546
547     /* Zero out both checksum buffers */
548     ret = k5_pac_zero_signature(context, pac, KRB5_PAC_SERVER_CHECKSUM,
549                                 &pac_data);
550     if (ret != 0) {
551         free(pac_data.data);
552         return ret;
553     }
554
555     ret = k5_pac_zero_signature(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
556                                 &pac_data);
557     if (ret != 0) {
558         free(pac_data.data);
559         return ret;
560     }
561
562     ret = krb5_c_verify_checksum(context, server,
563                                  KRB5_KEYUSAGE_APP_DATA_CKSUM,
564                                  &pac_data, &checksum, &valid);
565
566     free(pac_data.data);
567
568     if (ret != 0) {
569         return ret;
570     }
571
572     if (valid == FALSE)
573         ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
574
575     return ret;
576 }
577
578 static krb5_error_code
579 k5_pac_verify_kdc_checksum(krb5_context context,
580                            const krb5_pac pac,
581                            const krb5_keyblock *privsvr)
582 {
583     krb5_error_code ret;
584     krb5_data server_checksum, privsvr_checksum;
585     krb5_checksum checksum;
586     krb5_boolean valid;
587     krb5_octet *p;
588
589     ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_PRIVSVR_CHECKSUM,
590                                &privsvr_checksum);
591     if (ret != 0)
592         return ret;
593
594     if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
595         return KRB5_BAD_MSIZE;
596
597     ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_SERVER_CHECKSUM,
598                                &server_checksum);
599     if (ret != 0)
600         return ret;
601
602     if (server_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
603         return KRB5_BAD_MSIZE;
604
605     p = (krb5_octet *)privsvr_checksum.data;
606     checksum.checksum_type = load_32_le(p);
607     checksum.length = privsvr_checksum.length - PAC_SIGNATURE_DATA_LENGTH;
608     checksum.contents = p + PAC_SIGNATURE_DATA_LENGTH;
609     if (!krb5_c_is_keyed_cksum(checksum.checksum_type))
610         return KRB5KRB_AP_ERR_INAPP_CKSUM;
611
612     server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
613     server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
614
615     ret = krb5_c_verify_checksum(context, privsvr,
616                                  KRB5_KEYUSAGE_APP_DATA_CKSUM,
617                                  &server_checksum, &checksum, &valid);
618     if (ret != 0)
619         return ret;
620
621     if (valid == FALSE)
622         ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
623
624     return ret;
625 }
626
627 krb5_error_code KRB5_CALLCONV
628 krb5_pac_verify(krb5_context context,
629                 const krb5_pac pac,
630                 krb5_timestamp authtime,
631                 krb5_const_principal principal,
632                 const krb5_keyblock *server,
633                 const krb5_keyblock *privsvr)
634 {
635     krb5_error_code ret;
636
637     if (server != NULL) {
638         ret = k5_pac_verify_server_checksum(context, pac, server);
639         if (ret != 0)
640             return ret;
641     }
642
643     if (privsvr != NULL) {
644         ret = k5_pac_verify_kdc_checksum(context, pac, privsvr);
645         if (ret != 0)
646             return ret;
647     }
648
649     if (principal != NULL) {
650         ret = k5_pac_validate_client(context, pac, authtime, principal);
651         if (ret != 0)
652             return ret;
653     }
654
655     pac->verified = TRUE;
656
657     return 0;
658 }
659
660 /*
661  * PAC auth data attribute backend
662  */
663 struct mspac_context {
664     krb5_pac pac;
665 };
666
667 static krb5_error_code
668 mspac_init(krb5_context kcontext, void **plugin_context)
669 {
670     *plugin_context = NULL;
671     return 0;
672 }
673
674 static void
675 mspac_flags(krb5_context kcontext,
676             void *plugin_context,
677             krb5_authdatatype ad_type,
678             krb5_flags *flags)
679 {
680     *flags = AD_USAGE_TGS_REQ;
681 }
682
683 static void
684 mspac_fini(krb5_context kcontext, void *plugin_context)
685 {
686     return;
687 }
688
689 static krb5_error_code
690 mspac_request_init(krb5_context kcontext,
691                    krb5_authdata_context context,
692                    void *plugin_context,
693                    void **request_context)
694 {
695     struct mspac_context *pacctx;
696
697     pacctx = (struct mspac_context *)malloc(sizeof(*pacctx));
698     if (pacctx == NULL)
699         return ENOMEM;
700
701     pacctx->pac = NULL;
702
703     *request_context = pacctx;
704
705     return 0;
706 }
707
708 static krb5_error_code
709 mspac_import_authdata(krb5_context kcontext,
710                       krb5_authdata_context context,
711                       void *plugin_context,
712                       void *request_context,
713                       krb5_authdata **authdata,
714                       krb5_boolean kdc_issued,
715                       krb5_const_principal kdc_issuer)
716 {
717     krb5_error_code code;
718     struct mspac_context *pacctx = (struct mspac_context *)request_context;
719
720     if (kdc_issued)
721         return EINVAL;
722
723     if (pacctx->pac != NULL) {
724         krb5_pac_free(kcontext, pacctx->pac);
725         pacctx->pac = NULL;
726     }
727
728     assert(authdata[0] != NULL);
729     assert((authdata[0]->ad_type & AD_TYPE_FIELD_TYPE_MASK) ==
730            KRB5_AUTHDATA_WIN2K_PAC);
731
732     code = krb5_pac_parse(kcontext, authdata[0]->contents,
733                           authdata[0]->length, &pacctx->pac);
734
735     return code;
736 }
737
738 static krb5_error_code
739 mspac_export_authdata(krb5_context kcontext,
740                       krb5_authdata_context context,
741                       void *plugin_context,
742                       void *request_context,
743                       krb5_flags usage,
744                       krb5_authdata ***out_authdata)
745 {
746     struct mspac_context *pacctx = (struct mspac_context *)request_context;
747     krb5_error_code code;
748     krb5_authdata **authdata;
749     krb5_data data;
750
751     if (pacctx->pac == NULL)
752         return 0;
753
754     authdata = calloc(2, sizeof(krb5_authdata *));
755     if (authdata == NULL)
756         return ENOMEM;
757
758     authdata[0] = calloc(1, sizeof(krb5_authdata));
759     if (authdata[0] == NULL) {
760         free(authdata);
761         return ENOMEM;
762     }
763     authdata[1] = NULL;
764
765     code = krb5int_copy_data_contents(kcontext, &pacctx->pac->data, &data);
766     if (code != 0) {
767         krb5_free_authdata(kcontext, authdata);
768         return code;
769     }
770
771     authdata[0]->magic = KV5M_AUTHDATA;
772     authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
773     authdata[0]->length = data.length;
774     authdata[0]->contents = (krb5_octet *)data.data;
775
776     authdata[1] = NULL;
777
778     *out_authdata = authdata;
779
780     return 0;
781 }
782
783 static krb5_error_code
784 mspac_verify(krb5_context kcontext,
785              krb5_authdata_context context,
786              void *plugin_context,
787              void *request_context,
788              const krb5_auth_context *auth_context,
789              const krb5_keyblock *key,
790              const krb5_ap_req *req)
791 {
792     krb5_error_code code;
793     struct mspac_context *pacctx = (struct mspac_context *)request_context;
794
795     if (pacctx->pac == NULL)
796         return EINVAL;
797
798     code = krb5_pac_verify(kcontext, pacctx->pac,
799                            req->ticket->enc_part2->times.authtime,
800                            req->ticket->enc_part2->client, key, NULL);
801     if (code != 0)
802         TRACE_MSPAC_VERIFY_FAIL(kcontext, code);
803
804     /*
805      * If the above verification failed, don't fail the whole authentication,
806      * just don't mark the PAC as verified.  A checksum mismatch can occur if
807      * the PAC was copied from a cross-realm TGT by an ignorant KDC, and Apple
808      * Mac OS X Server Open Directory (as of 10.6) generates PACs with no
809      * server checksum at all.
810      */
811     return 0;
812 }
813
814 static void
815 mspac_request_fini(krb5_context kcontext,
816                    krb5_authdata_context context,
817                    void *plugin_context,
818                    void *request_context)
819 {
820     struct mspac_context *pacctx = (struct mspac_context *)request_context;
821
822     if (pacctx != NULL) {
823         if (pacctx->pac != NULL)
824             krb5_pac_free(kcontext, pacctx->pac);
825
826         free(pacctx);
827     }
828 }
829
830 #define STRLENOF(x) (sizeof((x)) - 1)
831
832 static struct {
833     krb5_ui_4 type;
834     krb5_data attribute;
835 } mspac_attribute_types[] = {
836     { (krb5_ui_4)-1,            { KV5M_DATA, STRLENOF("urn:mspac:"),
837                                   "urn:mspac:" } },
838     { KRB5_PAC_LOGON_INFO,       { KV5M_DATA,
839                                    STRLENOF("urn:mspac:logon-info"),
840                                    "urn:mspac:logon-info" } },
841     { KRB5_PAC_CREDENTIALS_INFO, { KV5M_DATA,
842                                    STRLENOF("urn:mspac:credentials-info"),
843                                    "urn:mspac:credentials-info" } },
844     { KRB5_PAC_SERVER_CHECKSUM,  { KV5M_DATA,
845                                    STRLENOF("urn:mspac:server-checksum"),
846                                    "urn:mspac:server-checksum" } },
847     { KRB5_PAC_PRIVSVR_CHECKSUM, { KV5M_DATA,
848                                    STRLENOF("urn:mspac:privsvr-checksum"),
849                                    "urn:mspac:privsvr-checksum" } },
850     { KRB5_PAC_CLIENT_INFO,      { KV5M_DATA,
851                                    STRLENOF("urn:mspac:client-info"),
852                                    "urn:mspac:client-info" } },
853     { KRB5_PAC_DELEGATION_INFO,  { KV5M_DATA,
854                                    STRLENOF("urn:mspac:delegation-info"),
855                                    "urn:mspac:delegation-info" } },
856     { KRB5_PAC_UPN_DNS_INFO,     { KV5M_DATA,
857                                    STRLENOF("urn:mspac:upn-dns-info"),
858                                    "urn:mspac:upn-dns-info" } },
859 };
860
861 #define MSPAC_ATTRIBUTE_COUNT   (sizeof(mspac_attribute_types)/sizeof(mspac_attribute_types[0]))
862
863 static krb5_error_code
864 mspac_type2attr(krb5_ui_4 type, krb5_data *attr)
865 {
866     unsigned int i;
867
868     for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
869         if (mspac_attribute_types[i].type == type) {
870             *attr = mspac_attribute_types[i].attribute;
871             return 0;
872         }
873     }
874
875     return ENOENT;
876 }
877
878 static krb5_error_code
879 mspac_attr2type(const krb5_data *attr, krb5_ui_4 *type)
880 {
881     unsigned int i;
882
883     for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
884         if (attr->length == mspac_attribute_types[i].attribute.length &&
885             strncasecmp(attr->data, mspac_attribute_types[i].attribute.data, attr->length) == 0) {
886             *type = mspac_attribute_types[i].type;
887             return 0;
888         }
889     }
890
891     if (attr->length > STRLENOF("urn:mspac:") &&
892         strncasecmp(attr->data, "urn:mspac:", STRLENOF("urn:mspac:")) == 0)
893     {
894         char *p = &attr->data[STRLENOF("urn:mspac:")];
895         char *endptr;
896
897         *type = strtoul(p, &endptr, 10);
898         if (*type != 0 && *endptr == '\0')
899             return 0;
900     }
901
902     return ENOENT;
903 }
904
905 static krb5_error_code
906 mspac_get_attribute_types(krb5_context kcontext,
907                           krb5_authdata_context context,
908                           void *plugin_context,
909                           void *request_context,
910                           krb5_data **out_attrs)
911 {
912     struct mspac_context *pacctx = (struct mspac_context *)request_context;
913     unsigned int i, j;
914     krb5_data *attrs;
915     krb5_error_code code;
916
917     if (pacctx->pac == NULL)
918         return ENOENT;
919
920     attrs = calloc(1 + pacctx->pac->pac->cBuffers + 1, sizeof(krb5_data));
921     if (attrs == NULL)
922         return ENOMEM;
923
924     j = 0;
925
926     /* The entire PAC */
927     code = krb5int_copy_data_contents(kcontext,
928                                       &mspac_attribute_types[0].attribute,
929                                       &attrs[j++]);
930     if (code != 0) {
931         free(attrs);
932         return code;
933     }
934
935     /* PAC buffers */
936     for (i = 0; i < pacctx->pac->pac->cBuffers; i++) {
937         krb5_data attr;
938
939         code = mspac_type2attr(pacctx->pac->pac->Buffers[i].ulType, &attr);
940         if (code == 0) {
941             code = krb5int_copy_data_contents(kcontext, &attr, &attrs[j++]);
942             if (code != 0) {
943                 krb5int_free_data_list(kcontext, attrs);
944                 return code;
945             }
946         } else {
947             int length;
948
949             length = asprintf(&attrs[j].data, "urn:mspac:%d",
950                               pacctx->pac->pac->Buffers[i].ulType);
951             if (length < 0) {
952                 krb5int_free_data_list(kcontext, attrs);
953                 return ENOMEM;
954             }
955             attrs[j++].length = length;
956         }
957     }
958     attrs[j].data = NULL;
959     attrs[j].length = 0;
960
961     *out_attrs = attrs;
962
963     return 0;
964 }
965
966 static krb5_error_code
967 mspac_get_attribute(krb5_context kcontext,
968                     krb5_authdata_context context,
969                     void *plugin_context,
970                     void *request_context,
971                     const krb5_data *attribute,
972                     krb5_boolean *authenticated,
973                     krb5_boolean *complete,
974                     krb5_data *value,
975                     krb5_data *display_value,
976                     int *more)
977 {
978     struct mspac_context *pacctx = (struct mspac_context *)request_context;
979     krb5_error_code code;
980     krb5_ui_4 type;
981
982     if (display_value != NULL) {
983         display_value->data = NULL;
984         display_value->length = 0;
985     }
986
987     if (*more != -1 || pacctx->pac == NULL)
988         return ENOENT;
989
990     /* If it didn't verify, pretend it didn't exist. */
991     if (!pacctx->pac->verified) {
992         TRACE_MSPAC_DISCARD_UNVERF(kcontext);
993         return ENOENT;
994     }
995
996     code = mspac_attr2type(attribute, &type);
997     if (code != 0)
998         return code;
999
1000     /* -1 is a magic type that refers to the entire PAC */
1001     if (type == (krb5_ui_4)-1) {
1002         if (value != NULL)
1003             code = krb5int_copy_data_contents(kcontext,
1004                                               &pacctx->pac->data,
1005                                               value);
1006         else
1007             code = 0;
1008     } else {
1009         if (value != NULL)
1010             code = krb5_pac_get_buffer(kcontext, pacctx->pac, type, value);
1011         else
1012             code = k5_pac_locate_buffer(kcontext, pacctx->pac, type, NULL);
1013     }
1014     if (code == 0) {
1015         *authenticated = pacctx->pac->verified;
1016         *complete = TRUE;
1017     }
1018
1019     *more = 0;
1020
1021     return code;
1022 }
1023
1024 static krb5_error_code
1025 mspac_set_attribute(krb5_context kcontext,
1026                     krb5_authdata_context context,
1027                     void *plugin_context,
1028                     void *request_context,
1029                     krb5_boolean complete,
1030                     const krb5_data *attribute,
1031                     const krb5_data *value)
1032 {
1033     struct mspac_context *pacctx = (struct mspac_context *)request_context;
1034     krb5_error_code code;
1035     krb5_ui_4 type;
1036
1037     if (pacctx->pac == NULL)
1038         return ENOENT;
1039
1040     code = mspac_attr2type(attribute, &type);
1041     if (code != 0)
1042         return code;
1043
1044     /* -1 is a magic type that refers to the entire PAC */
1045     if (type == (krb5_ui_4)-1) {
1046         krb5_pac newpac;
1047
1048         code = krb5_pac_parse(kcontext, value->data, value->length, &newpac);
1049         if (code != 0)
1050             return code;
1051
1052         krb5_pac_free(kcontext, pacctx->pac);
1053         pacctx->pac = newpac;
1054     } else {
1055         code = krb5_pac_add_buffer(kcontext, pacctx->pac, type, value);
1056     }
1057
1058     return code;
1059 }
1060
1061 static krb5_error_code
1062 mspac_export_internal(krb5_context kcontext,
1063                       krb5_authdata_context context,
1064                       void *plugin_context,
1065                       void *request_context,
1066                       krb5_boolean restrict_authenticated,
1067                       void **ptr)
1068 {
1069     struct mspac_context *pacctx = (struct mspac_context *)request_context;
1070     krb5_error_code code;
1071     krb5_pac pac;
1072
1073     *ptr = NULL;
1074
1075     if (pacctx->pac == NULL)
1076         return ENOENT;
1077
1078     if (restrict_authenticated && (pacctx->pac->verified) == FALSE)
1079         return ENOENT;
1080
1081     code = krb5_pac_parse(kcontext, pacctx->pac->data.data,
1082                           pacctx->pac->data.length, &pac);
1083     if (code == 0) {
1084         pac->verified = pacctx->pac->verified;
1085         *ptr = pac;
1086     }
1087
1088     return code;
1089 }
1090
1091 static void
1092 mspac_free_internal(krb5_context kcontext,
1093                     krb5_authdata_context context,
1094                     void *plugin_context,
1095                     void *request_context,
1096                     void *ptr)
1097 {
1098     if (ptr != NULL)
1099         krb5_pac_free(kcontext, (krb5_pac)ptr);
1100
1101     return;
1102 }
1103
1104 static krb5_error_code
1105 mspac_size(krb5_context kcontext,
1106            krb5_authdata_context context,
1107            void *plugin_context,
1108            void *request_context,
1109            size_t *sizep)
1110 {
1111     struct mspac_context *pacctx = (struct mspac_context *)request_context;
1112
1113     *sizep += sizeof(krb5_int32);
1114
1115     if (pacctx->pac != NULL)
1116         *sizep += pacctx->pac->data.length;
1117
1118     *sizep += sizeof(krb5_int32);
1119
1120     return 0;
1121 }
1122
1123 static krb5_error_code
1124 mspac_externalize(krb5_context kcontext,
1125                   krb5_authdata_context context,
1126                   void *plugin_context,
1127                   void *request_context,
1128                   krb5_octet **buffer,
1129                   size_t *lenremain)
1130 {
1131     krb5_error_code code = 0;
1132     struct mspac_context *pacctx = (struct mspac_context *)request_context;
1133     size_t required = 0;
1134     krb5_octet *bp;
1135     size_t remain;
1136
1137     bp = *buffer;
1138     remain = *lenremain;
1139
1140     if (pacctx->pac != NULL) {
1141         mspac_size(kcontext, context, plugin_context,
1142                    request_context, &required);
1143
1144         if (required <= remain) {
1145             krb5_ser_pack_int32((krb5_int32)pacctx->pac->data.length,
1146                                 &bp, &remain);
1147             krb5_ser_pack_bytes((krb5_octet *)pacctx->pac->data.data,
1148                                 (size_t)pacctx->pac->data.length,
1149                                 &bp, &remain);
1150             krb5_ser_pack_int32((krb5_int32)pacctx->pac->verified,
1151                                 &bp, &remain);
1152         } else {
1153             code = ENOMEM;
1154         }
1155     } else {
1156         krb5_ser_pack_int32(0, &bp, &remain); /* length */
1157         krb5_ser_pack_int32(0, &bp, &remain); /* verified */
1158     }
1159
1160     *buffer = bp;
1161     *lenremain = remain;
1162
1163     return code;
1164 }
1165
1166 static krb5_error_code
1167 mspac_internalize(krb5_context kcontext,
1168                   krb5_authdata_context context,
1169                   void *plugin_context,
1170                   void *request_context,
1171                   krb5_octet **buffer,
1172                   size_t *lenremain)
1173 {
1174     struct mspac_context *pacctx = (struct mspac_context *)request_context;
1175     krb5_error_code code;
1176     krb5_int32 ibuf;
1177     krb5_octet *bp;
1178     size_t remain;
1179     krb5_pac pac = NULL;
1180
1181     bp = *buffer;
1182     remain = *lenremain;
1183
1184     /* length */
1185     code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
1186     if (code != 0)
1187         return code;
1188
1189     if (ibuf != 0) {
1190         code = krb5_pac_parse(kcontext, bp, ibuf, &pac);
1191         if (code != 0)
1192             return code;
1193
1194         bp += ibuf;
1195         remain -= ibuf;
1196     }
1197
1198     /* verified */
1199     code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
1200     if (code != 0) {
1201         krb5_pac_free(kcontext, pac);
1202         return code;
1203     }
1204
1205     if (pac != NULL) {
1206         pac->verified = (ibuf != 0);
1207     }
1208
1209     if (pacctx->pac != NULL) {
1210         krb5_pac_free(kcontext, pacctx->pac);
1211     }
1212
1213     pacctx->pac = pac;
1214
1215     *buffer = bp;
1216     *lenremain = remain;
1217
1218     return 0;
1219 }
1220
1221 static krb5_error_code
1222 mspac_copy(krb5_context kcontext,
1223            krb5_authdata_context context,
1224            void *plugin_context,
1225            void *request_context,
1226            void *dst_plugin_context,
1227            void *dst_request_context)
1228 {
1229     struct mspac_context *srcctx = (struct mspac_context *)request_context;
1230     struct mspac_context *dstctx = (struct mspac_context *)dst_request_context;
1231     krb5_error_code code = 0;
1232
1233     assert(dstctx != NULL);
1234     assert(dstctx->pac == NULL);
1235
1236     if (srcctx->pac != NULL)
1237         code = k5_pac_copy(kcontext, srcctx->pac, &dstctx->pac);
1238
1239     return code;
1240 }
1241
1242 static krb5_authdatatype mspac_ad_types[] = { KRB5_AUTHDATA_WIN2K_PAC, 0 };
1243
1244 krb5plugin_authdata_client_ftable_v0 krb5int_mspac_authdata_client_ftable = {
1245     "mspac",
1246     mspac_ad_types,
1247     mspac_init,
1248     mspac_fini,
1249     mspac_flags,
1250     mspac_request_init,
1251     mspac_request_fini,
1252     mspac_get_attribute_types,
1253     mspac_get_attribute,
1254     mspac_set_attribute,
1255     NULL, /* delete_attribute_proc */
1256     mspac_export_authdata,
1257     mspac_import_authdata,
1258     mspac_export_internal,
1259     mspac_free_internal,
1260     mspac_verify,
1261     mspac_size,
1262     mspac_externalize,
1263     mspac_internalize,
1264     mspac_copy
1265 };