#include "gssapiP_eap.h"
+static OM_uint32
+gssEapExportPartialContext(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_buffer_t token)
+{
+ token->length = 0;
+ token->value = NULL;
+
+ /*
+ * The format of this token awaits definition by libradsec.
+ */
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+gssEapExportSecContext(OM_uint32 *minor,
+ gss_ctx_id_t ctx,
+ gss_buffer_t token)
+{
+ OM_uint32 major, tmpMinor;
+ size_t length;
+ gss_buffer_desc initiatorName, acceptorName;
+ gss_buffer_desc partialCtx, key;
+ unsigned char *p;
+
+ initiatorName.length = 0;
+ initiatorName.value = NULL;
+
+ acceptorName.length = 0;
+ acceptorName.value = NULL;
+
+ partialCtx.length = 0;
+ partialCtx.value = NULL;
+
+ if ((CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) ||
+ ctx->mechanismUsed == GSS_C_NO_OID)
+ return GSS_S_NO_CONTEXT;
+
+ key.length = KRB_KEY_LENGTH(&ctx->rfc3961Key);
+ key.value = KRB_KEY_DATA(&ctx->rfc3961Key);
+
+ if (ctx->initiatorName != GSS_C_NO_NAME) {
+ major = gssEapExportName(minor, ctx->initiatorName, &initiatorName, TRUE);
+ if (GSS_ERROR(major))
+ goto cleanup;
+ }
+ if (ctx->acceptorName != GSS_C_NO_NAME) {
+ major = gssEapExportName(minor, ctx->acceptorName, &acceptorName, TRUE);
+ if (GSS_ERROR(major))
+ goto cleanup;
+ }
+
+ /*
+ * The partial context is only transmitted for unestablished acceptor
+ * contexts.
+ */
+ if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) {
+ major = gssEapExportPartialContext(minor, ctx, &partialCtx);
+ if (GSS_ERROR(major))
+ goto cleanup;
+ }
+
+ length = 16; /* version, state, flags, etc */
+ length += 4 + ctx->mechanismUsed->length; /* mechanismUsed */
+ length += 8 + key.length; /* rfc3961Key.value */
+ length += 4 + initiatorName.length; /* initiatorName.value */
+ length += 4 + acceptorName.length; /* acceptorName.value */
+ length += 24 + sequenceSize(ctx->seqState); /* seqState */
+
+ if (partialCtx.value != NULL)
+ length += 4 + partialCtx.length; /* partialCtx.value */
+
+ token->value = GSSEAP_MALLOC(length);
+ if (token->value == NULL) {
+ *minor = ENOMEM;
+ major = GSS_S_FAILURE;
+ goto cleanup;
+ }
+ token->length = length;
+
+ p = (unsigned char *)token->value;
+
+ store_uint32_be(EAP_EXPORT_CONTEXT_V1, &p[0]); /* version */
+ store_uint32_be(ctx->state, &p[4]);
+ store_uint32_be(ctx->flags, &p[8]);
+ store_uint32_be(ctx->gssFlags, &p[12]);
+ p = store_oid(ctx->mechanismUsed, &p[16]);
+
+ store_uint32_be(ctx->encryptionType, &p[0]);
+ p = store_buffer(&key, &p[4], FALSE);
+
+ p = store_buffer(&initiatorName, p, FALSE);
+ p = store_buffer(&acceptorName, p, FALSE);
+
+ store_uint64_be(ctx->expiryTime, &p[0]);
+ store_uint64_be(ctx->sendSeq, &p[8]);
+ store_uint64_be(ctx->recvSeq, &p[16]);
+ p += 24;
+ sequenceExternalize(ctx->seqState, &p, &length);
+
+ if (partialCtx.value != NULL)
+ p = store_buffer(&partialCtx, p, FALSE);
+
+ assert(p == (unsigned char *)token->value + token->length);
+
+ major = GSS_S_COMPLETE;
+ *minor = 0;
+
+cleanup:
+ if (GSS_ERROR(major))
+ gss_release_buffer(&tmpMinor, token);
+ gss_release_buffer(&tmpMinor, &initiatorName);
+ gss_release_buffer(&tmpMinor, &acceptorName);
+ gss_release_buffer(&tmpMinor, &partialCtx);
+
+ return major;
+}
+
OM_uint32
gss_export_sec_context(OM_uint32 *minor,
gss_ctx_id_t *context_handle,
gss_buffer_t interprocess_token)
{
- GSSEAP_NOT_IMPLEMENTED;
+ OM_uint32 major, tmpMinor;
+ gss_ctx_id_t ctx = *context_handle;
+
+ if (ctx == GSS_C_NO_CONTEXT)
+ return GSS_S_NO_CONTEXT;
+
+ GSSEAP_MUTEX_LOCK(&ctx->mutex);
+
+ major = gssEapExportSecContext(minor, ctx, interprocess_token);
+ if (GSS_ERROR(major)) {
+ GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
+ return major;
+ }
+
+ *context_handle = GSS_C_NO_CONTEXT;
+
+ GSSEAP_MUTEX_UNLOCK(&ctx->mutex);
+
+ gssEapReleaseContext(&tmpMinor, &ctx);
+
+ return GSS_S_COMPLETE;
}
gss_name_t initiatorName;
gss_name_t acceptorName;
time_t expiryTime;
+ uint64_t sendSeq, recvSeq;
+ void *seqState;
union {
struct eap_gss_initiator_ctx initiator;
#define initiatorCtx ctxU.initiator
struct eap_gss_acceptor_ctx acceptor;
#define acceptorCtx ctxU.acceptor
} ctxU;
- uint64_t sendSeq, recvSeq;
- void *seqState;
};
#define TOK_FLAG_SENDER_IS_ACCEPTOR 0x01
#include "gssapiP_eap.h"
+static OM_uint32
+gssEapImportPartialContext(OM_uint32 *minor,
+ unsigned char **pBuf,
+ size_t *pRemain,
+ gss_ctx_id_t ctx)
+{
+ unsigned char *p = *pBuf;
+ size_t remain = *pRemain;
+ gss_buffer_desc buf;
+
+ if (remain < 4) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ buf.length = load_uint32_be(p);
+
+ if (buf.length != 0) {
+ *minor = EINVAL;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+importMechanismOid(OM_uint32 *minor,
+ unsigned char **pBuf,
+ size_t *pRemain,
+ gss_OID *pOid)
+{
+ OM_uint32 major;
+ unsigned char *p = *pBuf;
+ size_t remain = *pRemain;
+ gss_OID_desc oidBuf;
+
+ oidBuf.length = load_uint32_be(p);
+ if (remain < 4 + oidBuf.length || oidBuf.length == 0) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ oidBuf.elements = &p[4];
+
+ if (!gssEapIsConcreteMechanismOid(&oidBuf)) {
+ return GSS_S_BAD_MECH;
+ }
+
+ if (!gssEapInternalizeOid(&oidBuf, pOid)) {
+ major = duplicateOid(minor, &oidBuf, pOid);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ *pBuf += 4 + oidBuf.length;
+ *pRemain -= 4 + oidBuf.length;
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+importKerberosKey(OM_uint32 *minor,
+ unsigned char **pBuf,
+ size_t *pRemain,
+ krb5_keyblock *key)
+{
+ unsigned char *p = *pBuf;
+ size_t remain = *pRemain;
+ OM_uint32 encryptionType;
+ OM_uint32 length;
+ gss_buffer_desc tmp;
+
+ if (remain < 8) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ encryptionType = load_uint32_be(&p[0]);
+ length = load_uint32_be(&p[4]);
+
+ if ((length != 0) != (encryptionType != ENCTYPE_NULL)) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (remain - 8 < length) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (load_buffer(&p[8], length, &tmp) == NULL) {
+ *minor = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ KRB_KEY_TYPE(key) = encryptionType;
+ KRB_KEY_LENGTH(key) = tmp.length;
+ KRB_KEY_DATA(key) = (unsigned char *)tmp.value;
+
+ *pBuf += 8 + length;
+ *pRemain -= 8 + length;
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+importName(OM_uint32 *minor,
+ unsigned char **pBuf,
+ size_t *pRemain,
+ gss_name_t *pName)
+{
+ OM_uint32 major;
+ unsigned char *p = *pBuf;
+ size_t remain = *pRemain;
+ gss_buffer_desc tmp;
+
+ if (remain < 4) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ tmp.length = load_uint32_be(p);
+ if (tmp.length != 0) {
+ if (remain - 4 < tmp.length) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ tmp.value = p + 4;
+
+ major = gssEapImportName(minor, &tmp, GSS_C_NT_EXPORT_NAME, pName);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ *pBuf += 4 + tmp.length;
+ *pRemain -= 4 + tmp.length;
+
+ *minor = 0;
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32
+gssEapImportContext(OM_uint32 *minor,
+ gss_buffer_t token,
+ gss_ctx_id_t ctx)
+{
+ OM_uint32 major;
+ unsigned char *p = (unsigned char *)token->value;
+ size_t remain = token->length;
+
+ if (remain < 16) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ if (load_uint32_be(&p[0]) != EAP_EXPORT_CONTEXT_V1) {
+ *minor = EINVAL;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ ctx->state = load_uint32_be(&p[4]);
+ ctx->flags = load_uint32_be(&p[8]);
+ ctx->gssFlags = load_uint32_be(&p[12]);
+ p += 16;
+ remain -= 16;
+
+ /* Validate state */
+ if (ctx->state < EAP_STATE_AUTHENTICATE ||
+ ctx->state > EAP_STATE_ESTABLISHED)
+ return GSS_S_DEFECTIVE_TOKEN;
+
+ /* Only acceptor can export partial context tokens */
+ if (CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx))
+ return GSS_S_DEFECTIVE_TOKEN;
+
+ major = importMechanismOid(minor, &p, &remain, &ctx->mechanismUsed);
+ if (GSS_ERROR(major))
+ return major;
+
+ major = importKerberosKey(minor, &p, &remain, &ctx->rfc3961Key);
+ if (GSS_ERROR(major))
+ return major;
+
+ ctx->encryptionType = KRB_KEY_TYPE(&ctx->rfc3961Key);
+
+ major = importName(minor, &p, &remain, &ctx->initiatorName);
+ if (GSS_ERROR(major))
+ return major;
+
+ major = importName(minor, &p, &remain, &ctx->acceptorName);
+ if (GSS_ERROR(major))
+ return major;
+
+ /* Check that, if context is established, names are valid */
+ if (CTX_IS_ESTABLISHED(ctx) &&
+ (CTX_IS_INITIATOR(ctx) ? ctx->acceptorName == GSS_C_NO_NAME
+ : ctx->initiatorName == GSS_C_NO_NAME)) {
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+
+ if (remain < 24 + sequenceSize(ctx->seqState)) {
+ *minor = ERANGE;
+ return GSS_S_DEFECTIVE_TOKEN;
+ }
+ ctx->expiryTime = (time_t)load_uint64_be(&p[0]); /* XXX */
+ ctx->sendSeq = load_uint64_be(&p[8]);
+ ctx->recvSeq = load_uint64_be(&p[16]);
+ p += 24;
+ remain -= 24;
+
+ *minor = sequenceInternalize(&ctx->seqState, &p, &remain);
+ if (*minor != 0)
+ return GSS_S_FAILURE;
+
+ /*
+ * The partial context should only be expected for unestablished
+ * acceptor contexts.
+ */
+ if (!CTX_IS_INITIATOR(ctx) && !CTX_IS_ESTABLISHED(ctx)) {
+ major = gssEapImportPartialContext(minor, &p, &remain, ctx);
+ if (GSS_ERROR(major))
+ return major;
+ }
+
+ assert(remain == 0);
+
+ *minor = 0;
+ major = GSS_S_COMPLETE;
+
+ return major;
+}
+
OM_uint32
gss_import_sec_context(OM_uint32 *minor,
gss_buffer_t interprocess_token,
gss_ctx_id_t *context_handle)
{
- GSSEAP_NOT_IMPLEMENTED;
+ OM_uint32 major, tmpMinor;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+
+ *context_handle = GSS_C_NO_CONTEXT;
+
+ if (interprocess_token == GSS_C_NO_BUFFER ||
+ interprocess_token->length == 0)
+ return GSS_S_DEFECTIVE_TOKEN;
+
+ major = gssEapAllocContext(minor, &ctx);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ major = gssEapImportContext(minor, interprocess_token, ctx);
+ if (GSS_ERROR(major))
+ goto cleanup;
+
+ *context_handle = ctx;
+
+cleanup:
+ if (GSS_ERROR(major))
+ gssEapReleaseContext(&tmpMinor, &ctx);
+
+ return major;
}
gss_buffer_desc buf;
gss_OID_desc oid;
- buf.length = ctx->rfc3961Key.length;
- buf.value = ctx->rfc3961Key.contents;
+ buf.length = KRB_KEY_LENGTH(&ctx->rfc3961Key);
+ buf.value = KRB_KEY_DATA(&ctx->rfc3961Key);
major = gss_add_buffer_set_member(minor, &buf, dataSet);
if (GSS_ERROR(major))
#include "util_saml.h"
#include "util_radius.h"
-#define KRB_KEYTYPE(key) ((key)->enctype)
+#define KRB_KEY_TYPE(key) ((key)->enctype)
+#define KRB_KEY_DATA(key) ((key)->contents)
+#define KRB_KEY_LENGTH(key) ((key)->length)
+#define KRB_KEY_INIT(key) do { \
+ KRB_KEY_TYPE(key) = ENCTYPE_NULL; \
+ KRB_KEY_DATA(key) = NULL; \
+ KRB_KEY_LENGTH(key) = 0; \
+ } while (0)
enum gss_eap_token_type {
TOK_TYPE_NONE = 0x0000, /* no token */
TOK_TYPE_GSS_CB = 0x0603, /* draft-howlett-eap-gss */
};
+#define EAP_EXPORT_CONTEXT_V1 1
+
/* util_buffer.c */
OM_uint32
makeStringBuffer(OM_uint32 *minor,
int
sequenceExternalize(void *vqueue, unsigned char **buf, size_t *lenremain);
-int
-sequenceSize(void *vqueue, size_t *sizep);
+size_t
+sequenceSize(void *vqueue);
void
sequenceFree(void **vqueue);
return ((uint64_t)load_uint32_be(p) << 32) | load_uint32_be(p + 4);
}
-static inline void
+static inline unsigned char *
store_buffer(gss_buffer_t buffer, void *vp, int wide_nums)
{
unsigned char *p = (unsigned char *)vp;
- if (wide_nums)
+ if (wide_nums) {
store_uint64_be(buffer->length, p);
- else
+ p += 8;
+ } else {
store_uint32_be(buffer->length, p);
- if (buffer->value != NULL)
- memcpy(p + 4, buffer->value, buffer->length);
+ p += 4;
+ }
+
+ if (buffer->value != NULL) {
+ memcpy(p, buffer->value, buffer->length);
+ p += buffer->length;
+ }
+
+ return p;
}
+static inline unsigned char *
+load_buffer(const void *cvp, size_t length, gss_buffer_t buffer)
+{
+ buffer->length = 0;
+ buffer->value = GSSEAP_MALLOC(length);
+ if (buffer->value == NULL)
+ return NULL;
+ buffer->length = length;
+ memcpy(buffer->value, cvp, length);
+ return (unsigned char *)cvp + length;
+}
+
+static inline unsigned char *
+store_oid(gss_OID oid, void *vp)
+{
+ gss_buffer_desc buf;
+
+ if (oid != GSS_C_NO_OID) {
+ buf.length = oid->length;
+ buf.value = oid->elements;
+ } else {
+ buf.length = 0;
+ buf.value = NULL;
+ }
+
+ return store_buffer(&buf, vp, FALSE);
+}
#endif /* _UTIL_H_ */
if (verify)
*valid = FALSE;
- code = krb5_c_crypto_length(context, KRB_KEYTYPE(key),
+ code = krb5_c_crypto_length(context, KRB_KEY_TYPE(key),
KRB5_CRYPTO_TYPE_CHECKSUM, &k5_checksumlen);
if (code != 0)
return code;
krb5_crypto_iov *kiov;
if (iv) {
- code = krb5_c_block_size(context, KRB_KEYTYPE(key), &blocksize);
+ code = krb5_c_block_size(context, KRB_KEY_TYPE(key), &blocksize);
if (code)
return(code);
}
code = mapIov(context, dce_style, ec, rrc,
- KRB_KEYTYPE(key), iov, iov_count,
+ KRB_KEY_TYPE(key), iov, iov_count,
&kiov, &kiov_count);
if (code == 0) {
code = krb5_c_encrypt_iov(context, key, usage, pivd, kiov, kiov_count);
krb5_crypto_iov *kiov;
if (iv) {
- code = krb5_c_block_size(context, KRB_KEYTYPE(key), &blocksize);
+ code = krb5_c_block_size(context, KRB_KEY_TYPE(key), &blocksize);
if (code)
return(code);
}
code = mapIov(context, dce_style, ec, rrc,
- KRB_KEYTYPE(key), iov, iov_count,
+ KRB_KEY_TYPE(key), iov, iov_count,
&kiov, &kiov_count);
if (code == 0) {
code = krb5_c_decrypt_iov(context, key, usage, pivd, kiov, kiov_count);
GSSEAP_KRB_INIT(&context);
- kd.contents = NULL;
- kd.length = 0;
- KRB_KEYTYPE(&kd) = enctype;
+ KRB_KEY_INIT(&kd);
+ KRB_KEY_TYPE(&kd) = enctype;
prf.data = NULL;
prf.length = 0;
data.length = keybytes;
data.data = (char *)key;
- kd.contents = GSSEAP_MALLOC(keylength);
- if (kd.contents == NULL) {
+ KRB_KEY_DATA(&kd) = GSSEAP_MALLOC(keylength);
+ if (KRB_KEY_DATA(&kd) == NULL) {
code = ENOMEM;
goto cleanup;
}
- kd.length = keylength;
+ KRB_KEY_LENGTH(&kd) = keylength;
/* Convert MSK into a Kerberos key */
code = krb5_c_random_to_key(context, enctype, &data, &kd);
goto cleanup;
*pKey = kd;
- kd.contents = NULL;
+ KRB_KEY_DATA(&kd) = NULL;
cleanup:
- if (kd.contents != NULL) {
- memset(kd.contents, 0, kd.length);
- GSSEAP_FREE(kd.contents);
+ if (KRB_KEY_DATA(&kd) != NULL) {
+ memset(KRB_KEY_DATA(&kd), 0, KRB_KEY_LENGTH(&kd));
+ GSSEAP_FREE(KRB_KEY_DATA(&kd));
}
if (prf.data != NULL) {
memset(prf.data, 0, prf.length);
return GSS_S_BAD_NAME;
if (p[1] != GSS_EAP_MECHANISM->length)
return GSS_S_BAD_MECH;
- if (memcmp(p, GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length))
+ if (memcmp(&p[2], GSS_EAP_MECHANISM->elements, GSS_EAP_MECHANISM->length))
return GSS_S_BAD_MECH;
p += 2 + GSS_EAP_MECHANISM->length;
remain -= 2 + GSS_EAP_MECHANISM->length;
/* NAME_LEN */
len = load_uint32_be(p);
p += 4;
+ remain -= 4;
if (remain < len)
return GSS_S_BAD_NAME;
return GSS_S_COMPLETE;
}
-OM_uint32 gssEapImportName(OM_uint32 *minor,
- const gss_buffer_t nameBuffer,
- gss_OID nameType,
- gss_name_t *name)
+OM_uint32
+gssEapImportName(OM_uint32 *minor,
+ const gss_buffer_t nameBuffer,
+ gss_OID nameType,
+ gss_name_t *name)
{
OM_uint32 major, tmpMinor;
return major;
}
-OM_uint32 gssEapExportName(OM_uint32 *minor,
- const gss_name_t name,
- gss_buffer_t exportedName,
- int composite)
+OM_uint32
+gssEapExportName(OM_uint32 *minor,
+ const gss_name_t name,
+ gss_buffer_t exportedName,
+ int composite)
{
OM_uint32 major = GSS_S_FAILURE, tmpMinor;
krb5_context krbContext;
exportedName->length = 6 + GSS_EAP_MECHANISM->length + 4 + krbNameLen;
if (composite) {
/* TODO: export SAML/AVP, this is pending specification */
- GSSEAP_NOT_IMPLEMENTED;
+
}
exportedName->value = GSSEAP_MALLOC(exportedName->length);
{
queue *q;
- if ((q = (queue *) malloc(sizeof(queue))) == NULL)
+ if ((q = (queue *) GSSEAP_MALLOC(sizeof(queue))) == NULL)
return(ENOMEM);
/* This stops valgrind from complaining about writing uninitialized
q = (queue *) (*vqueue);
- free(q);
+ GSSEAP_FREE(q);
*vqueue = NULL;
}
/*
* These support functions are for the serialization routines
*/
-int
-sequenceSize(void *vqueue, size_t *sizep)
+size_t
+sequenceSize(void *vqueue)
{
- *sizep += sizeof(queue);
- return 0;
+ return sizeof(queue);
}
int
sequenceExternalize(void *vqueue, unsigned char **buf, size_t *lenremain)
{
if (*lenremain < sizeof(queue))
- return ENOMEM;
+ return ERANGE;
memcpy(*buf, vqueue, sizeof(queue));
*buf += sizeof(queue);
*lenremain -= sizeof(queue);
void *q;
if (*lenremain < sizeof(queue))
- return EINVAL;
- if ((q = malloc(sizeof(queue))) == 0)
+ return ERANGE;
+ if ((q = GSSEAP_MALLOC(sizeof(queue))) == 0)
return ENOMEM;
memcpy(q, *buf, sizeof(queue));
*buf += sizeof(queue);