/* This file is in the public domain. */ #include #include #include extern const char *__progname; #include "panic.h" #include "nested.h" #include "stdio-util.h" #include "algs.h" typedef struct encstate ENCSTATE; struct encstate { void *algstate; char *buf; int buflen; int ptr; int fill; } ; #define VFINDFN_ROSTR(lc,uc)\ void *lc##alg_vfind_rostr(ROSTR name) { int i; uc##ALG *a; \ for (i=0;(a=alglist_##lc[i]);i++) if (str_equalcC(name,a->name))\ return(a); return(0); } #define VFINDFN_C(lc,uc)\ void *lc##alg_vfind_c(const char *name) { int i; uc##ALG *a; \ for (i=0;(a=alglist_##lc[i]);i++) if (!strcmp(name,a->name)) \ return(a); return(0); } #define LISTFN(lc,uc)\ void *lc##alg_list(int i) { return(alglist_##lc[i]); } #define NAMEFN(lc,uc)\ const char *lc##alg_name(void *a) { return(((uc##ALG *)a)->name); } #define COMMENTFN(lc,uc)\ const char *lc##alg_comment(void *a) { return(((uc##ALG *)a)->comment); } static const char *never_disabled(void *a __attribute__((__unused__))) { return(0); } static void nil_servidle_init(void *a __attribute__((__unused__))) { } static int nil_servidle(void *a __attribute__((__unused__))) { return(AIO_BLOCK_NIL); } static void nil_servproc(void *a __attribute__((__unused__))) { } /**** ENCALG ****/ static VFINDFN_C(enc,ENC) static VFINDFN_ROSTR(enc,ENC) static LISTFN(enc,ENC) static NAMEFN(enc,ENC) static COMMENTFN(enc,ENC) #define encalg_disabled never_disabled #define encalg_servidle_init nil_servidle_init #define encalg_servidle nil_servidle #define encalg_servproc nil_servproc static void *encalg_deflist(int i) { int j; for (j=0;;j++) { if (alglist_enc[j] == 0) return(0); if (alglist_enc[j]->hide) continue; if (i < 1) return(alglist_enc[j]); i --; } } void *encalg_init(ENCALG *alg, const void *key, const void *iv, char rw) { void *algstate; ENCSTATE *s; switch (rw) { case 'r': algstate = (*alg->init_dec)(key,iv); break; case 'w': algstate = (*alg->init_enc)(key,iv); break; default: panic("bad direction"); break; } if (! algstate) return(0); s = malloc(sizeof(ENCSTATE)); if (! s) return(0); s->algstate = algstate; s->buflen = alg->blksize; if (s->buflen < 2048) s->buflen = (2048/alg->blksize) * alg->blksize; s->buf = malloc(s->buflen); s->ptr = 0; s->fill = 0; return(s); } int encalg_i(ENCALG *a, void *state, void (*w)(const void *, int), const void *buf, int nb, int want) { ENCSTATE *s; int n; const char *bp; int used; s = state; bp = buf; if (s->ptr) { if (s->ptr+nb < a->blksize) { bcopy(bp,s->buf+s->ptr,nb); s->ptr += nb; return(nb); } n = a->blksize - s->ptr; bcopy(bp,s->buf+s->ptr,n); bp += n; nb -= n; used = n; (*a->process)(s->algstate,s->buf,s->buf,a->blksize); (*w)(s->buf,a->blksize); want -= a->blksize; s->ptr = 0; } else { used = 0; } while ((want > 0) && (nb >= a->blksize)) { n = want + a->blksize - 1; if (n > s->buflen) n = s->buflen; if (n > nb) n = nb; n -= n % a->blksize; if (n < 1) panic("block vanished"); (*a->process)(s->algstate,bp,s->buf,n); (*w)(s->buf,n); bp += n; nb -= n; used += n; want -= n; } if ((want > 0) && (nb > 0)) { bcopy(bp,s->buf,nb); s->ptr = nb; used += nb; } return(used); } void encalg_o(ENCALG *a, void *state, void (*w)(const void *, int), const void *buf, int nb) { ENCSTATE *s; int n; const char *bp; s = state; bp = buf; if (s->ptr) { if (s->ptr+nb < a->blksize) { bcopy(bp,s->buf+s->ptr,nb); s->ptr += nb; return; } n = a->blksize - s->ptr; bcopy(bp,s->buf+s->ptr,n); bp += n; nb -= n; (*a->process)(s->algstate,s->buf,s->buf,a->blksize); (*w)(s->buf,a->blksize); s->ptr = 0; } while (nb >= a->blksize) { n = s->buflen; if (n > nb) n = nb; n -= n % a->blksize; if (n < 1) panic("block vanished"); (*a->process)(s->algstate,bp,s->buf,n); (*w)(s->buf,n); bp += n; nb -= n; } if (nb > 0) { bcopy(bp,s->buf,nb); s->ptr = nb; } } void encalg_done(ENCALG *a, void *state) { ENCSTATE *s; s = state; (*a->done)(s->algstate); free(s->buf); free(s); } /**** COMPALG ****/ static VFINDFN_C(comp,COMP) static VFINDFN_ROSTR(comp,COMP) static LISTFN(comp,COMP) #define compalg_deflist compalg_list static NAMEFN(comp,COMP) static COMMENTFN(comp,COMP) #define compalg_disabled never_disabled #define compalg_servidle_init nil_servidle_init #define compalg_servidle nil_servidle #define compalg_servproc nil_servproc void *compalg_init(COMPALG *alg, char rw) { switch (rw) { case 'r': return((*alg->init_decomp)()); break; case 'w': return((*alg->init_comp)()); break; } panic("bad direction"); } /**** MACALG ****/ static VFINDFN_C(mac,MAC) static VFINDFN_ROSTR(mac,MAC) static LISTFN(mac,MAC) static NAMEFN(mac,MAC) static COMMENTFN(mac,MAC) #define macalg_disabled never_disabled #define macalg_servidle_init nil_servidle_init #define macalg_servidle nil_servidle #define macalg_servproc nil_servproc static void *macalg_deflist(int i) { int j; for (j=0;;j++) { if (alglist_mac[j] == 0) return(0); if (alglist_mac[j]->hide) continue; if (i < 1) return(alglist_mac[j]); i --; } } void *macalg_init(MACALG *alg, const void *key) { return((*alg->init)(key)); } /**** KEXALG ****/ static VFINDFN_C(kex,KEX) static VFINDFN_ROSTR(kex,KEX) static LISTFN(kex,KEX) static NAMEFN(kex,KEX) static COMMENTFN(kex,KEX) static void *kexalg_deflist(int i) { int j; for (j=0;;j++) { if (alglist_kex[j] == 0) return(0); if ((*alglist_kex[j]->disabled)()) continue; if (i < 1) return(alglist_kex[j]); i --; } } static const char *kexalg_disabled(void *av) { return((*((KEXALG *)av)->disabled)()); } static void kexalg_servidle_init(void *av) { (*((KEXALG *)av)->servinit)(av); } static int kexalg_servidle(void *av) { return((*((KEXALG *)av)->servidle)(av)); } static void kexalg_servproc(void *av) { (*((KEXALG *)av)->servproc)(av); } /**** HKALG ****/ static VFINDFN_C(hk,HK) static VFINDFN_ROSTR(hk,HK) static LISTFN(hk,HK) #define hkalg_deflist hkalg_list static NAMEFN(hk,HK) #define hkalg_disabled never_disabled #define hkalg_servidle_init nil_servidle_init #define hkalg_servidle nil_servidle #define hkalg_servproc nil_servproc static const char *hkalg_comment(void *alg) { HKALG *a; static char *s = 0; FILE *f; int i; a = alg; if (s) free(s); f = fopen_alloc(&s,0); if (a == a->rep) { fprintf(f,"identity file %s, host-key file %s",a->deffile,a->hkfile); } else { fprintf(f,"alternative algorithm for %s",a->rep->name); } fprintf(f,", alts"); for (i=0;a->altnames[i];i++) fprintf(f," %s",a->altnames[i]); putc('\0',f); fclose(f); return(s); } /**** UAALG ****/ static VFINDFN_C(ua,UA) static VFINDFN_ROSTR(ua,UA) static LISTFN(ua,UA) #define uaalg_deflist uaalg_list static NAMEFN(ua,UA) static COMMENTFN(ua,UA) #define uaalg_disabled never_disabled #define uaalg_servidle_init nil_servidle_init #define uaalg_servidle nil_servidle #define uaalg_servproc nil_servproc void alglist_clear(ALGLIST *l) { ALE *e; if (l->flags & ALF_DEFAULT) return; while ((e = l->head)) { l->head = e->link; free(e); } l->tail = &l->head; } /* some code depends on alglist_map walking ALEs in order */ int alglist_map(ALGLIST *l, int (*fn)(void *)) { ALE *e; int rv; int v; if (l->flags & ALF_DEFAULT) panic("list is default"); rv = 0; for (e=l->head;e;e=e->link) { v = (*fn)(e->alg); if (v < 0) return(v); rv += v; } return(rv); } void *alglist_first(ALGLIST *l) { if (l->flags & ALF_DEFAULT) panic("list is default"); return(l->head?l->head->alg:0); } int alglist_empty(ALGLIST *l) { if (l->flags & ALF_DEFAULT) panic("list is default"); return(l->head?0:1); } void alglist_init(ALGLIST *l, const ALGTYPE *type) { l->type = type; l->flags = 0; l->head = 0; l->tail = &l->head; } int alglist_present(ALGLIST *list, void *alg) { ALE *e; if (list->flags & ALF_DEFAULT) panic("list is default"); for (e=list->head;e;e=e->link) if (e->alg == alg) return(1); return(0); } void alglist_dropalg(ALGLIST *list, void *alg) { ALE *e; ALE **ep; ep = &list->head; while ((e = *ep)) { if (e->alg == alg) { *ep = e->link; free(e); } else { ep = &e->link; } } } void alglist_append1(ALGLIST *list, void *alg) { ALE *e; e = malloc(sizeof(ALE)); e->alg = alg; e->link = 0; *list->tail = e; list->tail = &e->link; } void alglist_prepend1(ALGLIST *list, void *alg) { ALE *e; e = malloc(sizeof(ALE)); e->alg = alg; if (! list->head) list->tail = &e->link; e->link = list->head; list->head = e; } void alglist_replace(ALGLIST *list, ALGLIST *with) { alglist_clear(list); alglist_map(with,({ NESTED int foo(void *alg) { alglist_append1(list,alg); return(0); } &foo; })); } void alglist_expand_default(ALGLIST *list) { if (list->flags & ALF_DEFAULT) { ALE *e; void *alg; int i; list->tail = &list->head; for (i=0;(alg=(*list->type->deflist)(i));i++) { e = malloc(sizeof(ALE)); e->alg = alg; *list->tail = e; list->tail = &e->link; } *list->tail = 0; list->flags &= ~ALF_DEFAULT; } } void alglist_filter(ALGLIST *list, int (*test)(void *)) { ALE *e; ALE **ep; if (list->flags & ALF_DEFAULT) panic("list is default"); ep = &list->head; while ((e = *ep)) { if ((*test)(e->alg)) { ep = &e->link; } else { *ep = e->link; free(e); } } } void alglist_dump(ALGLIST *l, FILE *f) { ALE *e; for (e=l->head;e;e=e->link) fprintf(f," %s",(*l->type->name)(e->alg)); } int alglists_identical(ALGLIST *a, ALGLIST *b) { ALE *ea; ALE *eb; ea = a->head; eb = b->head; while (ea && eb) { if (ea->alg != eb->alg) return(0); ea = ea->link; eb = eb->link; } return(ea==eb); } #define TYPE(x,tag) \ const ALGTYPE at_##x = { tag, \ &x##alg_vfind_c, \ &x##alg_vfind_rostr, \ &x##alg_name, \ &x##alg_comment, \ &x##alg_disabled, \ &x##alg_list, \ &x##alg_deflist, \ &x##alg_servidle_init, \ &x##alg_servidle, \ &x##alg_servproc } TYPE(enc,"encryption"); TYPE(comp,"compression"); TYPE(mac,"MAC"); TYPE(kex,"key exchange"); TYPE(hk,"host key verification"); TYPE(ua,"user authentication"); #undef TYPE const ALGTYPE *at__list[AT__N] = { &at_enc, &at_comp, &at_mac, &at_kex, &at_hk, &at_ua };