/* This file is in the public domain. */ #include #include #include extern const char *__progname; #include "pp.h" #include "bpp.h" #include "rnd.h" #include "str.h" #include "algs.h" #include "msgs.h" #include "util.h" #include "errf.h" #include "panic.h" #include "config.h" #include "nested.h" #include "pkt-util.h" #include "pollloop.h" #include "stdio-util.h" #include "transport.h" typedef struct transport_state TRANSPORT_STATE; typedef struct kex_state KEX_STATE; struct transport_state { LAYER *l; int first_kex; int whoami; #define WHOAMI_CLIENT 1 #define WHOAMI_SERVER 2 int stage; #define TSS_KICKOFF 1 #define TSS_REKEY 2 #define TSS_SENT_KEXINIT 3 #define TSS_RUNNING_KEX 4 #define TSS_NEWKEY_WAIT 5 #define TSS_DATA 6 int kickoff_id; void *method_state; KEX_STATE *kex_state; PKTQ oq; int queuestate; #define TQS_IDLE 1 #define TQS_QUEUEING 2 } ; struct kex_state { ENCALG *enc_c2s; ENCALG *enc_s2c; MACALG *mac_c2s; MACALG *mac_s2c; COMPALG *comp_c2s; COMPALG *comp_s2c; int ignorenext; } ; static void *put_alg_list(void *opp, ALGLIST *algs) { char *buf; int n; FILE *f; NESTED int foo(void *alg) { const char *name; name = (*algs->type->name)(alg); fprintf(f,",%s",name); return(0); } f = fopen_alloc(&buf,&n); alglist_map(algs,&foo); fclose(f); if (n < 1) panic("no output"); opp = put_string(opp,buf+1,n-1); free(buf); return(opp); } static void print_peer_offer(const char *tag, const void *name, int namelen, void *alg, int matched) { static const char *prevtag = 0; if (tag != prevtag) { if (! prevtag) verb(OFFER,"%s: peer's algorithms (%%=unrecognized, *=disabled):",__progname); verb(OFFER,"\n"); prevtag = tag; if (! tag) return; verb(OFFER,"%12s:",tag); } if (tag) verb(OFFER," %s%.*s",alg?matched?"":"*":"%",namelen,(const char *)name); } static void print_my_offer(const char *tag, ALGLIST *list) { verb(OFFER,"%12s:",tag); alglist_map(list,({ NESTED int foo(void *alg) { verb(OFFER," %s",(*list->type->name)(alg)); return(0); } &foo; })); verb(OFFER,"\n"); } static void setalgs_list(ROSTR s, ALGLIST *list, ALGLIST *mylist, void *(*vfind)(ROSTR), const char *tag) { NESTED int foo(const void *name, int len) { ROSTR n; void *alg; n.len = len; n.data = name; alg = (*vfind)(n); if (VERB(OFFER)) print_peer_offer(tag,name,len,alg,alg?alglist_present(mylist,alg):0); if (alg) alglist_append1(list,alg); return(0); } comma_list(s.data,s.len,foo); } #define FOO(specific,short,str)\ static void setalgs_##specific(ROSTR s, void *arg)\ { setalgs_list(s,arg,&algs_##specific,at_##short.find_rostr,str); } FOO(kex,kex,"kex") FOO(hk,hk,"hk") FOO(enc_c2s,enc,"enc c->s") FOO(enc_s2c,enc,"enc s->c") FOO(mac_c2s,mac,"mac c->s") FOO(mac_s2c,mac,"mac s->c") FOO(comp_c2s,comp,"comp c->s") FOO(comp_s2c,comp,"comp s->c") #undef FOO static void dump_alglists(ALGLIST *clist, ALGLIST *slist) { char *s; FILE *f; NESTED int cfoo(void *alg) { fprintf(f," %s",(*clist->type->name)(alg)); return(0); } NESTED int sfoo(void *alg) { fprintf(f," %s",(*slist->type->name)(alg)); return(0); } f = fopen_alloc(&s,0); fprintf(f,"Client list:"); alglist_map(clist,&cfoo); putc('\0',f); fclose(f); logmsg(LM_NOTE|LM_PEER,"%s",s); free(s); f = fopen_alloc(&s,0); fprintf(f,"Server list:"); alglist_map(slist,&sfoo); putc('\0',f); fclose(f); logmsg(LM_NOTE|LM_PEER,"%s",s); free(s); } static void *derive_blob(BPP *b, char c, int len) { unsigned char *t0; unsigned char *t; void *h; void (*last)(void); NESTED void c_and_id(void) { (*b->kex_hash->process)(h,&c,1); (*b->kex_hash->process)(h,b->sessid,b->sessidlen); } NESTED void blob_so_far(void) { (*b->kex_hash->process)(h,t0,t-t0); } last = &c_and_id; t0 = malloc(len); t = t0; while (1) { h = (*b->kex_hash->init)(); (*b->kex_hash->process)(h,b->kex_k,b->kex_klen); (*b->kex_hash->process)(h,b->kex_h,b->kex_hlen); (*last)(); if (len < b->kex_hash->hashlen) { (*b->kex_hash->done)(h,b->hashbuf); bcopy(b->hashbuf,t,len); return(t0); } else { (*b->kex_hash->done)(h,t); if (len == b->kex_hash->hashlen) return(t0); t += b->kex_hash->hashlen; last = &blob_so_far; len -= b->kex_hash->hashlen; } } } #if 0 static void dumpblob(const char *tag, const void *data, int len) { int i; printf("%s",tag); for (i=0;icanuse)(forserver)); } alglist_filter(&algs_hk,&foo); } static void send_kexinit(TRANSPORT_STATE *s, LAYER *l) { unsigned char *opp; unsigned char lclcookie[16]; filter_hkalgs(s->whoami==WHOAMI_SERVER); if (alglist_empty(&algs_hk)) { logmsg(LM_NOTE|LM_PEER,"no host keys available"); send_disconnect(SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,"No host keys available",-1,"en",-1); exit(0); } if (VERB(OFFER)) { verb(OFFER,"%s: my algorithms:\n",__progname); print_my_offer("kex",&algs_kex); print_my_offer("hk",&algs_hk); print_my_offer("enc c->s",&algs_enc_c2s); print_my_offer("enc s->c",&algs_enc_s2c); print_my_offer("mac c->s",&algs_mac_c2s); print_my_offer("mac s->c",&algs_mac_s2c); print_my_offer("comp c->s",&algs_comp_c2s); print_my_offer("comp s->c",&algs_comp_s2c); } random_data(&lclcookie[0],16); opp = &l->b->opkt[0]; *opp++ = SSH_MSG_KEXINIT; bcopy(&lclcookie[0],opp,16); opp += 16; opp = put_alg_list(opp,&algs_kex); opp = put_alg_list(opp,&algs_hk); opp = put_alg_list(opp,&algs_enc_c2s); opp = put_alg_list(opp,&algs_enc_s2c); opp = put_alg_list(opp,&algs_mac_c2s); opp = put_alg_list(opp,&algs_mac_s2c); opp = put_alg_list(opp,&algs_comp_c2s); opp = put_alg_list(opp,&algs_comp_s2c); opp = put_string(opp,config_str("lang-c2s")?:"",-1); opp = put_string(opp,config_str("lang-s2c")?:"",-1); *opp++ = 0; opp = put_uint32(opp,0); l->b->oplen = opp - &l->b->opkt[0]; switch (s->whoami) { case WHOAMI_CLIENT: free(l->b->c_kexinit_payload); l->b->c_kexinit_paylen = l->b->oplen; l->b->c_kexinit_payload = malloc(l->b->c_kexinit_paylen); bcopy(&l->b->opkt[0],l->b->c_kexinit_payload,l->b->c_kexinit_paylen); break; case WHOAMI_SERVER: free(l->b->s_kexinit_payload); l->b->s_kexinit_paylen = l->b->oplen; l->b->s_kexinit_payload = malloc(l->b->s_kexinit_paylen); bcopy(&l->b->opkt[0],l->b->s_kexinit_payload,l->b->s_kexinit_paylen); break; default: panic("don't know who I am"); break; } below_opkt(l); } static KEX_STATE *recv_kexinit(TRANSPORT_STATE *ts, LAYER *l, int firsttime) { BPP *b; KEX_STATE *ks; unsigned char remcookie[16]; ALGLIST remalgs_kex; ALGLIST *c_kex; ALGLIST *s_kex; ALGLIST remalgs_hk; ALGLIST *c_hk; ALGLIST *s_hk; ALGLIST remalgs_enc_c2s; ALGLIST remalgs_enc_s2c; ALGLIST remalgs_mac_c2s; ALGLIST remalgs_mac_s2c; ALGLIST remalgs_comp_c2s; ALGLIST remalgs_comp_s2c; STR remlang_c2s; STR remlang_s2c; KEXALG *guessed_kex; HKALG *guessed_hk; int guessed_pkt; unsigned int extensions; b = l->b; ks = malloc(sizeof(KEX_STATE)); switch (ts->whoami) { case WHOAMI_CLIENT: if (! firsttime) free(b->s_kexinit_payload); b->s_kexinit_paylen = b->iplen; b->s_kexinit_payload = malloc(b->s_kexinit_paylen); bcopy(&b->ipkt[0],b->s_kexinit_payload,b->s_kexinit_paylen); c_kex = &algs_kex; s_kex = &remalgs_kex; c_hk = &algs_hk; s_hk = &remalgs_hk; break; case WHOAMI_SERVER: if (! firsttime) free(b->c_kexinit_payload); b->c_kexinit_paylen = b->iplen; b->c_kexinit_payload = malloc(b->c_kexinit_paylen); bcopy(&b->ipkt[0],b->c_kexinit_payload,b->c_kexinit_paylen); c_kex = &remalgs_kex; s_kex = &algs_kex; c_hk = &remalgs_hk; s_hk = &algs_hk; break; default: panic("don't know who I am"); break; } alglist_init(&remalgs_kex,&at_kex); alglist_init(&remalgs_hk,&at_hk); alglist_init(&remalgs_enc_c2s,&at_enc); alglist_init(&remalgs_enc_s2c,&at_enc); alglist_init(&remalgs_mac_c2s,&at_mac); alglist_init(&remalgs_mac_s2c,&at_mac); alglist_init(&remalgs_comp_c2s,&at_comp); alglist_init(&remalgs_comp_s2c,&at_comp); parse_packet(b,&pp_fail, PP_IGNORE(1), PP_BLOB(16,&remcookie[0]), PP_STRING_CALL(setalgs_kex,&remalgs_kex), PP_STRING_CALL(setalgs_hk,&remalgs_hk), PP_STRING_CALL(setalgs_enc_c2s,&remalgs_enc_c2s), PP_STRING_CALL(setalgs_enc_s2c,&remalgs_enc_s2c), PP_STRING_CALL(setalgs_mac_c2s,&remalgs_mac_c2s), PP_STRING_CALL(setalgs_mac_s2c,&remalgs_mac_s2c), PP_STRING_CALL(setalgs_comp_c2s,&remalgs_comp_c2s), PP_STRING_CALL(setalgs_comp_s2c,&remalgs_comp_s2c), PP_STRING(&remlang_c2s), PP_STRING(&remlang_s2c), PP_BOOL(&guessed_pkt), PP_UINT32(&extensions), PP_ENDSHERE ); if (VERB(OFFER)) print_peer_offer(0,0,0,0,0); /* In case the peer sent a guessed kex packet, save its guesses. */ guessed_kex = alglist_first(&remalgs_kex); guessed_hk = alglist_first(&remalgs_hk); /* Choose algorithms. */ /* Kex algorithm is the first client kex algorithm for which - the server also supports the algorithm, - if the algorithm requires an encryption-capable host key, there is an encryption-capable algorithm in the server's server_host_key_algorithms list that is also supported by the client, and - if the algorithm requires a signature-capable host key, there is a signature-capable algorithm in the server's server_host_key_algorithms list that is also supported by the client. */ if (! alglist_map(c_kex, ({ NESTED int foo(void *alg) { /* Skip this algorithm if: no server support, or, it needs encryption and we fail to find a suitable encryption algorithm, or ditto for signature algs. */ if ( !alglist_present(s_kex,alg) || ( ((KEXALG *)alg)->need_enc && !alglist_map(s_hk, ({ NESTED int foo(void *alg2) { if ( ((HKALG *)alg2)->can_enc && alglist_present(c_hk,alg2) ) return(-1); return(0); } &foo; })) ) || ( ((KEXALG *)alg)->need_sig && !alglist_map(s_hk, ({ NESTED int foo(void *alg2) { if ( ((HKALG *)alg2)->can_sig && alglist_present(c_hk,alg2) ) return(-1); return(0); } &foo; })) ) ) return(0); /* This algorithm is suitable. Save it and stop looking. */ b->kex = alg; return(-1); } &foo; }))) { send_disconnect(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,0,0,0,0); logmsg(LM_NOTE|LM_PEER,"no usable key-exchange algorithm found"); dump_alglists(c_kex,s_kex); exit(1); } if (VERB(KEX)) verb(KEX,"Using kex %s\n",b->kex->name); /* Host-key algorithm is the first client hk algorithm which - the server also supports, and - supports any capabilities required by the kex algorithm (encryption and/or signature). */ if (! alglist_map(c_hk, ({ NESTED int foo(void *alg) { if ( (b->kex->need_enc && !((HKALG *)alg)->can_enc) || (b->kex->need_sig && !((HKALG *)alg)->can_sig) || !alglist_present(s_hk,alg) ) return(0); b->hk = alg; return(-1); } &foo; }))) { send_disconnect(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,0,0,0,0); logmsg(LM_NOTE|LM_PEER,"no usable server host key algorithm found"); dump_alglists(c_hk,s_hk); exit(1); } if (VERB(KEX)) verb(KEX,"Using hk %s\n",b->hk->name); /* For encryption, MAC, and compression, the algorithm chosen is the first one on the client's list which the server also supports. */ #define FOO(lcllist,algvar,remlist,msgtxt) do {\ if (! alglist_map((ts->whoami==WHOAMI_CLIENT)?lcllist:remlist, \ ({ NESTED int foo(void *alg) \ { if (alglist_present((ts->whoami==WHOAMI_CLIENT)?remlist:lcllist,alg))\ { ks->algvar = alg; \ return(-1); \ } \ return(0); \ } \ &foo; \ }))) \ { send_disconnect(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,0,0,0,0); \ logmsg(LM_NOTE|LM_PEER,"no usable %s algorithm found",msgtxt); \ dump_alglists( (ts->whoami == WHOAMI_CLIENT) ? lcllist : remlist, \ (ts->whoami == WHOAMI_CLIENT) ? remlist : lcllist); \ exit(1); \ } \ if (VERB(KEX)) verb(KEX,"Using %s %s\n",msgtxt,ks->algvar->name); \ } while (0) FOO(&algs_enc_c2s,enc_c2s,&remalgs_enc_c2s,"client->server encryption"); FOO(&algs_enc_s2c,enc_s2c,&remalgs_enc_s2c,"server->client encryption"); FOO(&algs_mac_c2s,mac_c2s,&remalgs_mac_c2s,"client->server MAC"); FOO(&algs_mac_s2c,mac_s2c,&remalgs_mac_s2c,"server->client MAC"); FOO(&algs_comp_c2s,comp_c2s,&remalgs_comp_c2s,"client->server compression"); FOO(&algs_comp_s2c,comp_s2c,&remalgs_comp_s2c,"server->client compression"); #undef FOO if (VERB(KEX)) verb(KEX,"Guessed kex packet sent by peer: %s\n",guessed_pkt?"yes":"no"); ks->ignorenext = ( guessed_pkt && ( (guessed_kex != b->kex) || (guessed_hk != b->hk) ) ); if (VERB(KEX) && ks->ignorenext) verb(KEX,"...ignoring it\n"); alglist_clear(&remalgs_kex); alglist_clear(&remalgs_hk); alglist_clear(&remalgs_enc_c2s); alglist_clear(&remalgs_enc_s2c); alglist_clear(&remalgs_mac_c2s); alglist_clear(&remalgs_mac_s2c); alglist_clear(&remalgs_comp_c2s); alglist_clear(&remalgs_comp_s2c); free_str(remlang_c2s); free_str(remlang_s2c); return(ks); } static void kex_done(LAYER *l, int firsttime) { if (firsttime) { l->b->sessidlen = l->b->kex_hlen; l->b->sessid = malloc(l->b->sessidlen); bcopy(l->b->kex_h,l->b->sessid,l->b->sessidlen); } if (! firsttime) free(l->b->hashbuf); l->b->hashbuf = malloc(l->b->kex_hash->hashlen); l->b->opkt[0] = SSH_MSG_NEWKEYS; l->b->oplen = 1; below_opkt(l); } static void install_new_keys_w(BPP *b, TRANSPORT_STATE *s) { void *enc_iv; void *enc_key; void *mac_key; encalg_done(b->w_enc,b->w_encstate); (*b->w_comp->done)(b->w_compstate); (*b->w_mac->done)(b->w_macstate); switch (s->whoami) { case WHOAMI_CLIENT: enc_iv = derive_blob(b,'A',s->kex_state->enc_c2s->ivsize); enc_key = derive_blob(b,'C',s->kex_state->enc_c2s->keysize); mac_key = derive_blob(b,'E',s->kex_state->mac_c2s->keylen); b->w_enc = s->kex_state->enc_c2s; b->w_comp = s->kex_state->comp_c2s; b->w_mac = s->kex_state->mac_c2s; break; case WHOAMI_SERVER: enc_iv = derive_blob(b,'B',s->kex_state->enc_s2c->ivsize); enc_key = derive_blob(b,'D',s->kex_state->enc_s2c->keysize); mac_key = derive_blob(b,'F',s->kex_state->mac_s2c->keylen); b->w_enc = s->kex_state->enc_s2c; b->w_comp = s->kex_state->comp_s2c; b->w_mac = s->kex_state->mac_s2c; break; default: panic("don't know who I am"); break; } if (VERB(CRYPTO)) { int i; verb(CRYPTO,"send enc IV ="); for (i=0;iw_enc->ivsize;i++) verb(CRYPTO," %02x",((const unsigned char *)enc_iv)[i]); verb(CRYPTO,"\nsend enc key ="); for (i=0;iw_enc->keysize;i++) verb(CRYPTO," %02x",((const unsigned char *)enc_key)[i]); verb(CRYPTO,"\nsend MAC key ="); for (i=0;iw_mac->keylen;i++) verb(CRYPTO," %02x",((const unsigned char *)mac_key)[i]); verb(CRYPTO,"\n"); } b->w_encstate = encalg_init(b->w_enc,enc_key,enc_iv,'w'); b->w_compstate = compalg_init(b->w_comp,'w'); b->w_macstate = macalg_init(b->w_mac,mac_key); free(enc_iv); free(enc_key); free(mac_key); } static void install_new_keys_r(BPP *b, TRANSPORT_STATE *s) { void *enc_iv; void *enc_key; void *mac_key; encalg_done(b->r_enc,b->r_encstate); (*b->r_comp->done)(b->r_compstate); (*b->r_mac->done)(b->r_macstate); switch (s->whoami) { case WHOAMI_CLIENT: enc_iv = derive_blob(b,'B',s->kex_state->enc_s2c->ivsize); enc_key = derive_blob(b,'D',s->kex_state->enc_s2c->keysize); mac_key = derive_blob(b,'F',s->kex_state->mac_s2c->keylen); b->r_enc = s->kex_state->enc_s2c; b->r_comp = s->kex_state->comp_s2c; b->r_mac = s->kex_state->mac_s2c; break; case WHOAMI_SERVER: enc_iv = derive_blob(b,'A',s->kex_state->enc_c2s->ivsize); enc_key = derive_blob(b,'C',s->kex_state->enc_c2s->keysize); mac_key = derive_blob(b,'E',s->kex_state->mac_c2s->keylen); b->r_enc = s->kex_state->enc_c2s; b->r_comp = s->kex_state->comp_c2s; b->r_mac = s->kex_state->mac_c2s; break; default: panic("don't know who I am"); break; } if (VERB(CRYPTO)) { int i; verb(CRYPTO,"recv enc IV ="); for (i=0;iw_enc->ivsize;i++) verb(CRYPTO," %02x",((const unsigned char *)enc_iv)[i]); verb(CRYPTO,"\nrecv enc key ="); for (i=0;iw_enc->keysize;i++) verb(CRYPTO," %02x",((const unsigned char *)enc_key)[i]); verb(CRYPTO,"\nrecv MAC key ="); for (i=0;iw_mac->keylen;i++) verb(CRYPTO," %02x",((const unsigned char *)mac_key)[i]); verb(CRYPTO,"\n"); } b->r_encstate = encalg_init(b->r_enc,enc_key,enc_iv,'r'); b->r_compstate = compalg_init(b->r_comp,'r'); b->r_macstate = macalg_init(b->r_mac,mac_key); free(enc_iv); free(enc_key); free(mac_key); } static int kickoff(void *sv) { TRANSPORT_STATE *s; s = sv; remove_block_id(s->kickoff_id); s->kickoff_id = PL_NOID; if (s->stage == TSS_KICKOFF) { send_kexinit(s,s->l); s->stage = TSS_SENT_KEXINIT; } return(BLOCK_LOOP); } static void *transport_init(int whoami, LAYER *l) { TRANSPORT_STATE *s; s = malloc(sizeof(TRANSPORT_STATE)); s->l = l; s->whoami = whoami; s->stage = TSS_KICKOFF; s->first_kex = 1; pktq_init(&s->oq); s->queuestate = TQS_IDLE; s->kickoff_id = add_block_fn(&kickoff,s); return(s); } static void *transport_init_c(LAYER *l) { return(transport_init(WHOAMI_CLIENT,l)); } static void *transport_init_s(LAYER *l) { return(transport_init(WHOAMI_SERVER,l)); } static void init_kex(TRANSPORT_STATE *s, LAYER *l) { switch (s->whoami) { case WHOAMI_CLIENT: s->method_state = (*l->b->kex->init_c)(l); break; case WHOAMI_SERVER: s->method_state = (*l->b->kex->init_s)(l); break; default: panic("don't know who I am"); break; } } static void transport_i(LAYER *l, void *arg) { TRANSPORT_STATE *s; BPP *b; s = arg; b = l->b; switch (s->stage) { case TSS_DATA: if (! b->want_rekey) { switch (b->ipkt[0]) { case SSH_MSG_KEXINIT: s->kex_state = recv_kexinit(s,l,0); send_kexinit(s,l); s->stage = TSS_RUNNING_KEX; init_kex(s,l); break; case SSH_MSG_NEWKEYS: badmsg("DATA","MSG_NEWKEYS"); break; default: above_ipkt(l); break; } break; } /* s->stage = TSS_REKEY; overwritten just below */ /* fall through */ case TSS_KICKOFF: case TSS_REKEY: send_kexinit(s,l); s->stage = TSS_SENT_KEXINIT; /* fall through */ case TSS_SENT_KEXINIT: switch (b->ipkt[0]) { case SSH_MSG_KEXINIT: s->kex_state = recv_kexinit(s,l,s->first_kex); s->stage = TSS_RUNNING_KEX; init_kex(s,l); break; case SSH_MSG_NEWKEYS: logmsg(LM_WARN|LM_PEER,"misplaced NEWKEYS (at SENT_KEXINIT)"); exit(1); break; default: if (s->first_kex) { logmsg(LM_WARN|LM_PEER,"KEXINIT packet isn't"); exit(1); } above_ipkt(l); break; } break; case TSS_RUNNING_KEX: if (s->kex_state->ignorenext) { /* XXX: should we ignore the next packet, or the next packet with a type field in the key-exchange range? We ignore the next packet, since that's how the draft is worded. Since only key-exchange and debug/ignore/disconnect packets are allowed during key exchange anyway, receiving anything else is a protocol error (we arguably should check). */ s->kex_state->ignorenext = 0; return; } switch (b->ipkt[0]) { case SSH_MSG_KEXINIT: badmsg("RUNNING_KEX","MSG_KEXINIT"); break; case SSH_MSG_NEWKEYS: badmsg("RUNNING_KEX","MSG_NEWKEYS"); break; default: if ((b->ipkt[0] >= 30) && (b->ipkt[0] <= 49)) { if ((*b->kex->run)(l,s->method_state)) { kex_done(l,s->first_kex); if (VERB(CRYPTO)) { int i; verb(CRYPTO,"K from kex ="); for (i=0;ikex_klen;i++) verb(CRYPTO," %02x",((const unsigned char *)b->kex_k)[i]); verb(CRYPTO,"\nH from kex ="); for (i=0;ikex_hlen;i++) verb(CRYPTO," %02x",((const unsigned char *)b->kex_h)[i]); verb(CRYPTO,"\nSession ID ="); for (i=0;isessidlen;i++) verb(CRYPTO," %02x",((const unsigned char *)b->sessid)[i]); verb(CRYPTO,"\n"); } install_new_keys_w(b,s); s->stage = TSS_NEWKEY_WAIT; } } else { badmsg("RUNNING_KEX","type %d",b->ipkt[0]); } break; } break; case TSS_NEWKEY_WAIT: switch (b->ipkt[0]) { case SSH_MSG_KEXINIT: badmsg("NEWKEY_WAIT","MSG_NEWKEYS"); break; case SSH_MSG_NEWKEYS: { NESTED int rekey_pkts(int bb) { return(1<<((bb<8)?16:(bb<16)?bb*2:30)); } install_new_keys_r(b,s); free(s->kex_state); s->stage = TSS_DATA; s->first_kex = 0; b->ibytes_to_rekey = 1<<30; b->obytes_to_rekey = 1<<30; b->ipkts_to_rekey = rekey_pkts(b->r_enc->blksize); b->opkts_to_rekey = rekey_pkts(b->w_enc->blksize); b->want_rekey = 0; if (s->queuestate == TQS_QUEUEING) { while (pktq_get(&s->oq,&b->opkt[0],&b->oplen,sizeof(b->opkt))) { below_opkt(l); } s->queuestate = TQS_IDLE; } } break; default: badmsg("NEWKEY_WAIT","type %d",b->ipkt[0]); break; } break; } } static void transport_o(LAYER *l, void *arg) { TRANSPORT_STATE *s; s = arg; if (s->queuestate != TQS_IDLE) { pktq_put(&s->oq,&l->b->opkt[0],l->b->oplen); return; } switch (s->stage) { case TSS_REKEY: case TSS_KICKOFF: s->queuestate = TQS_QUEUEING; pktq_put(&s->oq,&l->b->opkt[0],l->b->oplen); send_kexinit(s,l); s->stage = TSS_SENT_KEXINIT; break; default: s->queuestate = TQS_QUEUEING; pktq_put(&s->oq,&l->b->opkt[0],l->b->oplen); return; case TSS_DATA: below_opkt(l); break; } } LAYERDESC layer_transport_c = { &transport_init_c, &transport_i, &transport_o }; LAYERDESC layer_transport_s = { &transport_init_s, &transport_i, &transport_o };