2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License, Version 1.0 only
2N/A * (the "License"). You may not use this file except in compliance
2N/A * with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * token.c
2N/A *
2N/A * Copyright (c) 1997, by Sun Microsystems, Inc.
2N/A * All rights reserved.
2N/A *
2N/A */
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <string.h>
2N/A
2N/A#include "dh_gssapi.h"
2N/A#include "crypto.h"
2N/A
2N/Aextern int
2N/Aget_der_length(unsigned char **, unsigned int, unsigned int *);
2N/A
2N/Aextern unsigned int
2N/Ader_length_size(unsigned int);
2N/A
2N/Aextern int
2N/Aput_der_length(unsigned int, unsigned char **, unsigned int);
2N/A
2N/A#define MSO_BIT (8*(sizeof (int) - 1)) /* Most significant octet bit */
2N/A
2N/Astatic OM_uint32
2N/A__xdr_encode_token(XDR *, gss_buffer_t, dh_token_t, dh_key_set_t);
2N/A
2N/Astatic OM_uint32
2N/A__xdr_decode_token(XDR *, gss_buffer_t,
2N/A dh_token_t, dh_key_set_t, dh_signature_t);
2N/A
2N/A/*
2N/A * get_qop: For a Diffie-Hellman token_t, return the associate QOP
2N/A */
2N/Astatic dh_qop_t
2N/Aget_qop(dh_token_t t)
2N/A{
2N/A dh_token_body_t body = &t->ver.dh_version_u.body;
2N/A switch (body->type) {
2N/A case DH_INIT_CNTX:
2N/A case DH_ACCEPT_CNTX:
2N/A return (DH_MECH_QOP);
2N/A case DH_MIC:
2N/A return (body->dh_token_body_desc_u.sign.qop);
2N/A case DH_WRAP:
2N/A return (body->dh_token_body_desc_u.seal.mic.qop);
2N/A default:
2N/A /* Should never get here */
2N/A return (DH_MECH_QOP);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * __make_ap_token: This routine generates a Diffie-Hellman serialized
2N/A * token which has an ASN.1 application 0 header prepended. The unserialized
2N/A * token supplied should be of type DH_INIT_CNTX.
2N/A *
2N/A * The ASN.1 applicationtion prefix is encoded as follows:
2N/A *
2N/A * +------+
2N/A * | 0x60 | 1 TAG for APPLICATION 0
2N/A * +------+
2N/A * | |
2N/A * ~ ~ app_size DER encoded length of oid_size + token_size
2N/A * | |
2N/A * +------+
2N/A * | 0x06 | 1 TAG for OID
2N/A * +------+
2N/A * | | der_length_size
2N/A * ~ ~ (mech->length) DER encoded length of mech->length
2N/A * | |
2N/A * +------+
2N/A * | |
2N/A * ~ ~ mech->length OID elements (mech->elements)
2N/A * | |
2N/A * +------+
2N/A * | 0x00 | 0-3 XDR padding
2N/A * +------+
2N/A * | |
2N/A * ~ ~ Serialized DH token
2N/A * | |
2N/A * +------+
2N/A * | 0x00 | 0-3 Left over XDR padding
2N/A * +------+
2N/A *
2N/A * We will define the token_size to be the sizeof the serialize token plus
2N/A * 3 the maximum XDR paddinging that will be needed. Thus the XDR padding
2N/A * plus the left over XDR padding will alway equal 3.
2N/A */
2N/AOM_uint32
2N/A__make_ap_token(gss_buffer_t result, /* The serialized token */
2N/A gss_OID mech, /* The mechanism this is for */
2N/A dh_token_t token, /* The unserialized input token */
2N/A dh_key_set_t keys /* The session keys to sign the token */)
2N/A{
2N/A unsigned int size, hsize, token_size, app_size, oid_size, start;
2N/A XDR xdrs;
2N/A unsigned char *sv, *buf, *xdrmem;
2N/A OM_uint32 stat;
2N/A
2N/A /* Allocate the signature for the input token */
2N/A if ((stat = __alloc_sig(get_qop(token),
2N/A &token->verifier))
2N/A != DH_SUCCESS)
2N/A return (stat);
2N/A
2N/A /*
2N/A * We will first determine the size of the output token in
2N/A * a bottom up fashion.
2N/A */
2N/A
2N/A /* Fetch the size of a serialized DH token */
2N/A token_size = xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)token);
2N/A
2N/A /*
2N/A * The token itself needs to be pasted on to the ASN.1
2N/A * application header on BYTES_PER_XDR_UNIT boundry. So we may
2N/A * need upto BYTES_PER_XDR_UNIT - 1 extra bytes.
2N/A */
2N/A token_size += BYTES_PER_XDR_UNIT -1;
2N/A
2N/A
2N/A oid_size = mech->length;
2N/A oid_size += der_length_size(mech->length);
2N/A oid_size += 1; /* tag x06 for Oid */
2N/A /* bytes to store the length */
2N/A app_size = der_length_size(oid_size + token_size);
2N/A
2N/A hsize = app_size + oid_size;
2N/A hsize += 1; /* tag 0x60 for application 0 */
2N/A size = hsize + token_size;
2N/A
2N/A /* Allocate a buffer to serialize into */
2N/A buf = New(unsigned char, size);
2N/A if (buf == NULL) {
2N/A __free_signature(&token->verifier);
2N/A return (DH_NOMEM_FAILURE);
2N/A }
2N/A
2N/A result->value = sv = buf;
2N/A result->length = size;
2N/A
2N/A /* ASN.1 application 0 header */
2N/A
2N/A /* Encode the tag */
2N/A *buf++ = 0x60;
2N/A /* Encode the app length */
2N/A put_der_length(oid_size + token_size, &buf, app_size);
2N/A
2N/A /* Encode the OID tag */
2N/A *buf++ = 0x06;
2N/A /* Encode the OID length */
2N/A put_der_length(mech->length, &buf, oid_size);
2N/A /* Encode the OID elemeents */
2N/A memcpy(buf, mech->elements, mech->length);
2N/A
2N/A /* Encode the Diffie-Hellmam token */
2N/A /*
2N/A * Token has to be on BYTES_PER_XDR_UNIT boundry. (RNDUP is
2N/A * from xdr.h)
2N/A */
2N/A start = RNDUP(hsize);
2N/A /* Buffer for xdrmem_create to use */
2N/A xdrmem = &sv[start];
2N/A
2N/A xdrmem_create(&xdrs, (caddr_t)xdrmem, token_size, XDR_ENCODE);
2N/A /* Paste the DH token on */
2N/A if ((stat = __xdr_encode_token(&xdrs, NULL, token, keys))
2N/A != DH_SUCCESS) {
2N/A __free_signature(&token->verifier);
2N/A __dh_release_buffer(result);
2N/A }
2N/A
2N/A /* We're done with the signature, the token has been serialized */
2N/A __free_signature(&token->verifier);
2N/A
2N/A return (stat);
2N/A}
2N/A
2N/A/*
2N/A * __make_token: Given an unserialized DH token, serialize it puting the
2N/A * serialized output in result. If this token has a type of DH_MIC, then
2N/A * the optional message, msg, should be supplied. The mic caluclated will be
2N/A * over the message as well as the serialized token.
2N/A */
2N/AOM_uint32
2N/A__make_token(gss_buffer_t result, /* Serialized token goes here */
2N/A gss_buffer_t msg, /* Optional message for DH_MIC tokens */
2N/A dh_token_t token, /* The token to encode */
2N/A dh_key_set_t keys /* The keys to encrypt the check sum with */)
2N/A{
2N/A unsigned int token_size;
2N/A XDR xdrs;
2N/A unsigned char *buf;
2N/A OM_uint32 stat;
2N/A
2N/A /* Allocate a signature for this token */
2N/A if ((stat = __alloc_sig(get_qop(token),
2N/A &token->verifier))
2N/A != DH_SUCCESS)
2N/A return (stat);
2N/A
2N/A /* Get the output token size to know how much to allocate */
2N/A token_size = xdr_sizeof((xdrproc_t)xdr_dh_token_desc, (void *)token);
2N/A
2N/A /* Allocate the buffer to hold the serialized token */
2N/A buf = New(unsigned char, token_size);
2N/A if (buf == NULL) {
2N/A __free_signature(&token->verifier);
2N/A return (DH_NOMEM_FAILURE);
2N/A }
2N/A
2N/A /* Set the result */
2N/A result->length = token_size;
2N/A result->value = (void *)buf;
2N/A
2N/A /* Create the xdr stream using the allocated buffer */
2N/A xdrmem_create(&xdrs, (char *)buf, token_size, XDR_ENCODE);
2N/A
2N/A /* Encode the token */
2N/A if ((stat = __xdr_encode_token(&xdrs, msg, token, keys))
2N/A != DH_SUCCESS) {
2N/A __free_signature(&token->verifier);
2N/A __dh_release_buffer(result);
2N/A }
2N/A
2N/A /* Release the signature */
2N/A __free_signature(&token->verifier);
2N/A return (stat);
2N/A}
2N/A
2N/A/*
2N/A * __get_ap_token: This routine deserializes a Diffie-Hellman serialized
2N/A * token which has an ASN.1 application 0 header prepended. The resulting
2N/A * unserialized token supplied should be of type DH_INIT_CNTX..
2N/A *
2N/A * The ASN.1 applicationtion prefix and token is encoded as follows:
2N/A *
2N/A * +------+
2N/A * | 0x60 | 1 TAG for APPLICATION 0
2N/A * +------+
2N/A * | |
2N/A * ~ ~ app_size DER encoded length of oid_size + token_size
2N/A * | |
2N/A * +------+
2N/A * | 0x06 | 1 TAG for OID
2N/A * +------+
2N/A * | | der_length_size
2N/A * ~ ~ (mech->length) DER encoded length of mech->length
2N/A * | |
2N/A * +------+
2N/A * | |
2N/A * ~ ~ mech->length OID elements (mech->elements)
2N/A * | |
2N/A * +------+
2N/A * | 0x00 | 0-3 XDR padding
2N/A * +------+
2N/A * | |
2N/A * ~ ~ Serialized DH token
2N/A * | |
2N/A * +------+
2N/A * | 0x00 | 0-3 Left over XDR padding
2N/A * +------+
2N/A *
2N/A * We will define the token_size to be the sizeof the serialize token plus
2N/A * 3 the maximum XDR paddinging that will be needed. Thus the XDR padding
2N/A * plus the left over XDR padding will alway equal 3.
2N/A */
2N/AOM_uint32
2N/A__get_ap_token(gss_buffer_t input, /* The token to deserialize */
2N/A gss_OID mech, /* This context's OID */
2N/A dh_token_t token, /* The resulting token */
2N/A dh_signature_t sig /* The signature found over the input token */)
2N/A{
2N/A unsigned char *buf, *p;
2N/A unsigned int oid_len, token_len, bytes, hsize;
2N/A int len;
2N/A OM_uint32 stat;
2N/A XDR xdrs;
2N/A
2N/A /* Set p and buf to point to the beginning of the token */
2N/A p = buf = (unsigned char *)input->value;
2N/A
2N/A /* Check that this is an ASN.1 APPLICATION 0 token */
2N/A if (*p++ != 0x60)
2N/A return (DH_DECODE_FAILURE);
2N/A
2N/A /* Determine the length for the DER encoding of the packet length */
2N/A if ((len = get_der_length(&p, input->length - 1, &bytes)) < 0)
2N/A return (DH_DECODE_FAILURE);
2N/A
2N/A /*
2N/A * See if the number of bytes specified by the
2N/A * encoded length is all there
2N/A */
2N/A if (input->length - 1 - bytes != len)
2N/A return (DH_DECODE_FAILURE);
2N/A
2N/A /*
2N/A * Running total of the APPLICATION 0 prefix so far. One for the
2N/A * tag (0x60) and the bytes necessary to encode the length of the
2N/A * packet.
2N/A */
2N/A hsize = 1 + bytes;
2N/A
2N/A /* Check that we're now looking at an OID */
2N/A if (*p++ != 0x06)
2N/A return (DH_DECODE_FAILURE);
2N/A
2N/A /* Get OID length and the number of bytes that to encode it */
2N/A oid_len = get_der_length(&p, len - 1, &bytes);
2N/A
2N/A /*
2N/A * Now add the byte for the OID tag, plus the bytes for the oid
2N/A * length, plus the oid length its self. That is, add the size
2N/A * of the encoding of the OID to the running total of the
2N/A * APPLICATION 0 header. The result is the total size of the header.
2N/A */
2N/A hsize += 1 + bytes + oid_len;
2N/A
2N/A /*
2N/A * The DH token length is the application length minus the length
2N/A * of the OID encoding.
2N/A */
2N/A token_len = len - 1 - bytes - oid_len;
2N/A
2N/A /* Sanity check the token length */
2N/A if (input->length - hsize != token_len)
2N/A return (DH_DECODE_FAILURE);
2N/A
2N/A /* Check that this token is for this OID */
2N/A if (mech->length != oid_len)
2N/A return (DH_DECODE_FAILURE);
2N/A if (memcmp(mech->elements, p, oid_len) != 0)
2N/A return (DH_DECODE_FAILURE);
2N/A
2N/A /* Round up the header size to XDR boundry */
2N/A hsize = RNDUP(hsize);
2N/A
2N/A /* Get the start of XDR encoded token */
2N/A p = &buf[hsize];
2N/A
2N/A /* Create and XDR stream to decode from */
2N/A xdrmem_create(&xdrs, (caddr_t)p, token_len, XDR_DECODE);
2N/A
2N/A /*
2N/A * Clear the deserialized token (we'll have the xdr routines
2N/A * do the the allocations).
2N/A */
2N/A memset(token, 0, sizeof (dh_token_desc));
2N/A
2N/A /* Zero out the signature */
2N/A memset(sig, 0, sizeof (*sig));
2N/A
2N/A /*
2N/A * Decode the DH_INIT_CNTX token. Note that at this point we have no
2N/A * session keys established, so that keys is null. The unencrypted
2N/A * signature will be made available to the caller in sig. The
2N/A * caller can then attempt to decrypt the session keys in token
2N/A * and encrypt the returned sig with those keys to check the
2N/A * integrity of the token.
2N/A */
2N/A if ((stat = __xdr_decode_token(&xdrs, NULL, token, NULL, sig))
2N/A != DH_SUCCESS) {
2N/A xdr_free(xdr_dh_token_desc, (char *)token);
2N/A return (stat);
2N/A }
2N/A
2N/A return (stat);
2N/A}
2N/A
2N/A/*
2N/A * __get_token: Deserialize a supplied Diffie-Hellman token. Note the
2N/A * session keys should always be supplied to this routine. The message
2N/A * should only be supplied if the token is of DH_MIC type.
2N/A */
2N/AOM_uint32
2N/A__get_token(gss_buffer_t input, /* The token to deserialize */
2N/A gss_buffer_t msg, /* Optional message to generate verifier over */
2N/A dh_token_t token, /* The decode token */
2N/A dh_key_set_t keys /* The session keys */)
2N/A{
2N/A XDR xdrs;
2N/A dh_signature sig;
2N/A OM_uint32 stat;
2N/A
2N/A /* Create a an XDR stream out of the input token */
2N/A xdrmem_create(&xdrs, (caddr_t)input->value, input->length, XDR_DECODE);
2N/A
2N/A /* Clear the token_desc and signature. */
2N/A memset(token, 0, sizeof (dh_token_desc));
2N/A memset(&sig, 0, sizeof (sig));
2N/A
2N/A /* Decode the token */
2N/A if ((stat = __xdr_decode_token(&xdrs, msg, token, keys, &sig))
2N/A != DH_SUCCESS)
2N/A /* If we fail release the deserialized token */
2N/A xdr_free(xdr_dh_token_desc, (char *)token);
2N/A
2N/A /* We always free the signature */
2N/A __free_signature(&sig);
2N/A
2N/A return (stat);
2N/A}
2N/A
2N/A/*
2N/A * Warning these routines assumes that xdrs was created with xdrmem_create!
2N/A */
2N/A
2N/A/*
2N/A * __xdr_encode_token: Given an allocated xdrs stream serialize the supplied
2N/A * token_desc pointed to by objp, using keys to encrypt the signature. If
2N/A * msg is non null then calculate the signature over msg as well as the
2N/A * serialized token. Note this protocol is designed with the signature as
2N/A * the last part of any token. In this way the signature that is calculated is
2N/A * always done over the entire token. All fields in any token are thus
2N/A * protected from tampering
2N/A */
2N/Astatic OM_uint32
2N/A__xdr_encode_token(register XDR *xdrs, gss_buffer_t msg,
2N/A dh_token_desc *objp, dh_key_set_t keys)
2N/A{
2N/A OM_uint32 stat;
2N/A
2N/A /* Check that xdrs is valid */
2N/A if (xdrs == 0 || xdrs->x_op != XDR_ENCODE)
2N/A return (DH_BADARG_FAILURE);
2N/A
2N/A /* Encode the protocol versioned body */
2N/A if (!xdr_dh_version(xdrs, &objp->ver))
2N/A return (DH_ENCODE_FAILURE);
2N/A
2N/A /* Calculate the signature */
2N/A stat = __mk_sig(get_qop(objp), xdrs->x_base,
2N/A xdr_getpos(xdrs), msg, keys,
2N/A &objp->verifier);
2N/A
2N/A if (stat != DH_SUCCESS)
2N/A return (stat);
2N/A
2N/A /* Encode the signature */
2N/A if (!xdr_dh_signature(xdrs, &objp->verifier))
2N/A return (DH_ENCODE_FAILURE);
2N/A
2N/A return (DH_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * __xdr_decode_token: Decode a token from an XDR stream into a token_desc
2N/A * pointed to by objp. We will calculate a signature over the serialized
2N/A * token and an optional message. The calculated signature will be
2N/A * returned to the caller in sig. If the supplied keys are available this
2N/A * routine will compare that the verifier in the deserialized token is
2N/A * the same as the calculated signature over the input stream. This is
2N/A * the usual case. However if the supplied serialized token is DH_INIT_CNTX,
2N/A * the keys have not yet been established. So we just give the caller back
2N/A * our raw signature (Non encrypted) and the deserialized token. Higher in
2N/A * the food chain (currently __dh_gss_accept_sec_context), we will attempt
2N/A * to decrypt the session keys and call __verify_sig with the decrypted
2N/A * session keys the signature returned from this routine and the deserialized
2N/A * token.
2N/A *
2N/A * Note it is assumed that sig does point to a valid uninitialized signature.
2N/A */
2N/A
2N/Astatic OM_uint32
2N/A__xdr_decode_token(register XDR *xdrs, gss_buffer_t msg,
2N/A dh_token_desc *objp, dh_key_set_t keys, dh_signature_t sig)
2N/A{
2N/A OM_uint32 stat;
2N/A
2N/A /* Check that we are decoding */
2N/A if (xdrs == 0 || xdrs->x_op != XDR_DECODE)
2N/A return (DH_BADARG_FAILURE);
2N/A
2N/A /* Decode the protocol versioned body */
2N/A if (!xdr_dh_version(xdrs, &objp->ver))
2N/A return (DH_DECODE_FAILURE);
2N/A
2N/A /* Allocate the signature for this tokens QOP */
2N/A if ((stat = __alloc_sig(get_qop(objp), sig)) != DH_SUCCESS)
2N/A return (stat);
2N/A
2N/A /*
2N/A * Call __mk_sig in crypto.c to calculate the signature based on
2N/A * the decoded QOP. __mk_sig will encrypt the signature with the
2N/A * supplied keys if they are available. If keys is null the signature
2N/A * will be just the unencrypted check sum.
2N/A */
2N/A stat = __mk_sig(get_qop(objp), xdrs->x_base,
2N/A xdr_getpos(xdrs), msg, keys, sig);
2N/A if (stat != DH_SUCCESS)
2N/A return (stat);
2N/A
2N/A /* Now decode the supplied signature */
2N/A if (!xdr_dh_signature(xdrs, &objp->verifier))
2N/A return (stat);
2N/A
2N/A /*
2N/A * If we have keys then we can check that the signatures
2N/A * are the same
2N/A */
2N/A if (keys && !__cmpsig(sig, &objp->verifier))
2N/A return (DH_VERIFIER_MISMATCH);
2N/A
2N/A return (DH_SUCCESS);
2N/A}