2 * Copyright (c) 2010, 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.
56 #include "gssapiP_eap.h"
59 * Caller must provide TOKEN | DATA | PADDING | TRAILER, except
60 * for DCE in which case it can just provide TOKEN | DATA (must
61 * guarantee that DATA is padded)
64 unwrapToken(OM_uint32 *minor,
68 gss_iov_buffer_desc *iov,
70 enum gss_eap_token_type toktype)
73 gss_iov_buffer_t header;
74 gss_iov_buffer_t padding;
75 gss_iov_buffer_t trailer;
76 unsigned char acceptorFlag;
77 unsigned char *ptr = NULL;
80 size_t dataLen, assocDataLen;
84 krb5_context krbContext;
86 GSSEAP_KRB_INIT(&krbContext);
90 if (qop_state != NULL)
91 *qop_state = GSS_C_QOP_DEFAULT;
93 header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
94 assert(header != NULL);
96 padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
97 if (padding != NULL && padding->buffer.length != 0)
98 return GSS_S_DEFECTIVE_TOKEN;
100 trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
102 acceptorFlag = CTX_IS_INITIATOR(ctx) ? TOK_FLAG_SENDER_IS_ACCEPTOR : 0;
105 keyUsage = !CTX_IS_INITIATOR(ctx)
106 ? KEY_USAGE_INITIATOR_SEAL
107 : KEY_USAGE_ACCEPTOR_SEAL;
109 case TOK_TYPE_GSS_CB:
110 keyUsage = KEY_USAGE_CHANNEL_BINDINGS;
114 keyUsage = !CTX_IS_INITIATOR(ctx)
115 ? KEY_USAGE_INITIATOR_SIGN
116 : KEY_USAGE_ACCEPTOR_SIGN;
120 gssEapIovMessageLength(iov, iov_count, &dataLen, &assocDataLen);
122 ptr = (unsigned char *)header->buffer.value;
124 if (header->buffer.length < 16) {
126 return GSS_S_DEFECTIVE_TOKEN;
129 if ((ptr[2] & TOK_FLAG_SENDER_IS_ACCEPTOR) != acceptorFlag) {
130 return GSS_S_BAD_SIG;
133 if (ptr[2] & TOK_FLAG_ACCEPTOR_SUBKEY) {
134 return GSS_S_BAD_SIG;
137 if (toktype == TOK_TYPE_WRAP) {
138 unsigned int krbTrailerLen;
140 if (load_uint16_be(ptr) != TOK_TYPE_WRAP)
142 conf_flag = ((ptr[2] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
145 ec = load_uint16_be(ptr + 4);
146 rrc = load_uint16_be(ptr + 6);
147 seqnum = load_uint64_be(ptr + 8);
149 code = krb5_c_crypto_length(krbContext,
151 conf_flag ? KRB5_CRYPTO_TYPE_TRAILER :
152 KRB5_CRYPTO_TYPE_CHECKSUM,
156 return GSS_S_FAILURE;
160 if (trailer == NULL) {
161 size_t desired_rrc = krbTrailerLen;
164 desired_rrc += 16; /* E(Header) */
166 if ((ctx->gssFlags & GSS_C_DCE_STYLE) == 0)
170 /* According to MS, we only need to deal with a fixed RRC for DCE */
171 if (rrc != desired_rrc)
173 } else if (rrc != 0) {
178 unsigned char *althdr;
181 code = gssEapDecrypt(krbContext,
182 ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0),
183 ec, rrc, &ctx->rfc3961Key,
184 keyUsage, 0, iov, iov_count);
187 return GSS_S_BAD_SIG;
190 /* Validate header integrity */
192 althdr = (unsigned char *)header->buffer.value + 16 + ec;
194 althdr = (unsigned char *)trailer->buffer.value + ec;
196 if (load_uint16_be(althdr) != TOK_TYPE_WRAP
197 || althdr[2] != ptr[2]
198 || althdr[3] != ptr[3]
199 || memcmp(althdr + 8, ptr + 8, 8) != 0) {
201 return GSS_S_BAD_SIG;
204 /* Verify checksum: note EC is checksum size here, not padding */
205 if (ec != krbTrailerLen)
208 /* Zero EC, RRC before computing checksum */
209 store_uint16_be(0, ptr + 4);
210 store_uint16_be(0, ptr + 6);
212 code = gssEapVerify(krbContext, ctx->checksumType, rrc,
213 &ctx->rfc3961Key, keyUsage,
214 iov, iov_count, &valid);
215 if (code != 0 || valid == FALSE) {
217 return GSS_S_BAD_SIG;
221 code = sequenceCheck(&ctx->seqState, seqnum);
222 } else if (toktype == TOK_TYPE_MIC || toktype == TOK_TYPE_GSS_CB) {
223 if (load_uint16_be(ptr) != toktype)
229 seqnum = load_uint64_be(ptr + 8);
231 code = gssEapVerify(krbContext, ctx->checksumType, 0,
232 &ctx->rfc3961Key, keyUsage,
233 iov, iov_count, &valid);
234 if (code != 0 || valid == FALSE) {
236 return GSS_S_BAD_SIG;
238 if (toktype != TOK_TYPE_GSS_CB)
239 code = sequenceCheck(&ctx->seqState, seqnum);
240 } else if (toktype == TOK_TYPE_DELETE_CONTEXT) {
241 if (load_uint16_be(ptr) != TOK_TYPE_DELETE_CONTEXT)
250 if (conf_state != NULL)
251 *conf_state = conf_flag;
258 return GSS_S_DEFECTIVE_TOKEN;
262 rotateLeft(void *ptr, size_t bufsiz, size_t rc)
272 tbuf = GSSEAP_MALLOC(rc);
276 memcpy(tbuf, ptr, rc);
277 memmove(ptr, (char *)ptr + rc, bufsiz - rc);
278 memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
285 * Split a STREAM | SIGN_DATA | DATA into
286 * HEADER | SIGN_DATA | DATA | PADDING | TRAILER
289 unwrapStream(OM_uint32 *minor,
292 gss_qop_t *qop_state,
293 gss_iov_buffer_desc *iov,
295 enum gss_eap_token_type toktype)
298 OM_uint32 code = 0, major = GSS_S_FAILURE;
299 krb5_context krbContext;
300 int conf_req_flag, toktype2;
302 gss_iov_buffer_desc *tiov = NULL;
303 gss_iov_buffer_t stream, data = NULL;
304 gss_iov_buffer_t theader, tdata = NULL, tpadding, ttrailer;
306 GSSEAP_KRB_INIT(&krbContext);
308 assert(toktype == TOK_TYPE_WRAP);
310 if (toktype != TOK_TYPE_WRAP || (ctx->gssFlags & GSS_C_DCE_STYLE)) {
315 stream = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM);
316 assert(stream != NULL);
318 if (stream->buffer.length < 16) {
319 major = GSS_S_DEFECTIVE_TOKEN;
323 ptr = (unsigned char *)stream->buffer.value;
324 toktype2 = load_uint16_be(ptr);
327 tiov = (gss_iov_buffer_desc *)GSSEAP_CALLOC((size_t)iov_count + 2,
328 sizeof(gss_iov_buffer_desc));
335 theader = &tiov[i++];
336 theader->type = GSS_IOV_BUFFER_TYPE_HEADER;
337 theader->buffer.value = stream->buffer.value;
338 theader->buffer.length = 16;
340 /* n[SIGN_DATA] | DATA | m[SIGN_DATA] */
341 for (j = 0; j < iov_count; j++) {
342 OM_uint32 type = GSS_IOV_BUFFER_TYPE(iov[j].type);
344 if (type == GSS_IOV_BUFFER_TYPE_DATA) {
346 /* only a single DATA buffer can appear */
354 if (type == GSS_IOV_BUFFER_TYPE_DATA ||
355 type == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
360 /* a single DATA buffer must be present */
365 /* PADDING | TRAILER */
366 tpadding = &tiov[i++];
367 tpadding->type = GSS_IOV_BUFFER_TYPE_PADDING;
368 tpadding->buffer.length = 0;
369 tpadding->buffer.value = NULL;
371 ttrailer = &tiov[i++];
372 ttrailer->type = GSS_IOV_BUFFER_TYPE_TRAILER;
376 unsigned int krbHeaderLen = 0;
377 unsigned int krbTrailerLen = 0;
379 conf_req_flag = ((ptr[0] & TOK_FLAG_WRAP_CONFIDENTIAL) != 0);
380 ec = conf_req_flag ? load_uint16_be(ptr + 2) : 0;
381 rrc = load_uint16_be(ptr + 4);
384 code = rotateLeft((unsigned char *)stream->buffer.value + 16,
385 stream->buffer.length - 16, rrc);
388 store_uint16_be(0, ptr + 4); /* set RRC to zero */
392 code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
393 KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
396 theader->buffer.length += krbHeaderLen; /* length validated later */
399 /* no PADDING for CFX, EC is used instead */
400 code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
402 ? KRB5_CRYPTO_TYPE_TRAILER
403 : KRB5_CRYPTO_TYPE_CHECKSUM,
408 ttrailer->buffer.length = ec + (conf_req_flag ? 16 : 0 /* E(Header) */) +
410 ttrailer->buffer.value = (unsigned char *)stream->buffer.value +
411 stream->buffer.length - ttrailer->buffer.length;
414 /* IOV: -----------0-------------+---1---+--2--+----------------3--------------*/
415 /* Old: GSS-Header | Conf | Data | Pad | */
416 /* CFX: GSS-Header | Kerb-Header | Data | | EC | E(Header) | Kerb-Trailer */
417 /* GSS: -------GSS-HEADER--------+-DATA--+-PAD-+----------GSS-TRAILER----------*/
419 /* validate lengths */
420 if (stream->buffer.length < theader->buffer.length +
421 tpadding->buffer.length +
422 ttrailer->buffer.length) {
423 code = KRB5_BAD_MSIZE;
424 major = GSS_S_DEFECTIVE_TOKEN;
429 tdata->buffer.length = stream->buffer.length - ttrailer->buffer.length -
430 tpadding->buffer.length - theader->buffer.length;
432 assert(data != NULL);
434 if (data->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
435 code = gssEapAllocIov(tdata, tdata->buffer.length);
439 memcpy(tdata->buffer.value,
440 (unsigned char *)stream->buffer.value + theader->buffer.length,
441 tdata->buffer.length);
443 tdata->buffer.value = (unsigned char *)stream->buffer.value +
444 theader->buffer.length;
447 assert(i <= iov_count + 2);
449 major = unwrapToken(&code, ctx, conf_state, qop_state,
451 if (major == GSS_S_COMPLETE) {
453 } else if (tdata->type & GSS_IOV_BUFFER_FLAG_ALLOCATED) {
456 gss_release_buffer(&tmp, &tdata->buffer);
457 tdata->type &= ~(GSS_IOV_BUFFER_FLAG_ALLOCATED);
470 gssEapUnwrapOrVerifyMIC(OM_uint32 *minor,
473 gss_qop_t *qop_state,
474 gss_iov_buffer_desc *iov,
476 enum gss_eap_token_type toktype)
480 if (!CTX_IS_ESTABLISHED(ctx))
481 return GSS_S_NO_CONTEXT;
483 if (ctx->encryptionType == ENCTYPE_NULL)
484 return GSS_S_UNAVAILABLE;
486 if (gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_STREAM) != NULL) {
487 major = unwrapStream(minor, ctx, conf_state, qop_state,
488 iov, iov_count, toktype);
490 major = unwrapToken(minor, ctx, conf_state, qop_state,
491 iov, iov_count, toktype);
498 gss_unwrap_iov(OM_uint32 *minor,
501 gss_qop_t *qop_state,
502 gss_iov_buffer_desc *iov,
505 return gssEapUnwrapOrVerifyMIC(minor, ctx, conf_state, qop_state,
506 iov, iov_count, TOK_TYPE_WRAP);