add some documentation
[moonshot.git] / mech_eap / util_token.c
1 /*
2  * Copyright (c) 2011, 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  * Portions Copyright 1993 by OpenVision Technologies, Inc.
34  *
35  * Permission to use, copy, modify, distribute, and sell this software
36  * and its documentation for any purpose is hereby granted without fee,
37  * provided that the above copyright notice appears in all copies and
38  * that both that copyright notice and this permission notice appear in
39  * supporting documentation, and that the name of OpenVision not be used
40  * in advertising or publicity pertaining to distribution of the software
41  * without specific, written prior permission. OpenVision makes no
42  * representations about the suitability of this software for any
43  * purpose.  It is provided "as is" without express or implied warranty.
44  *
45  * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
46  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
47  * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
48  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
49  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
50  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
51  * PERFORMANCE OF THIS SOFTWARE.
52  */
53
54 /*
55  * Utility routines for GSS tokens.
56  */
57
58 #include "gssapiP_eap.h"
59
60 OM_uint32
61 gssEapDecodeInnerTokens(OM_uint32 *minor,
62                         const gss_buffer_t buffer,
63                         gss_buffer_set_t *pExtensions,
64                         OM_uint32 **pTypes)
65 {
66     OM_uint32 major, tmpMinor;
67     gss_buffer_set_t extensions = GSS_C_NO_BUFFER_SET;
68     OM_uint32 *types = NULL;
69     unsigned char *p;
70     size_t remain;
71
72     *pExtensions = GSS_C_NO_BUFFER_SET;
73     *pTypes = NULL;
74
75     major = gss_create_empty_buffer_set(minor, &extensions);
76     if (GSS_ERROR(major))
77         goto cleanup;
78
79     if (buffer->length == 0) {
80         major = GSS_S_COMPLETE;
81         goto cleanup;
82     }
83
84     p = (unsigned char *)buffer->value;
85     remain = buffer->length;
86
87     do {
88         OM_uint32 *ntypes;
89         gss_buffer_desc extension;
90
91         if (remain < 8) {
92             major = GSS_S_DEFECTIVE_TOKEN;
93             *minor = GSSEAP_TOK_TRUNC;
94             goto cleanup;
95         }
96
97         ntypes = GSSEAP_REALLOC(types,
98                                 (extensions->count + 1) * sizeof(OM_uint32));
99         if (ntypes == NULL) {
100             major = GSS_S_FAILURE;
101             *minor = ENOMEM;
102             goto cleanup;
103         }
104         types = ntypes;
105
106         types[extensions->count] = load_uint32_be(&p[0]);
107         extension.length = load_uint32_be(&p[4]);
108
109         if (remain < ITOK_HEADER_LENGTH + extension.length) {
110             major = GSS_S_DEFECTIVE_TOKEN;
111             *minor = GSSEAP_TOK_TRUNC;
112             goto cleanup;
113         }
114         extension.value = &p[8];
115
116         major = gss_add_buffer_set_member(minor, &extension, &extensions);
117         if (GSS_ERROR(major))
118             goto cleanup;
119
120         p      += ITOK_HEADER_LENGTH + extension.length;
121         remain -= ITOK_HEADER_LENGTH + extension.length;
122     } while (remain != 0);
123
124 cleanup:
125     if (GSS_ERROR(major)) {
126         gss_release_buffer_set(&tmpMinor, &extensions);
127         if (types != NULL)
128             GSSEAP_FREE(types);
129     } else {
130         *pExtensions = extensions;
131         *pTypes = types;
132     }
133
134     return major;
135 }
136
137 /*
138  * Add some data to the initiator/acceptor conversation.
139  */
140 static OM_uint32
141 recordTokens(OM_uint32 *minor,
142              gss_ctx_id_t ctx,
143              gss_buffer_t tokens,
144              size_t tokensCount)
145 {
146     unsigned char *buf;
147     size_t i, size, offset;
148
149     size = ctx->conversation.length;
150
151     for (i = 0; i < tokensCount; i++)
152         size += tokens[i].length;
153
154     buf = GSSEAP_REALLOC(ctx->conversation.value, size);
155     if (buf == NULL) {
156         *minor = ENOMEM;
157         return GSS_S_FAILURE;
158     }
159
160     offset = ctx->conversation.length;
161
162     ctx->conversation.length = size;
163     ctx->conversation.value = buf;
164
165     for (i = 0; i < tokensCount; i++) {
166         memcpy(buf + offset, tokens[i].value, tokens[i].length);
167         offset += tokens[i].length;
168     }
169
170     *minor = 0;
171     return GSS_S_COMPLETE;
172 }
173
174 /*
175  * Record the context token header.
176  */
177 OM_uint32
178 gssEapRecordContextTokenHeader(OM_uint32 *minor,
179                                gss_ctx_id_t ctx,
180                                enum gss_eap_token_type tokType)
181 {
182     unsigned char wireOidHeader[2], wireTokType[2];
183     gss_buffer_desc buffers[3];
184
185     assert(ctx->mechanismUsed != GSS_C_NO_OID);
186
187     wireOidHeader[0] = 0x06;
188     wireOidHeader[1] = ctx->mechanismUsed->length;
189     buffers[0].length = sizeof(wireOidHeader);
190     buffers[0].value  = wireOidHeader;
191
192     buffers[1].length = ctx->mechanismUsed->length;
193     buffers[1].value  = ctx->mechanismUsed->elements;
194
195     store_uint16_be(tokType, wireTokType);
196     buffers[2].length = sizeof(wireTokType);
197     buffers[2].value = wireTokType;
198
199     return recordTokens(minor, ctx, buffers, sizeof(buffers)/sizeof(buffers[0]));
200 }
201
202 /*
203  * Record an inner context token.
204  */
205 OM_uint32
206 gssEapRecordInnerContextToken(OM_uint32 *minor,
207                               gss_ctx_id_t ctx,
208                               gss_buffer_t innerToken,
209                               OM_uint32 itokType)
210 {
211     gss_buffer_desc buffers[2];
212     unsigned char itokHeader[ITOK_HEADER_LENGTH];
213
214     assert(innerToken != GSS_C_NO_BUFFER);
215
216     store_uint32_be(itokType,           &itokHeader[0]);
217     store_uint32_be(innerToken->length, &itokHeader[4]);
218     buffers[0].length = sizeof(itokHeader);
219     buffers[0].value  = itokHeader;
220
221     buffers[1] = *innerToken;
222
223     return recordTokens(minor, ctx, buffers, sizeof(buffers)/sizeof(buffers[0]));
224 }
225
226 OM_uint32
227 gssEapVerifyContextToken(OM_uint32 *minor,
228                          gss_ctx_id_t ctx,
229                          const gss_buffer_t inputToken,
230                          enum gss_eap_token_type tokType,
231                          gss_buffer_t innerInputToken)
232 {
233     OM_uint32 major;
234     size_t bodySize;
235     unsigned char *p = (unsigned char *)inputToken->value;
236     gss_OID_desc oidBuf;
237     gss_OID oid;
238     enum gss_eap_token_type actualTokType;
239     gss_buffer_desc tokenBuf;
240
241     if (ctx->mechanismUsed != GSS_C_NO_OID) {
242         oid = ctx->mechanismUsed;
243     } else {
244         oidBuf.elements = NULL;
245         oidBuf.length = 0;
246         oid = &oidBuf;
247     }
248
249     major = verifyTokenHeader(minor, oid, &bodySize, &p,
250                               inputToken->length, &actualTokType);
251     if (GSS_ERROR(major))
252         return major;
253
254     if (actualTokType != tokType) {
255         *minor = GSSEAP_WRONG_TOK_ID;
256         return GSS_S_DEFECTIVE_TOKEN;
257     }
258
259     if (ctx->mechanismUsed == GSS_C_NO_OID) {
260         if (!gssEapIsConcreteMechanismOid(oid)) {
261             *minor = GSSEAP_WRONG_MECH;
262             return GSS_S_BAD_MECH;
263         }
264
265         if (!gssEapInternalizeOid(oid, &ctx->mechanismUsed)) {
266             major = duplicateOid(minor, oid, &ctx->mechanismUsed);
267             if (GSS_ERROR(major))
268                 return major;
269         }
270     }
271
272     innerInputToken->length = bodySize;
273     innerInputToken->value = p;
274
275     /*
276      * Add OID, tokenType, body to conversation; variable length
277      * header omitted. A better API to verifyTokenHeader would
278      * avoid this ugly pointer arithmetic. XXX FIXME
279      */
280     tokenBuf.value = p - (2 + oid->length + 2);
281     tokenBuf.length = 2 + oid->length + 2 + bodySize;
282
283     major = recordTokens(minor, ctx, &tokenBuf, 1);
284     if (GSS_ERROR(major))
285         return major;
286
287     *minor = 0;
288     return GSS_S_COMPLETE;
289 }
290
291 OM_uint32
292 gssEapEncodeSupportedExts(OM_uint32 *minor,
293                           OM_uint32 *types,
294                           size_t typesCount,
295                           gss_buffer_t outputToken)
296 {
297     size_t i;
298     unsigned char *p;
299
300     outputToken->value = GSSEAP_MALLOC(4 * typesCount);
301     if (outputToken->value == NULL) {
302         *minor = ENOMEM;
303         return GSS_S_FAILURE;
304     }
305     p = (unsigned char *)outputToken->value;
306
307     outputToken->length = 4 * typesCount;
308
309     for (i = 0; i < typesCount; i++) {
310         store_uint32_be(types[i], p);
311         p += 4;
312     }
313
314     *minor = 0;
315     return GSS_S_COMPLETE;
316 }
317
318 OM_uint32
319 gssEapProcessSupportedExts(OM_uint32 *minor,
320                            gss_buffer_t inputToken,
321                            struct gss_eap_itok_map *map,
322                            size_t mapCount,
323                            OM_uint32 *flags)
324 {
325     size_t i;
326     unsigned char *p;
327
328     if ((inputToken->length % 4) != 0) {
329         *minor = GSSEAP_TOK_TRUNC;
330         return GSS_S_DEFECTIVE_TOKEN;
331     }
332
333     p = (unsigned char *)inputToken->value;
334
335     for (i = 0; i < inputToken->length / 4; i++) {
336         OM_uint32 type = load_uint32_be(p);
337         size_t j;
338
339         for (j = 0; j < mapCount; j++) {
340             if (map->type == type) {
341                 *flags |= map->flag;
342                 break;
343             }
344         }
345
346         p += 4;
347     }
348
349     *minor = 0;
350     return GSS_S_COMPLETE;
351 }
352
353 /*
354  * $Id: util_token.c 23457 2009-12-08 00:04:48Z tlyu $
355  */
356
357 /* XXXX this code currently makes the assumption that a mech oid will
358    never be longer than 127 bytes.  This assumption is not inherent in
359    the interfaces, so the code can be fixed if the OSI namespace
360    balloons unexpectedly. */
361
362 /*
363  * Each token looks like this:
364  * 0x60                 tag for APPLICATION 0, SEQUENCE
365  *                              (constructed, definite-length)
366  * <length>             possible multiple bytes, need to parse/generate
367  * 0x06                 tag for OBJECT IDENTIFIER
368  * <moid_length>        compile-time constant string (assume 1 byte)
369  * <moid_bytes>         compile-time constant string
370  * <inner_bytes>        the ANY containing the application token
371  * bytes 0,1 are the token type
372  * bytes 2,n are the token data
373  *
374  * Note that the token type field is a feature of RFC 1964 mechanisms and
375  * is not used by other GSSAPI mechanisms.  As such, a token type of -1
376  * is interpreted to mean that no token type should be expected or
377  * generated.
378  *
379  * For the purposes of this abstraction, the token "header" consists of
380  * the sequence tag and length octets, the mech OID DER encoding, and the
381  * first two inner bytes, which indicate the token type.  The token
382  * "body" consists of everything else.
383  */
384
385 static size_t
386 der_length_size(size_t length)
387 {
388     if (length < (1<<7))
389         return 1;
390     else if (length < (1<<8))
391         return 2;
392 #if INT_MAX == 0x7fff
393     else
394         return 3;
395 #else
396     else if (length < (1<<16))
397         return 3;
398     else if (length < (1<<24))
399         return 4;
400     else
401         return 5;
402 #endif
403 }
404
405 static void
406 der_write_length(unsigned char **buf, size_t length)
407 {
408     if (length < (1<<7)) {
409         *(*buf)++ = (unsigned char)length;
410     } else {
411         *(*buf)++ = (unsigned char)(der_length_size(length)+127);
412 #if INT_MAX > 0x7fff
413         if (length >= (1<<24))
414             *(*buf)++ = (unsigned char)(length>>24);
415         if (length >= (1<<16))
416             *(*buf)++ = (unsigned char)((length>>16)&0xff);
417 #endif
418         if (length >= (1<<8))
419             *(*buf)++ = (unsigned char)((length>>8)&0xff);
420         *(*buf)++ = (unsigned char)(length&0xff);
421     }
422 }
423
424 /* returns decoded length, or < 0 on failure.  Advances buf and
425    decrements bufsize */
426
427 static int
428 der_read_length(unsigned char **buf, ssize_t *bufsize)
429 {
430     unsigned char sf;
431     int ret;
432
433     if (*bufsize < 1)
434         return -1;
435
436     sf = *(*buf)++;
437     (*bufsize)--;
438     if (sf & 0x80) {
439         if ((sf &= 0x7f) > ((*bufsize)-1))
440             return -1;
441         if (sf > sizeof(int))
442             return -1;
443         ret = 0;
444         for (; sf; sf--) {
445             ret = (ret<<8) + (*(*buf)++);
446             (*bufsize)--;
447         }
448     } else {
449         ret = sf;
450     }
451
452     return ret;
453 }
454
455 /* returns the length of a token, given the mech oid and the body size */
456
457 size_t
458 tokenSize(size_t body_size)
459 {
460     return 1 + der_length_size(body_size) + body_size;
461 }
462
463 /* fills in a buffer with the token header.  The buffer is assumed to
464    be the right size.  buf is advanced past the token header */
465
466 void
467 makeTokenHeader(
468     size_t body_size,
469     unsigned char **buf)
470 {
471     *(*buf)++ = 0x60;
472     der_write_length(buf, body_size);
473 }
474
475 /*
476  * Given a buffer containing a token, reads and verifies the token,
477  * leaving buf advanced past the token header, and setting body_size
478  * to the number of remaining bytes.  Returns 0 on success,
479  * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the
480  * mechanism in the token does not match the mech argument.  buf and
481  * *body_size are left unmodified on error.
482  */
483
484 OM_uint32
485 verifyTokenHeader(OM_uint32 *minor,
486                   gss_OID mech,
487                   size_t *body_size,
488                   unsigned char **buf_in,
489                   size_t toksize_in,
490                   enum gss_eap_token_type *ret_tok_type)
491 {
492     unsigned char *buf = *buf_in;
493     ssize_t seqsize;
494     gss_OID_desc toid;
495     ssize_t toksize = (ssize_t)toksize_in;
496
497     *minor = GSSEAP_BAD_TOK_HEADER;
498
499     if (ret_tok_type != NULL)
500         *ret_tok_type = TOK_TYPE_NONE;
501
502     if ((toksize -= 1) < 0)
503         return GSS_S_DEFECTIVE_TOKEN;
504
505     if (*buf++ != 0x60)
506         return GSS_S_DEFECTIVE_TOKEN;
507
508     seqsize = der_read_length(&buf, &toksize);
509     if (seqsize < 0)
510         return GSS_S_DEFECTIVE_TOKEN;
511
512     if (seqsize != toksize)
513         return GSS_S_DEFECTIVE_TOKEN;
514
515     if ((toksize -= 1) < 0)
516         return GSS_S_DEFECTIVE_TOKEN;
517
518     if (*buf++ != 0x06)
519         return GSS_S_DEFECTIVE_TOKEN;
520
521     if ((toksize -= 1) < 0)
522         return GSS_S_DEFECTIVE_TOKEN;
523
524     toid.length = *buf++;
525
526     if ((toksize -= toid.length) < 0)
527         return GSS_S_DEFECTIVE_TOKEN;
528
529     toid.elements = buf;
530     buf += toid.length;
531
532     if (mech->elements == NULL) {
533         *mech = toid;
534         if (toid.length == 0)
535             return GSS_S_BAD_MECH;
536     } else if (!oidEqual(&toid, mech)) {
537         *minor = GSSEAP_WRONG_MECH;
538         return GSS_S_BAD_MECH;
539     }
540
541     if (ret_tok_type != NULL) {
542         if ((toksize -= 2) < 0)
543             return GSS_S_DEFECTIVE_TOKEN;
544
545         *ret_tok_type = load_uint16_be(buf);
546         buf += 2;
547     }
548
549     *buf_in = buf;
550     *body_size = toksize;
551
552     *minor = 0;
553     return GSS_S_COMPLETE;
554 }