#ident "$Id: wpse_main.c,v 1.3 2007/01/02 22:33:51 kwc Exp $"
#include "autoconf.h"
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <arpa/inet.h>
#include <stdio.h>
#include <krb5/krb5.h>
#include <krb5/preauth_plugin.h>
#define KRB5_PADATA_WPSE_REQ 131
static int
client_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
{
return PA_REAL;
}
static krb5_error_code
client_init(krb5_context kcontext, void **ctx)
{
int *pctx;
pctx = malloc(sizeof(int));
if (pctx == NULL)
return ENOMEM;
*pctx = 0;
*ctx = pctx;
return 0;
}
static void
client_fini(krb5_context kcontext, void *ctx)
{
int *pctx;
pctx = ctx;
if (pctx) {
#ifdef DEBUG
fprintf(stderr, "wpse module called total of %d times\n", *pctx);
#endif
free(pctx);
}
}
static krb5_error_code
client_process(krb5_context kcontext,
void *plugin_context,
void *request_context,
krb5_get_init_creds_opt *opt,
preauth_get_client_data_proc client_get_data_proc,
struct _krb5_preauth_client_rock *rock,
krb5_kdc_req *request,
krb5_data *encoded_request_body,
krb5_data *encoded_previous_request,
krb5_pa_data *pa_data,
krb5_prompter_fct prompter,
void *prompter_data,
preauth_get_as_key_proc gak_fct,
void *gak_data,
krb5_data *salt, krb5_data *s2kparams,
krb5_keyblock *as_key,
krb5_pa_data ***out_pa_data)
{
krb5_pa_data **send_pa;
krb5_int32 nnonce, enctype;
krb5_keyblock *kb;
krb5_error_code status;
int *pctx;
#ifdef DEBUG
fprintf(stderr, "%d bytes of preauthentication data (type %d)\n",
pa_data->length, pa_data->pa_type);
#endif
pctx = plugin_context;
if (pctx) {
(*pctx)++;
}
if (pa_data->length == 0) {
send_pa = malloc(2 * sizeof(krb5_pa_data *));
if (send_pa == NULL)
return ENOMEM;
send_pa[1] = NULL;
send_pa[0] = malloc(sizeof(krb5_pa_data));
if (send_pa[0] == NULL) {
free(send_pa);
return ENOMEM;
}
send_pa[0]->pa_type = KRB5_PADATA_WPSE_REQ;
send_pa[0]->length = 4;
send_pa[0]->contents = malloc(4);
if (send_pa[0]->contents == NULL) {
free(send_pa[0]);
free(send_pa);
return ENOMEM;
}
nnonce = htonl(request->nonce);
memcpy(send_pa[0]->contents, &nnonce, 4);
*out_pa_data = send_pa;
} else {
if (pa_data->length > 4) {
memcpy(&enctype, pa_data->contents, 4);
kb = NULL;
status = krb5_init_keyblock(kcontext, ntohl(enctype),
pa_data->length - 4, &kb);
if (status != 0)
return status;
memcpy(kb->contents, pa_data->contents + 4, pa_data->length - 4);
#ifdef DEBUG
fprintf(stderr, "Recovered key type=%d, length=%d.\n",
kb->enctype, kb->length);
#endif
status = krb5_copy_keyblock_contents(kcontext, kb, as_key);
krb5_free_keyblock(kcontext, kb);
return status;
}
return KRB5KRB_ERR_GENERIC;
}
return 0;
}
#define WPSE_MAGIC 0x77707365
typedef struct _wpse_req_ctx
{
int magic;
int value;
} wpse_req_ctx;
static void
client_req_init(krb5_context kcontext, void *plugin_context, void **req_context_p)
{
wpse_req_ctx *ctx;
*req_context_p = NULL;
ctx = (wpse_req_ctx *) malloc(sizeof(*ctx));
if (ctx == NULL)
return;
ctx->magic = WPSE_MAGIC;
ctx->value = 0xc0dec0de;
*req_context_p = ctx;
}
static void
client_req_cleanup(krb5_context kcontext, void *plugin_context, void *req_context)
{
wpse_req_ctx *ctx = (wpse_req_ctx *)req_context;
if (ctx) {
#ifdef DEBUG
fprintf(stderr, "client_req_cleanup: req_ctx at %p has magic %x and value %x\n",
ctx, ctx->magic, ctx->value);
#endif
if (ctx->magic != WPSE_MAGIC) {
#ifdef DEBUG
fprintf(stderr, "client_req_cleanup: req_context at %p has bad magic value %x\n",
ctx, ctx->magic);
#endif
return;
}
free(ctx);
}
return;
}
static krb5_error_code
client_gic_opt(krb5_context kcontext,
void *plugin_context,
krb5_get_init_creds_opt *opt,
const char *attr,
const char *value)
{
#ifdef DEBUG
fprintf(stderr, "(wpse) client_gic_opt: received '%s' = '%s'\n",
attr, value);
#endif
return 0;
}
static krb5_error_code
server_free_pa_request_context(krb5_context kcontext, void *plugin_context,
void **request_context)
{
if (*request_context != NULL) {
free(*request_context);
*request_context = NULL;
}
return 0;
}
static krb5_error_code
server_get_edata(krb5_context kcontext,
krb5_kdc_req *request,
struct _krb5_db_entry_new *client,
struct _krb5_db_entry_new *server,
preauth_get_entry_data_proc server_get_entry_data,
void *pa_module_context,
krb5_pa_data *data)
{
data->length = 0;
data->contents = NULL;
return 0;
}
static krb5_error_code
server_verify(krb5_context kcontext,
struct _krb5_db_entry_new *client,
krb5_data *req_pkt,
krb5_kdc_req *request,
krb5_enc_tkt_part *enc_tkt_reply,
krb5_pa_data *data,
preauth_get_entry_data_proc server_get_entry_data,
void *pa_module_context,
void **pa_request_context,
krb5_data **e_data,
krb5_authdata ***authz_data)
{
krb5_int32 nnonce;
krb5_data *test_edata;
krb5_authdata **my_authz_data;
#ifdef DEBUG
fprintf(stderr, "wpse: server_verify()!\n");
#endif
if (data->length != 4)
return KRB5KDC_ERR_PREAUTH_FAILED;
memcpy(&nnonce, data->contents, 4);
nnonce = ntohl(nnonce);
if (memcmp(&nnonce, &request->nonce, 4) != 0)
return KRB5KDC_ERR_PREAUTH_FAILED;
enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
enc_tkt_reply->flags |= TKT_FLG_HW_AUTH;
if (*pa_request_context == NULL)
*pa_request_context = malloc(4);
#ifdef DEBUG
fprintf(stderr, "wpse: doing authorization data!\n");
#endif
#if 1
#define AD_ALLOC_SIZE 5000
krb5_octet ad_header[] = {0x30, 0x82, 0x13, 0x84, 0x04, 0x82, 0x13, 0x80};
#else
#define AD_ALLOC_SIZE 100
krb5_octet ad_header[] = {0x30, 0x62, 0x04, 0x60};
#endif
my_authz_data = malloc(2 * sizeof(*my_authz_data));
if (my_authz_data != NULL) {
my_authz_data[1] = NULL;
my_authz_data[0] = malloc(sizeof(krb5_authdata));
if (my_authz_data[0] == NULL) {
free(my_authz_data);
return ENOMEM;
}
my_authz_data[0]->contents = malloc(AD_ALLOC_SIZE);
if (my_authz_data[0]->contents == NULL) {
free(my_authz_data[0]);
free(my_authz_data);
return ENOMEM;
}
memset(my_authz_data[0]->contents, '\0', AD_ALLOC_SIZE);
my_authz_data[0]->magic = KV5M_AUTHDATA;
my_authz_data[0]->ad_type = 1;
my_authz_data[0]->length = AD_ALLOC_SIZE;
memcpy(my_authz_data[0]->contents, ad_header, sizeof(ad_header));
sprintf(my_authz_data[0]->contents + sizeof(ad_header),
"wpse authorization data: %d bytes worth!\n", AD_ALLOC_SIZE);
*authz_data = my_authz_data;
#ifdef DEBUG
fprintf(stderr, "Returning %d bytes of authorization data\n",
AD_ALLOC_SIZE);
#endif
}
test_edata = malloc(sizeof(*test_edata));
if (test_edata != NULL) {
test_edata->data = malloc(20);
if (test_edata->data == NULL) {
free(test_edata);
} else {
test_edata->length = 20;
memset(test_edata->data, '#', 20);
*e_data = test_edata;
}
}
return 0;
}
static krb5_error_code
server_return(krb5_context kcontext,
krb5_pa_data *padata,
struct _krb5_db_entry_new *client,
krb5_data *req_pkt,
krb5_kdc_req *request,
krb5_kdc_rep *reply,
struct _krb5_key_data *client_key,
krb5_keyblock *encrypting_key,
krb5_pa_data **send_pa,
preauth_get_entry_data_proc server_get_entry_data,
void *pa_module_context,
void **pa_request_context)
{
krb5_keyblock *kb;
krb5_int32 enctype;
int i;
*send_pa = NULL;
for (i = 0; i < request->nktypes; i++) {
kb = NULL;
if (krb5_init_keyblock(kcontext, request->ktype[i], 0, &kb) == 0) {
break;
}
}
if (i >= request->nktypes) {
return 0;
}
if (krb5_c_make_random_key(kcontext, request->ktype[i], kb) != 0) {
krb5_free_keyblock(kcontext, kb);
return 0;
}
#ifdef DEBUG
fprintf(stderr, "Generated random key, type=%d, length=%d.\n",
kb->enctype, kb->length);
#endif
*send_pa = malloc(sizeof(krb5_pa_data));
if (*send_pa == NULL) {
krb5_free_keyblock(kcontext, kb);
return ENOMEM;
}
(*send_pa)->pa_type = KRB5_PADATA_WPSE_REQ;
(*send_pa)->length = 4 + kb->length;
(*send_pa)->contents = malloc(4 + kb->length);
if ((*send_pa)->contents == NULL) {
free(*send_pa);
*send_pa = NULL;
krb5_free_keyblock(kcontext, kb);
return ENOMEM;
}
enctype = htonl(kb->enctype);
memcpy((*send_pa)->contents, &enctype, 4);
memcpy((*send_pa)->contents + 4, kb->contents, kb->length);
krb5_free_keyblock_contents(kcontext, encrypting_key);
krb5_copy_keyblock_contents(kcontext, kb, encrypting_key);
krb5_free_keyblock(kcontext, kb);
return 0;
}
static int
server_get_flags(krb5_context kcontext, krb5_preauthtype pa_type)
{
return PA_HARDWARE | PA_REPLACES_KEY | PA_SUFFICIENT;
}
static krb5_preauthtype supported_client_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
static krb5_preauthtype supported_server_pa_types[] = {KRB5_PADATA_WPSE_REQ, 0};
struct krb5plugin_preauth_client_ftable_v1 preauthentication_client_1 = {
"wpse",
&supported_client_pa_types[0],
NULL,
client_init,
client_fini,
client_get_flags,
client_req_init,
client_req_cleanup,
client_process,
NULL,
client_gic_opt
};
struct krb5plugin_preauth_server_ftable_v1 preauthentication_server_1 = {
"wpse",
&supported_server_pa_types[0],
NULL,
NULL,
server_get_flags,
server_get_edata,
server_verify,
server_return,
server_free_pa_request_context,
};