1e9e2580a9b0df85541355e8697883689b00a1b8
[krb5.git] / src / lib / gssapi / krb5 / k5unseal.c
1 /*
2  * Copyright 1993 by OpenVision Technologies, Inc.
3  * 
4  * Permission to use, copy, modify, distribute, and sell this software
5  * and its documentation for any purpose is hereby granted without fee,
6  * provided that the above copyright notice appears in all copies and
7  * that both that copyright notice and this permission notice appear in
8  * supporting documentation, and that the name of OpenVision not be used
9  * in advertising or publicity pertaining to distribution of the software
10  * without specific, written prior permission. OpenVision makes no
11  * representations about the suitability of this software for any
12  * purpose.  It is provided "as is" without express or implied warranty.
13  * 
14  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
18  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
19  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20  * PERFORMANCE OF THIS SOFTWARE.
21  */
22
23 #include "gssapiP_krb5.h"
24 #include <memory.h>
25 #include <krb5/rsa-md5.h>
26
27 /* message_buffer is an input if SIGN, output if SEAL, and ignored if DEL_CTX
28    conf_state is only valid if SEAL.
29    */
30
31 OM_uint32 INTERFACE
32 kg_unseal(minor_status, context_handle, input_token_buffer, message_buffer,
33           conf_state, qop_state, toktype)
34      OM_uint32 *minor_status;
35      gss_ctx_id_t context_handle;
36      gss_buffer_t input_token_buffer;
37      gss_buffer_t message_buffer;
38      int *conf_state;
39      int *qop_state;
40      int toktype;
41 {
42    krb5_gss_ctx_id_rec *ctx;
43    krb5_error_code code;
44    int bodysize;
45    int tmsglen;
46    int signalg;
47    int sealalg;
48    gss_buffer_desc token;
49    unsigned char *ptr;
50    krb5_checksum desmac;
51    MD5_CTX md5;
52    unsigned char *cksum;
53    krb5_timestamp now;
54    unsigned char *plain;
55    int plainlen;
56
57    if (toktype == KG_TOK_SEAL_MSG) {
58       message_buffer->length = 0;
59       message_buffer->value = NULL;
60    }
61
62    /* validate the context handle */
63    if (! kg_validate_ctx_id(context_handle)) {
64       *minor_status = (OM_uint32) G_VALIDATE_FAILED;
65       return(GSS_S_NO_CONTEXT);
66    }
67
68    ctx = (krb5_gss_ctx_id_rec *) context_handle;
69
70    if (! ctx->established) {
71       *minor_status = KG_CTX_INCOMPLETE;
72       return(GSS_S_NO_CONTEXT);
73    }
74
75    /* parse the token, leave the data in message_buffer, setting conf_state */
76
77    /* verify the header */
78
79    ptr = (unsigned char *) input_token_buffer->value;
80
81    if (! g_verify_token_header(gss_mech_krb5, &bodysize,
82                                &ptr, toktype, input_token_buffer->length)) {
83       *minor_status = 0;
84       return(GSS_S_DEFECTIVE_TOKEN);
85    }
86
87    if (toktype == KG_TOK_SEAL_MSG)
88       tmsglen = bodysize-22;
89
90    /* get the sign and seal algorithms */
91
92    signalg = ptr[0] + (ptr[1]<<8);
93    sealalg = ptr[2] + (ptr[3]<<8);
94
95    if (((signalg != 0) && (signalg != 1)) ||
96        ((toktype != KG_TOK_SEAL_MSG) && (sealalg != 0xffff)) ||
97        ((toktype == KG_TOK_SEAL_MSG) && 
98         ((sealalg != 0xffff) && (sealalg != 0))) ||
99        (ptr[4] != 0xff) ||
100        (ptr[5] != 0xff)) {
101       *minor_status = 0;
102       return(GSS_S_DEFECTIVE_TOKEN);
103    }
104
105    /* get the token parameters */
106
107    /* decode the message, if SEAL */
108
109    if (toktype == KG_TOK_SEAL_MSG) {
110       if (sealalg == 0) {
111          if ((plain = (unsigned char *) xmalloc(tmsglen)) == NULL) {
112             *minor_status = ENOMEM;
113             return(GSS_S_FAILURE);
114          }
115
116          if (code = kg_decrypt(&ctx->enc, NULL, ptr+22, plain, tmsglen)) {
117             xfree(plain);
118             *minor_status = code;
119             return(GSS_S_FAILURE);
120          }
121       } else {
122          plain = ptr+22;
123       }
124
125       plainlen = tmsglen;
126
127       if (sealalg && ctx->big_endian)
128          token.length = tmsglen;
129       else
130          token.length = tmsglen - 8 - plain[tmsglen-1];
131
132       if (token.length) {
133          if ((token.value = xmalloc(token.length)) == NULL) {
134             if (sealalg == 0)
135                xfree(plain);
136             *minor_status = ENOMEM;
137             return(GSS_S_FAILURE);
138          }
139
140          if (sealalg && ctx->big_endian)
141             memcpy(token.value, plain, token.length);
142          else
143             memcpy(token.value, plain+8, token.length);
144       }
145    } else if (toktype == KG_TOK_SIGN_MSG) {
146       token = *message_buffer;
147       plain = token.value;
148       plainlen = token.length;
149    } else {
150       token.length = 0;
151       token.value = NULL;
152       plain = token.value;
153       plainlen = token.length;
154    }
155
156    /* compute the checksum of the message */
157
158    if (signalg == 0) {
159       /* compute the checksum of the message */
160
161       MD5Init(&md5);
162       MD5Update(&md5, (unsigned char *) ptr-2, 8);
163       if (ctx->big_endian)
164          MD5Update(&md5, token.value, token.length);
165       else
166          MD5Update(&md5, plain, plainlen);
167       MD5Final(&md5);
168
169       if (sealalg == 0)
170          xfree(plain);
171
172       /* XXX this depends on the key being a single-des key, but that's
173          all that kerberos supports right now */
174
175       if (code = krb5_calculate_checksum(context, CKSUMTYPE_DESCBC, md5.digest,
176                                          16, ctx->seq.key->contents, 
177                                          ctx->seq.key->length,
178                                          &desmac)) {
179          if (toktype == KG_TOK_SEAL_MSG)
180             xfree(token.value);
181          *minor_status = code;
182          return(GSS_S_FAILURE);
183       }
184
185       cksum = desmac.contents;
186    } else {
187       if (! ctx->seed_init) {
188          if (code = kg_make_seed(ctx->subkey, ctx->seed)) {
189             if (sealalg == 0)
190                xfree(plain);
191             if (toktype == KG_TOK_SEAL_MSG)
192                xfree(token.value);
193             *minor_status = code;
194             return(GSS_S_FAILURE);
195          }
196          ctx->seed_init = 1;
197       }
198
199       MD5Init(&md5);
200       MD5Update(&md5, ctx->seed, sizeof(ctx->seed));
201       MD5Update(&md5, (unsigned char *) ptr-2, 8);
202       if (ctx->big_endian)
203          MD5Update(&md5, token.value, token.length);
204       else
205          MD5Update(&md5, plain, plainlen);
206       MD5Final(&md5);
207
208       if (sealalg == 0)
209          xfree(plain);
210
211       cksum = md5.digest;
212    }
213       
214    /* compare the computed checksum against the transmitted checksum */
215
216    if (memcmp(cksum, ptr+14, 8) != 0) {
217       if (signalg == 0)
218          xfree(desmac.contents);
219       if (toktype == KG_TOK_SEAL_MSG)
220          xfree(token.value);
221       *minor_status = 0;
222       return(GSS_S_BAD_SIG);
223    }
224
225    if (signalg == 0)
226       xfree(desmac.contents);
227
228    /* XXX this is where the seq_num check would go */
229    
230    /* it got through unscathed.  Make sure the context is unexpired */
231
232    if (toktype == KG_TOK_SEAL_MSG)
233       *message_buffer = token;
234
235    if (conf_state)
236       *conf_state = (sealalg == 0);
237
238    if (qop_state)
239       *qop_state = GSS_C_QOP_DEFAULT;
240
241    if (code = krb5_timeofday(ctx->context, &now)) {
242       *minor_status = code;
243       return(GSS_S_FAILURE);
244    }
245
246    if (now > ctx->endtime) {
247       *minor_status = 0;
248       return(GSS_S_CONTEXT_EXPIRED);
249    }
250
251    /* success */
252
253    *minor_status = 0;
254    return(GSS_S_COMPLETE);
255 }