diff --git a/lib/gssapi/gssapi/gssapi_krb5.h b/lib/gssapi/gssapi/gssapi_krb5.h index 7006207b8..6841f423b 100644 --- a/lib/gssapi/gssapi/gssapi_krb5.h +++ b/lib/gssapi/gssapi/gssapi_krb5.h @@ -58,13 +58,16 @@ extern gss_OID GSS_KRB5_MECHANISM; #define gss_mech_krb5 GSS_KRB5_MECHANISM #define gss_krb5_nt_general_name GSS_KRB5_NT_PRINCIPAL_NAME -/* Extensions contexts */ -extern gss_OID GSS_C_PEER_HAS_UPDATED_SPNEGO; +/* Extensions set contexts options */ extern gss_OID GSS_KRB5_COPY_CCACHE_X; -extern gss_OID GSS_KRB5_GET_TKT_FLAGS_X; -extern gss_OID GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X; extern gss_OID GSS_KRB5_COMPAT_DES3_MIC_X; extern gss_OID GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X; +/* Extensions inquire context inre */ +extern gss_OID GSS_KRB5_GET_TKT_FLAGS_X; +extern gss_OID GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X; +extern gss_OID GSS_C_PEER_HAS_UPDATED_SPNEGO; +extern gss_OID GSS_KRB5_EXPORT_LUCID_CONTEXT_X; +extern gss_OID GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X; /* Extensions creds */ extern gss_OID GSS_KRB5_IMPORT_CRED_X; @@ -113,6 +116,60 @@ gsskrb5_extract_authz_data_from_sec_context OM_uint32 gss_krb5_compat_des3_mic(OM_uint32 *, gss_ctx_id_t, int); +/* + * Lucid - NFSv4 interface to GSS-API KRB5 to expose key material to + * do GSS content token handling in-kernel. + */ + +typedef struct gss_krb5_lucid_key { + OM_uint32 type; + OM_uint32 length; + void * data; +} gss_krb5_lucid_key_t; + +typedef struct gss_krb5_rfc1964_keydata { + OM_uint32 sign_alg; + OM_uint32 seal_alg; + gss_krb5_lucid_key_t ctx_key; +} gss_krb5_rfc1964_keydata_t; + +typedef struct gss_krb5_cfx_keydata { + OM_uint32 have_acceptor_subkey; + gss_krb5_lucid_key_t ctx_key; + gss_krb5_lucid_key_t acceptor_subkey; +} gss_krb5_cfx_keydata_t; + +typedef struct gss_krb5_lucid_context_v1 { + OM_uint32 version; + OM_uint32 initiate; + OM_uint32 endtime; + OM_uint64 send_seq; + OM_uint64 recv_seq; + OM_uint32 protocol; + gss_krb5_rfc1964_keydata_t rfc1964_kd; + gss_krb5_cfx_keydata_t cfx_kd; +} gss_krb5_lucid_context_v1_t; + +typedef struct gss_krb5_lucid_context_version { + OM_uint32 version; /* Structure version number */ +} gss_krb5_lucid_context_version_t; + +/* + * Function declarations + */ + +OM_uint32 +gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + OM_uint32 version, + void **kctx); + + +OM_uint32 +gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, + void *kctx); + + #ifdef __cplusplus } diff --git a/lib/gssapi/krb5/inquire_sec_context_by_oid.c b/lib/gssapi/krb5/inquire_sec_context_by_oid.c index f75796df1..4b7ff0942 100644 --- a/lib/gssapi/krb5/inquire_sec_context_by_oid.c +++ b/lib/gssapi/krb5/inquire_sec_context_by_oid.c @@ -177,6 +177,132 @@ static OM_uint32 inquire_sec_context_has_updated_spnego return is_updated ? GSS_S_COMPLETE : GSS_S_FAILURE; } +/* + * + */ + +static OM_uint32 +export_lucid_sec_context_v1(OM_uint32 *minor_status, + gsskrb5_ctx context_handle, + gss_buffer_set_t *data_set) +{ + krb5_storage *sp = NULL; + OM_uint32 major_status = GSS_S_COMPLETE; + krb5_error_code ret; + krb5_keyblock *key = NULL; + int32_t number; + int is_cfx; + krb5_data data; + + *minor_status = 0; + + GSSAPI_KRB5_INIT (); + + HEIMDAL_MUTEX_lock(&context_handle->ctx_id_mutex); + + _gsskrb5i_is_cfx(context_handle, &is_cfx); + + sp = krb5_storage_emem(); + if (sp == NULL) { + ret = ENOMEM; + goto out; + } + + ret = krb5_store_int32(sp, 1); + if (ret) goto out; + ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0); + if (ret) goto out; + ret = krb5_store_int32(sp, context_handle->lifetime); + if (ret) goto out; + krb5_auth_con_getlocalseqnumber (_gsskrb5_context, + context_handle->auth_context, + &number); + ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ + ret = krb5_store_uint32(sp, (uint32_t)number); + krb5_auth_getremoteseqnumber (_gsskrb5_context, + context_handle->auth_context, + &number); + ret = krb5_store_uint32(sp, (uint32_t)0); /* store top half as zero */ + ret = krb5_store_uint32(sp, (uint32_t)number); + ret = krb5_store_int32(sp, (is_cfx) ? 1 : 0); + if (ret) goto out; + + ret = _gsskrb5i_get_subkey(context_handle, &key); + if (ret) goto out; + + if (is_cfx == 0) { + int sign_alg, seal_alg; + + ret = krb5_store_keyblock(sp, *key); + if (ret) goto out; + + switch (key->keytype) { + case ETYPE_DES_CBC_CRC: + case ETYPE_DES_CBC_MD4: + case ETYPE_DES_CBC_MD5: + sign_alg = 0; + seal_alg = 0; + break; + case ETYPE_DES3_CBC_MD5: + case ETYPE_DES3_CBC_SHA1: + sign_alg = 4; + seal_alg = 2; + break; + case ETYPE_ARCFOUR_HMAC_MD5: + case ETYPE_ARCFOUR_HMAC_MD5_56: + sign_alg = 17; + seal_alg = 16; + break; + default: + sign_alg = -1; + seal_alg = -1; + break; + } + ret = krb5_store_int32(sp, sign_alg); + if (ret) goto out; + ret = krb5_store_int32(sp, seal_alg); + if (ret) goto out; + } else { + int subkey_p = (context_handle->more_flags & ACCEPTOR_SUBKEY) ? 1 : 0; + + ret = krb5_store_int32(sp, subkey_p); + if (ret) goto out; + ret = krb5_store_keyblock(sp, *key); + if (ret) goto out; + if (subkey_p) { + ret = krb5_store_keyblock(sp, *key); + if (ret) goto out; + } + } + ret = krb5_storage_to_data(sp, &data); + if (ret) goto out; + + { + gss_buffer_desc ad_data; + + ad_data.value = data.data; + ad_data.length = data.length; + + ret = gss_add_buffer_set_member(minor_status, &ad_data, data_set); + krb5_data_free(&data); + if (ret) + goto out; + } + +out: + if (key) + krb5_free_keyblock (_gsskrb5_context, key); + if (sp) + krb5_storage_free(sp); + if (ret) { + *minor_status = ret; + major_status = GSS_S_FAILURE; + } + HEIMDAL_MUTEX_unlock(&context_handle->ctx_id_mutex); + return major_status; +} + + OM_uint32 _gsskrb5_inquire_sec_context_by_oid (OM_uint32 *minor_status, const gss_ctx_id_t context_handle, @@ -206,6 +332,15 @@ OM_uint32 _gsskrb5_inquire_sec_context_by_oid ctx, suffix, data_set); + } else if (oid_prefix_equal(desired_object, + GSS_KRB5_EXPORT_LUCID_CONTEXT_X, + &suffix)) { + if (suffix == 1) + return export_lucid_sec_context_v1(minor_status, + ctx, + data_set); + *minor_status = 0; + return GSS_S_FAILURE; } else { *minor_status = 0; return GSS_S_FAILURE; diff --git a/lib/gssapi/mech/gss_krb5.c b/lib/gssapi/mech/gss_krb5.c index d4f94e78a..6ddccb742 100644 --- a/lib/gssapi/mech/gss_krb5.c +++ b/lib/gssapi/mech/gss_krb5.c @@ -202,3 +202,187 @@ gsskrb5_register_acceptor_identity(const char *identity) return (GSS_S_COMPLETE); } + +static krb5_error_code +set_key(krb5_keyblock *keyblock, gss_krb5_lucid_key_t *key) +{ + key->type = keyblock->keytype; + key->length = keyblock->keyvalue.length; + key->data = malloc(key->length); + if (key->data == NULL && key->length != 0) + return ENOMEM; + memcpy(key->data, keyblock->keyvalue.data, key->length); + return 0; +} + +static void +free_key(gss_krb5_lucid_key_t *key) +{ + memset(key->data, 0, key->length); + free(key->data); + memset(key, 0, sizeof(*key)); +} + + +OM_uint32 +gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status, + gss_ctx_id_t *context_handle, + OM_uint32 version, + void **rctx) +{ + krb5_context context = NULL; + krb5_error_code ret; + gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; + OM_uint32 major_status; + gss_krb5_lucid_context_v1_t *ctx = NULL; + krb5_storage *sp = NULL; + uint32_t num; + + if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT) { + ret = EINVAL; + return GSS_S_FAILURE; + } + + major_status = + gss_inquire_sec_context_by_oid (minor_status, + *context_handle, + GSS_KRB5_EXPORT_LUCID_CONTEXT_X, + &data_set); + if (major_status) + return major_status; + + if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) { + gss_release_buffer_set(minor_status, &data_set); + *minor_status = EINVAL; + return GSS_S_FAILURE; + } + + ret = krb5_init_context(&context); + if (ret) + goto out; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx) { + ret = ENOMEM; + goto out; + } + + sp = krb5_storage_from_mem(data_set->elements[0].value, + data_set->elements[0].length); + if (sp == NULL) { + ret = ENOMEM; + goto out; + } + + ret = krb5_ret_uint32(sp, &num); + if (ret) goto out; + if (num != 1) { + ret = EINVAL; + goto out; + } + ctx->version = 1; + /* initiator */ + ret = krb5_ret_uint32(sp, &ctx->initiate); + if (ret) goto out; + /* endtime */ + ret = krb5_ret_uint32(sp, &ctx->endtime); + if (ret) goto out; + /* send_seq */ + ret = krb5_ret_uint32(sp, &num); + if (ret) goto out; + ctx->send_seq = ((uint64_t)num) << 32; + ret = krb5_ret_uint32(sp, &num); + if (ret) goto out; + ctx->send_seq |= num; + /* recv_seq */ + ret = krb5_ret_uint32(sp, &num); + if (ret) goto out; + ctx->recv_seq = ((uint64_t)num) << 32; + ret = krb5_ret_uint32(sp, &num); + if (ret) goto out; + ctx->recv_seq |= num; + /* protocol */ + ret = krb5_ret_uint32(sp, &ctx->protocol); + if (ret) goto out; + if (ctx->protocol == 0) { + krb5_keyblock key; + + /* sign_alg */ + ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg); + if (ret) goto out; + /* seal_alg */ + ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg); + if (ret) goto out; + /* ctx_key */ + ret = krb5_ret_keyblock(sp, &key); + if (ret) goto out; + ret = set_key(&key, &ctx->rfc1964_kd.ctx_key); + krb5_free_keyblock_contents(context, &key); + if (ret) goto out; + } else if (ctx->protocol == 1) { + krb5_keyblock key; + + /* acceptor_subkey */ + ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey); + if (ret) goto out; + /* ctx_key */ + ret = krb5_ret_keyblock(sp, &key); + if (ret) goto out; + ret = set_key(&key, &ctx->cfx_kd.ctx_key); + krb5_free_keyblock_contents(context, &key); + if (ret) goto out; + /* acceptor_subkey */ + if (ctx->cfx_kd.have_acceptor_subkey) { + ret = krb5_ret_keyblock(sp, &key); + if (ret) goto out; + ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey); + krb5_free_keyblock_contents(context, &key); + if (ret) goto out; + } + } else { + ret = EINVAL; + goto out; + } + + *rctx = ctx; + +out: + gss_release_buffer_set(minor_status, &data_set); + if (sp) + krb5_storage_free(sp); + if (context) + krb5_free_context(context); + + if (ret) { + if (ctx) + gss_krb5_free_lucid_sec_context(NULL, ctx); + + *minor_status = ret; + return GSS_S_FAILURE; + } + *minor_status = 0; + return GSS_S_COMPLETE; +} + +OM_uint32 +gss_krb5_free_lucid_sec_context(OM_uint32 *minor_status, void *c) +{ + gss_krb5_lucid_context_v1_t *ctx = c; + + if (ctx->version != 1) { + *minor_status = 0; + return GSS_S_FAILURE; + } + + if (ctx->protocol == 0) { + free_key(&ctx->rfc1964_kd.ctx_key); + } else if (ctx->protocol == 1) { + free_key(&ctx->cfx_kd.ctx_key); + if (ctx->cfx_kd.have_acceptor_subkey) + free_key(&ctx->cfx_kd.acceptor_subkey); + } + free(ctx); + if (minor_status) + *minor_status = 0; + return GSS_S_COMPLETE; +}