gss_wrap_iov_length
[mech_eap.git] / wrap_iov_length.c
1 /*
2  * Copyright (c) 2010, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
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.
15  *
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.
19  *
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
30  * SUCH DAMAGE.
31  */
32
33 #include "gssapiP_eap.h"
34
35 #define INIT_IOV_DATA(_iov)     do { (_iov)->buffer.value = NULL;       \
36         (_iov)->buffer.length = 0; }                                    \
37     while (0)
38
39 OM_uint32
40 gss_wrap_iov_length(OM_uint32 *minor,
41                     gss_ctx_id_t ctx,
42                     int conf_req_flag,
43                     gss_qop_t qop_req,
44                     int *conf_state,
45                     gss_iov_buffer_desc *iov,
46                     int iov_count)
47 {
48     gss_iov_buffer_t header, trailer, padding;
49     size_t dataLength, assocDataLength;
50     size_t gssHeaderLen, gssPadLen, gssTrailerLen;
51     unsigned int krbHeaderLen = 0, krbTrailerLen = 0, krbPadLen = 0;
52     krb5_error_code code;
53     krb5_context krbContext;
54     int dce_style;
55     size_t ec;
56
57     if (qop_req != GSS_C_QOP_DEFAULT)
58         return GSS_S_FAILURE;
59
60     if (!CTX_IS_ESTABLISHED(ctx))
61         return GSS_S_NO_CONTEXT;
62
63     GSSEAP_KRB_INIT(&krbContext);
64
65     header = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
66     if (header == NULL) {
67         *minor = EINVAL;
68         return GSS_S_FAILURE;
69     }
70     INIT_IOV_DATA(header);
71
72     trailer = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
73     if (trailer != NULL) {
74         INIT_IOV_DATA(trailer);
75     }
76
77     dce_style = ((ctx->gssFlags & GSS_C_DCE_STYLE) != 0);
78
79     /* For CFX, EC is used instead of padding, and is placed in header or trailer */
80     padding = gssEapLocateIov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
81     if (padding != NULL) {
82         INIT_IOV_DATA(padding);
83     }
84
85     gssEapIovMessageLength(iov, iov_count, &dataLength, &assocDataLength);
86
87     if (conf_req_flag && gssEapIsIntegrityOnly(iov, iov_count))
88         conf_req_flag = FALSE;
89
90     gssHeaderLen = gssPadLen = gssTrailerLen = 0;
91
92     code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
93                                 conf_req_flag ?
94                                 KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM,
95                                 &krbTrailerLen);
96     if (code != 0) {
97         *minor = code;
98         return GSS_S_FAILURE;
99     }
100
101     if (conf_req_flag) {
102         code = krb5_c_crypto_length(krbContext, ctx->encryptionType,
103                                     KRB5_CRYPTO_TYPE_HEADER, &krbHeaderLen);
104         if (code != 0) {
105             *minor = code;
106             return GSS_S_FAILURE;
107         }
108     }
109
110     gssHeaderLen = 16; /* Header */
111     if (conf_req_flag) {
112         gssHeaderLen += krbHeaderLen; /* Kerb-Header */
113         gssTrailerLen = 16 /* E(Header) */ + krbTrailerLen; /* Kerb-Trailer */
114
115         code = krb5_c_padding_length(krbContext, ctx->encryptionType,
116                                      dataLength - assocDataLength + 16 /* E(Header) */,
117                                      &krbPadLen);
118         if (code != 0) {
119             *minor = code;
120             return GSS_S_FAILURE;
121         }
122
123         if (krbPadLen == 0 && dce_style) {
124             /* Windows rejects AEAD tokens with non-zero EC */
125             code = krb5_c_block_size(krbContext, ctx->encryptionType, &ec);
126             if (code != 0) {
127                 *minor = code;
128                 return GSS_S_FAILURE;
129             }
130         } else
131             ec = krbPadLen;
132
133         gssTrailerLen += ec;
134     } else {
135         gssTrailerLen = krbTrailerLen; /* Kerb-Checksum */
136     }
137
138     dataLength += gssPadLen;
139
140     if (trailer == NULL)
141         gssHeaderLen += gssTrailerLen;
142     else
143         trailer->buffer.length = gssTrailerLen;
144
145     assert(gssPadLen == 0 || padding != NULL);
146
147     if (padding != NULL)
148         padding->buffer.length = gssPadLen;
149
150     header->buffer.length = gssHeaderLen;
151
152     if (conf_state != NULL)
153         *conf_state = conf_req_flag;
154
155     *minor = 0;
156     return GSS_S_COMPLETE;
157 }