diff --git a/Makefile b/Makefile index a30a91696..65f5e135d 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,8 @@ CFLAGS=-g YFLAGS = -d -SOURCES = principal.c principal_p.c data.c context.c misc.c string2key.c +SOURCES = principal.c principal_p.c data.c context.c misc.c string2key.c \ + krbhst.c getport.c send_to_kdc.c OBJECTS = $(SOURCES:%.c=%.o) config_file.o diff --git a/d.c b/d.c new file mode 100644 index 000000000..5ec9dab51 --- /dev/null +++ b/d.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include + +int +buf_getbyte (Buffer *b) +{ + if (b->p >= b->buf + b->len) + return EOF; + return *b->p++; +} + +void +buf_init (Buffer *b, char *ptr, unsigned len) +{ + b->buf = b->p = ptr; + b->len = len; +} + +Buffer * +buf_derive (Buffer *b, Buffer *tmp, int len) +{ + tmp->buf = tmp->p = b->p; + if(len == -1) + tmp->len = buf_bytesleft (b); + else + tmp->len = len; + return tmp; +} + +int +buf_bytesleft (Buffer *b) +{ + return b->len - (b->p - b->buf); +} + +void +buf_advance (Buffer *b, int n) +{ + b->p += n; +} + +int +buf_length (Buffer *b) +{ + return b->p - b->buf; +} + +Identifier * +getid (Buffer *b, Identifier *i) +{ + int c, len; + + c = buf_getbyte (b); + if (c == EOF) + return NULL; + i->class = c >> 6; + i->type = (c >> 5) & 1; + i->tag = c & 0x1F; + if (i->tag == 0x1F) { + do { + c = buf_getbyte (b); + if (c == EOF) + return NULL; + i->tag = i->tag * 0x80 + (c & 0x7F); + } while( c & 0x80); + } + + c = buf_getbyte (b); + if (c == EOF) + return NULL; + len = c; + if (len < 0x80) { + i->len = len; + } else if(len > 0x80) { + len &= 0x7F; + i->len = 0; + while (len--) { + c = buf_getbyte (b); + if (c == EOF) + return NULL; + i->len = i->len * 0x100 + c; + } + } else if (len == 0x80) + i->len = -1; + return i; +} + +Identifier * +matchid (Buffer *b, Identifier *i) +{ + Identifier tmp; + + if (getid (b, &tmp) == NULL) + return NULL; + if (tmp.class == i->class && tmp.type == i->type && tmp.tag == i->tag) { + i->len = tmp.len; + return i; + } else + return NULL; +} + +static Identifier dummy; + +Identifier * +matchid3 (Buffer *b, Identifier *i, Der_class class, Der_type type, + unsigned tag) +{ + i->class = class; + i->type = type; + i->tag = tag; + return matchid (b, i); +} + +Identifier * +matchcontextid (Buffer *b, Identifier *i, int tag) +{ + Identifier tmp; + + if (matchid3 (b, &tmp, CONTEXT, CONS, tag) == NULL || + matchid (b, i) == NULL) + return NULL; + return i; +} + +Identifier * +matchcontextid3 (Buffer *b, Identifier *i, Der_class class, + Der_type dtype, + int type, + unsigned tag) +{ + i->class = class; + i->type = dtype; + i->tag = type; + return matchcontextid (b, i, tag); +} + +int +der_get_integer (Buffer *b, void *val) +{ + int c; + int res; + int len; + + res = len = 0; + while ((c = buf_getbyte (b)) != EOF) { + res = res * 0x100 + c; + ++len; + } + *((int *)val) = res; + return len; +} + +int +der_get_octetstring (Buffer *b, void *val) +{ + krb5_data *str = (krb5_data *)val; + int len, c; + char *p; + + len = buf_bytesleft (b); + str->len = len; + str->data = p = malloc (len + 1); + while (len && (c = buf_getbyte (b)) != EOF) { + *p++ = c; + --len; + } + *p++ = '\0'; + return len; +} + +int +der_get_generalizedtime (Buffer *b, void *val) +{ + time_t *t = (time_t *)val; + int len; + krb5_data str; + struct tm tm; + + len = der_get_octetstring (b, &str); + sscanf (str.data, "%04d%02d%02d%02d%02d%02dZ", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, + &tm.tm_min, &tm.tm_sec); + tm.tm_year -= 1900; + tm.tm_mon -= 1; + tm.tm_isdst = 0; + + *t = mktime (&tm); + *t -= timezone; + + string_free (str); + return len; +} + +static int (*get_univ_funcs[])(Buffer *, void *val) = { + NULL, /* 0 */ + NULL, /* 1 */ + der_get_integer, /* 2 */ + NULL, /* 3 */ + der_get_octetstring, /* 4 */ + NULL, /* 5 */ + NULL, /* 6 */ + NULL, /* 7 */ + NULL, /* 8 */ + NULL, /* 9 */ + NULL, /* 10 */ + NULL, /* 11 */ + NULL, /* 12 */ + NULL, /* 13 */ + NULL, /* 14 */ + NULL, /* 15 */ + NULL, /* 16 */ + NULL, /* 17 */ + NULL, /* 18 */ + NULL, /* 19 */ + NULL, /* 20 */ + NULL, /* 21 */ + NULL, /* 22 */ + NULL, /* 23 */ + der_get_generalizedtime, /* 24 */ + NULL, /* 25 */ + NULL, /* 26 */ + der_get_octetstring, /* 27 */ +}; + +int +der_get_val (Buffer *b, int type, void *val) +{ + return (*(get_univ_funcs[type]))(b, val); +} + +void +getzeros (Buffer *b, int len) +{ + if (len == -1) { + buf_getbyte (b); + buf_getbyte (b); + } +} + +int +getdata (Buffer *b, Identifier *i, void *arg) +{ + Buffer tmp; + int res; + + buf_derive (b, &tmp, i->len); + res = der_get_val (&tmp, i->tag, arg); + if (i->len == -1) { + getzeros (b, i->len); + buf_advance (b, res); + } else + buf_advance (b, i->len); + return res; +} diff --git a/d.h b/d.h new file mode 100644 index 000000000..4432c6de5 --- /dev/null +++ b/d.h @@ -0,0 +1,27 @@ +#include + +struct Buffer { + unsigned char *buf; + unsigned char *p; + unsigned len; +}; + +typedef struct Buffer Buffer; + +int buf_getbyte (Buffer *); +void buf_init (Buffer *, char *, unsigned); +Buffer *buf_derive (Buffer *, Buffer *, int); +int buf_bytesleft (Buffer *b); +void buf_advance (Buffer *b, int n); +int buf_length (Buffer *b); + +struct Identifier { + Der_class class; + Der_type type; + unsigned tag; + int len; +}; + +typedef struct Identifier Identifier; + +Identifier *getid (Buffer *b, Identifier *i); diff --git a/der.c b/der.c new file mode 100644 index 000000000..2d4f364cc --- /dev/null +++ b/der.c @@ -0,0 +1,29 @@ +#include +#include +#include + +/* + * Type functions + */ + +krb5_data +string_make_n (int n, char *s) +{ + krb5_data ret; + + ret.len = n; + ret.data = s; + return ret; +} + +krb5_data +string_make (char *s) +{ + return string_make_n (strlen (s), s); +} + +void +string_free (krb5_data s) +{ + free (s.data); +} diff --git a/der.h b/der.h new file mode 100644 index 000000000..30ed912e3 --- /dev/null +++ b/der.h @@ -0,0 +1,30 @@ +#ifndef DER_H + +#define DER_H + +typedef enum {UNIV = 0, APPL = 1, CONTEXT = 2 , PRIVATE = 3} Der_class; + +typedef enum {PRIM = 0, CONS = 1} Der_type; + +/* Universal tags */ + +enum { + UT_Integer = 2, + UT_BitString = 3, + UT_OctetString = 4, + UT_Null = 5, + UT_ObjID = 6, + UT_Sequence = 16, + UT_Set = 17, + UT_PrintableString = 19, + UT_IA5String = 22, + UT_UTCTime = 23, + UT_GeneralizedTime = 24, + UT_GeneralString = 27, +}; + +krb5_data string_make (char *); +krb5_data string_make_n (int len, char *); +void string_free (krb5_data); + +#endif /* DER_H */ diff --git a/der_get.c b/der_get.c new file mode 100644 index 000000000..6b80d80a3 --- /dev/null +++ b/der_get.c @@ -0,0 +1,179 @@ +#include +#include +#include + +/* + * Functions for parsing DER + */ + +unsigned +der_get_length (unsigned char *ptr, unsigned *res) +{ + unsigned char *p = ptr; + unsigned char c; + + c = *p++; + if (c < 0x80) { + *res = c; + return 1; + } else { + c &= 0x7F; + *res = 0; + while (c--) + *res = *res * 0x100 + *p++; + return p - ptr; + } +} + +unsigned +der_get_tag (unsigned char *ptr, Der_class *class, Der_type *type, + unsigned *tag) +{ + unsigned char *p = ptr; + unsigned char o1; + + o1 = *p++; + *class = o1 >> 6; + *type = (o1 >> 5) & 1; + *tag = o1 & 0x1F; + if (*tag == 0x1F) { + do { + o1 = *p++; + *tag = *tag * 0x80 + (o1 & 0x7F); + } while( o1 & 0x80); + } + return p - ptr; +} + +unsigned +der_get_integer (unsigned char *ptr, int len, void *v) +{ + unsigned char *p = ptr; + unsigned *res = v; + + *res = 0; + while(len--) + *res = *res * 0x100 + *p++; + return p - ptr; +} + +unsigned +der_get_octetstring (unsigned char *ptr, int len, void *v) +{ + unsigned char *p = ptr; + krb5_data *res = v; + + res->data = malloc(len + 1); + res->len = len; + memcpy (*res, p, len); + (*res)[len] = '\0'; + p += len; + + return p - ptr; +} + +static unsigned (*get_univ_funcs[])(unsigned char *, int len, void *val) = { + NULL, /* 0 */ + NULL, /* 1 */ + der_get_integer, /* 2 */ + NULL, /* 3 */ + der_get_octetstring, /* 4 */ + NULL, /* 5 */ + NULL, /* 6 */ + NULL, /* 7 */ + NULL, /* 8 */ + NULL, /* 9 */ + NULL, /* 10 */ + NULL, /* 11 */ + NULL, /* 12 */ + NULL, /* 13 */ + NULL, /* 14 */ + NULL, /* 15 */ + NULL, /* 16 */ + NULL, /* 17 */ + NULL, /* 18 */ + NULL, /* 19 */ + NULL, /* 20 */ + NULL, /* 21 */ + NULL, /* 22 */ + NULL, /* 23 */ + der_get_octetstring, /* 24 */ + NULL, /* 25 */ + NULL, /* 26 */ + der_get_octetstring, /* 27 */ +}; + +unsigned +der_get_val (unsigned char *ptr, int type, int len, void *val) +{ + return (*(get_univ_funcs[type]))(ptr, len, val); +} + +unsigned +der_get_type (unsigned char *ptr, Der_class *class, Der_type *type, + unsigned *tag, unsigned *len) +{ + unsigned char *p = ptr; + + return p - ptr; +} + +int +der_match_type (unsigned char **ptr, Der_class class, Der_type type, + unsigned tag, unsigned *len) +{ + unsigned char *p = ptr; + Der_class c1; + Der_type t1; + unsigned tag1; + + p += der_get_tag (p, &c1, &t1, &tag1); + if (c1 != class || t1 != type || tag != tag1) + return -1; + p += der_get_length (p, len); + + return p - ptr; +} + +int +der_get_context (unsigned char *ptr, unsigned *tag, unsigned *type, + unsigned *len) +{ + unsigned char *p = ptr; + Der_class class; + Der_type foo; + + p += der_get_tag (p, &class, &foo, tag); + if (class != CONTEXT || foo != CONS ) + return -1; + p += der_get_length (p, len); + p += der_get_tag (p, &class, &foo, type); + if (class != UNIV || foo != PRIM) + return -1; + p += der_get_length (p, len); + + return p - ptr; +} + +int +der_match_context (unsigned char *ptr, unsigned tag, int type, void *arg) +{ + unsigned char *p = ptr; + int len; + int tlen; + + len = der_match_type (p, CONTEXT, CONS, tag, &tlen); + if (len < 0) + return len; + else + p += len; + len = der_match_type (p, UNIV, PRIM, type, &tlen); + if (len < 0) + return len; + else + p += len; + + p += der_get_val (p, type, tlen, arg); + + return p - ptr; +} diff --git a/e.c b/e.c new file mode 100644 index 000000000..57a3829c8 --- /dev/null +++ b/e.c @@ -0,0 +1,384 @@ +#include +#include +#include +#include +#include + +int +der_get_principalname (Buffer *b, PrincipalName *p) +{ + Identifier i; + int cur, max; + char *str; + int len; + + p->num_strings = 0; + + if (matchid3 (b, &i, UNIV, CONS, UT_Sequence) == NULL) + return -1; + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 0) == NULL) + return -1; + getdata (b, &i, &p->name_type); + if (matchcontextid3 (b, &i, UNIV, CONS, UT_Sequence, 1) == NULL) + return -1; + cur = 0; + max = 1; + p->names = malloc(sizeof(char *) * max); + while (matchid3 (b, &i, UNIV, PRIM, UT_GeneralString)) { + if (cur >= max) { + max *= 2; + p->names = realloc (p->names, sizeof(char *) * max); + } + getdata (b, &i, &p->names[cur++]); + } + p->num_strings = cur; + return buf_length (b); +} + +int +der_get_encrypteddata (Buffer *b, EncryptedData *e) +{ + Identifier i0, i1, i; + int len; + + if (matchid3 (b, &i0, UNIV, CONS, UT_Sequence) == NULL) + return -1; + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 0) == NULL) + return -1; + getdata (b, &i, &e->etype); + getid (b, &i); + if (i.tag == 1) { + int kvno; + + if (matchid3 (b, &i, UNIV, PRIM, UT_Integer) == NULL) + return -1; + getdata (b, &i, &kvno); + e->kvno = malloc (sizeof (int)); + *(e->kvno) = kvno; + } else + e->kvno = NULL; + if (matchid3 (b, &i1, CONTEXT, CONS, 2) == NULL) + return -1; + if (matchid3 (b, &i, UNIV, PRIM, UT_OctetString) == NULL) + return -1; + getdata (b, &i, &e->cipher); + getzeros (b, i1.len); + getzeros (b, i0.len); + return buf_length (b); +} + +int +der_get_ticket (Buffer *b, Ticket *t) +{ + Identifier i0, i1, i; + Buffer tmp; + int len; + + if (matchid3 (b, &i0, APPL, CONS, APPL_TICKET) == NULL) + return -1; + if (matchid3 (b, &i1, UNIV, CONS, UT_Sequence) == NULL) + return -1; + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 0) == NULL) + return -1; + getdata (b, &i, &t->tkt_vno); + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_GeneralString, 1) == NULL) + return -1; + getdata (b, &i, &t->realm); + if (matchid3 (b, &i, CONTEXT, CONS, 2) == NULL) + return -1; + buf_derive(b, &tmp, i.len); + len = der_get_principalname (&tmp, &t->sname); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + if (matchid3 (b, &i, CONTEXT, CONS, 3) == NULL) + return -1; + buf_derive (b, &tmp, i.len); + len = der_get_encrypteddata (&tmp, &t->enc_part); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + getzeros (b, i1.len); + getzeros (b, i0.len); + return buf_length (b); +} + +int +der_get_kdc_rep (Buffer *b, int msg, Kdc_Rep *k) +{ + Identifier i, i0; + Buffer tmp; + int len; + + if (matchid3 (b, &i0, UNIV, CONS, UT_Sequence) == NULL) + return -1; + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 0) == NULL) + return -1; + + getdata (b, &i, &k->pvno); + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 1) == NULL) + return -1; + getdata (b, &i, &k->msg_type); + if (k->msg_type != msg) + return -1; + getid (b, &i); + if (i.tag == 2) + abort (); /* XXX */ + if (i.tag != 3) + return -1; + if (matchid3 (b, &i, UNIV, PRIM, UT_GeneralString) == NULL) + return -1; + getdata (b, &i, &k->realm); + if (matchid3 (b, &i, CONTEXT, CONS, 4) == NULL) + return -1; + buf_derive(b, &tmp, i.len); + len = der_get_principalname (&tmp, &k->cname); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + if (matchid3 (b, &i, CONTEXT, CONS, 5) == NULL) + return -1; + buf_derive(b, &tmp, i.len); + len = der_get_ticket (&tmp, &k->ticket); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + if (matchid3 (b, &i, CONTEXT, CONS, 6) == NULL) + return -1; + buf_derive(b, &tmp, i.len); + len = der_get_encrypteddata (&tmp, &k->enc_part); + if(len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + getzeros (b, i0.len); + return buf_length (b); +} + +static int +der_get_kdc_rep_msg (Buffer *b, int msg, Kdc_Rep *a) +{ + Identifier i; + + if (matchid3(b, &i, APPL, CONS, msg) == NULL) + return -1; + return der_get_kdc_rep (b, msg, a); +} + +int +der_get_as_rep (Buffer *b, As_Rep *a) +{ + return der_get_kdc_rep_msg (b, KRB_AS_REP, a); +} + +int +der_get_tgs_rep (Buffer *b, Tgs_Rep *a) +{ + return der_get_kdc_rep_msg (b, KRB_TGS_REP, a); +} + +int +der_get_encryptionkey (Buffer *b, EncryptionKey *k) +{ + Identifier i; + + if (matchid3 (b, &i, UNIV, CONS, UT_Sequence) == NULL) + return -1; + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 0) == NULL) + return -1; + getdata (b, &i, &k->keytype); + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_OctetString, 1) == NULL) + return -1; + getdata (b, &i, &k->keyvalue); + return buf_length (b); +} + +int +der_get_hostaddresses (Buffer *b, HostAddresses *h) +{ + Identifier i; + int cur, max; + + if (matchid3 (b, &i, UNIV, CONS, UT_Sequence) == NULL) + return -1; + cur = 0; + max = 1; + h->addrs = malloc (sizeof (*h->addrs)); + while (matchid3 (b, &i, UNIV, CONS, UT_Sequence)) { + if (cur >= max) { + max *= 2; + h->addrs = realloc (h->addrs, sizeof(*h->addrs) * max); + } + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 0) == NULL) + return -1; + getdata (b, &i, &h->addrs[cur].addr_type); + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_OctetString, 1) == NULL) + return -1; + getdata (b, &i, &h->addrs[cur].addr); + ++cur; + } + h->number = cur; + return buf_length (b); +} + +int +der_get_lastreq (Buffer *b, LastReq *l) +{ + Identifier i; + int cur, max; + + if (matchid3 (b, &i, UNIV, CONS, UT_Sequence) == NULL) + return -1; + cur = 0; + max = 1; + l->values = malloc (sizeof(*l->values)); + while (matchid3 (b, &i, UNIV, CONS, UT_Sequence)) { + if (cur >= max) { + max *= 2; + l->values = realloc (l->values, sizeof (*l->values) * max); + } + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 0) == NULL) + return -1; + getdata (b, &i, &l->values[cur].lr_type); + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_GeneralizedTime, 1) == NULL) + return -1; + getdata (b, &i, &l->values[cur].lr_value); + ++cur; + } + l->number = cur; + return buf_length (b); +} + +int +der_get_ticketflags (Buffer *b, TicketFlags *t) +{ + Identifier i; + + return buf_bytesleft (b); +} + +int +der_get_enckdcreppart (Buffer *b, int msg, EncKdcRepPart *a) +{ + Identifier i; + Buffer tmp; + int len; + + if (matchid3 (b, &i, UNIV, CONS, UT_Sequence) == NULL) + return -1; + if (matchid3 (b, &i, CONTEXT, CONS, 0) == NULL) + return -1; + buf_derive (b, &tmp, i.len); + len = der_get_encryptionkey (&tmp, &a->key); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + if (matchid3 (b, &i, CONTEXT, CONS, 1) == NULL) + return -1; + buf_derive (b, &tmp, i.len); + len = der_get_lastreq (&tmp, &a->req); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_Integer, 2) == NULL) + return -1; + getdata (b, &i, &a->nonce); + getid (b, &i); + if (i.tag == 3) { + if (matchid3 (b, &i, UNIV, PRIM, UT_GeneralizedTime) == NULL) + return NULL; + + a->key_expiration = malloc (sizeof(*a->key_expiration)); + getdata (b, &i, a->key_expiration); + getid (b, &i); + } else + a->key_expiration = NULL; + if (i.tag != 4) + return NULL; + buf_derive (b, &tmp, i.len); + len = der_get_ticketflags (&tmp, &a->flags); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + if (matchcontextid3 (b, &i, UNIV, PRIM, UT_GeneralizedTime, 5) == NULL) + return NULL; + getdata (b, &i, &a->authtime); + getid (b, &i); + if (i.tag == 6) { + if (matchid3 (b, &i, UNIV, PRIM, UT_GeneralizedTime) == NULL) + return NULL; + + a->starttime = malloc (sizeof(*a->starttime)); + getdata (b, &i, a->starttime); + getid (b, &i); + } else + a->starttime = NULL; + if (i.tag != 7) + return NULL; + if (matchid3 (b, &i, UNIV, PRIM, UT_GeneralizedTime) == NULL) + return NULL; + getdata (b, &i, &a->authtime); + getid (b, &i); + if (i.tag == 8) { + if (matchid3 (b, &i, UNIV, PRIM, UT_GeneralizedTime) == NULL) + return NULL; + + a->renew_till = malloc (sizeof(*a->renew_till)); + getdata (b, &i, a->renew_till); + getid (b, &i); + } else + a->renew_till = NULL; + if (i.tag != 9) + return NULL; + if (matchid3 (b, &i, UNIV, PRIM, UT_GeneralString) == NULL) + return NULL; + getdata (b, &i, &a->srealm); + if (matchid3 (b, &i, CONTEXT, CONS, 10) == NULL) + return NULL; + buf_derive(b, &tmp, i.len); + len = der_get_principalname (&tmp, &a->sname); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + getid (b, &i); + if (i.tag == 11) { + buf_derive (b, &tmp, i.len); + len = der_get_hostaddresses (&tmp, &a->caddr); + if (len == -1) + return -1; + buf_advance (b, len); + getzeros (b, i.len); + } else + a->caddr.number = 0; + return buf_length (b); +} + +static int +der_get_enckdcreppart_msg (Buffer *b, int msg, EncKdcRepPart *a) +{ + Identifier i; + + if (matchid3 (b, &i, APPL, CONS, msg) == NULL) + return -1; + return der_get_enckdcreppart (b, msg, a); +} + +int +der_get_encasreppart (Buffer *b, EncASRepPart *a) +{ + return der_get_enckdcreppart_msg (b, KRB_ENCASREPPART, a); +} + +int +der_get_enctgsreppart (Buffer *b, EncTGSRepPart *a) +{ + return der_get_enckdcreppart_msg (b, KRB_ENCKDCREPPART, a); +} diff --git a/get_in_tkt.c b/get_in_tkt.c index 8d630318d..3d5bf1c54 100644 --- a/get_in_tkt.c +++ b/get_in_tkt.c @@ -1,5 +1,35 @@ #include "krb5_locl.h" +static krb5_error_code +krb5_get_salt (krb5_principal princ, + krb5_data realm, + krb5_data *salt) +{ + size_t len; + int i; + krb5_error_code err; + char *p; + + len = realm->len; + for (i = 0; i < princ->ncomp; ++i) + len += princ->comp[i].length; + err = krb5_alloc (salt, len); + if (err) + return err; + p = salt->data; + strncpy (p, realm->data, realm->len); + p += realm->len; + for (i = 0; i < princ->cnomp; ++i) { + strncpy (p, princ->comp[i].data, princ->comp[i].length); + p += princ->comp[i].length; + } + return 0; +} + +/* + * + */ + krb5_error_code krb5_get_in_tkt(krb5_context context, krb5_flags options, @@ -14,5 +44,65 @@ krb5_get_in_tkt(krb5_context context, krb5_ccache ccache, krb5_kdc_rep **ret_as_reply) { - + As_Req a; + Kdc_Rep rep; + krb5_principal server; + krb5_data req, resp; + char buf[BUFSIZ]; + Buffer buf; + krb5_data salt; + krb5_keyblock *key; + + server.type = KRB_NT_SRV_INST; + server.ncomp = 2; + server.comp = malloc (sizeof(*server.comp) * server.ncomp); + server.comp[0] = string_make ("krbtgt"); + server.comp[1] = creds->client.realm; + + a.pvno = 5; + a.msg_type = KRB_AS_REQ; +/* a.kdc_options */ + a.cname = &creds->client; + a.sname = &server; + a.realm = creds->client.realm; + a.till = creds->times.endtime; + a.nonce = 17; + if (etypes) + a.etypes = etypes; + else + a.etypes = context->etypes; + if (addrs) + a.addresses = addrs; + else + a.addresses = krb5_get_all_client_addrs (); + + req.data = buf; + + req.len = der_put_as_req (req.data + sizeof(buf) - 1, &a); + string_free (server.comp[0]); + free (server.comp); + if (addrs == NULL) + free (a.addresses); + + err = krb5_sendto_kdc (context, &req, a.realm, &resp); + if (err) { + return err; + } + buf_init (&buffer, resp.data, resp.len); + if (der_get_as_rep (&buffer, &rep) == -1) { + return ASN1_PARSE_ERROR; + } + err = krb5_get_salt (creds->client, creds->client.realm, &salt); + if (err) + return err; + err = (*key_proc)(context, b.enc_part.etype, salt, keyseed, &key); + krb5_data_free (&salt); + if (err) + return err; + + err = (*decrypt_proc)(context, key, decryptarg, &rep); + memset (&key.contents.data, 0, key.contents.length); + krb5_data_free (&key.contents); + if (err) + return err; } diff --git a/get_in_tkt_pw.c b/get_in_tkt_pw.c new file mode 100644 index 000000000..dfe52d004 --- /dev/null +++ b/get_in_tkt_pw.c @@ -0,0 +1,41 @@ +#include "krb5_locl.h" + +static krb5_error_code +key_proc (krb5_context context, + krb5_keytype type, + krb5_data *salt, + krb5_const_pointer keyseed, + krb5_keyblock **key) +{ + krb5_error_code err; + char *password = (char *)keyseed; + char buf[BUFSIZ]; + + key = malloc (sizeof (*key)); + if (key == NULL) + return ENOMEM; + key->keytype = type; + if (password == NULL) { + des_read_pw_string (buf, sizeof(buf), "Password: ", 0); + password = buf; + } + err = krb5_string_to_key (password, salt, key); + memset (buf, 0, sizeof(buf)); + return err; +} + +krb5_error_code +krb5_get_in_tkt_with_password (krb5_context context, + krb5_flags options, + krb5_address *const *addrs, + const krb5_enctype *etypes, + const krb5_preauthtype *pre_auth_types, + const char *password, + krb5_ccache ccache, + krb5_creds *creds, + krb5_kdc-rep **ret_as_reply) +{ + return krb5_get_in_tkt (context, options, addrs, etypes, + pre_auth_types, key_proc, password, + NULL, NULL, creds, cache, ret_as_reply); +} diff --git a/get_port.c b/get_port.c new file mode 100644 index 000000000..767201672 --- /dev/null +++ b/get_port.c @@ -0,0 +1,17 @@ +#include + +int +krb5_getportbyname (const char *service, + const char *proto, + int default_port) +{ + struct servent *sp; + + if ((sp = getservbyname (service, proto)) == NULL) { + fprintf (stderr, "%s/%s unknown service, " + "using default port %d\n", service, proto, + ntohs(default_port)); + return default_port; + } else + return sp->s_port; +} diff --git a/k5_der.c b/k5_der.c new file mode 100644 index 000000000..35e9489ca --- /dev/null +++ b/k5_der.c @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include + +static void +time2generalizedtime (krb5_data *s, time_t t) +{ + struct tm *tm; + + s->data = malloc(16); + s->len = 15; + tm = gmtime (&t); + sprintf (s->data, "%04d%02d%02d%02d%02d%02dZ", tm->tm_year + 1900, + tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec); +} + +unsigned +der_put_context_etypes (unsigned char *ptr, int tag, + EncryptionType *etypes, unsigned num_etypes) +{ + unsigned char *p = ptr; + int i; + + for (i = num_etypes - 1; i >= 0; --i) + p -= der_put_type_and_value (p, UT_Integer, &etypes[i]); + p -= der_put_type (p, UNIV, CONS, UT_Sequence, ptr - p); + p -= der_put_type (p, CONTEXT, CONS, tag, ptr - p); + return ptr - p; +} + +unsigned +der_put_context_principalname (unsigned char *ptr, int tag, + PrincipalName *name) +{ + unsigned char *p = ptr; + int i; + + if (name == NULL) + return 0; + for (i = name->num_strings - 1; i >= 0; --i) + p -= der_put_type_and_value (p, UT_GeneralString, + &name->names[i]); + p -= der_put_type (p, UNIV, CONS, UT_Sequence, ptr - p); + p -= der_put_type (p, CONTEXT, CONS, 1, ptr - p); + p -= der_put_context (p, 0, UT_Integer, &name->name_type); + p -= der_put_type (p, UNIV, CONS, UT_Sequence, ptr - p); + p -= der_put_type (p, CONTEXT, CONS, tag, ptr - p); + return ptr - p; +} + +unsigned +der_put_context_kdcoptions (unsigned char *ptr, int tag, KdcOptions *k) +{ + unsigned char *p = ptr; + /* XXX */ + + *p-- = '\0'; + *p-- = '\0'; + *p-- = '\0'; + *p-- = '\0'; + *p-- = '\0'; + *p-- = 5; + *p-- = 3; + p -= der_put_type (p, CONTEXT, CONS, tag, ptr - p); + return ptr - p; +} + +unsigned +der_put_context_hostaddresses (unsigned char *ptr, int tag, + HostAddress *addrs, + unsigned naddr) +{ + unsigned char *p = ptr; + int i; + + for(i = naddr - 1; i >= 0; --i) { + p -= der_put_context (p, 1, UT_OctetString, &addrs[i].addr); + p -= der_put_context (p, 0, UT_Integer, &addrs[i].addr_type); + } + p -= der_put_type (p, UNIV, CONS, UT_Sequence, ptr - p); + p -= der_put_type (p, UNIV, CONS, UT_Sequence, ptr - p); + p -= der_put_type (p, CONTEXT, CONS, tag, ptr - p); + return ptr - p; +} + +unsigned +der_put_kdc_req_body (unsigned char *ptr, Kdc_Req *k) +{ + unsigned char *p = ptr; + unsigned random = 17; + + /* additional-tickets[11] SEQUENCE OF Ticket OPTIONAL */ + /* enc-authorization-data[10] EncryptedData OPTIONAL */ + p -= der_put_context_hostaddresses (p, 9, k->addrs, + k->num_addrs); + /* addresses[9] HostAddresses OPTIONAL */ + p -= der_put_context_etypes (p, 8, k->etypes, k->num_etypes); + p -= der_put_context (p, 7, UT_Integer, &random); + /* rtime[6] KerberosTime OPTIONAL */ + { + krb5_data t; + + time2generalizedtime (&t, k->till); + p -= der_put_context (p, 5, UT_GeneralizedTime, &t); + string_free (t); + } + /* from[4] KerberosTime OPTIONAL */ + p -= der_put_context_principalname (p, 3, k->sname); + p -= der_put_context (p, 2, UT_GeneralString, &k->realm); + p -= der_put_context_principalname (p, 1, k->cname); + p -= der_put_context_kdcoptions (p, 0, &k->kdc_options); + p -= der_put_type (p, UNIV, CONS, UT_Sequence, ptr - p); + return ptr - p; +} + +unsigned +der_put_kdc_req (unsigned char *ptr, int msg_type, Kdc_Req *k) +{ + unsigned char *p = ptr; + + p -= der_put_kdc_req_body (p, k); + p -= der_put_type (p, CONTEXT, CONS, 4, ptr - p); + /* padata[3] SEQUENCE OF PA-DATA OPTIONAL */ + p -= der_put_context (p, 2, UT_Integer, &k->msg_type); + p -= der_put_context (p, 1, UT_Integer, &k->pvno); + p -= der_put_type (p, UNIV, CONS, UT_Sequence, ptr - p); + return ptr - p; +} + +unsigned +der_put_as_req (unsigned char *ptr, As_Req *a) +{ + unsigned char *p = ptr; + + p -= der_put_kdc_req (p, a->msg_type, a); + p -= der_put_type (p, APPL, CONS, a->msg_type, ptr - p); + return ptr - p; +} + +#if 0 + +/* + * Get functions + */ + +int +der_get_principalname (unsigned char *ptr, Principalname *name) +{ + unsigned char *p = ptr; + unsigned char *p0; + int tlen, tlen2; + + len = der_match_type (p, UNIV, CONS, UT_Sequence, &tlen); + if (len < 0) + return len; + else + p += len; + p0 = p; + len = der_match_context (p, 0, UT_Integer, &name->name_type); + if (len < 0) + return len; + else + p += len; + len = der_match_type (p, CONTEXT, CONS, 1, &tlen2); + if (len < 0) + return len; + else + p =+ len; + len = der_match_type () + while(p < p0 + tlen) { + } + + + return ptr - p; +} + +int +der_get_kdc_rep (unsigned char *ptr, unsigned mylen, int msg_type, + Kdc_Rep *k) +{ + unsigned char *p = ptr; + unsigned tlen, slen; + int len; + unsigned kvno, msg1; + unsigned tag; + int type; + + len = der_match_type (p, UNIV, CONS, UT_Sequence, &tlen); + if (len < 0) + return len; + else + p += len; + len = der_match_context (p, 0, UT_Integer, &kvno); + if (len < 0) + return len; + else + p += len; + if (kvno != 5) + return -1; + len = der_match_context (p, 1, UT_Integer, &msg1); + if (len < 0) + return len; + else + p += len; + if (msg1 != msg_type) + return -1; + len = der_get_context (p, &tag, &type, &tlen); + if (len < 0) + return len; + else + p += len; + if (tag == 2) + abort (); /* XXX */ + else if (tag == 3) { + p += der_get_val (p, UT_GeneralString, tlen, &k->realm); + } + len = der_get_context (p, &tag, &type, &tlen); + if (len < 0) + return len; + else + p += len; + der_get_principalname + return p - ptr; +} + +int +der_get_as_rep (unsigned char *ptr, As_Rep *a) +{ + unsigned char *p = ptr; + int len; + unsigned tlen; + + len = der_match_type (p, APPL, CONS, KRB_AS_REP, &tlen); + if(len < 0) + return len; + else + p += len; + len = der_get_kdc_rep (p, tlen, KRB_AS_REP, a); + if (len < 0) + return len; + else + p += len; + + return p - ptr; +} + +#endif + diff --git a/k5_der.h b/k5_der.h new file mode 100644 index 000000000..67e8c32cc --- /dev/null +++ b/k5_der.h @@ -0,0 +1,189 @@ +#include +#include + +/* + * Message types. + */ + +enum { + KRB_AS_REQ = 10, + KRB_AS_REP = 11, + KRB_TGS_REQ = 12, + KRB_TGS_REP = 13, + KRB_AP_REQ = 14, + KRB_AP_REP = 15, + KRB_SAFE = 20, + KRB_PRIV = 21, + KRB_CRED = 22, + KRB_ENCASREPPART = 25, + KRB_ENCKDCREPPART = 26, + KRB_ERROR = 30 +}; + +/* + * Application + */ + +enum { + APPL_TICKET = 1 +}; + +struct HostAddress { + int addr_type; + krb5_data addr; +}; + +typedef struct HostAddress HostAddress; + +struct HostAddresses { + int number; + HostAddress *addrs; +}; + +typedef struct HostAddresses HostAddresses; + +struct PrincipalName { + int name_type; + unsigned num_strings; + krb5_data *names; +}; + +enum { + nt_unknown = 0, + nt_principal = 1, + nt_srv_inst = 2, + nt_srv_hst = 3, + nt_srv_xhst = 4, + nt_uid = 5 +}; + +typedef struct PrincipalName PrincipalName; + +struct KdcOptions { + unsigned + reserved : 1, + forwardable : 1, + forwarded : 1, + proxiable : 1, + proxy : 1, + allow_postdate : 1, + postdated : 1, + unused7 : 1, + renewable : 1, + unused9 : 1, + unused10 : 1, + unused11 : 1, + renewable_ok : 1, + enc_tkt_in_skey : 1, + renew : 1, + validate : 1; +}; + +typedef struct KdcOptions KdcOptions; + +typedef krb5_data Realm; + +typedef int EncryptionType; + +struct Kdc_Req { + int pvno; + int msg_type; + KdcOptions kdc_options; + PrincipalName *cname; + Realm realm; + PrincipalName *sname; + time_t till; + unsigned num_etypes; + EncryptionType *etypes; + HostAddress *addrs; + unsigned num_addrs; +}; + +typedef struct Kdc_Req Kdc_Req; + +typedef Kdc_Req As_Req; + +struct EncryptedData { + int etype; + int *kvno; + krb5_data cipher; +}; + +typedef struct EncryptedData EncryptedData; + +struct Ticket { + int tkt_vno; + Realm realm; + PrincipalName sname; + EncryptedData enc_part; +}; + +typedef struct Ticket Ticket; + +struct Kdc_Rep { + int pvno; + int msg_type; + Realm realm; + PrincipalName cname; + Ticket ticket; + EncryptedData enc_part; +}; + +typedef struct Kdc_Rep Kdc_Rep; + +typedef Kdc_Rep As_Rep; + +typedef Kdc_Rep Tgs_Rep; + +struct EncryptionKey { + int keytype; + krb5_data keyvalue; +}; + +typedef struct EncryptionKey EncryptionKey; + +struct LastReq { + int number; + struct { + int lr_type; + time_t lr_value; + } *values; +}; + +typedef struct LastReq LastReq; + +struct TicketFlags { + unsigned forwardable:1, + forwarded:1, + proxiable:1, + proxy:1, + may_postdate:1, + postdated:1, + invalid:1, + renewable:1, + initial:1, + pre_authent:1, + hw_authent:1; +}; + +typedef struct TicketFlags TicketFlags; + +struct EncKdcRepPart { + EncryptionKey key; + LastReq req; + int nonce; + time_t *key_expiration; + TicketFlags flags; + time_t authtime; + time_t *starttime; + time_t endtime; + time_t *renew_till; + Realm srealm; + PrincipalName sname; + HostAddresses caddr; +}; + +typedef struct EncKdcRepPart EncKdcRepPart; + +typedef EncKdcRepPart EncASRepPart; +typedef EncKdcRepPart EncTGSRepPart; diff --git a/krbhst.c b/krbhst.c new file mode 100644 index 000000000..c05714d56 --- /dev/null +++ b/krbhst.c @@ -0,0 +1,27 @@ +#include "krb5_locl.h" + +krb5_error_code +krb5_get_krbhst (krb5_context context, + const krb5_data *realm, + char ***hostlist) +{ + krb5_error_code err; + char buf[BUFSIZ]; + char *val; + + sprintf (buf, "realms %s kdc", realm.data); + err = krb5_get_config_tag (context.cf, buf, &val); + if (err) + return err; + **hostlist = malloc (2 * sizeof (char *)); + (*hostlist)[0] = val; + (*hostlist)[1] = NULL; + return 0; +} + +krb5_error_code +krb5_free_krbhst (krb5_context context, + char *const *hostlist) +{ + free (hostlist); +} diff --git a/lib/krb5/get_in_tkt.c b/lib/krb5/get_in_tkt.c index 8d630318d..3d5bf1c54 100644 --- a/lib/krb5/get_in_tkt.c +++ b/lib/krb5/get_in_tkt.c @@ -1,5 +1,35 @@ #include "krb5_locl.h" +static krb5_error_code +krb5_get_salt (krb5_principal princ, + krb5_data realm, + krb5_data *salt) +{ + size_t len; + int i; + krb5_error_code err; + char *p; + + len = realm->len; + for (i = 0; i < princ->ncomp; ++i) + len += princ->comp[i].length; + err = krb5_alloc (salt, len); + if (err) + return err; + p = salt->data; + strncpy (p, realm->data, realm->len); + p += realm->len; + for (i = 0; i < princ->cnomp; ++i) { + strncpy (p, princ->comp[i].data, princ->comp[i].length); + p += princ->comp[i].length; + } + return 0; +} + +/* + * + */ + krb5_error_code krb5_get_in_tkt(krb5_context context, krb5_flags options, @@ -14,5 +44,65 @@ krb5_get_in_tkt(krb5_context context, krb5_ccache ccache, krb5_kdc_rep **ret_as_reply) { - + As_Req a; + Kdc_Rep rep; + krb5_principal server; + krb5_data req, resp; + char buf[BUFSIZ]; + Buffer buf; + krb5_data salt; + krb5_keyblock *key; + + server.type = KRB_NT_SRV_INST; + server.ncomp = 2; + server.comp = malloc (sizeof(*server.comp) * server.ncomp); + server.comp[0] = string_make ("krbtgt"); + server.comp[1] = creds->client.realm; + + a.pvno = 5; + a.msg_type = KRB_AS_REQ; +/* a.kdc_options */ + a.cname = &creds->client; + a.sname = &server; + a.realm = creds->client.realm; + a.till = creds->times.endtime; + a.nonce = 17; + if (etypes) + a.etypes = etypes; + else + a.etypes = context->etypes; + if (addrs) + a.addresses = addrs; + else + a.addresses = krb5_get_all_client_addrs (); + + req.data = buf; + + req.len = der_put_as_req (req.data + sizeof(buf) - 1, &a); + string_free (server.comp[0]); + free (server.comp); + if (addrs == NULL) + free (a.addresses); + + err = krb5_sendto_kdc (context, &req, a.realm, &resp); + if (err) { + return err; + } + buf_init (&buffer, resp.data, resp.len); + if (der_get_as_rep (&buffer, &rep) == -1) { + return ASN1_PARSE_ERROR; + } + err = krb5_get_salt (creds->client, creds->client.realm, &salt); + if (err) + return err; + err = (*key_proc)(context, b.enc_part.etype, salt, keyseed, &key); + krb5_data_free (&salt); + if (err) + return err; + + err = (*decrypt_proc)(context, key, decryptarg, &rep); + memset (&key.contents.data, 0, key.contents.length); + krb5_data_free (&key.contents); + if (err) + return err; } diff --git a/lib/krb5/get_in_tkt_pw.c b/lib/krb5/get_in_tkt_pw.c new file mode 100644 index 000000000..dfe52d004 --- /dev/null +++ b/lib/krb5/get_in_tkt_pw.c @@ -0,0 +1,41 @@ +#include "krb5_locl.h" + +static krb5_error_code +key_proc (krb5_context context, + krb5_keytype type, + krb5_data *salt, + krb5_const_pointer keyseed, + krb5_keyblock **key) +{ + krb5_error_code err; + char *password = (char *)keyseed; + char buf[BUFSIZ]; + + key = malloc (sizeof (*key)); + if (key == NULL) + return ENOMEM; + key->keytype = type; + if (password == NULL) { + des_read_pw_string (buf, sizeof(buf), "Password: ", 0); + password = buf; + } + err = krb5_string_to_key (password, salt, key); + memset (buf, 0, sizeof(buf)); + return err; +} + +krb5_error_code +krb5_get_in_tkt_with_password (krb5_context context, + krb5_flags options, + krb5_address *const *addrs, + const krb5_enctype *etypes, + const krb5_preauthtype *pre_auth_types, + const char *password, + krb5_ccache ccache, + krb5_creds *creds, + krb5_kdc-rep **ret_as_reply) +{ + return krb5_get_in_tkt (context, options, addrs, etypes, + pre_auth_types, key_proc, password, + NULL, NULL, creds, cache, ret_as_reply); +} diff --git a/lib/krb5/get_port.c b/lib/krb5/get_port.c new file mode 100644 index 000000000..767201672 --- /dev/null +++ b/lib/krb5/get_port.c @@ -0,0 +1,17 @@ +#include + +int +krb5_getportbyname (const char *service, + const char *proto, + int default_port) +{ + struct servent *sp; + + if ((sp = getservbyname (service, proto)) == NULL) { + fprintf (stderr, "%s/%s unknown service, " + "using default port %d\n", service, proto, + ntohs(default_port)); + return default_port; + } else + return sp->s_port; +} diff --git a/lib/krb5/krbhst.c b/lib/krb5/krbhst.c new file mode 100644 index 000000000..c05714d56 --- /dev/null +++ b/lib/krb5/krbhst.c @@ -0,0 +1,27 @@ +#include "krb5_locl.h" + +krb5_error_code +krb5_get_krbhst (krb5_context context, + const krb5_data *realm, + char ***hostlist) +{ + krb5_error_code err; + char buf[BUFSIZ]; + char *val; + + sprintf (buf, "realms %s kdc", realm.data); + err = krb5_get_config_tag (context.cf, buf, &val); + if (err) + return err; + **hostlist = malloc (2 * sizeof (char *)); + (*hostlist)[0] = val; + (*hostlist)[1] = NULL; + return 0; +} + +krb5_error_code +krb5_free_krbhst (krb5_context context, + char *const *hostlist) +{ + free (hostlist); +} diff --git a/lib/krb5/send_to_kdc.c b/lib/krb5/send_to_kdc.c new file mode 100644 index 000000000..51e62f94d --- /dev/null +++ b/lib/krb5/send_to_kdc.c @@ -0,0 +1,85 @@ +#include "krb5_locl.h" + +static int +send_and_recv (int fd, + struct sockaddr_in *addr, + krb5_data *send, + krb5_data *recv) +{ + struct fdset fdset; + struct timeval timeout; + int ret; + long nbytes; + + if (sendto (fd, send->data, send->len, 0, + (struct sockaddr *)addr, sizeof(*addr)) < 0) + return -1; + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + timeout.tv_sec = 3; + timeout.tv_usec = 0; + ret = select (fd + 1, &fdset, NULL, NULL, &timeout); + if (ret <= 0) + return -1; + else { + ioctl (fd, FIONREAD, &nbytes); + + nbytes -= sizeof(struct udphdr) + sizeof(struct iphdr); + + recv->data = malloc (nbytes); + ret = recvfrom (fd, recv->data, nbytes, 0, NULL, 0); + if (ret < 0) { + free (recv->data); + return -1; + } + recv->data = realloc (recv->data, ret); + recv->len = ret; + return 0; + } +} + +krb5_error_code +krb5_sentdo_kdc (krb5_context context, + const krb5_data *send, + const krb5_data *realm, + krb5_data *receive) +{ + krb5_error_code err; + char **hostlist, **hp, *p; + struct hostent *hostent; + int fd; + int port; + int i; + + port = krb5_getportbyname ("kerberos", "udp", htons(750)); + fd = socket (AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return errno; + + err = krb5_get_krbhst (context, realm, &hostlist); + if (err) { + close (fd); + return err; + } + for (i = 0; i < 3; ++i) + for (hp = hostlist; p = *hp; ++hp) { + char *addr; + + hostent = gethostbyname (p); + while (addr = *hostent->h_addr_list++) { + struct sockaddr_in a; + + memset (a, 0, sizeof(a)); + a.sin_family = AF_INET; + a.sin_port = port; + a.sin_addr = *((struct in_addr *)addr); + + if (send_and_recv (fd, &a, send, recv) == 0) { + krb5_free_krbhst (context, hostlist); + return KDC_ERR_NONE; + } + } + } + krb5_free_krbhst (context, hostlist); + return KRB5_KDC_UNREACH; +} diff --git a/lib/krb5/str2key.c b/lib/krb5/str2key.c new file mode 100644 index 000000000..9e9f2ba55 --- /dev/null +++ b/lib/krb5/str2key.c @@ -0,0 +1,141 @@ +#include + +/* + * Reverse 8 bytes + */ + +static void +reverse (unsigned char *s) +{ + static unsigned char tbl[] = { + 0x0, + 0x8, + 0x4, + 0xC, + 0x2, + 0xA, + 0x6, + 0xE, + 0x1, + 0x9, + 0x5, + 0xD, + 0x3, + 0xB, + 0x7, + 0xF + }; + + char tmp; + +#define REVONE(str, i, j) \ +do { tmp = str[i]; str[i] = str[j]; str[j] = tmp;} while(0) + + REVONE(s,0,7); + REVONE(s,1,6); + REVONE(s,2,5); + REVONE(s,3,4); +#undef REVONE + +#define REVTWO(q) \ +q = (tbl[q & 0x0F] << 4) | (tbl[q >> 4]) + + REVTWO(s[0]); + REVTWO(s[1]); + REVTWO(s[2]); + REVTWO(s[3]); + REVTWO(s[4]); + REVTWO(s[5]); + REVTWO(s[6]); + REVTWO(s[7]); + +#undef REVTWO +} + +/* + * A = A xor B. A & B is 8 bytes. + */ + +static void +xor (unsigned char *a, unsigned char *b) +{ + a[0] ^= b[0]; + a[1] ^= b[1]; + a[2] ^= b[2]; + a[3] ^= b[3]; + a[4] ^= b[4]; + a[5] ^= b[5]; + a[6] ^= b[6]; + a[7] ^= b[7]; +} + +/* + * Init a from b + */ + +static void +init (unsigned char *a, unsigned char *b) +{ + a[0] = b[0] << 1; + a[1] = b[1] << 1; + a[2] = b[2] << 1; + a[3] = b[3] << 1; + a[4] = b[4] << 1; + a[5] = b[5] << 1; + a[6] = b[6] << 1; + a[7] = b[7] << 1; +} + +void +krb5_string_to_key (char *str, + krb5_data *salt, + krb5_keyblock *key) +{ + int odd, i; + size_t len; + char *s, *p; + des_cblock tempkey; + des_key_schedule sched; + krb5_error_code err; + + len = strlen(str) + salt->len; +#if 1 + len = (len + 7) / 8 * 8; +#endif + p = s = malloc (len); + if (p == NULL) + return ENOMEM; + err = krb5_data_alloc (&key->contents, sizeof(des_cblock)); + if (err) { + free (p); + return err; + } + memset (s, 0, len); + strncpy (p, str, strlen(str)); + p += strlen(str); + strncpy (p, salt->data, salt->len); + odd = 1; + memset (tempkey, 0, sizeof(tempkey)); + for (i = 0; i < len; i += 8) { + unsigned char tmp[8]; + + init (tmp, &s[i]); + + if (odd == 0) { + odd = 1; + reverse (tmp); + init (tmp, tmp); + } else + odd = 0; + xor (tempkey, tmp); + } + des_set_odd_parity (&tempkey); + des_set_key (&tempkey, sched); + des_cbc_cksum ((des_cblock *)s, &tempkey, len, sched, &tempkey); + free (s); + des_set_odd_parity (&tempkey); + if (des_is_weak_key (&tempkey)) + xor ((char *)&tempkey, "0x000x000x000x000x000x000x000xF0"); + memcpy (key->contents.data, &tempkey, sizeof(tempkey)); + return 0; +} diff --git a/send_to_kdc.c b/send_to_kdc.c new file mode 100644 index 000000000..51e62f94d --- /dev/null +++ b/send_to_kdc.c @@ -0,0 +1,85 @@ +#include "krb5_locl.h" + +static int +send_and_recv (int fd, + struct sockaddr_in *addr, + krb5_data *send, + krb5_data *recv) +{ + struct fdset fdset; + struct timeval timeout; + int ret; + long nbytes; + + if (sendto (fd, send->data, send->len, 0, + (struct sockaddr *)addr, sizeof(*addr)) < 0) + return -1; + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + timeout.tv_sec = 3; + timeout.tv_usec = 0; + ret = select (fd + 1, &fdset, NULL, NULL, &timeout); + if (ret <= 0) + return -1; + else { + ioctl (fd, FIONREAD, &nbytes); + + nbytes -= sizeof(struct udphdr) + sizeof(struct iphdr); + + recv->data = malloc (nbytes); + ret = recvfrom (fd, recv->data, nbytes, 0, NULL, 0); + if (ret < 0) { + free (recv->data); + return -1; + } + recv->data = realloc (recv->data, ret); + recv->len = ret; + return 0; + } +} + +krb5_error_code +krb5_sentdo_kdc (krb5_context context, + const krb5_data *send, + const krb5_data *realm, + krb5_data *receive) +{ + krb5_error_code err; + char **hostlist, **hp, *p; + struct hostent *hostent; + int fd; + int port; + int i; + + port = krb5_getportbyname ("kerberos", "udp", htons(750)); + fd = socket (AF_INET, SOCK_DGRAM, 0); + if (fd < 0) + return errno; + + err = krb5_get_krbhst (context, realm, &hostlist); + if (err) { + close (fd); + return err; + } + for (i = 0; i < 3; ++i) + for (hp = hostlist; p = *hp; ++hp) { + char *addr; + + hostent = gethostbyname (p); + while (addr = *hostent->h_addr_list++) { + struct sockaddr_in a; + + memset (a, 0, sizeof(a)); + a.sin_family = AF_INET; + a.sin_port = port; + a.sin_addr = *((struct in_addr *)addr); + + if (send_and_recv (fd, &a, send, recv) == 0) { + krb5_free_krbhst (context, hostlist); + return KDC_ERR_NONE; + } + } + } + krb5_free_krbhst (context, hostlist); + return KRB5_KDC_UNREACH; +} diff --git a/str2key.c b/str2key.c new file mode 100644 index 000000000..9e9f2ba55 --- /dev/null +++ b/str2key.c @@ -0,0 +1,141 @@ +#include + +/* + * Reverse 8 bytes + */ + +static void +reverse (unsigned char *s) +{ + static unsigned char tbl[] = { + 0x0, + 0x8, + 0x4, + 0xC, + 0x2, + 0xA, + 0x6, + 0xE, + 0x1, + 0x9, + 0x5, + 0xD, + 0x3, + 0xB, + 0x7, + 0xF + }; + + char tmp; + +#define REVONE(str, i, j) \ +do { tmp = str[i]; str[i] = str[j]; str[j] = tmp;} while(0) + + REVONE(s,0,7); + REVONE(s,1,6); + REVONE(s,2,5); + REVONE(s,3,4); +#undef REVONE + +#define REVTWO(q) \ +q = (tbl[q & 0x0F] << 4) | (tbl[q >> 4]) + + REVTWO(s[0]); + REVTWO(s[1]); + REVTWO(s[2]); + REVTWO(s[3]); + REVTWO(s[4]); + REVTWO(s[5]); + REVTWO(s[6]); + REVTWO(s[7]); + +#undef REVTWO +} + +/* + * A = A xor B. A & B is 8 bytes. + */ + +static void +xor (unsigned char *a, unsigned char *b) +{ + a[0] ^= b[0]; + a[1] ^= b[1]; + a[2] ^= b[2]; + a[3] ^= b[3]; + a[4] ^= b[4]; + a[5] ^= b[5]; + a[6] ^= b[6]; + a[7] ^= b[7]; +} + +/* + * Init a from b + */ + +static void +init (unsigned char *a, unsigned char *b) +{ + a[0] = b[0] << 1; + a[1] = b[1] << 1; + a[2] = b[2] << 1; + a[3] = b[3] << 1; + a[4] = b[4] << 1; + a[5] = b[5] << 1; + a[6] = b[6] << 1; + a[7] = b[7] << 1; +} + +void +krb5_string_to_key (char *str, + krb5_data *salt, + krb5_keyblock *key) +{ + int odd, i; + size_t len; + char *s, *p; + des_cblock tempkey; + des_key_schedule sched; + krb5_error_code err; + + len = strlen(str) + salt->len; +#if 1 + len = (len + 7) / 8 * 8; +#endif + p = s = malloc (len); + if (p == NULL) + return ENOMEM; + err = krb5_data_alloc (&key->contents, sizeof(des_cblock)); + if (err) { + free (p); + return err; + } + memset (s, 0, len); + strncpy (p, str, strlen(str)); + p += strlen(str); + strncpy (p, salt->data, salt->len); + odd = 1; + memset (tempkey, 0, sizeof(tempkey)); + for (i = 0; i < len; i += 8) { + unsigned char tmp[8]; + + init (tmp, &s[i]); + + if (odd == 0) { + odd = 1; + reverse (tmp); + init (tmp, tmp); + } else + odd = 0; + xor (tempkey, tmp); + } + des_set_odd_parity (&tempkey); + des_set_key (&tempkey, sched); + des_cbc_cksum ((des_cblock *)s, &tempkey, len, sched, &tempkey); + free (s); + des_set_odd_parity (&tempkey); + if (des_is_weak_key (&tempkey)) + xor ((char *)&tempkey, "0x000x000x000x000x000x000x000xF0"); + memcpy (key->contents.data, &tempkey, sizeof(tempkey)); + return 0; +}