1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/k5sealv3.c */
3 /*
4 * Copyright 2003,2004,2007 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 /* draft-ietf-krb-wg-gssapi-cfx-05 */
28
29 #include "k5-int.h"
30 #include "gssapiP_krb5.h"
31
32 int
gss_krb5int_rotate_left(void * ptr,size_t bufsiz,size_t rc)33 gss_krb5int_rotate_left (void *ptr, size_t bufsiz, size_t rc)
34 {
35 /* Optimize for receiving. After some debugging is done, the MIT
36 implementation won't do any rotates on sending, and while
37 debugging, they'll be randomly chosen.
38
39 Return 1 for success, 0 for failure (ENOMEM). */
40 void *tbuf;
41
42 if (bufsiz == 0)
43 return 1;
44 rc = rc % bufsiz;
45 if (rc == 0)
46 return 1;
47
48 tbuf = malloc(rc);
49 if (tbuf == 0)
50 return 0;
51 memcpy(tbuf, ptr, rc);
52 memmove(ptr, (char *)ptr + rc, bufsiz - rc);
53 memcpy((char *)ptr + bufsiz - rc, tbuf, rc);
54 free(tbuf);
55 return 1;
56 }
57
58 static const gss_buffer_desc empty_message = { 0, 0 };
59
60 krb5_error_code
gss_krb5int_make_seal_token_v3(krb5_context context,krb5_gss_ctx_id_rec * ctx,const gss_buffer_desc * message,gss_buffer_t token,int conf_req_flag,int toktype)61 gss_krb5int_make_seal_token_v3 (krb5_context context,
62 krb5_gss_ctx_id_rec *ctx,
63 const gss_buffer_desc * message,
64 gss_buffer_t token,
65 int conf_req_flag, int toktype)
66 {
67 size_t bufsize = 16;
68 unsigned char *outbuf = NULL;
69 krb5_error_code err;
70 int key_usage;
71 unsigned char acceptor_flag;
72 const gss_buffer_desc *message2 = message;
73 #ifdef CFX_EXERCISE
74 size_t rrc;
75 #endif
76 size_t ec;
77 unsigned short tok_id;
78 krb5_checksum sum = { 0 };
79 krb5_key key;
80 krb5_cksumtype cksumtype;
81 krb5_data plain = empty_data();
82
83 token->value = NULL;
84 token->length = 0;
85
86 acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
87 key_usage = (toktype == KG_TOK_WRAP_MSG
88 ? (ctx->initiate
89 ? KG_USAGE_INITIATOR_SEAL
90 : KG_USAGE_ACCEPTOR_SEAL)
91 : (ctx->initiate
92 ? KG_USAGE_INITIATOR_SIGN
93 : KG_USAGE_ACCEPTOR_SIGN));
94 if (ctx->have_acceptor_subkey) {
95 key = ctx->acceptor_subkey;
96 cksumtype = ctx->acceptor_subkey_cksumtype;
97 } else {
98 key = ctx->subkey;
99 cksumtype = ctx->cksumtype;
100 }
101 assert(key != NULL);
102
103 #ifdef CFX_EXERCISE
104 {
105 static int initialized = 0;
106 if (!initialized) {
107 srand(time(0));
108 initialized = 1;
109 }
110 }
111 #endif
112
113 if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
114 krb5_enc_data cipher;
115 size_t ec_max;
116 size_t encrypt_size;
117
118 /* 300: Adds some slop. */
119 if (SIZE_MAX - 300 < message->length) {
120 err = ENOMEM;
121 goto cleanup;
122 }
123 ec_max = SIZE_MAX - message->length - 300;
124 if (ec_max > 0xffff)
125 ec_max = 0xffff;
126 #ifdef CFX_EXERCISE
127 /* For testing only. For performance, always set ec = 0. */
128 ec = ec_max & rand();
129 #else
130 ec = 0;
131 #endif
132 err = alloc_data(&plain, message->length + 16 + ec);
133 if (err)
134 goto cleanup;
135
136 /* Get size of ciphertext. */
137 encrypt_size = krb5_encrypt_size(plain.length, key->keyblock.enctype);
138 if (encrypt_size > SIZE_MAX / 2) {
139 err = ENOMEM;
140 goto cleanup;
141 }
142 bufsize = 16 + encrypt_size;
143 /* Allocate space for header plus encrypted data. */
144 outbuf = gssalloc_malloc(bufsize);
145 if (outbuf == NULL) {
146 err = ENOMEM;
147 goto cleanup;
148 }
149
150 /* TOK_ID */
151 store_16_be(KG2_TOK_WRAP_MSG, outbuf);
152 /* flags */
153 outbuf[2] = (acceptor_flag | FLAG_WRAP_CONFIDENTIAL |
154 (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
155 /* filler */
156 outbuf[3] = 0xff;
157 /* EC */
158 store_16_be(ec, outbuf+4);
159 /* RRC */
160 store_16_be(0, outbuf+6);
161 store_64_be(ctx->seq_send, outbuf+8);
162
163 memcpy(plain.data, message->value, message->length);
164 if (ec != 0)
165 memset(plain.data + message->length, 'x', ec);
166 memcpy(plain.data + message->length + ec, outbuf, 16);
167
168 cipher.ciphertext.data = (char *)outbuf + 16;
169 cipher.ciphertext.length = bufsize - 16;
170 cipher.enctype = key->keyblock.enctype;
171 err = krb5_k_encrypt(context, key, key_usage, 0, &plain, &cipher);
172 if (err)
173 goto cleanup;
174
175 /* Now that we know we're returning a valid token.... */
176 ctx->seq_send++;
177
178 #ifdef CFX_EXERCISE
179 rrc = rand() & 0xffff;
180 if (gss_krb5int_rotate_left(outbuf+16, bufsize-16,
181 (bufsize-16) - (rrc % (bufsize - 16))))
182 store_16_be(rrc, outbuf+6);
183 /* If the rotate fails, don't worry about it. */
184 #endif
185 } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
186 size_t cksumsize;
187
188 /* Here, message is the application-supplied data; message2 is
189 what goes into the output token. They may be the same, or
190 message2 may be empty (for MIC). */
191
192 tok_id = KG2_TOK_WRAP_MSG;
193
194 wrap_with_checksum:
195 err = alloc_data(&plain, message->length + 16);
196 if (err)
197 goto cleanup;
198
199 err = krb5_c_checksum_length(context, cksumtype, &cksumsize);
200 if (err)
201 goto cleanup;
202
203 assert(cksumsize <= 0xffff);
204
205 bufsize = 16 + message2->length + cksumsize;
206 outbuf = gssalloc_malloc(bufsize);
207 if (outbuf == NULL) {
208 err = ENOMEM;
209 goto cleanup;
210 }
211
212 /* TOK_ID */
213 store_16_be(tok_id, outbuf);
214 /* flags */
215 outbuf[2] = (acceptor_flag
216 | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
217 /* filler */
218 outbuf[3] = 0xff;
219 if (toktype == KG_TOK_WRAP_MSG) {
220 /* Use 0 for checksum calculation, substitute
221 checksum length later. */
222 /* EC */
223 store_16_be(0, outbuf+4);
224 /* RRC */
225 store_16_be(0, outbuf+6);
226 } else {
227 /* MIC and DEL store 0xFF in EC and RRC. */
228 store_16_be(0xffff, outbuf+4);
229 store_16_be(0xffff, outbuf+6);
230 }
231 store_64_be(ctx->seq_send, outbuf+8);
232
233 memcpy(plain.data, message->value, message->length);
234 memcpy(plain.data + message->length, outbuf, 16);
235
236 /* Fill in the output token -- data contents, if any, and
237 space for the checksum. */
238 if (message2->length)
239 memcpy(outbuf + 16, message2->value, message2->length);
240
241 err = krb5_k_make_checksum(context, cksumtype, key,
242 key_usage, &plain, &sum);
243 if (err) {
244 zap(outbuf,bufsize);
245 goto cleanup;
246 }
247 if (sum.length != cksumsize)
248 abort();
249 memcpy(outbuf + 16 + message2->length, sum.contents, cksumsize);
250 /* Now that we know we're actually generating the token... */
251 ctx->seq_send++;
252
253 if (toktype == KG_TOK_WRAP_MSG) {
254 #ifdef CFX_EXERCISE
255 rrc = rand() & 0xffff;
256 /* If the rotate fails, don't worry about it. */
257 if (gss_krb5int_rotate_left(outbuf+16, bufsize-16,
258 (bufsize-16) - (rrc % (bufsize - 16))))
259 store_16_be(rrc, outbuf+6);
260 #endif
261 /* Fix up EC field. */
262 store_16_be(cksumsize, outbuf+4);
263 } else {
264 store_16_be(0xffff, outbuf+6);
265 }
266 } else if (toktype == KG_TOK_MIC_MSG) {
267 tok_id = KG2_TOK_MIC_MSG;
268 message2 = &empty_message;
269 goto wrap_with_checksum;
270 } else if (toktype == KG_TOK_DEL_CTX) {
271 tok_id = KG2_TOK_DEL_CTX;
272 message = message2 = &empty_message;
273 goto wrap_with_checksum;
274 } else
275 abort();
276
277 token->value = outbuf;
278 token->length = bufsize;
279 outbuf = NULL;
280 err = 0;
281
282 cleanup:
283 krb5_free_checksum_contents(context, &sum);
284 zapfree(plain.data, plain.length);
285 gssalloc_free(outbuf);
286 return err;
287 }
288