From 7a674c006b3d09735c9340ad74f02556fbd91cbd Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Sun, 4 Sep 2016 01:23:37 +0200 Subject: [PATCH] ssl: fix TLS renegotiation, add test for this A handshake starts a new session, be sure to clear the previous state to avoid creating a decoder with wrong secrets. Renegotiations are also kind of transparant to the application layer, so be sure to re-use an existing SslFlow. This fixes the Follow SSL stream functionality which would previously ignore everything except for the first session. The capture file contains a crafted HTTP request/response over TLS 1.2, interleaved with renegotiations. The HTTP response contains the Python script used to generate the traffic. Surprise! Change-Id: I0110ce76893d4a79330845e53e47e10f1c79e47e Reviewed-on: https://code.wireshark.org/review/17480 Petri-Dish: Peter Wu Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu --- epan/dissectors/packet-ssl-utils.c | 57 +++++++++++++++++++++++++-- test/captures/tls-renegotiation.pcap | Bin 0 -> 12935 bytes test/suite-decryption.sh | 17 ++++++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 test/captures/tls-renegotiation.pcap diff --git a/epan/dissectors/packet-ssl-utils.c b/epan/dissectors/packet-ssl-utils.c index 7c8f37622c..5ac15f01f1 100644 --- a/epan/dissectors/packet-ssl-utils.c +++ b/epan/dissectors/packet-ssl-utils.c @@ -2780,7 +2780,6 @@ ssl_create_decoder(const SslCipherSuite *cipher_suite, gint compression, } dec->seq = 0; dec->decomp = ssl_create_decompressor(compression); - dec->flow = ssl_create_flow(); /* TODO this does nothing as dec->evp is always NULL. */ if (dec->evp) @@ -3222,6 +3221,10 @@ create_decoders: goto fail; } + /* Continue the SSL stream after renegotiation with new keys. */ + ssl_session->client_new->flow = ssl_session->client ? ssl_session->client->flow : ssl_create_flow(); + ssl_session->server_new->flow = ssl_session->server ? ssl_session->server->flow : ssl_create_flow(); + ssl_debug_printf("%s: client seq %d, server seq %d\n", G_STRFUNC, ssl_session->client_new->seq, ssl_session->server_new->seq); g_free(key_block.data); @@ -4109,6 +4112,48 @@ ssl_get_session(conversation_t *conversation, dissector_handle_t ssl_handle) return ssl_session; } +/* Resets the decryption parameters for the next decoder. */ +static void ssl_reset_session(SslSession *session, SslDecryptSession *ssl, gboolean is_client) +{ + if (ssl) { + /* Ensure that secrets are not restored using stale identifiers. Split + * between client and server in case the packets somehow got out of order. */ + gint clear_flags = SSL_HAVE_SESSION_KEY | SSL_MASTER_SECRET | SSL_PRE_MASTER_SECRET; + + if (is_client) { + clear_flags |= SSL_CLIENT_EXTENDED_MASTER_SECRET; + ssl->session_id.data_len = 0; + ssl->session_ticket.data_len = 0; + ssl->master_secret.data_len = 0; + ssl->client_random.data_len = 0; + } else { + clear_flags |= SSL_SERVER_EXTENDED_MASTER_SECRET | SSL_NEW_SESSION_TICKET; + ssl->server_random.data_len = 0; + ssl->pre_master_secret.data_len = 0; +#if defined(HAVE_LIBGNUTLS) && defined(HAVE_LIBGCRYPT) + ssl->private_key = NULL; +#endif + ssl->psk.data_len = 0; + } + + if (ssl->state & clear_flags) { + ssl_debug_printf("%s detected renegotiation, clearing 0x%02x (%s side)\n", + G_STRFUNC, ssl->state & clear_flags, is_client ? "client" : "server"); + ssl->state &= ~clear_flags; + } + } + + /* These flags might be used for non-decryption purposes and may affect the + * dissection, so reset them as well. */ + if (is_client) { + session->client_cert_type = 0; + } else { + session->compression = 0; + session->server_cert_type = 0; + /* session->is_session_resumed is already handled in the ServerHello dissection. */ + } +} + static guint32 ssl_starttls(dissector_handle_t ssl_handle, packet_info *pinfo, dissector_handle_t app_handle, guint32 last_nontls_frame) @@ -5441,12 +5486,16 @@ ssl_dissect_hnd_hello_ext_cert_type(ssl_common_dissect_t *hf, tvbuff_t *tvb, static gint ssl_dissect_hnd_hello_common(ssl_common_dissect_t *hf, tvbuff_t *tvb, proto_tree *tree, guint32 offset, - SslDecryptSession *ssl, gboolean from_server) + SslSession *session, SslDecryptSession *ssl, + gboolean from_server) { nstime_t gmt_unix_time; guint8 sessid_length; proto_tree *rnd_tree; + /* Prepare for renegotiation by resetting the state. */ + ssl_reset_session(session, ssl, !from_server); + if (ssl) { StringInfo *rnd; if (from_server) @@ -5778,7 +5827,7 @@ ssl_dissect_hnd_cli_hello(ssl_common_dissect_t *hf, tvbuff_t *tvb, offset += 2; /* dissect fields that are also present in ClientHello */ - offset = ssl_dissect_hnd_hello_common(hf, tvb, tree, offset, ssl, FALSE); + offset = ssl_dissect_hnd_hello_common(hf, tvb, tree, offset, session, ssl, FALSE); /* fields specific for DTLS (cookie_len, cookie) */ if (dtls_hfs != NULL) { @@ -5898,7 +5947,7 @@ ssl_dissect_hnd_srv_hello(ssl_common_dissect_t *hf, tvbuff_t *tvb, offset += 2; /* dissect fields that are also present in ClientHello */ - offset = ssl_dissect_hnd_hello_common(hf, tvb, tree, offset, ssl, TRUE); + offset = ssl_dissect_hnd_hello_common(hf, tvb, tree, offset, session, ssl, TRUE); if (ssl) { /* store selected cipher suite for decryption */ diff --git a/test/captures/tls-renegotiation.pcap b/test/captures/tls-renegotiation.pcap new file mode 100644 index 0000000000000000000000000000000000000000..7d772a58f5223ee5007a81f248e45e179842f317 GIT binary patch literal 12935 zcmaJ{1yq#V_nn}-OG-cq>6T7u5J5^x>FyAuI~7qnr9-4ax*G&RLQ+6lkQ5N%Ki~Lx zKi>QMxNF_DnBik{_CEXEb1uVJdwC`VgaG>ALI8nbFFelsHAAV;K=QD!fqiwMfTTen zA;_s1NDS$2iw_6{26l%8u4;Nx-U;wz2Bfg?oE-#$AY4o!Kv2+-H*%oB`(P*(89>gw z78wKrkcH|lk#SW!BwV;8t6}J;8F>F689={8u7M#-zoE}n7c2L|BCq( zhF;@~1J*cafW-*@9WQ_i#A-*42Fig!0sHELBgR9{;D|-E+U=vYT9D)N>n-lLW3g01`@B?fXsBD59v}-xhk#&dVFO*j9%cd2!hSO*h#EwR z1V(}&K@q`-5JV^f7y*I+g;9q8>O?|;hx30kQF5Y_7chzOGbh zw=;SYlbEORaH*{mQFSf}#}*R=69I$;lL00KAOk>=2u2tI9Rvda{{n$e+5N#sux*e5 zmjM+NV`ckb#%QL79TN$aDOfi62PPPdjtF8W;J{gHqzjJU+1LYE+yP$ZIOMe;xQx@6+zTdc#?j!R7cvahIjck?EqeF z$y{EwfWOc)I&HM$ILU}CMqbevAH8ncSBySrLuidr$!w&=g`c9gOyBTrifXC}?G@HP zB~^IgotKq(9;g`hT^Is0893`cyD~ckj4cHg0uchiETc1_=ZdS$;dgV9@Q06|Ji()3 zWv2qZO^ZOnPUQRN2QYvK@DUK%pqsSlEND()Xw zAAbwpeMgm@7m?|J?vfYEaFcZ2+FU)C-EC<$)K|!L?tNGCvOGyw%y@Q{)m$4FizXRs zC-YjjetOHYk3f__gi_UByhfjl#0Yo?a))K~rTOBB8RN}-3Br`^1CmEB@~qBYTPnE^ z7aN|J!HfVxz=F8~F#T7UMgQCMkYDgh?Yy+;@7mATbK$y&0yDkApKAZ`78-~I<~v|t zT|n)F(IDplBLDUu5lLW(=l|dI!8jn0Kgd@s=@i4yyHhF3YKV}*n>SHQ^p}?%ZRo+M zkym163V?YUOiQ8}-6p(2B@alq?yx*zVt)D*G@-|6b`j>;7!vcOo90i+it z1Tfv;WdsuA6i-+$Ei4$;JKWicWPCvI;11HM=_h&jNR7c>&$48mE)BDUuE5)(U91i^ zv05)oCZOy;Uj&iqFK>BHeW`qZ3Mk)y2Mr_y(+JpC7hEUSm1;nzhq#wI{XTB)p`<4j zaQTve^)G1t)(HVPb%CT7!dg-;>}_UHMva!Qc1OJ{2ekT`7X{^xQd-l=NAg7y@JOXT zjrqrPay`G-7E+yz;ol-42H);BeV128tv4z+!NYWkn$@C@*k7>da7R7q_vA09M#a~) zj+AEptoM)uie8m04wX+Z*aAvpt|K=Q*wKtEMya z4Pu+z)5kBBuTc@kvS;JC8NYQy7%D-sZ?mO;hbrAcbZHioq@4F$olj)w5YRHe<60MA zz+K#aspW4M7jU_7$v|K(HoxLx9GI2}0HR(kIw%j87GPgp@D7#;1;G*Z{)k8dLyY=6 zJ2U_yF)TZVt&f`W{_MsAt-=%l_SFR!VhRd| z3qgK~_?NT^aA^S{2LBd9ABM;Z*fb+n!xrU`KeJxHN=Lq=K$qL!PNz4l#oTl(NqHoY zCUAu9`#W2AO1a6V2fBoKUdA?z?_AhrWWOsVwJ`}OVLEU>>=P*9F=M@ znVSCfGh1Xf2355#BhV_Z5}nUds;2^!pz7quQA)g{&ql`>^EuY~iCF-xT zI6pLo+}AiL`_sOx>Jb={$ub7>4}YCGayaIOPg|rtSl0&lslNcmz#^uAhl(CGWUFK2`RQrA$(KU-^%!H2n4B%9}}mlOUWMJ4zkcDt#lcmoQAHc;i791 zlOCHeil_0MB5(DO$Y$t*qdIBm=_On5$c1iq4xdWRG?|n{hHi~2e%-EmLZS6JKJCKc z1SR<4*AEU{n1gB=(5g!o1bs&n9sT=zWH-l-V@U1f(eAIMxFSRd3}O!6F$6jndh?F{?mc&Y;W5+g8YLVm=&( zPDK9rNjraQaB6wGaJg00!F1M-%sZUToNtRYwOxL7gLOY^r|<@lvqAU<5Z|XR$5Gw% z!&mNZF>S8#&1wAzIG24bn{gnHm2WUWr+*+k3oK3|(q59~uzMI4zGGw<%r3@vEB4K( z{V(oet%)T{1IIX^fGl7I^xvZj6aqhq{&lq~6n5c~+l7^+^OceW3djIJjO)h&Il$U4 zu&*w73pRp60Yv^6mnHf8Z2Jq51nhjF`Hzw`0Em@NR6GyC*7&F-m_PXo@`Ny(Ov}Ka z)tW(Gs|yq;ZhppyG*9Z(TElHPBK8#h@lP(XZ;;aPW<_J>W|rK}z@0HX1-#|MjeBK8 z>xRaoy|ZnqqgXx>aSnm4kK~mn?sss7e>&Nc7ZeBS#|vT|e@%0fwh%f0^@Q}vjv zxiWKs-wt#~=++i}r;1lPgS0pfz0*#1+wyY>Q&qYG8Qr;p-NH9cuO{A{Jwh4@&bOCw zc?mRPc2zF#rrK^a#66Z6me8eu=un=FMklr*5xZh^dve^7kfKF~p!2W1$$MweC}cgE zB!H<{_q!6LO&`%$PkC<#X=zdOA4s8>Fof(hNE`4t*&T`++o55(7avg8*RB_y5V77u z`fiAj8&gFmhD)bA0rF%S0}`M;gawL$i4W|n3!Z?0l{z2+>a~{%_AfpMYT_gIQZ&N7-wsn=~^83_(a+sPuSNb6F;7S>?6pW;-mU!ebr*c%4zqbe0aw;$j3G1^UCw_ zA+@Oh_m&V|w~PoZQ_=FUaO?1yGX=VMq-MMXs4%j9G(v#KDcethpx(Xih3RFU@9mAC z2mzh!LFo{pvWVM1Up?eNB*l!F6`!adNulpRxd$IovwcGx{#vyzJ(B;wh0e=wc# z4R&fkN+CW0o$g1k$T4c=dj}PR(!-vx1g*lx%xh{60bX-WmkIh^Aw||MTnb{an)AI< zb5=mj0f=$0AfOOfu7Q1Z!RLvmP((PQ`yUZWV2CzZl>#8gK2vqz`OK_ffyOlUs-c!G2a;W+9L7lNbvEU@aia0g*g1LWE?l|UC`t3voF_;s}U_|Y%5Y2;yl?)(0%0j$ua6*C;CB8)bOS&?+ zbO6!+4Nem<#5^FQ88I^98p8|;;^tB|xEdc~ZhEYtin15B>o+@lk85ZAa4vgUhaSsv zm$Ub@kHVYa-GhONI1UEuxhhe`b_H#Yef?r0!fl0zvPI7q5^V6lJ8cGXGqAG#nuwDk z8Pyx9(e!{Ea+*F!y-?X4+eXjSWW*I&sKUQB^#g6ZC@0qWwG){o#khSE8*RM%q6g;a zH%M#cD5IjDE^cOq4@aJM8vP7F+X6=Wza6vE02dPRlJ@_EEY^St>2^g(fc6A{cnm|l zW^if*5Y-6(h)4o;6HG(UZ^%fE;+D#$m4!VvGr5=CBuKG;PL@e} z_p})!M&ZPDD}8Zw;>OubURYzDKe;iE?I-*EKKBviB_w7`9Anps;UrBP&e9gp#BK5r zF%4#2G-{z%)ZX@uU$U%UXXDZzwV_RVEGQWvxuhDQkUbD@&G8CmH`_fUM}q|cVRyBX z%-5lpIGou~DM8zG;_k*-y7Tn>*;s2`X$I=jL(ft>S0m0=8`Dlhg>JyHQ=th`g@B8*!qej#BYEXhnPk!}}HHzz=b)0bGtvyuwLP>c@ zL%aXni;rnDNHA$pd3e(+9fRB}%ZzgyJwtIt1v!H})(=7PqBFuijp;<(B#oEgkg37sjWe)r?Y z_-!vKu+YbFXtOv$FQi+D^FMO>Ua>n#v-3(u;>Yb_GuE!^DO4VJG61j(j)jY!Z zpqplK<nFzK0l&(iO){gJ@Q4XS5p8QMc<88Cc#B~jg6RS zAzWP~@zk&u(PZOO;2O3WpLaTApQ+B7`AgKA z-n(Sm!|d$*NsfY)InD|rrGAoVm?6HzB0XJXIFVD z5?!EL0VGCV=SjC9`gs%N}UxVyu%+ib}uowuy+C0lgtp*O!jl%o^4 zdr|14&;BUuMTSRc&B&_~c!JmsP>|f{9Bz(lzg@9gz3+@VBOd#c$2_WpV9}_mU+2kt z8f+30zL%5ORKXt(@a{0#kTeJ;Dofb(l;XxNHkFtF39=PNx~2_6QQSUKB+x6xI-w zkoUxFPEVDEBxMVd;76_=KC>7e*Pla;J>5lEv{zjrv3-h`ovWlIq7&b7GX-2LUq%TK zZInm7hUiK&5S9kyGOXWDRPly6u@6S{%7mCpg}jlA4#vOV!7|$Z&y2#uQUMTWN3lS# zD-3-9zfss?r4>%J{}S;p>DuAa0YvpKN8#KE7*Q2?d#FJ+es-Tfa0>S$!(iHkOfpT{ z`dY#^hQBvybu!CnhRxht%AY2)ae39r7DrGy>6qlp;i+1Nh*();>CO-xXxk`mmP%RT}c4l27j@*{t_MS^U zhnMNhPQF`jwvqGx1*J#n`TRmC;pe-CuMOAh*%l(qALGT|FIdI6CpubUjX|+=PSqBW zW$`p#2+B2$HQK5}C#}QQ>pkkT{HVG2n|-jC>spZ@PP$Wz<2TftuO;40E^OaNRY5U8M$LUG$ zfH@)be`gllk8mT%j$?ry!%P6|s|#)fwUu@NQGNc8h$P_93z@%-5D8cdaCpW}Fy`(X zIo|t(mS}gZZX7O8{U|SUy75>ri<%y3&Y|O|TTS{dkJjyjpYta5jdFYv+jQOhM%8bz z+jBTQkgbpI8qxWNgFi(_F~)fKMV=!OKBsh$Hy^U;r99URD|ymlL1ttio76}!_PMV> zqu*5!JM0`fRf3+Qv=7pfvMT)`0p|Ug8)mf*aY?!(7fWPwV=cRIxB+yG`r|at;C@3( zJ-qE;iwKP1-@Q$H`lhEa1nDrW)GMv{WyZFz;WEqDw-D>Nn2gZv;zmC3n-q`tCAWHh z&jk%*p-hrhp=vVE-?w_|@%mcOZrO9nKi%6Upz9Cu!ROiSvV1<_bGy0*k@~16Bn?}A zhstYr>?jIZKA1U08m|XPVc-N7r~#%lu&*xo?Yabt1+Qg+OTmBF@;f#cE>&XKT#$AC z<{yGb!nwqb2i7rk&DKj&quz14z&AErV%9==)01VKrkXjr#1JIpWL7ac&Kxc-C#;yj zlyV>5LWgAbZ{0L>NO@RJE+s%|u+h%&eq|+cmsFpz6hC5Ty6%%Xi3bsr^oOLKrxX?s znA00i83fNLZX2IcXsJ>b`zJhuaao3ahJB44JK%Qwb;;$o9aV{8b}aap9TQ;Yi3AWE zNx|23mQFb0-$(dA5Mg$_(n1RWM0jT*N{J2XQWY3^$S|lR=2DRCJXg;8sj=u6M1*H9 z9^F~ojRa?<1C6*OyAX7Pm7R^T;b1e2P&fX`sPYmy!{}QpAguEO@HIrC*zlxPToU~& zX$7#P>B5L+U8%b&SXc!B;t?alb)BUPPPFS1@h_sfaOnV||LZI(FvKc&XF=ZRccTa( zCO<+aKfzr%(;JDDH)}}`ZED;alcw6a*(?jn6EOTh|GF2=LYOs8>Io1A0TaS?#Bkum z$}hvXLM-NA#DFkX0K`EU;x(NG7eM4!{39aF-&Z;d91-4G%9eHPG?{UD)>e{sTG`tf zkeaQLbjIqQR@HbC?l}=;FV4N`m&+#nMz}#F_@M5Uh==GcjwcC;8;3@*PG8uO;G6>YB}Q5uQVNLc$202x-oUEq&X!O zrZ1FFX!krfp0y_)v+X>E=z}*+2rJ!iKb~Hu?JqyZq$ibO!Hn?Y zio^dl!X0LW>pDvhfT;fYkBB7T7gsvVr4c|GWh)X&A%up5jSXR@w}cb?`E;PS(P>uD z$()Ol`0e{r^EsfO(+WLhDQ)@EDhwN>zS&dXlP>VF4DxUM{E%!c=^NIZtyHd6=kbOm z-Adi27XxYC%IXPS%4cTzZ?^~96b{Q6$6gr-QXVqW@;Dv!tA*z9YBnp6q)L4b9|ngZ zMfmZYNb+o{IZNBN4d1buIX)6`7qBFzdz)CJ@eP9b!xDZPe@Kzoh>AaD%S~Gm&@kS3<(kfCz;bVrDyWPV>WbU+u_Ws3k=it~rAcbZu z2-kHMJa{ehUJCxZmdmhQxE_?kI?KB&odqs9oQol6LHBSr7lMY(W*%D(Zp%|TQie}- z@5E0~P1iT}(s~XsTo96c5uI*Qr)OC41Zu#!;HT6%gJ1WG+*11l_L)Q&ciV07L~VGH zWg*YxB)M!*=(62_65#J~!6u)O*8c57GpDzb%f6m*{SR*G$y~p4J%9^$$93n@N-x}w z$Cq4w+wnmu%#NY|vg6+6xfEG}2>O8ng71I#U6l2eJ^)ck;*W?hJ0i_p_T4<#KXc@+ zz&Z;`X|l)}HE7~y+;~|QQ`KzJ>m&8M)?Y15V?;5}jJ)NJvL&&XDE0clj|&J%wiFkV zJRW*JOM6+z8m*W9O9Z))OyFZR!gAT5h}RI&%dMJS+3Bj}n(P|&+MO^~%}+z0V`;n7 zYWHJ#iP|8nLR?j~^j&Pqt0X;z+ z!*u!%J0d9dl1c4;Go-KKG$rwUYiYfc7SvdVUdtOuW@sURDmpf zy4XAnPW!O)iaEY0-!(%B(Qb(@RHNk)i?5+PDfA89OJJFc}lhQ60OEW>fOi~ z!I@$E7q^?@Z^chM9sTe*U$a|j%7Hy{D+gPl3g#x3ox^}E(&bSvEb&$y%eunj!R`#LE` zRjb;p2C?rnA7&-=(2X0i>fcfnt-H&VsldHDSVky6olm(FEv*-(X&0`^dqAh=OCsA_ zfE&ans!Ja4V0_QQQ99>6#OvqC;)M$L+IV)V`e`_Z!>_lJiEmH8k4m0r(be1U2avh7#sL0)Gj* zW9P4GyC_7vF<-y8Dcb7f>LyOcBXx1r0v;$2C%<8{d}IC)KcFhqdEU508j7?evd1E5 zLCo7!-NcWUw;#Y2SE}^EUT0slTkzGRi(X$uUZmG+Pu{tg#T?1tGs)73*h`WHe_YI+ zMy<%J3xwKVxZp4)SDhOqKLs;u>)X4r=qkmqirZ{pd7y;67!-MTKSYu|(K@G|(U!RL< zfN>Uzv7bYhdQ~ig?qds(lRkOwc-LIT4h$ zfbdbyX>kI*?-d#!XI1H~0oM8F7z5{GHuv(ZQfY3+2@q*X299jf4`BaPub*QWwnHam zKNPkiwAFVpvMLUGNc!T?#zd|>S}nk;qHn>){AD*zF3Ywmp~`7%VKk0)U(ydtU&67q z&D^+9pF6@&8=dI|<9uXQ-ktqm>hTGPYD#5&m%iu7%-F^lYiH7o&1R!cvhFxv_L}%0 zsfBmc=9x)nsQ51|D`ZB$cl@bKZG~LPbCsh*w#z>swz6ijG1k#V*AuriwC!EwG@rfh zqh#}zF!K%I*=EdbayhpO#^L2T*G96fv{J_XGA8uMO!61chG4yL{5<6-E6Be^YyQm= z<6$k5ki$8yrwt7+4I17Oy8Y5!Ly4D?KT_X0*}RqeQkXw%ryx6>GO$wkeOons`#eK5 z$jXznG_K2%#PgXm-Nf*Y4WYbX8+z@`g{II~(uE=6kyr}P<%YXGa)xe0#h?VeGvxtw zlE4AmmH04(*W!n{GRvN5T_1{*=GAP@^<#w+6(%`skvh$P#wj!U9gltuvxHW+{gDoElc)l$;uB<QogOJJS@7`CU2X3#p(` zE9VaGS@2AiwSCdf29C{_cfJB>7p4cK(wjjdBD|*9`VCNz1Fm?pn?)S0(tUI-M{~G> z3ewsniBmg^rkz5`S*N9Z-7Dn&(Kq6>j0UKtD(O?D#2yfgOC&{cReb(j*_vUz!gqiC z!!J&}{C)*+&nnX~E)N0mG*$@(F$G@KPykZFSeSz@!X}YTD;rWivVq0^33#!yTR~i@ zFPQ5_T2%7Bna~RQS}98ZcF{yOb@G{JYYAop)kwlz3;0Eg#VK_9grmYPHfBD1y{9kcs8r(j!w7mM5D2x{(>2I|Pd9P2 zI{o7PZAbMH>rZ^qiSql$7A=kAvmsh9W@5xf!dIg>8#zO%&*Hq+pQr~Yv(b#+Z!KW_ z#dYTG#g~@OYw^|WB(4yPM*p?7ANmV#Hk#d^oQEH6tZOq1K8Z<4&-EIQu;P><#Rslb zh0vmpGAyi1K%@;t<7gK>fWZo(^4d?D@Zm$^yUQ`?uOTtih3k=tFAieK|K@yP49Y_Q z5TiO#uDL?4^utHBqf5l!quL`A_^5_^_2!T#c8RFog>uc^7)k(-@#!Vvl^9KZNnkPl zeK&?9iV*@I%DGasZqZ_}#;g?byy1*+vOlRNrnNy`ufA6_=4i=KV{UOHATONvt*kbS x2#)UQ!y5pxXjmA$uu&1%R~P&*H4GH+D-jj)Wf*@EGX