2
* Copyright (c) 2011, JANET(UK)
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
12
* 2. Redistributions in binary form must reproduce the above copyright
13
* notice, this list of conditions and the following disclaimer in the
14
* documentation and/or other materials provided with the distribution.
16
* 3. Neither the name of JANET(UK) nor the names of its contributors
17
* may be used to endorse or promote products derived from this software
18
* without specific prior written permission.
20
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33
* Copyright 2008 by the Massachusetts Institute of Technology.
34
* All Rights Reserved.
36
* Export of this software from the United States of America may
37
* require a specific license from the United States Government.
38
* It is the responsibility of any person or organization contemplating
39
* export to obtain such a license before exporting.
41
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
42
* distribute this software and its documentation for any purpose and
43
* without fee is hereby granted, provided that the above copyright
44
* notice appear in all copies and that both that copyright notice and
45
* this permission notice appear in supporting documentation, and that
46
* the name of M.I.T. not be used in advertising or publicity pertaining
47
* to distribution of the software without specific, written prior
48
* permission. Furthermore if you modify this software you must label
49
* your software as modified software and not distribute it in such a
50
* fashion that it might be confused with the original M.I.T. software.
51
* M.I.T. makes no representations about the suitability of
52
* this software for any purpose. It is provided "as is" without express
53
* or implied warranty.
57
* Message protection services: unwrap with scatter-gather API.
60
#include "gssapiP_eap.h"
63
* Caller must provide TOKEN | DATA | PADDING | TRAILER, except
64
* for DCE in which case it can just provide TOKEN | DATA (must
65
* guarantee that DATA is padded)
68
unwrapToken(OM_uint32 *minor,
70
#ifdef HAVE_HEIMDAL_VERSION
71
krb5_crypto krbCrypto,
73
krb5_keyblock *unused GSSEAP_UNUSED,
77
gss_iov_buffer_desc *iov,
79
enum gss_eap_token_type toktype)
81
OM_uint32 major = GSS_S_FAILURE, code;
82
gss_iov_buffer_t header;
83
gss_iov_buffer_t padding;
84
gss_iov_buffer_t trailer;
86
unsigned char *ptr = NULL;
89
size_t dataLen, assocDataLen;
93
krb5_context krbContext;
94
#ifdef HAVE_HEIMDAL_VERSION
95
int freeCrypto = (krbCrypto == NULL);
98
GSSEAP_KRB_INIT(&krbContext);
102
if (qop_state != NULL)
103
*qop_state = GSS_C_QOP_DEFAULT;
105
header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
106
GSSEAP_ASSERT(header != NULL);
108
padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
109
if (padding != NULL && padding->buffer.length != 0) {
110
code = GSSEAP_BAD_PADDING_IOV;
111
major = GSS_S_DEFECTIVE_TOKEN;
115
trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
117
flags = rfc4121Flags(ctx, TRUE);
119
if (toktype == TOK_TYPE_WRAP) {
120
keyUsage = !CTX_IS_INITIATOR(ctx)
121
? KEY_USAGE_INITIATOR_SEAL
122
: KEY_USAGE_ACCEPTOR_SEAL;
124
keyUsage = !CTX_IS_INITIATOR(ctx)
125
? KEY_USAGE_INITIATOR_SIGN
126
: KEY_USAGE_ACCEPTOR_SIGN;
129
gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
131
ptr = (unsigned char *)header->buffer.value;
133
if (header->buffer.length < 16) {
134
code = GSSEAP_TOK_TRUNC;
135
major = GSS_S_DEFECTIVE_TOKEN;
139
if ((ptr[2] & flags) != flags) {
140
code = GSSEAP_BAD_DIRECTION;
141
major = GSS_S_BAD_SIG;
145
#ifdef HAVE_HEIMDAL_VERSION
146
if (krbCrypto == NULL) {
147
code = krb5_crypto_init(krbContext, &ctx->rfc3961Key,
148
ETYPE_NULL, &krbCrypto);
154
if (toktype == TOK_TYPE_WRAP) {
155
size_t krbTrailerLen;
157
if (load_uint16_be(ptr) != TOK_TYPE_WRAP)
159
conf_flag = ((ptr[2] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
162
ec = load_uint16_be(ptr + 4);
163
rrc = load_uint16_be(ptr + 6);
164
seqnum = load_uint64_be(ptr + 8);
166
code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
167
conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
168
KRB5_CRYPTO_TYPE_CHECKSUM,
174
if (trailer == NULL) {
175
size_t desired_rrc = krbTrailerLen;
178
desired_rrc += 16; /* E(Header) */
180
if ((ctx->gssFlags & GSS_C_DCE_STYLE) == 0)
184
/* According to MS, we only need to deal with a fixed RRC for DCE */
185
if (rrc != desired_rrc)
187
} else if (rrc != 0) {
192
unsigned char *althdr;
195
code = gssEapDecrypt(krbContext,
196
((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
197
ec, rrc, KRB_CRYPTO_CONTEXT(ctx), keyUsage,
200
major = GSS_S_BAD_SIG;
204
/* Validate header integrity */
206
althdr = (unsigned char *)header->buffer.value + 16 + ec;
208
althdr = (unsigned char *)trailer->buffer.value + ec;
210
if (load_uint16_be(althdr) != TOK_TYPE_WRAP
211
|| althdr[2] != ptr[2]
212
|| althdr[3] != ptr[3]
213
|| memcmp(althdr + 8, ptr + 8, 8) != 0) {
214
code = GSSEAP_BAD_WRAP_TOKEN;
215
major = GSS_S_BAD_SIG;
219
/* Verify checksum: note EC is checksum size here, not padding */
220
if (ec != krbTrailerLen)
223
/* Zero EC, RRC before computing checksum */
224
store_uint16_be(0, ptr + 4);
225
store_uint16_be(0, ptr + 6);
227
code = gssEapVerify(krbContext, ctx->checksumType, rrc,
228
KRB_CRYPTO_CONTEXT(ctx), keyUsage,
229
iov, iov_count, &valid);
230
if (code != 0 || valid == FALSE) {
231
major = GSS_S_BAD_SIG;
236
major = sequenceCheck(&code, &ctx->seqState, seqnum);
237
if (GSS_ERROR(major))
239
} else if (toktype == TOK_TYPE_MIC) {
240
if (load_uint16_be(ptr) != toktype)
246
seqnum = load_uint64_be(ptr + 8);
249
* Although MIC tokens don't have a RRC, they are similarly
250
* composed of a header and a checksum. So the verify_mic()
251
* can be implemented with a single header buffer, fake the
252
* RRC to the putative trailer length if no trailer buffer.
254
code = gssEapVerify(krbContext, ctx->checksumType,
255
trailer != NULL ? 0 : header->buffer.length - 16,
256
KRB_CRYPTO_CONTEXT(ctx), keyUsage,
257
iov, iov_count, &valid);
258
if (code != 0 || valid == FALSE) {
259
major = GSS_S_BAD_SIG;
262
major = sequenceCheck(&code, &ctx->seqState, seqnum);
263
if (GSS_ERROR(major))
265
} else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
266
if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT)
273
if (conf_state != NULL)
274
*conf_state = conf_flag;
277
major = GSS_S_COMPLETE;
281
code = GSSEAP_BAD_WRAP_TOKEN;
282
major = GSS_S_DEFECTIVE_TOKEN;
286
#ifdef HAVE_HEIMDAL_VERSION
287
if (freeCrypto && krbCrypto != NULL)
288
krb5_crypto_destroy(krbContext, krbCrypto);
295
rotateLeft(void *ptr, size_t bufsiz, size_t rc)
305
tbuf = GSSEAP_MALLOC(rc);
309
memcpy(tbuf, ptr, rc);
310
memmove(ptr, (char *)ptr + rc, bufsiz - rc);
311
memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
318
* Split a STREAM | SIGN_DATA | DATA into
319
* HEADER | SIGN_DATA | DATA | PADDING | TRAILER
322
unwrapStream(OM_uint32 *minor,
325
gss_qop_t *qop_state,
326
gss_iov_buffer_desc *iov,
328
enum gss_eap_token_type toktype)
331
OM_uint32 code = 0, major = GSS_S_FAILURE;
332
krb5_context krbContext;
335
gss_iov_buffer_desc *tiov = NULL;
336
gss_iov_buffer_t stream, data = NULL;
337
gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;
338
#ifdef HAVE_HEIMDAL_VERSION
339
krb5_crypto krbCrypto = NULL;
342
GSSEAP_KRB_INIT(&krbContext);
344
GSSEAP_ASSERT(toktype == TOK_TYPE_WRAP);
346
if (toktype != TOK_TYPE_WRAP) {
347
code = GSSEAP_WRONG_TOK_ID;
351
stream = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM);
352
GSSEAP_ASSERT(stream != NULL);
354
if (stream->buffer.length < 16) {
355
major = GSS_S_DEFECTIVE_TOKEN;
359
ptr = (unsigned char *)stream->buffer.value;
360
ptr += 2; /* skip token type */
362
tiov = (gss_iov_buffer_desc *)GSSEAP_CALLOC((size_t)iov_count + 2,
363
sizeof(gss_iov_buffer_desc));
370
theader = &tiov[i++];
371
theader->type = GSS_IOV_BUFFER_TYPE_HEADER;
372
theader->buffer.value = stream->buffer.value;
373
theader->buffer.length = 16;
375
/* n[SIGN_DATA] | DATA | m[SIGN_DATA] */
376
for (j = 0; j < iov_count; j++) {
377
OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type);
379
if (type == GSS_IOV_BUFFER_TYPE_DATA) {
381
/* only a single DATA buffer can appear */
382
code = GSSEAP_BAD_STREAM_IOV;
389
if (type == GSS_IOV_BUFFER_TYPE_DATA ||
390
type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
395
/* a single DATA buffer must be present */
396
code = GSSEAP_BAD_STREAM_IOV;
400
/* PADDING | TRAILER */
401
tpadding = &tiov[i++];
402
tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING;
403
tpadding->buffer.length = 0;
404
tpadding->buffer.value = NULL;
406
ttrailer = &tiov[i++];
407
ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER;
409
#ifdef HAVE_HEIMDAL_VERSION
410
code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
417
size_t krbHeaderLen = 0;
418
size_t krbTrailerLen = 0;
420
conf_req_flag = ((ptr[0] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
421
ec = conf_req_flag ? load_uint16_be(ptr + 2) : 0;
422
rrc = load_uint16_be(ptr + 4);
425
code = rotateLeft((unsigned char *)stream->buffer.value + 16,
426
stream->buffer.length - 16, rrc);
429
store_uint16_be(0, ptr + 4); /* set RRC to zero */
433
code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
434
KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
437
theader->buffer.length += krbHeaderLen; /* length validated later */
440
/* no PADDING for CFX, EC is used instead */
441
code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
443
? KRB5_CRYPTO_TYPE_TRAILER
444
: KRB5_CRYPTO_TYPE_CHECKSUM,
449
ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) +
451
ttrailer->buffer.value = (unsigned char *)stream->buffer.value +
452
stream->buffer.length - ttrailer->buffer.length;
455
/* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/
456
/* CFX: GSS-Header | Kerb-Header | Data | | EC | E(Header) | Kerb-Trailer */
457
/* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/
459
/* validate lengths */
460
if (stream->buffer.length < theader->buffer.length +
461
tpadding->buffer.length +
462
ttrailer->buffer.length) {
463
major = GSS_S_DEFECTIVE_TOKEN;
464
code = GSSEAP_TOK_TRUNC;
469
tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length -
470
tpadding->buffer.length - theader->buffer.length;
472
GSSEAP_ASSERT(data != NULL);
474
if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
475
code = gssEapAllocIov(tdata, tdata->buffer.length);
479
memcpy(tdata->buffer.value,
480
(unsigned char *)stream->buffer.value + theader->buffer.length,
481
tdata->buffer.length);
483
tdata->buffer.value = (unsigned char *)stream->buffer.value +
484
theader->buffer.length;
487
GSSEAP_ASSERT(i <= iov_count + 2);
489
major = unwrapToken(&code, ctx, KRB_CRYPTO_CONTEXT(ctx),
490
conf_state, qop_state, tiov, i, toktype);
491
if (major == GSS_S_COMPLETE) {
493
} else if (tdata->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
496
gss_release_buffer(&tmp, &tdata->buffer);
497
tdata->type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
503
#ifdef HAVE_HEIMDAL_VERSION
504
if (krbCrypto != NULL)
505
krb5_crypto_destroy(krbContext, krbCrypto);
514
gssEapUnwrapOrVerifyMIC(OM_uint32 *minor,
517
gss_qop_t *qop_state,
518
gss_iov_buffer_desc *iov,
520
enum gss_eap_token_type toktype)
524
if (ctx->encryptionType == ENCTYPE_NULL) {
525
*minor = GSSEAP_KEY_UNAVAILABLE;
526
return GSS_S_UNAVAILABLE;
529
if (gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) {
530
major = unwrapStream(minor, ctx, conf_state, qop_state,
531
iov, iov_count, toktype);
533
major = unwrapToken(minor, ctx,
534
NULL, /* krbCrypto */
535
conf_state, qop_state,
536
iov, iov_count, toktype);
542
OM_uint32 GSSAPI_CALLCONV
543
gss_unwrap_iov(OM_uint32 *minor,
546
gss_qop_t *qop_state,
547
gss_iov_buffer_desc *iov,
552
if (ctx == GSS_C_NO_CONTEXT) {
554
return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
559
GSSEAP_MUTEX_LOCK(&ctx->mutex);
561
if (!CTX_IS_ESTABLISHED(ctx)) {
562
major = GSS_S_NO_CONTEXT;
563
*minor = GSSEAP_CONTEXT_INCOMPLETE;
567
major = gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state,
568
iov, iov_count, TOK_TYPE_WRAP);
569
if (GSS_ERROR(major))
573
GSSEAP_MUTEX_UNLOCK(&ctx->mutex);