/* This file is in the public domain. */ #define IDLEKEYS 2 /* # keys to try to keep on hand at all times */ #include #include #include #include #include #include #include #include #include #include extern const char *__progname; #include "pp.h" #include "rnd.h" #include "errf.h" #include "msgs.h" #include "hkdb.h" #include "panic.h" #include "config.h" #include "nested.h" #include "verbose.h" #include "pollloop.h" #include "pkt-util.h" #include "algs-list.h" #include "scm-rights.h" typedef struct hrs_state HRS_STATE; typedef struct kgpriv KGPRIV; typedef struct kglen KGLEN; typedef struct kgkey KGKEY; typedef struct kgclient KGCLIENT; typedef struct sppriv SPPRIV; struct hrs_state { int state; #define HRSS_NONE 1 #define HRSS_S_GETKEY 2 #define HRSS_S_SENTKEY 3 #define HRSS_C_GETKEY 4 #define HRSS_C_CONFIRM 5 int vers; LAYER *layer; KEXALG *kex; HASHALG *hash; int minklen; int klen; STR enc_k; STR k_t; void *ks_privdata; int ks_privlen; MP_INT k_t_e; MP_INT k_t_n; MP_INT k_t_d; MP_INT k; MP_INT h; } ; struct sppriv { int dgfd; int sfd; int id; } ; struct kgpriv { int bits; int idlecount; KEXALG *running; KGCLIENT *client_h; KGCLIENT *client_t; KGKEY *keys; KGKEY **keytail; int nkeys; int sp[2]; int id; MP_INT kg_e; MP_INT kg_n; MP_INT kg_d; void *kg; int kgdone; } ; struct kgkey { KGKEY *link; KGLEN *len; MP_INT kg_e; MP_INT kg_n; MP_INT kg_d; } ; struct kgclient { KGCLIENT *flink; KGCLIENT *blink; KGPRIV *p; int fd; int id; } ; static void ensure_host_key(HRS_STATE *s) { BPP *b; b = s->layer->b; if (b->K_S.len > 0) return; (*b->hk->host_key)(b,&s->ks_privdata,&s->ks_privlen); } static void rd_key(int id __attribute__((__unused__)), void *svp) { HRS_STATE *s; BPP *b; SPPRIV *p; int lens[3]; int totallen; int rv; unsigned char *repbuf; unsigned char *bp; s = svp; b = s->layer->b; p = s->kex->servpriv; rv = recv(p->sfd,&lens,sizeof(lens),MSG_WAITALL); if (rv < 0) { logmsg(LM_ERR|LM_PEER,"recv: %s",strerror(errno)); exit(1); } if (rv != sizeof(lens)) { logmsg(LM_ERR|LM_PEER,"short recv (%d, wanted %d)",rv,(int)sizeof(lens)); exit(1); } if ( (lens[0] < 0) || (lens[0] > s->minklen) || (lens[1] < 0) || (lens[1] > s->minklen) || (lens[2] < 0) || (lens[2] > s->minklen) ) { logmsg(LM_ERR|LM_PEER,"impossible reply lengths %d %d %d",lens[0],lens[1],lens[2]); exit(1); } totallen = lens[0] + lens[1] + lens[2]; repbuf = malloc(totallen); rv = recv(p->sfd,repbuf,totallen,MSG_WAITALL); if (rv < 0) { logmsg(LM_ERR|LM_PEER,"recv: %s",strerror(errno)); exit(1); } if (rv != totallen) { logmsg(LM_ERR|LM_PEER,"short recv (%d, wanted %d)",rv,totallen); exit(1); } data_to_mpint(&s->k_t_e,&repbuf[0],lens[0]); data_to_mpint(&s->k_t_n,&repbuf[lens[0]],lens[1]); data_to_mpint(&s->k_t_d,&repbuf[lens[0]+lens[1]],lens[2]); close(p->sfd); p->sfd = -1; remove_poll_id(p->id); p->id = PL_NOID; b->opkt[0] = SSH_MSG_KEXRSA_PUBKEY; if (VERB(CRYPTO)) { FILE *f; f = verb_fopen(VERBOSE_CRYPTO); fprintf(f,"kex K_T:\ne = "); mpz_out_str(f,16,&s->k_t_e); fprintf(f,"\nd = "); mpz_out_str(f,16,&s->k_t_d); fprintf(f,"\nn = "); mpz_out_str(f,16,&s->k_t_n); fprintf(f,"\n"); fclose(f); } (*hkalg_ssh_rsa.private)("encode",&s->k_t_e,&s->k_t_n,&s->k_t); bp = &b->opkt[1]; switch (s->vers) { default: panic("impossible version"); break; case 1: bp = put_string(bp,s->k_t.data,s->k_t.len); break; case 4: ensure_host_key(s); bp = put_string(bp,b->K_S.data,b->K_S.len); bp = put_string(bp,s->k_t.data,s->k_t.len); break; } b->oplen = bp - &b->opkt[0]; below_opkt(s->layer); s->state = HRSS_S_SENTKEY; } static void request_key(HRS_STATE *s) { SPPRIV *p; int sp[2]; struct msghdr mh; struct cmsghdr cmh; struct iovec iov; unsigned char ctlbuf[CMSPACE(sizeof(int))]; p = s->kex->servpriv; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&sp[0]) < 0) { logmsg(LM_ERR|LM_PEER,"socketpair: %s",strerror(errno)); exit(1); } iov.iov_base = &mh; iov.iov_len = 1; cmh.cmsg_len = CMLEN(sizeof(int)); cmh.cmsg_level = SOL_SOCKET; cmh.cmsg_type = SCM_RIGHTS; bcopy(&cmh,&ctlbuf[0],sizeof(struct cmsghdr)); bcopy(&sp[1],&ctlbuf[CMSKIP(&cmh)],sizeof(int)); mh.msg_name = 0; mh.msg_namelen = 0; mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = (void *) &ctlbuf[0]; mh.msg_controllen = CMSPACE(sizeof(int)); mh.msg_flags = 0; if (sendmsg(p->dgfd,&mh,0) < 0) { logmsg(LM_ERR|LM_PEER,"sendmsg: %s",strerror(errno)); exit(1); } close(sp[1]); p->sfd = sp[0]; p->id = add_poll_fd(sp[0],&rwtest_always,&rwtest_never,&rd_key,0,s); } static HRS_STATE *new_hrs_state(void) { HRS_STATE *s; s = malloc(sizeof(HRS_STATE)); s->state = HRSS_NONE; s->vers = 0; s->layer = 0; s->kex = 0; s->hash = 0; s->minklen = 0; s->klen = 0; s->enc_k = STRZERO; s->k_t = STRZERO; s->ks_privdata = 0; s->ks_privlen = 0; mpz_init(&s->k_t_e); mpz_init(&s->k_t_n); mpz_init(&s->k_t_d); mpz_init(&s->k); mpz_init(&s->h); return(s); } static void free_hrs_state(HRS_STATE *s) { free_str(s->enc_k); free_str(s->k_t); mpz_clear(&s->k_t_e); mpz_clear(&s->k_t_n); mpz_clear(&s->k_t_d); mpz_clear(&s->k); mpz_clear(&s->h); free(s); } static void *kex_harris_ssh_rsa_init_s(LAYER *l, KEXALG *kex, HASHALG *hash, int minklen, int vers) { HRS_STATE *s; if (! l->b->hk->can_sig) { logmsg(LM_ERR|LM_PEER,"kex %s: server host key algorithm (%s) can't do signatures!",l->b->kex->name,l->b->hk->name); exit(1); } s = new_hrs_state(); s->state = HRSS_S_GETKEY; s->vers = vers; s->layer = l; s->kex = kex; s->hash = hash; s->minklen = minklen; request_key(s); return(s); } static const char *kex_harris_ssh_rsa_disabled(void) { return(0); } static void *kex_harris_ssh_rsa_init_1024_sha1_01_s(LAYER *l) { return( kex_harris_ssh_rsa_init_s( l, &kexalg_harris_rsa1024_sha1_01, &hashalg_sha1, 1024, 1 ) ); } static void *kex_harris_ssh_rsa_init_2048_sha256_01_s(LAYER *l) { return( kex_harris_ssh_rsa_init_s( l, &kexalg_harris_rsa2048_sha256_01, &hashalg_sha256, 2048, 1 ) ); } static void *kex_harris_ssh_rsa_init_1024_sha1_04_s(LAYER *l) { return( kex_harris_ssh_rsa_init_s( l, &kexalg_harris_rsa1024_sha1_01, &hashalg_sha1, 1024, 4 ) ); } static void *kex_harris_ssh_rsa_init_2048_sha256_04_s(LAYER *l) { return( kex_harris_ssh_rsa_init_s( l, &kexalg_harris_rsa2048_sha256_01, &hashalg_sha256, 2048, 4 ) ); } static void *kex_harris_ssh_rsa_init_c(LAYER *l, HASHALG *hash, int minklen, int vers) { HRS_STATE *s; if (! l->b->hk->can_sig) { logmsg(LM_ERR|LM_PEER,"kex %s: server host key algorithm (%s) can't do signatures!",l->b->kex->name,l->b->hk->name); exit(1); } s = new_hrs_state(); s->state = HRSS_C_GETKEY; s->vers = vers; s->hash = hash; s->minklen = minklen; return(s); } static void *kex_harris_ssh_rsa_init_1024_sha1_01_c(LAYER *l) { return(kex_harris_ssh_rsa_init_c(l,&hashalg_sha1,1024,1)); } static void *kex_harris_ssh_rsa_init_2048_sha256_01_c(LAYER *l) { return(kex_harris_ssh_rsa_init_c(l,&hashalg_sha256,2048,1)); } static void *kex_harris_ssh_rsa_init_1024_sha1_04_c(LAYER *l) { return(kex_harris_ssh_rsa_init_c(l,&hashalg_sha1,1024,4)); } static void *kex_harris_ssh_rsa_init_2048_sha256_04_c(LAYER *l) { return(kex_harris_ssh_rsa_init_c(l,&hashalg_sha256,2048,4)); } static void mgf_xor(const void *seed, int seedlen, const HASHALG *hash, const void *in, int len, void *out) { void *hh; unsigned char h[hash->hashlen]; unsigned char c[4]; int counter; int x; int i; counter = 0; i = 0; x = hash->hashlen; while (len > 0) { if (x >= hash->hashlen) { c[0] = counter >> 24 ; c[1] = (counter >> 16) & 0xff; c[2] = (counter >> 8) & 0xff; c[3] = counter & 0xff; hh = (*hash->init)(); (*hash->process)(hh,seed,seedlen); (*hash->process)(hh,&c[0],4); (*hash->done)(hh,&h[0]); counter ++; x = 0; } ((unsigned char *)out)[i] = ((const unsigned char *)in)[i] ^ h[x]; i ++; x ++; len --; } } static int kex_harris_run_c_1(LAYER *l, HRS_STATE *s) { BPP *b; int nbits; int nbytes; int kbytes; unsigned char *kbuf; unsigned char *db; unsigned char *em; int pslen; void *h; int hlen; MP_INT m; MP_INT c; unsigned char *opp; unsigned char seed[s->hash->hashlen]; NESTED void fail(const void *dataptr __attribute__((__unused__)), const char *fmt, ...) { va_list ap; FILE *f; f = logmsg_fopen(LM_NOTE|LM_PEER); fprintf(f,"key-exchangae protocol error: "); va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); fclose(f); exit(1); } b = l->b; if (b->ipkt[0] != SSH_MSG_KEXRSA_PUBKEY) fail(0,"KEXRSA_PUBKEY packet isn't"); switch (s->vers) { default: panic("impossible version"); break; case 1: parse_packet(b,&pp_fail, PP_IGNORE(1), PP_STRING(&s->k_t), PP_ENDSHERE ); break; case 4: parse_packet(b,&pp_fail, PP_IGNORE(1), PP_STRING(&b->K_S), PP_STRING(&s->k_t), PP_ENDSHERE ); break; } mpz_init(&m); mpz_init(&c); (*hkalg_ssh_rsa.private)("decode",str_to_rostr(s->k_t),&s->k_t_e,&s->k_t_n,&fail); if (VERB(CRYPTO)) { FILE *f; f = verb_fopen(VERBOSE_CRYPTO); fprintf(f,"kex K_T:\ne = "); mpz_out_str(f,16,&s->k_t_e); fprintf(f,"\nn = "); mpz_out_str(f,16,&s->k_t_n); fprintf(f,"\n"); fclose(f); verb(CRYPTO,"my hash=%s (len=%d) minklen=%d\n",s->hash->name,s->hash->hashlen,s->minklen); } if (mpz_cmp(&s->k_t_e,&s->k_t_n) >= 0) fail(0,"public key e >= n"); hlen = s->hash->hashlen; nbits = mpz_sizeinbase(&s->k_t_n,2); if (nbits < s->minklen) fail(0,"public key n too small"); nbytes = (nbits + 7) >> 3; s->klen = nbits - (2*hlen*8) - 49; if (s->klen < 32) fail(0,"k too small (? minklen %d)",s->minklen); kbytes = (s->klen + 7) >> 3; kbuf = malloc(kbytes); random_data(kbuf,kbytes); if (s->klen & 7) kbuf[0] &= 0xff >> (8 - (s->klen & 7)); if (VERB(CRYPTO)) { int i; verb(CRYPTO,"klen=%d, k=",s->klen); for (i=0;ik,kbuf,kbytes); free(kbuf); kbytes = mpint_bytes(&s->k); kbuf = malloc(kbytes); pslen = nbytes - 1 - hlen - hlen - 1 - kbytes; if (pslen < 0) fail(0,"pslen negative?"); /* pslen will normally be zero, but don't check this - if the random k value begins with enough 0 bits, its mpint encoding will be shorter than kbytes. */ if (put_mpint(kbuf,&s->k) != kbuf+kbytes) panic("K length mismatch"); db = malloc(nbytes-1-hlen); em = malloc(nbytes); /* Compute lHash = Hash(L); put it in DB. Our L is the empty string. */ h = (*s->hash->init)(); (*s->hash->done)(h,db); /* Clear out PS */ if (pslen) bzero(db+hlen,pslen); /* Drop in the 0x01 */ db[hlen+pslen] = 0x01; /* Fill in M */ bcopy(kbuf,db+(nbytes-1-hlen)-kbytes,kbytes); /* Generate the seed */ random_data(&seed[0],hlen); /* Generate dbMask, XOR it with DB, and drop it into em */ mgf_xor(&seed[0],hlen,s->hash,db,nbytes-1-hlen,em+1+hlen); /* Generate seedMask, XOR it with seed, and drop it into em */ mgf_xor(em+1+hlen,nbytes-1-hlen,s->hash,&seed[0],hlen,em+1); /* Fill in the 0x00 in em */ em[0] = 0x00; if (VERB(CRYPTO)) { int i; verb(CRYPTO,"em="); for (i=0;ik_t_e,&s->k_t_n); /* Build return packet, including converting c to output format */ mpint_to_data(&c,em,nbytes); if (VERB(CRYPTO)) { int i; verb(CRYPTO,"encrypted="); for (i=0;ienc_k = blk_to_str(em,nbytes); opp = &b->opkt[0]; *opp++ = SSH_MSG_KEXRSA_SECRET; opp = put_string(opp,em,nbytes); b->oplen = opp - &b->opkt[0]; /* Send it, clean up, set state, and return. */ below_opkt(l); free(kbuf); free(db); mpz_clear(&m); mpz_clear(&c); s->state = HRSS_C_CONFIRM; return(0); } static int kex_harris_run_s(LAYER *l, HRS_STATE *s) { BPP *b; int nbits; int nbytes; int hlen; int i; MP_INT c; MP_INT m; void *hh; unsigned char *em; unsigned char *db; unsigned char *opp; HASHALG *sighash; unsigned char *sigbuf; STR sig; unsigned char lhash[s->hash->hashlen]; unsigned char seed[s->hash->hashlen]; NESTED void fail(const void *dataptr __attribute__((__unused__)), const char *fmt, ...) { va_list ap; FILE *f; send_disconnect(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,0,0,0,0); f = logmsg_fopen(LM_NOTE|LM_PEER); fprintf(f,"%s: key-exchange protocol error: ",__progname); va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); fclose(f); exit(1); } b = l->b; if (! b->hk->can_sig) { logmsg(LM_ERR|LM_PEER,"kex %s: server host key algorithm (%s) can't do signatures!",l->b->kex->name,l->b->hk->name); exit(1); } if (b->ipkt[0] != SSH_MSG_KEXRSA_SECRET) fail(0,"KEXRSA_SECRET packet isn't"); parse_packet(b,&pp_fail, PP_IGNORE(1), PP_STRING(&s->enc_k), PP_ENDSHERE ); if (VERB(CRYPTO)) { int i; verb(CRYPTO,"encrypted="); for (i=0;ienc_k.len;i++) verb(CRYPTO,"%02x",s->enc_k.data[i]); verb(CRYPTO,"\n"); } hlen = s->hash->hashlen; nbits = mpz_sizeinbase(&s->k_t_n,2); nbytes = (nbits + 7) >> 3; if (s->enc_k.len != nbytes) fail(0,"bad secret: length wrong"); if (nbytes < (2*hlen)+2) { /* This can't happen, since both these values were our choice! */ panic("impossible"); } mpz_init(&c); mpz_init(&m); db = malloc(nbytes-1-hlen); em = malloc(nbytes); /* Convert C to c */ data_to_mpint(&c,s->enc_k.data,nbytes); /* Check that c < n */ if (mpz_cmp(&c,&s->k_t_n) >= 0) fail(0,"bad secret: c > n"); if (VERB(CRYPTO)) { FILE *f; f = verb_fopen(VERBOSE_CRYPTO); fprintf(f,"c="); mpz_out_str(f,16,&c); fprintf(f,"\n"); fclose(f); } /* Raw RSA decrypt */ mpz_powm(&m,&c,&s->k_t_d,&s->k_t_n); /* Convert m to em */ mpint_to_data(&m,em,nbytes); if (VERB(CRYPTO)) { int i; verb(CRYPTO,"em="); for (i=0;ihash->init)(); (*s->hash->done)(hh,&lhash[0]); if (VERB(CRYPTO)) { int i; verb(CRYPTO,"lHash(%s)=",s->hash->name); for (i=0;ihash,em+1,hlen,&seed[0]); if (VERB(CRYPTO)) { int i; verb(CRYPTO,"seed="); for (i=0;ihash,em+1+hlen,nbytes-1-hlen,db); if (VERB(CRYPTO)) { int i; verb(CRYPTO,"db="); for (i=0;i= nbytes-1-hlen) || (db[i] != 0x01)) fail(0,"bad secret: no 0x01"); /* Error-check. */ if (bcmp(db,&lhash[0],hlen)) fail(0,"bad secret: label hash wrong"); if (em[0] != 0x00) fail(0,"bad secret: em prefix byte wrong"); /* Extract the secret. */ if (VERB(CRYPTO)) { int j; verb(CRYPTO,"encoded k="); for (j=0;jk), PP_ENDSHERE ); if (VERB(CRYPTO)) { FILE *f; f = verb_fopen(VERBOSE_CRYPTO); fprintf(f,"k="); mpz_out_str(f,16,&s->k); fprintf(f,"\n"); fclose(f); } /* Generate kex outputs */ ensure_host_key(s); b->kex_hlen = s->hash->hashlen; b->kex_h = malloc(b->kex_hlen); hh = (*s->hash->init)(); opp = put_string(&b->opkt[0],&b->c_version[0],b->c_vlen); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],&b->s_version[0],b->s_vlen); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],b->c_kexinit_payload,b->c_kexinit_paylen); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],b->s_kexinit_payload,b->s_kexinit_paylen); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],b->K_S.data,b->K_S.len); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],s->k_t.data,s->k_t.len); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); switch (s->vers) { default: panic("impossible version"); break; case 1: break; case 4: opp = put_string(&b->opkt[0],s->enc_k.data,s->enc_k.len); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); break; } b->kex_klen = mpint_bytes(&s->k); b->kex_k = malloc(b->kex_klen); if (put_mpint(b->kex_k,&s->k) != b->kex_klen+(unsigned char *)b->kex_k) panic("K length wrong"); (*s->hash->process)(hh,b->kex_k,b->kex_klen); (*s->hash->done)(hh,b->kex_h); b->kex_hash = s->hash; /* Sign H and build return packet */ sighash = (*b->hk->prehash)(); sigbuf = malloc(sighash->hashlen); hh = (*sighash->init)(); (*sighash->process)(hh,b->kex_h,b->kex_hlen); (*sighash->done)(hh,sigbuf); ensure_host_key(s); if (! (*b->hk->sign)(&sig,b->K_S.data,b->K_S.len,s->ks_privdata,s->ks_privlen,sigbuf)) { logmsg(LM_ERR|LM_PEER,"can't sign kex hash"); exit(1); } opp = &b->opkt[0]; *opp++ = SSH_MSG_KEXRSA_DONE; switch (s->vers) { default: panic("impossible version"); break; case 1: opp = put_string(opp,b->K_S.data,b->K_S.len); opp = put_string(opp,sig.data,sig.len); break; case 4: opp = put_string(opp,sig.data,sig.len); break; } b->oplen = opp - &b->opkt[0]; /* Send return packet, clean up, and done! */ below_opkt(l); free_hrs_state(s); mpz_clear(&c); mpz_clear(&m); free(em); free(db); free(sigbuf); free_str(sig); return(1); } static int kex_harris_run_c_2(LAYER *l, HRS_STATE *s) { BPP *b; STR hsig; void *hh; HASHALG *sighash; unsigned char *sigbuf; unsigned char *opp; NESTED void fail(const void *dataptr __attribute__((__unused__)), const char *fmt, ...) { va_list ap; FILE *f; f = logmsg_fopen(LM_NOTE|LM_PEER); fprintf(f,"key-exchangae protocol error: "); va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); fclose(f); exit(1); } b = l->b; if (b->ipkt[0] != SSH_MSG_KEXRSA_DONE) fail(0,"KEXRSA_DONE packet isn't"); switch (s->vers) { default: panic("impossible version"); break; case 1: parse_packet(b,&pp_fail, PP_IGNORE(1), PP_STRING(&b->K_S), PP_STRING(&hsig), PP_ENDSHERE ); break; case 4: parse_packet(b,&pp_fail, PP_IGNORE(1), PP_STRING(&hsig), PP_ENDSHERE ); break; } check_host_key(b->hk,b->K_S.data,b->K_S.len); hh = (*s->hash->init)(); opp = put_string(&b->opkt[0],&b->c_version[0],b->c_vlen); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],&b->s_version[0],b->s_vlen); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],b->c_kexinit_payload,b->c_kexinit_paylen); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],b->s_kexinit_payload,b->s_kexinit_paylen); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],b->K_S.data,b->K_S.len); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); opp = put_string(&b->opkt[0],s->k_t.data,s->k_t.len); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); switch (s->vers) { default: panic("impossible version"); break; case 1: break; case 4: opp = put_string(&b->opkt[0],s->enc_k.data,s->enc_k.len); (*s->hash->process)(hh,&b->opkt[0],opp-&b->opkt[0]); break; } b->kex_klen = mpint_bytes(&s->k); b->kex_k = malloc(b->kex_klen); if (put_mpint(b->kex_k,&s->k) != b->kex_klen+(unsigned char *)b->kex_k) panic("K length wrong"); (*s->hash->process)(hh,b->kex_k,b->kex_klen); b->kex_hlen = s->hash->hashlen; b->kex_h = malloc(b->kex_hlen); (*s->hash->done)(hh,b->kex_h); sighash = (*b->hk->prehash)(); sigbuf = malloc(sighash->hashlen); hh = (*sighash->init)(); (*sighash->process)(hh,b->kex_h,b->kex_hlen); (*sighash->done)(hh,sigbuf); if (! (*b->hk->checksig)(b->K_S.data,b->K_S.len,sigbuf,hsig.data,hsig.len)) { logmsg(LM_WARN|LM_PEER,"kex hash signature wrong"); exit(1); } b->kex_hash = s->hash; /* Clean up and done! */ free_str(hsig); free_hrs_state(s); free(sigbuf); return(1); } static int kex_harris_ssh_rsa_run(LAYER *l, void *sv) { HRS_STATE *s; s = sv; switch (s->state) { case HRSS_S_GETKEY: send_disconnect(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,0,0,0,0); logmsg(LM_NOTE|LM_PEER,"key-exchange protocol error: client sent something before we did"); exit(1); break; case HRSS_S_SENTKEY: return(kex_harris_run_s(l,s)); break; case HRSS_C_GETKEY: return(kex_harris_run_c_1(l,s)); break; case HRSS_C_CONFIRM: return(kex_harris_run_c_2(l,s)); break; } panic("bad state"); } static void kg_start(KGPRIV *p, int force) { if (!force && (p->nkeys >= p->idlecount)) { p->kg = 0; return; } if (VERB(IDLEKG)) verb(IDLEKG,"kg_start for %d\n",p->bits); mpz_init(&p->kg_e); mpz_init(&p->kg_n); mpz_init(&p->kg_d); (*hkalg_ssh_rsa.private)("gen-init",&p->kg,&p->kg_e,&p->kg_n,&p->kg_d,p->bits); p->kgdone = 0; } static void kgclient_rd(int id __attribute__((__unused__)), void *cv) { KGCLIENT *c; c = cv; if (VERB(IDLEKG)) verb(IDLEKG,"aborting client %p\n",(void *)c); if (c->flink) c->flink->blink = c->blink; else c->p->client_t = c->blink; if (c->blink) c->blink->flink = c->flink; else c->p->client_h = c->flink; close(c->fd); remove_poll_id(c->id); free(c); } static void key_to_client(int fd, MP_INT *e, MP_INT *n, MP_INT *d) { int ebytes; int nbytes; int dbytes; unsigned char *data; ebytes = (mpz_sizeinbase(e,2) + 7) >> 3; nbytes = (mpz_sizeinbase(n,2) + 7) >> 3; dbytes = (mpz_sizeinbase(d,2) + 7) >> 3; data = malloc((3*sizeof(int))+ebytes+nbytes+dbytes); bcopy(&ebytes,data,sizeof(int)); bcopy(&nbytes,data+sizeof(int),sizeof(int)); bcopy(&dbytes,data+(2*sizeof(int)),sizeof(int)); mpint_to_data(e,data+(3*sizeof(int)),ebytes); mpint_to_data(n,data+(3*sizeof(int))+ebytes,nbytes); mpint_to_data(d,data+(3*sizeof(int))+ebytes+nbytes,dbytes); write(fd,data,(3*sizeof(int))+ebytes+nbytes+dbytes); free(data); } static void kg_rd(int id __attribute__((__unused__)), void *pv) { KGPRIV *p; struct msghdr mh; struct cmsghdr cmh; struct iovec iov; unsigned char ctlbuf[CMSPACE(sizeof(int))]; char junk; int rv; int cfd; KGKEY *k; KGCLIENT *c; p = pv; iov.iov_base = &junk; iov.iov_len = 1; mh.msg_name = 0; mh.msg_namelen = 0; mh.msg_iov = &iov; mh.msg_iovlen = 1; mh.msg_control = (void *)&ctlbuf[0]; mh.msg_controllen = CMSPACE(sizeof(int)); mh.msg_flags = 0; rv = recvmsg(p->sp[0],&mh,0); if (rv < 0) { switch (errno) { case EINTR: case EWOULDBLOCK: return; break; } logmsg(LM_ERR|LM_PEER,"recvmsg: %s",strerror(errno)); exit(1); } if (mh.msg_controllen < sizeof(struct cmsghdr)) { logmsg(LM_ERR|LM_PEER,"control length (%d) too small",(int)mh.msg_controllen); return; } bcopy(&ctlbuf[0],&cmh,sizeof(struct cmsghdr)); if ((cmh.cmsg_level != SOL_SOCKET) || (cmh.cmsg_type != SCM_RIGHTS)) { logmsg(LM_ERR|LM_PEER,"level/type wrong (%d/%d)",(int)cmh.cmsg_level,(int)cmh.cmsg_type); return; } if (cmh.cmsg_len < CMLEN(sizeof(int))) { logmsg(LM_ERR|LM_PEER,"cmsg_len too short (%d)",(int)cmh.cmsg_len); return; } bcopy(&ctlbuf[CMSKIP(&cmh)],&cfd,sizeof(int)); if (VERB(IDLEKG)) verb(IDLEKG,"client request for %d\n",p->bits); if (p->nkeys > 0) { k = p->keys; if (! k) panic("missing keys"); if (VERB(IDLEKG)) verb(IDLEKG,"...returning pregenned key\n"); key_to_client(cfd,&k->kg_e,&k->kg_n,&k->kg_d); if (! (p->keys = k->link)) p->keytail = &p->keys; p->nkeys --; if (! p->kg) kg_start(p,0); close(cfd); return; } if (! p->kg) kg_start(p,1); c = malloc(sizeof(KGCLIENT)); if (VERB(IDLEKG)) verb(IDLEKG,"...waiting for keygen %p\n",(void *)c); c->flink = 0; c->blink = p->client_t; p->client_t = c; if (c->blink) c->blink->flink = c; else p->client_h = c; c->p = p; c->fd = cfd; c->id = add_poll_fd(c->fd,&rwtest_always,&rwtest_never,&kgclient_rd,0,c); } static void kex_harris_ssh_rsa_servinit(KEXALG *a) { KGPRIV *p; p = a->servpriv; if (p->running) { if (alglist_present(&algs_kex,a) && (p->idlecount < IDLEKEYS)) { p->idlecount = IDLEKEYS; } return; } p->running = a; p->idlecount = alglist_present(&algs_kex,a) ? IDLEKEYS : 0; p->client_h = 0; p->client_t = 0; p->keys = 0; p->keytail = &p->keys; p->nkeys = 0; if (socketpair(AF_LOCAL,SOCK_DGRAM,0,&p->sp[0]) < 0) { logmsg(LM_ERR|LM_PEER,"socketpair: %s",strerror(errno)); exit(1); } p->id = add_poll_fd(p->sp[0],&rwtest_always,&rwtest_never,&kg_rd,0,p); kg_start(p,0); } static int kex_harris_ssh_rsa_servidle(KEXALG *a) { KGPRIV *p; KGKEY *k; KGCLIENT *c; p = a->servpriv; if (p->running != a) return(BLOCK_NIL); if (! p->kg) return(BLOCK_NIL); (*hkalg_ssh_rsa.private)("gen-step",p->kg,&p->kgdone); if (! p->kgdone) return(BLOCK_LOOP); if (VERB(IDLEKG)) verb(IDLEKG,"kg done for %d\n",p->bits); c = p->client_h; if (c) { if (VERB(IDLEKG)) verb(IDLEKG,"...sending to client\n"); key_to_client(c->fd,&p->kg_e,&p->kg_n,&p->kg_d); close(c->fd); remove_poll_id(c->id); p->client_h = c->flink; if (c->flink) c->flink->blink = 0; else p->client_t = 0; free(c); } else { if (VERB(IDLEKG)) verb(IDLEKG,"...saving\n"); k = malloc(sizeof(KGKEY)); k->link = 0; *p->keytail = k; p->keytail = &k->link; p->nkeys ++; k->kg_e = p->kg_e; k->kg_n = p->kg_n; k->kg_d = p->kg_d; } kg_start(p,0); return(BLOCK_LOOP); } static void kex_harris_ssh_rsa_servproc(KEXALG *a) { KGPRIV *kgp; SPPRIV *spp; KGKEY *k; KGCLIENT *c; kgp = a->servpriv; while ((k = kgp->keys)) { kgp->keys = k->link; mpz_clear(&k->kg_e); mpz_clear(&k->kg_n); mpz_clear(&k->kg_d); free(k); } while ((c = kgp->client_h)) { kgp->client_h = c->flink; close(c->fd); remove_poll_id(c->id); free(c); } if (kgp->kg) { (*hkalg_ssh_rsa.private)("gen-abort",kgp->kg); mpz_clear(&kgp->kg_e); mpz_clear(&kgp->kg_n); mpz_clear(&kgp->kg_d); kgp->kg = 0; } if (kgp->sp[0] >= 0) { close(kgp->sp[0]); kgp->sp[0] = -1; } if (kgp->id != PL_NOID) { remove_poll_id(kgp->id); kgp->id = PL_NOID; } spp = malloc(sizeof(SPPRIV)); spp->dgfd = kgp->sp[1]; spp->sfd = -1; spp->id = PL_NOID; a->servpriv = spp; } static KGPRIV kg_1024 = { 1024 }; static KGPRIV kg_2048 = { 2048 }; KEXALG kexalg_harris_rsa1024_sha1_01 = { "rsa1024-sha1-draft-01@putty.projects.tartarus.org", 0, 0, 1, &kex_harris_ssh_rsa_disabled, &kex_harris_ssh_rsa_init_1024_sha1_01_c, &kex_harris_ssh_rsa_init_1024_sha1_01_s, &kex_harris_ssh_rsa_run, &kex_harris_ssh_rsa_servinit, &kex_harris_ssh_rsa_servidle, &kex_harris_ssh_rsa_servproc, &kg_1024 }; KEXALG kexalg_harris_rsa2048_sha256_01 = { "rsa2048-sha256-draft-01@putty.projects.tartarus.org", 0, 0, 1, &kex_harris_ssh_rsa_disabled, &kex_harris_ssh_rsa_init_2048_sha256_01_c, &kex_harris_ssh_rsa_init_2048_sha256_01_s, &kex_harris_ssh_rsa_run, &kex_harris_ssh_rsa_servinit, &kex_harris_ssh_rsa_servidle, &kex_harris_ssh_rsa_servproc, &kg_2048 }; KEXALG kexalg_harris_rsa1024_sha1_04 = { "rsa1024-sha1-draft-04@putty.projects.tartarus.org", 0, 0, 1, &kex_harris_ssh_rsa_disabled, &kex_harris_ssh_rsa_init_1024_sha1_04_c, &kex_harris_ssh_rsa_init_1024_sha1_04_s, &kex_harris_ssh_rsa_run, &kex_harris_ssh_rsa_servinit, &kex_harris_ssh_rsa_servidle, &kex_harris_ssh_rsa_servproc, &kg_1024 }; KEXALG kexalg_harris_rsa2048_sha256_04 = { "rsa2048-sha256-draft-04@putty.projects.tartarus.org", 0, 0, 1, &kex_harris_ssh_rsa_disabled, &kex_harris_ssh_rsa_init_2048_sha256_04_c, &kex_harris_ssh_rsa_init_2048_sha256_04_s, &kex_harris_ssh_rsa_run, &kex_harris_ssh_rsa_servinit, &kex_harris_ssh_rsa_servidle, &kex_harris_ssh_rsa_servproc, &kg_2048 };