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 = gssEapLocateHeaderIov(iov, iov_count, toktype);
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, toktype, &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);
248 /* For MIC tokens, the GSS header and checksum are in the same buffer.
249 * Fake up an RRC so that the checksum is expected in the header. */
250 rrc = (trailer != NULL) ? 0 : header->buffer.length - 16;
251 code = gssEapVerify(krbContext, ctx->checksumType, rrc,
252 KRB_CRYPTO_CONTEXT(ctx), keyUsage,
253 iov, iov_count, toktype, &valid);
254 if (code != 0 || valid == FALSE) {
255 major = GSS_S_BAD_SIG;
258 major = sequenceCheck(&code, &ctx->seqState, seqnum);
259 if (GSS_ERROR(major))
261 } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
262 if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT)
269 if (conf_state != NULL)
270 *conf_state = conf_flag;
273 major = GSS_S_COMPLETE;
277 code = GSSEAP_BAD_WRAP_TOKEN;
278 major = GSS_S_DEFECTIVE_TOKEN;
282 #ifdef HAVE_HEIMDAL_VERSION
283 if (freeCrypto && krbCrypto != NULL)
284 krb5_crypto_destroy(krbContext, krbCrypto);
291 rotateLeft(void *ptr, size_t bufsiz, size_t rc)
301 tbuf = GSSEAP_MALLOC(rc);
305 memcpy(tbuf, ptr, rc);
306 memmove(ptr, (char *)ptr + rc, bufsiz - rc);
307 memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
314 * Split a STREAM | SIGN_DATA | DATA into
315 * HEADER | SIGN_DATA | DATA | PADDING | TRAILER
318 unwrapStream(OM_uint32 *minor,
321 gss_qop_t *qop_state,
322 gss_iov_buffer_desc *iov,
324 enum gss_eap_token_type toktype)
327 OM_uint32 code = 0, major = GSS_S_FAILURE;
328 krb5_context krbContext;
331 gss_iov_buffer_desc *tiov = NULL;
332 gss_iov_buffer_t stream, data = NULL;
333 gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;
334 #ifdef HAVE_HEIMDAL_VERSION
335 krb5_crypto krbCrypto = NULL;
338 GSSEAP_KRB_INIT(&krbContext);
340 GSSEAP_ASSERT(toktype == TOK_TYPE_WRAP);
342 if (toktype != TOK_TYPE_WRAP) {
343 code = GSSEAP_WRONG_TOK_ID;
347 stream = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM);
348 GSSEAP_ASSERT(stream != NULL);
350 if (stream->buffer.length < 16) {
351 major = GSS_S_DEFECTIVE_TOKEN;
355 ptr = (unsigned char *)stream->buffer.value;
356 ptr += 2; /* skip token type */
358 tiov = (gss_iov_buffer_desc *)GSSEAP_CALLOC((size_t)iov_count + 2,
359 sizeof(gss_iov_buffer_desc));
366 theader = &tiov[i++];
367 theader->type = GSS_IOV_BUFFER_TYPE_HEADER;
368 theader->buffer.value = stream->buffer.value;
369 theader->buffer.length = 16;
371 /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */
372 for (j = 0; j < iov_count; j++) {
373 OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type);
375 if (type == GSS_IOV_BUFFER_TYPE_DATA) {
377 /* only a single DATA buffer can appear */
378 code = GSSEAP_BAD_STREAM_IOV;
385 if (type == GSS_IOV_BUFFER_TYPE_DATA ||
386 type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
391 /* a single DATA buffer must be present */
392 code = GSSEAP_BAD_STREAM_IOV;
396 /* PADDING | TRAILER */
397 tpadding = &tiov[i++];
398 tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING;
399 tpadding->buffer.length = 0;
400 tpadding->buffer.value = NULL;
402 ttrailer = &tiov[i++];
403 ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER;
405 #ifdef HAVE_HEIMDAL_VERSION
406 code = krb5_crypto_init(krbContext, &ctx->rfc3961Key, ETYPE_NULL, &krbCrypto);
413 size_t krbHeaderLen = 0;
414 size_t krbTrailerLen = 0;
416 conf_req_flag = ((ptr[0] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
417 ec = conf_req_flag ? load_uint16_be(ptr + 2) : 0;
418 rrc = load_uint16_be(ptr + 4);
421 code = rotateLeft((unsigned char *)stream->buffer.value + 16,
422 stream->buffer.length - 16, rrc);
425 store_uint16_be(0, ptr + 4); /* set RRC to zero */
429 code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
430 KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
433 theader->buffer.length += krbHeaderLen; /* length validated later */
436 /* no PADDING for CFX, EC is used instead */
437 code = krbCryptoLength(krbContext, KRB_CRYPTO_CONTEXT(ctx),
439 ? KRB5_CRYPTO_TYPE_TRAILER
440 : KRB5_CRYPTO_TYPE_CHECKSUM,
445 ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) +
447 ttrailer->buffer.value = (unsigned char *)stream->buffer.value +
448 stream->buffer.length - ttrailer->buffer.length;
451 /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/
452 /* CFX: GSS-Header | Kerb-Header | Data | | EC | E(Header) | Kerb-Trailer */
453 /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/
455 /* validate lengths */
456 if (stream->buffer.length < theader->buffer.length +
457 tpadding->buffer.length +
458 ttrailer->buffer.length) {
459 major = GSS_S_DEFECTIVE_TOKEN;
460 code = GSSEAP_TOK_TRUNC;
465 tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length -
466 tpadding->buffer.length - theader->buffer.length;
468 GSSEAP_ASSERT(data != NULL);
470 if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
471 code = gssEapAllocIov(tdata, tdata->buffer.length);
475 memcpy(tdata->buffer.value,
476 (unsigned char *)stream->buffer.value + theader->buffer.length,
477 tdata->buffer.length);
479 tdata->buffer.value = (unsigned char *)stream->buffer.value +
480 theader->buffer.length;
483 GSSEAP_ASSERT(i <= iov_count + 2);
485 major = unwrapToken(&code, ctx, KRB_CRYPTO_CONTEXT(ctx),
486 conf_state, qop_state, tiov, i, toktype);
487 if (major == GSS_S_COMPLETE) {
489 } else if (tdata->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
492 gss_release_buffer(&tmp, &tdata->buffer);
493 tdata->type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
499 #ifdef HAVE_HEIMDAL_VERSION
500 if (krbCrypto != NULL)
501 krb5_crypto_destroy(krbContext, krbCrypto);
510 gssEapUnwrapOrVerifyMIC(OM_uint32 *minor,
513 gss_qop_t *qop_state,
514 gss_iov_buffer_desc *iov,
516 enum gss_eap_token_type toktype)
520 if (ctx->encryptionType == ENCTYPE_NULL) {
521 *minor = GSSEAP_KEY_UNAVAILABLE;
522 return GSS_S_UNAVAILABLE;
525 if (gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) {
526 major = unwrapStream(minor, ctx, conf_state, qop_state,
527 iov, iov_count, toktype);
529 major = unwrapToken(minor, ctx,
530 NULL, /* krbCrypto */
531 conf_state, qop_state,
532 iov, iov_count, toktype);
538 OM_uint32 GSSAPI_CALLCONV
539 gss_unwrap_iov(OM_uint32 *minor,
542 gss_qop_t *qop_state,
543 gss_iov_buffer_desc *iov,
548 if (ctx == GSS_C_NO_CONTEXT) {
550 return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_NO_CONTEXT;
555 GSSEAP_MUTEX_LOCK(&ctx->mutex);
557 if (!CTX_IS_ESTABLISHED(ctx)) {
558 major = GSS_S_NO_CONTEXT;
559 *minor = GSSEAP_CONTEXT_INCOMPLETE;
563 major = gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state,
564 iov, iov_count, TOK_TYPE_WRAP);
565 if (GSS_ERROR(major))
569 GSSEAP_MUTEX_UNLOCK(&ctx->mutex);