PDFium VCL lähettää RFC 3161 -aikaleimapyynnöt libcurlin kautta ei-Windows-kohteilla, dynaamisesti sidottuna kahdeksaan symboliin, peilaten Windows-taustajärjestelmän muotoa, joka sitoutuu WinHTTP:hen. Kaksi asetusvalintaa päättää, onko kuljetus luotettava kuormituksessa, ja koko yksikkö varmistettiin koneella, joka ei voinut kääntää sitä kohdealustalleen
Aikaleimaus on se, mikä tekee allekirjoituksesta jotain, mikä selviää varmenteen vanhenemisesta, ja se on verkkotoiminto, joka istuu allekirjoitusoperaation sisällä. Tuo yhdistelmä tekee kuljetusvalinnasta merkityksellisen tavalla, jolla se yleensä ei ole: se ajaa taustasäikeessä, se puhuu palvelimelle, jota et hallitse, ja jumitus siellä pysäyttää allekirjoitusputken, ei sivun latausta
Miksi libcurl eikä FPC:n HTTP-asiakas?
Koska vaihtoehto raahaa TLS-pinon repositorioon ja tekee sinusta sitten sen versiotunnistuksen ylläpitäjän. Ilmeinen reitti Free Pascalilla on fphttpclient OpenSSL-socketkerroksella, ja se kaatuu yksityiskohtiin: FPC 3.2.2:n OpenSSL-sidonnat tunnistavat OpenSSL 3.x:n epäluotettavasti useimmissa nykyisissä jakeluissa, ja macOS lisää LibreSSL-erot päälle. Se, mikä alkaa pienenä HTTP-kutsuna, muuttuu toisen TLS-ABI:n jatkuvaksi ylläpidoksi
libcurl ratkaisee oman TLS-taustansa ja validoi ketjut alustan trust storea vasten, joten Pascal-puoli ei tarvitse niistä mitään. Sidontakerros on kahdeksan symbolia. Tuo määrä on argumentti: pienempi pinta koodisi ja liikkuvan riippuvuuden välillä tarkoittaa vähemmän paikkoja, joissa jakelun päivitys voi rikkoa sinut, ja se vastaa olemassa olevaa Windows-taustaa, joka sitoo kourallisen WinHTTP-sisääntulopisteitä samalla tavalla
uses
FPdfTsaFpc;
var
ReqDer, RespDer: TBytes;
begin
if not TsaHttpAvailable then
raise Exception.Create('no HTTP transport for timestamping');
Writeln('TSA transport: ', TsaHttpBackendName);
ReqDer := BuildTimeStampQuery(DocumentDigest);
if PostTimeStampQuery('https://tsa.example.org/tsr', ReqDer, RespDer) then
AttachTimeStampToken(RespDer)
else
raise Exception.Create('timestamp request failed');
end;
C-variaadisen funktion määrittely Pascalissa
curl_easy_setopt ja curl_easy_getinfo ovat variaadisia C-puolella, eikä Object Pascal pysty ilmaisemaan sitä. Toimiva lähestymistapa on määritellä useita kiinteitä prototyyppejä, yksi per argumenttiluokka, kaikki osoittaen samaan vientisymboliin: longin ottava muunnelma, osoittimen ottava muunnelma ja niin edelleen, valittuna kutsupaikalla sen mukaan, mitä oikeasti passitat
Tämä on turvallista tarkasta syystä, joka kannattaa ymmärtää kopioimisen sijaan. Kukin niistä argumenttityypeistä passataan kokonaislukurekisterissä voimassa olevan alustan kutsukonvention alla, mikä on täsmälleen se paikka, josta C-toteutuksen va_arg lukee sen. Temppu pitää siis kokonaisluvuille, osoittimille ja kahvoille, ja se ei pidä liukulukuargumenteille, jotka matkustavat eri rekistereissä. Älä lisää doublea ottavaa muunnelmaa olettaen, että kaava yleistyy
// Yksi vientisymboli, useita kiinteitä prototyyppejä. Jokainen muunnelma
// passaa argumenttinsa kokonaislukurekisterissä, ja juuri sieltä C-puoli
// lukee sen. Liukulukumuunnelma ei toimisi, eikä sitä saa lisätä
type
TCurlSetOptLong = function(Handle: Pointer; Option: Integer;
Value: NativeInt): Integer; cdecl;
TCurlSetOptPtr = function(Handle: Pointer; Option: Integer;
Value: Pointer): Integer; cdecl;
var
curl_easy_setopt_long: TCurlSetOptLong;
curl_easy_setopt_ptr: TCurlSetOptPtr;
Kaksi asetusta, jotka päättävät, valmistuuko pyyntö
Ensimmäinen on eksplisiittinen tyhjä Expect:-otsikko. libcurl kytkee HTTP 100-continue -kättelyn päälle pyyntörungoille, jotka ylittävät suunnilleen kilotavun, ja sertifikaattipyynnön sisältävä aikaleimakysely ylittää yleensä tuon kynnyksen. Osa TSA-palvelimista ei koskaan vastaa jatkoa, joten asiakas odottaa koko timeoutin ennen rungon lähettämistä, jonka palvelin olisi hyväksynyt heti. Tyhjän Expect:-otsikon lähettäminen tukahduttaa kättelyn, ja pyyntö menee läpi yhdellä kierroksella
Toinen on CURLOPT_NOSIGNAL, joka on asetettava. Ilman sitä libcurl toteuttaa nimentoratkaisun timeoutinsa SIGALRMilla, ja tuo mekanismi ei ole säieturvallinen. Allekirjoitus ajaa taustasäikeessä, joten oletuskäytös on piilevä kaatuma, joka ilmestyy samanaikaisuudessa eikä koskaan yksisäikeisessä testissä. Lipun asettaminen poistaa signaaliin perustuvan polun ja maksaa vain resolverin timeoutin tarkkuuden
Molemmat viat jakavat profiilin, joka tekee niistä kalliita löytää myöhemmin. Kumpikaan ei ilmesty funktionaalisessa testissä hyvin käyttäytyvää palvelinta vastaan yhdellä säikeellä. Molemmat ilmestyvät tuotannossa, yhtä tiettyä TSA:ta vastaan, kuormituksessa. Kun sidot verkkokirjaston, lue, mitä sen oletukset olettavat prosessistasi, ennen kuin oletat niiden täsmäävän
Miten varmistat koodin, jota kääntäjäsi ei koskaan näe?
Saamalla kääntäjän näkemään sen joka tapauksessa, hallitun kopion kautta. Kehityskoneella täällä ei ole Linux- eikä macOS-ristikkäiskääntäjää, joten aikaleimausyksikön ei-Windows-haarat eivät koskaan saavuta koodigeneraattoria tavallisessa käännöksessä. Koodi, jota ei koskaan käännetä, on koodia, joka mätänee hiljaa: uudelleennimetty jaettu tyyppi, muuttunut parametrilista, lisätty yksikköriippuvuus, eikä kukaan huomaa kuukausiin
Tekniikka on mekaaninen. Kopioi yksikkö väliaikaiseen hakemistoon, nimeä se uudelleen ja korvaa jokainen Windows-ehto, sekä {$IFDEF MSWINDOWS}-muoto että {$IF DEFINED(MSWINDOWS)-muoto, symbolilla, jota ei koskaan määritellä. Käännä sitten kopio. Kun kaikki 3 828 riviä kääntyvät, olet todistanut, että ei-Windows-polku käyttää yksiköitä, jotka ovat olemassa, kutsuu taustafunktioita täsmäävillä signatuureilla ja viittaa tyyppeihin, jotka ovat näkyvissä. Se ei ole näyttöä siitä, että kuljetus toimii, eikä mikään muu kuin kohdealusta anna sinulle sitä. Se on näyttöä siitä, että haara ei ole jo rikki, mikä on vikamuoto, joka oikeasti kertyy
Kumppanitapa on jättää libcurl-yksikkö itse vapaaksi alustavaheista, jotta se osallistuu tavalliseen Windows-käännökseen, vaikka mikään siellä ei viittaa siihen. Päivittäinen käännös pitää silloin vartioimassa sen syntaksia ja tyyppejään ilmaiseksi. Yksikkö, joka kääntyy vain alustalla, jota sinulla ei ole, on yksikkö ilman mitään kääntäjää tarkistamassa sitä, ja sama päättely pätee ristikkäiskääntäjätyön yli, jonka artikkeli Delphi- ja FPC-ristikkäiskääntäjien sudenkuopat kuvaa
Sen rajoittaminen, mikä palaa takaisin
Aikaleimavastaus on pieni DER-rakenne, eikä mikään kuljetuksessa valvo sitä. Palvelin, joka on murrettu, väärin määritelty tai yksinkertaisesti osoitettu väärään URL:aan, voi palauttaa mielivaltaisen virran, ja asiakas, joka lukee, kunnes yhteys sulkeutuu, kerryttää sitä ilomielin. Molemmat kuljetukset kattovat siksi vastauksen, mikä on oikea paikka rajalle: hylkääminen kuljetuksessa estää ylimitoitettua runkoa varautumasta koskaan, kun taas jäsennystason tarkistus laukeaa vasta, kun muisti on jo sitoutunut
Sama päättely pätee URL:aan. Tausta hyväksyy vain skeemat, joita se pystyy merkityksellisesti puhumaan, joten asetusvirhe epäonnistuu heti selkeällä viestillä sen sijaan, että se passitettaisiin libcurlille tulkittavaksi sillä tavalla, jolla sen protokollatuki sallii
Missä kuljetus istuu allekirjoitustarinassa
Aikaleimaus on pitkäaikaisen validoinnin tarinan ensimmäinen askel, ei koko tarina. Tunnus on kiinnitettävä allekirjoitukseen, validointimateriaali on kirjattava dokumentin security storeen, ja arkisto-aikaleimat on uusittava ennen kuin nykyinen heikkenee. Koko kaari on käsitelty artikkelissa pitkäaikaiset PDF-allekirjoitukset RFC 3161 -aikaleimoilla ja DSS:llä
Kuljetus on myös yksi pala laajempaa siirrettävyysasemaa: artikkelin natiivikirjaston lataaminen millä tahansa kohteella kuvailema natiivikirjaston lataaja käsittelee saman ongelmaluokan itse PDFium-binäärille. Molemmissa tapauksissa kaava on identtinen: sido pieni määrä symboleita dynaamisesti, raportoi tarkasti, mikä ei sidoutunut, äläkä koskaan anna puuttuvan riippuvuuden muuttua linkitysaikaiseksi viaksi, joka estää sovellusta käynnistymästä
Windowsin ja ei-Windowsin aikaleimataustat toimituvat molemmat PDFium Delphi -komponentin mukana, valittuna kohteen eikä konfiguraation perusteella, joten Lazarus-sovellus Linuxilla ja Delphi-sovellus Windowsilla tuottavat saman aikaleimatun allekirjoituksen eri putkistojen kautta