1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * token.c
24 *
25 * Copyright (c) 1997, by Sun Microsystems, Inc.
26 * All rights reserved.
27 *
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33
34 #include "dh_gssapi.h"
35 #include "crypto.h"
36
37 extern int
38 get_der_length(unsigned char **, unsigned int, unsigned int *);
39
40 extern unsigned int
41 der_length_size(unsigned int);
42
43 extern int
44 put_der_length(unsigned int, unsigned char **, unsigned int);
45
46 #define MSO_BIT (8*(sizeof (int) - 1)) /* Most significant octet bit */
47
48 static OM_uint32
49 __xdr_encode_token(XDR *, gss_buffer_t, dh_token_t, dh_key_set_t);
50
51 static OM_uint32
52 __xdr_decode_token(XDR *, gss_buffer_t,
53 dh_token_t, dh_key_set_t, dh_signature_t);
54
55 /*
56 * get_qop: For a Diffie-Hellman token_t, return the associate QOP
57 */
58 static dh_qop_t
get_qop(dh_token_t t)59 get_qop(dh_token_t t)
60 {
61 dh_token_body_t body = &t->ver.dh_version_u.body;
62 switch (body->type) {
63 case DH_INIT_CNTX:
64 case DH_ACCEPT_CNTX:
65 return (DH_MECH_QOP);
66 case DH_MIC:
67 return (body->dh_token_body_desc_u.sign.qop);
68 case DH_WRAP:
69 return (body->dh_token_body_desc_u.seal.mic.qop);
70 default:
71 /* Should never get here */
72 return (DH_MECH_QOP);
73 }
74 }
75
76 /*
77 * __make_ap_token: This routine generates a Diffie-Hellman serialized
78 * token which has an ASN.1 application 0 header prepended. The unserialized
79 * token supplied should be of type DH_INIT_CNTX.
80 *
81 * The ASN.1 applicationtion prefix is encoded as follows:
82 *
83 * +------+
84 * | 0x60 | 1 TAG for APPLICATION 0
85 * +------+
86 * | |
87 * ~ ~ app_size DER encoded length of oid_size + token_size
88 * | |
89 * +------+
90 * | 0x06 | 1 TAG for OID
91 * +------+
92 * | | der_length_size
93 * ~ ~ (mech->length) DER encoded length of mech->length
94 * | |
95 * +------+
96 * | |
97 * ~ ~ mech->length OID elements (mech->elements)
98 * | |
99 * +------+
100 * | 0x00 | 0-3 XDR padding
101 * +------+
102 * | |
103 * ~ ~ Serialized DH token
104 * | |
105 * +------+
106 * | 0x00 | 0-3 Left over XDR padding
107 * +------+
108 *
109 * We will define the token_size to be the sizeof the serialize token plus
110 * 3 the maximum XDR paddinging that will be needed. Thus the XDR padding
111 * plus the left over XDR padding will alway equal 3.
112 */
113 OM_uint32
__make_ap_token(gss_buffer_t result,gss_OID mech,dh_token_t token,dh_key_set_t keys)114 __make_ap_token(gss_buffer_t result, /* The serialized token */
115 gss_OID mech, /* The mechanism this is for */
116 dh_token_t token, /* The unserialized input token */
117 dh_key_set_t keys /* The session keys to sign the token */)
118 {
119 unsigned int size, hsize, token_size, app_size, oid_size, start;
120 XDR xdrs;
121 unsigned char *sv, *buf, *xdrmem;
122 OM_uint32 stat;
123
124 /* Allocate the signature for the input token */
125 if ((stat = __alloc_sig(get_qop(token),
126 &token->verifier))
127 != DH_SUCCESS)
128 return (stat);
129
130 /*
131 * We will first determine the size of the output token in
132 * a bottom up fashion.
133 */
134
135 /* Fetch the size of a serialized DH token */
136 token_size = xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)token);
137
138 /*
139 * The token itself needs to be pasted on to the ASN.1
140 * application header on BYTES_PER_XDR_UNIT boundry. So we may
141 * need upto BYTES_PER_XDR_UNIT - 1 extra bytes.
142 */
143 token_size += BYTES_PER_XDR_UNIT -1;
144
145
146 oid_size = mech->length;
147 oid_size += der_length_size(mech->length);
148 oid_size += 1; /* tag x06 for Oid */
149 /* bytes to store the length */
150 app_size = der_length_size(oid_size + token_size);
151
152 hsize = app_size + oid_size;
153 hsize += 1; /* tag 0x60 for application 0 */
154 size = hsize + token_size;
155
156 /* Allocate a buffer to serialize into */
157 buf = New(unsigned char, size);
158 if (buf == NULL) {
159 __free_signature(&token->verifier);
160 return (DH_NOMEM_FAILURE);
161 }
162
163 result->value = sv = buf;
164 result->length = size;
165
166 /* ASN.1 application 0 header */
167
168 /* Encode the tag */
169 *buf++ = 0x60;
170 /* Encode the app length */
171 put_der_length(oid_size + token_size, &buf, app_size);
172
173 /* Encode the OID tag */
174 *buf++ = 0x06;
175 /* Encode the OID length */
176 put_der_length(mech->length, &buf, oid_size);
177 /* Encode the OID elemeents */
178 memcpy(buf, mech->elements, mech->length);
179
180 /* Encode the Diffie-Hellmam token */
181 /*
182 * Token has to be on BYTES_PER_XDR_UNIT boundry. (RNDUP is
183 * from xdr.h)
184 */
185 start = RNDUP(hsize);
186 /* Buffer for xdrmem_create to use */
187 xdrmem = &sv[start];
188
189 xdrmem_create(&xdrs, (caddr_t)xdrmem, token_size, XDR_ENCODE);
190 /* Paste the DH token on */
191 if ((stat = __xdr_encode_token(&xdrs, NULL, token, keys))
192 != DH_SUCCESS) {
193 __free_signature(&token->verifier);
194 __dh_release_buffer(result);
195 }
196
197 /* We're done with the signature, the token has been serialized */
198 __free_signature(&token->verifier);
199
200 return (stat);
201 }
202
203 /*
204 * __make_token: Given an unserialized DH token, serialize it puting the
205 * serialized output in result. If this token has a type of DH_MIC, then
206 * the optional message, msg, should be supplied. The mic caluclated will be
207 * over the message as well as the serialized token.
208 */
209 OM_uint32
__make_token(gss_buffer_t result,gss_buffer_t msg,dh_token_t token,dh_key_set_t keys)210 __make_token(gss_buffer_t result, /* Serialized token goes here */
211 gss_buffer_t msg, /* Optional message for DH_MIC tokens */
212 dh_token_t token, /* The token to encode */
213 dh_key_set_t keys /* The keys to encrypt the check sum with */)
214 {
215 unsigned int token_size;
216 XDR xdrs;
217 unsigned char *buf;
218 OM_uint32 stat;
219
220 /* Allocate a signature for this token */
221 if ((stat = __alloc_sig(get_qop(token),
222 &token->verifier))
223 != DH_SUCCESS)
224 return (stat);
225
226 /* Get the output token size to know how much to allocate */
227 token_size = xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)token);
228
229 /* Allocate the buffer to hold the serialized token */
230 buf = New(unsigned char, token_size);
231 if (buf == NULL) {
232 __free_signature(&token->verifier);
233 return (DH_NOMEM_FAILURE);
234 }
235
236 /* Set the result */
237 result->length = token_size;
238 result->value = (void *)buf;
239
240 /* Create the xdr stream using the allocated buffer */
241 xdrmem_create(&xdrs, (char *)buf, token_size, XDR_ENCODE);
242
243 /* Encode the token */
244 if ((stat = __xdr_encode_token(&xdrs, msg, token, keys))
245 != DH_SUCCESS) {
246 __free_signature(&token->verifier);
247 __dh_release_buffer(result);
248 }
249
250 /* Release the signature */
251 __free_signature(&token->verifier);
252 return (stat);
253 }
254
255 /*
256 * __get_ap_token: This routine deserializes a Diffie-Hellman serialized
257 * token which has an ASN.1 application 0 header prepended. The resulting
258 * unserialized token supplied should be of type DH_INIT_CNTX..
259 *
260 * The ASN.1 applicationtion prefix and token is encoded as follows:
261 *
262 * +------+
263 * | 0x60 | 1 TAG for APPLICATION 0
264 * +------+
265 * | |
266 * ~ ~ app_size DER encoded length of oid_size + token_size
267 * | |
268 * +------+
269 * | 0x06 | 1 TAG for OID
270 * +------+
271 * | | der_length_size
272 * ~ ~ (mech->length) DER encoded length of mech->length
273 * | |
274 * +------+
275 * | |
276 * ~ ~ mech->length OID elements (mech->elements)
277 * | |
278 * +------+
279 * | 0x00 | 0-3 XDR padding
280 * +------+
281 * | |
282 * ~ ~ Serialized DH token
283 * | |
284 * +------+
285 * | 0x00 | 0-3 Left over XDR padding
286 * +------+
287 *
288 * We will define the token_size to be the sizeof the serialize token plus
289 * 3 the maximum XDR paddinging that will be needed. Thus the XDR padding
290 * plus the left over XDR padding will alway equal 3.
291 */
292 OM_uint32
__get_ap_token(gss_buffer_t input,gss_OID mech,dh_token_t token,dh_signature_t sig)293 __get_ap_token(gss_buffer_t input, /* The token to deserialize */
294 gss_OID mech, /* This context's OID */
295 dh_token_t token, /* The resulting token */
296 dh_signature_t sig /* The signature found over the input token */)
297 {
298 unsigned char *buf, *p;
299 unsigned int oid_len, token_len, bytes, hsize;
300 int len;
301 OM_uint32 stat;
302 XDR xdrs;
303
304 /* Set p and buf to point to the beginning of the token */
305 p = buf = (unsigned char *)input->value;
306
307 /* Check that this is an ASN.1 APPLICATION 0 token */
308 if (*p++ != 0x60)
309 return (DH_DECODE_FAILURE);
310
311 /* Determine the length for the DER encoding of the packet length */
312 if ((len = get_der_length(&p, input->length - 1, &bytes)) < 0)
313 return (DH_DECODE_FAILURE);
314
315 /*
316 * See if the number of bytes specified by the
317 * encoded length is all there
318 */
319 if (input->length - 1 - bytes != len)
320 return (DH_DECODE_FAILURE);
321
322 /*
323 * Running total of the APPLICATION 0 prefix so far. One for the
324 * tag (0x60) and the bytes necessary to encode the length of the
325 * packet.
326 */
327 hsize = 1 + bytes;
328
329 /* Check that we're now looking at an OID */
330 if (*p++ != 0x06)
331 return (DH_DECODE_FAILURE);
332
333 /* Get OID length and the number of bytes that to encode it */
334 oid_len = get_der_length(&p, len - 1, &bytes);
335
336 /*
337 * Now add the byte for the OID tag, plus the bytes for the oid
338 * length, plus the oid length its self. That is, add the size
339 * of the encoding of the OID to the running total of the
340 * APPLICATION 0 header. The result is the total size of the header.
341 */
342 hsize += 1 + bytes + oid_len;
343
344 /*
345 * The DH token length is the application length minus the length
346 * of the OID encoding.
347 */
348 token_len = len - 1 - bytes - oid_len;
349
350 /* Sanity check the token length */
351 if (input->length - hsize != token_len)
352 return (DH_DECODE_FAILURE);
353
354 /* Check that this token is for this OID */
355 if (mech->length != oid_len)
356 return (DH_DECODE_FAILURE);
357 if (memcmp(mech->elements, p, oid_len) != 0)
358 return (DH_DECODE_FAILURE);
359
360 /* Round up the header size to XDR boundry */
361 hsize = RNDUP(hsize);
362
363 /* Get the start of XDR encoded token */
364 p = &buf[hsize];
365
366 /* Create and XDR stream to decode from */
367 xdrmem_create(&xdrs, (caddr_t)p, token_len, XDR_DECODE);
368
369 /*
370 * Clear the deserialized token (we'll have the xdr routines
371 * do the the allocations).
372 */
373 memset(token, 0, sizeof (dh_token_desc));
374
375 /* Zero out the signature */
376 memset(sig, 0, sizeof (*sig));
377
378 /*
379 * Decode the DH_INIT_CNTX token. Note that at this point we have no
380 * session keys established, so that keys is null. The unencrypted
381 * signature will be made available to the caller in sig. The
382 * caller can then attempt to decrypt the session keys in token
383 * and encrypt the returned sig with those keys to check the
384 * integrity of the token.
385 */
386 if ((stat = __xdr_decode_token(&xdrs, NULL, token, NULL, sig))
387 != DH_SUCCESS) {
388 xdr_free(xdr_dh_token_desc, (char *)token);
389 return (stat);
390 }
391
392 return (stat);
393 }
394
395 /*
396 * __get_token: Deserialize a supplied Diffie-Hellman token. Note the
397 * session keys should always be supplied to this routine. The message
398 * should only be supplied if the token is of DH_MIC type.
399 */
400 OM_uint32
__get_token(gss_buffer_t input,gss_buffer_t msg,dh_token_t token,dh_key_set_t keys)401 __get_token(gss_buffer_t input, /* The token to deserialize */
402 gss_buffer_t msg, /* Optional message to generate verifier over */
403 dh_token_t token, /* The decode token */
404 dh_key_set_t keys /* The session keys */)
405 {
406 XDR xdrs;
407 dh_signature sig;
408 OM_uint32 stat;
409
410 /* Create a an XDR stream out of the input token */
411 xdrmem_create(&xdrs, (caddr_t)input->value, input->length, XDR_DECODE);
412
413 /* Clear the token_desc and signature. */
414 memset(token, 0, sizeof (dh_token_desc));
415 memset(&sig, 0, sizeof (sig));
416
417 /* Decode the token */
418 if ((stat = __xdr_decode_token(&xdrs, msg, token, keys, &sig))
419 != DH_SUCCESS)
420 /* If we fail release the deserialized token */
421 xdr_free(xdr_dh_token_desc, (char *)token);
422
423 /* We always free the signature */
424 __free_signature(&sig);
425
426 return (stat);
427 }
428
429 /*
430 * Warning these routines assumes that xdrs was created with xdrmem_create!
431 */
432
433 /*
434 * __xdr_encode_token: Given an allocated xdrs stream serialize the supplied
435 * token_desc pointed to by objp, using keys to encrypt the signature. If
436 * msg is non null then calculate the signature over msg as well as the
437 * serialized token. Note this protocol is designed with the signature as
438 * the last part of any token. In this way the signature that is calculated is
439 * always done over the entire token. All fields in any token are thus
440 * protected from tampering
441 */
442 static OM_uint32
__xdr_encode_token(register XDR * xdrs,gss_buffer_t msg,dh_token_desc * objp,dh_key_set_t keys)443 __xdr_encode_token(register XDR *xdrs, gss_buffer_t msg,
444 dh_token_desc *objp, dh_key_set_t keys)
445 {
446 OM_uint32 stat;
447
448 /* Check that xdrs is valid */
449 if (xdrs == 0 || xdrs->x_op != XDR_ENCODE)
450 return (DH_BADARG_FAILURE);
451
452 /* Encode the protocol versioned body */
453 if (!xdr_dh_version(xdrs, &objp->ver))
454 return (DH_ENCODE_FAILURE);
455
456 /* Calculate the signature */
457 stat = __mk_sig(get_qop(objp), xdrs->x_base,
458 xdr_getpos(xdrs), msg, keys,
459 &objp->verifier);
460
461 if (stat != DH_SUCCESS)
462 return (stat);
463
464 /* Encode the signature */
465 if (!xdr_dh_signature(xdrs, &objp->verifier))
466 return (DH_ENCODE_FAILURE);
467
468 return (DH_SUCCESS);
469 }
470
471 /*
472 * __xdr_decode_token: Decode a token from an XDR stream into a token_desc
473 * pointed to by objp. We will calculate a signature over the serialized
474 * token and an optional message. The calculated signature will be
475 * returned to the caller in sig. If the supplied keys are available this
476 * routine will compare that the verifier in the deserialized token is
477 * the same as the calculated signature over the input stream. This is
478 * the usual case. However if the supplied serialized token is DH_INIT_CNTX,
479 * the keys have not yet been established. So we just give the caller back
480 * our raw signature (Non encrypted) and the deserialized token. Higher in
481 * the food chain (currently __dh_gss_accept_sec_context), we will attempt
482 * to decrypt the session keys and call __verify_sig with the decrypted
483 * session keys the signature returned from this routine and the deserialized
484 * token.
485 *
486 * Note it is assumed that sig does point to a valid uninitialized signature.
487 */
488
489 static OM_uint32
__xdr_decode_token(register XDR * xdrs,gss_buffer_t msg,dh_token_desc * objp,dh_key_set_t keys,dh_signature_t sig)490 __xdr_decode_token(register XDR *xdrs, gss_buffer_t msg,
491 dh_token_desc *objp, dh_key_set_t keys, dh_signature_t sig)
492 {
493 OM_uint32 stat;
494
495 /* Check that we are decoding */
496 if (xdrs == 0 || xdrs->x_op != XDR_DECODE)
497 return (DH_BADARG_FAILURE);
498
499 /* Decode the protocol versioned body */
500 if (!xdr_dh_version(xdrs, &objp->ver))
501 return (DH_DECODE_FAILURE);
502
503 /* Allocate the signature for this tokens QOP */
504 if ((stat = __alloc_sig(get_qop(objp), sig)) != DH_SUCCESS)
505 return (stat);
506
507 /*
508 * Call __mk_sig in crypto.c to calculate the signature based on
509 * the decoded QOP. __mk_sig will encrypt the signature with the
510 * supplied keys if they are available. If keys is null the signature
511 * will be just the unencrypted check sum.
512 */
513 stat = __mk_sig(get_qop(objp), xdrs->x_base,
514 xdr_getpos(xdrs), msg, keys, sig);
515 if (stat != DH_SUCCESS)
516 return (stat);
517
518 /* Now decode the supplied signature */
519 if (!xdr_dh_signature(xdrs, &objp->verifier))
520 return (stat);
521
522 /*
523 * If we have keys then we can check that the signatures
524 * are the same
525 */
526 if (keys && !__cmpsig(sig, &objp->verifier))
527 return (DH_VERIFIER_MISMATCH);
528
529 return (DH_SUCCESS);
530 }
531