xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/k5seal.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 1993 by OpenVision Technologies, Inc.
4  *
5  * Permission to use, copy, modify, distribute, and sell this software
6  * and its documentation for any purpose is hereby granted without fee,
7  * provided that the above copyright notice appears in all copies and
8  * that both that copyright notice and this permission notice appear in
9  * supporting documentation, and that the name of OpenVision not be used
10  * in advertising or publicity pertaining to distribution of the software
11  * without specific, written prior permission. OpenVision makes no
12  * representations about the suitability of this software for any
13  * purpose.  It is provided "as is" without express or implied warranty.
14  *
15  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
19  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
20  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
21  * PERFORMANCE OF THIS SOFTWARE.
22  */
23 
24 /*
25  * Copyright (C) 1998 by the FundsXpress, INC.
26  *
27  * All rights reserved.
28  *
29  * Export of this software from the United States of America may require
30  * a specific license from the United States Government.  It is the
31  * responsibility of any person or organization contemplating export to
32  * obtain such a license before exporting.
33  *
34  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
35  * distribute this software and its documentation for any purpose and
36  * without fee is hereby granted, provided that the above copyright
37  * notice appear in all copies and that both that copyright notice and
38  * this permission notice appear in supporting documentation, and that
39  * the name of FundsXpress. not be used in advertising or publicity pertaining
40  * to distribution of the software without specific, written prior
41  * permission.  FundsXpress makes no representations about the suitability of
42  * this software for any purpose.  It is provided "as is" without express
43  * or implied warranty.
44  *
45  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
46  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
47  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
48  */
49 
50 #include "gssapiP_krb5.h"
51 
52 #include <assert.h>
53 
54 static krb5_error_code
make_seal_token_v1(krb5_context context,krb5_key enc,krb5_key seq,uint64_t * seqnum,int direction,gss_buffer_t text,gss_buffer_t token,int signalg,size_t cksum_size,int sealalg,int do_encrypt,int toktype,gss_OID oid)55 make_seal_token_v1 (krb5_context context,
56                     krb5_key enc,
57                     krb5_key seq,
58                     uint64_t *seqnum,
59                     int direction,
60                     gss_buffer_t text,
61                     gss_buffer_t token,
62                     int signalg,
63                     size_t cksum_size,
64                     int sealalg,
65                     int do_encrypt,
66                     int toktype,
67                     gss_OID oid)
68 {
69     krb5_error_code code;
70     size_t sumlen;
71     char *data_ptr;
72     krb5_data plaind;
73     krb5_checksum md5cksum;
74     /* msglen contains the message length
75      * we are signing/encrypting.  tmsglen
76      * contains the length of the message
77      * we plan to write out to the token.
78      * tlen is the length of the token
79      * including header. */
80     unsigned int conflen=0, tmsglen, tlen, msglen;
81     unsigned char *t, *metadata, *checksum, *payload;
82     unsigned char *plain;
83     unsigned char pad;
84     krb5_keyusage sign_usage = KG_USAGE_SIGN;
85     struct k5buf buf;
86 
87     assert((!do_encrypt) || (toktype == KG_TOK_SEAL_MSG));
88     /* create the token buffer */
89     /* Do we need confounder? */
90     if (do_encrypt || toktype == KG_TOK_SEAL_MSG)
91         conflen = kg_confounder_size(context, enc->keyblock.enctype);
92     else conflen = 0;
93 
94     if (toktype == KG_TOK_SEAL_MSG) {
95         switch (sealalg) {
96         case SEAL_ALG_MICROSOFT_RC4:
97             msglen = conflen + text->length+1;
98             pad = 1;
99             break;
100         default:
101             /* XXX knows that des block size is 8 */
102             msglen = (conflen+text->length+8)&(~7);
103             pad = 8-(text->length%8);
104         }
105         tmsglen = msglen;
106     } else {
107         tmsglen = 0;
108         msglen = text->length;
109         pad = 0;
110     }
111 
112     tlen = g_token_size(oid, 14 + cksum_size + tmsglen);
113     t = gssalloc_malloc(tlen);
114     if (t == NULL)
115         return(ENOMEM);
116     k5_buf_init_fixed(&buf, t, tlen);
117 
118     /*** fill in the token */
119 
120     g_make_token_header(&buf, oid, 14 + cksum_size + tmsglen, toktype);
121     metadata = k5_buf_get_space(&buf, 14);
122     checksum = k5_buf_get_space(&buf, cksum_size);
123     payload = k5_buf_get_space(&buf, tmsglen);
124     assert(metadata != NULL && checksum != NULL && payload != NULL);
125     assert(buf.len == tlen);
126 
127     /* 0..1 SIGN_ALG */
128     store_16_le(signalg, &metadata[0]);
129 
130     /* 2..3 SEAL_ALG or Filler */
131     if ((toktype == KG_TOK_SEAL_MSG) && do_encrypt) {
132         store_16_le(sealalg, &metadata[2]);
133     } else {
134         /* No seal */
135         metadata[2] = 0xFF;
136         metadata[3] = 0xFF;
137     }
138 
139     /* 4..5 Filler */
140     metadata[4] = 0xFF;
141     metadata[5] = 0xFF;
142 
143     /* pad the plaintext, encrypt if needed, and stick it in the token */
144 
145     /* initialize the the checksum */
146     switch (signalg) {
147     case SGN_ALG_HMAC_SHA1_DES3_KD:
148         md5cksum.checksum_type = CKSUMTYPE_HMAC_SHA1_DES3;
149         break;
150     case SGN_ALG_HMAC_MD5:
151         md5cksum.checksum_type = CKSUMTYPE_HMAC_MD5_ARCFOUR;
152         if (toktype != KG_TOK_SEAL_MSG)
153             sign_usage = 15;
154         break;
155     default:
156         abort ();
157     }
158 
159     code = krb5_c_checksum_length(context, md5cksum.checksum_type, &sumlen);
160     if (code) {
161         gssalloc_free(t);
162         return(code);
163     }
164     md5cksum.length = sumlen;
165 
166 
167     if ((plain = (unsigned char *) xmalloc(msglen ? msglen : 1)) == NULL) {
168         gssalloc_free(t);
169         return(ENOMEM);
170     }
171 
172     if (conflen) {
173         if ((code = kg_make_confounder(context, enc->keyblock.enctype,
174                                        plain))) {
175             xfree(plain);
176             gssalloc_free(t);
177             return(code);
178         }
179     }
180 
181     memcpy(plain+conflen, text->value, text->length);
182     if (pad) memset(plain+conflen+text->length, pad, pad);
183 
184     /* compute the checksum */
185 
186     /* 8 = head of token body as specified by mech spec */
187     if (! (data_ptr = xmalloc(8 + msglen))) {
188         xfree(plain);
189         gssalloc_free(t);
190         return(ENOMEM);
191     }
192     /* Checksum over the token ID, metadata bytes, and plaintext. */
193     memcpy(data_ptr, metadata - 2, 8);
194     memcpy(data_ptr + 8, plain, msglen);
195     plaind.length = 8 + msglen;
196     plaind.data = data_ptr;
197     code = krb5_k_make_checksum(context, md5cksum.checksum_type, seq,
198                                 sign_usage, &plaind, &md5cksum);
199     xfree(data_ptr);
200 
201     if (code) {
202         xfree(plain);
203         gssalloc_free(t);
204         return(code);
205     }
206     switch(signalg) {
207     case SGN_ALG_HMAC_SHA1_DES3_KD:
208         /*
209          * Using key derivation, the call to krb5_c_make_checksum
210          * already dealt with encrypting.
211          */
212         if (md5cksum.length != cksum_size)
213             abort ();
214         memcpy(checksum, md5cksum.contents, md5cksum.length);
215         break;
216     case SGN_ALG_HMAC_MD5:
217         memcpy(checksum, md5cksum.contents, cksum_size);
218         break;
219     }
220 
221     krb5_free_checksum_contents(context, &md5cksum);
222 
223     /* create the seq_num */
224 
225     code = kg_make_seq_num(context, seq, direction?0:0xff,
226                            (krb5_ui_4)*seqnum, checksum, metadata + 6);
227     if (code) {
228         xfree (plain);
229         gssalloc_free(t);
230         return(code);
231     }
232 
233     if (do_encrypt) {
234         switch(sealalg) {
235         case SEAL_ALG_MICROSOFT_RC4:
236         {
237             unsigned char bigend_seqnum[4];
238             krb5_keyblock *enc_key;
239             int i;
240             store_32_be(*seqnum, bigend_seqnum);
241             code = krb5_k_key_keyblock(context, enc, &enc_key);
242             if (code)
243             {
244                 xfree(plain);
245                 gssalloc_free(t);
246                 return(code);
247             }
248             assert (enc_key->length == 16);
249             for (i = 0; i <= 15; i++)
250                 ((char *) enc_key->contents)[i] ^=0xf0;
251             code = kg_arcfour_docrypt(enc_key, 0, bigend_seqnum, 4, plain,
252                                       tmsglen, payload);
253             krb5_free_keyblock (context, enc_key);
254             if (code)
255             {
256                 xfree(plain);
257                 gssalloc_free(t);
258                 return(code);
259             }
260         }
261         break;
262         default:
263             code = kg_encrypt(context, enc, KG_USAGE_SEAL, NULL,  plain,
264                               payload, tmsglen);
265             if (code) {
266                 xfree(plain);
267                 gssalloc_free(t);
268                 return(code);
269             }
270         }
271     }else {
272         if (tmsglen)
273             memcpy(payload, plain, tmsglen);
274     }
275     xfree(plain);
276 
277 
278     /* that's it.  return the token */
279 
280     (*seqnum)++;
281     *seqnum &= 0xffffffffL;
282 
283     token->length = tlen;
284     token->value = (void *) t;
285 
286     return(0);
287 }
288 
289 /* if signonly is true, ignore conf_req, conf_state,
290    and do not encode the ENC_TYPE, MSG_LENGTH, or MSG_TEXT fields */
291 
292 OM_uint32
kg_seal(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer,int toktype)293 kg_seal(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
294         int conf_req_flag, gss_qop_t qop_req,
295         gss_buffer_t input_message_buffer, int *conf_state,
296         gss_buffer_t output_message_buffer, int toktype)
297 {
298     krb5_gss_ctx_id_rec *ctx;
299     krb5_error_code code;
300     krb5_context context;
301 
302     output_message_buffer->length = 0;
303     output_message_buffer->value = NULL;
304 
305     /* Only default qop or matching established cryptosystem is allowed.
306 
307        There are NO EXTENSIONS to this set for AES and friends!  The
308        new spec says "just use 0".  The old spec plus extensions would
309        actually allow for certain non-zero values.  Fix this to handle
310        them later.  */
311     if (qop_req != 0) {
312         *minor_status = (OM_uint32) G_UNKNOWN_QOP;
313         return GSS_S_BAD_QOP;
314     }
315 
316     ctx = (krb5_gss_ctx_id_rec *) context_handle;
317 
318     if (ctx->terminated || !ctx->established) {
319         *minor_status = KG_CTX_INCOMPLETE;
320         return(GSS_S_NO_CONTEXT);
321     }
322 
323     context = ctx->k5_context;
324     switch (ctx->proto)
325     {
326     case 0:
327         code = make_seal_token_v1(context, ctx->enc, ctx->seq,
328                                   &ctx->seq_send, ctx->initiate,
329                                   input_message_buffer, output_message_buffer,
330                                   ctx->signalg, ctx->cksum_size, ctx->sealalg,
331                                   conf_req_flag, toktype, ctx->mech_used);
332         break;
333     case 1:
334         code = gss_krb5int_make_seal_token_v3(context, ctx,
335                                               input_message_buffer,
336                                               output_message_buffer,
337                                               conf_req_flag, toktype);
338         break;
339     default:
340         code = G_UNKNOWN_QOP;   /* XXX */
341         break;
342     }
343 
344     if (code) {
345         *minor_status = code;
346         save_error_info(*minor_status, context);
347         return(GSS_S_FAILURE);
348     }
349 
350     if (conf_state)
351         *conf_state = conf_req_flag;
352 
353     *minor_status = 0;
354     return(GSS_S_COMPLETE);
355 }
356 
357 OM_uint32 KRB5_CALLCONV
krb5_gss_wrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer)358 krb5_gss_wrap(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
359               int conf_req_flag, gss_qop_t qop_req,
360               gss_buffer_t input_message_buffer, int *conf_state,
361               gss_buffer_t output_message_buffer)
362 {
363     return(kg_seal(minor_status, context_handle, conf_req_flag,
364                    qop_req, input_message_buffer, conf_state,
365                    output_message_buffer, KG_TOK_WRAP_MSG));
366 }
367 
368 OM_uint32 KRB5_CALLCONV
krb5_gss_get_mic(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_buffer_t message_buffer,gss_buffer_t message_token)369 krb5_gss_get_mic(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
370                  gss_qop_t qop_req, gss_buffer_t message_buffer,
371                  gss_buffer_t message_token)
372 {
373     return(kg_seal(minor_status, context_handle, 0,
374                    qop_req, message_buffer, NULL,
375                    message_token, KG_TOK_MIC_MSG));
376 }
377