| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979 | 
							- /*
 
-  * Packet protocol layer for the SSH-2 transport protocol (RFC 4253).
 
-  */
 
- #include <assert.h>
 
- #include "putty.h"
 
- #include "ssh.h"
 
- #include "sshbpp.h"
 
- #include "sshppl.h"
 
- #include "sshcr.h"
 
- #include "storage.h"
 
- #include "ssh2transport.h"
 
- #include "mpint.h"
 
- const struct ssh_signkey_with_user_pref_id ssh2_hostkey_algs[] = {
 
-     #define ARRAYENT_HOSTKEY_ALGORITHM(type, alg) { &alg, type },
 
-     HOSTKEY_ALGORITHMS(ARRAYENT_HOSTKEY_ALGORITHM)
 
- };
 
- const static ssh2_macalg *const macs[] = {
 
-     &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
 
- };
 
- const static ssh2_macalg *const buggymacs[] = {
 
-     &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
 
- };
 
- static ssh_compressor *ssh_comp_none_init(void)
 
- {
 
-     return NULL;
 
- }
 
- static void ssh_comp_none_cleanup(ssh_compressor *handle)
 
- {
 
- }
 
- static ssh_decompressor *ssh_decomp_none_init(void)
 
- {
 
-     return NULL;
 
- }
 
- static void ssh_decomp_none_cleanup(ssh_decompressor *handle)
 
- {
 
- }
 
- static void ssh_comp_none_block(ssh_compressor *handle,
 
-                                 const unsigned char *block, int len,
 
-                                 unsigned char **outblock, int *outlen,
 
-                                 int minlen)
 
- {
 
- }
 
- static bool ssh_decomp_none_block(ssh_decompressor *handle,
 
-                                   const unsigned char *block, int len,
 
-                                   unsigned char **outblock, int *outlen)
 
- {
 
-     return false;
 
- }
 
- const static ssh_compression_alg ssh_comp_none = {
 
-     "none", NULL,
 
-     ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block,
 
-     ssh_decomp_none_init, ssh_decomp_none_cleanup, ssh_decomp_none_block,
 
-     NULL
 
- };
 
- const static ssh_compression_alg *const compressions[] = {
 
-     &ssh_zlib, &ssh_comp_none
 
- };
 
- static void ssh2_transport_free(PacketProtocolLayer *);
 
- static void ssh2_transport_process_queue(PacketProtocolLayer *);
 
- static bool ssh2_transport_get_specials(
 
-     PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx);
 
- static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl,
 
-                                        SessionSpecialCode code, int arg);
 
- static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl);
 
- static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl);
 
- static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf);
 
- static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s);
 
- static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def);
 
- static void ssh2_transport_higher_layer_packet_callback(void *context);
 
- static const struct PacketProtocolLayerVtable ssh2_transport_vtable = {
 
-     ssh2_transport_free,
 
-     ssh2_transport_process_queue,
 
-     ssh2_transport_get_specials,
 
-     ssh2_transport_special_cmd,
 
-     ssh2_transport_want_user_input,
 
-     ssh2_transport_got_user_input,
 
-     ssh2_transport_reconfigure,
 
-     NULL, /* no protocol name for this layer */
 
- };
 
- #ifndef NO_GSSAPI
 
- static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
 
-                                       bool definitely_rekeying);
 
- #endif
 
- static bool ssh2_transport_timer_update(struct ssh2_transport_state *s,
 
-                                         unsigned long rekey_time);
 
- static const char *const kexlist_descr[NKEXLIST] = {
 
-     "key exchange algorithm",
 
-     "host key algorithm",
 
-     "client-to-server cipher",
 
-     "server-to-client cipher",
 
-     "client-to-server MAC",
 
-     "server-to-client MAC",
 
-     "client-to-server compression method",
 
-     "server-to-client compression method"
 
- };
 
- PacketProtocolLayer *ssh2_transport_new(
 
-     Conf *conf, const char *host, int port, const char *fullhostname,
 
-     const char *client_greeting, const char *server_greeting,
 
-     struct ssh_connection_shared_gss_state *shgss,
 
-     struct DataTransferStats *stats, PacketProtocolLayer *higher_layer,
 
-     bool is_server)
 
- {
 
-     struct ssh2_transport_state *s = snew(struct ssh2_transport_state);
 
-     memset(s, 0, sizeof(*s));
 
-     s->ppl.vt = &ssh2_transport_vtable;
 
-     s->conf = conf_copy(conf);
 
-     s->savedhost = dupstr(host);
 
-     s->savedport = port;
 
-     s->fullhostname = dupstr(fullhostname);
 
-     s->shgss = shgss;
 
-     s->client_greeting = dupstr(client_greeting);
 
-     s->server_greeting = dupstr(server_greeting);
 
-     s->stats = stats;
 
-     s->hostkeyblob = strbuf_new();
 
-     pq_in_init(&s->pq_in_higher);
 
-     pq_out_init(&s->pq_out_higher);
 
-     s->pq_out_higher.pqb.ic = &s->ic_pq_out_higher;
 
-     s->ic_pq_out_higher.fn = ssh2_transport_higher_layer_packet_callback;
 
-     s->ic_pq_out_higher.ctx = &s->ppl;
 
-     s->higher_layer = higher_layer;
 
-     s->higher_layer->selfptr = &s->higher_layer;
 
-     ssh_ppl_setup_queues(s->higher_layer, &s->pq_in_higher, &s->pq_out_higher);
 
- #ifndef NO_GSSAPI
 
-     s->gss_cred_expiry = GSS_NO_EXPIRATION;
 
-     s->shgss->srv_name = GSS_C_NO_NAME;
 
-     s->shgss->ctx = NULL;
 
- #endif
 
-     s->thc = ssh_transient_hostkey_cache_new();
 
-     s->gss_kex_used = false;
 
-     s->outgoing_kexinit = strbuf_new();
 
-     s->incoming_kexinit = strbuf_new();
 
-     if (is_server) {
 
-         s->client_kexinit = s->incoming_kexinit;
 
-         s->server_kexinit = s->outgoing_kexinit;
 
-         s->out.mkkey_adjust = 1;
 
-     } else {
 
-         s->client_kexinit = s->outgoing_kexinit;
 
-         s->server_kexinit = s->incoming_kexinit;
 
-         s->in.mkkey_adjust = 1;
 
-     }
 
-     ssh2_transport_set_max_data_size(s);
 
-     return &s->ppl;
 
- }
 
- static void ssh2_transport_free(PacketProtocolLayer *ppl)
 
- {
 
-     struct ssh2_transport_state *s =
 
-         container_of(ppl, struct ssh2_transport_state, ppl);
 
-     /*
 
-      * As our last act before being freed, move any outgoing packets
 
-      * off our higher layer's output queue on to our own output queue.
 
-      * We might be being freed while the SSH connection is still alive
 
-      * (because we're initiating shutdown from our end), in which case
 
-      * we don't want those last few packets to get lost.
 
-      *
 
-      * (If our owner were to have already destroyed our output pq
 
-      * before wanting to free us, then it would have to reset our
 
-      * publicly visible out_pq field to NULL to inhibit this attempt.
 
-      * But that's not how I expect the shutdown sequence to go in
 
-      * practice.)
 
-      */
 
-     if (s->ppl.out_pq)
 
-         pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher);
 
-     conf_free(s->conf);
 
-     ssh_ppl_free(s->higher_layer);
 
-     pq_in_clear(&s->pq_in_higher);
 
-     pq_out_clear(&s->pq_out_higher);
 
-     sfree(s->savedhost);
 
-     sfree(s->fullhostname);
 
-     sfree(s->client_greeting);
 
-     sfree(s->server_greeting);
 
-     sfree(s->keystr);
 
-     sfree(s->hostkey_str);
 
-     strbuf_free(s->hostkeyblob);
 
-     sfree(s->fingerprint);
 
-     if (s->hkey && !s->hostkeys) {
 
-         ssh_key_free(s->hkey);
 
-         s->hkey = NULL;
 
-     }
 
-     if (s->f) mp_free(s->f);
 
-     if (s->p) mp_free(s->p);
 
-     if (s->g) mp_free(s->g);
 
-     if (s->K) mp_free(s->K);
 
-     if (s->dh_ctx)
 
-         dh_cleanup(s->dh_ctx);
 
-     if (s->rsa_kex_key)
 
-         ssh_rsakex_freekey(s->rsa_kex_key);
 
-     if (s->ecdh_key)
 
-         ssh_ecdhkex_freekey(s->ecdh_key);
 
-     if (s->exhash)
 
-         ssh_hash_free(s->exhash);
 
-     strbuf_free(s->outgoing_kexinit);
 
-     strbuf_free(s->incoming_kexinit);
 
-     ssh_transient_hostkey_cache_free(s->thc);
 
-     expire_timer_context(s);
 
-     sfree(s);
 
- }
 
- /*
 
-  * SSH-2 key derivation (RFC 4253 section 7.2).
 
-  */
 
- static void ssh2_mkkey(
 
-     struct ssh2_transport_state *s, strbuf *out,
 
-     mp_int *K, unsigned char *H, char chr, int keylen)
 
- {
 
-     int hlen = s->kex_alg->hash->hlen;
 
-     int keylen_padded;
 
-     unsigned char *key;
 
-     ssh_hash *h;
 
-     if (keylen == 0)
 
-         return;
 
-     /*
 
-      * Round the requested amount of key material up to a multiple of
 
-      * the length of the hash we're using to make it. This makes life
 
-      * simpler because then we can just write each hash output block
 
-      * straight into the output buffer without fiddling about
 
-      * truncating the last one. Since it's going into a strbuf, and
 
-      * strbufs are always smemclr()ed on free, there's no need to
 
-      * worry about leaving extra potentially-sensitive data in memory
 
-      * that the caller didn't ask for.
 
-      */
 
-     keylen_padded = ((keylen + hlen - 1) / hlen) * hlen;
 
-     out->len = 0;
 
-     key = strbuf_append(out, keylen_padded);
 
-     /* First hlen bytes. */
 
-     h = ssh_hash_new(s->kex_alg->hash);
 
-     if (!(s->ppl.remote_bugs & BUG_SSH2_DERIVEKEY))
 
-         put_mp_ssh2(h, K);
 
-     put_data(h, H, hlen);
 
-     put_byte(h, chr);
 
-     put_data(h, s->session_id, s->session_id_len);
 
-     ssh_hash_final(h, key);
 
-     /* Subsequent blocks of hlen bytes. */
 
-     if (keylen_padded > hlen) {
 
-         int offset;
 
-         h = ssh_hash_new(s->kex_alg->hash);
 
-         if (!(s->ppl.remote_bugs & BUG_SSH2_DERIVEKEY))
 
-             put_mp_ssh2(h, K);
 
-         put_data(h, H, hlen);
 
-         for (offset = hlen; offset < keylen_padded; offset += hlen) {
 
-             put_data(h, key + offset - hlen, hlen);
 
-             ssh_hash *h2 = ssh_hash_copy(h);
 
-             ssh_hash_final(h2, key + offset);
 
-         }
 
-         ssh_hash_free(h);
 
-     }
 
- }
 
- /*
 
-  * Find a slot in a KEXINIT algorithm list to use for a new algorithm.
 
-  * If the algorithm is already in the list, return a pointer to its
 
-  * entry, otherwise return an entry from the end of the list.
 
-  * This assumes that every time a particular name is passed in, it
 
-  * comes from the same string constant.  If this isn't true, this
 
-  * function may need to be rewritten to use strcmp() instead.
 
-  */
 
- static struct kexinit_algorithm *ssh2_kexinit_addalg(struct kexinit_algorithm
 
-                                                      *list, const char *name)
 
- {
 
-     int i;
 
-     for (i = 0; i < MAXKEXLIST; i++)
 
-         if (list[i].name == NULL || list[i].name == name) {
 
-             list[i].name = name;
 
-             return &list[i];
 
-         }
 
-     assert(!"No space in KEXINIT list");
 
-     return NULL;
 
- }
 
- bool ssh2_common_filter_queue(PacketProtocolLayer *ppl)
 
- {
 
-     static const char *const ssh2_disconnect_reasons[] = {
 
-         NULL,
 
-         "host not allowed to connect",
 
-         "protocol error",
 
-         "key exchange failed",
 
-         "host authentication failed",
 
-         "MAC error",
 
-         "compression error",
 
-         "service not available",
 
-         "protocol version not supported",
 
-         "host key not verifiable",
 
-         "connection lost",
 
-         "by application",
 
-         "too many connections",
 
-         "auth cancelled by user",
 
-         "no more auth methods available",
 
-         "illegal user name",
 
-     };
 
-     PktIn *pktin;
 
-     ptrlen msg;
 
-     int reason;
 
-     while ((pktin = pq_peek(ppl->in_pq)) != NULL) {
 
-         switch (pktin->type) {
 
-           case SSH2_MSG_DISCONNECT:
 
-             reason = get_uint32(pktin);
 
-             msg = get_string(pktin);
 
-             ssh_remote_error(
 
-                 ppl->ssh, "Remote side sent disconnect message\n"
 
-                 "type %d (%s):\n\"%.*s\"", reason,
 
-                 ((reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
 
-                  ssh2_disconnect_reasons[reason] : "unknown"),
 
-                 PTRLEN_PRINTF(msg));
 
-             pq_pop(ppl->in_pq);
 
-             return true;               /* indicate that we've been freed */
 
-           case SSH2_MSG_DEBUG:
 
-             /* XXX maybe we should actually take notice of the return value */
 
-             get_bool(pktin);
 
-             msg = get_string(pktin);
 
-             ppl_logevent("Remote debug message: %.*s", PTRLEN_PRINTF(msg));
 
-             pq_pop(ppl->in_pq);
 
-             break;
 
-           case SSH2_MSG_IGNORE:
 
-             /* Do nothing, because we're ignoring it! Duhh. */
 
-             pq_pop(ppl->in_pq);
 
-             break;
 
-           default:
 
-             return false;
 
-         }
 
-     }
 
-     return false;
 
- }
 
- static bool ssh2_transport_filter_queue(struct ssh2_transport_state *s)
 
- {
 
-     PktIn *pktin;
 
-     while (1) {
 
-         if (ssh2_common_filter_queue(&s->ppl))
 
-             return true;
 
-         if ((pktin = pq_peek(s->ppl.in_pq)) == NULL)
 
-             return false;
 
-         /* Pass on packets to the next layer if they're outside
 
-          * the range reserved for the transport protocol. */
 
-         if (pktin->type >= 50) {
 
-             /* ... except that we shouldn't tolerate higher-layer
 
-              * packets coming from the server before we've seen
 
-              * the first NEWKEYS. */
 
-             if (!s->higher_layer_ok) {
 
-                 ssh_proto_error(s->ppl.ssh, "Received premature higher-"
 
-                                 "layer packet, type %d (%s)", pktin->type,
 
-                                 ssh2_pkt_type(s->ppl.bpp->pls->kctx,
 
-                                               s->ppl.bpp->pls->actx,
 
-                                               pktin->type));
 
-                 return true;
 
-             }
 
-             pq_pop(s->ppl.in_pq);
 
-             pq_push(&s->pq_in_higher, pktin);
 
-         } else {
 
-             /* Anything else is a transport-layer packet that the main
 
-              * process_queue coroutine should handle. */
 
-             return false;
 
-         }
 
-     }
 
- }
 
- PktIn *ssh2_transport_pop(struct ssh2_transport_state *s)
 
- {
 
-     ssh2_transport_filter_queue(s);
 
-     return pq_pop(s->ppl.in_pq);
 
- }
 
- static void ssh2_write_kexinit_lists(
 
-     BinarySink *pktout,
 
-     struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST],
 
-     Conf *conf, int remote_bugs,
 
-     const char *hk_host, int hk_port, const ssh_keyalg *hk_prev,
 
-     ssh_transient_hostkey_cache *thc,
 
-     ssh_key *const *our_hostkeys, int our_nhostkeys,
 
-     bool first_time, bool can_gssapi_keyex, bool transient_hostkey_mode)
 
- {
 
-     int i, j, k;
 
-     bool warn;
 
-     int n_preferred_kex;
 
-     const ssh_kexes *preferred_kex[KEX_MAX + 1]; /* +1 for GSSAPI */
 
-     int n_preferred_hk;
 
-     int preferred_hk[HK_MAX];
 
-     int n_preferred_ciphers;
 
-     const ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
 
-     const ssh_compression_alg *preferred_comp;
 
-     const ssh2_macalg *const *maclist;
 
-     int nmacs;
 
-     struct kexinit_algorithm *alg;
 
-     /*
 
-      * Set up the preferred key exchange. (NULL => warn below here)
 
-      */
 
-     n_preferred_kex = 0;
 
-     if (can_gssapi_keyex)
 
-         preferred_kex[n_preferred_kex++] = &ssh_gssk5_sha1_kex;
 
-     for (i = 0; i < KEX_MAX; i++) {
 
-         switch (conf_get_int_int(conf, CONF_ssh_kexlist, i)) {
 
-           case KEX_DHGEX:
 
-             preferred_kex[n_preferred_kex++] =
 
-                 &ssh_diffiehellman_gex;
 
-             break;
 
-           case KEX_DHGROUP14:
 
-             preferred_kex[n_preferred_kex++] =
 
-                 &ssh_diffiehellman_group14;
 
-             break;
 
-           case KEX_DHGROUP1:
 
-             preferred_kex[n_preferred_kex++] =
 
-                 &ssh_diffiehellman_group1;
 
-             break;
 
-           case KEX_RSA:
 
-             preferred_kex[n_preferred_kex++] =
 
-                 &ssh_rsa_kex;
 
-             break;
 
-           case KEX_ECDH:
 
-             preferred_kex[n_preferred_kex++] =
 
-                 &ssh_ecdh_kex;
 
-             break;
 
-           case KEX_WARN:
 
-             /* Flag for later. Don't bother if it's the last in
 
-              * the list. */
 
-             if (i < KEX_MAX - 1) {
 
-                 preferred_kex[n_preferred_kex++] = NULL;
 
-             }
 
-             break;
 
-         }
 
-     }
 
-     /*
 
-      * Set up the preferred host key types. These are just the ids
 
-      * in the enum in putty.h, so 'warn below here' is indicated
 
-      * by HK_WARN.
 
-      */
 
-     n_preferred_hk = 0;
 
-     for (i = 0; i < HK_MAX; i++) {
 
-         int id = conf_get_int_int(conf, CONF_ssh_hklist, i);
 
-         /* As above, don't bother with HK_WARN if it's last in the
 
-          * list */
 
-         if (id != HK_WARN || i < HK_MAX - 1)
 
-             preferred_hk[n_preferred_hk++] = id;
 
-     }
 
-     /*
 
-      * Set up the preferred ciphers. (NULL => warn below here)
 
-      */
 
-     n_preferred_ciphers = 0;
 
-     for (i = 0; i < CIPHER_MAX; i++) {
 
-         switch (conf_get_int_int(conf, CONF_ssh_cipherlist, i)) {
 
-           case CIPHER_BLOWFISH:
 
-             preferred_ciphers[n_preferred_ciphers++] = &ssh2_blowfish;
 
-             break;
 
-           case CIPHER_DES:
 
-             if (conf_get_bool(conf, CONF_ssh2_des_cbc))
 
-                 preferred_ciphers[n_preferred_ciphers++] = &ssh2_des;
 
-             break;
 
-           case CIPHER_3DES:
 
-             preferred_ciphers[n_preferred_ciphers++] = &ssh2_3des;
 
-             break;
 
-           case CIPHER_AES:
 
-             preferred_ciphers[n_preferred_ciphers++] = &ssh2_aes;
 
-             break;
 
-           case CIPHER_ARCFOUR:
 
-             preferred_ciphers[n_preferred_ciphers++] = &ssh2_arcfour;
 
-             break;
 
-           case CIPHER_CHACHA20:
 
-             preferred_ciphers[n_preferred_ciphers++] = &ssh2_ccp;
 
-             break;
 
-           case CIPHER_WARN:
 
-             /* Flag for later. Don't bother if it's the last in
 
-              * the list. */
 
-             if (i < CIPHER_MAX - 1) {
 
-                 preferred_ciphers[n_preferred_ciphers++] = NULL;
 
-             }
 
-             break;
 
-         }
 
-     }
 
-     /*
 
-      * Set up preferred compression.
 
-      */
 
-     if (conf_get_bool(conf, CONF_compression))
 
-         preferred_comp = &ssh_zlib;
 
-     else
 
-         preferred_comp = &ssh_comp_none;
 
-     for (i = 0; i < NKEXLIST; i++)
 
-         for (j = 0; j < MAXKEXLIST; j++)
 
-             kexlists[i][j].name = NULL;
 
-     /* List key exchange algorithms. */
 
-     warn = false;
 
-     for (i = 0; i < n_preferred_kex; i++) {
 
-         const ssh_kexes *k = preferred_kex[i];
 
-         if (!k) warn = true;
 
-         else for (j = 0; j < k->nkexes; j++) {
 
-                 alg = ssh2_kexinit_addalg(kexlists[KEXLIST_KEX],
 
-                                           k->list[j]->name);
 
-                 alg->u.kex.kex = k->list[j];
 
-                 alg->u.kex.warn = warn;
 
-             }
 
-     }
 
-     /* List server host key algorithms. */
 
-     if (our_hostkeys) {
 
-         /*
 
-          * In server mode, we just list the algorithms that match the
 
-          * host keys we actually have.
 
-          */
 
-         for (i = 0; i < our_nhostkeys; i++) {
 
-             alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
 
-                                       ssh_key_alg(our_hostkeys[i])->ssh_id);
 
-             alg->u.hk.hostkey = ssh_key_alg(our_hostkeys[i]);
 
-             alg->u.hk.warn = false;
 
-         }
 
-     } else if (first_time) {
 
-         /*
 
-          * In the first key exchange, we list all the algorithms
 
-          * we're prepared to cope with, but prefer those algorithms
 
-          * for which we have a host key for this host.
 
-          *
 
-          * If the host key algorithm is below the warning
 
-          * threshold, we warn even if we did already have a key
 
-          * for it, on the basis that if the user has just
 
-          * reconfigured that host key type to be warned about,
 
-          * they surely _do_ want to be alerted that a server
 
-          * they're actually connecting to is using it.
 
-          */
 
-         warn = false;
 
-         for (i = 0; i < n_preferred_hk; i++) {
 
-             if (preferred_hk[i] == HK_WARN)
 
-                 warn = true;
 
-             for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
 
-                 if (ssh2_hostkey_algs[j].id != preferred_hk[i])
 
-                     continue;
 
-                 if (have_ssh_host_key(hk_host, hk_port,
 
-                                       ssh2_hostkey_algs[j].alg->cache_id)) {
 
-                     alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
 
-                                               ssh2_hostkey_algs[j].alg->ssh_id);
 
-                     alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg;
 
-                     alg->u.hk.warn = warn;
 
-                 }
 
-             }
 
-         }
 
-         warn = false;
 
-         for (i = 0; i < n_preferred_hk; i++) {
 
-             if (preferred_hk[i] == HK_WARN)
 
-                 warn = true;
 
-             for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
 
-                 if (ssh2_hostkey_algs[j].id != preferred_hk[i])
 
-                     continue;
 
-                 alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
 
-                                           ssh2_hostkey_algs[j].alg->ssh_id);
 
-                 alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg;
 
-                 alg->u.hk.warn = warn;
 
-             }
 
-         }
 
- #ifndef NO_GSSAPI
 
-     } else if (transient_hostkey_mode) {
 
-         /*
 
-          * If we've previously done a GSSAPI KEX, then we list
 
-          * precisely the algorithms for which a previous GSS key
 
-          * exchange has delivered us a host key, because we expect
 
-          * one of exactly those keys to be used in any subsequent
 
-          * non-GSS-based rekey.
 
-          *
 
-          * An exception is if this is the key exchange we
 
-          * triggered for the purposes of populating that cache -
 
-          * in which case the cache will currently be empty, which
 
-          * isn't helpful!
 
-          */
 
-         warn = false;
 
-         for (i = 0; i < n_preferred_hk; i++) {
 
-             if (preferred_hk[i] == HK_WARN)
 
-                 warn = true;
 
-             for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
 
-                 if (ssh2_hostkey_algs[j].id != preferred_hk[i])
 
-                     continue;
 
-                 if (ssh_transient_hostkey_cache_has(
 
-                         thc, ssh2_hostkey_algs[j].alg)) {
 
-                     alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
 
-                                               ssh2_hostkey_algs[j].alg->ssh_id);
 
-                     alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg;
 
-                     alg->u.hk.warn = warn;
 
-                 }
 
-             }
 
-         }
 
- #endif
 
-     } else {
 
-         /*
 
-          * In subsequent key exchanges, we list only the host key
 
-          * algorithm that was selected in the first key exchange,
 
-          * so that we keep getting the same host key and hence
 
-          * don't have to interrupt the user's session to ask for
 
-          * reverification.
 
-          */
 
-         assert(hk_prev);
 
-         alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], hk_prev->ssh_id);
 
-         alg->u.hk.hostkey = hk_prev;
 
-         alg->u.hk.warn = false;
 
-     }
 
-     if (can_gssapi_keyex) {
 
-         alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], "null");
 
-         alg->u.hk.hostkey = NULL;
 
-     }
 
-     /* List encryption algorithms (client->server then server->client). */
 
-     for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) {
 
-         warn = false;
 
- #ifdef FUZZING
 
-         alg = ssh2_kexinit_addalg(kexlists[k], "none");
 
-         alg->u.cipher.cipher = NULL;
 
-         alg->u.cipher.warn = warn;
 
- #endif /* FUZZING */
 
-         for (i = 0; i < n_preferred_ciphers; i++) {
 
-             const ssh2_ciphers *c = preferred_ciphers[i];
 
-             if (!c) warn = true;
 
-             else for (j = 0; j < c->nciphers; j++) {
 
-                     alg = ssh2_kexinit_addalg(kexlists[k],
 
-                                               c->list[j]->ssh2_id);
 
-                     alg->u.cipher.cipher = c->list[j];
 
-                     alg->u.cipher.warn = warn;
 
-                 }
 
-         }
 
-     }
 
-     /*
 
-      * Be prepared to work around the buggy MAC problem.
 
-      */
 
-     if (remote_bugs & BUG_SSH2_HMAC) {
 
-         maclist = buggymacs;
 
-         nmacs = lenof(buggymacs);
 
-     } else {
 
-         maclist = macs;
 
-         nmacs = lenof(macs);
 
-     }
 
-     /* List MAC algorithms (client->server then server->client). */
 
-     for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) {
 
- #ifdef FUZZING
 
-         alg = ssh2_kexinit_addalg(kexlists[j], "none");
 
-         alg->u.mac.mac = NULL;
 
-         alg->u.mac.etm = false;
 
- #endif /* FUZZING */
 
-         for (i = 0; i < nmacs; i++) {
 
-             alg = ssh2_kexinit_addalg(kexlists[j], maclist[i]->name);
 
-             alg->u.mac.mac = maclist[i];
 
-             alg->u.mac.etm = false;
 
-         }
 
-         for (i = 0; i < nmacs; i++) {
 
-             /* For each MAC, there may also be an ETM version,
 
-              * which we list second. */
 
-             if (maclist[i]->etm_name) {
 
-                 alg = ssh2_kexinit_addalg(kexlists[j], maclist[i]->etm_name);
 
-                 alg->u.mac.mac = maclist[i];
 
-                 alg->u.mac.etm = true;
 
-             }
 
-         }
 
-     }
 
-     /* List client->server compression algorithms,
 
-      * then server->client compression algorithms. (We use the
 
-      * same set twice.) */
 
-     for (j = KEXLIST_CSCOMP; j <= KEXLIST_SCCOMP; j++) {
 
-         assert(lenof(compressions) > 1);
 
-         /* Prefer non-delayed versions */
 
-         alg = ssh2_kexinit_addalg(kexlists[j], preferred_comp->name);
 
-         alg->u.comp.comp = preferred_comp;
 
-         alg->u.comp.delayed = false;
 
-         if (preferred_comp->delayed_name) {
 
-             alg = ssh2_kexinit_addalg(kexlists[j],
 
-                                       preferred_comp->delayed_name);
 
-             alg->u.comp.comp = preferred_comp;
 
-             alg->u.comp.delayed = true;
 
-         }
 
-         for (i = 0; i < lenof(compressions); i++) {
 
-             const ssh_compression_alg *c = compressions[i];
 
-             alg = ssh2_kexinit_addalg(kexlists[j], c->name);
 
-             alg->u.comp.comp = c;
 
-             alg->u.comp.delayed = false;
 
-             if (c->delayed_name) {
 
-                 alg = ssh2_kexinit_addalg(kexlists[j], c->delayed_name);
 
-                 alg->u.comp.comp = c;
 
-                 alg->u.comp.delayed = true;
 
-             }
 
-         }
 
-     }
 
-     /*
 
-      * Finally, format the lists into text and write them into the
 
-      * outgoing KEXINIT packet.
 
-      */
 
-     for (i = 0; i < NKEXLIST; i++) {
 
-         strbuf *list = strbuf_new();
 
-         for (j = 0; j < MAXKEXLIST; j++) {
 
-             if (kexlists[i][j].name == NULL) break;
 
-             add_to_commasep(list, kexlists[i][j].name);
 
-         }
 
-         put_stringsb(pktout, list);
 
-     }
 
-     /* List client->server languages. Empty list. */
 
-     put_stringz(pktout, "");
 
-     /* List server->client languages. Empty list. */
 
-     put_stringz(pktout, "");
 
- }
 
- static bool ssh2_scan_kexinits(
 
-     ptrlen client_kexinit, ptrlen server_kexinit,
 
-     struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST],
 
-     const ssh_kex **kex_alg, const ssh_keyalg **hostkey_alg,
 
-     transport_direction *cs, transport_direction *sc,
 
-     bool *warn_kex, bool *warn_hk, bool *warn_cscipher, bool *warn_sccipher,
 
-     Ssh *ssh, bool *ignore_guess_cs_packet, bool *ignore_guess_sc_packet,
 
-     int *n_server_hostkeys, int server_hostkeys[MAXKEXLIST])
 
- {
 
-     BinarySource client[1], server[1];
 
-     int i;
 
-     bool guess_correct;
 
-     ptrlen clists[NKEXLIST], slists[NKEXLIST];
 
-     const struct kexinit_algorithm *selected[NKEXLIST];
 
-     BinarySource_BARE_INIT(client, client_kexinit.ptr, client_kexinit.len);
 
-     BinarySource_BARE_INIT(server, server_kexinit.ptr, server_kexinit.len);
 
-     /* Skip packet type bytes and random cookies. */
 
-     get_data(client, 1 + 16);
 
-     get_data(server, 1 + 16);
 
-     guess_correct = true;
 
-     /* Find the matching string in each list, and map it to its
 
-      * kexinit_algorithm structure. */
 
-     for (i = 0; i < NKEXLIST; i++) {
 
-         ptrlen clist, slist, cword, sword, found;
 
-         bool cfirst, sfirst;
 
-         int j;
 
-         clists[i] = get_string(client);
 
-         slists[i] = get_string(server);
 
-         if (get_err(client) || get_err(server)) {
 
-             /* Report a better error than the spurious "Couldn't
 
-              * agree" that we'd generate if we pressed on regardless
 
-              * and treated the empty get_string() result as genuine */
 
-             ssh_proto_error(ssh, "KEXINIT packet was incomplete");
 
-             return false;
 
-         }
 
-         for (cfirst = true, clist = clists[i];
 
-              get_commasep_word(&clist, &cword); cfirst = false)
 
-             for (sfirst = true, slist = slists[i];
 
-                  get_commasep_word(&slist, &sword); sfirst = false)
 
-                 if (ptrlen_eq_ptrlen(cword, sword)) {
 
-                     found = cword;
 
-                     goto found_match;
 
-                 }
 
-         /* No matching string found in the two lists. Delay reporting
 
-          * a fatal error until below, because sometimes it turns out
 
-          * not to be fatal. */
 
-         selected[i] = NULL;
 
-         /*
 
-          * However, even if a failure to agree on any algorithm at all
 
-          * is not completely fatal (e.g. because it's the MAC
 
-          * negotiation for a cipher that comes with a built-in MAC),
 
-          * it still invalidates the guessed key exchange packet. (RFC
 
-          * 4253 section 7, not contradicted by OpenSSH's
 
-          * PROTOCOL.chacha20poly1305 or as far as I can see by their
 
-          * code.)
 
-          */
 
-         guess_correct = false;
 
-         continue;
 
-       found_match:
 
-         selected[i] = NULL;
 
-         for (j = 0; j < MAXKEXLIST; j++) {
 
-             if (ptrlen_eq_string(found, kexlists[i][j].name)) {
 
-                 selected[i] = &kexlists[i][j];
 
-                 break;
 
-             }
 
-         }
 
-         assert(selected[i]); /* kexlists[] must cover one of the inputs */
 
-         /*
 
-          * If the kex or host key algorithm is not the first one in
 
-          * both sides' lists, that means the guessed key exchange
 
-          * packet (if any) is officially wrong.
 
-          */
 
-         if ((i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) && !(cfirst || sfirst))
 
-             guess_correct = false;
 
-     }
 
-     /*
 
-      * Skip language strings in both KEXINITs, and read the flags
 
-      * saying whether a guessed KEX packet follows.
 
-      */
 
-     get_string(client);
 
-     get_string(client);
 
-     get_string(server);
 
-     get_string(server);
 
-     if (ignore_guess_cs_packet)
 
-         *ignore_guess_cs_packet = get_bool(client) && !guess_correct;
 
-     if (ignore_guess_sc_packet)
 
-         *ignore_guess_sc_packet = get_bool(server) && !guess_correct;
 
-     /*
 
-      * Now transcribe the selected algorithm set into the output data.
 
-      */
 
-     for (i = 0; i < NKEXLIST; i++) {
 
-         const struct kexinit_algorithm *alg;
 
-         /*
 
-          * If we've already selected a cipher which requires a
 
-          * particular MAC, then just select that. This is the case in
 
-          * which it's not a fatal error if the actual MAC string lists
 
-          * didn't include any matching error.
 
-          */
 
-         if (i == KEXLIST_CSMAC && cs->cipher &&
 
-             cs->cipher->required_mac) {
 
-             cs->mac = cs->cipher->required_mac;
 
-             cs->etm_mode = !!(cs->mac->etm_name);
 
-             continue;
 
-         }
 
-         if (i == KEXLIST_SCMAC && sc->cipher &&
 
-             sc->cipher->required_mac) {
 
-             sc->mac = sc->cipher->required_mac;
 
-             sc->etm_mode = !!(sc->mac->etm_name);
 
-             continue;
 
-         }
 
-         alg = selected[i];
 
-         if (!alg) {
 
-             /*
 
-              * Otherwise, any match failure _is_ a fatal error.
 
-              */
 
-             ssh_sw_abort(ssh, "Couldn't agree a %s (available: %.*s)",
 
-                          kexlist_descr[i], PTRLEN_PRINTF(slists[i]));
 
-             return false;
 
-         }
 
-         switch (i) {
 
-           case KEXLIST_KEX:
 
-             *kex_alg = alg->u.kex.kex;
 
-             *warn_kex = alg->u.kex.warn;
 
-             break;
 
-           case KEXLIST_HOSTKEY:
 
-             /*
 
-              * Ignore an unexpected/inappropriate offer of "null",
 
-              * we offer "null" when we're willing to use GSS KEX,
 
-              * but it is only acceptable when GSSKEX is actually
 
-              * selected.
 
-              */
 
-             if (alg->u.hk.hostkey == NULL &&
 
-                 (*kex_alg)->main_type != KEXTYPE_GSS)
 
-                 continue;
 
-             *hostkey_alg = alg->u.hk.hostkey;
 
-             *warn_hk = alg->u.hk.warn;
 
-             break;
 
-           case KEXLIST_CSCIPHER:
 
-             cs->cipher = alg->u.cipher.cipher;
 
-             *warn_cscipher = alg->u.cipher.warn;
 
-             break;
 
-           case KEXLIST_SCCIPHER:
 
-             sc->cipher = alg->u.cipher.cipher;
 
-             *warn_sccipher = alg->u.cipher.warn;
 
-             break;
 
-           case KEXLIST_CSMAC:
 
-             cs->mac = alg->u.mac.mac;
 
-             cs->etm_mode = alg->u.mac.etm;
 
-             break;
 
-           case KEXLIST_SCMAC:
 
-             sc->mac = alg->u.mac.mac;
 
-             sc->etm_mode = alg->u.mac.etm;
 
-             break;
 
-           case KEXLIST_CSCOMP:
 
-             cs->comp = alg->u.comp.comp;
 
-             cs->comp_delayed = alg->u.comp.delayed;
 
-             break;
 
-           case KEXLIST_SCCOMP:
 
-             sc->comp = alg->u.comp.comp;
 
-             sc->comp_delayed = alg->u.comp.delayed;
 
-             break;
 
-           default:
 
-             unreachable("Bad list index in scan_kexinits");
 
-         }
 
-     }
 
-     if (server_hostkeys) {
 
-         /*
 
-          * Finally, make an auxiliary pass over the server's host key
 
-          * list to find all the host key algorithms offered by the
 
-          * server which we know about at all, whether we selected each
 
-          * one or not. We return these as a list of indices into the
 
-          * constant ssh2_hostkey_algs[] array.
 
-          */
 
-         *n_server_hostkeys = 0;
 
-         ptrlen list = slists[KEXLIST_HOSTKEY];
 
-         for (ptrlen word; get_commasep_word(&list, &word) ;) {
 
-             for (i = 0; i < lenof(ssh2_hostkey_algs); i++)
 
-                 if (ptrlen_eq_string(word, ssh2_hostkey_algs[i].alg->ssh_id)) {
 
-                     server_hostkeys[(*n_server_hostkeys)++] = i;
 
-                     break;
 
-                 }
 
-         }
 
-     }
 
-     return true;
 
- }
 
- void ssh2transport_finalise_exhash(struct ssh2_transport_state *s)
 
- {
 
-     put_mp_ssh2(s->exhash, s->K);
 
-     assert(ssh_hash_alg(s->exhash)->hlen <= sizeof(s->exchange_hash));
 
-     ssh_hash_final(s->exhash, s->exchange_hash);
 
-     s->exhash = NULL;
 
- #if 0
 
-     debug("Exchange hash is:\n");
 
-     dmemdump(s->exchange_hash, s->kex_alg->hash->hlen);
 
- #endif
 
- }
 
- static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
 
- {
 
-     struct ssh2_transport_state *s =
 
-         container_of(ppl, struct ssh2_transport_state, ppl);
 
-     PktIn *pktin;
 
-     PktOut *pktout;
 
-     /* Filter centrally handled messages off the front of the queue on
 
-      * every entry to this coroutine, no matter where we're resuming
 
-      * from, even if we're _not_ looping on pq_pop. That way we can
 
-      * still proactively handle those messages even if we're waiting
 
-      * for a user response. */
 
-     ssh2_transport_filter_queue(s);
 
-     crBegin(s->crState);
 
-     s->in.cipher = s->out.cipher = NULL;
 
-     s->in.mac = s->out.mac = NULL;
 
-     s->in.comp = s->out.comp = NULL;
 
-     s->got_session_id = false;
 
-     s->need_gss_transient_hostkey = false;
 
-     s->warned_about_no_gss_transient_hostkey = false;
 
-   begin_key_exchange:
 
- #ifndef NO_GSSAPI
 
-     if (s->need_gss_transient_hostkey) {
 
-         /*
 
-          * This flag indicates a special case in which we must not do
 
-          * GSS key exchange even if we could. (See comments below,
 
-          * where the flag was set on the previous key exchange.)
 
-          */
 
-         s->can_gssapi_keyex = false;
 
-     } else if (conf_get_bool(s->conf, CONF_try_gssapi_kex)) {
 
-         /*
 
-          * We always check if we have GSS creds before we come up with
 
-          * the kex algorithm list, otherwise future rekeys will fail
 
-          * when creds expire. To make this so, this code section must
 
-          * follow the begin_key_exchange label above, otherwise this
 
-          * section would execute just once per-connection.
 
-          *
 
-          * Update GSS state unless the reason we're here is that a
 
-          * timer just checked the GSS state and decided that we should
 
-          * rekey to update delegated credentials. In that case, the
 
-          * state is "fresh".
 
-          */
 
-         if (s->rekey_class != RK_GSS_UPDATE)
 
-             ssh2_transport_gss_update(s, true);
 
-         /* Do GSSAPI KEX when capable */
 
-         s->can_gssapi_keyex = s->gss_status & GSS_KEX_CAPABLE;
 
-         /*
 
-          * But not when failure is likely. [ GSS implementations may
 
-          * attempt (and fail) to use a ticket that is almost expired
 
-          * when retrieved from the ccache that actually expires by the
 
-          * time the server receives it. ]
 
-          *
 
-          * Note: The first time always try KEXGSS if we can, failures
 
-          * will be very rare, and disabling the initial GSS KEX is
 
-          * worse. Some day GSS libraries will ignore cached tickets
 
-          * whose lifetime is critically short, and will instead use
 
-          * fresh ones.
 
-          */
 
-         if (!s->got_session_id && (s->gss_status & GSS_CTXT_MAYFAIL) != 0)
 
-             s->can_gssapi_keyex = false;
 
-         s->gss_delegate = conf_get_bool(s->conf, CONF_gssapifwd);
 
-     } else {
 
-         s->can_gssapi_keyex = false;
 
-     }
 
- #endif
 
-     s->ppl.bpp->pls->kctx = SSH2_PKTCTX_NOKEX;
 
-     /*
 
-      * Construct our KEXINIT packet, in a strbuf so we can refer to it
 
-      * later.
 
-      */
 
-     s->client_kexinit->len = 0;
 
-     put_byte(s->outgoing_kexinit, SSH2_MSG_KEXINIT);
 
-     random_read(strbuf_append(s->outgoing_kexinit, 16), 16);
 
-     ssh2_write_kexinit_lists(
 
-         BinarySink_UPCAST(s->outgoing_kexinit), s->kexlists,
 
-         s->conf, s->ppl.remote_bugs,
 
-         s->savedhost, s->savedport, s->hostkey_alg, s->thc,
 
-         s->hostkeys, s->nhostkeys,
 
-         !s->got_session_id, s->can_gssapi_keyex,
 
-         s->gss_kex_used && !s->need_gss_transient_hostkey);
 
-     /* First KEX packet does _not_ follow, because we're not that brave. */
 
-     put_bool(s->outgoing_kexinit, false);
 
-     put_uint32(s->outgoing_kexinit, 0);             /* reserved */
 
-     /*
 
-      * Send our KEXINIT.
 
-      */
 
-     pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXINIT);
 
-     put_data(pktout, s->outgoing_kexinit->u + 1,
 
-              s->outgoing_kexinit->len - 1); /* omit initial packet type byte */
 
-     pq_push(s->ppl.out_pq, pktout);
 
-     /*
 
-      * Flag that KEX is in progress.
 
-      */
 
-     s->kex_in_progress = true;
 
-     /*
 
-      * Wait for the other side's KEXINIT, and save it.
 
-      */
 
-     crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
 
-     if (pktin->type != SSH2_MSG_KEXINIT) {
 
-         ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
 
-                         "expecting KEXINIT, type %d (%s)", pktin->type,
 
-                         ssh2_pkt_type(s->ppl.bpp->pls->kctx,
 
-                                       s->ppl.bpp->pls->actx, pktin->type));
 
-         return;
 
-     }
 
-     s->incoming_kexinit->len = 0;
 
-     put_byte(s->incoming_kexinit, SSH2_MSG_KEXINIT);
 
-     put_data(s->incoming_kexinit, get_ptr(pktin), get_avail(pktin));
 
-     /*
 
-      * Work through the two KEXINIT packets in parallel to find the
 
-      * selected algorithm identifiers.
 
-      */
 
-     {
 
-         int nhk, hks[MAXKEXLIST], i, j;
 
-         if (!ssh2_scan_kexinits(
 
-                 ptrlen_from_strbuf(s->client_kexinit),
 
-                 ptrlen_from_strbuf(s->server_kexinit),
 
-                 s->kexlists, &s->kex_alg, &s->hostkey_alg, &s->out, &s->in,
 
-                 &s->warn_kex, &s->warn_hk, &s->warn_cscipher,
 
-                 &s->warn_sccipher, s->ppl.ssh, NULL, &s->ignorepkt, &nhk, hks))
 
-             return; /* false means a fatal error function was called */
 
-         /*
 
-          * In addition to deciding which host key we're actually going
 
-          * to use, we should make a list of the host keys offered by
 
-          * the server which we _don't_ have cached. These will be
 
-          * offered as cross-certification options by ssh_get_specials.
 
-          *
 
-          * We also count the key we're currently using for KEX as one
 
-          * we've already got, because by the time this menu becomes
 
-          * visible, it will be.
 
-          */
 
-         s->n_uncert_hostkeys = 0;
 
-         for (i = 0; i < nhk; i++) {
 
-             j = hks[i];
 
-             if (ssh2_hostkey_algs[j].alg != s->hostkey_alg &&
 
-                 !have_ssh_host_key(s->savedhost, s->savedport,
 
-                                    ssh2_hostkey_algs[j].alg->cache_id)) {
 
-                 s->uncert_hostkeys[s->n_uncert_hostkeys++] = j;
 
-             }
 
-         }
 
-     }
 
-     if (s->warn_kex) {
 
-         s->dlgret = seat_confirm_weak_crypto_primitive(
 
-             s->ppl.seat, "key-exchange algorithm", s->kex_alg->name,
 
-             ssh2_transport_dialog_callback, s);
 
-         crMaybeWaitUntilV(s->dlgret >= 0);
 
-         if (s->dlgret == 0) {
 
-             ssh_user_close(s->ppl.ssh, "User aborted at kex warning");
 
-             return;
 
-         }
 
-     }
 
-     if (s->warn_hk) {
 
-         int j, k;
 
-         char *betteralgs;
 
-         /*
 
-          * Change warning box wording depending on why we chose a
 
-          * warning-level host key algorithm. If it's because
 
-          * that's all we have *cached*, list the host keys we
 
-          * could usefully cross-certify. Otherwise, use the same
 
-          * standard wording as any other weak crypto primitive.
 
-          */
 
-         betteralgs = NULL;
 
-         for (j = 0; j < s->n_uncert_hostkeys; j++) {
 
-             const struct ssh_signkey_with_user_pref_id *hktype =
 
-                 &ssh2_hostkey_algs[s->uncert_hostkeys[j]];
 
-             bool better = false;
 
-             for (k = 0; k < HK_MAX; k++) {
 
-                 int id = conf_get_int_int(s->conf, CONF_ssh_hklist, k);
 
-                 if (id == HK_WARN) {
 
-                     break;
 
-                 } else if (id == hktype->id) {
 
-                     better = true;
 
-                     break;
 
-                 }
 
-             }
 
-             if (better) {
 
-                 if (betteralgs) {
 
-                     char *old_ba = betteralgs;
 
-                     betteralgs = dupcat(betteralgs, ",",
 
-                                         hktype->alg->ssh_id,
 
-                                         (const char *)NULL);
 
-                     sfree(old_ba);
 
-                 } else {
 
-                     betteralgs = dupstr(hktype->alg->ssh_id);
 
-                 }
 
-             }
 
-         }
 
-         if (betteralgs) {
 
-                 /* Use the special warning prompt that lets us provide
 
-                  * a list of better algorithms */
 
-                 s->dlgret = seat_confirm_weak_cached_hostkey(
 
-                     s->ppl.seat, s->hostkey_alg->ssh_id, betteralgs,
 
-                     ssh2_transport_dialog_callback, s);
 
-             sfree(betteralgs);
 
-         } else {
 
-                 /* If none exist, use the more general 'weak crypto'
 
-                  * warning prompt */
 
-                 s->dlgret = seat_confirm_weak_crypto_primitive(
 
-                     s->ppl.seat, "host key type", s->hostkey_alg->ssh_id,
 
-                     ssh2_transport_dialog_callback, s);
 
-         }
 
-         crMaybeWaitUntilV(s->dlgret >= 0);
 
-         if (s->dlgret == 0) {
 
-             ssh_user_close(s->ppl.ssh, "User aborted at host key warning");
 
-             return;
 
-         }
 
-     }
 
-     if (s->warn_cscipher) {
 
-         s->dlgret = seat_confirm_weak_crypto_primitive(
 
-             s->ppl.seat, "client-to-server cipher", s->out.cipher->ssh2_id,
 
-             ssh2_transport_dialog_callback, s);
 
-         crMaybeWaitUntilV(s->dlgret >= 0);
 
-         if (s->dlgret == 0) {
 
-             ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
 
-             return;
 
-         }
 
-     }
 
-     if (s->warn_sccipher) {
 
-         s->dlgret = seat_confirm_weak_crypto_primitive(
 
-             s->ppl.seat, "server-to-client cipher", s->in.cipher->ssh2_id,
 
-             ssh2_transport_dialog_callback, s);
 
-         crMaybeWaitUntilV(s->dlgret >= 0);
 
-         if (s->dlgret == 0) {
 
-             ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
 
-             return;
 
-         }
 
-     }
 
-     /*
 
-      * If the other side has sent an initial key exchange packet that
 
-      * we must treat as a wrong guess, wait for it, and discard it.
 
-      */
 
-     if (s->ignorepkt)
 
-         crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
 
-     /*
 
-      * Actually perform the key exchange.
 
-      */
 
-     s->exhash = ssh_hash_new(s->kex_alg->hash);
 
-     put_stringz(s->exhash, s->client_greeting);
 
-     put_stringz(s->exhash, s->server_greeting);
 
-     put_string(s->exhash, s->client_kexinit->u, s->client_kexinit->len);
 
-     put_string(s->exhash, s->server_kexinit->u, s->server_kexinit->len);
 
-     s->crStateKex = 0;
 
-     while (1) {
 
-         bool aborted = false;
 
-         ssh2kex_coroutine(s, &aborted);
 
-         if (aborted)
 
-             return;    /* disaster: our entire state has been freed */
 
-         if (!s->crStateKex)
 
-             break;     /* kex phase has terminated normally */
 
-         crReturnV;
 
-     }
 
-     /*
 
-      * The exchange hash from the very first key exchange is also
 
-      * the session id, used in session key construction and
 
-      * authentication.
 
-      */
 
-     if (!s->got_session_id) {
 
-         assert(sizeof(s->exchange_hash) <= sizeof(s->session_id));
 
-         memcpy(s->session_id, s->exchange_hash, sizeof(s->exchange_hash));
 
-         s->session_id_len = s->kex_alg->hash->hlen;
 
-         assert(s->session_id_len <= sizeof(s->session_id));
 
-         s->got_session_id = true;
 
-     }
 
-     /*
 
-      * Send SSH2_MSG_NEWKEYS.
 
-      */
 
-     pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_NEWKEYS);
 
-     pq_push(s->ppl.out_pq, pktout);
 
-     /* Start counting down the outgoing-data limit for these cipher keys. */
 
-     s->stats->out.running = true;
 
-     s->stats->out.remaining = s->max_data_size;
 
-     /*
 
-      * Force the BPP to synchronously marshal all packets up to and
 
-      * including that NEWKEYS into wire format, before we switch over
 
-      * to new crypto.
 
-      */
 
-     ssh_bpp_handle_output(s->ppl.bpp);
 
-     /*
 
-      * We've sent outgoing NEWKEYS, so create and initialise outgoing
 
-      * session keys.
 
-      */
 
-     {
 
-         strbuf *cipher_key = strbuf_new();
 
-         strbuf *cipher_iv = strbuf_new();
 
-         strbuf *mac_key = strbuf_new();
 
-         if (s->out.cipher) {
 
-             ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash,
 
-                        'A' + s->out.mkkey_adjust, s->out.cipher->blksize);
 
-             ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash,
 
-                        'C' + s->out.mkkey_adjust,
 
-                        s->out.cipher->padded_keybytes);
 
-         }
 
-         if (s->out.mac) {
 
-             ssh2_mkkey(s, mac_key, s->K, s->exchange_hash,
 
-                        'E' + s->out.mkkey_adjust, s->out.mac->keylen);
 
-         }
 
-         ssh2_bpp_new_outgoing_crypto(
 
-             s->ppl.bpp,
 
-             s->out.cipher, cipher_key->u, cipher_iv->u,
 
-             s->out.mac, s->out.etm_mode, mac_key->u,
 
-             s->out.comp, s->out.comp_delayed);
 
-         strbuf_free(cipher_key);
 
-         strbuf_free(cipher_iv);
 
-         strbuf_free(mac_key);
 
-     }
 
-     /*
 
-      * Now our end of the key exchange is complete, we can send all
 
-      * our queued higher-layer packets. Transfer the whole of the next
 
-      * layer's outgoing queue on to our own.
 
-      */
 
-     pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher);
 
-     /*
 
-      * Expect SSH2_MSG_NEWKEYS from server.
 
-      */
 
-     crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
 
-     if (pktin->type != SSH2_MSG_NEWKEYS) {
 
-         ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
 
-                         "expecting SSH_MSG_NEWKEYS, type %d (%s)",
 
-                         pktin->type,
 
-                         ssh2_pkt_type(s->ppl.bpp->pls->kctx,
 
-                                       s->ppl.bpp->pls->actx,
 
-                                       pktin->type));
 
-         return;
 
-     }
 
-     /* Start counting down the incoming-data limit for these cipher keys. */
 
-     s->stats->in.running = true;
 
-     s->stats->in.remaining = s->max_data_size;
 
-     /*
 
-      * We've seen incoming NEWKEYS, so create and initialise
 
-      * incoming session keys.
 
-      */
 
-     {
 
-         strbuf *cipher_key = strbuf_new();
 
-         strbuf *cipher_iv = strbuf_new();
 
-         strbuf *mac_key = strbuf_new();
 
-         if (s->in.cipher) {
 
-             ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash,
 
-                        'A' + s->in.mkkey_adjust, s->in.cipher->blksize);
 
-             ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash,
 
-                        'C' + s->in.mkkey_adjust,
 
-                        s->in.cipher->padded_keybytes);
 
-         }
 
-         if (s->in.mac) {
 
-             ssh2_mkkey(s, mac_key, s->K, s->exchange_hash,
 
-                        'E' + s->in.mkkey_adjust, s->in.mac->keylen);
 
-         }
 
-         ssh2_bpp_new_incoming_crypto(
 
-             s->ppl.bpp,
 
-             s->in.cipher, cipher_key->u, cipher_iv->u,
 
-             s->in.mac, s->in.etm_mode, mac_key->u,
 
-             s->in.comp, s->in.comp_delayed);
 
-         strbuf_free(cipher_key);
 
-         strbuf_free(cipher_iv);
 
-         strbuf_free(mac_key);
 
-     }
 
-     /*
 
-      * Free shared secret.
 
-      */
 
-     mp_free(s->K); s->K = NULL;
 
-     /*
 
-      * Update the specials menu to list the remaining uncertified host
 
-      * keys.
 
-      */
 
-     seat_update_specials_menu(s->ppl.seat);
 
-     /*
 
-      * Key exchange is over. Loop straight back round if we have a
 
-      * deferred rekey reason.
 
-      */
 
-     if (s->deferred_rekey_reason) {
 
-         ppl_logevent("%s", s->deferred_rekey_reason);
 
-         pktin = NULL;
 
-         s->deferred_rekey_reason = NULL;
 
-         goto begin_key_exchange;
 
-     }
 
-     /*
 
-      * Otherwise, schedule a timer for our next rekey.
 
-      */
 
-     s->kex_in_progress = false;
 
-     s->last_rekey = GETTICKCOUNT();
 
-     (void) ssh2_transport_timer_update(s, 0);
 
-     /*
 
-      * Now we're encrypting. Get the next-layer protocol started if it
 
-      * hasn't already, and then sit here waiting for reasons to go
 
-      * back to the start and do a repeat key exchange. One of those
 
-      * reasons is that we receive KEXINIT from the other end; the
 
-      * other is if we find rekey_reason is non-NULL, i.e. we've
 
-      * decided to initiate a rekey ourselves for some reason.
 
-      */
 
-     if (!s->higher_layer_ok) {
 
-         if (!s->hostkeys) {
 
-             /* We're the client, so send SERVICE_REQUEST. */
 
-             pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_REQUEST);
 
-             put_stringz(pktout, s->higher_layer->vt->name);
 
-             pq_push(s->ppl.out_pq, pktout);
 
-             crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
 
-             if (pktin->type != SSH2_MSG_SERVICE_ACCEPT) {
 
-                 ssh_sw_abort(s->ppl.ssh, "Server refused request to start "
 
-                              "'%s' protocol", s->higher_layer->vt->name);
 
-                 return;
 
-             }
 
-         } else {
 
-             ptrlen service_name;
 
-             /* We're the server, so expect SERVICE_REQUEST. */
 
-             crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
 
-             if (pktin->type != SSH2_MSG_SERVICE_REQUEST) {
 
-                 ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
 
-                                 "expecting SERVICE_REQUEST, type %d (%s)",
 
-                                 pktin->type,
 
-                                 ssh2_pkt_type(s->ppl.bpp->pls->kctx,
 
-                                               s->ppl.bpp->pls->actx,
 
-                                               pktin->type));
 
-                 return;
 
-             }
 
-             service_name = get_string(pktin);
 
-             if (!ptrlen_eq_string(service_name, s->higher_layer->vt->name)) {
 
-                 ssh_proto_error(s->ppl.ssh, "Client requested service "
 
-                                 "'%.*s' when we only support '%s'",
 
-                                 PTRLEN_PRINTF(service_name),
 
-                                 s->higher_layer->vt->name);
 
-                 return;
 
-             }
 
-             pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_ACCEPT);
 
-             put_stringz(pktout, s->higher_layer->vt->name);
 
-             pq_push(s->ppl.out_pq, pktout);
 
-         }
 
-         s->higher_layer_ok = true;
 
-         queue_idempotent_callback(&s->higher_layer->ic_process_queue);
 
-     }
 
-     s->rekey_class = RK_NONE;
 
-     do {
 
-         crReturnV;
 
-         /* Pass through outgoing packets from the higher layer. */
 
-         pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher);
 
-         /* Wait for either a KEXINIT, or something setting
 
-          * s->rekey_class. This call to ssh2_transport_pop also has
 
-          * the side effect of transferring incoming packets _to_ the
 
-          * higher layer (via filter_queue). */
 
-         if ((pktin = ssh2_transport_pop(s)) != NULL) {
 
-             if (pktin->type != SSH2_MSG_KEXINIT) {
 
-                 ssh_proto_error(s->ppl.ssh, "Received unexpected transport-"
 
-                                 "layer packet outside a key exchange, "
 
-                                 "type %d (%s)", pktin->type,
 
-                                 ssh2_pkt_type(s->ppl.bpp->pls->kctx,
 
-                                               s->ppl.bpp->pls->actx,
 
-                                               pktin->type));
 
-                 return;
 
-             }
 
-             pq_push_front(s->ppl.in_pq, pktin);
 
-             ppl_logevent("Remote side initiated key re-exchange");
 
-             s->rekey_class = RK_SERVER;
 
-         }
 
-         if (s->rekey_class == RK_POST_USERAUTH) {
 
-             /*
 
-              * userauth has seen a USERAUTH_SUCCESS. This may be the
 
-              * moment to do an immediate rekey with different
 
-              * parameters. But it may not; so here we turn that rekey
 
-              * class into either RK_NONE or RK_NORMAL.
 
-              *
 
-              * Currently the only reason for this is if we've done a
 
-              * GSS key exchange and don't have anything in our
 
-              * transient hostkey cache, in which case we should make
 
-              * an attempt to populate the cache now.
 
-              */
 
-             if (s->need_gss_transient_hostkey) {
 
-                 s->rekey_reason = "populating transient host key cache";
 
-                 s->rekey_class = RK_NORMAL;
 
-             } else {
 
-                 /* No need to rekey at this time. */
 
-                 s->rekey_class = RK_NONE;
 
-             }
 
-         }
 
-         if (!s->rekey_class) {
 
-             /* If we don't yet have any other reason to rekey, check
 
-              * if we've hit our data limit in either direction. */
 
-             if (!s->stats->in.running) {
 
-                 s->rekey_reason = "too much data received";
 
-                 s->rekey_class = RK_NORMAL;
 
-             } else if (!s->stats->out.running) {
 
-                 s->rekey_reason = "too much data sent";
 
-                 s->rekey_class = RK_NORMAL;
 
-             }
 
-         }
 
-         if (s->rekey_class != RK_NONE && s->rekey_class != RK_SERVER) {
 
-             /*
 
-              * Special case: if the server bug is set that doesn't
 
-              * allow rekeying, we give a different log message and
 
-              * continue waiting. (If such a server _initiates_ a
 
-              * rekey, we process it anyway!)
 
-              */
 
-             if ((s->ppl.remote_bugs & BUG_SSH2_REKEY)) {
 
-                 ppl_logevent("Remote bug prevents key re-exchange (%s)",
 
-                              s->rekey_reason);
 
-                 /* Reset the counters, so that at least this message doesn't
 
-                  * hit the event log _too_ often. */
 
-                 s->stats->in.running = s->stats->out.running = true;
 
-                 s->stats->in.remaining = s->stats->out.remaining =
 
-                     s->max_data_size;
 
-                 (void) ssh2_transport_timer_update(s, 0);
 
-                 s->rekey_class = RK_NONE;
 
-             } else {
 
-                 ppl_logevent("Initiating key re-exchange (%s)",
 
-                              s->rekey_reason);
 
-             }
 
-         }
 
-     } while (s->rekey_class == RK_NONE);
 
-     /* Once we exit the above loop, we really are rekeying. */
 
-     goto begin_key_exchange;
 
-     crFinishV;
 
- }
 
- static void ssh2_transport_higher_layer_packet_callback(void *context)
 
- {
 
-     PacketProtocolLayer *ppl = (PacketProtocolLayer *)context;
 
-     ssh_ppl_process_queue(ppl);
 
- }
 
- static void ssh2_transport_timer(void *ctx, unsigned long now)
 
- {
 
-     struct ssh2_transport_state *s = (struct ssh2_transport_state *)ctx;
 
-     unsigned long mins;
 
-     unsigned long ticks;
 
-     if (s->kex_in_progress || now != s->next_rekey)
 
-         return;
 
-     mins = sanitise_rekey_time(conf_get_int(s->conf, CONF_ssh_rekey_time), 60);
 
-     if (mins == 0)
 
- 	return;
 
-     /* Rekey if enough time has elapsed */
 
-     ticks = mins * 60 * TICKSPERSEC;
 
-     if (now - s->last_rekey > ticks - 30*TICKSPERSEC) {
 
-         s->rekey_reason = "timeout";
 
-         s->rekey_class = RK_NORMAL;
 
-         queue_idempotent_callback(&s->ppl.ic_process_queue);
 
-         return;
 
-     }
 
- #ifndef NO_GSSAPI
 
-     /*
 
-      * Rekey now if we have a new cred or context expires this cycle,
 
-      * but not if this is unsafe.
 
-      */
 
-     if (conf_get_int(s->conf, CONF_gssapirekey)) {
 
-         ssh2_transport_gss_update(s, false);
 
-         if ((s->gss_status & GSS_KEX_CAPABLE) != 0 &&
 
-             (s->gss_status & GSS_CTXT_MAYFAIL) == 0 &&
 
-             (s->gss_status & (GSS_CRED_UPDATED|GSS_CTXT_EXPIRES)) != 0) {
 
-             s->rekey_reason = "GSS credentials updated";
 
-             s->rekey_class = RK_GSS_UPDATE;
 
-             queue_idempotent_callback(&s->ppl.ic_process_queue);
 
-             return;
 
-         }
 
-     }
 
- #endif
 
-     /* Try again later. */
 
-     (void) ssh2_transport_timer_update(s, 0);
 
- }
 
- /*
 
-  * The rekey_time is zero except when re-configuring.
 
-  *
 
-  * We either schedule the next timer and return false, or return true
 
-  * to run the callback now, which will call us again to re-schedule on
 
-  * completion.
 
-  */
 
- static bool ssh2_transport_timer_update(struct ssh2_transport_state *s,
 
-                                         unsigned long rekey_time)
 
- {
 
-     unsigned long mins;
 
-     unsigned long ticks;
 
-     mins = sanitise_rekey_time(conf_get_int(s->conf, CONF_ssh_rekey_time), 60);
 
-     ticks = mins * 60 * TICKSPERSEC;
 
-     /* Handle change from previous setting */
 
-     if (rekey_time != 0 && rekey_time != mins) {
 
-         unsigned long next;
 
-         unsigned long now = GETTICKCOUNT();
 
-         mins = rekey_time;
 
-         ticks = mins * 60 * TICKSPERSEC;
 
-         next = s->last_rekey + ticks;
 
-         /* If overdue, caller will rekey synchronously now */
 
-         if (now - s->last_rekey > ticks)
 
-             return true;
 
-         ticks = next - now;
 
-     }
 
- #ifndef NO_GSSAPI
 
-     if (s->gss_kex_used) {
 
-         /*
 
-          * If we've used GSSAPI key exchange, then we should
 
-          * periodically check whether we need to do another one to
 
-          * pass new credentials to the server.
 
-          */
 
-         unsigned long gssmins;
 
-         /* Check cascade conditions more frequently if configured */
 
-         gssmins = sanitise_rekey_time(
 
-             conf_get_int(s->conf, CONF_gssapirekey), GSS_DEF_REKEY_MINS);
 
-         if (gssmins > 0) {
 
-             if (gssmins < mins)
 
-                 ticks = (mins = gssmins) * 60 * TICKSPERSEC;
 
-             if ((s->gss_status & GSS_KEX_CAPABLE) != 0) {
 
-                 /*
 
-                  * Run next timer even sooner if it would otherwise be
 
-                  * too close to the context expiration time
 
-                  */
 
-                 if ((s->gss_status & GSS_CTXT_EXPIRES) == 0 &&
 
-                     s->gss_ctxt_lifetime - mins * 60 < 2 * MIN_CTXT_LIFETIME)
 
-                     ticks -= 2 * MIN_CTXT_LIFETIME * TICKSPERSEC;
 
-             }
 
-         }
 
-     }
 
- #endif
 
-     /* Schedule the next timer */
 
-     s->next_rekey = schedule_timer(ticks, ssh2_transport_timer, s);
 
-     return false;
 
- }
 
- void ssh2_transport_dialog_callback(void *loginv, int ret)
 
- {
 
-     struct ssh2_transport_state *s = (struct ssh2_transport_state *)loginv;
 
-     s->dlgret = ret;
 
-     ssh_ppl_process_queue(&s->ppl);
 
- }
 
- #ifndef NO_GSSAPI
 
- /*
 
-  * This is called at the beginning of each SSH rekey to determine
 
-  * whether we are GSS capable, and if we did GSS key exchange, and are
 
-  * delegating credentials, it is also called periodically to determine
 
-  * whether we should rekey in order to delegate (more) fresh
 
-  * credentials. This is called "credential cascading".
 
-  *
 
-  * On Windows, with SSPI, we may not get the credential expiration, as
 
-  * Windows automatically renews from cached passwords, so the
 
-  * credential effectively never expires. Since we still want to
 
-  * cascade when the local TGT is updated, we use the expiration of a
 
-  * newly obtained context as a proxy for the expiration of the TGT.
 
-  */
 
- static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
 
-                                       bool definitely_rekeying)
 
- {
 
-     PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
 
-     int gss_stat;
 
-     time_t gss_cred_expiry;
 
-     unsigned long mins;
 
-     Ssh_gss_buf gss_sndtok;
 
-     Ssh_gss_buf gss_rcvtok;
 
-     Ssh_gss_ctx gss_ctx;
 
-     s->gss_status = 0;
 
-     /*
 
-      * Nothing to do if no GSSAPI libraries are configured or GSSAPI
 
-      * auth is not enabled.
 
-      */
 
-     if (s->shgss->libs->nlibraries == 0)
 
-         return;
 
-     if (!conf_get_bool(s->conf, CONF_try_gssapi_auth) &&
 
-         !conf_get_bool(s->conf, CONF_try_gssapi_kex))
 
-         return;
 
-     /* Import server name and cache it */
 
-     if (s->shgss->srv_name == GSS_C_NO_NAME) {
 
-         gss_stat = s->shgss->lib->import_name(
 
-             s->shgss->lib, s->fullhostname, &s->shgss->srv_name);
 
-         if (gss_stat != SSH_GSS_OK) {
 
-             if (gss_stat == SSH_GSS_BAD_HOST_NAME)
 
-                 ppl_logevent("GSSAPI import name failed - Bad service name;"
 
-                              " won't use GSS key exchange");
 
-             else
 
-                 ppl_logevent("GSSAPI import name failed;"
 
-                              " won't use GSS key exchange");
 
-             return;
 
-         }
 
-     }
 
-     /*
 
-      * Do we (still) have credentials? Capture the credential
 
-      * expiration when available
 
-      */
 
-     gss_stat = s->shgss->lib->acquire_cred(
 
-         s->shgss->lib, &gss_ctx, &gss_cred_expiry);
 
-     if (gss_stat != SSH_GSS_OK)
 
-         return;
 
-     SSH_GSS_CLEAR_BUF(&gss_sndtok);
 
-     SSH_GSS_CLEAR_BUF(&gss_rcvtok);
 
-     /*
 
-      * When acquire_cred yields no useful expiration, get a proxy for
 
-      * the cred expiration from the context expiration.
 
-      */
 
-     gss_stat = s->shgss->lib->init_sec_context(
 
-         s->shgss->lib, &gss_ctx, s->shgss->srv_name,
 
-         0 /* don't delegate */, &gss_rcvtok, &gss_sndtok,
 
-         (gss_cred_expiry == GSS_NO_EXPIRATION ? &gss_cred_expiry : NULL),
 
-         &s->gss_ctxt_lifetime);
 
-     /* This context was for testing only. */
 
-     if (gss_ctx)
 
-         s->shgss->lib->release_cred(s->shgss->lib, &gss_ctx);
 
-     if (gss_stat != SSH_GSS_OK &&
 
-         gss_stat != SSH_GSS_S_CONTINUE_NEEDED) {
 
-         /*
 
-          * No point in verbosely interrupting the user to tell them we
 
-          * couldn't get GSS credentials, if this was only a check
 
-          * between key exchanges to see if fresh ones were available.
 
-          * When we do do a rekey, this message (if displayed) will
 
-          * appear among the standard rekey blurb, but when we're not,
 
-          * it shouldn't pop up all the time regardless.
 
-          */
 
-         if (definitely_rekeying)
 
-             ppl_logevent("No GSSAPI security context available");
 
-         return;
 
-     }
 
-     if (gss_sndtok.length)
 
-         s->shgss->lib->free_tok(s->shgss->lib, &gss_sndtok);
 
-     s->gss_status |= GSS_KEX_CAPABLE;
 
-     /*
 
-      * When rekeying to cascade, avoding doing this too close to the
 
-      * context expiration time, since the key exchange might fail.
 
-      */
 
-     if (s->gss_ctxt_lifetime < MIN_CTXT_LIFETIME)
 
-         s->gss_status |= GSS_CTXT_MAYFAIL;
 
-     /*
 
-      * If we're not delegating credentials, rekeying is not used to
 
-      * refresh them. We must avoid setting GSS_CRED_UPDATED or
 
-      * GSS_CTXT_EXPIRES when credential delegation is disabled.
 
-      */
 
-     if (!conf_get_bool(s->conf, CONF_gssapifwd))
 
-         return;
 
-     if (s->gss_cred_expiry != GSS_NO_EXPIRATION &&
 
-         difftime(gss_cred_expiry, s->gss_cred_expiry) > 0)
 
-         s->gss_status |= GSS_CRED_UPDATED;
 
-     mins = sanitise_rekey_time(
 
-         conf_get_int(s->conf, CONF_gssapirekey), GSS_DEF_REKEY_MINS);
 
-     if (mins > 0 && s->gss_ctxt_lifetime <= mins * 60)
 
-         s->gss_status |= GSS_CTXT_EXPIRES;
 
- }
 
- ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ppl)
 
- {
 
-     struct ssh2_transport_state *s;
 
-     assert(ppl->vt == &ssh2_transport_vtable);
 
-     s = container_of(ppl, struct ssh2_transport_state, ppl);
 
-     assert(s->got_session_id);
 
-     return make_ptrlen(s->session_id, s->session_id_len);
 
- }
 
- void ssh2_transport_notify_auth_done(PacketProtocolLayer *ppl)
 
- {
 
-     struct ssh2_transport_state *s;
 
-     assert(ppl->vt == &ssh2_transport_vtable);
 
-     s = container_of(ppl, struct ssh2_transport_state, ppl);
 
-     s->rekey_reason = NULL;            /* will be filled in later */
 
-     s->rekey_class = RK_POST_USERAUTH;
 
-     queue_idempotent_callback(&s->ppl.ic_process_queue);
 
- }
 
- #endif /* NO_GSSAPI */
 
- static bool ssh2_transport_get_specials(
 
-     PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
 
- {
 
-     struct ssh2_transport_state *s =
 
-         container_of(ppl, struct ssh2_transport_state, ppl);
 
-     bool need_separator = false;
 
-     bool toret = false;
 
-     if (ssh_ppl_get_specials(s->higher_layer, add_special, ctx)) {
 
-         need_separator = true;
 
-         toret = true;
 
-     }
 
-     /*
 
-      * Don't bother offering rekey-based specials if we've decided the
 
-      * remote won't cope with it, since we wouldn't bother sending it
 
-      * if asked anyway.
 
-      */
 
-     if (!(s->ppl.remote_bugs & BUG_SSH2_REKEY)) {
 
-         if (need_separator) {
 
-             add_special(ctx, NULL, SS_SEP, 0);
 
-             need_separator = false;
 
-         }
 
-         add_special(ctx, "Repeat key exchange", SS_REKEY, 0);
 
-         toret = true;
 
-         if (s->n_uncert_hostkeys) {
 
-             int i;
 
-             add_special(ctx, NULL, SS_SEP, 0);
 
-             add_special(ctx, "Cache new host key type", SS_SUBMENU, 0);
 
-             for (i = 0; i < s->n_uncert_hostkeys; i++) {
 
-                 const ssh_keyalg *alg =
 
-                     ssh2_hostkey_algs[s->uncert_hostkeys[i]].alg;
 
-                 add_special(ctx, alg->ssh_id, SS_XCERT, s->uncert_hostkeys[i]);
 
-             }
 
-             add_special(ctx, NULL, SS_EXITMENU, 0);
 
-         }
 
-     }
 
-     return toret;
 
- }
 
- static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl,
 
-                                        SessionSpecialCode code, int arg)
 
- {
 
-     struct ssh2_transport_state *s =
 
-         container_of(ppl, struct ssh2_transport_state, ppl);
 
-     if (code == SS_REKEY) {
 
- 	if (!s->kex_in_progress) {
 
-             s->rekey_reason = "at user request";
 
-             s->rekey_class = RK_NORMAL;
 
-             queue_idempotent_callback(&s->ppl.ic_process_queue);
 
- 	}
 
-     } else if (code == SS_XCERT) {
 
- 	if (!s->kex_in_progress) {
 
-             s->cross_certifying = s->hostkey_alg = ssh2_hostkey_algs[arg].alg;
 
-             s->rekey_reason = "cross-certifying new host key";
 
-             s->rekey_class = RK_NORMAL;
 
-             queue_idempotent_callback(&s->ppl.ic_process_queue);
 
- 	}
 
-     } else {
 
-         /* Send everything else to the next layer up. This includes
 
-          * SS_PING/SS_NOP, which we _could_ handle here - but it's
 
-          * better to put them in the connection layer, so they'll
 
-          * still work in bare connection mode. */
 
-         ssh_ppl_special_cmd(s->higher_layer, code, arg);
 
-     }
 
- }
 
- /* Safely convert rekey_time to unsigned long minutes */
 
- static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def)
 
- {
 
-     if (rekey_time < 0 || rekey_time > MAX_TICK_MINS)
 
-         rekey_time = def;
 
-     return (unsigned long)rekey_time;
 
- }
 
- static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s)
 
- {
 
-     s->max_data_size = parse_blocksize(
 
-         conf_get_str(s->conf, CONF_ssh_rekey_data));
 
- }
 
- static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
 
- {
 
-     struct ssh2_transport_state *s;
 
-     const char *rekey_reason = NULL;
 
-     bool rekey_mandatory = false;
 
-     unsigned long old_max_data_size, rekey_time;
 
-     int i;
 
-     assert(ppl->vt == &ssh2_transport_vtable);
 
-     s = container_of(ppl, struct ssh2_transport_state, ppl);
 
-     rekey_time = sanitise_rekey_time(
 
-         conf_get_int(conf, CONF_ssh_rekey_time), 60);
 
-     if (ssh2_transport_timer_update(s, rekey_time))
 
-         rekey_reason = "timeout shortened";
 
-     old_max_data_size = s->max_data_size;
 
-     ssh2_transport_set_max_data_size(s);
 
-     if (old_max_data_size != s->max_data_size &&
 
- 	s->max_data_size != 0) {
 
-         if (s->max_data_size < old_max_data_size) {
 
-             unsigned long diff = old_max_data_size - s->max_data_size;
 
-             /* We must decrement both counters, so avoid short-circuit
 
-              * evaluation skipping one */
 
-             bool out_expired = DTS_CONSUME(s->stats, out, diff);
 
-             bool in_expired = DTS_CONSUME(s->stats, in, diff);
 
-             if (out_expired || in_expired)
 
-                 rekey_reason = "data limit lowered";
 
-         } else {
 
-             unsigned long diff = s->max_data_size - old_max_data_size;
 
-             if (s->stats->out.running)
 
-                 s->stats->out.remaining += diff;
 
-             if (s->stats->in.running)
 
-                 s->stats->in.remaining += diff;
 
-         }
 
-     }
 
-     if (conf_get_bool(s->conf, CONF_compression) !=
 
- 	conf_get_bool(conf, CONF_compression)) {
 
-         rekey_reason = "compression setting changed";
 
-         rekey_mandatory = true;
 
-     }
 
-     for (i = 0; i < CIPHER_MAX; i++)
 
- 	if (conf_get_int_int(s->conf, CONF_ssh_cipherlist, i) !=
 
- 	    conf_get_int_int(conf, CONF_ssh_cipherlist, i)) {
 
-         rekey_reason = "cipher settings changed";
 
-         rekey_mandatory = true;
 
-     }
 
-     if (conf_get_bool(s->conf, CONF_ssh2_des_cbc) !=
 
- 	conf_get_bool(conf, CONF_ssh2_des_cbc)) {
 
-         rekey_reason = "cipher settings changed";
 
-         rekey_mandatory = true;
 
-     }
 
-     conf_free(s->conf);
 
-     s->conf = conf_copy(conf);
 
-     if (rekey_reason) {
 
-         if (!s->kex_in_progress && !ssh2_bpp_rekey_inadvisable(s->ppl.bpp)) {
 
-             s->rekey_reason = rekey_reason;
 
-             s->rekey_class = RK_NORMAL;
 
-             queue_idempotent_callback(&s->ppl.ic_process_queue);
 
-         } else if (rekey_mandatory) {
 
-             s->deferred_rekey_reason = rekey_reason;
 
-         }
 
-     }
 
-     /* Also pass the configuration along to our higher layer */
 
-     ssh_ppl_reconfigure(s->higher_layer, conf);
 
- }
 
- static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl)
 
- {
 
-     struct ssh2_transport_state *s =
 
-         container_of(ppl, struct ssh2_transport_state, ppl);
 
-     /* Just delegate this to the higher layer */
 
-     return ssh_ppl_want_user_input(s->higher_layer);
 
- }
 
- static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl)
 
- {
 
-     struct ssh2_transport_state *s =
 
-         container_of(ppl, struct ssh2_transport_state, ppl);
 
-     /* Just delegate this to the higher layer */
 
-     ssh_ppl_got_user_input(s->higher_layer);
 
- }
 
 
  |