Files
heimdal/kdc/kerberos5.c
Johan Danielsson c8fe1ef20f Rename contents to keyvalue.
git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@1896 ec53bebd-3082-4978-b11e-865c3cabbd6b
1997-06-10 15:09:40 +00:00

496 lines
12 KiB
C

#include "kdc_locl.h"
RCSID("$Id$");
#ifndef MIN
#define MIN(A,B) ((A)<(B)?(A):(B))
#endif
#define MAX_TIME ((time_t)((1U << 31) - 1))
krb5_error_code
krb5_encrypt (krb5_context context,
void *ptr,
size_t len,
krb5_keyblock *keyblock,
krb5_data *result);
krb5_error_code
as_rep(krb5_context context,
KDC_REQ *req,
krb5_data *data)
{
KDC_REQ_BODY *b = &req->req_body;
AS_REP rep;
KDCOptions f = b->kdc_options;
hdb_entry *client, *server;
int use_etype;
EncTicketPart *et = calloc(1, sizeof(*et));
EncKDCRepPart *ek = calloc(1, sizeof(*ek));
krb5_principal client_princ;
client = db_fetch(context, b->cname, b->realm);
server = db_fetch(context, b->sname, b->realm);
if(client == NULL)
return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
if(server == NULL){
hdb_free_entry(context, client);
free(client);
return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
}
principalname2krb5_principal (&client_princ, *(b->cname), b->realm);
/* XXX Check for pa_enc_timestamp */
if(req->padata == NULL || req->padata->len < 1 ||
req->padata->val->padata_type != pa_enc_timestamp) {
PA_DATA foo;
u_char buf[16];
int len;
krb5_data foo_data;
foo.padata_type = pa_enc_timestamp;
foo.padata_value.length = 0;
foo.padata_value.data = NULL;
len = encode_PA_DATA(buf + sizeof(buf) - 1,
sizeof(buf),
&foo);
foo_data.length = len;
foo_data.data = buf + sizeof(buf) - len;
krb5_mk_error (client_princ,
KRB5KDC_ERR_PREAUTH_REQUIRED,
"Need to use PA-ENC-TIMESTAMP",
&foo_data,
data);
return 0;
} else {
krb5_data ts_data;
PA_ENC_TS_ENC p;
int len;
EncryptedData enc_data;
len = decode_EncryptedData(req->padata->val->padata_value.data,
req->padata->val->padata_value.length,
&enc_data);
if (len < 0) {
krb5_mk_error (client_princ,
KRB5KRB_AP_ERR_BAD_INTEGRITY,
"Couldn't decode",
NULL,
data);
return 0;
}
krb5_decrypt (context,
enc_data.cipher.data,
enc_data.cipher.length,
&client->keyblock,
&ts_data);
len = decode_PA_ENC_TS_ENC(ts_data.data,
ts_data.length,
&p);
if (len < 0) {
krb5_mk_error (client_princ,
KRB5KRB_AP_ERR_BAD_INTEGRITY,
"Couldn't decode",
NULL,
data);
return 0;
}
if (kdc_time - p.patimestamp > 300) {
krb5_mk_error (client_princ,
KRB5KDC_ERR_PREAUTH_FAILED,
"Too large time skew",
NULL,
data);
return 0;
}
}
if(b->etype.len == 0)
return KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
use_etype = b->etype.val[0];
memset(&rep, 0, sizeof(rep));
rep.pvno = 5;
rep.msg_type = krb_as_rep;
copy_Realm(&b->realm, &rep.crealm);
copy_PrincipalName(b->cname, &rep.cname);
rep.ticket.tkt_vno = 5;
copy_Realm(&b->realm, &rep.ticket.realm);
copy_PrincipalName(b->sname, &rep.ticket.sname);
if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey)
return KRB5KDC_ERR_BADOPTION;
et->flags.initial = 1;
et->flags.forwardable = f.forwardable;
et->flags.proxiable = f.proxiable;
et->flags.may_postdate = f.allow_postdate;
mk_des_keyblock(&et->key);
copy_PrincipalName(b->cname, &et->cname);
copy_Realm(&b->realm, &et->crealm);
{
time_t start;
time_t t;
start = et->authtime = kdc_time;
if(f.postdated && req->req_body.from){
et->starttime = malloc(sizeof(*et->starttime));
start = *et->starttime = *req->req_body.from;
et->flags.invalid = 1;
et->flags.postdated = 1; /* XXX ??? */
}
if(b->till == 0)
b->till = MAX_TIME;
t = b->till;
if(client->max_life)
t = MIN(t, start + client->max_life);
if(server->max_life)
t = MIN(t, start + server->max_life);
#if 0
t = MIN(t, start + realm->max_life);
#endif
et->endtime = t;
if(f.renewable_ok && et->endtime < b->till){
f.renewable = 1;
if(b->rtime == NULL){
b->rtime = malloc(sizeof(*b->rtime));
*b->rtime = 0;
}
if(*b->rtime < b->till)
*b->rtime = b->till;
}
if(f.renewable && b->rtime){
t = *b->rtime;
if(t == 0)
t = MAX_TIME;
if(client->max_renew)
t = MIN(t, start + client->max_renew);
if(server->max_renew)
t = MIN(t, start + server->max_renew);
#if 0
t = MIN(t, start + realm->max_renew);
#endif
et->renew_till = malloc(sizeof(*et->renew_till));
*et->renew_till = t;
}
}
if(b->addresses){
et->caddr = malloc(sizeof(*et->caddr));
copy_HostAddresses(b->addresses, et->caddr);
}
memset(ek, 0, sizeof(*ek));
copy_EncryptionKey(&et->key, &ek->key);
/* MIT must have at least one last_req */
ek->last_req.len = 1;
ek->last_req.val = malloc(sizeof(*ek->last_req.val));
ek->last_req.val->lr_type = 0;
ek->last_req.val->lr_value = 0;
ek->nonce = b->nonce;
ek->flags = et->flags;
ek->authtime = et->authtime;
ek->starttime = et->starttime;
ek->endtime = et->endtime;
ek->renew_till = et->renew_till;
copy_Realm(&rep.ticket.realm, &ek->srealm);
copy_PrincipalName(&rep.ticket.sname, &ek->sname);
if(et->caddr){
ek->caddr = malloc(sizeof(*ek->caddr));
copy_HostAddresses(et->caddr, ek->caddr);
}
{
unsigned char buf[1024]; /* XXX The data could be indefinite */
int len;
len = encode_EncTicketPart(buf + sizeof(buf) - 1, sizeof(buf), et);
free_EncTicketPart(et);
free(et);
if(len < 0)
return ASN1_OVERFLOW;
rep.ticket.enc_part.etype = ETYPE_DES_CBC_CRC;
rep.ticket.enc_part.kvno = NULL;
krb5_encrypt(context, buf + sizeof(buf) - len, len, &server->keyblock,
&rep.ticket.enc_part.cipher);
len = encode_EncASRepPart(buf + sizeof(buf) - 1, sizeof(buf), ek);
free_EncKDCRepPart(ek);
free(ek);
if(len < 0)
return ASN1_OVERFLOW;
rep.enc_part.etype = ETYPE_DES_CBC_CRC;
rep.enc_part.kvno = NULL;
krb5_encrypt(context, buf + sizeof(buf) - len, len, &client->keyblock,
&rep.enc_part.cipher);
len = encode_AS_REP(buf + sizeof(buf) - 1, sizeof(buf), &rep);
if(len < 0)
return ASN1_OVERFLOW;
free_AS_REP(&rep);
krb5_data_copy(data, buf + sizeof(buf) - len, len);
}
return 0;
}
krb5_error_code
tgs_rep(krb5_context context,
KDC_REQ *req,
krb5_data *data)
{
KDC_REQ_BODY *b = &req->req_body;
KDCOptions f = req->req_body.kdc_options;
EncTicketPart *tgt;
hdb_entry *server, *krbtgt, *client;
TGS_REP rep;
EncTicketPart *et = calloc(1, sizeof(*et));
EncKDCRepPart *ek = calloc(1, sizeof(*ek));
if(req->padata == NULL || req->padata->len < 1)
return KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
if(req->padata->val->padata_type != pa_tgs_req)
return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
{
krb5_auth_context ac = NULL;
krb5_principal princ;
krb5_flags ap_req_options;
krb5_ticket *ticket;
krb5_error_code err;
hdb_entry *ent;
err = krb5_build_principal(context,
&princ,
strlen(req->req_body.realm),
req->req_body.realm,
"krbtgt",
req->req_body.realm,
NULL);
if(err) return err;
{
PrincipalName p;
p.name_string.val = calloc(2, sizeof(*p.name_string.val));
p.name_string.len = 2;
p.name_string.val[0] = "krbtgt";
p.name_string.val[1] = req->req_body.realm;
krbtgt = db_fetch(context, &p, req->req_body.realm);
free(p.name_string.val);
}
if(ent == NULL)
return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
err = krb5_rd_req_with_keyblock(context, &ac,
&req->padata->val->padata_value,
princ,
&krbtgt->keyblock,
&ap_req_options,
&ticket);
if(err)
return err;
/* XXX Check authenticator */
server = db_fetch(context, b->sname, b->realm);
if(server == NULL)
return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
tgt = &ticket->tkt;
client = db_fetch(context, &tgt->cname, tgt->crealm);
if(client == NULL)
return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
memset(&rep, 0, sizeof(rep));
rep.pvno = 5;
rep.msg_type = krb_tgs_rep;
rep.crealm = tgt->crealm;
rep.cname = tgt->cname;
rep.ticket.tkt_vno = 5;
rep.ticket.sname = *b->sname;
rep.ticket.realm = b->realm;
et->caddr = ticket->tkt.caddr;
if(f.forwardable){
if(!tgt->flags.forwardable)
return KRB5KDC_ERR_BADOPTION;
et->flags.forwardable = 1;
}
if(f.forwarded){
if(!tgt->flags.forwardable)
return KRB5KDC_ERR_BADOPTION;
et->flags.forwarded = 1;
et->caddr = req->req_body.addresses;
/* resp.caddr := req.addresses */
}
et->flags.forwarded = tgt->flags.forwarded;
if(f.proxiable){
if(!tgt->flags.proxiable)
return KRB5KDC_ERR_BADOPTION;
et->flags.proxiable = 1;
}
if(f.proxy){
if(!tgt->flags.proxiable)
return KRB5KDC_ERR_BADOPTION;
et->flags.proxy = 1;
et->caddr = req->req_body.addresses;
/* resp.caddr := req.addresses */
}
if(f.allow_postdate){
if(!tgt->flags.may_postdate)
return KRB5KDC_ERR_BADOPTION;
et->flags.may_postdate = 1;
}
if(f.postdated){
if(!tgt->flags.may_postdate)
return KRB5KDC_ERR_BADOPTION;
et->flags.postdated = 1;
et->flags.invalid = 1;
et->starttime = malloc(sizeof(*et->starttime));
*et->starttime = *req->req_body.from;
}
if(f.validate){
if(!tgt->flags.invalid)
return KRB5KDC_ERR_BADOPTION;
if(*tgt->starttime > kdc_time)
return KRB5KRB_AP_ERR_TKT_NYV;
/* XXX tkt = tgt */
et->flags.invalid = 0;
}
/* check for excess flags */
et->authtime = tgt->authtime;
if(f.renew){
time_t old_life;
if(!tgt->flags.renewable)
return KRB5KDC_ERR_BADOPTION;
if(*tgt->renew_till >= kdc_time)
return KRB5KRB_AP_ERR_TKT_EXPIRED;
/* XXX tkt = tgt */
et->starttime = malloc(sizeof(*et->starttime));
*et->starttime = kdc_time;
old_life = tgt->endtime - *tgt->starttime;
et->endtime = MIN(*tgt->renew_till,
*et->starttime + old_life);
}else{
time_t till;
et->starttime = malloc(sizeof(*et->starttime));
*et->starttime = kdc_time;
till = b->till;
if(till == 0)
till = MAX_TIME;
if(client->max_life)
till = MIN(till, *et->starttime + client->max_life);
if(server->max_life)
till = MIN(till, *et->starttime + server->max_life);
till = MIN(till, tgt->endtime);
#if 0
till = MIN(till, et->starttime + realm->max_life);
#endif
et->endtime = till;
if(f.renewable_ok &&
et->endtime < b->till &&
tgt->flags.renewable){
f.renewable = 1;
b->rtime = malloc(sizeof(*b->rtime));
*b->rtime = MIN(b->till, *tgt->renew_till);
}
}
if(f.renewable && tgt->flags.renewable && b->rtime){
time_t rtime;
rtime = *b->rtime;
if(rtime == 0)
rtime = MAX_TIME;
et->flags.renewable = 1;
if(client->max_renew)
rtime = MIN(rtime, *et->starttime + client->max_renew);
if(server->max_renew)
rtime = MIN(rtime, *et->starttime + server->max_renew);
rtime = MIN(rtime, *tgt->renew_till);
#if 0
rtime = MIN(rtime, *et->starttime + realm->max_renew);
#endif
et->renew_till = malloc(sizeof(*et->renew_till));
*et->renew_till = rtime;
}
/* XXX Check enc-authorization-data */
mk_des_keyblock(&et->key);
et->crealm = tgt->crealm;
et->cname = tgt->cname;
et->transited = tgt->transited;
memset(ek, 0, sizeof(*ek));
ek->key = et->key;
/* MIT must have at least one last_req */
ek->last_req.len = 1;
ek->last_req.val = malloc(sizeof(*ek->last_req.val));
ek->last_req.val->lr_type = 0;
ek->last_req.val->lr_value = 0;
ek->nonce = b->nonce;
ek->flags = et->flags;
ek->authtime = et->authtime;
ek->starttime = et->starttime;
ek->endtime = et->endtime;
ek->renew_till = et->renew_till;
ek->srealm = rep.ticket.realm;
ek->sname = rep.ticket.sname;
ek->caddr = et->caddr;
{
unsigned char buf[1024]; /* XXX The data could be indefinite */
int len;
len = encode_EncTicketPart(buf + sizeof(buf) - 1, sizeof(buf), et);
if(len < 0)
return ASN1_OVERFLOW;
rep.ticket.enc_part.etype = ETYPE_DES_CBC_CRC;
rep.ticket.enc_part.kvno = NULL;
krb5_encrypt(context, buf + sizeof(buf) - len, len, &server->keyblock,
&rep.ticket.enc_part.cipher);
len = encode_EncTGSRepPart(buf + sizeof(buf) - 1, sizeof(buf), ek);
if(len < 0)
return ASN1_OVERFLOW;
rep.enc_part.etype = ETYPE_DES_CBC_CRC;
rep.enc_part.kvno = NULL;
{
krb5_keyblock kb;
kb.keytype = tgt->key.keytype;
kb.keyvalue = tgt->key.keyvalue;
krb5_encrypt(context, buf + sizeof(buf) - len, len, &kb,
&rep.enc_part.cipher);
}
len = encode_TGS_REP(buf + sizeof(buf) - 1, sizeof(buf), &rep);
if(len < 0)
return ASN1_OVERFLOW;
free_TGS_REP(&rep);
krb5_data_copy(data, buf + sizeof(buf) - len, len);
}
return 0;
}
}