If no server given, interate over keytab to find a key that can

decrypt the request. The resulting server principal is what in the
keytab, the real service can be fetched from.

git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@24257 ec53bebd-3082-4978-b11e-865c3cabbd6b
This commit is contained in:
Love Hörnquist Åstrand
2009-01-11 21:44:48 +00:00
parent f0bfba4982
commit 6239532d9a

View File

@@ -524,6 +524,7 @@ struct krb5_rd_req_out_ctx_data {
krb5_keyblock *keyblock; krb5_keyblock *keyblock;
krb5_flags ap_req_options; krb5_flags ap_req_options;
krb5_ticket *ticket; krb5_ticket *ticket;
krb5_principal server;
}; };
/* /*
@@ -561,7 +562,7 @@ krb5_rd_req_in_set_keytab(krb5_context context,
* *
* @return Kerberos 5 error code, see krb5_get_error_message(). * @return Kerberos 5 error code, see krb5_get_error_message().
* *
* @ingroup krb5 * @ingroup krb5_auth
*/ */
krb5_error_code KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_FUNCTION
@@ -608,23 +609,40 @@ krb5_rd_req_out_get_keyblock(krb5_context context,
return krb5_copy_keyblock(context, out->keyblock, keyblock); return krb5_copy_keyblock(context, out->keyblock, keyblock);
} }
/**
* Get the principal that was used in the request from the
* client. Might not match whats in the ticket if krb5_rd_req_ctx()
* searched in the keytab for a matching key.
*
* @param context a Kerberos 5 context.
* @param out a krb5_rd_req_out_ctx from krb5_rd_req_ctx().
* @param principal return principal, free with krb5_free_principal().
*
* @ingroup krb5_auth
*/
krb5_error_code KRB5_LIB_FUNCTION
krb5_rd_req_out_get_server(krb5_context context,
krb5_rd_req_out_ctx out,
krb5_principal *principal)
{
return krb5_copy_principal(context, out->server, principal);
}
void KRB5_LIB_FUNCTION void KRB5_LIB_FUNCTION
krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx) krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx)
{ {
free(ctx); free(ctx);
} }
krb5_error_code KRB5_LIB_FUNCTION /**
_krb5_rd_req_out_ctx_alloc(krb5_context context, krb5_rd_req_out_ctx *ctx) * Free the krb5_rd_req_out_ctx.
{ *
*ctx = calloc(1, sizeof(**ctx)); * @param context Keberos 5 context.
if (*ctx == NULL) { * @param ctx krb5_rd_req_out_ctx context to free.
krb5_set_error_message(context, ENOMEM, *
N_("malloc: out of memory", "")); * @ingroup krb5_auth
return ENOMEM; */
}
return 0;
}
void KRB5_LIB_FUNCTION void KRB5_LIB_FUNCTION
krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx) krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
@@ -633,6 +651,8 @@ krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
krb5_free_ticket(context, ctx->ticket); krb5_free_ticket(context, ctx->ticket);
if (ctx->keyblock) if (ctx->keyblock)
krb5_free_keyblock(context, ctx->keyblock); krb5_free_keyblock(context, ctx->keyblock);
if (ctx->server)
krb5_free_principal(context, ctx->server);
free(ctx); free(ctx);
} }
@@ -770,8 +790,24 @@ out:
return ret; return ret;
} }
/* /**
* The core server function that verify application authentication
* requests from clients.
* *
* @param context Keberos 5 context.
* @param auth_context the authentication context, can be NULL, then
* default values for the authentication context will used.
* @param inbuf the (AP-REQ) authentication buffer
* @param server the server with authenticate as, if NULL the function
* will try to find any avaiable credentintial in the keytab
* that will verify the reply.
* @param inctx control the behavior of the function, if NULL, the
* default behavior is used.
* @param outctx the return outctx,can be NULL. If set and function
* returns 0, free with krb5_rd_req_out_ctx_free()
* @return Kerberos 5 error code, see krb5_get_error_message().
*
* @ingroup krb5_auth
*/ */
krb5_error_code KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_FUNCTION
@@ -784,12 +820,17 @@ krb5_rd_req_ctx(krb5_context context,
{ {
krb5_error_code ret; krb5_error_code ret;
krb5_ap_req ap_req; krb5_ap_req ap_req;
krb5_principal service = NULL;
krb5_rd_req_out_ctx o = NULL; krb5_rd_req_out_ctx o = NULL;
krb5_keytab keytab = NULL;
ret = _krb5_rd_req_out_ctx_alloc(context, &o); *outctx = NULL;
if (ret)
goto out; o = calloc(1, sizeof(*o));
if (o == NULL) {
krb5_set_error_message(context, ENOMEM,
N_("malloc: out of memory", ""));
return ENOMEM;
}
if (*auth_context == NULL) { if (*auth_context == NULL) {
ret = krb5_auth_con_init(context, auth_context); ret = krb5_auth_con_init(context, auth_context);
@@ -801,15 +842,6 @@ krb5_rd_req_ctx(krb5_context context,
if(ret) if(ret)
goto out; goto out;
if(server == NULL){
ret = _krb5_principalname2krb5_principal(context,
&service,
ap_req.ticket.sname,
ap_req.ticket.realm);
if (ret)
goto out;
server = service;
}
if (ap_req.ap_options.use_session_key && if (ap_req.ap_options.use_session_key &&
(*auth_context)->keyblock == NULL) { (*auth_context)->keyblock == NULL) {
ret = KRB5KRB_AP_ERR_NOKEY; ret = KRB5KRB_AP_ERR_NOKEY;
@@ -831,35 +863,134 @@ krb5_rd_req_ctx(krb5_context context,
&o->keyblock); &o->keyblock);
if (ret) if (ret)
goto out; goto out;
} else { } else if (server) {
krb5_keytab keytab = NULL; krb5_keytab id = NULL;
if (inctx && inctx->keytab) if (inctx && inctx->keytab)
keytab = inctx->keytab; id = inctx->keytab;
ret = get_key_from_keytab(context, ret = get_key_from_keytab(context,
auth_context, auth_context,
&ap_req, &ap_req,
server, server,
keytab, id,
&o->keyblock); &o->keyblock);
if(ret) if(ret)
goto out; goto out;
} }
ret = krb5_verify_ap_req2(context, /*
auth_context, * If we got an exact keymatch, use that.
&ap_req, */
server, if (o->keyblock) {
o->keyblock, ret = krb5_verify_ap_req2(context,
0, auth_context,
&o->ap_req_options, &ap_req,
&o->ticket, server,
KRB5_KU_AP_REQ_AUTH); o->keyblock,
0,
&o->ap_req_options,
&o->ticket,
KRB5_KU_AP_REQ_AUTH);
if (ret)
goto out;
} else {
krb5_keytab id = NULL;
krb5_kt_cursor cursor;
krb5_keytab_entry entry;
int done = 0, kvno = 0;
memset(&cursor, 0, sizeof(cursor));
if (ap_req.ticket.enc_part.kvno)
kvno = *ap_req.ticket.enc_part.kvno;
if (inctx && inctx->keytab)
id = inctx->keytab;
if(id == NULL) {
krb5_kt_default(context, &keytab);
id = keytab;
}
if (id == NULL)
goto out;
ret = krb5_kt_start_seq_get(context, id, &cursor);
if (ret)
goto out;
/*
* Interate over keytab to find a key that can decrypt the request.
*/
done = 0;
while (!done) {
krb5_principal p;
ret = krb5_kt_next_entry(context, id, &entry, &cursor);
if (ret)
goto out;
if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype ||
(kvno && kvno != entry.vno)) {
krb5_kt_free_entry (context, &entry);
continue;
}
ret = krb5_verify_ap_req2(context,
auth_context,
&ap_req,
server,
&entry.keyblock,
0,
&o->ap_req_options,
&o->ticket,
KRB5_KU_AP_REQ_AUTH);
if (ret) {
krb5_kt_free_entry (context, &entry);
continue;
}
/*
* Found a match, save the keyblock for PAC processing,
* and update the service principal in the ticket to match
* whatever is in the keytab.
*/
ret = krb5_copy_keyblock(context,
&entry.keyblock,
&o->keyblock);
if (ret) {
krb5_kt_free_entry (context, &entry);
goto out;
}
ret = krb5_copy_principal(context, entry.principal, &p);
if (ret) {
krb5_kt_free_entry (context, &entry);
goto out;
}
krb5_free_principal(context, o->ticket->server);
o->ticket->server = p;
krb5_kt_free_entry (context, &entry);
done = 1;
}
krb5_kt_end_seq_get (context, id, &cursor);
}
/* Save that principal that was in the request */
ret = _krb5_principalname2krb5_principal(context,
&o->server,
ap_req.ticket.sname,
ap_req.ticket.realm);
if (ret) if (ret)
goto out; goto out;
/* If there is a PAC, verify its server signature */ /* If there is a PAC, verify its server signature */
if (inctx->check_pac) { if (inctx->check_pac) {
krb5_pac pac; krb5_pac pac;
@@ -894,7 +1025,9 @@ out:
*outctx = o; *outctx = o;
free_AP_REQ(&ap_req); free_AP_REQ(&ap_req);
if(service)
krb5_free_principal(context, service); if (keytab)
krb5_kt_close(context, keytab);
return ret; return ret;
} }