From 4bf1736a83d8e73eb917fdc7302c04622e80c361 Mon Sep 17 00:00:00 2001 From: Vladimir Stoilov <vladimir@safing.io> Date: Fri, 28 Jun 2024 13:20:18 +0300 Subject: [PATCH] [service] Add check for kext command size --- .../interception/windowskext2/handler.go | 9 + windows_kext/kextinterface/info.go | 206 +++++++++++++----- windows_kext/kextinterface/protocol_test.go | 9 + .../kextinterface/testdata/rust_info_test.bin | Bin 57546 -> 58213 bytes windows_kext/protocol/src/info.rs | 17 ++ .../protocol/testdata/go_command_test.bin | Bin 43436 -> 42792 bytes 6 files changed, 186 insertions(+), 55 deletions(-) diff --git a/service/firewall/interception/windowskext2/handler.go b/service/firewall/interception/windowskext2/handler.go index bb6348dd..166ebbc2 100644 --- a/service/firewall/interception/windowskext2/handler.go +++ b/service/firewall/interception/windowskext2/handler.go @@ -5,11 +5,13 @@ package windowskext import ( "context" + "errors" "fmt" "net" "time" "github.com/safing/portmaster/service/process" + "github.com/safing/portmaster/windows_kext/kextinterface" "github.com/tevino/abool" @@ -32,8 +34,15 @@ func (v *VersionInfo) String() string { func Handler(ctx context.Context, packets chan packet.Packet, bandwidthUpdate chan *packet.BandwidthUpdate) { for { packetInfo, err := RecvVerdictRequest() + + if errors.Is(err, kextinterface.ErrUnexpectedInfoSize) || errors.Is(err, kextinterface.ErrUnexpectedReadError) { + log.Criticalf("unexpected kext info data: %s", err) + continue // Depending on the info type this may not affect the functionality. Try to continue reading the next commands. + } + if err != nil { log.Warningf("failed to get packet from windows kext: %s", err) + // Probably IO error, nothing else we can do. return } diff --git a/windows_kext/kextinterface/info.go b/windows_kext/kextinterface/info.go index 763c3e8e..a2f5cd91 100644 --- a/windows_kext/kextinterface/info.go +++ b/windows_kext/kextinterface/info.go @@ -3,6 +3,7 @@ package kextinterface import ( "encoding/binary" "errors" + "fmt" "io" ) @@ -18,6 +19,7 @@ const ( var ( ErrUnknownInfoType = errors.New("unknown info type") + ErrUnexpectedInfoSize = errors.New("unexpected info size") ErrUnexpectedReadError = errors.New("unexpected read error") ) @@ -135,117 +137,215 @@ type Info struct { BandwidthStats *BandwidthStatsArray } -func RecvInfo(reader io.Reader) (*Info, error) { - var infoType byte - err := binary.Read(reader, binary.LittleEndian, &infoType) +type readHelper struct { + infoType byte + commandSize uint32 + + readSize int + + reader io.Reader +} + +func newReadHelper(reader io.Reader) (*readHelper, error) { + helper := &readHelper{reader: reader} + + err := binary.Read(reader, binary.LittleEndian, &helper.infoType) if err != nil { return nil, err } - // Read size of data - var size uint32 - err = binary.Read(reader, binary.LittleEndian, &size) + err = binary.Read(reader, binary.LittleEndian, &helper.commandSize) if err != nil { return nil, err } + return helper, nil +} + +func (r *readHelper) ReadData(data any) error { + err := binary.Read(r, binary.LittleEndian, data) + if err != nil { + return errors.Join(ErrUnexpectedReadError, err) + } + + if err := r.checkOverRead(); err != nil { + return err + } + + return nil +} + +// Passing size = 0 will read the rest of the command. +func (r *readHelper) ReadBytes(size uint32) ([]byte, error) { + if uint32(r.readSize) >= r.commandSize { + return nil, errors.Join(fmt.Errorf("cannot read more bytes than the command size: %d >= %d", r.readSize, r.commandSize), ErrUnexpectedReadError) + } + + if size == 0 { + size = r.commandSize - uint32(r.readSize) + } + + if r.commandSize < uint32(r.readSize)+size { + return nil, ErrUnexpectedInfoSize + } + + bytes := make([]byte, size) + err := binary.Read(r, binary.LittleEndian, bytes) + if err != nil { + return nil, errors.Join(ErrUnexpectedReadError, err) + } + + return bytes, nil +} + +func (r *readHelper) ReadUntilTheEnd() { + _, _ = r.ReadBytes(0) +} + +func (r *readHelper) checkOverRead() error { + if uint32(r.readSize) > r.commandSize { + return ErrUnexpectedInfoSize + } + + return nil +} + +func (r *readHelper) Read(p []byte) (n int, err error) { + n, err = r.reader.Read(p) + r.readSize += n + return +} + +func RecvInfo(reader io.Reader) (*Info, error) { + helper, err := newReadHelper(reader) + if err != nil { + return nil, err + } + + // Make sure the whole command is read before return. + defer helper.ReadUntilTheEnd() + // Read data - switch infoType { + switch helper.infoType { case InfoConnectionIpv4: { + parseError := fmt.Errorf("failed to parse InfoConnectionIpv4") + newInfo := ConnectionV4{} var fixedSizeValues connectionV4Internal - err = binary.Read(reader, binary.LittleEndian, &fixedSizeValues) + // Read fixed size values. + err = helper.ReadData(&fixedSizeValues) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err, fmt.Errorf("fixed")) } - // Read size of payload - var size uint32 - err = binary.Read(reader, binary.LittleEndian, &size) + newInfo.connectionV4Internal = fixedSizeValues + // Read size of payload. + var payloadSize uint32 + err = helper.ReadData(&payloadSize) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err, fmt.Errorf("payloadsize")) } - newInfo := ConnectionV4{connectionV4Internal: fixedSizeValues, Payload: make([]byte, size)} - err = binary.Read(reader, binary.LittleEndian, &newInfo.Payload) - if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + + // Check if there is payload. + if payloadSize > 0 { + // Read payload. + newInfo.Payload, err = helper.ReadBytes(payloadSize) + if err != nil { + return nil, errors.Join(parseError, err, fmt.Errorf("payload")) + } } return &Info{ConnectionV4: &newInfo}, nil } case InfoConnectionIpv6: { - var fixedSizeValues connectionV6Internal - err = binary.Read(reader, binary.LittleEndian, &fixedSizeValues) + parseError := fmt.Errorf("failed to parse InfoConnectionIpv6") + newInfo := ConnectionV6{} + + // Read fixed size values. + err = helper.ReadData(&newInfo.connectionV6Internal) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } - // Read size of payload - var size uint32 - err = binary.Read(reader, binary.LittleEndian, &size) + + // Read size of payload. + var payloadSize uint32 + err = helper.ReadData(&payloadSize) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } - newInfo := ConnectionV6{connectionV6Internal: fixedSizeValues, Payload: make([]byte, size)} - err = binary.Read(reader, binary.LittleEndian, &newInfo.Payload) - if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + + // Check if there is payload. + if payloadSize > 0 { + // Read payload. + newInfo.Payload, err = helper.ReadBytes(payloadSize) + if err != nil { + return nil, errors.Join(parseError, err) + } } + return &Info{ConnectionV6: &newInfo}, nil } case InfoConnectionEndEventV4: { + parseError := fmt.Errorf("failed to parse InfoConnectionEndEventV4") var connectionEnd ConnectionEndV4 - err = binary.Read(reader, binary.LittleEndian, &connectionEnd) + + // Read fixed size values. + err = helper.ReadData(&connectionEnd) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } return &Info{ConnectionEndV4: &connectionEnd}, nil } case InfoConnectionEndEventV6: { + parseError := fmt.Errorf("failed to parse InfoConnectionEndEventV6") var connectionEnd ConnectionEndV6 - err = binary.Read(reader, binary.LittleEndian, &connectionEnd) + + // Read fixed size values. + err = helper.ReadData(&connectionEnd) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } return &Info{ConnectionEndV6: &connectionEnd}, nil } case InfoLogLine: { + parseError := fmt.Errorf("failed to parse InfoLogLine") logLine := LogLine{} // Read severity - err = binary.Read(reader, binary.LittleEndian, &logLine.Severity) + err = helper.ReadData(&logLine.Severity) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } // Read string - line := make([]byte, size-1) // -1 for the severity enum. - err = binary.Read(reader, binary.LittleEndian, &line) + bytes, err := helper.ReadBytes(0) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } - logLine.Line = string(line) + logLine.Line = string(bytes) return &Info{LogLine: &logLine}, nil } case InfoBandwidthStatsV4: { + parseError := fmt.Errorf("failed to parse InfoBandwidthStatsV4") // Read Protocol var protocol uint8 - err = binary.Read(reader, binary.LittleEndian, &protocol) + err = helper.ReadData(&protocol) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } // Read size of array var size uint32 - err = binary.Read(reader, binary.LittleEndian, &size) + err = helper.ReadData(&size) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } // Read array statsArray := make([]BandwidthValueV4, size) for i := range int(size) { - err = binary.Read(reader, binary.LittleEndian, &statsArray[i]) + err = helper.ReadData(&statsArray[i]) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } } @@ -253,24 +353,25 @@ func RecvInfo(reader io.Reader) (*Info, error) { } case InfoBandwidthStatsV6: { + parseError := fmt.Errorf("failed to parse InfoBandwidthStatsV6") // Read Protocol var protocol uint8 - err = binary.Read(reader, binary.LittleEndian, &protocol) + err = helper.ReadData(&protocol) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } // Read size of array var size uint32 - err = binary.Read(reader, binary.LittleEndian, &size) + err = helper.ReadData(&size) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } // Read array statsArray := make([]BandwidthValueV6, size) for i := range int(size) { - err = binary.Read(reader, binary.LittleEndian, &statsArray[i]) + err = helper.ReadData(&statsArray[i]) if err != nil { - return nil, errors.Join(ErrUnexpectedReadError, err) + return nil, errors.Join(parseError, err) } } @@ -278,10 +379,5 @@ func RecvInfo(reader io.Reader) (*Info, error) { } } - // Command not recognized, read until the end of command and return. - // During normal operation this should not happen. - unknownData := make([]byte, size) - _, _ = reader.Read(unknownData) - return nil, ErrUnknownInfoType } diff --git a/windows_kext/kextinterface/protocol_test.go b/windows_kext/kextinterface/protocol_test.go index cf047442..c09cf286 100644 --- a/windows_kext/kextinterface/protocol_test.go +++ b/windows_kext/kextinterface/protocol_test.go @@ -18,8 +18,17 @@ func TestRustInfoFile(t *testing.T) { defer func() { _ = file.Close() }() + first := true for { info, err := RecvInfo(file) + // First info should be with invalid size. + if first { + if !errors.Is(err, ErrUnexpectedInfoSize) { + t.Errorf("unexpected error: %s\n", err) + } + first = false + continue + } if err != nil { if errors.Is(err, ErrUnexpectedReadError) { t.Errorf("unexpected error: %s\n", err) diff --git a/windows_kext/kextinterface/testdata/rust_info_test.bin b/windows_kext/kextinterface/testdata/rust_info_test.bin index 3f9049a9d776e2eedec281a37386a26f05a396e1..3b8588c7f89039b308ff0da53345ebd70d20134a 100644 GIT binary patch literal 58213 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 IPwkug4@6CXZ2$lO literal 57546 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 diff --git a/windows_kext/protocol/src/info.rs b/windows_kext/protocol/src/info.rs index b8eb0c79..fc50d589 100644 --- a/windows_kext/protocol/src/info.rs +++ b/windows_kext/protocol/src/info.rs @@ -441,6 +441,22 @@ fn generate_test_info_file() -> Result<(), std::io::Error> { for _ in 0..selected.capacity() { selected.push(enums.choose(&mut rng).unwrap().clone()); } + // Write wrong size data. + let mut info = connection_info_v6( + 1, + 2, + 3, + 4, + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], + [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], + 5, + 6, + 7, + &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + ); + info.assert_size(); + info.0[0] = InfoType::ConnectionIpv4 as u8; + file.write_all(&info.0)?; for value in selected { file.write_all(&match value { @@ -548,5 +564,6 @@ fn generate_test_info_file() -> Result<(), std::io::Error> { } })?; } + return Ok(()); } diff --git a/windows_kext/protocol/testdata/go_command_test.bin b/windows_kext/protocol/testdata/go_command_test.bin index 586c70adf31fba822a9baddee108d6f8a36b6763..d518dbf12a3ac354dd6381a8f4a530e549449206 100644 GIT binary patch literal 42792 zcmcJY+k)f94Mb^MWSD#M|390;_@PRL(fUYPuOCcOjXN3*pqrfEe);9s-yY}nm;0}O z`};rs`LBQf=hvei{P(xxasBek@%z7D@82NEZ5Ur&cRW5Js$ah-{?6Di^SBQPd784X zf`gFDzXwnbeV^oY+z+EIDX1PH*~0#K-1p<YATTuq#n;T#JvN_Q&iXqF%XL1E>(LJ{ zxrcr*7*xSU?Qs>wBYfz%!eMG0<l{3ai+i@33ZdNMydPIE4H=`hKSBlPaTfW<m9vZL z{176@W{c0+J{lBK#GXTg7NiS@;UEC<T@ljG2LzJSl_S_=DT<c}E~;1s#WU9*NclD4 z$GzeY#QC=P)c?=xybn6Bn)^pTI3JJW##SyxN!=Fn%+Tn^l+!aJdNT=C9wARl#QmVY zdtHymHCe}b+$G_4^=e?rDEgB|s7Fk^<A^YNDx6a!RNo^p)T>3luj>z&pVA<t!DDO( z@wB>ZPNxy%i|Qz>Wp%ah&@d(?k1zH#SobFmjAfMb>}Y*lhjJV25>(4Y_g21FkLs4X zZ-H6ow}d$41}_Id!x#MadnwFR>FlI>J5{k6duE`;mo!>W4(eqEcOPd*zt2<^QOqLr zRFIj9J|qT*Q6*5r6|)p9T!eCDorNMNqd%40K<SHHI;kj&D!){inNfLbyADOAm2H83 zt;P{hdoGuBS#b1FsIT_UzAuVv4NPXKvLS0dO^TH;0z^1fH5}U7yp$`3N4J(%(7jLU zuqzteR|`({+Hj>XisoL@HRAiC(kaKboo0FR9nM$oSPxpH{7pJn4Rkj5THhc2ERy%a z2>qj=sA>=1w~`<<)P{bnUMPs7t6_FaqvTwQgjCvrOmc-!iV==m0<5VTywqSwg3bjK z$`;jm#+)by>edJ#XW61PuqrCm(VbHJcZggK0wsc)bGT5T!B-3Zy}F-za;aCQzH=-& zp}M#E@0jpX*>7W(at+h2uLuPd{F0&cm>Z*OgVX2|qFlRrx|I+Wqfk6JOdTI{(95!_ z#Uh^TO<P#J-dRvgYFXfQ+X))v84jbXhN{!zt8TwZLaj4jsbcbtifnCIX}d}PWyO`t z0cDUz08=jkezL;aO-%Pt5Al6&4PSh#hgeU-S)oukQ+&zA2QxV>o>Rs&d?ojYkV}1H zdWt6<TuOwRTam{+M*xP%+K@YsG<Mu*xNl|ixWT6dj+zn=gwn%d;RjFa@E^n{Ypx7U zGXf*fCP(4udnk%ghOe{}KwO9V=*87rw~&lkZhdR6;huj)D5X=%sZ^8v-zmx4641z? ze2)&LaUJisYGu5qADv6$QyU<r15`k}37r69MZF;{;5eArSp<f=<%Y^tp`2NVRKCt9 zP_K~^HC|m-WA90;$oGh!Rk33$tyKV;Lawh(9mQujKIY9O`A-UGp})JLI@I=o_QH(e z7>6jkKtT#>^pGL&P@AUB6Q(&s4|Hal?V??`S=xoHlc_@4S|tyhT~Tv3g(X6^q@>Hv zMWG;8nXih}LlL1KLF5HBb5FGZPuCzBIIOs-TZ`YqDwm%~B<90?&v4a)=lYP<W}$e6 zSf)>DvCh>~SJSi?c<lcQX^@MnJyXeQ$&+#6QWXF(KPb0r10)PttXN;!50Ky1xE8^1 zY9K9%<v?`aL)q1AZriPB+APz=MM(hVB2T2u?ly#j!xSwjM)ew7+^<#l>^O=8b*w*Y z1+=xPPK!CH04ja(qXj#Saz!h~qlz0m<072wvlEIoE1sxtt7cx2qf)7D^dY1r)sn7K zt~r=^PPr%+pyhOMBr*&2l+_BCDhgTnSkGoiXT-C#ivY!vofitT#VP6PmM$0tmgzjs z&#aO)cX6`_cf6s(!cJz7dSS0Ov4R=dL%BO;W;Rs<vXl<7Osq*Xg{5vk_0B>Ac`GOt z8=*6-CVcc4uT)k#wS9*MSq|#pqH-Rc-(-~xi7BL(&Yxo7<HPx%@vUcplEv9pfDEoO zW<4nW>sF;pt4V2CuTi#df!FP9a!tD>kNNEFQYu<&=4-=c8ok&rsjD=lUeij<{uv4c zhZQj`>-|IvZ$FAaJj-X>6i+T&8i(C4qpL}!j_4de)&GjQ=D(*@WMC#=IQ+x3p6Ki- zBz4<-;M%M3n75X6n#2*I3r;G7F43Gu8a8cBGq6vgKngluL`TALdFHDHU)6FCfHYd) zp=*d?HgKDG-Dg{*e^LTmk90yr@zWNeWoINV*egt>nNVA*#Ztu(Zc_l-j5RW-I(4Si zR&7LmACtRR72aUfnh2Rv|3}*naIlstOO3Y$JNAa<Al=f@XHXpz`1?2^b65w*dxKL) zEqV9p^H!-A5~!5--L^dz5-J(g5NiW`h6qB<R$~|oLKV}vOzfaW4Gq~+JMqyb=e*hf zO1w5m{hVzh8X=%S6kV2UG@`xQH7)5flD*VWM_@Y3=GFR`X+4FgD7guT_YPMvqQjHW zEO40B6*8&|parg!9HD-np3?%`PTk<sqN`^nu5F~%)Nf7GY~m#msFDaZxY&ZIi?w!1 zh=u3QO(2pW>%N%Eq}_uu(S~fM9wA8h;MWn8VlCNe_oNimH!_-^AKQ!L)j;3#svz*| z4YQ3)gTZRNLv1OYZ#@^g5YejRRmZB9wI#wR3KZ(Lrz?0^QJ>=^&ENztoHEmxVh+Sv zG4ag?Azc(^^Bih$8RDx|s{=1;L3HTdTZv!WCXiCfTB6R@xpHmoe&X%oj`{l_p6Sib z4sV7+al*57o~Z=NW+bnj)P`w17NSW&HsP>d6x$i8=c!d`mP?qdhqWyYIoq962uSDP z^pocJ5=5($U|BU#DiFJs*RC*!OG=?0JbQ+8#$(0LhZu^Ys{3iiKw&0|&MP@|c*N!i z;nXmA3#MeE>`B7~xeT&eH&F7zn|7_AM*aR-rig4v6=02q<fmZ=hthEn-MmYk$E9+U z5VwxkmqAJk)PWkzdY&(Q<rh}`OXeEw9#98xSV!pWJenUZq~Ra2Mp47b9NKVNu1=XW zC#6k@!0>Ma7_^s4DmxwbO&$cf<-xGjr}Yx=&{z?WtB4`tF~z|LxOmGEBDZ8esQEwS zd*0_%8lYFAClQ4VwWmQaIP630k%4JrH}4=_tJzn1{=Ng9NBK$z*)tW)x=YbiIaDT5 z27?@yjScTB)q#84G%^g2%*y?nt$Pm)=uXA(VznDulTSjiBeZAu|EB(c2;nfRz*nIR znhzTIZMsTu4Xy3j-S5!BYmZprp&W+JZ>6uwxB_qC@h=fL{Sb;t%|GjMOlOK+$%|PE zVd5cwsfl+Ms8p-SfO5fMmQsbdPI*AEPNkoAb5-R+H1p+v)g1^M;G!jq=h+UIUv><F zEK%RL;?$ghxSOSExKUM^9wZiCUDJ}fUV*6%uu7=Zd#`)h<xvlzvGMaCBCE6`+@et7 z5;{4WjL|xcxZ#I--3;ow7|OD9Z#}mrAPu|>hT$zsaj4u=!8@~`)vTsYorY10sKf>e zr2Io0YnrL6$%<PjkWvX>x8Yb2UuJt0glgse_yaj8sdK5UpJK7@Umz{}oxX55->qmo zG4-%tL)vhdS;QwlN(hBRBk@&e@57n?zC*q(^PdG!D$1m@Q-;*j`ee%Ptww|{B4LF^ zg9dQ*VO4hV&-r(|>FLX9P`PlJJu6i3Nd!{NH?GjW;;*0ks&yH_9yITBt}_cNUf<7Z z)+=T*2R`hM_w&rTul7e+V*-}18H!4%zPN$-;%FG?CSlOxnXV`*?td_q)JyMhR&?kJ zN9QCIytt4xvrS&VVn-=B-14&BlnTw3?PpTp38d`FBtN4S-kz7eW@oBj+L;eVgqPF$ z!i=x@kB^4V3rWQd)WV$FT_>>%)v8(`ZcFAX2nvTaqxb-eHZaT+Zkj8LGnB7zffP=< zbiUX667Gk4yEV4GwtkkQN|X*{7ARjg#c|W%zFJ3b{F6RpM`y@q&R|lZ_ELnls`*PT zkPW>h)p=0_lwyG5v@pI3vF4Lq{6)U`fXx2<-r6C}1tjBZi4aqwele@<YVDu0!@S)6 zv@gj8GM!?eZs{tdW*l|RDcif_(^RK%1MO@{=F=60DC`Z<6#@ulCf4-DRjP3m5B_1j zi{ziayNWr48`14%#+yAKZ8fd;_TE<2isukRhFZ;neYgwafy0!CM+JjhwlrC#e%|rG zhADS}V&UFrp$}a^O6GJGeE3BsNJ?kUa00BFy+u>qm|kwiYhX?+rI9bk6<FOa^7T8E z84jDa?R?Ixh?w_|o%@q6A-2JK;ikphWd3x?D4yw}v?2pk6+W}r$}B$X7E-rrA%I+d zpP^GH<>>@i*e=p|)1o^4OeXDDiOH|oX_7SZ%?a_ZbJOz34^~rc?nb^PWI4t2J&8Fb z{b5PCPZ(QFybx$l99rDSeT5e$?Hp?E8Stq>n?q`liJtfnOsSpdmmAINL=YYhvwXhF zj9gRZLjhET&4GHz$D*Q5UsXH$S1N$`sG1797CXE!7AThVjkDgp*2EHuCxJG0i&Ln= zYd(iwptxZSX+*>c|AW9kcbKXoh>!;4(j7&w$UCo#P!G1=d<6wDfV`fvr&%3t_)W7% zix1^J5mBx7oPe^@>1mx%g+K=2F!i+8fgSVYD~RrG03}2{=^-g`ZfZ|2sZi7QgfY7M z<?AyQLG5Q{q9#Z^7>}cgBmd?@OrGG|m#ep~uTYg9cdPDOq|gCO8V7(`<BO-dg|x8; z%-dEFXC!}mPo(&&Tcs)xYsW8ZEgV|ePtU^rhn43o$)`CtA80o^Eh5SnAy;aN(pief zm5LxI1(nBN)pwZpxh!Jq2$F!qR$biF$Bg(&`yXLP8XgQ^;*X7~4HY_G;_(kz<@qU| z6G|s#1OtXO;?loEUC&$J_$@G8RlGJBUEHYwl|Wf$3IugSd4=f8!?fO0<A-Cbh*^iu z3NeinI@+9w!;7>9(iY6k)fh=>HH#`xW6sP0++YRj({_Iwpw~Y29gVF;4{=M3`O969 zHf~!$d^!!%D<gzlEmjDZJ(;Q2tuVani;usq(pe5^dXV9;Td!Bf6>djoW8f`+sN43k zd8x#dYp~vQsV7j(eRQ2~rv2oAx%S-ndl#7Jd9|$+HNE6|1a%iqRS0Y+sv|>mrdg;L zv(5aj1Pg<9*7M~xQ-)$2K>2RF-9EIAb9}lQWaxj&GuyII9(_EqG_-<T2lbeUy#KT+ zbOmAj1|XEhwwVOMcS^v0qb-BC(HYuTszX~brO4nQS8PZ<SPq**U~Q|IQ(wiy;?-k( zRYP#frKMdjxKK{vxaF1Mh5N4=K%|y+9T>zxwX|fEkcUZ1asOUr3`7fuMHF9O;;<;Q zeFhO~3yN<?<h1Bm8omlitIFGlH!XyYRQaZ?_T$@3po{`nbV|@?)uEP~zm-MHr>DrK z#)?`~7&fBH`fSyj4q1WEQHoMnN5TkwHIiz<diE9eL4&+z1n)|=0KHyjVhTOAP_8so zX4KYBujmEUHi!w+SrlYTeJc6TN(gW$wAvv-ycD<2!>sQ+)YBmW%4sRTZuQ1HM5It! zun_Q$P;Eu`#LEvzcvMlul{aL~gxc}&QBO!oWuS>E$)ItPX4gICD1PdM*iE$WVL@Zv z^24%eE~QYJS}xmtsGhXS=%~jJSm+5$bME>2Y%EMr5*0Cf=V-BK-9fYxQ4_nQgs^X% z9n|~IxWz>sO}h^HD*NoKSp0eUwk@xH7fq+>+{ZX5YZwfF<pDc{RzfK#U#nHyUEB59 zMSBv}{6qG&(zZ4rWP`2n5z1T#*v;qH-xyy#EV`oLEeKq!n=={@TJXS7)2D9LLun;a z-ODS!`2uWX%Q#xJA)$hzj+<wP!wqmUC7*xeBO*azMXbzscF>;B3EEsGY~5mah=}l+ zmW!|%R53+kU;sfTp1xQU;31BmLYykf43lM4dulq2A*OPCsSL=1qGzF?hMey-%o+#@ z(Q!}Be2CjBfdFvWlsv}$yswwfOM`W*qpy(O*Sp9%z+DJvSSc{7@dkVPLkgJlIdKun zYNQYk9F|R6B>@nsXI1goa|vR79U-aRAffs8O|z`+EeKtZb<2Ge*X2dQ^Tk*Ea9FnN zzm2#?`g8PA+(V>r7>x%V2q1dvg4U?~y~{bnZv1iShq;jXDdZiW@ao!2Mb)2bvuG>5 Tp79?V4C>}vZI{q>d*A;9LH~IY literal 43436 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 D1$2qa