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