xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/k5sealv3.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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