[{"data":1,"prerenderedAt":18292},["ShallowReactive",2],{"post-windows-2fa-gating-network-logons":3,"post-list":18041},{"id":4,"title":5,"alsoIn":6,"body":8,"category":18008,"date":18009,"description":18010,"draft":18011,"extension":18012,"image":18013,"links":18014,"meta":18023,"navigation":837,"order":18024,"path":18028,"readingTime":764,"seo":18029,"series":18030,"stem":18031,"strength":18032,"tags":18033,"unlisted":18011,"updated":18039,"__hash__":18040},"posts\u002Fposts\u002Fwindows-2fa-gating-network-logons.md","Build Your Own 2FA for the Windows Logon, Part 3: Gating Network Logons",[7],"tools",{"type":9,"value":10,"toc":17954},"minimark",[11,16,26,29,32,45,64,88,91,127,131,163,167,170,173,176,179,183,186,210,225,232,235,246,249,254,257,267,270,351,369,373,376,506,513,516,522,528,541,545,551,555,1200,1203,1246,1274,1278,1462,1480,1483,1487,2055,2065,2083,2100,2104,2406,2413,2429,2435,2439,2636,2653,2656,2660,3165,3168,3171,3203,3206,3210,3268,3283,3302,3362,3369,3384,3388,3742,3749,3763,3769,3773,4818,4832,4853,4870,4882,4886,5027,5044,5047,5051,5667,5670,5679,5694,5698,6322,6325,6375,6381,6392,6398,6404,6410,6414,6422,6426,6734,6748,6813,6820,6824,6999,7025,7028,7032,7551,7557,7567,7573,7577,8520,8552,8562,8578,8598,8607,8611,9866,9875,9878,9898,9907,9916,9919,9923,9937,9947,9964,9991,9995,10000,10003,10006,10020,10023,10027,11831,11845,11858,11862,12871,12877,12888,12891,13469,13481,13485,13488,13495,13498,13567,13573,13576,13593,13601,13604,13634,13647,13654,13669,13673,13678,13730,13747,13754,13757,13822,13827,13834,13838,13845,13897,13906,13910,13916,13923,14486,14489,14525,14561,14571,14580,14602,14606,14609,14623,16925,16928,17035,17038,17042,17048,17051,17057,17071,17075,17086,17097,17101,17113,17749,17769,17775,17779,17782,17788,17809,17813,17816,17895,17898,17902,17913,17916,17943,17950],[12,13,15],"h2",{"id":14},"foreword","Foreword",[17,18,19,20,25],"p",{},"This is the third part, and it's not the one I announced at the end of ",[21,22,24],"a",{"href":23},"\u002Fp\u002Fwindows-2fa-inside-lsa\u002F","Part 2",".",[17,27,28],{},"Back there I wrote that a Part 3 might take on the SSP\u002FAP, done right: signed, PPL-compatible, working with Credential Guard instead of poking at it. That one is still on the list. This article exists because of a complaint I kept getting about Part 2, and one of the people complaining was me.",[17,30,31],{},"The sub-authentication filter from Part 2 refuses every network logon for an enrolled account. That's correct, and it's what I asked it to do. It also means an enrolled account can't map a share anymore, can't log on over WinRM, and can't log on over RDP with NLA. Safe, yes, but you can't use the account over the network for anything.",[17,33,34,35,39,40,44],{},"The question for this part: can an account have 2FA ",[36,37,38],"em",{},"and"," reach ",[41,42,43],"code",{},"\\\\host\\share","? A code per connection is impossible, the next chapter explains why. A gate works well enough to be worth building.",[17,46,47,48,52,53,59,60,63],{},"I called the tool ",[49,50,51],"strong",{},"Ristretto",". A ristretto is a short shot, the same coffee with less water, and the tool is the same second factor with a shorter window. It lives in ",[21,54,58],{"href":55,"rel":56},"https:\u002F\u002Fgithub.com\u002FNobrac\u002FRistretto",[57],"nofollow","its own repository"," next to Doppio. It needs Doppio installed and enrolled, it reads Doppio's RID index read-only, and it links Doppio's ",[41,61,62],{},"verify.cpp"," unmodified.",[65,66,67,73],"blockquote",{},[17,68,69],{},[70,71,72],"span",{},"!WARNING",[17,74,75,76,79,80,83,84,87],{},"Same ground rules as Part 2. Throwaway VM, a snapshot before every reboot, ",[41,77,78],{},"RunAsPPL"," off, the BitLocker recovery key within reach, and a practised way to edit the registry offline from WinRE. The filter in this article takes over the ",[41,81,82],{},"Auth0"," slot from Doppio's ",[41,85,86],{},"TacSubAuth",", so you swap one unsigned DLL in lsass for another.",[17,89,90],{},"As in both earlier parts, all the code is in the article. It compiles for x64, and I'll be explicit about what I have run and what I haven't.",[65,92,93,98,104],{},[17,94,95],{},[70,96,97],{},"!NOTE",[17,99,100,103],{},[49,101,102],{},"Changelog, October 2026."," I changed this article twice after it first went up. The details are next to the code.",[105,106,107,118],"ul",{},[108,109,110,113,114,117],"li",{},[49,111,112],{},"The second review of Part 2."," Ristretto follows it now. The veto is ",[41,115,116],{},"STATUS_INVALID_WORKSTATION",", the filter sets all its out-parameters, leaves pass-through logons alone, and calls Doppio's enrollment read instead of a copy. The repo also has a test for the filter.",[108,119,120,123,124,126],{},[49,121,122],{},"A review of this part."," A second run of the installer lost the record of the filter it replaced, so uninstalling never brought ",[41,125,86],{}," back. That's fixed, and the installer checks LSA protection the way Doppio's does. Closing a gate stops new logons, not sessions that are already up, and the tool says so now. The gate tool and the text got a few smaller fixes.",[12,128,130],{"id":129},"prerequisites","Prerequisites",[105,132,133,136,146,149,152,157,160],{},[108,134,135],{},"Part 2, installed and working. Or at least Doppio from the repo, with the credential provider installed and an account enrolled.",[108,137,138,141,142,145],{},[41,139,140],{},"enroll \u002Freindex"," run at least once, so the RID index under ",[41,143,144],{},"HKLM\\SOFTWARE\\TheAdminCafe\\2FA\\Rids"," exists. Ristretto reads that index and won't install without it.",[108,147,148],{},"A Windows 10\u002F11 or Server VM that you can delete, with a snapshot. Before every reboot, not once at the beginning.",[108,150,151],{},"Visual Studio 2026 or 2022 (Community is enough) or the Build Tools, with \"Desktop development with C++\".",[108,153,154,156],{},[41,155,78],{}," off on the test VM, same as in Part 2.",[108,158,159],{},"Git, because Doppio comes in as a submodule.",[108,161,162],{},"A kernel debugger, if you can set one up. It's the only way to see what the filter does inside lsass.",[12,164,166],{"id":165},"why-there-is-no-second-factor-per-smb-connection","Why there is no second factor per SMB connection",[17,168,169],{},"You'd want the server to ask for a code when a connection comes in, the way the tile asks at the console. NTLM has no place for that question.",[17,171,172],{},"NTLMv2 is challenge\u002Fresponse. The server sends a challenge, the client computes a response from the password hash, and the server checks it against the SAM. The password never crosses the wire, and neither does anything else the user typed. There is no round trip in that handshake where the server can say \"and now a six-digit code\", and no client that would know what to do with it. Microsoft finished the protocol long before anybody wanted a second factor in it.",[17,174,175],{},"The sub-authentication filter sits further away from a UI. By the time MSV1_0 calls it, the password is already checked and the only thing left is yes or no. It gets a SAM record and a logon level. It can't prompt or wait, and anything slow it does on the logon path inside lsass.",[17,177,178],{},"The second factor has to happen somewhere else and at some other time, and the network logon has to look up the result later. In Ristretto that result is a timestamp in the registry, and I call it the gate.",[12,180,182],{"id":181},"the-gate-pattern","The gate pattern",[17,184,185],{},"You prove the second factor once, out of band, and that buys a time window in which the filter lets network logons for that account through. Duo calls the same idea Remembered Devices. Ristretto does it without a cloud:",[187,188,189,197,204],"ol",{},[108,190,191,194,195,25],{},[41,192,193],{},"ristretto.exe alice"," asks for a one-time code and checks it through Doppio's ",[41,196,62],{},[108,198,199,200,203],{},"On success it writes a unix timestamp to ",[41,201,202],{},"HKLM\\SOFTWARE\\TheAdminCafe\\Ristretto\\Gate\\\u003Crid>",". That key has the same SYSTEM-and-Administrators ACL that Doppio uses for its store.",[108,205,206,207,209],{},"On every network logon for an enrolled account, the filter reads that one value. Fresh enough: no objection. Stale or missing: veto, the same answer ",[41,208,86],{}," gives.",[17,211,212,213,216,217,220,221,224],{},"The window is 300 seconds by default, and the filter clamps it to 30 to 3600 seconds. ",[41,214,215],{},"ristretto \u002Fclose alice"," shuts it right away, ",[41,218,219],{},"\u002Fcloseall"," drops every open gate, and ",[41,222,223],{},"\u002Fmode off"," is a kill switch that takes effect within about five seconds, without a reboot.",[17,226,227],{},[228,229],"img",{"alt":230,"src":231},"Two timelines for one account. At t0 ristretto.exe accepts a one-time code and writes the current time to Gate\u003Crid>. In the first lane the gate stays open until t0 plus WindowSec, 300 seconds by default: an SMB logon before t0 is refused, one inside the window gets no objection, one after the window is refused. In the second lane \u002Fclose deletes the value early and the next logon is refused. The gate is per account, not per client. A clock set back before t0 counts as closed, Mode 0 switches the check off, and \u002Fcloseall deletes every gate.","\u002Fimages\u002Fposts\u002Fwindows-2fa-gating-network-logons\u002Fgate-timeline.svg",[17,233,234],{},"The window covers the whole account, every connection to it. While it's open, anyone who can authenticate as that account gets in: your second laptop, a colleague who knows the password, an NTLM relay that catches the right moment. The gate proves that somebody with the phone was at this machine a few minutes ago. It doesn't prove that the person on the other end of this particular SMB connection is that somebody.",[17,236,237,238,241,242,245],{},"The window also only covers new logons. The filter runs when a logon is made, not on every file you open. A share you map inside the window stays up after the window ends, and after ",[41,239,240],{},"\u002Fclose"," too, until it disconnects. The same goes for a WinRM session. ",[41,243,244],{},"Close-SmbSession"," on the protected machine ends SMB sessions.",[17,247,248],{},"You get an enrolled account that can still map a share, and you pay for it with a window in which nobody asks for the second factor. Five minutes of password-only is a lot better than permanent password-only, which is what you had before enrolling, and a lot weaker than the flat deny in Part 2. Pick the one that matches what you protect.",[250,251,253],"h3",{"id":252},"what-lives-where","What lives where",[17,255,256],{},"Everything Ristretto reads or writes, in one place:",[258,259,265],"pre",{"className":260,"code":262,"language":263,"meta":264},[261],"language-text","HKLM\\SOFTWARE\\TheAdminCafe\\2FA\\Rids                       (Doppio, read-only for us)\n    \u003Crid>          presence marker per enrolled account\n\nHKLM\\SOFTWARE\\TheAdminCafe\\Ristretto                      (SYSTEM + Administrators)\n    Mode           REG_DWORD  1 = gate enforced, 0 = kill switch\n    WindowSec      REG_DWORD  gate lifetime, clamped 30-3600\n    PrevAuth0      REG_SZ     what Auth0 was before us, or __none__\n\nHKLM\\SOFTWARE\\TheAdminCafe\\Ristretto\\Gate                 (SYSTEM + Administrators)\n    \u003Crid>          REG_QWORD  unix time of the last successful unlock\n\nHKLM\\SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0\n    Auth0          REG_SZ     RistrettoFilter\n\nC:\\Program Files\\TheAdminCafe\\\n    TacProvider.dll, enroll.exe     (Doppio)\n    ristretto.exe                   (must sit here, see below)\n    state.lock                      (Doppio's StateLock, shared)\n\nC:\\Windows\\System32\\RistrettoFilter.dll\n","text","",[41,266,262],{"__ignoreMap":264},[17,268,269],{},"And the event IDs, all under the source \"TheAdminCafe Ristretto\" in the Application log:",[271,272,273,289],"table",{},[274,275,276],"thead",{},[277,278,279,283,286],"tr",{},[280,281,282],"th",{},"ID",[280,284,285],{},"Written by",[280,287,288],{},"Meaning",[290,291,292,306,317,329,339],"tbody",{},[277,293,294,298,303],{},[295,296,297],"td",{},"300",[295,299,300],{},[41,301,302],{},"ristretto.exe",[295,304,305],{},"gate opened",[277,307,308,311,314],{},[295,309,310],{},"301",[295,312,313],{},"filter",[295,315,316],{},"network logon refused",[277,318,319,322,326],{},[295,320,321],{},"302",[295,323,324],{},[41,325,302],{},[295,327,328],{},"gate closed, or all gates closed",[277,330,331,334,336],{},[295,332,333],{},"303",[295,335,313],{},[295,337,338],{},"exception in the decision, refused (fail closed)",[277,340,341,344,348],{},[295,342,343],{},"304",[295,345,346],{},[41,347,302],{},[295,349,350],{},"enforcement switched on or off",[17,352,353,354,356,357,360,361,364,365,368],{},"301 and 303 come from inside lsass, and writing to the event log from there is an RPC to the eventlog service. The filter has that switched off by default, so you see those two in the kernel debugger only. Without the debugger you see a refusal as a failed 4625 in the Security log, logon type 3. MSV1_0 reports ",[41,355,116],{},", the code the filter returns, as the sub-status of an account restriction. So expect Sub Status ",[41,358,359],{},"0xC0000070",", most likely under Status ",[41,362,363],{},"0xC000006E",". I haven't seen that 4625 yet, so look at both fields. That needs failure auditing for logons, ",[41,366,367],{},"auditpol \u002Fget \u002Fsubcategory:Logon"," shows whether it's on.",[12,370,372],{"id":371},"architecture","Architecture",[17,374,375],{},"Two binaries, and only one of them runs in lsass:",[271,377,378,393],{},[274,379,380],{},[277,381,382,384,389],{},[280,383],{},[280,385,386],{},[41,387,388],{},"RistrettoFilter.dll",[280,390,391],{},[41,392,302],{},[290,394,395,410,423,449,469,484,495],{},[277,396,397,400,405],{},[295,398,399],{},"Source",[295,401,402],{},[41,403,404],{},"src\u002Fristretto_filter.cpp",[295,406,407],{},[41,408,409],{},"src\u002Fristretto.cpp",[277,411,412,415,420],{},[295,413,414],{},"Runs in",[295,416,417],{},[41,418,419],{},"lsass.exe",[295,421,422],{},"an elevated console",[277,424,425,428,433],{},[295,426,427],{},"Doppio code linked",[295,429,430],{},[41,431,432],{},"store_index.cpp",[295,434,435,438,439,438,442,438,444,438,446],{},[41,436,437],{},"totp.cpp",", ",[41,440,441],{},"store.cpp",[41,443,432],{},[41,445,62],{},[41,447,448],{},"statelock.cpp",[277,450,451,454,464],{},[295,452,453],{},"Reads",[295,455,456,438,459,438,461],{},[41,457,458],{},"2FA\\Rids",[41,460,51],{},[41,462,463],{},"Ristretto\\Gate",[295,465,466,467],{},"Doppio's store and state, ",[41,468,51],{},[277,470,471,474,477],{},[295,472,473],{},"Writes",[295,475,476],{},"nothing",[295,478,479,438,481,483],{},[41,480,463],{},[41,482,51],{}," config, Doppio's state",[277,485,486,489,492],{},[295,487,488],{},"Heap, CRT",[295,490,491],{},"no",[295,493,494],{},"yes",[277,496,497,500,503],{},[295,498,499],{},"A bug costs you",[295,501,502],{},"the machine",[295,504,505],{},"one gate",[17,507,508,509,512],{},"Everything that needs DPAPI, account lookups, ",[41,510,511],{},"std::wstring"," or a console sits in the exe, where a crash kills a tool. The DLL does the smallest possible thing: two registry reads and a comparison.",[17,514,515],{},"Who reads and writes what, as a picture:",[17,517,518],{},[228,519],{"alt":520,"src":521},"In C:\\Program Files\\TheAdminCafe, TacProvider.dll and ristretto.exe both call VerifyOtp and take the same state.lock. Both update the same 2FA\\State\u003CSID> value: one failure counter and one lastStep for tile and gate. ristretto.exe also writes the Ristretto config and the gate timestamp under Ristretto\\Gate\u003Crid>. RistrettoFilter.dll runs in lsass as MSV1_0\\Auth0 and only reads: the RID marker from enroll.exe, the config and the gate. If ristretto.exe sits in another folder, it gets its own lock file and the counter is no longer shared.","\u002Fimages\u002Fposts\u002Fwindows-2fa-gating-network-logons\u002Fristretto-parts.svg",[17,523,524],{},[228,525],{"alt":526,"src":527},"The filter's decision path: four checks, two outcomes. A logon that isn't a network logon to this machine, a pass-through logon, the kill switch and an account that isn't enrolled all get no objection. A failed read of the index is a veto. Only an enrolled account on a network logon reaches the gate check, and only a fresh gate gets no objection. An exception inside the __try is a veto too. The veto is STATUS_INVALID_WORKSTATION, a 4625 with Sub Status 0xC0000070. On every path the filter sets WhichFields and UserFlags to 0 and Authoritative to TRUE.","\u002Fimages\u002Fposts\u002Fwindows-2fa-gating-network-logons\u002Ffilter-decision-path.svg",[17,529,530,531,533,534,537,538,540],{},"Four questions, in that order, on every logon MSV1_0 hands the filter. Three of them exit to \"no objection\" and cost at most one registry read. Only an enrolled account on a network logon to this machine ever reaches the gate check. The veto is ",[41,532,116],{},", the same code Doppio's filter returns, so MSV1_0 sees the same refusal as with Doppio's filter. Only the condition in front of it changed. My first version returned ",[41,535,536],{},"STATUS_ACCOUNT_RESTRICTION",", like Doppio did back then. The second review of ",[21,539,24],{"href":23}," found that this code isn't on Microsoft's list for this function, and both filters changed.",[12,542,544],{"id":543},"the-filter","The filter",[17,546,547,550],{},[41,548,549],{},"ristretto_filter.cpp"," is one file, and all of it is in this chapter, in the order it sits in the repo, with the explanation between the pieces.",[250,552,554],{"id":553},"header-and-includes","Header and includes",[258,556,560],{"className":557,"code":558,"filename":549,"language":559,"meta":264,"style":264},"language-cpp shiki shiki-themes gruvbox-dark-medium","\u002F\u002F RistrettoFilter - standalone MSV1_0 sub-authentication FILTER (Auth0).\n\u002F\u002F\n\u002F\u002F Companion to Doppio (github.com\u002FNobrac\u002FDoppio), not a patch. It reads\n\u002F\u002F Doppio's RID enrollment index (HKLM\\SOFTWARE\\TheAdminCafe\\2FA\\Rids) and\n\u002F\u002F nothing else of Doppio's. While installed it takes over the Auth0 slot\n\u002F\u002F from TacSubAuth (the installer saves and restores it).\n\u002F\u002F\n\u002F\u002F Policy: a NETWORK logon for an enrolled local account needs a fresh gate\n\u002F\u002F timestamp (HKLM\\SOFTWARE\\TheAdminCafe\\Ristretto\\Gate\\\u003Crid>, written by\n\u002F\u002F ristretto.exe after a TOTP check). No fresh gate -> veto, which is exactly\n\u002F\u002F what TacSubAuth would have done anyway. Everything else: no objection.\n\u002F\u002F\n\u002F\u002F Same rules as Doppio's filter: runs in lsass, no heap, fixed buffers, one\n\u002F\u002F registry read per decision, no DPAPI, no LSA lookups, never touches\n\u002F\u002F credential material. Test-VM code: unsigned, needs RunAsPPL off.\n\u002F\u002F\n\u002F\u002F The RID-index read is Doppio's own tac::EnrollmentByRid, from\n\u002F\u002F store_index.cpp, linked into this DLL. That file holds nothing but the\n\u002F\u002F index reads: no DPAPI, no account lookups, no heap. Earlier versions of\n\u002F\u002F this filter copied the read by hand, because it used to live in store.cpp\n\u002F\u002F next to CryptUnprotectData and LookupAccountName\u002FSid, which must not go\n\u002F\u002F into lsass. Doppio split the file (commit 37a827d), so the copy is gone and\n\u002F\u002F the two can no longer drift apart; see SECURITY-FIXES.md, item 14.\n\u002F\u002F\n\u002F\u002F The out-parameters and the veto code follow the documentation of\n\u002F\u002F Msv1_0SubAuthenticationFilter, like Doppio's filter: see item 15.\n\u002F\u002F\n\u002F\u002F Registered as:\n\u002F\u002F   HKLM\\SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0\n\u002F\u002F     Auth0 (REG_SZ) = RistrettoFilter\n\u002F\u002F\n\u002F\u002F ---------------------------------------------------------------------\n\u002F\u002F Hardening notes for anything that runs inside lsass (see also\n\u002F\u002F SECURITY-FIXES.md):\n\u002F\u002F\n\u002F\u002F  * No CRT on the decision path. swprintf_s() calls the invalid-parameter\n\u002F\u002F    handler on overflow, and the default handler terminates the process.\n\u002F\u002F    In lsass that is a bugcheck and a reboot loop. Everything here uses\n\u002F\u002F    bounded append helpers that truncate instead.\n\u002F\u002F  * SEH (__try\u002F__except), not C++ try\u002Fcatch. With \u002FEHsc a catch(...) does\n\u002F\u002F    NOT catch access violations, so the original catch block could never\n\u002F\u002F    have fired on the failure that actually matters.\n\u002F\u002F  * The whole decision runs behind one __except that fails closed.\n\u002F\u002F  * Time comes from GetSystemTimeAsFileTime, not _time64: same value\n\u002F\u002F    (UTC unix seconds), no CRT, no locale, no heap.\n\u002F\u002F ---------------------------------------------------------------------\n\n#define SECURITY_WIN32\n#include \u003Cwindows.h>\n#include \u003Csspi.h>\n#include \u003Cntsecapi.h>\n#include \u003Csubauth.h>\n\n#include \"store.h\"      \u002F\u002F Doppio: tac::EnrollmentByRid, from store_index.cpp\n\n#pragma comment(lib, \"advapi32.lib\")\n\n#ifndef STATUS_SUCCESS\n#define STATUS_SUCCESS             ((NTSTATUS)0x00000000L)\n#endif\n\u002F\u002F The documentation of Msv1_0SubAuthenticationFilter lists the values the\n\u002F\u002F function \"must return\". STATUS_ACCOUNT_RESTRICTION, which this filter used\n\u002F\u002F to veto with, is not among them; STATUS_INVALID_WORKSTATION is, and it says\n\u002F\u002F what we mean: this account may not log on over this path right now.\n#ifndef STATUS_INVALID_WORKSTATION\n#define STATUS_INVALID_WORKSTATION ((NTSTATUS)0xC0000070L)\n#endif\n\n#ifndef MSV1_0_PASSTHRU\n#define MSV1_0_PASSTHRU            0x01\n#endif\n\n\u002F\u002F Writing to the event log from lsass is an RPC to the eventlog service;\n\u002F\u002F the conservative default on the logon path is off. The veto is always\n\u002F\u002F observable as a failed 4625 in the Security log either way.\n#define RISTRETTO_FILTER_EVENTLOG 0\n\n\u002F\u002F Log every *allowed* logon too. Off by default: it puts account names in\n\u002F\u002F front of anything holding a debugger on the box, on every SMB connection.\n#define RISTRETTO_FILTER_VERBOSE  0\n","cpp",[41,561,562,570,576,582,588,594,600,605,611,617,623,629,634,640,646,652,657,663,669,675,681,687,693,699,704,710,716,721,727,733,739,744,750,756,762,767,773,779,785,791,797,803,809,815,821,827,832,839,854,872,886,900,914,919,938,943,973,978,989,1021,1029,1035,1041,1047,1053,1063,1089,1096,1101,1111,1127,1134,1139,1145,1151,1157,1170,1175,1181,1187],{"__ignoreMap":264},[70,563,566],{"class":564,"line":565},"line",1,[70,567,569],{"class":568},"s-UKz","\u002F\u002F RistrettoFilter - standalone MSV1_0 sub-authentication FILTER (Auth0).\n",[70,571,573],{"class":564,"line":572},2,[70,574,575],{"class":568},"\u002F\u002F\n",[70,577,579],{"class":564,"line":578},3,[70,580,581],{"class":568},"\u002F\u002F Companion to Doppio (github.com\u002FNobrac\u002FDoppio), not a patch. It reads\n",[70,583,585],{"class":564,"line":584},4,[70,586,587],{"class":568},"\u002F\u002F Doppio's RID enrollment index (HKLM\\SOFTWARE\\TheAdminCafe\\2FA\\Rids) and\n",[70,589,591],{"class":564,"line":590},5,[70,592,593],{"class":568},"\u002F\u002F nothing else of Doppio's. While installed it takes over the Auth0 slot\n",[70,595,597],{"class":564,"line":596},6,[70,598,599],{"class":568},"\u002F\u002F from TacSubAuth (the installer saves and restores it).\n",[70,601,603],{"class":564,"line":602},7,[70,604,575],{"class":568},[70,606,608],{"class":564,"line":607},8,[70,609,610],{"class":568},"\u002F\u002F Policy: a NETWORK logon for an enrolled local account needs a fresh gate\n",[70,612,614],{"class":564,"line":613},9,[70,615,616],{"class":568},"\u002F\u002F timestamp (HKLM\\SOFTWARE\\TheAdminCafe\\Ristretto\\Gate\\\u003Crid>, written by\n",[70,618,620],{"class":564,"line":619},10,[70,621,622],{"class":568},"\u002F\u002F ristretto.exe after a TOTP check). No fresh gate -> veto, which is exactly\n",[70,624,626],{"class":564,"line":625},11,[70,627,628],{"class":568},"\u002F\u002F what TacSubAuth would have done anyway. Everything else: no objection.\n",[70,630,632],{"class":564,"line":631},12,[70,633,575],{"class":568},[70,635,637],{"class":564,"line":636},13,[70,638,639],{"class":568},"\u002F\u002F Same rules as Doppio's filter: runs in lsass, no heap, fixed buffers, one\n",[70,641,643],{"class":564,"line":642},14,[70,644,645],{"class":568},"\u002F\u002F registry read per decision, no DPAPI, no LSA lookups, never touches\n",[70,647,649],{"class":564,"line":648},15,[70,650,651],{"class":568},"\u002F\u002F credential material. Test-VM code: unsigned, needs RunAsPPL off.\n",[70,653,655],{"class":564,"line":654},16,[70,656,575],{"class":568},[70,658,660],{"class":564,"line":659},17,[70,661,662],{"class":568},"\u002F\u002F The RID-index read is Doppio's own tac::EnrollmentByRid, from\n",[70,664,666],{"class":564,"line":665},18,[70,667,668],{"class":568},"\u002F\u002F store_index.cpp, linked into this DLL. That file holds nothing but the\n",[70,670,672],{"class":564,"line":671},19,[70,673,674],{"class":568},"\u002F\u002F index reads: no DPAPI, no account lookups, no heap. Earlier versions of\n",[70,676,678],{"class":564,"line":677},20,[70,679,680],{"class":568},"\u002F\u002F this filter copied the read by hand, because it used to live in store.cpp\n",[70,682,684],{"class":564,"line":683},21,[70,685,686],{"class":568},"\u002F\u002F next to CryptUnprotectData and LookupAccountName\u002FSid, which must not go\n",[70,688,690],{"class":564,"line":689},22,[70,691,692],{"class":568},"\u002F\u002F into lsass. Doppio split the file (commit 37a827d), so the copy is gone and\n",[70,694,696],{"class":564,"line":695},23,[70,697,698],{"class":568},"\u002F\u002F the two can no longer drift apart; see SECURITY-FIXES.md, item 14.\n",[70,700,702],{"class":564,"line":701},24,[70,703,575],{"class":568},[70,705,707],{"class":564,"line":706},25,[70,708,709],{"class":568},"\u002F\u002F The out-parameters and the veto code follow the documentation of\n",[70,711,713],{"class":564,"line":712},26,[70,714,715],{"class":568},"\u002F\u002F Msv1_0SubAuthenticationFilter, like Doppio's filter: see item 15.\n",[70,717,719],{"class":564,"line":718},27,[70,720,575],{"class":568},[70,722,724],{"class":564,"line":723},28,[70,725,726],{"class":568},"\u002F\u002F Registered as:\n",[70,728,730],{"class":564,"line":729},29,[70,731,732],{"class":568},"\u002F\u002F   HKLM\\SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0\n",[70,734,736],{"class":564,"line":735},30,[70,737,738],{"class":568},"\u002F\u002F     Auth0 (REG_SZ) = RistrettoFilter\n",[70,740,742],{"class":564,"line":741},31,[70,743,575],{"class":568},[70,745,747],{"class":564,"line":746},32,[70,748,749],{"class":568},"\u002F\u002F ---------------------------------------------------------------------\n",[70,751,753],{"class":564,"line":752},33,[70,754,755],{"class":568},"\u002F\u002F Hardening notes for anything that runs inside lsass (see also\n",[70,757,759],{"class":564,"line":758},34,[70,760,761],{"class":568},"\u002F\u002F SECURITY-FIXES.md):\n",[70,763,765],{"class":564,"line":764},35,[70,766,575],{"class":568},[70,768,770],{"class":564,"line":769},36,[70,771,772],{"class":568},"\u002F\u002F  * No CRT on the decision path. swprintf_s() calls the invalid-parameter\n",[70,774,776],{"class":564,"line":775},37,[70,777,778],{"class":568},"\u002F\u002F    handler on overflow, and the default handler terminates the process.\n",[70,780,782],{"class":564,"line":781},38,[70,783,784],{"class":568},"\u002F\u002F    In lsass that is a bugcheck and a reboot loop. Everything here uses\n",[70,786,788],{"class":564,"line":787},39,[70,789,790],{"class":568},"\u002F\u002F    bounded append helpers that truncate instead.\n",[70,792,794],{"class":564,"line":793},40,[70,795,796],{"class":568},"\u002F\u002F  * SEH (__try\u002F__except), not C++ try\u002Fcatch. With \u002FEHsc a catch(...) does\n",[70,798,800],{"class":564,"line":799},41,[70,801,802],{"class":568},"\u002F\u002F    NOT catch access violations, so the original catch block could never\n",[70,804,806],{"class":564,"line":805},42,[70,807,808],{"class":568},"\u002F\u002F    have fired on the failure that actually matters.\n",[70,810,812],{"class":564,"line":811},43,[70,813,814],{"class":568},"\u002F\u002F  * The whole decision runs behind one __except that fails closed.\n",[70,816,818],{"class":564,"line":817},44,[70,819,820],{"class":568},"\u002F\u002F  * Time comes from GetSystemTimeAsFileTime, not _time64: same value\n",[70,822,824],{"class":564,"line":823},45,[70,825,826],{"class":568},"\u002F\u002F    (UTC unix seconds), no CRT, no locale, no heap.\n",[70,828,830],{"class":564,"line":829},46,[70,831,749],{"class":568},[70,833,835],{"class":564,"line":834},47,[70,836,838],{"emptyLinePlaceholder":837},true,"\n",[70,840,842,846,850],{"class":564,"line":841},48,[70,843,845],{"class":844},"svnLg","#",[70,847,849],{"class":848},"sM2RB","define",[70,851,853],{"class":852},"s59W0"," SECURITY_WIN32\n",[70,855,857,859,862,865,869],{"class":564,"line":856},49,[70,858,845],{"class":844},[70,860,861],{"class":848},"include",[70,863,864],{"class":844}," \u003C",[70,866,868],{"class":867},"sTqo2","windows.h",[70,870,871],{"class":844},">\n",[70,873,875,877,879,881,884],{"class":564,"line":874},50,[70,876,845],{"class":844},[70,878,861],{"class":848},[70,880,864],{"class":844},[70,882,883],{"class":867},"sspi.h",[70,885,871],{"class":844},[70,887,889,891,893,895,898],{"class":564,"line":888},51,[70,890,845],{"class":844},[70,892,861],{"class":848},[70,894,864],{"class":844},[70,896,897],{"class":867},"ntsecapi.h",[70,899,871],{"class":844},[70,901,903,905,907,909,912],{"class":564,"line":902},52,[70,904,845],{"class":844},[70,906,861],{"class":848},[70,908,864],{"class":844},[70,910,911],{"class":867},"subauth.h",[70,913,871],{"class":844},[70,915,917],{"class":564,"line":916},53,[70,918,838],{"emptyLinePlaceholder":837},[70,920,922,924,926,929,932,935],{"class":564,"line":921},54,[70,923,845],{"class":844},[70,925,861],{"class":848},[70,927,928],{"class":844}," \"",[70,930,931],{"class":867},"store.h",[70,933,934],{"class":844},"\"",[70,936,937],{"class":568},"      \u002F\u002F Doppio: tac::EnrollmentByRid, from store_index.cpp\n",[70,939,941],{"class":564,"line":940},55,[70,942,838],{"emptyLinePlaceholder":837},[70,944,946,948,951,954,958,961,963,965,968,970],{"class":564,"line":945},56,[70,947,845],{"class":844},[70,949,950],{"class":848},"pragma",[70,952,953],{"class":852}," comment",[70,955,957],{"class":956},"sG605","(",[70,959,960],{"class":852},"lib",[70,962,438],{"class":956},[70,964,934],{"class":844},[70,966,967],{"class":867},"advapi32.lib",[70,969,934],{"class":844},[70,971,972],{"class":956},")\n",[70,974,976],{"class":564,"line":975},57,[70,977,838],{"emptyLinePlaceholder":837},[70,979,981,983,986],{"class":564,"line":980},58,[70,982,845],{"class":844},[70,984,985],{"class":848},"ifndef",[70,987,988],{"class":852}," STATUS_SUCCESS\n",[70,990,992,994,996,999,1002,1006,1009,1012,1016,1019],{"class":564,"line":991},59,[70,993,845],{"class":844},[70,995,849],{"class":848},[70,997,998],{"class":852}," STATUS_SUCCESS",[70,1000,1001],{"class":844},"             ((",[70,1003,1005],{"class":1004},"sMWUE","NTSTATUS",[70,1007,1008],{"class":844},")",[70,1010,1011],{"class":867},"0x",[70,1013,1015],{"class":1014},"ssIPD","00000000",[70,1017,1018],{"class":867},"L",[70,1020,972],{"class":844},[70,1022,1024,1026],{"class":564,"line":1023},60,[70,1025,845],{"class":844},[70,1027,1028],{"class":848},"endif\n",[70,1030,1032],{"class":564,"line":1031},61,[70,1033,1034],{"class":568},"\u002F\u002F The documentation of Msv1_0SubAuthenticationFilter lists the values the\n",[70,1036,1038],{"class":564,"line":1037},62,[70,1039,1040],{"class":568},"\u002F\u002F function \"must return\". STATUS_ACCOUNT_RESTRICTION, which this filter used\n",[70,1042,1044],{"class":564,"line":1043},63,[70,1045,1046],{"class":568},"\u002F\u002F to veto with, is not among them; STATUS_INVALID_WORKSTATION is, and it says\n",[70,1048,1050],{"class":564,"line":1049},64,[70,1051,1052],{"class":568},"\u002F\u002F what we mean: this account may not log on over this path right now.\n",[70,1054,1056,1058,1060],{"class":564,"line":1055},65,[70,1057,845],{"class":844},[70,1059,985],{"class":848},[70,1061,1062],{"class":852}," STATUS_INVALID_WORKSTATION\n",[70,1064,1066,1068,1070,1073,1076,1078,1080,1082,1085,1087],{"class":564,"line":1065},66,[70,1067,845],{"class":844},[70,1069,849],{"class":848},[70,1071,1072],{"class":852}," STATUS_INVALID_WORKSTATION",[70,1074,1075],{"class":844}," ((",[70,1077,1005],{"class":1004},[70,1079,1008],{"class":844},[70,1081,1011],{"class":867},[70,1083,1084],{"class":1014},"C0000070",[70,1086,1018],{"class":867},[70,1088,972],{"class":844},[70,1090,1092,1094],{"class":564,"line":1091},67,[70,1093,845],{"class":844},[70,1095,1028],{"class":848},[70,1097,1099],{"class":564,"line":1098},68,[70,1100,838],{"emptyLinePlaceholder":837},[70,1102,1104,1106,1108],{"class":564,"line":1103},69,[70,1105,845],{"class":844},[70,1107,985],{"class":848},[70,1109,1110],{"class":852}," MSV1_0_PASSTHRU\n",[70,1112,1114,1116,1118,1121,1124],{"class":564,"line":1113},70,[70,1115,845],{"class":844},[70,1117,849],{"class":848},[70,1119,1120],{"class":852}," MSV1_0_PASSTHRU",[70,1122,1123],{"class":867},"            0x",[70,1125,1126],{"class":1014},"01\n",[70,1128,1130,1132],{"class":564,"line":1129},71,[70,1131,845],{"class":844},[70,1133,1028],{"class":848},[70,1135,1137],{"class":564,"line":1136},72,[70,1138,838],{"emptyLinePlaceholder":837},[70,1140,1142],{"class":564,"line":1141},73,[70,1143,1144],{"class":568},"\u002F\u002F Writing to the event log from lsass is an RPC to the eventlog service;\n",[70,1146,1148],{"class":564,"line":1147},74,[70,1149,1150],{"class":568},"\u002F\u002F the conservative default on the logon path is off. The veto is always\n",[70,1152,1154],{"class":564,"line":1153},75,[70,1155,1156],{"class":568},"\u002F\u002F observable as a failed 4625 in the Security log either way.\n",[70,1158,1160,1162,1164,1167],{"class":564,"line":1159},76,[70,1161,845],{"class":844},[70,1163,849],{"class":848},[70,1165,1166],{"class":852}," RISTRETTO_FILTER_EVENTLOG",[70,1168,1169],{"class":1014}," 0\n",[70,1171,1173],{"class":564,"line":1172},77,[70,1174,838],{"emptyLinePlaceholder":837},[70,1176,1178],{"class":564,"line":1177},78,[70,1179,1180],{"class":568},"\u002F\u002F Log every *allowed* logon too. Off by default: it puts account names in\n",[70,1182,1184],{"class":564,"line":1183},79,[70,1185,1186],{"class":568},"\u002F\u002F front of anything holding a debugger on the box, on every SMB connection.\n",[70,1188,1190,1192,1194,1197],{"class":564,"line":1189},80,[70,1191,845],{"class":844},[70,1193,849],{"class":848},[70,1195,1196],{"class":852}," RISTRETTO_FILTER_VERBOSE",[70,1198,1199],{"class":1014},"  0\n",[17,1201,1202],{},"The header comment is the contract, and the rest of the file has to keep it. Three things in there are worth reading twice:",[105,1204,1205,1230,1233],{},[108,1206,1207,1208,1210,1211,1214,1215,1217,1218,1221,1222,1225,1226,1229],{},"The enrollment check is Doppio's. The filter links one file of Doppio's, ",[41,1209,432],{},", and calls ",[41,1212,1213],{},"tac::EnrollmentByRid",". My first version copied fifteen lines instead. Back then the read sat in ",[41,1216,441],{},", next to ",[41,1219,1220],{},"CryptUnprotectData"," and ",[41,1223,1224],{},"LookupAccountName","\u002F",[41,1227,1228],{},"LookupAccountSid",". DPAPI and account lookups call back into LSA, and calling back into LSA from inside an LSA callback is how you get a deadlock on the logon path. The second review of Part 2 moved the index reads into their own file, without DPAPI, without account lookups and without a heap. So the copy is gone, and the two can't drift apart anymore.",[108,1231,1232],{},"Each hardening note is a finding from the audit later in this article. I put them at the top so the next person who edits the file reads them before they touch the code.",[108,1234,1235,1221,1238,1241,1242,1245],{},[41,1236,1237],{},"RISTRETTO_FILTER_EVENTLOG",[41,1239,1240],{},"RISTRETTO_FILTER_VERBOSE"," are both off. The event log is an RPC from inside lsass, and verbose logging puts an account name into ",[41,1243,1244],{},"OutputDebugStringW"," on every SMB connection, readable by anything that holds a debugger on the box. A veto logs to the debugger, an allow doesn't.",[17,1247,1248,1249,1252,1253,1256,1257,1221,1260,1262,1263,1265,1266,1269,1270,1273],{},"The ",[41,1250,1251],{},"#ifndef"," guards around the two ",[41,1254,1255],{},"STATUS_"," defines are there because ",[41,1258,1259],{},"ntstatus.h",[41,1261,868],{}," fight over them. Including ",[41,1264,1259],{}," cleanly needs ",[41,1267,1268],{},"WIN32_NO_STATUS"," games that aren't worth it for two constants. ",[41,1271,1272],{},"MSV1_0_PASSTHRU"," gets the same guard, in case a header already defines it. The export further down uses it.",[250,1275,1277],{"id":1276},"constants","Constants",[258,1279,1281],{"className":557,"code":1280,"filename":549,"language":559,"meta":264,"style":264},"namespace\n{\n    const wchar_t kGatePath[]   = L\"SOFTWARE\\\\TheAdminCafe\\\\Ristretto\\\\Gate\";\n    const wchar_t kConfigPath[] = L\"SOFTWARE\\\\TheAdminCafe\\\\Ristretto\";\n\n    \u002F\u002F Window bounds. The registry key is SYSTEM\u002FAdministrators only, so this\n    \u002F\u002F is not a privilege boundary - it is a guard against a typo turning the\n    \u002F\u002F gate into a permanently open door. 0xFFFFFFFF seconds is 136 years.\n    const DWORD kWindowDefault = 300;     \u002F\u002F 5 min\n    const DWORD kWindowMin     = 30;\n    const DWORD kWindowMax     = 3600;    \u002F\u002F 1 h\n\n    const DWORD kConfigRefreshMs = 5000;\n",[41,1282,1283,1288,1293,1336,1366,1370,1375,1380,1385,1406,1423,1442,1446],{"__ignoreMap":264},[70,1284,1285],{"class":564,"line":565},[70,1286,1287],{"class":956},"namespace\n",[70,1289,1290],{"class":564,"line":572},[70,1291,1292],{"class":844},"{\n",[70,1294,1295,1298,1301,1304,1307,1310,1313,1316,1319,1322,1324,1326,1328,1331,1333],{"class":564,"line":578},[70,1296,1297],{"class":956},"    const",[70,1299,1300],{"class":956}," wchar_t",[70,1302,1303],{"class":1004}," kGatePath",[70,1305,1306],{"class":844},"[]",[70,1308,1309],{"class":848},"   =",[70,1311,1312],{"class":844}," L\"",[70,1314,1315],{"class":867},"SOFTWARE",[70,1317,1318],{"class":1014},"\\\\",[70,1320,1321],{"class":867},"TheAdminCafe",[70,1323,1318],{"class":1014},[70,1325,51],{"class":867},[70,1327,1318],{"class":1014},[70,1329,1330],{"class":867},"Gate",[70,1332,934],{"class":844},[70,1334,1335],{"class":844},";\n",[70,1337,1338,1340,1342,1345,1347,1350,1352,1354,1356,1358,1360,1362,1364],{"class":564,"line":584},[70,1339,1297],{"class":956},[70,1341,1300],{"class":956},[70,1343,1344],{"class":1004}," kConfigPath",[70,1346,1306],{"class":844},[70,1348,1349],{"class":848}," =",[70,1351,1312],{"class":844},[70,1353,1315],{"class":867},[70,1355,1318],{"class":1014},[70,1357,1321],{"class":867},[70,1359,1318],{"class":1014},[70,1361,51],{"class":867},[70,1363,934],{"class":844},[70,1365,1335],{"class":844},[70,1367,1368],{"class":564,"line":590},[70,1369,838],{"emptyLinePlaceholder":837},[70,1371,1372],{"class":564,"line":596},[70,1373,1374],{"class":568},"    \u002F\u002F Window bounds. The registry key is SYSTEM\u002FAdministrators only, so this\n",[70,1376,1377],{"class":564,"line":602},[70,1378,1379],{"class":568},"    \u002F\u002F is not a privilege boundary - it is a guard against a typo turning the\n",[70,1381,1382],{"class":564,"line":607},[70,1383,1384],{"class":568},"    \u002F\u002F gate into a permanently open door. 0xFFFFFFFF seconds is 136 years.\n",[70,1386,1387,1389,1392,1395,1397,1400,1403],{"class":564,"line":613},[70,1388,1297],{"class":956},[70,1390,1391],{"class":1004}," DWORD",[70,1393,1394],{"class":1004}," kWindowDefault",[70,1396,1349],{"class":848},[70,1398,1399],{"class":1014}," 300",[70,1401,1402],{"class":844},";",[70,1404,1405],{"class":568},"     \u002F\u002F 5 min\n",[70,1407,1408,1410,1412,1415,1418,1421],{"class":564,"line":619},[70,1409,1297],{"class":956},[70,1411,1391],{"class":1004},[70,1413,1414],{"class":1004}," kWindowMin",[70,1416,1417],{"class":848},"     =",[70,1419,1420],{"class":1014}," 30",[70,1422,1335],{"class":844},[70,1424,1425,1427,1429,1432,1434,1437,1439],{"class":564,"line":625},[70,1426,1297],{"class":956},[70,1428,1391],{"class":1004},[70,1430,1431],{"class":1004}," kWindowMax",[70,1433,1417],{"class":848},[70,1435,1436],{"class":1014}," 3600",[70,1438,1402],{"class":844},[70,1440,1441],{"class":568},"    \u002F\u002F 1 h\n",[70,1443,1444],{"class":564,"line":631},[70,1445,838],{"emptyLinePlaceholder":837},[70,1447,1448,1450,1452,1455,1457,1460],{"class":564,"line":636},[70,1449,1297],{"class":956},[70,1451,1391],{"class":1004},[70,1453,1454],{"class":1004}," kConfigRefreshMs",[70,1456,1349],{"class":848},[70,1458,1459],{"class":1014}," 5000",[70,1461,1335],{"class":844},[17,1463,1464,1465,1468,1469,1471,1472,1475,1476,1479],{},"Both paths are relative to ",[41,1466,1467],{},"HKEY_LOCAL_MACHINE",", and so is Doppio's ",[41,1470,458],{},", which comes from ",[41,1473,1474],{},"storekeys.h"," now. The filter never opens ",[41,1477,1478],{},"HKEY_CURRENT_USER"," or anything per-user, it runs as SYSTEM inside lsass and has no business there.",[17,1481,1482],{},"The key is admin-only, and an admin can switch the filter off, so the window bounds don't stop an attacker. They stop a typo from turning the gate into a door that stays open for 136 years. More about that in the audit chapter.",[250,1484,1486],{"id":1485},"a-string-builder-without-the-crt","A string builder without the CRT",[258,1488,1490],{"className":557,"code":1489,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F ---- bounded, CRT-free string building ---------------------------\n    class Buf\n    {\n    public:\n        Buf(WCHAR* p, SIZE_T cap) : p_(p), cap_(cap), n_(0)\n        {\n            if (cap_) p_[0] = L'\\0';\n        }\n\n        void Str(const WCHAR* s)\n        {\n            if (!s) return;\n            while (*s && n_ + 1 \u003C cap_) p_[n_++] = *s++;\n            if (cap_) p_[n_] = L'\\0';\n        }\n\n        void U64(ULONGLONG v)\n        {\n            WCHAR t[21];\n            int i = 0;\n            do { t[i++] = (WCHAR)(L'0' + (ULONG)(v % 10)); v \u002F= 10; } while (v && i \u003C 20);\n            while (i > 0 && n_ + 1 \u003C cap_) p_[n_++] = t[--i];\n            if (cap_) p_[n_] = L'\\0';\n        }\n\n        SIZE_T Len() const { return n_; }\n\n    private:\n        WCHAR* p_;\n        SIZE_T cap_;\n        SIZE_T n_;\n    };\n",[41,1491,1492,1497,1505,1510,1518,1578,1583,1620,1625,1629,1652,1656,1675,1726,1754,1758,1762,1779,1783,1799,1814,1901,1951,1979,1983,1987,2012,2016,2023,2034,2042,2050],{"__ignoreMap":264},[70,1493,1494],{"class":564,"line":565},[70,1495,1496],{"class":568},"    \u002F\u002F ---- bounded, CRT-free string building ---------------------------\n",[70,1498,1499,1502],{"class":564,"line":572},[70,1500,1501],{"class":956},"    class",[70,1503,1504],{"class":852}," Buf\n",[70,1506,1507],{"class":564,"line":578},[70,1508,1509],{"class":844},"    {\n",[70,1511,1512,1515],{"class":564,"line":584},[70,1513,1514],{"class":956},"    public",[70,1516,1517],{"class":844},":\n",[70,1519,1520,1523,1525,1528,1531,1534,1537,1540,1543,1545,1548,1551,1553,1555,1558,1561,1563,1566,1568,1571,1573,1576],{"class":564,"line":590},[70,1521,1522],{"class":852},"        Buf",[70,1524,957],{"class":844},[70,1526,1527],{"class":852},"WCHAR",[70,1529,1530],{"class":956},"*",[70,1532,1533],{"class":1004}," p",[70,1535,1536],{"class":844},",",[70,1538,1539],{"class":852}," SIZE_T",[70,1541,1542],{"class":1004}," cap",[70,1544,1008],{"class":844},[70,1546,1547],{"class":844}," :",[70,1549,1550],{"class":852}," p_",[70,1552,957],{"class":844},[70,1554,17],{"class":1004},[70,1556,1557],{"class":844},"),",[70,1559,1560],{"class":852}," cap_",[70,1562,957],{"class":844},[70,1564,1565],{"class":1004},"cap",[70,1567,1557],{"class":844},[70,1569,1570],{"class":852}," n_",[70,1572,957],{"class":844},[70,1574,1575],{"class":1014},"0",[70,1577,972],{"class":844},[70,1579,1580],{"class":564,"line":596},[70,1581,1582],{"class":844},"        {\n",[70,1584,1585,1589,1592,1595,1597,1599,1602,1604,1607,1609,1612,1615,1618],{"class":564,"line":602},[70,1586,1588],{"class":1587},"sBQf5","            if",[70,1590,1591],{"class":844}," (",[70,1593,1594],{"class":1004},"cap_",[70,1596,1008],{"class":844},[70,1598,1550],{"class":1004},[70,1600,1601],{"class":844},"[",[70,1603,1575],{"class":1014},[70,1605,1606],{"class":844},"]",[70,1608,1349],{"class":848},[70,1610,1611],{"class":844}," L'",[70,1613,1614],{"class":1014},"\\0",[70,1616,1617],{"class":844},"'",[70,1619,1335],{"class":844},[70,1621,1622],{"class":564,"line":607},[70,1623,1624],{"class":844},"        }\n",[70,1626,1627],{"class":564,"line":613},[70,1628,838],{"emptyLinePlaceholder":837},[70,1630,1631,1634,1637,1639,1642,1645,1647,1650],{"class":564,"line":619},[70,1632,1633],{"class":956},"        void",[70,1635,1636],{"class":852}," Str",[70,1638,957],{"class":844},[70,1640,1641],{"class":956},"const",[70,1643,1644],{"class":852}," WCHAR",[70,1646,1530],{"class":956},[70,1648,1649],{"class":1004}," s",[70,1651,972],{"class":844},[70,1653,1654],{"class":564,"line":625},[70,1655,1582],{"class":844},[70,1657,1658,1660,1662,1665,1668,1670,1673],{"class":564,"line":631},[70,1659,1588],{"class":1587},[70,1661,1591],{"class":844},[70,1663,1664],{"class":1587},"!",[70,1666,1667],{"class":1004},"s",[70,1669,1008],{"class":844},[70,1671,1672],{"class":1587}," return",[70,1674,1335],{"class":844},[70,1676,1677,1680,1682,1684,1686,1689,1691,1694,1697,1699,1701,1703,1705,1707,1710,1713,1715,1717,1720,1722,1724],{"class":564,"line":636},[70,1678,1679],{"class":1587},"            while",[70,1681,1591],{"class":844},[70,1683,1530],{"class":848},[70,1685,1667],{"class":1004},[70,1687,1688],{"class":1587}," &&",[70,1690,1570],{"class":1004},[70,1692,1693],{"class":848}," +",[70,1695,1696],{"class":1014}," 1",[70,1698,864],{"class":848},[70,1700,1560],{"class":1004},[70,1702,1008],{"class":844},[70,1704,1550],{"class":1004},[70,1706,1601],{"class":844},[70,1708,1709],{"class":1004},"n_",[70,1711,1712],{"class":848},"++",[70,1714,1606],{"class":844},[70,1716,1349],{"class":848},[70,1718,1719],{"class":848}," *",[70,1721,1667],{"class":1004},[70,1723,1712],{"class":848},[70,1725,1335],{"class":844},[70,1727,1728,1730,1732,1734,1736,1738,1740,1742,1744,1746,1748,1750,1752],{"class":564,"line":642},[70,1729,1588],{"class":1587},[70,1731,1591],{"class":844},[70,1733,1594],{"class":1004},[70,1735,1008],{"class":844},[70,1737,1550],{"class":1004},[70,1739,1601],{"class":844},[70,1741,1709],{"class":1004},[70,1743,1606],{"class":844},[70,1745,1349],{"class":848},[70,1747,1611],{"class":844},[70,1749,1614],{"class":1014},[70,1751,1617],{"class":844},[70,1753,1335],{"class":844},[70,1755,1756],{"class":564,"line":648},[70,1757,1624],{"class":844},[70,1759,1760],{"class":564,"line":654},[70,1761,838],{"emptyLinePlaceholder":837},[70,1763,1764,1766,1769,1771,1774,1777],{"class":564,"line":659},[70,1765,1633],{"class":956},[70,1767,1768],{"class":852}," U64",[70,1770,957],{"class":844},[70,1772,1773],{"class":852},"ULONGLONG",[70,1775,1776],{"class":1004}," v",[70,1778,972],{"class":844},[70,1780,1781],{"class":564,"line":665},[70,1782,1582],{"class":844},[70,1784,1785,1788,1791,1793,1796],{"class":564,"line":671},[70,1786,1787],{"class":852},"            WCHAR",[70,1789,1790],{"class":1004}," t",[70,1792,1601],{"class":844},[70,1794,1795],{"class":1014},"21",[70,1797,1798],{"class":844},"];\n",[70,1800,1801,1804,1807,1809,1812],{"class":564,"line":677},[70,1802,1803],{"class":956},"            int",[70,1805,1806],{"class":1004}," i",[70,1808,1349],{"class":848},[70,1810,1811],{"class":1014}," 0",[70,1813,1335],{"class":844},[70,1815,1816,1819,1822,1824,1826,1829,1831,1833,1835,1837,1839,1842,1845,1847,1849,1851,1853,1856,1858,1861,1864,1867,1870,1872,1875,1877,1879,1882,1885,1887,1889,1891,1893,1895,1898],{"class":564,"line":683},[70,1817,1818],{"class":1587},"            do",[70,1820,1821],{"class":844}," {",[70,1823,1790],{"class":1004},[70,1825,1601],{"class":844},[70,1827,1828],{"class":1004},"i",[70,1830,1712],{"class":848},[70,1832,1606],{"class":844},[70,1834,1349],{"class":848},[70,1836,1591],{"class":844},[70,1838,1527],{"class":1004},[70,1840,1841],{"class":844},")(",[70,1843,1844],{"class":844},"L'",[70,1846,1575],{"class":867},[70,1848,1617],{"class":844},[70,1850,1693],{"class":848},[70,1852,1591],{"class":844},[70,1854,1855],{"class":1004},"ULONG",[70,1857,1841],{"class":844},[70,1859,1860],{"class":1004},"v",[70,1862,1863],{"class":848}," %",[70,1865,1866],{"class":1014}," 10",[70,1868,1869],{"class":844},"));",[70,1871,1776],{"class":1004},[70,1873,1874],{"class":848}," \u002F=",[70,1876,1866],{"class":1014},[70,1878,1402],{"class":844},[70,1880,1881],{"class":844}," }",[70,1883,1884],{"class":1587}," while",[70,1886,1591],{"class":844},[70,1888,1860],{"class":1004},[70,1890,1688],{"class":1587},[70,1892,1806],{"class":1004},[70,1894,864],{"class":848},[70,1896,1897],{"class":1014}," 20",[70,1899,1900],{"class":844},");\n",[70,1902,1903,1905,1907,1909,1912,1914,1916,1918,1920,1922,1924,1926,1928,1930,1932,1934,1936,1938,1940,1942,1944,1947,1949],{"class":564,"line":689},[70,1904,1679],{"class":1587},[70,1906,1591],{"class":844},[70,1908,1828],{"class":1004},[70,1910,1911],{"class":848}," >",[70,1913,1811],{"class":1014},[70,1915,1688],{"class":1587},[70,1917,1570],{"class":1004},[70,1919,1693],{"class":848},[70,1921,1696],{"class":1014},[70,1923,864],{"class":848},[70,1925,1560],{"class":1004},[70,1927,1008],{"class":844},[70,1929,1550],{"class":1004},[70,1931,1601],{"class":844},[70,1933,1709],{"class":1004},[70,1935,1712],{"class":848},[70,1937,1606],{"class":844},[70,1939,1349],{"class":848},[70,1941,1790],{"class":1004},[70,1943,1601],{"class":844},[70,1945,1946],{"class":848},"--",[70,1948,1828],{"class":1004},[70,1950,1798],{"class":844},[70,1952,1953,1955,1957,1959,1961,1963,1965,1967,1969,1971,1973,1975,1977],{"class":564,"line":695},[70,1954,1588],{"class":1587},[70,1956,1591],{"class":844},[70,1958,1594],{"class":1004},[70,1960,1008],{"class":844},[70,1962,1550],{"class":1004},[70,1964,1601],{"class":844},[70,1966,1709],{"class":1004},[70,1968,1606],{"class":844},[70,1970,1349],{"class":848},[70,1972,1611],{"class":844},[70,1974,1614],{"class":1014},[70,1976,1617],{"class":844},[70,1978,1335],{"class":844},[70,1980,1981],{"class":564,"line":701},[70,1982,1624],{"class":844},[70,1984,1985],{"class":564,"line":706},[70,1986,838],{"emptyLinePlaceholder":837},[70,1988,1989,1992,1995,1998,2001,2003,2005,2007,2009],{"class":564,"line":712},[70,1990,1991],{"class":852},"        SIZE_T",[70,1993,1994],{"class":852}," Len",[70,1996,1997],{"class":844},"()",[70,1999,2000],{"class":956}," const",[70,2002,1821],{"class":844},[70,2004,1672],{"class":1587},[70,2006,1570],{"class":1004},[70,2008,1402],{"class":844},[70,2010,2011],{"class":844}," }\n",[70,2013,2014],{"class":564,"line":718},[70,2015,838],{"emptyLinePlaceholder":837},[70,2017,2018,2021],{"class":564,"line":723},[70,2019,2020],{"class":956},"    private",[70,2022,1517],{"class":844},[70,2024,2025,2028,2030,2032],{"class":564,"line":729},[70,2026,2027],{"class":852},"        WCHAR",[70,2029,1530],{"class":956},[70,2031,1550],{"class":1004},[70,2033,1335],{"class":844},[70,2035,2036,2038,2040],{"class":564,"line":735},[70,2037,1991],{"class":852},[70,2039,1560],{"class":1004},[70,2041,1335],{"class":844},[70,2043,2044,2046,2048],{"class":564,"line":741},[70,2045,1991],{"class":852},[70,2047,1570],{"class":1004},[70,2049,1335],{"class":844},[70,2051,2052],{"class":564,"line":746},[70,2053,2054],{"class":844},"    };\n",[17,2056,2057,2060,2061,2064],{},[41,2058,2059],{},"Buf"," is the replacement for ",[41,2062,2063],{},"swprintf_s"," on the lsass path. It has two rules:",[105,2066,2067,2077],{},[108,2068,2069,2070,2072,2073,2076],{},"It never writes past ",[41,2071,1594],{},". ",[41,2074,2075],{},"n_ + 1 \u003C cap_"," keeps one slot for the terminator, and every append writes the terminator again, so the buffer is a valid string after every call.",[108,2078,2079,2080,2082],{},"It truncates when the buffer is full. A log line that ends in the middle of a word is a cosmetic problem. ",[41,2081,2063],{}," on overflow calls the invalid-parameter handler, and that ends the process, which here is lsass.",[17,2084,2085,2088,2089,2092,2093,2095,2096,2099],{},[41,2086,2087],{},"U64"," writes the digits backwards into a small stack array and then copies them in the right order. ",[41,2090,2091],{},"t[21]"," is enough for the 20 digits of the largest ",[41,2094,1773],{},", and the function needs neither ",[41,2097,2098],{},"_ultow"," nor the locale or the heap.",[250,2101,2103],{"id":2102},"the-rid-as-a-value-name","The RID as a value name",[258,2105,2107],{"className":557,"code":2106,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F RID as a decimal string, same convention as Doppio's 2FA\\Rids values.\n    \u002F\u002F\n    \u002F\u002F Written out longhand rather than through Buf on purpose. Buf terminates\n    \u002F\u002F correctly, but \u002Fanalyze cannot see that across the call and reports\n    \u002F\u002F C6054 (\"might not be zero-terminated\") at every use of the result. With\n    \u002F\u002F \u002FWX that ends the build, and silencing a not-terminated warning in code\n    \u002F\u002F that runs inside lsass is the wrong habit to get into - so the\n    \u002F\u002F termination is made obvious here instead.\n    \u002F\u002F\n    \u002F\u002F A ULONG is at most 10 digits (4294967295), so out[12] always fits the\n    \u002F\u002F digits plus the terminator.\n    void RidToValueName(ULONG rid, WCHAR (&out)[12])\n    {\n        WCHAR digits[10];\n        int n = 0;\n        do\n        {\n            digits[n++] = (WCHAR)(L'0' + (ULONG)(rid % 10));\n            rid \u002F= 10;\n        } while (rid != 0 && n \u003C 10);\n\n        int i = 0;\n        while (n > 0) out[i++] = digits[--n];\n        out[i] = L'\\0';                       \u002F\u002F i \u003C= 10, so this is in bounds\n    }\n",[41,2108,2109,2114,2119,2124,2129,2134,2139,2144,2149,2153,2158,2163,2199,2203,2217,2231,2236,2240,2286,2297,2323,2327,2339,2377,2401],{"__ignoreMap":264},[70,2110,2111],{"class":564,"line":565},[70,2112,2113],{"class":568},"    \u002F\u002F RID as a decimal string, same convention as Doppio's 2FA\\Rids values.\n",[70,2115,2116],{"class":564,"line":572},[70,2117,2118],{"class":568},"    \u002F\u002F\n",[70,2120,2121],{"class":564,"line":578},[70,2122,2123],{"class":568},"    \u002F\u002F Written out longhand rather than through Buf on purpose. Buf terminates\n",[70,2125,2126],{"class":564,"line":584},[70,2127,2128],{"class":568},"    \u002F\u002F correctly, but \u002Fanalyze cannot see that across the call and reports\n",[70,2130,2131],{"class":564,"line":590},[70,2132,2133],{"class":568},"    \u002F\u002F C6054 (\"might not be zero-terminated\") at every use of the result. With\n",[70,2135,2136],{"class":564,"line":596},[70,2137,2138],{"class":568},"    \u002F\u002F \u002FWX that ends the build, and silencing a not-terminated warning in code\n",[70,2140,2141],{"class":564,"line":602},[70,2142,2143],{"class":568},"    \u002F\u002F that runs inside lsass is the wrong habit to get into - so the\n",[70,2145,2146],{"class":564,"line":607},[70,2147,2148],{"class":568},"    \u002F\u002F termination is made obvious here instead.\n",[70,2150,2151],{"class":564,"line":613},[70,2152,2118],{"class":568},[70,2154,2155],{"class":564,"line":619},[70,2156,2157],{"class":568},"    \u002F\u002F A ULONG is at most 10 digits (4294967295), so out[12] always fits the\n",[70,2159,2160],{"class":564,"line":625},[70,2161,2162],{"class":568},"    \u002F\u002F digits plus the terminator.\n",[70,2164,2165,2168,2171,2173,2175,2178,2180,2182,2184,2187,2190,2193,2196],{"class":564,"line":631},[70,2166,2167],{"class":956},"    void",[70,2169,2170],{"class":852}," RidToValueName",[70,2172,957],{"class":844},[70,2174,1855],{"class":852},[70,2176,2177],{"class":1004}," rid",[70,2179,1536],{"class":844},[70,2181,1644],{"class":852},[70,2183,1591],{"class":844},[70,2185,2186],{"class":848},"&",[70,2188,2189],{"class":1004},"out",[70,2191,2192],{"class":844},")[",[70,2194,2195],{"class":1014},"12",[70,2197,2198],{"class":844},"])\n",[70,2200,2201],{"class":564,"line":636},[70,2202,1509],{"class":844},[70,2204,2205,2207,2210,2212,2215],{"class":564,"line":642},[70,2206,2027],{"class":852},[70,2208,2209],{"class":1004}," digits",[70,2211,1601],{"class":844},[70,2213,2214],{"class":1014},"10",[70,2216,1798],{"class":844},[70,2218,2219,2222,2225,2227,2229],{"class":564,"line":648},[70,2220,2221],{"class":956},"        int",[70,2223,2224],{"class":1004}," n",[70,2226,1349],{"class":848},[70,2228,1811],{"class":1014},[70,2230,1335],{"class":844},[70,2232,2233],{"class":564,"line":654},[70,2234,2235],{"class":1587},"        do\n",[70,2237,2238],{"class":564,"line":659},[70,2239,1582],{"class":844},[70,2241,2242,2245,2247,2250,2252,2254,2256,2258,2260,2262,2264,2266,2268,2270,2272,2274,2276,2279,2281,2283],{"class":564,"line":665},[70,2243,2244],{"class":1004},"            digits",[70,2246,1601],{"class":844},[70,2248,2249],{"class":1004},"n",[70,2251,1712],{"class":848},[70,2253,1606],{"class":844},[70,2255,1349],{"class":848},[70,2257,1591],{"class":844},[70,2259,1527],{"class":1004},[70,2261,1841],{"class":844},[70,2263,1844],{"class":844},[70,2265,1575],{"class":867},[70,2267,1617],{"class":844},[70,2269,1693],{"class":848},[70,2271,1591],{"class":844},[70,2273,1855],{"class":1004},[70,2275,1841],{"class":844},[70,2277,2278],{"class":1004},"rid",[70,2280,1863],{"class":848},[70,2282,1866],{"class":1014},[70,2284,2285],{"class":844},"));\n",[70,2287,2288,2291,2293,2295],{"class":564,"line":671},[70,2289,2290],{"class":1004},"            rid",[70,2292,1874],{"class":848},[70,2294,1866],{"class":1014},[70,2296,1335],{"class":844},[70,2298,2299,2302,2304,2306,2308,2311,2313,2315,2317,2319,2321],{"class":564,"line":677},[70,2300,2301],{"class":844},"        }",[70,2303,1884],{"class":1587},[70,2305,1591],{"class":844},[70,2307,2278],{"class":1004},[70,2309,2310],{"class":848}," !=",[70,2312,1811],{"class":1014},[70,2314,1688],{"class":1587},[70,2316,2224],{"class":1004},[70,2318,864],{"class":848},[70,2320,1866],{"class":1014},[70,2322,1900],{"class":844},[70,2324,2325],{"class":564,"line":683},[70,2326,838],{"emptyLinePlaceholder":837},[70,2328,2329,2331,2333,2335,2337],{"class":564,"line":689},[70,2330,2221],{"class":956},[70,2332,1806],{"class":1004},[70,2334,1349],{"class":848},[70,2336,1811],{"class":1014},[70,2338,1335],{"class":844},[70,2340,2341,2344,2346,2348,2350,2352,2354,2357,2359,2361,2363,2365,2367,2369,2371,2373,2375],{"class":564,"line":695},[70,2342,2343],{"class":1587},"        while",[70,2345,1591],{"class":844},[70,2347,2249],{"class":1004},[70,2349,1911],{"class":848},[70,2351,1811],{"class":1014},[70,2353,1008],{"class":844},[70,2355,2356],{"class":1004}," out",[70,2358,1601],{"class":844},[70,2360,1828],{"class":1004},[70,2362,1712],{"class":848},[70,2364,1606],{"class":844},[70,2366,1349],{"class":848},[70,2368,2209],{"class":1004},[70,2370,1601],{"class":844},[70,2372,1946],{"class":848},[70,2374,2249],{"class":1004},[70,2376,1798],{"class":844},[70,2378,2379,2382,2384,2386,2388,2390,2392,2394,2396,2398],{"class":564,"line":701},[70,2380,2381],{"class":1004},"        out",[70,2383,1601],{"class":844},[70,2385,1828],{"class":1004},[70,2387,1606],{"class":844},[70,2389,1349],{"class":848},[70,2391,1611],{"class":844},[70,2393,1614],{"class":1014},[70,2395,1617],{"class":844},[70,2397,1402],{"class":844},[70,2399,2400],{"class":568},"                       \u002F\u002F i \u003C= 10, so this is in bounds\n",[70,2402,2403],{"class":564,"line":706},[70,2404,2405],{"class":844},"    }\n",[17,2407,2408,2409,2412],{},"Doppio writes its index values as the decimal RID, ",[41,2410,2411],{},"1001"," for the first local user after the built-in ones. The filter has to build the same string.",[17,2414,2415,2417,2418,2421,2422,2424,2425,2428],{},[41,2416,2059],{}," would do the job, but ",[41,2419,2420],{},"\u002Fanalyze"," objects. ",[41,2423,2059],{}," terminates its string, but the analyzer can't see that across the call and reports C6054, \"string might not be zero-terminated\", at every place that uses the result. With ",[41,2426,2427],{},"\u002FWX"," that's a broken build. The easy fix would be to suppress the warning, and in code that runs inside lsass that's a habit I don't want. So the function does it longhand, and the termination is visible in the function itself.",[17,2430,2431,2434],{},[41,2432,2433],{},"WCHAR (&out)[12]"," is a reference to an array of 12 characters. The compiler refuses any other buffer at the call site, which is a bounds check you get for free.",[250,2436,2438],{"id":2437},"time-without-the-crt","Time without the CRT",[258,2440,2442],{"className":557,"code":2441,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F ---- time --------------------------------------------------------\n    \u002F\u002F Identical to _time64(nullptr): UTC seconds since the unix epoch.\n    ULONGLONG NowUnix()\n    {\n        FILETIME ft;\n        GetSystemTimeAsFileTime(&ft);          \u002F\u002F UTC, no CRT\n        ULARGE_INTEGER u;\n        u.LowPart  = ft.dwLowDateTime;\n        u.HighPart = ft.dwHighDateTime;\n        \u002F\u002F FILETIME counts 100 ns ticks from 1601-01-01.\n        const ULONGLONG kTicksToEpoch = 116444736000000000ULL;\n        if (u.QuadPart \u003C kTicksToEpoch) return 0;\n        return (u.QuadPart - kTicksToEpoch) \u002F 10000000ULL;\n    }\n",[41,2443,2444,2449,2454,2465,2469,2479,2497,2507,2529,2549,2554,2575,2602,2632],{"__ignoreMap":264},[70,2445,2446],{"class":564,"line":565},[70,2447,2448],{"class":568},"    \u002F\u002F ---- time --------------------------------------------------------\n",[70,2450,2451],{"class":564,"line":572},[70,2452,2453],{"class":568},"    \u002F\u002F Identical to _time64(nullptr): UTC seconds since the unix epoch.\n",[70,2455,2456,2459,2462],{"class":564,"line":578},[70,2457,2458],{"class":852},"    ULONGLONG",[70,2460,2461],{"class":852}," NowUnix",[70,2463,2464],{"class":844},"()\n",[70,2466,2467],{"class":564,"line":584},[70,2468,1509],{"class":844},[70,2470,2471,2474,2477],{"class":564,"line":590},[70,2472,2473],{"class":852},"        FILETIME",[70,2475,2476],{"class":1004}," ft",[70,2478,1335],{"class":844},[70,2480,2481,2484,2486,2488,2491,2494],{"class":564,"line":596},[70,2482,2483],{"class":852},"        GetSystemTimeAsFileTime",[70,2485,957],{"class":844},[70,2487,2186],{"class":848},[70,2489,2490],{"class":1004},"ft",[70,2492,2493],{"class":844},");",[70,2495,2496],{"class":568},"          \u002F\u002F UTC, no CRT\n",[70,2498,2499,2502,2505],{"class":564,"line":602},[70,2500,2501],{"class":852},"        ULARGE_INTEGER",[70,2503,2504],{"class":1004}," u",[70,2506,1335],{"class":844},[70,2508,2509,2512,2514,2517,2520,2522,2524,2527],{"class":564,"line":607},[70,2510,2511],{"class":1004},"        u",[70,2513,25],{"class":844},[70,2515,2516],{"class":1004},"LowPart",[70,2518,2519],{"class":848},"  =",[70,2521,2476],{"class":1004},[70,2523,25],{"class":844},[70,2525,2526],{"class":1004},"dwLowDateTime",[70,2528,1335],{"class":844},[70,2530,2531,2533,2535,2538,2540,2542,2544,2547],{"class":564,"line":613},[70,2532,2511],{"class":1004},[70,2534,25],{"class":844},[70,2536,2537],{"class":1004},"HighPart",[70,2539,1349],{"class":848},[70,2541,2476],{"class":1004},[70,2543,25],{"class":844},[70,2545,2546],{"class":1004},"dwHighDateTime",[70,2548,1335],{"class":844},[70,2550,2551],{"class":564,"line":619},[70,2552,2553],{"class":568},"        \u002F\u002F FILETIME counts 100 ns ticks from 1601-01-01.\n",[70,2555,2556,2559,2562,2565,2567,2570,2573],{"class":564,"line":625},[70,2557,2558],{"class":956},"        const",[70,2560,2561],{"class":852}," ULONGLONG",[70,2563,2564],{"class":1004}," kTicksToEpoch",[70,2566,1349],{"class":848},[70,2568,2569],{"class":1014}," 116444736000000000",[70,2571,2572],{"class":867},"ULL",[70,2574,1335],{"class":844},[70,2576,2577,2580,2582,2585,2587,2590,2592,2594,2596,2598,2600],{"class":564,"line":631},[70,2578,2579],{"class":1587},"        if",[70,2581,1591],{"class":844},[70,2583,2584],{"class":1004},"u",[70,2586,25],{"class":844},[70,2588,2589],{"class":1004},"QuadPart",[70,2591,864],{"class":848},[70,2593,2564],{"class":1004},[70,2595,1008],{"class":844},[70,2597,1672],{"class":1587},[70,2599,1811],{"class":1014},[70,2601,1335],{"class":844},[70,2603,2604,2607,2609,2611,2613,2615,2618,2620,2622,2625,2628,2630],{"class":564,"line":636},[70,2605,2606],{"class":1587},"        return",[70,2608,1591],{"class":844},[70,2610,2584],{"class":1004},[70,2612,25],{"class":844},[70,2614,2589],{"class":1004},[70,2616,2617],{"class":848}," -",[70,2619,2564],{"class":1004},[70,2621,1008],{"class":844},[70,2623,2624],{"class":848}," \u002F",[70,2626,2627],{"class":1014}," 10000000",[70,2629,2572],{"class":867},[70,2631,1335],{"class":844},[70,2633,2634],{"class":564,"line":642},[70,2635,2405],{"class":844},[17,2637,2638,2641,2642,2645,2646,2649,2650,2652],{},[41,2639,2640],{},"FILETIME"," counts 100-nanosecond ticks since 1 January 1601. ",[41,2643,2644],{},"116444736000000000"," is the number of those ticks between 1601 and 1970, so subtracting it and dividing by ten million gives unix seconds. That's the value ",[41,2647,2648],{},"_time64(nullptr)"," returns, without the CRT. ",[41,2651,302],{}," uses the identical function, so both sides agree on what \"now\" means.",[17,2654,2655],{},"A system clock before 1970 returns 0. You won't see that on a real machine, but an unsigned subtraction that wraps around would produce a gate timestamp in the far future, so the guard costs one comparison and closes the case.",[250,2657,2659],{"id":2658},"the-config-cache","The config cache",[258,2661,2663],{"className":557,"code":2662,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F ---- config cache ------------------------------------------------\n    \u002F\u002F Several logon threads run this concurrently. Aligned 32\u002F64-bit loads\n    \u002F\u002F are atomic on x64, and the refresh slot is claimed with an interlocked\n    \u002F\u002F compare-exchange so only one thread does the registry read.\n    volatile LONG64 g_nextCfgTick = 0;\n    volatile LONG   g_windowSec   = (LONG)kWindowDefault;\n    volatile LONG   g_enforce     = 1;      \u002F\u002F fail closed until told otherwise\n\n    void ReadConfigThrottled()\n    {\n        const LONG64 now  = (LONG64)GetTickCount64();\n        const LONG64 next = InterlockedCompareExchange64(&g_nextCfgTick, 0, 0);\n        if (now \u003C next) return;\n\n        \u002F\u002F Loser of the race keeps using the cached values - never blocks.\n        if (InterlockedCompareExchange64(&g_nextCfgTick, now + kConfigRefreshMs, next) != next)\n            return;\n\n        DWORD v = 0, cb = sizeof(v);\n        if (RegGetValueW(HKEY_LOCAL_MACHINE, kConfigPath, L\"WindowSec\",\n                         RRF_RT_REG_DWORD, nullptr, &v, &cb) == ERROR_SUCCESS)\n        {\n            if (v \u003C kWindowMin) v = kWindowMin;\n            if (v > kWindowMax) v = kWindowMax;\n            InterlockedExchange(&g_windowSec, (LONG)v);\n        }\n\n        cb = sizeof(v);\n        if (RegGetValueW(HKEY_LOCAL_MACHINE, kConfigPath, L\"Mode\",\n                         RRF_RT_REG_DWORD, nullptr, &v, &cb) == ERROR_SUCCESS)\n            InterlockedExchange(&g_enforce, v ? 1 : 0);\n        \u002F\u002F No value \u002F unreadable -> keep the previous (enforcing) state.\n    }\n",[41,2664,2665,2670,2675,2680,2685,2702,2726,2744,2748,2757,2761,2785,2816,2835,2839,2844,2879,2886,2890,2917,2946,2980,2984,3006,3028,3052,3056,3060,3075,3102,3130,3156,3161],{"__ignoreMap":264},[70,2666,2667],{"class":564,"line":565},[70,2668,2669],{"class":568},"    \u002F\u002F ---- config cache ------------------------------------------------\n",[70,2671,2672],{"class":564,"line":572},[70,2673,2674],{"class":568},"    \u002F\u002F Several logon threads run this concurrently. Aligned 32\u002F64-bit loads\n",[70,2676,2677],{"class":564,"line":578},[70,2678,2679],{"class":568},"    \u002F\u002F are atomic on x64, and the refresh slot is claimed with an interlocked\n",[70,2681,2682],{"class":564,"line":584},[70,2683,2684],{"class":568},"    \u002F\u002F compare-exchange so only one thread does the registry read.\n",[70,2686,2687,2690,2693,2696,2698,2700],{"class":564,"line":590},[70,2688,2689],{"class":956},"    volatile",[70,2691,2692],{"class":1004}," LONG64",[70,2694,2695],{"class":1004}," g_nextCfgTick",[70,2697,1349],{"class":848},[70,2699,1811],{"class":1014},[70,2701,1335],{"class":844},[70,2703,2704,2706,2709,2712,2714,2716,2719,2721,2724],{"class":564,"line":596},[70,2705,2689],{"class":956},[70,2707,2708],{"class":1004}," LONG",[70,2710,2711],{"class":1004},"   g_windowSec",[70,2713,1309],{"class":848},[70,2715,1591],{"class":844},[70,2717,2718],{"class":1004},"LONG",[70,2720,1008],{"class":844},[70,2722,2723],{"class":1004},"kWindowDefault",[70,2725,1335],{"class":844},[70,2727,2728,2730,2732,2735,2737,2739,2741],{"class":564,"line":602},[70,2729,2689],{"class":956},[70,2731,2708],{"class":1004},[70,2733,2734],{"class":1004},"   g_enforce",[70,2736,1417],{"class":848},[70,2738,1696],{"class":1014},[70,2740,1402],{"class":844},[70,2742,2743],{"class":568},"      \u002F\u002F fail closed until told otherwise\n",[70,2745,2746],{"class":564,"line":607},[70,2747,838],{"emptyLinePlaceholder":837},[70,2749,2750,2752,2755],{"class":564,"line":613},[70,2751,2167],{"class":956},[70,2753,2754],{"class":852}," ReadConfigThrottled",[70,2756,2464],{"class":844},[70,2758,2759],{"class":564,"line":619},[70,2760,1509],{"class":844},[70,2762,2763,2765,2767,2770,2772,2774,2777,2779,2782],{"class":564,"line":625},[70,2764,2558],{"class":956},[70,2766,2692],{"class":852},[70,2768,2769],{"class":1004}," now",[70,2771,2519],{"class":848},[70,2773,1591],{"class":844},[70,2775,2776],{"class":1004},"LONG64",[70,2778,1008],{"class":844},[70,2780,2781],{"class":852},"GetTickCount64",[70,2783,2784],{"class":844},"();\n",[70,2786,2787,2789,2791,2794,2796,2799,2801,2803,2806,2808,2810,2812,2814],{"class":564,"line":631},[70,2788,2558],{"class":956},[70,2790,2692],{"class":852},[70,2792,2793],{"class":1004}," next",[70,2795,1349],{"class":848},[70,2797,2798],{"class":852}," InterlockedCompareExchange64",[70,2800,957],{"class":844},[70,2802,2186],{"class":848},[70,2804,2805],{"class":1004},"g_nextCfgTick",[70,2807,1536],{"class":844},[70,2809,1811],{"class":1014},[70,2811,1536],{"class":844},[70,2813,1811],{"class":1014},[70,2815,1900],{"class":844},[70,2817,2818,2820,2822,2825,2827,2829,2831,2833],{"class":564,"line":636},[70,2819,2579],{"class":1587},[70,2821,1591],{"class":844},[70,2823,2824],{"class":1004},"now",[70,2826,864],{"class":848},[70,2828,2793],{"class":1004},[70,2830,1008],{"class":844},[70,2832,1672],{"class":1587},[70,2834,1335],{"class":844},[70,2836,2837],{"class":564,"line":642},[70,2838,838],{"emptyLinePlaceholder":837},[70,2840,2841],{"class":564,"line":648},[70,2842,2843],{"class":568},"        \u002F\u002F Loser of the race keeps using the cached values - never blocks.\n",[70,2845,2846,2848,2850,2853,2855,2857,2859,2861,2863,2865,2867,2869,2871,2873,2875,2877],{"class":564,"line":654},[70,2847,2579],{"class":1587},[70,2849,1591],{"class":844},[70,2851,2852],{"class":852},"InterlockedCompareExchange64",[70,2854,957],{"class":844},[70,2856,2186],{"class":848},[70,2858,2805],{"class":1004},[70,2860,1536],{"class":844},[70,2862,2769],{"class":1004},[70,2864,1693],{"class":848},[70,2866,1454],{"class":1004},[70,2868,1536],{"class":844},[70,2870,2793],{"class":1004},[70,2872,1008],{"class":844},[70,2874,2310],{"class":848},[70,2876,2793],{"class":1004},[70,2878,972],{"class":844},[70,2880,2881,2884],{"class":564,"line":659},[70,2882,2883],{"class":1587},"            return",[70,2885,1335],{"class":844},[70,2887,2888],{"class":564,"line":665},[70,2889,838],{"emptyLinePlaceholder":837},[70,2891,2892,2895,2897,2899,2901,2903,2906,2908,2911,2913,2915],{"class":564,"line":671},[70,2893,2894],{"class":852},"        DWORD",[70,2896,1776],{"class":1004},[70,2898,1349],{"class":848},[70,2900,1811],{"class":1014},[70,2902,1536],{"class":844},[70,2904,2905],{"class":1004}," cb",[70,2907,1349],{"class":848},[70,2909,2910],{"class":848}," sizeof",[70,2912,957],{"class":844},[70,2914,1860],{"class":1004},[70,2916,1900],{"class":844},[70,2918,2919,2921,2923,2926,2928,2930,2932,2934,2936,2938,2941,2943],{"class":564,"line":677},[70,2920,2579],{"class":1587},[70,2922,1591],{"class":844},[70,2924,2925],{"class":852},"RegGetValueW",[70,2927,957],{"class":844},[70,2929,1467],{"class":1004},[70,2931,1536],{"class":844},[70,2933,1344],{"class":1004},[70,2935,1536],{"class":844},[70,2937,1312],{"class":844},[70,2939,2940],{"class":867},"WindowSec",[70,2942,934],{"class":844},[70,2944,2945],{"class":844},",\n",[70,2947,2948,2951,2953,2956,2958,2961,2963,2965,2967,2970,2972,2975,2978],{"class":564,"line":683},[70,2949,2950],{"class":1004},"                         RRF_RT_REG_DWORD",[70,2952,1536],{"class":844},[70,2954,2955],{"class":1014}," nullptr",[70,2957,1536],{"class":844},[70,2959,2960],{"class":848}," &",[70,2962,1860],{"class":1004},[70,2964,1536],{"class":844},[70,2966,2960],{"class":848},[70,2968,2969],{"class":1004},"cb",[70,2971,1008],{"class":844},[70,2973,2974],{"class":848}," ==",[70,2976,2977],{"class":1004}," ERROR_SUCCESS",[70,2979,972],{"class":844},[70,2981,2982],{"class":564,"line":689},[70,2983,1582],{"class":844},[70,2985,2986,2988,2990,2992,2994,2996,2998,3000,3002,3004],{"class":564,"line":695},[70,2987,1588],{"class":1587},[70,2989,1591],{"class":844},[70,2991,1860],{"class":1004},[70,2993,864],{"class":848},[70,2995,1414],{"class":1004},[70,2997,1008],{"class":844},[70,2999,1776],{"class":1004},[70,3001,1349],{"class":848},[70,3003,1414],{"class":1004},[70,3005,1335],{"class":844},[70,3007,3008,3010,3012,3014,3016,3018,3020,3022,3024,3026],{"class":564,"line":701},[70,3009,1588],{"class":1587},[70,3011,1591],{"class":844},[70,3013,1860],{"class":1004},[70,3015,1911],{"class":848},[70,3017,1431],{"class":1004},[70,3019,1008],{"class":844},[70,3021,1776],{"class":1004},[70,3023,1349],{"class":848},[70,3025,1431],{"class":1004},[70,3027,1335],{"class":844},[70,3029,3030,3033,3035,3037,3040,3042,3044,3046,3048,3050],{"class":564,"line":706},[70,3031,3032],{"class":852},"            InterlockedExchange",[70,3034,957],{"class":844},[70,3036,2186],{"class":848},[70,3038,3039],{"class":1004},"g_windowSec",[70,3041,1536],{"class":844},[70,3043,1591],{"class":844},[70,3045,2718],{"class":1004},[70,3047,1008],{"class":844},[70,3049,1860],{"class":1004},[70,3051,1900],{"class":844},[70,3053,3054],{"class":564,"line":712},[70,3055,1624],{"class":844},[70,3057,3058],{"class":564,"line":718},[70,3059,838],{"emptyLinePlaceholder":837},[70,3061,3062,3065,3067,3069,3071,3073],{"class":564,"line":723},[70,3063,3064],{"class":1004},"        cb",[70,3066,1349],{"class":848},[70,3068,2910],{"class":848},[70,3070,957],{"class":844},[70,3072,1860],{"class":1004},[70,3074,1900],{"class":844},[70,3076,3077,3079,3081,3083,3085,3087,3089,3091,3093,3095,3098,3100],{"class":564,"line":729},[70,3078,2579],{"class":1587},[70,3080,1591],{"class":844},[70,3082,2925],{"class":852},[70,3084,957],{"class":844},[70,3086,1467],{"class":1004},[70,3088,1536],{"class":844},[70,3090,1344],{"class":1004},[70,3092,1536],{"class":844},[70,3094,1312],{"class":844},[70,3096,3097],{"class":867},"Mode",[70,3099,934],{"class":844},[70,3101,2945],{"class":844},[70,3103,3104,3106,3108,3110,3112,3114,3116,3118,3120,3122,3124,3126,3128],{"class":564,"line":735},[70,3105,2950],{"class":1004},[70,3107,1536],{"class":844},[70,3109,2955],{"class":1014},[70,3111,1536],{"class":844},[70,3113,2960],{"class":848},[70,3115,1860],{"class":1004},[70,3117,1536],{"class":844},[70,3119,2960],{"class":848},[70,3121,2969],{"class":1004},[70,3123,1008],{"class":844},[70,3125,2974],{"class":848},[70,3127,2977],{"class":1004},[70,3129,972],{"class":844},[70,3131,3132,3134,3136,3138,3141,3143,3145,3148,3150,3152,3154],{"class":564,"line":741},[70,3133,3032],{"class":852},[70,3135,957],{"class":844},[70,3137,2186],{"class":848},[70,3139,3140],{"class":1004},"g_enforce",[70,3142,1536],{"class":844},[70,3144,1776],{"class":1004},[70,3146,3147],{"class":848}," ?",[70,3149,1696],{"class":1014},[70,3151,1547],{"class":848},[70,3153,1811],{"class":1014},[70,3155,1900],{"class":844},[70,3157,3158],{"class":564,"line":746},[70,3159,3160],{"class":568},"        \u002F\u002F No value \u002F unreadable -> keep the previous (enforcing) state.\n",[70,3162,3163],{"class":564,"line":752},[70,3164,2405],{"class":844},[17,3166,3167],{},"This runs on every network logon, from several lsass threads at the same time. Reading the registry on every logon would work, but it's two more registry calls on a hot path for a value that changes maybe once a month.",[17,3169,3170],{},"The filter caches the config and refreshes it at most every five seconds:",[105,3172,3173,3182,3185,3195],{},[108,3174,3175,3177,3178,3181],{},[41,3176,2805],{}," holds the tick count when the next refresh is due. ",[41,3179,3180],{},"InterlockedCompareExchange64(&x, 0, 0)"," is the usual trick for an atomic read: compare with 0, and if equal write 0, which changes nothing.",[108,3183,3184],{},"The thread that sees the refresh is due tries to claim it with a compare-exchange from the old value to the new deadline. One thread wins. Every other thread returns and uses the cached values. Nobody waits, which matters on a logon path.",[108,3186,3187,3188,3190,3191,3194],{},"The filter clamps ",[41,3189,2940],{}," when it reads it. A value of 0 becomes 30, ",[41,3192,3193],{},"0xFFFFFFFF"," becomes 3600.",[108,3196,3197,3199,3200,3202],{},[41,3198,3140],{}," starts at 1. If ",[41,3201,3097],{}," is missing or unreadable, the filter keeps what it had, and on a fresh lsass that's \"enforce\". You have to set the kill switch on purpose.",[17,3204,3205],{},"On x64 an aligned 32-bit load is atomic, so the first version without the interlocked calls didn't have an exploitable race. The audit flagged it as incorrect, and the interlocked calls fix that.",[250,3207,3209],{"id":3208},"enrollment-in-three-outcomes","Enrollment in three outcomes",[258,3211,3213],{"className":557,"code":3212,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F ---- enrollment ----------------------------------------------------\n    \u002F\u002F tac::EnrollmentByRid (Doppio, store_index.cpp) has the three outcomes\n    \u002F\u002F this filter needs, with exactly the semantics the old hand copy had:\n    \u002F\u002F\n    \u002F\u002F   Rids key absent        -> No       Doppio is not installed: allow\n    \u002F\u002F   value absent           -> No       genuinely not enrolled: allow\n    \u002F\u002F   any other read error   -> Unknown  refuse this one account\n    \u002F\u002F\n    \u002F\u002F Keeping \"key absent\" permissive is deliberate (SECURITY-FIXES.md, item\n    \u002F\u002F 3): failing closed there would deny every network logon on the machine\n    \u002F\u002F the moment Doppio is uninstalled.\n",[41,3214,3215,3220,3225,3230,3234,3239,3244,3249,3253,3258,3263],{"__ignoreMap":264},[70,3216,3217],{"class":564,"line":565},[70,3218,3219],{"class":568},"    \u002F\u002F ---- enrollment ----------------------------------------------------\n",[70,3221,3222],{"class":564,"line":572},[70,3223,3224],{"class":568},"    \u002F\u002F tac::EnrollmentByRid (Doppio, store_index.cpp) has the three outcomes\n",[70,3226,3227],{"class":564,"line":578},[70,3228,3229],{"class":568},"    \u002F\u002F this filter needs, with exactly the semantics the old hand copy had:\n",[70,3231,3232],{"class":564,"line":584},[70,3233,2118],{"class":568},[70,3235,3236],{"class":564,"line":590},[70,3237,3238],{"class":568},"    \u002F\u002F   Rids key absent        -> No       Doppio is not installed: allow\n",[70,3240,3241],{"class":564,"line":596},[70,3242,3243],{"class":568},"    \u002F\u002F   value absent           -> No       genuinely not enrolled: allow\n",[70,3245,3246],{"class":564,"line":602},[70,3247,3248],{"class":568},"    \u002F\u002F   any other read error   -> Unknown  refuse this one account\n",[70,3250,3251],{"class":564,"line":607},[70,3252,2118],{"class":568},[70,3254,3255],{"class":564,"line":613},[70,3256,3257],{"class":568},"    \u002F\u002F Keeping \"key absent\" permissive is deliberate (SECURITY-FIXES.md, item\n",[70,3259,3260],{"class":564,"line":619},[70,3261,3262],{"class":568},"    \u002F\u002F 3): failing closed there would deny every network logon on the machine\n",[70,3264,3265],{"class":564,"line":625},[70,3266,3267],{"class":568},"    \u002F\u002F the moment Doppio is uninstalled.\n",[17,3269,3270,3271,3274,3275,3278,3279,3282],{},"Only a comment is left here. The function behind it changed most during the audit. The original ",[41,3272,3273],{},"IsEnrolledByRid"," returns a ",[41,3276,3277],{},"bool",", so every failure becomes \"not enrolled\". In a filter whose whole job is the veto, \"not enrolled\" means ",[41,3280,3281],{},"STATUS_SUCCESS",". One unexpected registry error and an enrolled account walks past the gate.",[17,3284,3285,3286,3289,3290,3292,3293,3295,3296,3298,3299,3301],{},"My fix was a function ",[41,3287,3288],{},"EnrollmentOf"," in this file, with three results instead of two. Doppio's own filter had the same hole, and after its second review Doppio has the same three results in ",[41,3291,1213],{},". The code is in ",[21,3294,24],{"href":23},", in ",[41,3297,432],{},". Ristretto calls it, and ",[41,3300,3288],{}," is gone:",[271,3303,3304,3319],{},[274,3305,3306],{},[277,3307,3308,3311,3316],{},[280,3309,3310],{},"What the registry says",[280,3312,3313],{},[41,3314,3315],{},"tac::Enrollment",[280,3317,3318],{},"What the filter does",[290,3320,3321,3337,3349],{},[277,3322,3323,3329,3334],{},[295,3324,3325,3328],{},[41,3326,3327],{},"Rids"," key missing",[295,3330,3331],{},[41,3332,3333],{},"No",[295,3335,3336],{},"allow, Doppio isn't installed, nothing of ours to enforce",[277,3338,3339,3342,3346],{},[295,3340,3341],{},"value for this RID missing",[295,3343,3344],{},[41,3345,3333],{},[295,3347,3348],{},"allow, not enrolled",[277,3350,3351,3354,3359],{},[295,3352,3353],{},"anything else that fails",[295,3355,3356],{},[41,3357,3358],{},"Unknown",[295,3360,3361],{},"refuse that one account",[17,3363,3364,3365,3368],{},"The first row is deliberate and the only one I'm still a bit unhappy with. Failing closed there would deny ",[36,3366,3367],{},"every"," network logon on the machine the moment somebody uninstalls Doppio. People rip out a security tool that bricks file sharing when you remove it. So a missing index allows the logon, and a broken read refuses it.",[17,3370,3371,3372,3375,3376,3379,3380,3383],{},"Doppio's ",[41,3373,3374],{},"QueryIndex"," calls ",[41,3377,3378],{},"RegQueryValueExW"," with all pointers ",[41,3381,3382],{},"nullptr",". That only asks \"does this value exist\". It doesn't read the data, because the value is a presence marker and nothing more.",[250,3385,3387],{"id":3386},"is-the-gate-fresh","Is the gate fresh?",[258,3389,3391],{"className":557,"code":3390,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F ---- gate ----------------------------------------------------------\n    bool GateFresh(ULONG rid, ULONG& ageSec)\n    {\n        ageSec = 0;\n\n        WCHAR value[12] = {};\n        RidToValueName(rid, value);\n\n        ULONGLONG ts = 0;\n        DWORD cb = sizeof(ts);\n        if (RegGetValueW(HKEY_LOCAL_MACHINE, kGatePath, value,\n                         RRF_RT_REG_QWORD, nullptr, &ts, &cb) != ERROR_SUCCESS)\n            return false;               \u002F\u002F missing or unreadable -> closed\n\n        const ULONGLONG now = NowUnix();\n        if (now \u003C ts) return false;     \u002F\u002F clock moved back -> locked\n\n        const ULONGLONG age = now - ts;\n        ageSec = (ULONG)(age > 0xFFFFFFFFull ? 0xFFFFFFFFull : age);\n\n        const ULONG window = (ULONG)InterlockedCompareExchange(&g_windowSec, 0, 0);\n        return age \u003C= (ULONGLONG)window;\n    }\n",[41,3392,3393,3398,3424,3428,3439,3443,3461,3476,3480,3494,3511,3533,3562,3574,3578,3592,3615,3619,3638,3678,3682,3718,3738],{"__ignoreMap":264},[70,3394,3395],{"class":564,"line":565},[70,3396,3397],{"class":568},"    \u002F\u002F ---- gate ----------------------------------------------------------\n",[70,3399,3400,3403,3406,3408,3410,3412,3414,3417,3419,3422],{"class":564,"line":572},[70,3401,3402],{"class":956},"    bool",[70,3404,3405],{"class":852}," GateFresh",[70,3407,957],{"class":844},[70,3409,1855],{"class":852},[70,3411,2177],{"class":1004},[70,3413,1536],{"class":844},[70,3415,3416],{"class":852}," ULONG",[70,3418,2186],{"class":956},[70,3420,3421],{"class":1004}," ageSec",[70,3423,972],{"class":844},[70,3425,3426],{"class":564,"line":578},[70,3427,1509],{"class":844},[70,3429,3430,3433,3435,3437],{"class":564,"line":584},[70,3431,3432],{"class":1004},"        ageSec",[70,3434,1349],{"class":848},[70,3436,1811],{"class":1014},[70,3438,1335],{"class":844},[70,3440,3441],{"class":564,"line":590},[70,3442,838],{"emptyLinePlaceholder":837},[70,3444,3445,3447,3450,3452,3454,3456,3458],{"class":564,"line":596},[70,3446,2027],{"class":1004},[70,3448,3449],{"class":1004}," value",[70,3451,1601],{"class":844},[70,3453,2195],{"class":1014},[70,3455,1606],{"class":844},[70,3457,1349],{"class":848},[70,3459,3460],{"class":844}," {};\n",[70,3462,3463,3466,3468,3470,3472,3474],{"class":564,"line":602},[70,3464,3465],{"class":852},"        RidToValueName",[70,3467,957],{"class":844},[70,3469,2278],{"class":1004},[70,3471,1536],{"class":844},[70,3473,3449],{"class":1004},[70,3475,1900],{"class":844},[70,3477,3478],{"class":564,"line":607},[70,3479,838],{"emptyLinePlaceholder":837},[70,3481,3482,3485,3488,3490,3492],{"class":564,"line":613},[70,3483,3484],{"class":852},"        ULONGLONG",[70,3486,3487],{"class":1004}," ts",[70,3489,1349],{"class":848},[70,3491,1811],{"class":1014},[70,3493,1335],{"class":844},[70,3495,3496,3498,3500,3502,3504,3506,3509],{"class":564,"line":619},[70,3497,2894],{"class":852},[70,3499,2905],{"class":1004},[70,3501,1349],{"class":848},[70,3503,2910],{"class":848},[70,3505,957],{"class":844},[70,3507,3508],{"class":1004},"ts",[70,3510,1900],{"class":844},[70,3512,3513,3515,3517,3519,3521,3523,3525,3527,3529,3531],{"class":564,"line":625},[70,3514,2579],{"class":1587},[70,3516,1591],{"class":844},[70,3518,2925],{"class":852},[70,3520,957],{"class":844},[70,3522,1467],{"class":1004},[70,3524,1536],{"class":844},[70,3526,1303],{"class":1004},[70,3528,1536],{"class":844},[70,3530,3449],{"class":1004},[70,3532,2945],{"class":844},[70,3534,3535,3538,3540,3542,3544,3546,3548,3550,3552,3554,3556,3558,3560],{"class":564,"line":631},[70,3536,3537],{"class":1004},"                         RRF_RT_REG_QWORD",[70,3539,1536],{"class":844},[70,3541,2955],{"class":1014},[70,3543,1536],{"class":844},[70,3545,2960],{"class":848},[70,3547,3508],{"class":1004},[70,3549,1536],{"class":844},[70,3551,2960],{"class":848},[70,3553,2969],{"class":1004},[70,3555,1008],{"class":844},[70,3557,2310],{"class":848},[70,3559,2977],{"class":1004},[70,3561,972],{"class":844},[70,3563,3564,3566,3569,3571],{"class":564,"line":636},[70,3565,2883],{"class":1587},[70,3567,3568],{"class":1014}," false",[70,3570,1402],{"class":844},[70,3572,3573],{"class":568},"               \u002F\u002F missing or unreadable -> closed\n",[70,3575,3576],{"class":564,"line":642},[70,3577,838],{"emptyLinePlaceholder":837},[70,3579,3580,3582,3584,3586,3588,3590],{"class":564,"line":648},[70,3581,2558],{"class":956},[70,3583,2561],{"class":852},[70,3585,2769],{"class":1004},[70,3587,1349],{"class":848},[70,3589,2461],{"class":852},[70,3591,2784],{"class":844},[70,3593,3594,3596,3598,3600,3602,3604,3606,3608,3610,3612],{"class":564,"line":654},[70,3595,2579],{"class":1587},[70,3597,1591],{"class":844},[70,3599,2824],{"class":1004},[70,3601,864],{"class":848},[70,3603,3487],{"class":1004},[70,3605,1008],{"class":844},[70,3607,1672],{"class":1587},[70,3609,3568],{"class":1014},[70,3611,1402],{"class":844},[70,3613,3614],{"class":568},"     \u002F\u002F clock moved back -> locked\n",[70,3616,3617],{"class":564,"line":659},[70,3618,838],{"emptyLinePlaceholder":837},[70,3620,3621,3623,3625,3628,3630,3632,3634,3636],{"class":564,"line":665},[70,3622,2558],{"class":956},[70,3624,2561],{"class":852},[70,3626,3627],{"class":1004}," age",[70,3629,1349],{"class":848},[70,3631,2769],{"class":1004},[70,3633,2617],{"class":848},[70,3635,3487],{"class":1004},[70,3637,1335],{"class":844},[70,3639,3640,3642,3644,3646,3648,3650,3653,3655,3658,3661,3664,3666,3668,3670,3672,3674,3676],{"class":564,"line":671},[70,3641,3432],{"class":1004},[70,3643,1349],{"class":848},[70,3645,1591],{"class":844},[70,3647,1855],{"class":1004},[70,3649,1841],{"class":844},[70,3651,3652],{"class":1004},"age",[70,3654,1911],{"class":848},[70,3656,3657],{"class":867}," 0x",[70,3659,3660],{"class":1014},"FFFFFFFF",[70,3662,3663],{"class":867},"ull",[70,3665,3147],{"class":848},[70,3667,3657],{"class":867},[70,3669,3660],{"class":1014},[70,3671,3663],{"class":867},[70,3673,1547],{"class":848},[70,3675,3627],{"class":1004},[70,3677,1900],{"class":844},[70,3679,3680],{"class":564,"line":677},[70,3681,838],{"emptyLinePlaceholder":837},[70,3683,3684,3686,3688,3691,3693,3695,3697,3699,3702,3704,3706,3708,3710,3712,3714,3716],{"class":564,"line":683},[70,3685,2558],{"class":956},[70,3687,3416],{"class":852},[70,3689,3690],{"class":1004}," window",[70,3692,1349],{"class":848},[70,3694,1591],{"class":844},[70,3696,1855],{"class":1004},[70,3698,1008],{"class":844},[70,3700,3701],{"class":852},"InterlockedCompareExchange",[70,3703,957],{"class":844},[70,3705,2186],{"class":848},[70,3707,3039],{"class":1004},[70,3709,1536],{"class":844},[70,3711,1811],{"class":1014},[70,3713,1536],{"class":844},[70,3715,1811],{"class":1014},[70,3717,1900],{"class":844},[70,3719,3720,3722,3724,3727,3729,3731,3733,3736],{"class":564,"line":689},[70,3721,2606],{"class":1587},[70,3723,3627],{"class":1004},[70,3725,3726],{"class":848}," \u003C=",[70,3728,1591],{"class":844},[70,3730,1773],{"class":1004},[70,3732,1008],{"class":844},[70,3734,3735],{"class":1004},"window",[70,3737,1335],{"class":844},[70,3739,3740],{"class":564,"line":695},[70,3741,2405],{"class":844},[17,3743,3744,3745,3748],{},"One registry read, ",[41,3746,3747],{},"RRF_RT_REG_QWORD"," so a value of the wrong type fails instead of being misread. Then three cases:",[105,3750,3751,3754,3757],{},[108,3752,3753],{},"Missing or unreadable: closed.",[108,3755,3756],{},"Timestamp in the future: closed. The clock went backwards since you opened the gate, by NTP, by hand or by a snapshot restore, and the filter treats that skew as closed.",[108,3758,3759,3760,25],{},"Otherwise: open if ",[41,3761,3762],{},"age \u003C= window",[17,3764,3765,3768],{},[41,3766,3767],{},"ageSec"," is for the log line, clamped to 32 bits so a gate from last year prints a number too.",[250,3770,3772],{"id":3771},"logging","Logging",[258,3774,3776],{"className":557,"code":3775,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F SAM name, for the log only. The decision runs on the RID alone.\n    void SamName(const USER_ALL_INFORMATION* ua, WCHAR (&out)[129])\n    {\n        out[0] = L'?';\n        out[1] = L'\\0';\n        if (!ua) return;\n\n        const UNICODE_STRING& us = ua->UserName;\n        if (!us.Buffer || us.Length == 0 || (us.Length % sizeof(WCHAR)) != 0)\n            return;\n\n        SIZE_T cch = us.Length \u002F sizeof(WCHAR);\n        if (cch > 128) cch = 128;\n        for (SIZE_T i = 0; i \u003C cch; ++i)\n        {\n            WCHAR c = us.Buffer[i];\n            if (c == L'\\0') { cch = i; break; }\n            \u002F\u002F Keep control characters out of the log line.\n            out[i] = (c \u003C 0x20) ? L'.' : c;\n        }\n        out[cch] = L'\\0';\n    }\n\n    void Debug(const WCHAR* text)\n    {\n        WCHAR line[384];\n        Buf b(line, ARRAYSIZE(line));\n        b.Str(L\"[Ristretto] \");\n        b.Str(text);\n        b.Str(L\"\\n\");\n        OutputDebugStringW(line);\n    }\n\n    void Report(WORD type, DWORD id, const WCHAR* text)\n    {\n        Debug(text);\n#if RISTRETTO_FILTER_EVENTLOG\n        HANDLE h = RegisterEventSourceW(nullptr, L\"TheAdminCafe Ristretto\");\n        if (h)\n        {\n            LPCWSTR s[1] = { text };\n            ReportEventW(h, type, 0, id, nullptr, 1, 0, s, nullptr);\n            DeregisterEventSource(h);\n        }\n#else\n        (void)type; (void)id;\n#endif\n    }\n\n    void ReportVeto(ULONG rid, const WCHAR* name, ULONG ageSec, const WCHAR* why)\n    {\n        WCHAR text[320];\n        Buf b(text, ARRAYSIZE(text));\n        b.Str(L\"Refused a network logon for enrolled account '\");\n        b.Str(name);\n        b.Str(L\"' (RID \");\n        b.U64(rid);\n        b.Str(L\"): \");\n        b.Str(why);\n        b.Str(L\" (last unlock \");\n        b.U64(ageSec);\n        b.Str(L\" s ago or none).\");\n        Report(EVENTLOG_WARNING_TYPE, 301, text);\n    }\n",[41,3777,3778,3783,3819,3823,3844,3865,3882,3886,3910,3967,3973,3977,4002,4026,4059,4063,4084,4120,4125,4165,4169,4189,4193,4197,4217,4221,4235,4257,4279,4293,4312,4323,4327,4331,4365,4369,4380,4390,4418,4429,4433,4455,4498,4509,4513,4520,4546,4552,4556,4560,4603,4607,4620,4640,4659,4674,4693,4707,4726,4741,4760,4774,4793,4814],{"__ignoreMap":264},[70,3779,3780],{"class":564,"line":565},[70,3781,3782],{"class":568},"    \u002F\u002F SAM name, for the log only. The decision runs on the RID alone.\n",[70,3784,3785,3787,3790,3792,3794,3797,3799,3802,3804,3806,3808,3810,3812,3814,3817],{"class":564,"line":572},[70,3786,2167],{"class":956},[70,3788,3789],{"class":852}," SamName",[70,3791,957],{"class":844},[70,3793,1641],{"class":956},[70,3795,3796],{"class":852}," USER_ALL_INFORMATION",[70,3798,1530],{"class":956},[70,3800,3801],{"class":1004}," ua",[70,3803,1536],{"class":844},[70,3805,1644],{"class":852},[70,3807,1591],{"class":844},[70,3809,2186],{"class":848},[70,3811,2189],{"class":1004},[70,3813,2192],{"class":844},[70,3815,3816],{"class":1014},"129",[70,3818,2198],{"class":844},[70,3820,3821],{"class":564,"line":578},[70,3822,1509],{"class":844},[70,3824,3825,3827,3829,3831,3833,3835,3837,3840,3842],{"class":564,"line":584},[70,3826,2381],{"class":1004},[70,3828,1601],{"class":844},[70,3830,1575],{"class":1014},[70,3832,1606],{"class":844},[70,3834,1349],{"class":848},[70,3836,1611],{"class":844},[70,3838,3839],{"class":867},"?",[70,3841,1617],{"class":844},[70,3843,1335],{"class":844},[70,3845,3846,3848,3850,3853,3855,3857,3859,3861,3863],{"class":564,"line":590},[70,3847,2381],{"class":1004},[70,3849,1601],{"class":844},[70,3851,3852],{"class":1014},"1",[70,3854,1606],{"class":844},[70,3856,1349],{"class":848},[70,3858,1611],{"class":844},[70,3860,1614],{"class":1014},[70,3862,1617],{"class":844},[70,3864,1335],{"class":844},[70,3866,3867,3869,3871,3873,3876,3878,3880],{"class":564,"line":596},[70,3868,2579],{"class":1587},[70,3870,1591],{"class":844},[70,3872,1664],{"class":1587},[70,3874,3875],{"class":1004},"ua",[70,3877,1008],{"class":844},[70,3879,1672],{"class":1587},[70,3881,1335],{"class":844},[70,3883,3884],{"class":564,"line":602},[70,3885,838],{"emptyLinePlaceholder":837},[70,3887,3888,3890,3893,3895,3898,3900,3902,3905,3908],{"class":564,"line":607},[70,3889,2558],{"class":956},[70,3891,3892],{"class":852}," UNICODE_STRING",[70,3894,2186],{"class":956},[70,3896,3897],{"class":1004}," us",[70,3899,1349],{"class":848},[70,3901,3801],{"class":1004},[70,3903,3904],{"class":844},"->",[70,3906,3907],{"class":1004},"UserName",[70,3909,1335],{"class":844},[70,3911,3912,3914,3916,3918,3921,3923,3926,3929,3931,3933,3936,3938,3940,3942,3944,3946,3948,3950,3952,3954,3956,3958,3961,3963,3965],{"class":564,"line":613},[70,3913,2579],{"class":1587},[70,3915,1591],{"class":844},[70,3917,1664],{"class":1587},[70,3919,3920],{"class":1004},"us",[70,3922,25],{"class":844},[70,3924,3925],{"class":1004},"Buffer",[70,3927,3928],{"class":1587}," ||",[70,3930,3897],{"class":1004},[70,3932,25],{"class":844},[70,3934,3935],{"class":1004},"Length",[70,3937,2974],{"class":848},[70,3939,1811],{"class":1014},[70,3941,3928],{"class":1587},[70,3943,1591],{"class":844},[70,3945,3920],{"class":1004},[70,3947,25],{"class":844},[70,3949,3935],{"class":1004},[70,3951,1863],{"class":848},[70,3953,2910],{"class":848},[70,3955,957],{"class":844},[70,3957,1527],{"class":1004},[70,3959,3960],{"class":844},"))",[70,3962,2310],{"class":848},[70,3964,1811],{"class":1014},[70,3966,972],{"class":844},[70,3968,3969,3971],{"class":564,"line":619},[70,3970,2883],{"class":1587},[70,3972,1335],{"class":844},[70,3974,3975],{"class":564,"line":625},[70,3976,838],{"emptyLinePlaceholder":837},[70,3978,3979,3981,3984,3986,3988,3990,3992,3994,3996,3998,4000],{"class":564,"line":631},[70,3980,1991],{"class":852},[70,3982,3983],{"class":1004}," cch",[70,3985,1349],{"class":848},[70,3987,3897],{"class":1004},[70,3989,25],{"class":844},[70,3991,3935],{"class":1004},[70,3993,2624],{"class":848},[70,3995,2910],{"class":848},[70,3997,957],{"class":844},[70,3999,1527],{"class":1004},[70,4001,1900],{"class":844},[70,4003,4004,4006,4008,4011,4013,4016,4018,4020,4022,4024],{"class":564,"line":636},[70,4005,2579],{"class":1587},[70,4007,1591],{"class":844},[70,4009,4010],{"class":1004},"cch",[70,4012,1911],{"class":848},[70,4014,4015],{"class":1014}," 128",[70,4017,1008],{"class":844},[70,4019,3983],{"class":1004},[70,4021,1349],{"class":848},[70,4023,4015],{"class":1014},[70,4025,1335],{"class":844},[70,4027,4028,4031,4033,4036,4038,4040,4042,4044,4046,4048,4050,4052,4055,4057],{"class":564,"line":642},[70,4029,4030],{"class":1587},"        for",[70,4032,1591],{"class":844},[70,4034,4035],{"class":1004},"SIZE_T",[70,4037,1806],{"class":1004},[70,4039,1349],{"class":848},[70,4041,1811],{"class":1014},[70,4043,1402],{"class":844},[70,4045,1806],{"class":1004},[70,4047,864],{"class":848},[70,4049,3983],{"class":1004},[70,4051,1402],{"class":844},[70,4053,4054],{"class":848}," ++",[70,4056,1828],{"class":1004},[70,4058,972],{"class":844},[70,4060,4061],{"class":564,"line":648},[70,4062,1582],{"class":844},[70,4064,4065,4067,4070,4072,4074,4076,4078,4080,4082],{"class":564,"line":654},[70,4066,1787],{"class":852},[70,4068,4069],{"class":1004}," c",[70,4071,1349],{"class":848},[70,4073,3897],{"class":1004},[70,4075,25],{"class":844},[70,4077,3925],{"class":1004},[70,4079,1601],{"class":844},[70,4081,1828],{"class":1004},[70,4083,1798],{"class":844},[70,4085,4086,4088,4090,4093,4095,4097,4099,4101,4103,4105,4107,4109,4111,4113,4116,4118],{"class":564,"line":659},[70,4087,1588],{"class":1587},[70,4089,1591],{"class":844},[70,4091,4092],{"class":1004},"c",[70,4094,2974],{"class":848},[70,4096,1611],{"class":844},[70,4098,1614],{"class":1014},[70,4100,1617],{"class":844},[70,4102,1008],{"class":844},[70,4104,1821],{"class":844},[70,4106,3983],{"class":1004},[70,4108,1349],{"class":848},[70,4110,1806],{"class":1004},[70,4112,1402],{"class":844},[70,4114,4115],{"class":1587}," break",[70,4117,1402],{"class":844},[70,4119,2011],{"class":844},[70,4121,4122],{"class":564,"line":665},[70,4123,4124],{"class":568},"            \u002F\u002F Keep control characters out of the log line.\n",[70,4126,4127,4130,4132,4134,4136,4138,4140,4142,4144,4146,4149,4151,4153,4155,4157,4159,4161,4163],{"class":564,"line":671},[70,4128,4129],{"class":1004},"            out",[70,4131,1601],{"class":844},[70,4133,1828],{"class":1004},[70,4135,1606],{"class":844},[70,4137,1349],{"class":848},[70,4139,1591],{"class":844},[70,4141,4092],{"class":1004},[70,4143,864],{"class":848},[70,4145,3657],{"class":867},[70,4147,4148],{"class":1014},"20",[70,4150,1008],{"class":844},[70,4152,3147],{"class":848},[70,4154,1611],{"class":844},[70,4156,25],{"class":867},[70,4158,1617],{"class":844},[70,4160,1547],{"class":848},[70,4162,4069],{"class":1004},[70,4164,1335],{"class":844},[70,4166,4167],{"class":564,"line":677},[70,4168,1624],{"class":844},[70,4170,4171,4173,4175,4177,4179,4181,4183,4185,4187],{"class":564,"line":683},[70,4172,2381],{"class":1004},[70,4174,1601],{"class":844},[70,4176,4010],{"class":1004},[70,4178,1606],{"class":844},[70,4180,1349],{"class":848},[70,4182,1611],{"class":844},[70,4184,1614],{"class":1014},[70,4186,1617],{"class":844},[70,4188,1335],{"class":844},[70,4190,4191],{"class":564,"line":689},[70,4192,2405],{"class":844},[70,4194,4195],{"class":564,"line":695},[70,4196,838],{"emptyLinePlaceholder":837},[70,4198,4199,4201,4204,4206,4208,4210,4212,4215],{"class":564,"line":701},[70,4200,2167],{"class":956},[70,4202,4203],{"class":852}," Debug",[70,4205,957],{"class":844},[70,4207,1641],{"class":956},[70,4209,1644],{"class":852},[70,4211,1530],{"class":956},[70,4213,4214],{"class":1004}," text",[70,4216,972],{"class":844},[70,4218,4219],{"class":564,"line":706},[70,4220,1509],{"class":844},[70,4222,4223,4225,4228,4230,4233],{"class":564,"line":712},[70,4224,2027],{"class":852},[70,4226,4227],{"class":1004}," line",[70,4229,1601],{"class":844},[70,4231,4232],{"class":1014},"384",[70,4234,1798],{"class":844},[70,4236,4237,4239,4242,4244,4246,4248,4251,4253,4255],{"class":564,"line":718},[70,4238,1522],{"class":852},[70,4240,4241],{"class":852}," b",[70,4243,957],{"class":844},[70,4245,564],{"class":1004},[70,4247,1536],{"class":844},[70,4249,4250],{"class":852}," ARRAYSIZE",[70,4252,957],{"class":844},[70,4254,564],{"class":1004},[70,4256,2285],{"class":844},[70,4258,4259,4262,4264,4267,4269,4272,4275,4277],{"class":564,"line":723},[70,4260,4261],{"class":1004},"        b",[70,4263,25],{"class":844},[70,4265,4266],{"class":852},"Str",[70,4268,957],{"class":844},[70,4270,4271],{"class":844},"L\"",[70,4273,4274],{"class":867},"[Ristretto] ",[70,4276,934],{"class":844},[70,4278,1900],{"class":844},[70,4280,4281,4283,4285,4287,4289,4291],{"class":564,"line":729},[70,4282,4261],{"class":1004},[70,4284,25],{"class":844},[70,4286,4266],{"class":852},[70,4288,957],{"class":844},[70,4290,263],{"class":1004},[70,4292,1900],{"class":844},[70,4294,4295,4297,4299,4301,4303,4305,4308,4310],{"class":564,"line":735},[70,4296,4261],{"class":1004},[70,4298,25],{"class":844},[70,4300,4266],{"class":852},[70,4302,957],{"class":844},[70,4304,4271],{"class":844},[70,4306,4307],{"class":1014},"\\n",[70,4309,934],{"class":844},[70,4311,1900],{"class":844},[70,4313,4314,4317,4319,4321],{"class":564,"line":741},[70,4315,4316],{"class":852},"        OutputDebugStringW",[70,4318,957],{"class":844},[70,4320,564],{"class":1004},[70,4322,1900],{"class":844},[70,4324,4325],{"class":564,"line":746},[70,4326,2405],{"class":844},[70,4328,4329],{"class":564,"line":752},[70,4330,838],{"emptyLinePlaceholder":837},[70,4332,4333,4335,4338,4340,4343,4346,4348,4350,4353,4355,4357,4359,4361,4363],{"class":564,"line":758},[70,4334,2167],{"class":956},[70,4336,4337],{"class":852}," Report",[70,4339,957],{"class":844},[70,4341,4342],{"class":852},"WORD",[70,4344,4345],{"class":1004}," type",[70,4347,1536],{"class":844},[70,4349,1391],{"class":852},[70,4351,4352],{"class":1004}," id",[70,4354,1536],{"class":844},[70,4356,2000],{"class":956},[70,4358,1644],{"class":852},[70,4360,1530],{"class":956},[70,4362,4214],{"class":1004},[70,4364,972],{"class":844},[70,4366,4367],{"class":564,"line":764},[70,4368,1509],{"class":844},[70,4370,4371,4374,4376,4378],{"class":564,"line":769},[70,4372,4373],{"class":852},"        Debug",[70,4375,957],{"class":844},[70,4377,263],{"class":1004},[70,4379,1900],{"class":844},[70,4381,4382,4384,4387],{"class":564,"line":775},[70,4383,845],{"class":844},[70,4385,4386],{"class":848},"if",[70,4388,4389],{"class":852}," RISTRETTO_FILTER_EVENTLOG\n",[70,4391,4392,4395,4398,4400,4403,4405,4407,4409,4411,4414,4416],{"class":564,"line":781},[70,4393,4394],{"class":852},"        HANDLE",[70,4396,4397],{"class":1004}," h",[70,4399,1349],{"class":848},[70,4401,4402],{"class":852}," RegisterEventSourceW",[70,4404,957],{"class":844},[70,4406,3382],{"class":1014},[70,4408,1536],{"class":844},[70,4410,1312],{"class":844},[70,4412,4413],{"class":867},"TheAdminCafe Ristretto",[70,4415,934],{"class":844},[70,4417,1900],{"class":844},[70,4419,4420,4422,4424,4427],{"class":564,"line":787},[70,4421,2579],{"class":1587},[70,4423,1591],{"class":844},[70,4425,4426],{"class":1004},"h",[70,4428,972],{"class":844},[70,4430,4431],{"class":564,"line":793},[70,4432,1582],{"class":844},[70,4434,4435,4438,4440,4442,4444,4446,4448,4450,4452],{"class":564,"line":799},[70,4436,4437],{"class":1004},"            LPCWSTR",[70,4439,1649],{"class":1004},[70,4441,1601],{"class":844},[70,4443,3852],{"class":1014},[70,4445,1606],{"class":844},[70,4447,1349],{"class":848},[70,4449,1821],{"class":844},[70,4451,4214],{"class":1004},[70,4453,4454],{"class":844}," };\n",[70,4456,4457,4460,4462,4464,4466,4468,4470,4472,4474,4476,4478,4480,4482,4484,4486,4488,4490,4492,4494,4496],{"class":564,"line":805},[70,4458,4459],{"class":852},"            ReportEventW",[70,4461,957],{"class":844},[70,4463,4426],{"class":1004},[70,4465,1536],{"class":844},[70,4467,4345],{"class":1004},[70,4469,1536],{"class":844},[70,4471,1811],{"class":1014},[70,4473,1536],{"class":844},[70,4475,4352],{"class":1004},[70,4477,1536],{"class":844},[70,4479,2955],{"class":1014},[70,4481,1536],{"class":844},[70,4483,1696],{"class":1014},[70,4485,1536],{"class":844},[70,4487,1811],{"class":1014},[70,4489,1536],{"class":844},[70,4491,1649],{"class":1004},[70,4493,1536],{"class":844},[70,4495,2955],{"class":1014},[70,4497,1900],{"class":844},[70,4499,4500,4503,4505,4507],{"class":564,"line":811},[70,4501,4502],{"class":852},"            DeregisterEventSource",[70,4504,957],{"class":844},[70,4506,4426],{"class":1004},[70,4508,1900],{"class":844},[70,4510,4511],{"class":564,"line":817},[70,4512,1624],{"class":844},[70,4514,4515,4517],{"class":564,"line":823},[70,4516,845],{"class":844},[70,4518,4519],{"class":848},"else\n",[70,4521,4522,4525,4528,4530,4533,4535,4537,4539,4541,4544],{"class":564,"line":829},[70,4523,4524],{"class":844},"        (",[70,4526,4527],{"class":956},"void",[70,4529,1008],{"class":844},[70,4531,4532],{"class":1004},"type",[70,4534,1402],{"class":844},[70,4536,1591],{"class":844},[70,4538,4527],{"class":956},[70,4540,1008],{"class":844},[70,4542,4543],{"class":1004},"id",[70,4545,1335],{"class":844},[70,4547,4548,4550],{"class":564,"line":834},[70,4549,845],{"class":844},[70,4551,1028],{"class":848},[70,4553,4554],{"class":564,"line":841},[70,4555,2405],{"class":844},[70,4557,4558],{"class":564,"line":856},[70,4559,838],{"emptyLinePlaceholder":837},[70,4561,4562,4564,4567,4569,4571,4573,4575,4577,4579,4581,4584,4586,4588,4590,4592,4594,4596,4598,4601],{"class":564,"line":874},[70,4563,2167],{"class":956},[70,4565,4566],{"class":852}," ReportVeto",[70,4568,957],{"class":844},[70,4570,1855],{"class":852},[70,4572,2177],{"class":1004},[70,4574,1536],{"class":844},[70,4576,2000],{"class":956},[70,4578,1644],{"class":852},[70,4580,1530],{"class":956},[70,4582,4583],{"class":1004}," name",[70,4585,1536],{"class":844},[70,4587,3416],{"class":852},[70,4589,3421],{"class":1004},[70,4591,1536],{"class":844},[70,4593,2000],{"class":956},[70,4595,1644],{"class":852},[70,4597,1530],{"class":956},[70,4599,4600],{"class":1004}," why",[70,4602,972],{"class":844},[70,4604,4605],{"class":564,"line":888},[70,4606,1509],{"class":844},[70,4608,4609,4611,4613,4615,4618],{"class":564,"line":902},[70,4610,2027],{"class":852},[70,4612,4214],{"class":1004},[70,4614,1601],{"class":844},[70,4616,4617],{"class":1014},"320",[70,4619,1798],{"class":844},[70,4621,4622,4624,4626,4628,4630,4632,4634,4636,4638],{"class":564,"line":916},[70,4623,1522],{"class":852},[70,4625,4241],{"class":852},[70,4627,957],{"class":844},[70,4629,263],{"class":1004},[70,4631,1536],{"class":844},[70,4633,4250],{"class":852},[70,4635,957],{"class":844},[70,4637,263],{"class":1004},[70,4639,2285],{"class":844},[70,4641,4642,4644,4646,4648,4650,4652,4655,4657],{"class":564,"line":921},[70,4643,4261],{"class":1004},[70,4645,25],{"class":844},[70,4647,4266],{"class":852},[70,4649,957],{"class":844},[70,4651,4271],{"class":844},[70,4653,4654],{"class":867},"Refused a network logon for enrolled account '",[70,4656,934],{"class":844},[70,4658,1900],{"class":844},[70,4660,4661,4663,4665,4667,4669,4672],{"class":564,"line":940},[70,4662,4261],{"class":1004},[70,4664,25],{"class":844},[70,4666,4266],{"class":852},[70,4668,957],{"class":844},[70,4670,4671],{"class":1004},"name",[70,4673,1900],{"class":844},[70,4675,4676,4678,4680,4682,4684,4686,4689,4691],{"class":564,"line":945},[70,4677,4261],{"class":1004},[70,4679,25],{"class":844},[70,4681,4266],{"class":852},[70,4683,957],{"class":844},[70,4685,4271],{"class":844},[70,4687,4688],{"class":867},"' (RID ",[70,4690,934],{"class":844},[70,4692,1900],{"class":844},[70,4694,4695,4697,4699,4701,4703,4705],{"class":564,"line":975},[70,4696,4261],{"class":1004},[70,4698,25],{"class":844},[70,4700,2087],{"class":852},[70,4702,957],{"class":844},[70,4704,2278],{"class":1004},[70,4706,1900],{"class":844},[70,4708,4709,4711,4713,4715,4717,4719,4722,4724],{"class":564,"line":980},[70,4710,4261],{"class":1004},[70,4712,25],{"class":844},[70,4714,4266],{"class":852},[70,4716,957],{"class":844},[70,4718,4271],{"class":844},[70,4720,4721],{"class":867},"): ",[70,4723,934],{"class":844},[70,4725,1900],{"class":844},[70,4727,4728,4730,4732,4734,4736,4739],{"class":564,"line":991},[70,4729,4261],{"class":1004},[70,4731,25],{"class":844},[70,4733,4266],{"class":852},[70,4735,957],{"class":844},[70,4737,4738],{"class":1004},"why",[70,4740,1900],{"class":844},[70,4742,4743,4745,4747,4749,4751,4753,4756,4758],{"class":564,"line":1023},[70,4744,4261],{"class":1004},[70,4746,25],{"class":844},[70,4748,4266],{"class":852},[70,4750,957],{"class":844},[70,4752,4271],{"class":844},[70,4754,4755],{"class":867}," (last unlock ",[70,4757,934],{"class":844},[70,4759,1900],{"class":844},[70,4761,4762,4764,4766,4768,4770,4772],{"class":564,"line":1031},[70,4763,4261],{"class":1004},[70,4765,25],{"class":844},[70,4767,2087],{"class":852},[70,4769,957],{"class":844},[70,4771,3767],{"class":1004},[70,4773,1900],{"class":844},[70,4775,4776,4778,4780,4782,4784,4786,4789,4791],{"class":564,"line":1037},[70,4777,4261],{"class":1004},[70,4779,25],{"class":844},[70,4781,4266],{"class":852},[70,4783,957],{"class":844},[70,4785,4271],{"class":844},[70,4787,4788],{"class":867}," s ago or none).",[70,4790,934],{"class":844},[70,4792,1900],{"class":844},[70,4794,4795,4798,4800,4803,4805,4808,4810,4812],{"class":564,"line":1043},[70,4796,4797],{"class":852},"        Report",[70,4799,957],{"class":844},[70,4801,4802],{"class":1004},"EVENTLOG_WARNING_TYPE",[70,4804,1536],{"class":844},[70,4806,4807],{"class":1014}," 301",[70,4809,1536],{"class":844},[70,4811,4214],{"class":1004},[70,4813,1900],{"class":844},[70,4815,4816],{"class":564,"line":1049},[70,4817,2405],{"class":844},[17,4819,4820,4823,4824,4827,4828,4831],{},[41,4821,4822],{},"SamName"," copies the account name out of ",[41,4825,4826],{},"USER_ALL_INFORMATION"," for the log only. The decision runs on the RID, never on the name. The function is careful because the ",[41,4829,4830],{},"UNICODE_STRING"," comes from the caller and is the one pointer in this file we dereference:",[105,4833,4834,4841,4847],{},[108,4835,4836,4838,4839,25],{},[41,4837,3935],{}," counts bytes, and it doesn't include a terminator. An odd byte count is corrupt, so the function gives up and logs ",[41,4840,3839],{},[108,4842,4843,4844,25],{},"It stops at 128 characters and at an embedded ",[41,4845,4846],{},"NUL",[108,4848,4849,4850,4852],{},"Control characters become ",[41,4851,25],{},", so a crafted name can't put a line break into a log line.",[17,4854,4855,4858,4859,4862,4863,4866,4867,25],{},[41,4856,4857],{},"Debug"," adds the ",[41,4860,4861],{},"[Ristretto]"," prefix. That prefix is what you filter for in the kernel debugger. ",[41,4864,4865],{},"Report"," sends the same text to the event log if you compile with ",[41,4868,4869],{},"RISTRETTO_FILTER_EVENTLOG 1",[17,4871,4872,4875,4876,4878,4879,4881],{},[41,4873,4874],{},"ReportVeto"," builds the line that the first draft built with ",[41,4877,2063],{},". Same text, now in a 320-character buffer with ",[41,4880,2059],{},". If somebody adds three words to it next year, the line gets cut at the end and lsass keeps running.",[250,4883,4885],{"id":4884},"the-exception-filter","The exception filter",[258,4887,4889],{"className":557,"code":4888,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F Which exceptions the filter swallows.\n    \u002F\u002F\n    \u002F\u002F A constant EXCEPTION_EXECUTE_HANDLER would catch everything, and\n    \u002F\u002F \u002Fanalyze is right to flag that (C6320): some exceptions must not be\n    \u002F\u002F handled. A stack overflow is the clear case - the guard page is gone by\n    \u002F\u002F the time the filter runs, so continuing on that thread is worse than\n    \u002F\u002F letting it go. Everything else here means a bad pointer from the\n    \u002F\u002F caller, and on that one the bugcheck is the worse outcome, so it is\n    \u002F\u002F caught and the logon refused.\n    LONG DecisionFilter(DWORD code)\n    {\n        switch (code)\n        {\n        case EXCEPTION_STACK_OVERFLOW:\n        case EXCEPTION_NONCONTINUABLE_EXCEPTION:\n            return EXCEPTION_CONTINUE_SEARCH;   \u002F\u002F not ours to survive\n        default:\n            return EXCEPTION_EXECUTE_HANDLER;\n        }\n    }\n",[41,4890,4891,4896,4900,4905,4910,4915,4920,4925,4930,4935,4953,4957,4968,4972,4982,4991,5003,5010,5019,5023],{"__ignoreMap":264},[70,4892,4893],{"class":564,"line":565},[70,4894,4895],{"class":568},"    \u002F\u002F Which exceptions the filter swallows.\n",[70,4897,4898],{"class":564,"line":572},[70,4899,2118],{"class":568},[70,4901,4902],{"class":564,"line":578},[70,4903,4904],{"class":568},"    \u002F\u002F A constant EXCEPTION_EXECUTE_HANDLER would catch everything, and\n",[70,4906,4907],{"class":564,"line":584},[70,4908,4909],{"class":568},"    \u002F\u002F \u002Fanalyze is right to flag that (C6320): some exceptions must not be\n",[70,4911,4912],{"class":564,"line":590},[70,4913,4914],{"class":568},"    \u002F\u002F handled. A stack overflow is the clear case - the guard page is gone by\n",[70,4916,4917],{"class":564,"line":596},[70,4918,4919],{"class":568},"    \u002F\u002F the time the filter runs, so continuing on that thread is worse than\n",[70,4921,4922],{"class":564,"line":602},[70,4923,4924],{"class":568},"    \u002F\u002F letting it go. Everything else here means a bad pointer from the\n",[70,4926,4927],{"class":564,"line":607},[70,4928,4929],{"class":568},"    \u002F\u002F caller, and on that one the bugcheck is the worse outcome, so it is\n",[70,4931,4932],{"class":564,"line":613},[70,4933,4934],{"class":568},"    \u002F\u002F caught and the logon refused.\n",[70,4936,4937,4940,4943,4945,4948,4951],{"class":564,"line":619},[70,4938,4939],{"class":852},"    LONG",[70,4941,4942],{"class":852}," DecisionFilter",[70,4944,957],{"class":844},[70,4946,4947],{"class":852},"DWORD",[70,4949,4950],{"class":1004}," code",[70,4952,972],{"class":844},[70,4954,4955],{"class":564,"line":625},[70,4956,1509],{"class":844},[70,4958,4959,4962,4964,4966],{"class":564,"line":631},[70,4960,4961],{"class":1587},"        switch",[70,4963,1591],{"class":844},[70,4965,41],{"class":1004},[70,4967,972],{"class":844},[70,4969,4970],{"class":564,"line":636},[70,4971,1582],{"class":844},[70,4973,4974,4977,4980],{"class":564,"line":642},[70,4975,4976],{"class":1587},"        case",[70,4978,4979],{"class":1004}," EXCEPTION_STACK_OVERFLOW",[70,4981,1517],{"class":844},[70,4983,4984,4986,4989],{"class":564,"line":648},[70,4985,4976],{"class":1587},[70,4987,4988],{"class":1004}," EXCEPTION_NONCONTINUABLE_EXCEPTION",[70,4990,1517],{"class":844},[70,4992,4993,4995,4998,5000],{"class":564,"line":654},[70,4994,2883],{"class":1587},[70,4996,4997],{"class":1004}," EXCEPTION_CONTINUE_SEARCH",[70,4999,1402],{"class":844},[70,5001,5002],{"class":568},"   \u002F\u002F not ours to survive\n",[70,5004,5005,5008],{"class":564,"line":659},[70,5006,5007],{"class":1587},"        default",[70,5009,1517],{"class":844},[70,5011,5012,5014,5017],{"class":564,"line":665},[70,5013,2883],{"class":1587},[70,5015,5016],{"class":1004}," EXCEPTION_EXECUTE_HANDLER",[70,5018,1335],{"class":844},[70,5020,5021],{"class":564,"line":671},[70,5022,1624],{"class":844},[70,5024,5025],{"class":564,"line":677},[70,5026,2405],{"class":844},[17,5028,5029,5032,5033,5036,5037,5040,5041,5043],{},[41,5030,5031],{},"DecisionFilter"," decides which exceptions the ",[41,5034,5035],{},"__except"," further down may swallow. My first fix used a plain ",[41,5038,5039],{},"EXCEPTION_EXECUTE_HANDLER",", which catches everything. ",[41,5042,2420],{}," flagged that as C6320, and it's right.",[17,5045,5046],{},"A stack overflow is the clear case. By the time the handler runs, the guard page is gone, and carrying on in that thread is worse than letting the exception go. A non-continuable exception is the same idea. Everything else that can happen in this file is a bad pointer from the caller, an access violation. For that one the alternative is a dead lsass, so the filter catches it and refuses the logon.",[250,5048,5050],{"id":5049},"the-decision","The decision",[258,5052,5054],{"className":557,"code":5053,"filename":549,"language":559,"meta":264,"style":264},"    \u002F\u002F The actual decision. Split out so the __try frame in the exported\n    \u002F\u002F function holds nothing but PODs - __try cannot coexist with objects\n    \u002F\u002F that need unwinding in the same frame.\n    NTSTATUS Decide(PUSER_ALL_INFORMATION UserAll)\n    {\n        if (!UserAll)\n            return STATUS_SUCCESS;          \u002F\u002F nothing to judge\n\n        ReadConfigThrottled();\n\n        if (!InterlockedCompareExchange(&g_enforce, 0, 0))\n            return STATUS_SUCCESS;          \u002F\u002F kill switch\n\n        const ULONG rid = UserAll->UserId;\n\n        WCHAR name[129];\n        SamName(UserAll, name);\n\n        switch (tac::EnrollmentByRid(rid))\n        {\n        case tac::Enrollment::No:\n            return STATUS_SUCCESS;          \u002F\u002F not enrolled: not our business\n\n        case tac::Enrollment::Unknown:\n            \u002F\u002F The index is there but this read failed. Do not guess \"not\n            \u002F\u002F enrolled\" - that is the one wrong answer that opens the gate.\n            ReportVeto(rid, name, 0, L\"the enrollment index could not be read\");\n            return STATUS_INVALID_WORKSTATION;\n\n        case tac::Enrollment::Yes:\n        default:\n            break;\n        }\n\n        ULONG age = 0;\n        if (GateFresh(rid, age))\n        {\n#if RISTRETTO_FILTER_VERBOSE\n            WCHAR ok[256];\n            Buf b(ok, ARRAYSIZE(ok));\n            b.Str(L\"Gate fresh for '\");\n            b.Str(name);\n            b.Str(L\"' (RID \");\n            b.U64(rid);\n            b.Str(L\", \");\n            b.U64(age);\n            b.Str(L\" s old). No objection.\");\n            Debug(ok);\n#endif\n            return STATUS_SUCCESS;\n        }\n\n        ReportVeto(rid, name, age, L\"no fresh gate\");\n        return STATUS_INVALID_WORKSTATION;\n    }\n}\n",[41,5055,5056,5061,5066,5071,5089,5093,5106,5117,5121,5128,5132,5159,5170,5174,5193,5197,5209,5224,5228,5249,5253,5271,5282,5286,5302,5307,5312,5340,5348,5352,5369,5375,5382,5386,5390,5403,5422,5426,5435,5449,5471,5491,5505,5523,5537,5555,5569,5588,5599,5605,5613,5617,5621,5649,5657,5661],{"__ignoreMap":264},[70,5057,5058],{"class":564,"line":565},[70,5059,5060],{"class":568},"    \u002F\u002F The actual decision. Split out so the __try frame in the exported\n",[70,5062,5063],{"class":564,"line":572},[70,5064,5065],{"class":568},"    \u002F\u002F function holds nothing but PODs - __try cannot coexist with objects\n",[70,5067,5068],{"class":564,"line":578},[70,5069,5070],{"class":568},"    \u002F\u002F that need unwinding in the same frame.\n",[70,5072,5073,5076,5079,5081,5084,5087],{"class":564,"line":584},[70,5074,5075],{"class":852},"    NTSTATUS",[70,5077,5078],{"class":852}," Decide",[70,5080,957],{"class":844},[70,5082,5083],{"class":852},"PUSER_ALL_INFORMATION",[70,5085,5086],{"class":1004}," UserAll",[70,5088,972],{"class":844},[70,5090,5091],{"class":564,"line":590},[70,5092,1509],{"class":844},[70,5094,5095,5097,5099,5101,5104],{"class":564,"line":596},[70,5096,2579],{"class":1587},[70,5098,1591],{"class":844},[70,5100,1664],{"class":1587},[70,5102,5103],{"class":1004},"UserAll",[70,5105,972],{"class":844},[70,5107,5108,5110,5112,5114],{"class":564,"line":602},[70,5109,2883],{"class":1587},[70,5111,998],{"class":1004},[70,5113,1402],{"class":844},[70,5115,5116],{"class":568},"          \u002F\u002F nothing to judge\n",[70,5118,5119],{"class":564,"line":607},[70,5120,838],{"emptyLinePlaceholder":837},[70,5122,5123,5126],{"class":564,"line":613},[70,5124,5125],{"class":852},"        ReadConfigThrottled",[70,5127,2784],{"class":844},[70,5129,5130],{"class":564,"line":619},[70,5131,838],{"emptyLinePlaceholder":837},[70,5133,5134,5136,5138,5140,5142,5144,5146,5148,5150,5152,5154,5156],{"class":564,"line":625},[70,5135,2579],{"class":1587},[70,5137,1591],{"class":844},[70,5139,1664],{"class":1587},[70,5141,3701],{"class":852},[70,5143,957],{"class":844},[70,5145,2186],{"class":848},[70,5147,3140],{"class":1004},[70,5149,1536],{"class":844},[70,5151,1811],{"class":1014},[70,5153,1536],{"class":844},[70,5155,1811],{"class":1014},[70,5157,5158],{"class":844},"))\n",[70,5160,5161,5163,5165,5167],{"class":564,"line":631},[70,5162,2883],{"class":1587},[70,5164,998],{"class":1004},[70,5166,1402],{"class":844},[70,5168,5169],{"class":568},"          \u002F\u002F kill switch\n",[70,5171,5172],{"class":564,"line":636},[70,5173,838],{"emptyLinePlaceholder":837},[70,5175,5176,5178,5180,5182,5184,5186,5188,5191],{"class":564,"line":642},[70,5177,2558],{"class":956},[70,5179,3416],{"class":852},[70,5181,2177],{"class":1004},[70,5183,1349],{"class":848},[70,5185,5086],{"class":1004},[70,5187,3904],{"class":844},[70,5189,5190],{"class":1004},"UserId",[70,5192,1335],{"class":844},[70,5194,5195],{"class":564,"line":648},[70,5196,838],{"emptyLinePlaceholder":837},[70,5198,5199,5201,5203,5205,5207],{"class":564,"line":654},[70,5200,2027],{"class":852},[70,5202,4583],{"class":1004},[70,5204,1601],{"class":844},[70,5206,3816],{"class":1014},[70,5208,1798],{"class":844},[70,5210,5211,5214,5216,5218,5220,5222],{"class":564,"line":659},[70,5212,5213],{"class":852},"        SamName",[70,5215,957],{"class":844},[70,5217,5103],{"class":1004},[70,5219,1536],{"class":844},[70,5221,4583],{"class":1004},[70,5223,1900],{"class":844},[70,5225,5226],{"class":564,"line":665},[70,5227,838],{"emptyLinePlaceholder":837},[70,5229,5230,5232,5234,5237,5240,5243,5245,5247],{"class":564,"line":671},[70,5231,4961],{"class":1587},[70,5233,1591],{"class":844},[70,5235,5236],{"class":852},"tac",[70,5238,5239],{"class":844},"::",[70,5241,5242],{"class":852},"EnrollmentByRid",[70,5244,957],{"class":844},[70,5246,2278],{"class":1004},[70,5248,5158],{"class":844},[70,5250,5251],{"class":564,"line":677},[70,5252,1582],{"class":844},[70,5254,5255,5257,5260,5262,5265,5267,5269],{"class":564,"line":683},[70,5256,4976],{"class":1587},[70,5258,5259],{"class":852}," tac",[70,5261,5239],{"class":844},[70,5263,5264],{"class":852},"Enrollment",[70,5266,5239],{"class":844},[70,5268,3333],{"class":1004},[70,5270,1517],{"class":844},[70,5272,5273,5275,5277,5279],{"class":564,"line":689},[70,5274,2883],{"class":1587},[70,5276,998],{"class":1004},[70,5278,1402],{"class":844},[70,5280,5281],{"class":568},"          \u002F\u002F not enrolled: not our business\n",[70,5283,5284],{"class":564,"line":695},[70,5285,838],{"emptyLinePlaceholder":837},[70,5287,5288,5290,5292,5294,5296,5298,5300],{"class":564,"line":701},[70,5289,4976],{"class":1587},[70,5291,5259],{"class":852},[70,5293,5239],{"class":844},[70,5295,5264],{"class":852},[70,5297,5239],{"class":844},[70,5299,3358],{"class":1004},[70,5301,1517],{"class":844},[70,5303,5304],{"class":564,"line":706},[70,5305,5306],{"class":568},"            \u002F\u002F The index is there but this read failed. Do not guess \"not\n",[70,5308,5309],{"class":564,"line":712},[70,5310,5311],{"class":568},"            \u002F\u002F enrolled\" - that is the one wrong answer that opens the gate.\n",[70,5313,5314,5317,5319,5321,5323,5325,5327,5329,5331,5333,5336,5338],{"class":564,"line":718},[70,5315,5316],{"class":852},"            ReportVeto",[70,5318,957],{"class":844},[70,5320,2278],{"class":1004},[70,5322,1536],{"class":844},[70,5324,4583],{"class":1004},[70,5326,1536],{"class":844},[70,5328,1811],{"class":1014},[70,5330,1536],{"class":844},[70,5332,1312],{"class":844},[70,5334,5335],{"class":867},"the enrollment index could not be read",[70,5337,934],{"class":844},[70,5339,1900],{"class":844},[70,5341,5342,5344,5346],{"class":564,"line":723},[70,5343,2883],{"class":1587},[70,5345,1072],{"class":1004},[70,5347,1335],{"class":844},[70,5349,5350],{"class":564,"line":729},[70,5351,838],{"emptyLinePlaceholder":837},[70,5353,5354,5356,5358,5360,5362,5364,5367],{"class":564,"line":735},[70,5355,4976],{"class":1587},[70,5357,5259],{"class":852},[70,5359,5239],{"class":844},[70,5361,5264],{"class":852},[70,5363,5239],{"class":844},[70,5365,5366],{"class":1004},"Yes",[70,5368,1517],{"class":844},[70,5370,5371,5373],{"class":564,"line":741},[70,5372,5007],{"class":1587},[70,5374,1517],{"class":844},[70,5376,5377,5380],{"class":564,"line":746},[70,5378,5379],{"class":1587},"            break",[70,5381,1335],{"class":844},[70,5383,5384],{"class":564,"line":752},[70,5385,1624],{"class":844},[70,5387,5388],{"class":564,"line":758},[70,5389,838],{"emptyLinePlaceholder":837},[70,5391,5392,5395,5397,5399,5401],{"class":564,"line":764},[70,5393,5394],{"class":852},"        ULONG",[70,5396,3627],{"class":1004},[70,5398,1349],{"class":848},[70,5400,1811],{"class":1014},[70,5402,1335],{"class":844},[70,5404,5405,5407,5409,5412,5414,5416,5418,5420],{"class":564,"line":769},[70,5406,2579],{"class":1587},[70,5408,1591],{"class":844},[70,5410,5411],{"class":852},"GateFresh",[70,5413,957],{"class":844},[70,5415,2278],{"class":1004},[70,5417,1536],{"class":844},[70,5419,3627],{"class":1004},[70,5421,5158],{"class":844},[70,5423,5424],{"class":564,"line":775},[70,5425,1582],{"class":844},[70,5427,5428,5430,5432],{"class":564,"line":781},[70,5429,845],{"class":844},[70,5431,4386],{"class":848},[70,5433,5434],{"class":852}," RISTRETTO_FILTER_VERBOSE\n",[70,5436,5437,5439,5442,5444,5447],{"class":564,"line":787},[70,5438,1787],{"class":852},[70,5440,5441],{"class":1004}," ok",[70,5443,1601],{"class":844},[70,5445,5446],{"class":1014},"256",[70,5448,1798],{"class":844},[70,5450,5451,5454,5456,5458,5461,5463,5465,5467,5469],{"class":564,"line":793},[70,5452,5453],{"class":852},"            Buf",[70,5455,4241],{"class":852},[70,5457,957],{"class":844},[70,5459,5460],{"class":1004},"ok",[70,5462,1536],{"class":844},[70,5464,4250],{"class":852},[70,5466,957],{"class":844},[70,5468,5460],{"class":1004},[70,5470,2285],{"class":844},[70,5472,5473,5476,5478,5480,5482,5484,5487,5489],{"class":564,"line":799},[70,5474,5475],{"class":1004},"            b",[70,5477,25],{"class":844},[70,5479,4266],{"class":852},[70,5481,957],{"class":844},[70,5483,4271],{"class":844},[70,5485,5486],{"class":867},"Gate fresh for '",[70,5488,934],{"class":844},[70,5490,1900],{"class":844},[70,5492,5493,5495,5497,5499,5501,5503],{"class":564,"line":805},[70,5494,5475],{"class":1004},[70,5496,25],{"class":844},[70,5498,4266],{"class":852},[70,5500,957],{"class":844},[70,5502,4671],{"class":1004},[70,5504,1900],{"class":844},[70,5506,5507,5509,5511,5513,5515,5517,5519,5521],{"class":564,"line":811},[70,5508,5475],{"class":1004},[70,5510,25],{"class":844},[70,5512,4266],{"class":852},[70,5514,957],{"class":844},[70,5516,4271],{"class":844},[70,5518,4688],{"class":867},[70,5520,934],{"class":844},[70,5522,1900],{"class":844},[70,5524,5525,5527,5529,5531,5533,5535],{"class":564,"line":817},[70,5526,5475],{"class":1004},[70,5528,25],{"class":844},[70,5530,2087],{"class":852},[70,5532,957],{"class":844},[70,5534,2278],{"class":1004},[70,5536,1900],{"class":844},[70,5538,5539,5541,5543,5545,5547,5549,5551,5553],{"class":564,"line":823},[70,5540,5475],{"class":1004},[70,5542,25],{"class":844},[70,5544,4266],{"class":852},[70,5546,957],{"class":844},[70,5548,4271],{"class":844},[70,5550,438],{"class":867},[70,5552,934],{"class":844},[70,5554,1900],{"class":844},[70,5556,5557,5559,5561,5563,5565,5567],{"class":564,"line":829},[70,5558,5475],{"class":1004},[70,5560,25],{"class":844},[70,5562,2087],{"class":852},[70,5564,957],{"class":844},[70,5566,3652],{"class":1004},[70,5568,1900],{"class":844},[70,5570,5571,5573,5575,5577,5579,5581,5584,5586],{"class":564,"line":834},[70,5572,5475],{"class":1004},[70,5574,25],{"class":844},[70,5576,4266],{"class":852},[70,5578,957],{"class":844},[70,5580,4271],{"class":844},[70,5582,5583],{"class":867}," s old). No objection.",[70,5585,934],{"class":844},[70,5587,1900],{"class":844},[70,5589,5590,5593,5595,5597],{"class":564,"line":841},[70,5591,5592],{"class":852},"            Debug",[70,5594,957],{"class":844},[70,5596,5460],{"class":1004},[70,5598,1900],{"class":844},[70,5600,5601,5603],{"class":564,"line":856},[70,5602,845],{"class":844},[70,5604,1028],{"class":848},[70,5606,5607,5609,5611],{"class":564,"line":874},[70,5608,2883],{"class":1587},[70,5610,998],{"class":1004},[70,5612,1335],{"class":844},[70,5614,5615],{"class":564,"line":888},[70,5616,1624],{"class":844},[70,5618,5619],{"class":564,"line":902},[70,5620,838],{"emptyLinePlaceholder":837},[70,5622,5623,5626,5628,5630,5632,5634,5636,5638,5640,5642,5645,5647],{"class":564,"line":916},[70,5624,5625],{"class":852},"        ReportVeto",[70,5627,957],{"class":844},[70,5629,2278],{"class":1004},[70,5631,1536],{"class":844},[70,5633,4583],{"class":1004},[70,5635,1536],{"class":844},[70,5637,3627],{"class":1004},[70,5639,1536],{"class":844},[70,5641,1312],{"class":844},[70,5643,5644],{"class":867},"no fresh gate",[70,5646,934],{"class":844},[70,5648,1900],{"class":844},[70,5650,5651,5653,5655],{"class":564,"line":921},[70,5652,2606],{"class":1587},[70,5654,1072],{"class":1004},[70,5656,1335],{"class":844},[70,5658,5659],{"class":564,"line":940},[70,5660,2405],{"class":844},[70,5662,5663],{"class":564,"line":945},[70,5664,5666],{"class":5665},"sM0OT","}\n",[17,5668,5669],{},"This is the diagram in code. The order matters for cost: the kill switch and the enrollment check exit early, and only an enrolled account pays for the gate read.",[17,5671,5672,5675,5676,5678],{},[41,5673,5674],{},"Decide"," copies the name before the switch on purpose. ",[41,5677,4874],{}," needs it for both refusal paths, and copying it once keeps the switch short.",[17,5680,5681,5683,5684,5687,5688,5690,5691,5693],{},[41,5682,5674],{}," is its own function because of ",[41,5685,5686],{},"__try",". MSVC doesn't allow ",[41,5689,5686],{}," in a function that has objects with destructors in the same frame (error C2712). ",[41,5692,5674],{}," is plain code with only PODs, but splitting it out keeps the rule obvious: the export is the wrapper, and nothing in the wrapper needs unwinding.",[250,5695,5697],{"id":5696},"the-export","The export",[258,5699,5701],{"className":557,"code":5700,"filename":549,"language":559,"meta":264,"style":264},"\u002F\u002F The documentation calls WhichFields, UserFlags, Authoritative, LogoffTime\n\u002F\u002F and KickoffTime [out]. The three we can answer for are written on every\n\u002F\u002F path, before anything can return:\n\u002F\u002F\n\u002F\u002F   * WhichFields = 0: nothing to write back to the SAM.\n\u002F\u002F   * UserFlags   = 0: we claim neither LOGON_GUEST nor LOGON_NOENCRYPTION.\n\u002F\u002F   * Authoritative = TRUE: the documentation says it \"should return valid\n\u002F\u002F     information regardless of the return value\". Earlier versions left it\n\u002F\u002F     as MSV1_0 passed it in unless they refused. For a local account there\n\u002F\u002F     is no other domain controller to ask, so our answer is final either\n\u002F\u002F     way.\n\u002F\u002F\n\u002F\u002F LogoffTime and KickoffTime stay untouched: they carry the account's logon\n\u002F\u002F hours, and a gate has no business extending them. Doppio's filter logs the\n\u002F\u002F incoming values for the VM test; this one stays quiet on allowed logons.\nNTSTATUS NTAPI Msv1_0SubAuthenticationFilter(\n    NETLOGON_LOGON_INFO_CLASS LogonLevel,\n    PVOID \u002F*LogonInformation*\u002F,            \u002F\u002F challenge\u002Fresponse - never read\n    ULONG Flags,\n    PUSER_ALL_INFORMATION UserAll,         \u002F\u002F hashes live here too - never read\n    PULONG WhichFields,\n    PULONG UserFlags,\n    PBOOLEAN Authoritative,\n    PLARGE_INTEGER \u002F*LogoffTime*\u002F,         \u002F\u002F account logon hours - not ours\n    PLARGE_INTEGER \u002F*KickoffTime*\u002F)\n{\n    if (WhichFields)\n        *WhichFields = 0;\n    if (UserFlags)\n        *UserFlags = 0;\n    if (Authoritative)\n        *Authoritative = TRUE;\n\n    const bool isNetwork = (LogonLevel == NetlogonNetworkInformation ||\n                            LogonLevel == NetlogonNetworkTransitiveInformation);\n\n    \u002F\u002F MSV1_0_PASSTHRU: \"the user is not connecting to this machine\". The\n    \u002F\u002F enrollment index only knows the RIDs of local accounts, and a RID from\n    \u002F\u002F another machine can be the same number as a local one, so a pass-through\n    \u002F\u002F logon is not ours to gate.\n    const bool passthru = (Flags & MSV1_0_PASSTHRU) != 0;\n\n    \u002F\u002F Anything that is not a network logon to this machine is out of scope,\n    \u002F\u002F including the interactive path Doppio's credential provider already\n    \u002F\u002F owns.\n    if (!isNetwork || passthru)\n        return STATUS_SUCCESS;\n\n    NTSTATUS result;\n\n    \u002F\u002F An access violation here would take lsass down with it, which is a\n    \u002F\u002F bugcheck and a reboot loop on the next boot. Catching it and refusing\n    \u002F\u002F is the lesser evil on the one path this filter exists for. Nothing\n    \u002F\u002F below allocates, so there is no state to unwind.\n    __try\n    {\n        result = Decide(UserAll);\n    }\n    __except (DecisionFilter(GetExceptionCode()))\n    {\n        Report(EVENTLOG_ERROR_TYPE, 303,\n               L\"Unhandled exception while deciding a network logon - refusing (fail closed).\");\n        result = STATUS_INVALID_WORKSTATION;\n    }\n\n    return result;\n}\n\nBOOL APIENTRY DllMain(HMODULE h, DWORD reason, LPVOID)\n{\n    if (reason == DLL_PROCESS_ATTACH)\n        DisableThreadLibraryCalls(h);\n    return TRUE;\n}\n",[41,5702,5703,5708,5713,5718,5722,5727,5732,5737,5742,5747,5752,5757,5761,5766,5771,5776,5789,5799,5812,5822,5834,5844,5853,5863,5876,5885,5889,5901,5914,5925,5937,5948,5961,5965,5990,6002,6006,6011,6016,6021,6026,6054,6058,6063,6068,6073,6090,6098,6102,6111,6115,6120,6125,6130,6135,6140,6144,6159,6163,6180,6184,6200,6212,6222,6226,6230,6239,6243,6247,6279,6283,6299,6310,6318],{"__ignoreMap":264},[70,5704,5705],{"class":564,"line":565},[70,5706,5707],{"class":568},"\u002F\u002F The documentation calls WhichFields, UserFlags, Authoritative, LogoffTime\n",[70,5709,5710],{"class":564,"line":572},[70,5711,5712],{"class":568},"\u002F\u002F and KickoffTime [out]. The three we can answer for are written on every\n",[70,5714,5715],{"class":564,"line":578},[70,5716,5717],{"class":568},"\u002F\u002F path, before anything can return:\n",[70,5719,5720],{"class":564,"line":584},[70,5721,575],{"class":568},[70,5723,5724],{"class":564,"line":590},[70,5725,5726],{"class":568},"\u002F\u002F   * WhichFields = 0: nothing to write back to the SAM.\n",[70,5728,5729],{"class":564,"line":596},[70,5730,5731],{"class":568},"\u002F\u002F   * UserFlags   = 0: we claim neither LOGON_GUEST nor LOGON_NOENCRYPTION.\n",[70,5733,5734],{"class":564,"line":602},[70,5735,5736],{"class":568},"\u002F\u002F   * Authoritative = TRUE: the documentation says it \"should return valid\n",[70,5738,5739],{"class":564,"line":607},[70,5740,5741],{"class":568},"\u002F\u002F     information regardless of the return value\". Earlier versions left it\n",[70,5743,5744],{"class":564,"line":613},[70,5745,5746],{"class":568},"\u002F\u002F     as MSV1_0 passed it in unless they refused. For a local account there\n",[70,5748,5749],{"class":564,"line":619},[70,5750,5751],{"class":568},"\u002F\u002F     is no other domain controller to ask, so our answer is final either\n",[70,5753,5754],{"class":564,"line":625},[70,5755,5756],{"class":568},"\u002F\u002F     way.\n",[70,5758,5759],{"class":564,"line":631},[70,5760,575],{"class":568},[70,5762,5763],{"class":564,"line":636},[70,5764,5765],{"class":568},"\u002F\u002F LogoffTime and KickoffTime stay untouched: they carry the account's logon\n",[70,5767,5768],{"class":564,"line":642},[70,5769,5770],{"class":568},"\u002F\u002F hours, and a gate has no business extending them. Doppio's filter logs the\n",[70,5772,5773],{"class":564,"line":648},[70,5774,5775],{"class":568},"\u002F\u002F incoming values for the VM test; this one stays quiet on allowed logons.\n",[70,5777,5778,5780,5783,5786],{"class":564,"line":654},[70,5779,1005],{"class":1004},[70,5781,5782],{"class":852}," NTAPI",[70,5784,5785],{"class":852}," Msv1_0SubAuthenticationFilter",[70,5787,5788],{"class":844},"(\n",[70,5790,5791,5794,5797],{"class":564,"line":659},[70,5792,5793],{"class":1004},"    NETLOGON_LOGON_INFO_CLASS",[70,5795,5796],{"class":1004}," LogonLevel",[70,5798,2945],{"class":844},[70,5800,5801,5804,5807,5809],{"class":564,"line":665},[70,5802,5803],{"class":1004},"    PVOID",[70,5805,5806],{"class":568}," \u002F*LogonInformation*\u002F",[70,5808,1536],{"class":844},[70,5810,5811],{"class":568},"            \u002F\u002F challenge\u002Fresponse - never read\n",[70,5813,5814,5817,5820],{"class":564,"line":671},[70,5815,5816],{"class":1004},"    ULONG",[70,5818,5819],{"class":1004}," Flags",[70,5821,2945],{"class":844},[70,5823,5824,5827,5829,5831],{"class":564,"line":677},[70,5825,5826],{"class":1004},"    PUSER_ALL_INFORMATION",[70,5828,5086],{"class":1004},[70,5830,1536],{"class":844},[70,5832,5833],{"class":568},"         \u002F\u002F hashes live here too - never read\n",[70,5835,5836,5839,5842],{"class":564,"line":683},[70,5837,5838],{"class":1004},"    PULONG",[70,5840,5841],{"class":1004}," WhichFields",[70,5843,2945],{"class":844},[70,5845,5846,5848,5851],{"class":564,"line":689},[70,5847,5838],{"class":1004},[70,5849,5850],{"class":1004}," UserFlags",[70,5852,2945],{"class":844},[70,5854,5855,5858,5861],{"class":564,"line":695},[70,5856,5857],{"class":1004},"    PBOOLEAN",[70,5859,5860],{"class":1004}," Authoritative",[70,5862,2945],{"class":844},[70,5864,5865,5868,5871,5873],{"class":564,"line":701},[70,5866,5867],{"class":1004},"    PLARGE_INTEGER",[70,5869,5870],{"class":568}," \u002F*LogoffTime*\u002F",[70,5872,1536],{"class":844},[70,5874,5875],{"class":568},"         \u002F\u002F account logon hours - not ours\n",[70,5877,5878,5880,5883],{"class":564,"line":706},[70,5879,5867],{"class":1004},[70,5881,5882],{"class":568}," \u002F*KickoffTime*\u002F",[70,5884,972],{"class":844},[70,5886,5887],{"class":564,"line":712},[70,5888,1292],{"class":844},[70,5890,5891,5894,5896,5899],{"class":564,"line":718},[70,5892,5893],{"class":1587},"    if",[70,5895,1591],{"class":844},[70,5897,5898],{"class":1004},"WhichFields",[70,5900,972],{"class":844},[70,5902,5903,5906,5908,5910,5912],{"class":564,"line":723},[70,5904,5905],{"class":848},"        *",[70,5907,5898],{"class":1004},[70,5909,1349],{"class":848},[70,5911,1811],{"class":1014},[70,5913,1335],{"class":844},[70,5915,5916,5918,5920,5923],{"class":564,"line":729},[70,5917,5893],{"class":1587},[70,5919,1591],{"class":844},[70,5921,5922],{"class":1004},"UserFlags",[70,5924,972],{"class":844},[70,5926,5927,5929,5931,5933,5935],{"class":564,"line":735},[70,5928,5905],{"class":848},[70,5930,5922],{"class":1004},[70,5932,1349],{"class":848},[70,5934,1811],{"class":1014},[70,5936,1335],{"class":844},[70,5938,5939,5941,5943,5946],{"class":564,"line":741},[70,5940,5893],{"class":1587},[70,5942,1591],{"class":844},[70,5944,5945],{"class":1004},"Authoritative",[70,5947,972],{"class":844},[70,5949,5950,5952,5954,5956,5959],{"class":564,"line":746},[70,5951,5905],{"class":848},[70,5953,5945],{"class":1004},[70,5955,1349],{"class":848},[70,5957,5958],{"class":1004}," TRUE",[70,5960,1335],{"class":844},[70,5962,5963],{"class":564,"line":752},[70,5964,838],{"emptyLinePlaceholder":837},[70,5966,5967,5969,5972,5975,5977,5979,5982,5984,5987],{"class":564,"line":758},[70,5968,1297],{"class":956},[70,5970,5971],{"class":956}," bool",[70,5973,5974],{"class":1004}," isNetwork",[70,5976,1349],{"class":848},[70,5978,1591],{"class":844},[70,5980,5981],{"class":1004},"LogonLevel",[70,5983,2974],{"class":848},[70,5985,5986],{"class":1004}," NetlogonNetworkInformation",[70,5988,5989],{"class":1587}," ||\n",[70,5991,5992,5995,5997,6000],{"class":564,"line":764},[70,5993,5994],{"class":1004},"                            LogonLevel",[70,5996,2974],{"class":848},[70,5998,5999],{"class":1004}," NetlogonNetworkTransitiveInformation",[70,6001,1900],{"class":844},[70,6003,6004],{"class":564,"line":769},[70,6005,838],{"emptyLinePlaceholder":837},[70,6007,6008],{"class":564,"line":775},[70,6009,6010],{"class":568},"    \u002F\u002F MSV1_0_PASSTHRU: \"the user is not connecting to this machine\". The\n",[70,6012,6013],{"class":564,"line":781},[70,6014,6015],{"class":568},"    \u002F\u002F enrollment index only knows the RIDs of local accounts, and a RID from\n",[70,6017,6018],{"class":564,"line":787},[70,6019,6020],{"class":568},"    \u002F\u002F another machine can be the same number as a local one, so a pass-through\n",[70,6022,6023],{"class":564,"line":793},[70,6024,6025],{"class":568},"    \u002F\u002F logon is not ours to gate.\n",[70,6027,6028,6030,6032,6035,6037,6039,6042,6044,6046,6048,6050,6052],{"class":564,"line":799},[70,6029,1297],{"class":956},[70,6031,5971],{"class":956},[70,6033,6034],{"class":1004}," passthru",[70,6036,1349],{"class":848},[70,6038,1591],{"class":844},[70,6040,6041],{"class":1004},"Flags",[70,6043,2960],{"class":848},[70,6045,1120],{"class":1004},[70,6047,1008],{"class":844},[70,6049,2310],{"class":848},[70,6051,1811],{"class":1014},[70,6053,1335],{"class":844},[70,6055,6056],{"class":564,"line":805},[70,6057,838],{"emptyLinePlaceholder":837},[70,6059,6060],{"class":564,"line":811},[70,6061,6062],{"class":568},"    \u002F\u002F Anything that is not a network logon to this machine is out of scope,\n",[70,6064,6065],{"class":564,"line":817},[70,6066,6067],{"class":568},"    \u002F\u002F including the interactive path Doppio's credential provider already\n",[70,6069,6070],{"class":564,"line":823},[70,6071,6072],{"class":568},"    \u002F\u002F owns.\n",[70,6074,6075,6077,6079,6081,6084,6086,6088],{"class":564,"line":829},[70,6076,5893],{"class":1587},[70,6078,1591],{"class":844},[70,6080,1664],{"class":1587},[70,6082,6083],{"class":1004},"isNetwork",[70,6085,3928],{"class":1587},[70,6087,6034],{"class":1004},[70,6089,972],{"class":844},[70,6091,6092,6094,6096],{"class":564,"line":834},[70,6093,2606],{"class":1587},[70,6095,998],{"class":1004},[70,6097,1335],{"class":844},[70,6099,6100],{"class":564,"line":841},[70,6101,838],{"emptyLinePlaceholder":837},[70,6103,6104,6106,6109],{"class":564,"line":856},[70,6105,5075],{"class":852},[70,6107,6108],{"class":1004}," result",[70,6110,1335],{"class":844},[70,6112,6113],{"class":564,"line":874},[70,6114,838],{"emptyLinePlaceholder":837},[70,6116,6117],{"class":564,"line":888},[70,6118,6119],{"class":568},"    \u002F\u002F An access violation here would take lsass down with it, which is a\n",[70,6121,6122],{"class":564,"line":902},[70,6123,6124],{"class":568},"    \u002F\u002F bugcheck and a reboot loop on the next boot. Catching it and refusing\n",[70,6126,6127],{"class":564,"line":916},[70,6128,6129],{"class":568},"    \u002F\u002F is the lesser evil on the one path this filter exists for. Nothing\n",[70,6131,6132],{"class":564,"line":921},[70,6133,6134],{"class":568},"    \u002F\u002F below allocates, so there is no state to unwind.\n",[70,6136,6137],{"class":564,"line":940},[70,6138,6139],{"class":1004},"    __try\n",[70,6141,6142],{"class":564,"line":945},[70,6143,1509],{"class":844},[70,6145,6146,6149,6151,6153,6155,6157],{"class":564,"line":975},[70,6147,6148],{"class":1004},"        result",[70,6150,1349],{"class":848},[70,6152,5078],{"class":852},[70,6154,957],{"class":844},[70,6156,5103],{"class":1004},[70,6158,1900],{"class":844},[70,6160,6161],{"class":564,"line":980},[70,6162,2405],{"class":844},[70,6164,6165,6168,6170,6172,6174,6177],{"class":564,"line":991},[70,6166,6167],{"class":852},"    __except",[70,6169,1591],{"class":844},[70,6171,5031],{"class":852},[70,6173,957],{"class":844},[70,6175,6176],{"class":852},"GetExceptionCode",[70,6178,6179],{"class":844},"()))\n",[70,6181,6182],{"class":564,"line":1023},[70,6183,1509],{"class":844},[70,6185,6186,6188,6190,6193,6195,6198],{"class":564,"line":1031},[70,6187,4797],{"class":852},[70,6189,957],{"class":844},[70,6191,6192],{"class":1004},"EVENTLOG_ERROR_TYPE",[70,6194,1536],{"class":844},[70,6196,6197],{"class":1014}," 303",[70,6199,2945],{"class":844},[70,6201,6202,6205,6208,6210],{"class":564,"line":1037},[70,6203,6204],{"class":844},"               L\"",[70,6206,6207],{"class":867},"Unhandled exception while deciding a network logon - refusing (fail closed).",[70,6209,934],{"class":844},[70,6211,1900],{"class":844},[70,6213,6214,6216,6218,6220],{"class":564,"line":1043},[70,6215,6148],{"class":1004},[70,6217,1349],{"class":848},[70,6219,1072],{"class":1004},[70,6221,1335],{"class":844},[70,6223,6224],{"class":564,"line":1049},[70,6225,2405],{"class":844},[70,6227,6228],{"class":564,"line":1055},[70,6229,838],{"emptyLinePlaceholder":837},[70,6231,6232,6235,6237],{"class":564,"line":1065},[70,6233,6234],{"class":1587},"    return",[70,6236,6108],{"class":1004},[70,6238,1335],{"class":844},[70,6240,6241],{"class":564,"line":1091},[70,6242,5666],{"class":844},[70,6244,6245],{"class":564,"line":1098},[70,6246,838],{"emptyLinePlaceholder":837},[70,6248,6249,6252,6255,6258,6260,6263,6265,6267,6269,6272,6274,6277],{"class":564,"line":1103},[70,6250,6251],{"class":1004},"BOOL",[70,6253,6254],{"class":852}," APIENTRY",[70,6256,6257],{"class":852}," DllMain",[70,6259,957],{"class":844},[70,6261,6262],{"class":1004},"HMODULE",[70,6264,4397],{"class":1004},[70,6266,1536],{"class":844},[70,6268,1391],{"class":1004},[70,6270,6271],{"class":1004}," reason",[70,6273,1536],{"class":844},[70,6275,6276],{"class":1004}," LPVOID",[70,6278,972],{"class":844},[70,6280,6281],{"class":564,"line":1113},[70,6282,1292],{"class":844},[70,6284,6285,6287,6289,6292,6294,6297],{"class":564,"line":1129},[70,6286,5893],{"class":1587},[70,6288,1591],{"class":844},[70,6290,6291],{"class":1004},"reason",[70,6293,2974],{"class":848},[70,6295,6296],{"class":1004}," DLL_PROCESS_ATTACH",[70,6298,972],{"class":844},[70,6300,6301,6304,6306,6308],{"class":564,"line":1136},[70,6302,6303],{"class":852},"        DisableThreadLibraryCalls",[70,6305,957],{"class":844},[70,6307,4426],{"class":1004},[70,6309,1900],{"class":844},[70,6311,6312,6314,6316],{"class":564,"line":1141},[70,6313,6234],{"class":1587},[70,6315,5958],{"class":1004},[70,6317,1335],{"class":844},[70,6319,6320],{"class":564,"line":1147},[70,6321,5666],{"class":844},[17,6323,6324],{},"Same signature as Doppio's filter in Part 2, and the same handling of the out-parameters. The filter sets three of them first, before anything can return:",[105,6326,6327,6333,6347,6357,6366],{},[108,6328,6329,6332],{},[41,6330,6331],{},"*WhichFields = 0",": we don't ask MSV1_0 to write any account field back.",[108,6334,6335,6338,6339,6342,6343,6346],{},[41,6336,6337],{},"*UserFlags = 0",": the filter claims neither ",[41,6340,6341],{},"LOGON_GUEST"," nor ",[41,6344,6345],{},"LOGON_NOENCRYPTION",". My first version didn't touch it.",[108,6348,6349,6352,6353,6356],{},[41,6350,6351],{},"*Authoritative = TRUE",", on a refusal and on \"no objection\". My first version set it only on a refusal and left it as MSV1_0 passed it in otherwise. The documentation says the filter \"should return valid information regardless of the return value\". ",[41,6354,6355],{},"FALSE"," means \"the logon request can be tried again on another domain controller\", and a local account has no other domain controller.",[108,6358,6359,1221,6362,6365],{},[41,6360,6361],{},"LogoffTime",[41,6363,6364],{},"KickoffTime"," stay as MSV1_0 calculated them. They carry the logon hours, and a veto has no business extending a session.",[108,6367,6368,6371,6372,6374],{},[41,6369,6370],{},"LogonInformation"," doesn't get a parameter name. For a network logon it points at the challenge and the response, and ",[41,6373,5103],{}," has the password hashes right next to the RID. The filter reads neither.",[17,6376,6377,6380],{},[41,6378,6379],{},"NetlogonNetworkTransitiveInformation"," is the pass-through variant, a network logon that a domain controller forwards for a trusted domain. On a workstation with local accounts you won't see it, but if the filter ever ends up on a DC, the filter should gate a transitive network logon like a direct one.",[17,6382,6383,6385,6386,6388,6389,6391],{},[41,6384,6041],{}," matters for one bit. With ",[41,6387,1272],{}," set, the documentation says \"the user is not connecting to this machine\". Doppio's index only knows the RIDs of local accounts, and a RID from another machine can be the same number as a local one. My first version ignored ",[41,6390,6041],{}," and would have checked such a logon against the local account with that RID. Now a pass-through logon gets no objection, the same as in Doppio's filter.",[17,6393,6394,6395,6397],{},"Everything that isn't a network logon returns before the ",[41,6396,5686],{},". Interactive logons belong to Doppio's tile, and batch and service logons are what the deny rights from Part 2 are for.",[17,6399,6400,6403],{},[41,6401,6402],{},"RistrettoFilter.def"," exports one function:",[258,6405,6408],{"className":6406,"code":6407,"filename":6402,"language":263,"meta":264},[261],"LIBRARY   RistrettoFilter\nEXPORTS\n    Msv1_0SubAuthenticationFilter\n",[41,6409,6407],{"__ignoreMap":264},[12,6411,6413],{"id":6412},"the-gate-tool","The gate tool",[17,6415,6416,6418,6419,6421],{},[41,6417,302],{}," is the other half. It runs elevated in a console, so it can use the heap, ",[41,6420,511],{},", DPAPI and everything else the filter can't. This chapter has the whole file again, in order.",[250,6423,6425],{"id":6424},"includes-and-the-doppio-api","Includes and the Doppio API",[258,6427,6430],{"className":557,"code":6428,"filename":6429,"language":559,"meta":264,"style":264},"\u002F\u002F ristretto.exe - opens the network gate for an enrolled local account.\n\u002F\u002F\n\u002F\u002F Verifies the TOTP through Doppio's own verify.cpp (linked UNMODIFIED), so\n\u002F\u002F lockout, replay protection and the shared state file behave exactly like at\n\u002F\u002F the logon tile:\n\u002F\u002F   - one failure counter for tile and gate (5 free, then doubling locks\n\u002F\u002F     5 -> 10 -> 20 -> 40 -> 60 min, capped)\n\u002F\u002F   - a code burned at the tile is Replayed here, and vice versa\n\u002F\u002F   - while locked, the code is not even checked\n\u002F\u002F\n\u002F\u002F Deliberately checks the code WITHOUT a password proof (verify.h says\n\u002F\u002F \"password first\" for the logon path). Acceptable here because: the gate\n\u002F\u002F grants nothing by itself (NTLM still checks the password at connection\n\u002F\u002F time), the tool is admin-only by three independent ACLs (state.lock file,\n\u002F\u002F State key, DPAPI secret), and the obvious fix is circular - LogonUserW with\n\u002F\u002F LOGON32_LOGON_NETWORK is vetoed by our own filter while the gate is closed.\n\u002F\u002F\n\u002F\u002F IMPORTANT: install this exe NEXT TO TacProvider.dll (same folder).\n\u002F\u002F Doppio's StateLock lives in the module's own directory - only the same\n\u002F\u002F directory means the same lock file, which is what makes the tile and the\n\u002F\u002F gate mutually exclusive.\n\u002F\u002F\n\u002F\u002F Run elevated: the secret value is ACL'd to SYSTEM + Administrators, and\n\u002F\u002F LoadSecretKey (DPAPI, machine scope) runs in this process.\n\u002F\u002F\n\u002F\u002F Doppio files used, unmodified: totp.cpp, store.cpp, store_index.cpp,\n\u002F\u002F verify.cpp, statelock.cpp.\n\n#include \u003Cwindows.h>\n#include \"verify.h\"\n#include \"store.h\"\n#include \u003Csddl.h>\n#include \u003Ccstdint>\n#include \u003Ccstdio>\n#include \u003Ccstdlib>\n#include \u003Ccstring>\n#include \u003Ccwchar>\n#include \u003Cstring>\n#include \u003Cvector>\n\n#pragma comment(lib, \"advapi32.lib\")\n","ristretto.cpp",[41,6431,6432,6437,6441,6446,6451,6456,6461,6466,6471,6476,6480,6485,6490,6495,6500,6505,6510,6514,6519,6524,6529,6534,6538,6543,6548,6552,6557,6562,6566,6578,6592,6604,6617,6630,6643,6656,6669,6682,6695,6708,6712],{"__ignoreMap":264},[70,6433,6434],{"class":564,"line":565},[70,6435,6436],{"class":568},"\u002F\u002F ristretto.exe - opens the network gate for an enrolled local account.\n",[70,6438,6439],{"class":564,"line":572},[70,6440,575],{"class":568},[70,6442,6443],{"class":564,"line":578},[70,6444,6445],{"class":568},"\u002F\u002F Verifies the TOTP through Doppio's own verify.cpp (linked UNMODIFIED), so\n",[70,6447,6448],{"class":564,"line":584},[70,6449,6450],{"class":568},"\u002F\u002F lockout, replay protection and the shared state file behave exactly like at\n",[70,6452,6453],{"class":564,"line":590},[70,6454,6455],{"class":568},"\u002F\u002F the logon tile:\n",[70,6457,6458],{"class":564,"line":596},[70,6459,6460],{"class":568},"\u002F\u002F   - one failure counter for tile and gate (5 free, then doubling locks\n",[70,6462,6463],{"class":564,"line":602},[70,6464,6465],{"class":568},"\u002F\u002F     5 -> 10 -> 20 -> 40 -> 60 min, capped)\n",[70,6467,6468],{"class":564,"line":607},[70,6469,6470],{"class":568},"\u002F\u002F   - a code burned at the tile is Replayed here, and vice versa\n",[70,6472,6473],{"class":564,"line":613},[70,6474,6475],{"class":568},"\u002F\u002F   - while locked, the code is not even checked\n",[70,6477,6478],{"class":564,"line":619},[70,6479,575],{"class":568},[70,6481,6482],{"class":564,"line":625},[70,6483,6484],{"class":568},"\u002F\u002F Deliberately checks the code WITHOUT a password proof (verify.h says\n",[70,6486,6487],{"class":564,"line":631},[70,6488,6489],{"class":568},"\u002F\u002F \"password first\" for the logon path). Acceptable here because: the gate\n",[70,6491,6492],{"class":564,"line":636},[70,6493,6494],{"class":568},"\u002F\u002F grants nothing by itself (NTLM still checks the password at connection\n",[70,6496,6497],{"class":564,"line":642},[70,6498,6499],{"class":568},"\u002F\u002F time), the tool is admin-only by three independent ACLs (state.lock file,\n",[70,6501,6502],{"class":564,"line":648},[70,6503,6504],{"class":568},"\u002F\u002F State key, DPAPI secret), and the obvious fix is circular - LogonUserW with\n",[70,6506,6507],{"class":564,"line":654},[70,6508,6509],{"class":568},"\u002F\u002F LOGON32_LOGON_NETWORK is vetoed by our own filter while the gate is closed.\n",[70,6511,6512],{"class":564,"line":659},[70,6513,575],{"class":568},[70,6515,6516],{"class":564,"line":665},[70,6517,6518],{"class":568},"\u002F\u002F IMPORTANT: install this exe NEXT TO TacProvider.dll (same folder).\n",[70,6520,6521],{"class":564,"line":671},[70,6522,6523],{"class":568},"\u002F\u002F Doppio's StateLock lives in the module's own directory - only the same\n",[70,6525,6526],{"class":564,"line":677},[70,6527,6528],{"class":568},"\u002F\u002F directory means the same lock file, which is what makes the tile and the\n",[70,6530,6531],{"class":564,"line":683},[70,6532,6533],{"class":568},"\u002F\u002F gate mutually exclusive.\n",[70,6535,6536],{"class":564,"line":689},[70,6537,575],{"class":568},[70,6539,6540],{"class":564,"line":695},[70,6541,6542],{"class":568},"\u002F\u002F Run elevated: the secret value is ACL'd to SYSTEM + Administrators, and\n",[70,6544,6545],{"class":564,"line":701},[70,6546,6547],{"class":568},"\u002F\u002F LoadSecretKey (DPAPI, machine scope) runs in this process.\n",[70,6549,6550],{"class":564,"line":706},[70,6551,575],{"class":568},[70,6553,6554],{"class":564,"line":712},[70,6555,6556],{"class":568},"\u002F\u002F Doppio files used, unmodified: totp.cpp, store.cpp, store_index.cpp,\n",[70,6558,6559],{"class":564,"line":718},[70,6560,6561],{"class":568},"\u002F\u002F verify.cpp, statelock.cpp.\n",[70,6563,6564],{"class":564,"line":723},[70,6565,838],{"emptyLinePlaceholder":837},[70,6567,6568,6570,6572,6574,6576],{"class":564,"line":729},[70,6569,845],{"class":844},[70,6571,861],{"class":848},[70,6573,864],{"class":844},[70,6575,868],{"class":867},[70,6577,871],{"class":844},[70,6579,6580,6582,6584,6586,6589],{"class":564,"line":735},[70,6581,845],{"class":844},[70,6583,861],{"class":848},[70,6585,928],{"class":844},[70,6587,6588],{"class":867},"verify.h",[70,6590,6591],{"class":844},"\"\n",[70,6593,6594,6596,6598,6600,6602],{"class":564,"line":741},[70,6595,845],{"class":844},[70,6597,861],{"class":848},[70,6599,928],{"class":844},[70,6601,931],{"class":867},[70,6603,6591],{"class":844},[70,6605,6606,6608,6610,6612,6615],{"class":564,"line":746},[70,6607,845],{"class":844},[70,6609,861],{"class":848},[70,6611,864],{"class":844},[70,6613,6614],{"class":867},"sddl.h",[70,6616,871],{"class":844},[70,6618,6619,6621,6623,6625,6628],{"class":564,"line":752},[70,6620,845],{"class":844},[70,6622,861],{"class":848},[70,6624,864],{"class":844},[70,6626,6627],{"class":867},"cstdint",[70,6629,871],{"class":844},[70,6631,6632,6634,6636,6638,6641],{"class":564,"line":758},[70,6633,845],{"class":844},[70,6635,861],{"class":848},[70,6637,864],{"class":844},[70,6639,6640],{"class":867},"cstdio",[70,6642,871],{"class":844},[70,6644,6645,6647,6649,6651,6654],{"class":564,"line":764},[70,6646,845],{"class":844},[70,6648,861],{"class":848},[70,6650,864],{"class":844},[70,6652,6653],{"class":867},"cstdlib",[70,6655,871],{"class":844},[70,6657,6658,6660,6662,6664,6667],{"class":564,"line":769},[70,6659,845],{"class":844},[70,6661,861],{"class":848},[70,6663,864],{"class":844},[70,6665,6666],{"class":867},"cstring",[70,6668,871],{"class":844},[70,6670,6671,6673,6675,6677,6680],{"class":564,"line":775},[70,6672,845],{"class":844},[70,6674,861],{"class":848},[70,6676,864],{"class":844},[70,6678,6679],{"class":867},"cwchar",[70,6681,871],{"class":844},[70,6683,6684,6686,6688,6690,6693],{"class":564,"line":781},[70,6685,845],{"class":844},[70,6687,861],{"class":848},[70,6689,864],{"class":844},[70,6691,6692],{"class":867},"string",[70,6694,871],{"class":844},[70,6696,6697,6699,6701,6703,6706],{"class":564,"line":787},[70,6698,845],{"class":844},[70,6700,861],{"class":848},[70,6702,864],{"class":844},[70,6704,6705],{"class":867},"vector",[70,6707,871],{"class":844},[70,6709,6710],{"class":564,"line":793},[70,6711,838],{"emptyLinePlaceholder":837},[70,6713,6714,6716,6718,6720,6722,6724,6726,6728,6730,6732],{"class":564,"line":799},[70,6715,845],{"class":844},[70,6717,950],{"class":848},[70,6719,953],{"class":852},[70,6721,957],{"class":956},[70,6723,960],{"class":852},[70,6725,438],{"class":956},[70,6727,934],{"class":844},[70,6729,967],{"class":867},[70,6731,934],{"class":844},[70,6733,972],{"class":956},[17,6735,6736,1221,6738,6740,6741,6744,6745,6747],{},[41,6737,6588],{},[41,6739,931],{}," come straight from the Doppio submodule. ",[41,6742,6743],{},"VerifyOtp"," from ",[41,6746,6588],{}," is the same function that Doppio's tile calls in Part 1:",[258,6749,6751],{"className":557,"code":6750,"language":559,"meta":264,"style":264},"OtpResult VerifyOtp(const std::wstring& sid, const std::wstring& code,\n                    uint64_t now, OtpInfo& info);\n",[41,6752,6753,6794],{"__ignoreMap":264},[70,6754,6755,6758,6761,6763,6765,6768,6770,6773,6775,6778,6780,6782,6784,6786,6788,6790,6792],{"class":564,"line":565},[70,6756,6757],{"class":852},"OtpResult",[70,6759,6760],{"class":852}," VerifyOtp",[70,6762,957],{"class":844},[70,6764,1641],{"class":956},[70,6766,6767],{"class":852}," std",[70,6769,5239],{"class":844},[70,6771,6772],{"class":852},"wstring",[70,6774,2186],{"class":956},[70,6776,6777],{"class":1004}," sid",[70,6779,1536],{"class":844},[70,6781,2000],{"class":956},[70,6783,6767],{"class":852},[70,6785,5239],{"class":844},[70,6787,6772],{"class":852},[70,6789,2186],{"class":956},[70,6791,4950],{"class":1004},[70,6793,2945],{"class":844},[70,6795,6796,6799,6801,6803,6806,6808,6811],{"class":564,"line":572},[70,6797,6798],{"class":956},"                    uint64_t",[70,6800,2769],{"class":1004},[70,6802,1536],{"class":844},[70,6804,6805],{"class":852}," OtpInfo",[70,6807,2186],{"class":956},[70,6809,6810],{"class":1004}," info",[70,6812,1900],{"class":844},[17,6814,6815,6816,6819],{},"It takes Doppio's ",[41,6817,6818],{},"StateLock",", loads the account's state, checks lockout, replay and the code, and saves the state again, all under the lock. Ristretto has no code check of its own for that reason.",[250,6821,6823],{"id":6822},"paths-acl-and-window","Paths, ACL and window",[258,6825,6827],{"className":557,"code":6826,"filename":6429,"language":559,"meta":264,"style":264},"namespace\n{\n    const wchar_t kGatePath[]   = L\"SOFTWARE\\\\TheAdminCafe\\\\Ristretto\\\\Gate\";\n    const wchar_t kConfigPath[] = L\"SOFTWARE\\\\TheAdminCafe\\\\Ristretto\";\n\n    \u002F\u002F Same ACL Doppio uses for its store (store.cpp, kKeySddl). The filter\n    \u002F\u002F reads the gate as SYSTEM; a normal user cannot forge a timestamp.\n    const wchar_t kKeySddl[]    = L\"D:P(A;OICI;KA;;;SY)(A;OICI;KA;;;BA)\";\n\n    \u002F\u002F Must match ristretto_filter.cpp. The filter clamps on read as well -\n    \u002F\u002F clamping on both sides means a value written by hand with regedit\n    \u002F\u002F still cannot produce a gate that never closes.\n    const DWORD kWindowDefault = 300;\n    const DWORD kWindowMin     = 30;\n    const DWORD kWindowMax     = 3600;\n}\n",[41,6828,6829,6833,6837,6869,6897,6901,6906,6911,6934,6938,6943,6948,6953,6967,6981,6995],{"__ignoreMap":264},[70,6830,6831],{"class":564,"line":565},[70,6832,1287],{"class":956},[70,6834,6835],{"class":564,"line":572},[70,6836,1292],{"class":844},[70,6838,6839,6841,6843,6845,6847,6849,6851,6853,6855,6857,6859,6861,6863,6865,6867],{"class":564,"line":578},[70,6840,1297],{"class":956},[70,6842,1300],{"class":956},[70,6844,1303],{"class":1004},[70,6846,1306],{"class":844},[70,6848,1309],{"class":848},[70,6850,1312],{"class":844},[70,6852,1315],{"class":867},[70,6854,1318],{"class":1014},[70,6856,1321],{"class":867},[70,6858,1318],{"class":1014},[70,6860,51],{"class":867},[70,6862,1318],{"class":1014},[70,6864,1330],{"class":867},[70,6866,934],{"class":844},[70,6868,1335],{"class":844},[70,6870,6871,6873,6875,6877,6879,6881,6883,6885,6887,6889,6891,6893,6895],{"class":564,"line":584},[70,6872,1297],{"class":956},[70,6874,1300],{"class":956},[70,6876,1344],{"class":1004},[70,6878,1306],{"class":844},[70,6880,1349],{"class":848},[70,6882,1312],{"class":844},[70,6884,1315],{"class":867},[70,6886,1318],{"class":1014},[70,6888,1321],{"class":867},[70,6890,1318],{"class":1014},[70,6892,51],{"class":867},[70,6894,934],{"class":844},[70,6896,1335],{"class":844},[70,6898,6899],{"class":564,"line":590},[70,6900,838],{"emptyLinePlaceholder":837},[70,6902,6903],{"class":564,"line":596},[70,6904,6905],{"class":568},"    \u002F\u002F Same ACL Doppio uses for its store (store.cpp, kKeySddl). The filter\n",[70,6907,6908],{"class":564,"line":602},[70,6909,6910],{"class":568},"    \u002F\u002F reads the gate as SYSTEM; a normal user cannot forge a timestamp.\n",[70,6912,6913,6915,6917,6920,6922,6925,6927,6930,6932],{"class":564,"line":607},[70,6914,1297],{"class":956},[70,6916,1300],{"class":956},[70,6918,6919],{"class":1004}," kKeySddl",[70,6921,1306],{"class":844},[70,6923,6924],{"class":848},"    =",[70,6926,1312],{"class":844},[70,6928,6929],{"class":867},"D:P(A;OICI;KA;;;SY)(A;OICI;KA;;;BA)",[70,6931,934],{"class":844},[70,6933,1335],{"class":844},[70,6935,6936],{"class":564,"line":613},[70,6937,838],{"emptyLinePlaceholder":837},[70,6939,6940],{"class":564,"line":619},[70,6941,6942],{"class":568},"    \u002F\u002F Must match ristretto_filter.cpp. The filter clamps on read as well -\n",[70,6944,6945],{"class":564,"line":625},[70,6946,6947],{"class":568},"    \u002F\u002F clamping on both sides means a value written by hand with regedit\n",[70,6949,6950],{"class":564,"line":631},[70,6951,6952],{"class":568},"    \u002F\u002F still cannot produce a gate that never closes.\n",[70,6954,6955,6957,6959,6961,6963,6965],{"class":564,"line":636},[70,6956,1297],{"class":956},[70,6958,1391],{"class":1004},[70,6960,1394],{"class":1004},[70,6962,1349],{"class":848},[70,6964,1399],{"class":1014},[70,6966,1335],{"class":844},[70,6968,6969,6971,6973,6975,6977,6979],{"class":564,"line":642},[70,6970,1297],{"class":956},[70,6972,1391],{"class":1004},[70,6974,1414],{"class":1004},[70,6976,1417],{"class":848},[70,6978,1420],{"class":1014},[70,6980,1335],{"class":844},[70,6982,6983,6985,6987,6989,6991,6993],{"class":564,"line":648},[70,6984,1297],{"class":956},[70,6986,1391],{"class":1004},[70,6988,1431],{"class":1004},[70,6990,1417],{"class":848},[70,6992,1436],{"class":1014},[70,6994,1335],{"class":844},[70,6996,6997],{"class":564,"line":654},[70,6998,5666],{"class":844},[17,7000,7001,7002,7005,7006,2072,7009,7012,7013,7016,7017,7020,7021,7024],{},"The SDDL reads like this: ",[41,7003,7004],{},"D:P"," is a protected DACL, so nothing inherits in from ",[41,7007,7008],{},"HKLM\\SOFTWARE",[41,7010,7011],{},"(A;OICI;KA;;;SY)"," gives SYSTEM full key access, inherited by subkeys, and the same again for ",[41,7014,7015],{},"BA",", the built-in Administrators. Nobody else gets any access, read included. A normal user can't see whether a gate is open, and can't forge a timestamp either. Values have no ACL of their own, the key's ACL covers them. And ",[41,7018,7019],{},"OI"," does nothing on a registry key, ",[41,7022,7023],{},"CI"," alone would do.",[17,7026,7027],{},"The exe repeats the window constants from the filter. Both sides clamp, and only the filter's clamp protects anything. The exe's clamp is a courtesy to the person typing.",[250,7029,7031],{"id":7030},"time-elevation-and-the-event-log","Time, elevation and the event log",[258,7033,7035],{"className":557,"code":7034,"filename":6429,"language":559,"meta":264,"style":264},"\u002F\u002F Identical value to _time64(nullptr) and to the filter's NowUnix():\n\u002F\u002F UTC seconds since the unix epoch. Both sides must agree exactly, so both\n\u002F\u002F now derive it the same way instead of trusting two different clocks.\nstatic uint64_t NowUnix()\n{\n    FILETIME ft;\n    GetSystemTimeAsFileTime(&ft);\n    ULARGE_INTEGER u;\n    u.LowPart  = ft.dwLowDateTime;\n    u.HighPart = ft.dwHighDateTime;\n    const uint64_t kTicksToEpoch = 116444736000000000ULL;\n    if (u.QuadPart \u003C kTicksToEpoch) return 0;\n    return (u.QuadPart - kTicksToEpoch) \u002F 10000000ULL;\n}\n\nstatic bool Elevated()\n{\n    HANDLE t = nullptr;\n    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &t))\n        return false;\n    TOKEN_ELEVATION e; DWORD cb = sizeof(e);\n    BOOL ok = GetTokenInformation(t, TokenElevation, &e, sizeof(e), &cb);\n    CloseHandle(t);\n    return ok && e.TokenIsElevated;\n}\n\nstatic void GateLog(WORD type, DWORD id, const std::wstring& text)\n{\n    HANDLE h = RegisterEventSourceW(nullptr, L\"TheAdminCafe Ristretto\");\n    if (h)\n    {\n        LPCWSTR s[1] = { text.c_str() };\n        ReportEventW(h, type, 0, id, nullptr, 1, 0, s, nullptr);\n        DeregisterEventSource(h);\n    }\n}\n",[41,7036,7037,7042,7047,7052,7064,7068,7077,7090,7099,7118,7136,7152,7176,7202,7206,7210,7221,7225,7238,7269,7277,7302,7345,7356,7373,7377,7381,7419,7423,7447,7457,7461,7489,7532,7543,7547],{"__ignoreMap":264},[70,7038,7039],{"class":564,"line":565},[70,7040,7041],{"class":568},"\u002F\u002F Identical value to _time64(nullptr) and to the filter's NowUnix():\n",[70,7043,7044],{"class":564,"line":572},[70,7045,7046],{"class":568},"\u002F\u002F UTC seconds since the unix epoch. Both sides must agree exactly, so both\n",[70,7048,7049],{"class":564,"line":578},[70,7050,7051],{"class":568},"\u002F\u002F now derive it the same way instead of trusting two different clocks.\n",[70,7053,7054,7057,7060,7062],{"class":564,"line":584},[70,7055,7056],{"class":956},"static",[70,7058,7059],{"class":956}," uint64_t",[70,7061,2461],{"class":852},[70,7063,2464],{"class":844},[70,7065,7066],{"class":564,"line":590},[70,7067,1292],{"class":844},[70,7069,7070,7073,7075],{"class":564,"line":596},[70,7071,7072],{"class":852},"    FILETIME",[70,7074,2476],{"class":1004},[70,7076,1335],{"class":844},[70,7078,7079,7082,7084,7086,7088],{"class":564,"line":602},[70,7080,7081],{"class":852},"    GetSystemTimeAsFileTime",[70,7083,957],{"class":844},[70,7085,2186],{"class":848},[70,7087,2490],{"class":1004},[70,7089,1900],{"class":844},[70,7091,7092,7095,7097],{"class":564,"line":607},[70,7093,7094],{"class":852},"    ULARGE_INTEGER",[70,7096,2504],{"class":1004},[70,7098,1335],{"class":844},[70,7100,7101,7104,7106,7108,7110,7112,7114,7116],{"class":564,"line":613},[70,7102,7103],{"class":1004},"    u",[70,7105,25],{"class":844},[70,7107,2516],{"class":1004},[70,7109,2519],{"class":848},[70,7111,2476],{"class":1004},[70,7113,25],{"class":844},[70,7115,2526],{"class":1004},[70,7117,1335],{"class":844},[70,7119,7120,7122,7124,7126,7128,7130,7132,7134],{"class":564,"line":619},[70,7121,7103],{"class":1004},[70,7123,25],{"class":844},[70,7125,2537],{"class":1004},[70,7127,1349],{"class":848},[70,7129,2476],{"class":1004},[70,7131,25],{"class":844},[70,7133,2546],{"class":1004},[70,7135,1335],{"class":844},[70,7137,7138,7140,7142,7144,7146,7148,7150],{"class":564,"line":625},[70,7139,1297],{"class":956},[70,7141,7059],{"class":956},[70,7143,2564],{"class":1004},[70,7145,1349],{"class":848},[70,7147,2569],{"class":1014},[70,7149,2572],{"class":867},[70,7151,1335],{"class":844},[70,7153,7154,7156,7158,7160,7162,7164,7166,7168,7170,7172,7174],{"class":564,"line":631},[70,7155,5893],{"class":1587},[70,7157,1591],{"class":844},[70,7159,2584],{"class":1004},[70,7161,25],{"class":844},[70,7163,2589],{"class":1004},[70,7165,864],{"class":848},[70,7167,2564],{"class":1004},[70,7169,1008],{"class":844},[70,7171,1672],{"class":1587},[70,7173,1811],{"class":1014},[70,7175,1335],{"class":844},[70,7177,7178,7180,7182,7184,7186,7188,7190,7192,7194,7196,7198,7200],{"class":564,"line":636},[70,7179,6234],{"class":1587},[70,7181,1591],{"class":844},[70,7183,2584],{"class":1004},[70,7185,25],{"class":844},[70,7187,2589],{"class":1004},[70,7189,2617],{"class":848},[70,7191,2564],{"class":1004},[70,7193,1008],{"class":844},[70,7195,2624],{"class":848},[70,7197,2627],{"class":1014},[70,7199,2572],{"class":867},[70,7201,1335],{"class":844},[70,7203,7204],{"class":564,"line":642},[70,7205,5666],{"class":844},[70,7207,7208],{"class":564,"line":648},[70,7209,838],{"emptyLinePlaceholder":837},[70,7211,7212,7214,7216,7219],{"class":564,"line":654},[70,7213,7056],{"class":956},[70,7215,5971],{"class":956},[70,7217,7218],{"class":852}," Elevated",[70,7220,2464],{"class":844},[70,7222,7223],{"class":564,"line":659},[70,7224,1292],{"class":844},[70,7226,7227,7230,7232,7234,7236],{"class":564,"line":665},[70,7228,7229],{"class":852},"    HANDLE",[70,7231,1790],{"class":1004},[70,7233,1349],{"class":848},[70,7235,2955],{"class":1014},[70,7237,1335],{"class":844},[70,7239,7240,7242,7244,7246,7249,7251,7254,7257,7260,7262,7264,7267],{"class":564,"line":671},[70,7241,5893],{"class":1587},[70,7243,1591],{"class":844},[70,7245,1664],{"class":1587},[70,7247,7248],{"class":852},"OpenProcessToken",[70,7250,957],{"class":844},[70,7252,7253],{"class":852},"GetCurrentProcess",[70,7255,7256],{"class":844},"(),",[70,7258,7259],{"class":1004}," TOKEN_QUERY",[70,7261,1536],{"class":844},[70,7263,2960],{"class":848},[70,7265,7266],{"class":1004},"t",[70,7268,5158],{"class":844},[70,7270,7271,7273,7275],{"class":564,"line":677},[70,7272,2606],{"class":1587},[70,7274,3568],{"class":1014},[70,7276,1335],{"class":844},[70,7278,7279,7282,7285,7287,7289,7291,7293,7295,7297,7300],{"class":564,"line":683},[70,7280,7281],{"class":1004},"    TOKEN_ELEVATION",[70,7283,7284],{"class":1004}," e",[70,7286,1402],{"class":844},[70,7288,1391],{"class":1004},[70,7290,2905],{"class":1004},[70,7292,1349],{"class":848},[70,7294,2910],{"class":848},[70,7296,957],{"class":844},[70,7298,7299],{"class":1004},"e",[70,7301,1900],{"class":844},[70,7303,7304,7307,7309,7311,7314,7316,7318,7320,7323,7325,7327,7329,7331,7333,7335,7337,7339,7341,7343],{"class":564,"line":689},[70,7305,7306],{"class":852},"    BOOL",[70,7308,5441],{"class":1004},[70,7310,1349],{"class":848},[70,7312,7313],{"class":852}," GetTokenInformation",[70,7315,957],{"class":844},[70,7317,7266],{"class":1004},[70,7319,1536],{"class":844},[70,7321,7322],{"class":1004}," TokenElevation",[70,7324,1536],{"class":844},[70,7326,2960],{"class":848},[70,7328,7299],{"class":1004},[70,7330,1536],{"class":844},[70,7332,2910],{"class":848},[70,7334,957],{"class":844},[70,7336,7299],{"class":1004},[70,7338,1557],{"class":844},[70,7340,2960],{"class":848},[70,7342,2969],{"class":1004},[70,7344,1900],{"class":844},[70,7346,7347,7350,7352,7354],{"class":564,"line":695},[70,7348,7349],{"class":852},"    CloseHandle",[70,7351,957],{"class":844},[70,7353,7266],{"class":1004},[70,7355,1900],{"class":844},[70,7357,7358,7360,7362,7364,7366,7368,7371],{"class":564,"line":701},[70,7359,6234],{"class":1587},[70,7361,5441],{"class":1004},[70,7363,1688],{"class":1587},[70,7365,7284],{"class":1004},[70,7367,25],{"class":844},[70,7369,7370],{"class":1004},"TokenIsElevated",[70,7372,1335],{"class":844},[70,7374,7375],{"class":564,"line":706},[70,7376,5666],{"class":844},[70,7378,7379],{"class":564,"line":712},[70,7380,838],{"emptyLinePlaceholder":837},[70,7382,7383,7385,7388,7391,7393,7395,7397,7399,7401,7403,7405,7407,7409,7411,7413,7415,7417],{"class":564,"line":718},[70,7384,7056],{"class":956},[70,7386,7387],{"class":956}," void",[70,7389,7390],{"class":852}," GateLog",[70,7392,957],{"class":844},[70,7394,4342],{"class":852},[70,7396,4345],{"class":1004},[70,7398,1536],{"class":844},[70,7400,1391],{"class":852},[70,7402,4352],{"class":1004},[70,7404,1536],{"class":844},[70,7406,2000],{"class":956},[70,7408,6767],{"class":852},[70,7410,5239],{"class":844},[70,7412,6772],{"class":852},[70,7414,2186],{"class":956},[70,7416,4214],{"class":1004},[70,7418,972],{"class":844},[70,7420,7421],{"class":564,"line":723},[70,7422,1292],{"class":844},[70,7424,7425,7427,7429,7431,7433,7435,7437,7439,7441,7443,7445],{"class":564,"line":729},[70,7426,7229],{"class":852},[70,7428,4397],{"class":1004},[70,7430,1349],{"class":848},[70,7432,4402],{"class":852},[70,7434,957],{"class":844},[70,7436,3382],{"class":1014},[70,7438,1536],{"class":844},[70,7440,1312],{"class":844},[70,7442,4413],{"class":867},[70,7444,934],{"class":844},[70,7446,1900],{"class":844},[70,7448,7449,7451,7453,7455],{"class":564,"line":735},[70,7450,5893],{"class":1587},[70,7452,1591],{"class":844},[70,7454,4426],{"class":1004},[70,7456,972],{"class":844},[70,7458,7459],{"class":564,"line":741},[70,7460,1509],{"class":844},[70,7462,7463,7466,7468,7470,7472,7474,7476,7478,7480,7482,7485,7487],{"class":564,"line":746},[70,7464,7465],{"class":1004},"        LPCWSTR",[70,7467,1649],{"class":1004},[70,7469,1601],{"class":844},[70,7471,3852],{"class":1014},[70,7473,1606],{"class":844},[70,7475,1349],{"class":848},[70,7477,1821],{"class":844},[70,7479,4214],{"class":1004},[70,7481,25],{"class":844},[70,7483,7484],{"class":852},"c_str",[70,7486,1997],{"class":844},[70,7488,4454],{"class":844},[70,7490,7491,7494,7496,7498,7500,7502,7504,7506,7508,7510,7512,7514,7516,7518,7520,7522,7524,7526,7528,7530],{"class":564,"line":752},[70,7492,7493],{"class":852},"        ReportEventW",[70,7495,957],{"class":844},[70,7497,4426],{"class":1004},[70,7499,1536],{"class":844},[70,7501,4345],{"class":1004},[70,7503,1536],{"class":844},[70,7505,1811],{"class":1014},[70,7507,1536],{"class":844},[70,7509,4352],{"class":1004},[70,7511,1536],{"class":844},[70,7513,2955],{"class":1014},[70,7515,1536],{"class":844},[70,7517,1696],{"class":1014},[70,7519,1536],{"class":844},[70,7521,1811],{"class":1014},[70,7523,1536],{"class":844},[70,7525,1649],{"class":1004},[70,7527,1536],{"class":844},[70,7529,2955],{"class":1014},[70,7531,1900],{"class":844},[70,7533,7534,7537,7539,7541],{"class":564,"line":758},[70,7535,7536],{"class":852},"        DeregisterEventSource",[70,7538,957],{"class":844},[70,7540,4426],{"class":1004},[70,7542,1900],{"class":844},[70,7544,7545],{"class":564,"line":764},[70,7546,2405],{"class":844},[70,7548,7549],{"class":564,"line":769},[70,7550,5666],{"class":844},[17,7552,7553,7556],{},[41,7554,7555],{},"NowUnix"," is the same function as in the filter, byte for byte in its logic. One binary writes the gate and another reads it, and they have to agree on what \"now\" is.",[17,7558,7559,7562,7563,7566],{},[41,7560,7561],{},"Elevated"," checks ",[41,7564,7565],{},"TokenElevation"," before anything else happens. Without elevation every registry write would fail with \"access denied\" somewhere in the middle, and the error message would be a lot less helpful than \"run elevated\".",[17,7568,7569,7572],{},[41,7570,7571],{},"GateLog"," writes to the Application log. From the exe that's an RPC from a normal process, so the problem from the filter doesn't apply.",[250,7574,7576],{"id":7575},"the-registry-helpers","The registry helpers",[258,7578,7580],{"className":557,"code":7579,"filename":6429,"language":559,"meta":264,"style":264},"\u002F\u002F store.cpp's OpenProtectedKey is static; this is an intentional copy, taken\n\u002F\u002F from Doppio 2776655. Creates or opens a key below HKLM with the protected\n\u002F\u002F ACL, and re-applies the ACL on existing keys. Doppio's own OpenProtectedKey\n\u002F\u002F stopped re-applying it in 37a827d, because Doppio's SaveState runs on the\n\u002F\u002F logon path; its enroll.exe repairs the ACL once per run instead. This tool\n\u002F\u002F is a console tool like enroll.exe, so the repair stays here on purpose.\nstatic LSTATUS OpenProtected(const wchar_t* path, HKEY* phKey)\n{\n    PSECURITY_DESCRIPTOR psd = nullptr;\n    if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(kKeySddl, SDDL_REVISION_1,\n                                                              &psd, nullptr))\n        return static_cast\u003CLSTATUS>(GetLastError());\n    SECURITY_ATTRIBUTES sa = { sizeof(sa), psd, FALSE };\n    DWORD disp = 0;\n    LSTATUS st = RegCreateKeyExW(HKEY_LOCAL_MACHINE, path, 0, nullptr, 0,\n                                 KEY_SET_VALUE | KEY_QUERY_VALUE | WRITE_DAC,\n                                 &sa, phKey, &disp);\n    if (st == ERROR_SUCCESS && disp == REG_OPENED_EXISTING_KEY)\n    {\n        st = RegSetKeySecurity(*phKey, DACL_SECURITY_INFORMATION, psd);\n        if (st != ERROR_SUCCESS) { RegCloseKey(*phKey); *phKey = nullptr; }\n    }\n    LocalFree(psd);\n    return st;\n}\n\n\u002F\u002F Same rules as the filter's ReadConfigThrottled: no readable value means the\n\u002F\u002F default, anything else is clamped, 0 included. The old version read 0 as\n\u002F\u002F \"default\" and showed 300 s where the filter allowed 30. One difference is\n\u002F\u002F left on purpose: when WindowSec disappears, the filter keeps its last value\n\u002F\u002F until lsass restarts. This tool cannot see that value and shows the default.\nstatic DWORD CurrentWindowSec()\n{\n    DWORD v = 0, cb = sizeof(v);\n    if (RegGetValueW(HKEY_LOCAL_MACHINE, kConfigPath, L\"WindowSec\",\n                     RRF_RT_REG_DWORD, nullptr, &v, &cb) != ERROR_SUCCESS)\n        return kWindowDefault;\n    if (v \u003C kWindowMin) v = kWindowMin;\n    if (v > kWindowMax) v = kWindowMax;\n    return v;\n}\n\nstatic bool WriteTs(DWORD rid, uint64_t ts)   \u002F\u002F ts = 0 deletes the value\n{\n    HKEY k = nullptr;\n    if (OpenProtected(kGatePath, &k) != ERROR_SUCCESS)\n        return false;\n    WCHAR value[16];\n    swprintf_s(value, L\"%lu\", static_cast\u003Cunsigned long>(rid));  \u002F\u002F same convention as 2FA\\Rids\n    LSTATUS st = ts ? RegSetValueExW(k, value, 0, REG_QWORD,\n                                     reinterpret_cast\u003Cconst BYTE*>(&ts), sizeof(ts))\n                    : RegDeleteValueW(k, value);\n    RegCloseKey(k);\n    return st == ERROR_SUCCESS || st == ERROR_FILE_NOT_FOUND;\n}\n\nstatic bool RidOf(const wchar_t* user, std::wstring& sid, DWORD& rid)\n{\n    \u002F\u002F Doppio's own resolution: normalizes, qualifies as COMPUTERNAME\\user,\n    \u002F\u002F requires a real local SAM account. Local-only scope comes with it.\n    if (!tac::ResolveLocalUserSid(user, sid))\n        return false;\n    return tac::RidFromSidString(sid, rid);\n}\n",[41,7581,7582,7587,7592,7597,7602,7607,7612,7644,7648,7662,7685,7699,7720,7750,7764,7799,7817,7837,7861,7865,7893,7932,7936,7947,7955,7959,7963,7968,7973,7978,7983,7988,7999,8003,8027,8053,8082,8090,8112,8134,8142,8146,8150,8176,8180,8194,8223,8231,8245,8285,8319,8348,8366,8377,8398,8402,8406,8446,8450,8455,8460,8486,8494,8516],{"__ignoreMap":264},[70,7583,7584],{"class":564,"line":565},[70,7585,7586],{"class":568},"\u002F\u002F store.cpp's OpenProtectedKey is static; this is an intentional copy, taken\n",[70,7588,7589],{"class":564,"line":572},[70,7590,7591],{"class":568},"\u002F\u002F from Doppio 2776655. Creates or opens a key below HKLM with the protected\n",[70,7593,7594],{"class":564,"line":578},[70,7595,7596],{"class":568},"\u002F\u002F ACL, and re-applies the ACL on existing keys. Doppio's own OpenProtectedKey\n",[70,7598,7599],{"class":564,"line":584},[70,7600,7601],{"class":568},"\u002F\u002F stopped re-applying it in 37a827d, because Doppio's SaveState runs on the\n",[70,7603,7604],{"class":564,"line":590},[70,7605,7606],{"class":568},"\u002F\u002F logon path; its enroll.exe repairs the ACL once per run instead. This tool\n",[70,7608,7609],{"class":564,"line":596},[70,7610,7611],{"class":568},"\u002F\u002F is a console tool like enroll.exe, so the repair stays here on purpose.\n",[70,7613,7614,7616,7619,7622,7624,7626,7629,7632,7634,7637,7639,7642],{"class":564,"line":602},[70,7615,7056],{"class":956},[70,7617,7618],{"class":852}," LSTATUS",[70,7620,7621],{"class":852}," OpenProtected",[70,7623,957],{"class":844},[70,7625,1641],{"class":956},[70,7627,7628],{"class":956}," wchar_t*",[70,7630,7631],{"class":1004}," path",[70,7633,1536],{"class":844},[70,7635,7636],{"class":852}," HKEY",[70,7638,1530],{"class":956},[70,7640,7641],{"class":1004}," phKey",[70,7643,972],{"class":844},[70,7645,7646],{"class":564,"line":607},[70,7647,1292],{"class":844},[70,7649,7650,7653,7656,7658,7660],{"class":564,"line":613},[70,7651,7652],{"class":852},"    PSECURITY_DESCRIPTOR",[70,7654,7655],{"class":1004}," psd",[70,7657,1349],{"class":848},[70,7659,2955],{"class":1014},[70,7661,1335],{"class":844},[70,7663,7664,7666,7668,7670,7673,7675,7678,7680,7683],{"class":564,"line":619},[70,7665,5893],{"class":1587},[70,7667,1591],{"class":844},[70,7669,1664],{"class":1587},[70,7671,7672],{"class":852},"ConvertStringSecurityDescriptorToSecurityDescriptorW",[70,7674,957],{"class":844},[70,7676,7677],{"class":1004},"kKeySddl",[70,7679,1536],{"class":844},[70,7681,7682],{"class":1004}," SDDL_REVISION_1",[70,7684,2945],{"class":844},[70,7686,7687,7690,7693,7695,7697],{"class":564,"line":625},[70,7688,7689],{"class":848},"                                                              &",[70,7691,7692],{"class":1004},"psd",[70,7694,1536],{"class":844},[70,7696,2955],{"class":1014},[70,7698,5158],{"class":844},[70,7700,7701,7703,7706,7709,7712,7714,7717],{"class":564,"line":631},[70,7702,2606],{"class":1587},[70,7704,7705],{"class":848}," static_cast\u003C",[70,7707,7708],{"class":1004},"LSTATUS",[70,7710,7711],{"class":848},">",[70,7713,957],{"class":844},[70,7715,7716],{"class":852},"GetLastError",[70,7718,7719],{"class":844},"());\n",[70,7721,7722,7725,7728,7730,7732,7734,7736,7739,7741,7743,7745,7748],{"class":564,"line":636},[70,7723,7724],{"class":852},"    SECURITY_ATTRIBUTES",[70,7726,7727],{"class":1004}," sa",[70,7729,1349],{"class":848},[70,7731,1821],{"class":844},[70,7733,2910],{"class":848},[70,7735,957],{"class":844},[70,7737,7738],{"class":1004},"sa",[70,7740,1557],{"class":844},[70,7742,7655],{"class":1004},[70,7744,1536],{"class":844},[70,7746,7747],{"class":1004}," FALSE",[70,7749,4454],{"class":844},[70,7751,7752,7755,7758,7760,7762],{"class":564,"line":642},[70,7753,7754],{"class":852},"    DWORD",[70,7756,7757],{"class":1004}," disp",[70,7759,1349],{"class":848},[70,7761,1811],{"class":1014},[70,7763,1335],{"class":844},[70,7765,7766,7769,7772,7774,7777,7779,7781,7783,7785,7787,7789,7791,7793,7795,7797],{"class":564,"line":648},[70,7767,7768],{"class":852},"    LSTATUS",[70,7770,7771],{"class":1004}," st",[70,7773,1349],{"class":848},[70,7775,7776],{"class":852}," RegCreateKeyExW",[70,7778,957],{"class":844},[70,7780,1467],{"class":1004},[70,7782,1536],{"class":844},[70,7784,7631],{"class":1004},[70,7786,1536],{"class":844},[70,7788,1811],{"class":1014},[70,7790,1536],{"class":844},[70,7792,2955],{"class":1014},[70,7794,1536],{"class":844},[70,7796,1811],{"class":1014},[70,7798,2945],{"class":844},[70,7800,7801,7804,7807,7810,7812,7815],{"class":564,"line":654},[70,7802,7803],{"class":1004},"                                 KEY_SET_VALUE",[70,7805,7806],{"class":848}," |",[70,7808,7809],{"class":1004}," KEY_QUERY_VALUE",[70,7811,7806],{"class":848},[70,7813,7814],{"class":1004}," WRITE_DAC",[70,7816,2945],{"class":844},[70,7818,7819,7822,7824,7826,7828,7830,7832,7835],{"class":564,"line":659},[70,7820,7821],{"class":848},"                                 &",[70,7823,7738],{"class":1004},[70,7825,1536],{"class":844},[70,7827,7641],{"class":1004},[70,7829,1536],{"class":844},[70,7831,2960],{"class":848},[70,7833,7834],{"class":1004},"disp",[70,7836,1900],{"class":844},[70,7838,7839,7841,7843,7846,7848,7850,7852,7854,7856,7859],{"class":564,"line":665},[70,7840,5893],{"class":1587},[70,7842,1591],{"class":844},[70,7844,7845],{"class":1004},"st",[70,7847,2974],{"class":848},[70,7849,2977],{"class":1004},[70,7851,1688],{"class":1587},[70,7853,7757],{"class":1004},[70,7855,2974],{"class":848},[70,7857,7858],{"class":1004}," REG_OPENED_EXISTING_KEY",[70,7860,972],{"class":844},[70,7862,7863],{"class":564,"line":671},[70,7864,1509],{"class":844},[70,7866,7867,7870,7872,7875,7877,7879,7882,7884,7887,7889,7891],{"class":564,"line":677},[70,7868,7869],{"class":1004},"        st",[70,7871,1349],{"class":848},[70,7873,7874],{"class":852}," RegSetKeySecurity",[70,7876,957],{"class":844},[70,7878,1530],{"class":848},[70,7880,7881],{"class":1004},"phKey",[70,7883,1536],{"class":844},[70,7885,7886],{"class":1004}," DACL_SECURITY_INFORMATION",[70,7888,1536],{"class":844},[70,7890,7655],{"class":1004},[70,7892,1900],{"class":844},[70,7894,7895,7897,7899,7901,7903,7905,7907,7909,7912,7914,7916,7918,7920,7922,7924,7926,7928,7930],{"class":564,"line":683},[70,7896,2579],{"class":1587},[70,7898,1591],{"class":844},[70,7900,7845],{"class":1004},[70,7902,2310],{"class":848},[70,7904,2977],{"class":1004},[70,7906,1008],{"class":844},[70,7908,1821],{"class":844},[70,7910,7911],{"class":852}," RegCloseKey",[70,7913,957],{"class":844},[70,7915,1530],{"class":848},[70,7917,7881],{"class":1004},[70,7919,2493],{"class":844},[70,7921,1719],{"class":848},[70,7923,7881],{"class":1004},[70,7925,1349],{"class":848},[70,7927,2955],{"class":1014},[70,7929,1402],{"class":844},[70,7931,2011],{"class":844},[70,7933,7934],{"class":564,"line":689},[70,7935,2405],{"class":844},[70,7937,7938,7941,7943,7945],{"class":564,"line":695},[70,7939,7940],{"class":852},"    LocalFree",[70,7942,957],{"class":844},[70,7944,7692],{"class":1004},[70,7946,1900],{"class":844},[70,7948,7949,7951,7953],{"class":564,"line":701},[70,7950,6234],{"class":1587},[70,7952,7771],{"class":1004},[70,7954,1335],{"class":844},[70,7956,7957],{"class":564,"line":706},[70,7958,5666],{"class":844},[70,7960,7961],{"class":564,"line":712},[70,7962,838],{"emptyLinePlaceholder":837},[70,7964,7965],{"class":564,"line":718},[70,7966,7967],{"class":568},"\u002F\u002F Same rules as the filter's ReadConfigThrottled: no readable value means the\n",[70,7969,7970],{"class":564,"line":723},[70,7971,7972],{"class":568},"\u002F\u002F default, anything else is clamped, 0 included. The old version read 0 as\n",[70,7974,7975],{"class":564,"line":729},[70,7976,7977],{"class":568},"\u002F\u002F \"default\" and showed 300 s where the filter allowed 30. One difference is\n",[70,7979,7980],{"class":564,"line":735},[70,7981,7982],{"class":568},"\u002F\u002F left on purpose: when WindowSec disappears, the filter keeps its last value\n",[70,7984,7985],{"class":564,"line":741},[70,7986,7987],{"class":568},"\u002F\u002F until lsass restarts. This tool cannot see that value and shows the default.\n",[70,7989,7990,7992,7994,7997],{"class":564,"line":746},[70,7991,7056],{"class":956},[70,7993,1391],{"class":852},[70,7995,7996],{"class":852}," CurrentWindowSec",[70,7998,2464],{"class":844},[70,8000,8001],{"class":564,"line":752},[70,8002,1292],{"class":844},[70,8004,8005,8007,8009,8011,8013,8015,8017,8019,8021,8023,8025],{"class":564,"line":758},[70,8006,7754],{"class":852},[70,8008,1776],{"class":1004},[70,8010,1349],{"class":848},[70,8012,1811],{"class":1014},[70,8014,1536],{"class":844},[70,8016,2905],{"class":1004},[70,8018,1349],{"class":848},[70,8020,2910],{"class":848},[70,8022,957],{"class":844},[70,8024,1860],{"class":1004},[70,8026,1900],{"class":844},[70,8028,8029,8031,8033,8035,8037,8039,8041,8043,8045,8047,8049,8051],{"class":564,"line":764},[70,8030,5893],{"class":1587},[70,8032,1591],{"class":844},[70,8034,2925],{"class":852},[70,8036,957],{"class":844},[70,8038,1467],{"class":1004},[70,8040,1536],{"class":844},[70,8042,1344],{"class":1004},[70,8044,1536],{"class":844},[70,8046,1312],{"class":844},[70,8048,2940],{"class":867},[70,8050,934],{"class":844},[70,8052,2945],{"class":844},[70,8054,8055,8058,8060,8062,8064,8066,8068,8070,8072,8074,8076,8078,8080],{"class":564,"line":769},[70,8056,8057],{"class":1004},"                     RRF_RT_REG_DWORD",[70,8059,1536],{"class":844},[70,8061,2955],{"class":1014},[70,8063,1536],{"class":844},[70,8065,2960],{"class":848},[70,8067,1860],{"class":1004},[70,8069,1536],{"class":844},[70,8071,2960],{"class":848},[70,8073,2969],{"class":1004},[70,8075,1008],{"class":844},[70,8077,2310],{"class":848},[70,8079,2977],{"class":1004},[70,8081,972],{"class":844},[70,8083,8084,8086,8088],{"class":564,"line":775},[70,8085,2606],{"class":1587},[70,8087,1394],{"class":1004},[70,8089,1335],{"class":844},[70,8091,8092,8094,8096,8098,8100,8102,8104,8106,8108,8110],{"class":564,"line":781},[70,8093,5893],{"class":1587},[70,8095,1591],{"class":844},[70,8097,1860],{"class":1004},[70,8099,864],{"class":848},[70,8101,1414],{"class":1004},[70,8103,1008],{"class":844},[70,8105,1776],{"class":1004},[70,8107,1349],{"class":848},[70,8109,1414],{"class":1004},[70,8111,1335],{"class":844},[70,8113,8114,8116,8118,8120,8122,8124,8126,8128,8130,8132],{"class":564,"line":787},[70,8115,5893],{"class":1587},[70,8117,1591],{"class":844},[70,8119,1860],{"class":1004},[70,8121,1911],{"class":848},[70,8123,1431],{"class":1004},[70,8125,1008],{"class":844},[70,8127,1776],{"class":1004},[70,8129,1349],{"class":848},[70,8131,1431],{"class":1004},[70,8133,1335],{"class":844},[70,8135,8136,8138,8140],{"class":564,"line":793},[70,8137,6234],{"class":1587},[70,8139,1776],{"class":1004},[70,8141,1335],{"class":844},[70,8143,8144],{"class":564,"line":799},[70,8145,5666],{"class":844},[70,8147,8148],{"class":564,"line":805},[70,8149,838],{"emptyLinePlaceholder":837},[70,8151,8152,8154,8156,8159,8161,8163,8165,8167,8169,8171,8173],{"class":564,"line":811},[70,8153,7056],{"class":956},[70,8155,5971],{"class":956},[70,8157,8158],{"class":852}," WriteTs",[70,8160,957],{"class":844},[70,8162,4947],{"class":852},[70,8164,2177],{"class":1004},[70,8166,1536],{"class":844},[70,8168,7059],{"class":956},[70,8170,3487],{"class":1004},[70,8172,1008],{"class":844},[70,8174,8175],{"class":568},"   \u002F\u002F ts = 0 deletes the value\n",[70,8177,8178],{"class":564,"line":817},[70,8179,1292],{"class":844},[70,8181,8182,8185,8188,8190,8192],{"class":564,"line":823},[70,8183,8184],{"class":852},"    HKEY",[70,8186,8187],{"class":1004}," k",[70,8189,1349],{"class":848},[70,8191,2955],{"class":1014},[70,8193,1335],{"class":844},[70,8195,8196,8198,8200,8203,8205,8208,8210,8212,8215,8217,8219,8221],{"class":564,"line":829},[70,8197,5893],{"class":1587},[70,8199,1591],{"class":844},[70,8201,8202],{"class":852},"OpenProtected",[70,8204,957],{"class":844},[70,8206,8207],{"class":1004},"kGatePath",[70,8209,1536],{"class":844},[70,8211,2960],{"class":848},[70,8213,8214],{"class":1004},"k",[70,8216,1008],{"class":844},[70,8218,2310],{"class":848},[70,8220,2977],{"class":1004},[70,8222,972],{"class":844},[70,8224,8225,8227,8229],{"class":564,"line":834},[70,8226,2606],{"class":1587},[70,8228,3568],{"class":1014},[70,8230,1335],{"class":844},[70,8232,8233,8236,8238,8240,8243],{"class":564,"line":841},[70,8234,8235],{"class":852},"    WCHAR",[70,8237,3449],{"class":1004},[70,8239,1601],{"class":844},[70,8241,8242],{"class":1014},"16",[70,8244,1798],{"class":844},[70,8246,8247,8250,8252,8255,8257,8259,8262,8264,8266,8268,8271,8274,8276,8278,8280,8282],{"class":564,"line":856},[70,8248,8249],{"class":852},"    swprintf_s",[70,8251,957],{"class":844},[70,8253,8254],{"class":1004},"value",[70,8256,1536],{"class":844},[70,8258,1312],{"class":844},[70,8260,8261],{"class":1014},"%lu",[70,8263,934],{"class":844},[70,8265,1536],{"class":844},[70,8267,7705],{"class":848},[70,8269,8270],{"class":956},"unsigned",[70,8272,8273],{"class":956}," long",[70,8275,7711],{"class":848},[70,8277,957],{"class":844},[70,8279,2278],{"class":1004},[70,8281,1869],{"class":844},[70,8283,8284],{"class":568},"  \u002F\u002F same convention as 2FA\\Rids\n",[70,8286,8287,8289,8291,8293,8295,8297,8300,8302,8304,8306,8308,8310,8312,8314,8317],{"class":564,"line":874},[70,8288,7768],{"class":852},[70,8290,7771],{"class":1004},[70,8292,1349],{"class":848},[70,8294,3487],{"class":1004},[70,8296,3147],{"class":848},[70,8298,8299],{"class":852}," RegSetValueExW",[70,8301,957],{"class":844},[70,8303,8214],{"class":1004},[70,8305,1536],{"class":844},[70,8307,3449],{"class":1004},[70,8309,1536],{"class":844},[70,8311,1811],{"class":1014},[70,8313,1536],{"class":844},[70,8315,8316],{"class":1004}," REG_QWORD",[70,8318,2945],{"class":844},[70,8320,8321,8324,8326,8329,8332,8334,8336,8338,8340,8342,8344,8346],{"class":564,"line":888},[70,8322,8323],{"class":848},"                                     reinterpret_cast\u003C",[70,8325,1641],{"class":956},[70,8327,8328],{"class":1004}," BYTE",[70,8330,8331],{"class":848},"*>",[70,8333,957],{"class":844},[70,8335,2186],{"class":848},[70,8337,3508],{"class":1004},[70,8339,1557],{"class":844},[70,8341,2910],{"class":848},[70,8343,957],{"class":844},[70,8345,3508],{"class":1004},[70,8347,5158],{"class":844},[70,8349,8350,8353,8356,8358,8360,8362,8364],{"class":564,"line":902},[70,8351,8352],{"class":848},"                    :",[70,8354,8355],{"class":852}," RegDeleteValueW",[70,8357,957],{"class":844},[70,8359,8214],{"class":1004},[70,8361,1536],{"class":844},[70,8363,3449],{"class":1004},[70,8365,1900],{"class":844},[70,8367,8368,8371,8373,8375],{"class":564,"line":916},[70,8369,8370],{"class":852},"    RegCloseKey",[70,8372,957],{"class":844},[70,8374,8214],{"class":1004},[70,8376,1900],{"class":844},[70,8378,8379,8381,8383,8385,8387,8389,8391,8393,8396],{"class":564,"line":921},[70,8380,6234],{"class":1587},[70,8382,7771],{"class":1004},[70,8384,2974],{"class":848},[70,8386,2977],{"class":1004},[70,8388,3928],{"class":1587},[70,8390,7771],{"class":1004},[70,8392,2974],{"class":848},[70,8394,8395],{"class":1004}," ERROR_FILE_NOT_FOUND",[70,8397,1335],{"class":844},[70,8399,8400],{"class":564,"line":940},[70,8401,5666],{"class":844},[70,8403,8404],{"class":564,"line":945},[70,8405,838],{"emptyLinePlaceholder":837},[70,8407,8408,8410,8412,8415,8417,8419,8421,8424,8426,8428,8430,8432,8434,8436,8438,8440,8442,8444],{"class":564,"line":975},[70,8409,7056],{"class":956},[70,8411,5971],{"class":956},[70,8413,8414],{"class":852}," RidOf",[70,8416,957],{"class":844},[70,8418,1641],{"class":956},[70,8420,7628],{"class":956},[70,8422,8423],{"class":1004}," user",[70,8425,1536],{"class":844},[70,8427,6767],{"class":852},[70,8429,5239],{"class":844},[70,8431,6772],{"class":852},[70,8433,2186],{"class":956},[70,8435,6777],{"class":1004},[70,8437,1536],{"class":844},[70,8439,1391],{"class":852},[70,8441,2186],{"class":956},[70,8443,2177],{"class":1004},[70,8445,972],{"class":844},[70,8447,8448],{"class":564,"line":980},[70,8449,1292],{"class":844},[70,8451,8452],{"class":564,"line":991},[70,8453,8454],{"class":568},"    \u002F\u002F Doppio's own resolution: normalizes, qualifies as COMPUTERNAME\\user,\n",[70,8456,8457],{"class":564,"line":1023},[70,8458,8459],{"class":568},"    \u002F\u002F requires a real local SAM account. Local-only scope comes with it.\n",[70,8461,8462,8464,8466,8468,8470,8472,8475,8477,8480,8482,8484],{"class":564,"line":1031},[70,8463,5893],{"class":1587},[70,8465,1591],{"class":844},[70,8467,1664],{"class":1587},[70,8469,5236],{"class":852},[70,8471,5239],{"class":844},[70,8473,8474],{"class":852},"ResolveLocalUserSid",[70,8476,957],{"class":844},[70,8478,8479],{"class":1004},"user",[70,8481,1536],{"class":844},[70,8483,6777],{"class":1004},[70,8485,5158],{"class":844},[70,8487,8488,8490,8492],{"class":564,"line":1037},[70,8489,2606],{"class":1587},[70,8491,3568],{"class":1014},[70,8493,1335],{"class":844},[70,8495,8496,8498,8500,8502,8505,8507,8510,8512,8514],{"class":564,"line":1043},[70,8497,6234],{"class":1587},[70,8499,5259],{"class":852},[70,8501,5239],{"class":844},[70,8503,8504],{"class":852},"RidFromSidString",[70,8506,957],{"class":844},[70,8508,8509],{"class":1004},"sid",[70,8511,1536],{"class":844},[70,8513,2177],{"class":1004},[70,8515,1900],{"class":844},[70,8517,8518],{"class":564,"line":1049},[70,8519,5666],{"class":844},[17,8521,8522,8524,8525,8527,8528,8531,8532,8534,8535,8538,8539,8541,8542,8545,8546,8548,8549,8551],{},[41,8523,8202],{}," is a copy of ",[41,8526,441],{},"'s ",[41,8529,8530],{},"OpenProtectedKey",", because that one is ",[41,8533,7056],{}," and not reachable from outside. It creates the key with the protected ACL, or, if the key already exists, writes the ACL again. That second part matters: if somebody loosened the ACL on the gate key by hand, the next ",[41,8536,8537],{},"ristretto"," call puts it back. Doppio's own ",[41,8540,8530],{}," stopped doing the second part in its second review. There it runs on the logon path, and ",[41,8543,8544],{},"enroll.exe"," repairs the ACL once per run instead. ",[41,8547,302],{}," is a console tool like ",[41,8550,8544],{},", so the repair stays in the copy, and the comment says which Doppio commit the copy came from.",[17,8553,8554,8557,8558,8561],{},[41,8555,8556],{},"WriteTs"," with ",[41,8559,8560],{},"ts = 0"," deletes the value, and a value that isn't there counts as success. Closing a gate that's already closed is not an error.",[17,8563,8564,8565,8567,8568,8570,8571,8573,8574,8577],{},"This file uses ",[41,8566,2063],{},", because in the exe an overflow only ends ",[41,8569,302],{},", which is annoying and harmless. And a ",[41,8572,1855],{}," has at most 10 digits, so ",[41,8575,8576],{},"value[16]"," can't overflow.",[17,8579,8580,8583,8584,8587,8588,8591,8592,8594,8595,25],{},[41,8581,8582],{},"RidOf"," uses Doppio's own account resolution. ",[41,8585,8586],{},"tac::ResolveLocalUserSid"," qualifies the name as ",[41,8589,8590],{},"COMPUTERNAME\\user"," and requires a real local SAM account, so a domain account or a group can never get a gate. ",[41,8593,8504],{}," takes the last sub-authority of the SID, the same RID the filter gets as ",[41,8596,8597],{},"UserAll->UserId",[17,8599,8600,8603,8604,8606],{},[41,8601,8602],{},"CurrentWindowSec"," reads the window with the filter's rules: no readable value means 300, anything else is clamped, 0 included. My first version read 0 as the default and showed 300 seconds where the filter allowed 30. One difference is left: when ",[41,8605,2940],{}," disappears, the filter keeps its last value until lsass restarts, and the tool can't see that value.",[250,8608,8610],{"id":8609},"opening-the-gate","Opening the gate",[258,8612,8614],{"className":557,"code":8613,"filename":6429,"language":559,"meta":264,"style":264},"static int Open(const wchar_t* user, const wchar_t* codeArg)\n{\n    std::wstring sid; DWORD rid = 0;\n    if (!RidOf(user, sid, rid))\n    {\n        wprintf(L\"'%s' is not a local user account on this machine.\\n\", user);\n        return 1;\n    }\n\n    WCHAR codeBuf[16] = {};\n    if (codeArg && *codeArg)\n    {\n        \u002F\u002F The code is single-use and burned below, so the exposure window is\n        \u002F\u002F short - but a command line is readable by any process on the box\n        \u002F\u002F and lands in 4688 audit records and shell history. Prompting is\n        \u002F\u002F the better habit; the argument stays for scripted use.\n        wprintf(L\"Note: passing the code as an argument leaves it in the command line \"\n                L\"and in your shell history. Prefer running without it.\\n\");\n        wcsncpy_s(codeBuf, codeArg, _TRUNCATE);\n    }\n    else\n    {\n        wprintf(L\"One-time code: \");\n        if (!fgetws(codeBuf, ARRAYSIZE(codeBuf), stdin))\n            return 1;\n    }\n    std::wstring code(codeBuf);\n    SecureZeroMemory(codeBuf, sizeof(codeBuf));\n    while (!code.empty() && (code.back() == L'\\n' || code.back() == L'\\r' || code.back() == L' '))\n        code.pop_back();\n\n    tac::OtpInfo info = {};\n    tac::OtpResult r = tac::VerifyOtp(sid, code, NowUnix(), info);\n    if (!code.empty())\n        SecureZeroMemory(&code[0], code.size() * sizeof(WCHAR));\n\n    switch (r)\n    {\n    case tac::OtpResult::Ok:\n        break;\n    case tac::OtpResult::NotEnrolled:\n        wprintf(L\"'%s' is not enrolled in Doppio. Run enroll.exe first.\\n\", user);\n        return 1;\n    case tac::OtpResult::LockedOut:\n        wprintf(L\"Locked. Try again in %u minutes. \"\n                L\"(The gate and the logon screen share one lockout.)\\n\", info.minutesLeft);\n        return 1;\n    case tac::OtpResult::Error:\n        wprintf(L\"Could not lock\u002Fread\u002Fwrite the 2FA state. Nothing opened.\\n\");\n        return 1;\n    case tac::OtpResult::Replayed:\n        wprintf(L\"That code was already used. Wait for the next one - and note that \"\n                L\"retrying a used code counts as a failure (%u in a row%s).\\n\",\n                info.failures, info.minutesLeft ? L\", account now locked\" : L\"\");\n        return 1;\n    default:   \u002F\u002F Wrong\n        if (info.minutesLeft)\n            wprintf(L\"Invalid one-time code. %u in a row - account locked for %u minutes.\\n\",\n                    info.failures, info.minutesLeft);\n        else\n            wprintf(L\"Invalid one-time code. (%u in a row)\\n\", info.failures);\n        return 1;\n    }\n\n    \u002F\u002F The code is burned now (lastStep was saved). If the timestamp write\n    \u002F\u002F fails, the user re-runs with the next code - annoying, never unsafe.\n    if (!WriteTs(rid, NowUnix()))\n    {\n        wprintf(L\"Code accepted, but the gate timestamp could not be written (elevated?).\\n\");\n        return 1;\n    }\n\n    const DWORD window = CurrentWindowSec();\n    GateLog(EVENTLOG_INFORMATION_TYPE, 300,\n            std::wstring(L\"Gate opened for '\") + user + L\"' (RID \" + std::to_wstring(rid) +\n            L\") for \" + std::to_wstring(window) + L\" s.\");\n    \u002F\u002F The filter only runs when a logon is made. A share mapped or a WinRM\n    \u002F\u002F session opened inside the window stays up after it closes, so say so.\n    wprintf(L\"Gate open for '%s' for %lu s.\\n\"\n            L\"Anyone who can authenticate as this account passes while the window lasts -\\n\"\n            L\"close it with \u002Fclose when you are done. Closing stops new logons only:\\n\"\n            L\"a session that is already up stays up until it disconnects.\\n\",\n            user, static_cast\u003Cunsigned long>(window));\n    return 0;\n}\n",[41,8615,8616,8645,8649,8672,8696,8700,8727,8735,8739,8743,8760,8777,8781,8786,8791,8796,8801,8814,8828,8849,8853,8858,8862,8877,8907,8915,8919,8935,8954,9031,9043,9047,9063,9100,9117,9154,9158,9170,9174,9192,9199,9216,9241,9249,9266,9285,9307,9315,9332,9349,9357,9374,9387,9410,9444,9452,9463,9478,9506,9525,9530,9560,9568,9572,9576,9581,9586,9606,9610,9627,9635,9639,9643,9657,9673,9723,9758,9763,9768,9793,9804,9816,9830,9852,9861],{"__ignoreMap":264},[70,8617,8618,8620,8623,8626,8628,8630,8632,8634,8636,8638,8640,8643],{"class":564,"line":565},[70,8619,7056],{"class":956},[70,8621,8622],{"class":956}," int",[70,8624,8625],{"class":852}," Open",[70,8627,957],{"class":844},[70,8629,1641],{"class":956},[70,8631,7628],{"class":956},[70,8633,8423],{"class":1004},[70,8635,1536],{"class":844},[70,8637,2000],{"class":956},[70,8639,7628],{"class":956},[70,8641,8642],{"class":1004}," codeArg",[70,8644,972],{"class":844},[70,8646,8647],{"class":564,"line":572},[70,8648,1292],{"class":844},[70,8650,8651,8654,8656,8658,8660,8662,8664,8666,8668,8670],{"class":564,"line":578},[70,8652,8653],{"class":852},"    std",[70,8655,5239],{"class":844},[70,8657,6772],{"class":1004},[70,8659,6777],{"class":1004},[70,8661,1402],{"class":844},[70,8663,1391],{"class":1004},[70,8665,2177],{"class":1004},[70,8667,1349],{"class":848},[70,8669,1811],{"class":1014},[70,8671,1335],{"class":844},[70,8673,8674,8676,8678,8680,8682,8684,8686,8688,8690,8692,8694],{"class":564,"line":584},[70,8675,5893],{"class":1587},[70,8677,1591],{"class":844},[70,8679,1664],{"class":1587},[70,8681,8582],{"class":852},[70,8683,957],{"class":844},[70,8685,8479],{"class":1004},[70,8687,1536],{"class":844},[70,8689,6777],{"class":1004},[70,8691,1536],{"class":844},[70,8693,2177],{"class":1004},[70,8695,5158],{"class":844},[70,8697,8698],{"class":564,"line":590},[70,8699,1509],{"class":844},[70,8701,8702,8705,8707,8709,8711,8714,8717,8719,8721,8723,8725],{"class":564,"line":596},[70,8703,8704],{"class":852},"        wprintf",[70,8706,957],{"class":844},[70,8708,4271],{"class":844},[70,8710,1617],{"class":867},[70,8712,8713],{"class":1014},"%s",[70,8715,8716],{"class":867},"' is not a local user account on this machine.",[70,8718,4307],{"class":1014},[70,8720,934],{"class":844},[70,8722,1536],{"class":844},[70,8724,8423],{"class":1004},[70,8726,1900],{"class":844},[70,8728,8729,8731,8733],{"class":564,"line":602},[70,8730,2606],{"class":1587},[70,8732,1696],{"class":1014},[70,8734,1335],{"class":844},[70,8736,8737],{"class":564,"line":607},[70,8738,2405],{"class":844},[70,8740,8741],{"class":564,"line":613},[70,8742,838],{"emptyLinePlaceholder":837},[70,8744,8745,8747,8750,8752,8754,8756,8758],{"class":564,"line":619},[70,8746,8235],{"class":1004},[70,8748,8749],{"class":1004}," codeBuf",[70,8751,1601],{"class":844},[70,8753,8242],{"class":1014},[70,8755,1606],{"class":844},[70,8757,1349],{"class":848},[70,8759,3460],{"class":844},[70,8761,8762,8764,8766,8769,8771,8773,8775],{"class":564,"line":625},[70,8763,5893],{"class":1587},[70,8765,1591],{"class":844},[70,8767,8768],{"class":1004},"codeArg",[70,8770,1688],{"class":1587},[70,8772,1719],{"class":848},[70,8774,8768],{"class":1004},[70,8776,972],{"class":844},[70,8778,8779],{"class":564,"line":631},[70,8780,1509],{"class":844},[70,8782,8783],{"class":564,"line":636},[70,8784,8785],{"class":568},"        \u002F\u002F The code is single-use and burned below, so the exposure window is\n",[70,8787,8788],{"class":564,"line":642},[70,8789,8790],{"class":568},"        \u002F\u002F short - but a command line is readable by any process on the box\n",[70,8792,8793],{"class":564,"line":648},[70,8794,8795],{"class":568},"        \u002F\u002F and lands in 4688 audit records and shell history. Prompting is\n",[70,8797,8798],{"class":564,"line":654},[70,8799,8800],{"class":568},"        \u002F\u002F the better habit; the argument stays for scripted use.\n",[70,8802,8803,8805,8807,8809,8812],{"class":564,"line":659},[70,8804,8704],{"class":852},[70,8806,957],{"class":844},[70,8808,4271],{"class":844},[70,8810,8811],{"class":867},"Note: passing the code as an argument leaves it in the command line ",[70,8813,6591],{"class":844},[70,8815,8816,8819,8822,8824,8826],{"class":564,"line":665},[70,8817,8818],{"class":844},"                L\"",[70,8820,8821],{"class":867},"and in your shell history. Prefer running without it.",[70,8823,4307],{"class":1014},[70,8825,934],{"class":844},[70,8827,1900],{"class":844},[70,8829,8830,8833,8835,8838,8840,8842,8844,8847],{"class":564,"line":671},[70,8831,8832],{"class":852},"        wcsncpy_s",[70,8834,957],{"class":844},[70,8836,8837],{"class":1004},"codeBuf",[70,8839,1536],{"class":844},[70,8841,8642],{"class":1004},[70,8843,1536],{"class":844},[70,8845,8846],{"class":1004}," _TRUNCATE",[70,8848,1900],{"class":844},[70,8850,8851],{"class":564,"line":677},[70,8852,2405],{"class":844},[70,8854,8855],{"class":564,"line":683},[70,8856,8857],{"class":1587},"    else\n",[70,8859,8860],{"class":564,"line":689},[70,8861,1509],{"class":844},[70,8863,8864,8866,8868,8870,8873,8875],{"class":564,"line":695},[70,8865,8704],{"class":852},[70,8867,957],{"class":844},[70,8869,4271],{"class":844},[70,8871,8872],{"class":867},"One-time code: ",[70,8874,934],{"class":844},[70,8876,1900],{"class":844},[70,8878,8879,8881,8883,8885,8888,8890,8892,8894,8896,8898,8900,8902,8905],{"class":564,"line":701},[70,8880,2579],{"class":1587},[70,8882,1591],{"class":844},[70,8884,1664],{"class":1587},[70,8886,8887],{"class":852},"fgetws",[70,8889,957],{"class":844},[70,8891,8837],{"class":1004},[70,8893,1536],{"class":844},[70,8895,4250],{"class":852},[70,8897,957],{"class":844},[70,8899,8837],{"class":1004},[70,8901,1557],{"class":844},[70,8903,8904],{"class":1004}," stdin",[70,8906,5158],{"class":844},[70,8908,8909,8911,8913],{"class":564,"line":706},[70,8910,2883],{"class":1587},[70,8912,1696],{"class":1014},[70,8914,1335],{"class":844},[70,8916,8917],{"class":564,"line":712},[70,8918,2405],{"class":844},[70,8920,8921,8923,8925,8927,8929,8931,8933],{"class":564,"line":718},[70,8922,8653],{"class":852},[70,8924,5239],{"class":844},[70,8926,6772],{"class":852},[70,8928,4950],{"class":852},[70,8930,957],{"class":844},[70,8932,8837],{"class":1004},[70,8934,1900],{"class":844},[70,8936,8937,8940,8942,8944,8946,8948,8950,8952],{"class":564,"line":723},[70,8938,8939],{"class":852},"    SecureZeroMemory",[70,8941,957],{"class":844},[70,8943,8837],{"class":1004},[70,8945,1536],{"class":844},[70,8947,2910],{"class":848},[70,8949,957],{"class":844},[70,8951,8837],{"class":1004},[70,8953,2285],{"class":844},[70,8955,8956,8959,8961,8963,8965,8967,8970,8972,8974,8976,8978,8980,8983,8985,8987,8989,8991,8993,8995,8997,8999,9001,9003,9005,9007,9010,9012,9014,9016,9018,9020,9022,9024,9026,9029],{"class":564,"line":729},[70,8957,8958],{"class":1587},"    while",[70,8960,1591],{"class":844},[70,8962,1664],{"class":1587},[70,8964,41],{"class":1004},[70,8966,25],{"class":844},[70,8968,8969],{"class":852},"empty",[70,8971,1997],{"class":844},[70,8973,1688],{"class":1587},[70,8975,1591],{"class":844},[70,8977,41],{"class":1004},[70,8979,25],{"class":844},[70,8981,8982],{"class":852},"back",[70,8984,1997],{"class":844},[70,8986,2974],{"class":848},[70,8988,1611],{"class":844},[70,8990,4307],{"class":1014},[70,8992,1617],{"class":844},[70,8994,3928],{"class":1587},[70,8996,4950],{"class":1004},[70,8998,25],{"class":844},[70,9000,8982],{"class":852},[70,9002,1997],{"class":844},[70,9004,2974],{"class":848},[70,9006,1611],{"class":844},[70,9008,9009],{"class":1014},"\\r",[70,9011,1617],{"class":844},[70,9013,3928],{"class":1587},[70,9015,4950],{"class":1004},[70,9017,25],{"class":844},[70,9019,8982],{"class":852},[70,9021,1997],{"class":844},[70,9023,2974],{"class":848},[70,9025,1611],{"class":844},[70,9027,9028],{"class":844}," '",[70,9030,5158],{"class":844},[70,9032,9033,9036,9038,9041],{"class":564,"line":735},[70,9034,9035],{"class":1004},"        code",[70,9037,25],{"class":844},[70,9039,9040],{"class":852},"pop_back",[70,9042,2784],{"class":844},[70,9044,9045],{"class":564,"line":741},[70,9046,838],{"emptyLinePlaceholder":837},[70,9048,9049,9052,9054,9057,9059,9061],{"class":564,"line":746},[70,9050,9051],{"class":852},"    tac",[70,9053,5239],{"class":844},[70,9055,9056],{"class":852},"OtpInfo",[70,9058,6810],{"class":1004},[70,9060,1349],{"class":848},[70,9062,3460],{"class":844},[70,9064,9065,9067,9069,9071,9074,9076,9078,9080,9082,9084,9086,9088,9090,9092,9094,9096,9098],{"class":564,"line":752},[70,9066,9051],{"class":852},[70,9068,5239],{"class":844},[70,9070,6757],{"class":852},[70,9072,9073],{"class":1004}," r",[70,9075,1349],{"class":848},[70,9077,5259],{"class":852},[70,9079,5239],{"class":844},[70,9081,6743],{"class":852},[70,9083,957],{"class":844},[70,9085,8509],{"class":1004},[70,9087,1536],{"class":844},[70,9089,4950],{"class":1004},[70,9091,1536],{"class":844},[70,9093,2461],{"class":852},[70,9095,7256],{"class":844},[70,9097,6810],{"class":1004},[70,9099,1900],{"class":844},[70,9101,9102,9104,9106,9108,9110,9112,9114],{"class":564,"line":758},[70,9103,5893],{"class":1587},[70,9105,1591],{"class":844},[70,9107,1664],{"class":1587},[70,9109,41],{"class":1004},[70,9111,25],{"class":844},[70,9113,8969],{"class":852},[70,9115,9116],{"class":844},"())\n",[70,9118,9119,9122,9124,9126,9128,9130,9132,9135,9137,9139,9142,9144,9146,9148,9150,9152],{"class":564,"line":764},[70,9120,9121],{"class":852},"        SecureZeroMemory",[70,9123,957],{"class":844},[70,9125,2186],{"class":848},[70,9127,41],{"class":1004},[70,9129,1601],{"class":844},[70,9131,1575],{"class":1014},[70,9133,9134],{"class":844},"],",[70,9136,4950],{"class":1004},[70,9138,25],{"class":844},[70,9140,9141],{"class":852},"size",[70,9143,1997],{"class":844},[70,9145,1719],{"class":848},[70,9147,2910],{"class":848},[70,9149,957],{"class":844},[70,9151,1527],{"class":1004},[70,9153,2285],{"class":844},[70,9155,9156],{"class":564,"line":769},[70,9157,838],{"emptyLinePlaceholder":837},[70,9159,9160,9163,9165,9168],{"class":564,"line":775},[70,9161,9162],{"class":1587},"    switch",[70,9164,1591],{"class":844},[70,9166,9167],{"class":1004},"r",[70,9169,972],{"class":844},[70,9171,9172],{"class":564,"line":781},[70,9173,1509],{"class":844},[70,9175,9176,9179,9181,9183,9185,9187,9190],{"class":564,"line":787},[70,9177,9178],{"class":1587},"    case",[70,9180,5259],{"class":852},[70,9182,5239],{"class":844},[70,9184,6757],{"class":852},[70,9186,5239],{"class":844},[70,9188,9189],{"class":1004},"Ok",[70,9191,1517],{"class":844},[70,9193,9194,9197],{"class":564,"line":793},[70,9195,9196],{"class":1587},"        break",[70,9198,1335],{"class":844},[70,9200,9201,9203,9205,9207,9209,9211,9214],{"class":564,"line":799},[70,9202,9178],{"class":1587},[70,9204,5259],{"class":852},[70,9206,5239],{"class":844},[70,9208,6757],{"class":852},[70,9210,5239],{"class":844},[70,9212,9213],{"class":1004},"NotEnrolled",[70,9215,1517],{"class":844},[70,9217,9218,9220,9222,9224,9226,9228,9231,9233,9235,9237,9239],{"class":564,"line":805},[70,9219,8704],{"class":852},[70,9221,957],{"class":844},[70,9223,4271],{"class":844},[70,9225,1617],{"class":867},[70,9227,8713],{"class":1014},[70,9229,9230],{"class":867},"' is not enrolled in Doppio. Run enroll.exe first.",[70,9232,4307],{"class":1014},[70,9234,934],{"class":844},[70,9236,1536],{"class":844},[70,9238,8423],{"class":1004},[70,9240,1900],{"class":844},[70,9242,9243,9245,9247],{"class":564,"line":811},[70,9244,2606],{"class":1587},[70,9246,1696],{"class":1014},[70,9248,1335],{"class":844},[70,9250,9251,9253,9255,9257,9259,9261,9264],{"class":564,"line":817},[70,9252,9178],{"class":1587},[70,9254,5259],{"class":852},[70,9256,5239],{"class":844},[70,9258,6757],{"class":852},[70,9260,5239],{"class":844},[70,9262,9263],{"class":1004},"LockedOut",[70,9265,1517],{"class":844},[70,9267,9268,9270,9272,9274,9277,9280,9283],{"class":564,"line":823},[70,9269,8704],{"class":852},[70,9271,957],{"class":844},[70,9273,4271],{"class":844},[70,9275,9276],{"class":867},"Locked. Try again in ",[70,9278,9279],{"class":1014},"%u",[70,9281,9282],{"class":867}," minutes. ",[70,9284,6591],{"class":844},[70,9286,9287,9289,9292,9294,9296,9298,9300,9302,9305],{"class":564,"line":829},[70,9288,8818],{"class":844},[70,9290,9291],{"class":867},"(The gate and the logon screen share one lockout.)",[70,9293,4307],{"class":1014},[70,9295,934],{"class":844},[70,9297,1536],{"class":844},[70,9299,6810],{"class":1004},[70,9301,25],{"class":844},[70,9303,9304],{"class":1004},"minutesLeft",[70,9306,1900],{"class":844},[70,9308,9309,9311,9313],{"class":564,"line":834},[70,9310,2606],{"class":1587},[70,9312,1696],{"class":1014},[70,9314,1335],{"class":844},[70,9316,9317,9319,9321,9323,9325,9327,9330],{"class":564,"line":841},[70,9318,9178],{"class":1587},[70,9320,5259],{"class":852},[70,9322,5239],{"class":844},[70,9324,6757],{"class":852},[70,9326,5239],{"class":844},[70,9328,9329],{"class":1004},"Error",[70,9331,1517],{"class":844},[70,9333,9334,9336,9338,9340,9343,9345,9347],{"class":564,"line":856},[70,9335,8704],{"class":852},[70,9337,957],{"class":844},[70,9339,4271],{"class":844},[70,9341,9342],{"class":867},"Could not lock\u002Fread\u002Fwrite the 2FA state. Nothing opened.",[70,9344,4307],{"class":1014},[70,9346,934],{"class":844},[70,9348,1900],{"class":844},[70,9350,9351,9353,9355],{"class":564,"line":874},[70,9352,2606],{"class":1587},[70,9354,1696],{"class":1014},[70,9356,1335],{"class":844},[70,9358,9359,9361,9363,9365,9367,9369,9372],{"class":564,"line":888},[70,9360,9178],{"class":1587},[70,9362,5259],{"class":852},[70,9364,5239],{"class":844},[70,9366,6757],{"class":852},[70,9368,5239],{"class":844},[70,9370,9371],{"class":1004},"Replayed",[70,9373,1517],{"class":844},[70,9375,9376,9378,9380,9382,9385],{"class":564,"line":902},[70,9377,8704],{"class":852},[70,9379,957],{"class":844},[70,9381,4271],{"class":844},[70,9383,9384],{"class":867},"That code was already used. Wait for the next one - and note that ",[70,9386,6591],{"class":844},[70,9388,9389,9391,9394,9396,9399,9401,9404,9406,9408],{"class":564,"line":916},[70,9390,8818],{"class":844},[70,9392,9393],{"class":867},"retrying a used code counts as a failure (",[70,9395,9279],{"class":1014},[70,9397,9398],{"class":867}," in a row",[70,9400,8713],{"class":1014},[70,9402,9403],{"class":867},").",[70,9405,4307],{"class":1014},[70,9407,934],{"class":844},[70,9409,2945],{"class":844},[70,9411,9412,9415,9417,9420,9422,9424,9426,9428,9430,9432,9435,9437,9439,9442],{"class":564,"line":921},[70,9413,9414],{"class":1004},"                info",[70,9416,25],{"class":844},[70,9418,9419],{"class":1004},"failures",[70,9421,1536],{"class":844},[70,9423,6810],{"class":1004},[70,9425,25],{"class":844},[70,9427,9304],{"class":1004},[70,9429,3147],{"class":848},[70,9431,1312],{"class":844},[70,9433,9434],{"class":867},", account now locked",[70,9436,934],{"class":844},[70,9438,1547],{"class":848},[70,9440,9441],{"class":844}," L\"\"",[70,9443,1900],{"class":844},[70,9445,9446,9448,9450],{"class":564,"line":940},[70,9447,2606],{"class":1587},[70,9449,1696],{"class":1014},[70,9451,1335],{"class":844},[70,9453,9454,9457,9460],{"class":564,"line":945},[70,9455,9456],{"class":1587},"    default",[70,9458,9459],{"class":844},":",[70,9461,9462],{"class":568},"   \u002F\u002F Wrong\n",[70,9464,9465,9467,9469,9472,9474,9476],{"class":564,"line":975},[70,9466,2579],{"class":1587},[70,9468,1591],{"class":844},[70,9470,9471],{"class":1004},"info",[70,9473,25],{"class":844},[70,9475,9304],{"class":1004},[70,9477,972],{"class":844},[70,9479,9480,9483,9485,9487,9490,9492,9495,9497,9500,9502,9504],{"class":564,"line":980},[70,9481,9482],{"class":852},"            wprintf",[70,9484,957],{"class":844},[70,9486,4271],{"class":844},[70,9488,9489],{"class":867},"Invalid one-time code. ",[70,9491,9279],{"class":1014},[70,9493,9494],{"class":867}," in a row - account locked for ",[70,9496,9279],{"class":1014},[70,9498,9499],{"class":867}," minutes.",[70,9501,4307],{"class":1014},[70,9503,934],{"class":844},[70,9505,2945],{"class":844},[70,9507,9508,9511,9513,9515,9517,9519,9521,9523],{"class":564,"line":991},[70,9509,9510],{"class":1004},"                    info",[70,9512,25],{"class":844},[70,9514,9419],{"class":1004},[70,9516,1536],{"class":844},[70,9518,6810],{"class":1004},[70,9520,25],{"class":844},[70,9522,9304],{"class":1004},[70,9524,1900],{"class":844},[70,9526,9527],{"class":564,"line":1023},[70,9528,9529],{"class":1587},"        else\n",[70,9531,9532,9534,9536,9538,9541,9543,9546,9548,9550,9552,9554,9556,9558],{"class":564,"line":1031},[70,9533,9482],{"class":852},[70,9535,957],{"class":844},[70,9537,4271],{"class":844},[70,9539,9540],{"class":867},"Invalid one-time code. (",[70,9542,9279],{"class":1014},[70,9544,9545],{"class":867}," in a row)",[70,9547,4307],{"class":1014},[70,9549,934],{"class":844},[70,9551,1536],{"class":844},[70,9553,6810],{"class":1004},[70,9555,25],{"class":844},[70,9557,9419],{"class":1004},[70,9559,1900],{"class":844},[70,9561,9562,9564,9566],{"class":564,"line":1037},[70,9563,2606],{"class":1587},[70,9565,1696],{"class":1014},[70,9567,1335],{"class":844},[70,9569,9570],{"class":564,"line":1043},[70,9571,2405],{"class":844},[70,9573,9574],{"class":564,"line":1049},[70,9575,838],{"emptyLinePlaceholder":837},[70,9577,9578],{"class":564,"line":1055},[70,9579,9580],{"class":568},"    \u002F\u002F The code is burned now (lastStep was saved). If the timestamp write\n",[70,9582,9583],{"class":564,"line":1065},[70,9584,9585],{"class":568},"    \u002F\u002F fails, the user re-runs with the next code - annoying, never unsafe.\n",[70,9587,9588,9590,9592,9594,9596,9598,9600,9602,9604],{"class":564,"line":1091},[70,9589,5893],{"class":1587},[70,9591,1591],{"class":844},[70,9593,1664],{"class":1587},[70,9595,8556],{"class":852},[70,9597,957],{"class":844},[70,9599,2278],{"class":1004},[70,9601,1536],{"class":844},[70,9603,2461],{"class":852},[70,9605,6179],{"class":844},[70,9607,9608],{"class":564,"line":1098},[70,9609,1509],{"class":844},[70,9611,9612,9614,9616,9618,9621,9623,9625],{"class":564,"line":1103},[70,9613,8704],{"class":852},[70,9615,957],{"class":844},[70,9617,4271],{"class":844},[70,9619,9620],{"class":867},"Code accepted, but the gate timestamp could not be written (elevated?).",[70,9622,4307],{"class":1014},[70,9624,934],{"class":844},[70,9626,1900],{"class":844},[70,9628,9629,9631,9633],{"class":564,"line":1113},[70,9630,2606],{"class":1587},[70,9632,1696],{"class":1014},[70,9634,1335],{"class":844},[70,9636,9637],{"class":564,"line":1129},[70,9638,2405],{"class":844},[70,9640,9641],{"class":564,"line":1136},[70,9642,838],{"emptyLinePlaceholder":837},[70,9644,9645,9647,9649,9651,9653,9655],{"class":564,"line":1141},[70,9646,1297],{"class":956},[70,9648,1391],{"class":852},[70,9650,3690],{"class":1004},[70,9652,1349],{"class":848},[70,9654,7996],{"class":852},[70,9656,2784],{"class":844},[70,9658,9659,9662,9664,9667,9669,9671],{"class":564,"line":1147},[70,9660,9661],{"class":852},"    GateLog",[70,9663,957],{"class":844},[70,9665,9666],{"class":1004},"EVENTLOG_INFORMATION_TYPE",[70,9668,1536],{"class":844},[70,9670,1399],{"class":1014},[70,9672,2945],{"class":844},[70,9674,9675,9678,9680,9682,9684,9686,9689,9691,9693,9695,9697,9699,9701,9703,9705,9707,9709,9711,9714,9716,9718,9720],{"class":564,"line":1153},[70,9676,9677],{"class":852},"            std",[70,9679,5239],{"class":844},[70,9681,6772],{"class":852},[70,9683,957],{"class":844},[70,9685,4271],{"class":844},[70,9687,9688],{"class":867},"Gate opened for '",[70,9690,934],{"class":844},[70,9692,1008],{"class":844},[70,9694,1693],{"class":848},[70,9696,8423],{"class":1004},[70,9698,1693],{"class":848},[70,9700,1312],{"class":844},[70,9702,4688],{"class":867},[70,9704,934],{"class":844},[70,9706,1693],{"class":848},[70,9708,6767],{"class":852},[70,9710,5239],{"class":844},[70,9712,9713],{"class":852},"to_wstring",[70,9715,957],{"class":844},[70,9717,2278],{"class":1004},[70,9719,1008],{"class":844},[70,9721,9722],{"class":848}," +\n",[70,9724,9725,9728,9731,9733,9735,9737,9739,9741,9743,9745,9747,9749,9751,9754,9756],{"class":564,"line":1159},[70,9726,9727],{"class":844},"            L\"",[70,9729,9730],{"class":867},") for ",[70,9732,934],{"class":844},[70,9734,1693],{"class":848},[70,9736,6767],{"class":852},[70,9738,5239],{"class":844},[70,9740,9713],{"class":852},[70,9742,957],{"class":844},[70,9744,3735],{"class":1004},[70,9746,1008],{"class":844},[70,9748,1693],{"class":848},[70,9750,1312],{"class":844},[70,9752,9753],{"class":867}," s.",[70,9755,934],{"class":844},[70,9757,1900],{"class":844},[70,9759,9760],{"class":564,"line":1172},[70,9761,9762],{"class":568},"    \u002F\u002F The filter only runs when a logon is made. A share mapped or a WinRM\n",[70,9764,9765],{"class":564,"line":1177},[70,9766,9767],{"class":568},"    \u002F\u002F session opened inside the window stays up after it closes, so say so.\n",[70,9769,9770,9773,9775,9777,9780,9782,9785,9787,9789,9791],{"class":564,"line":1183},[70,9771,9772],{"class":852},"    wprintf",[70,9774,957],{"class":844},[70,9776,4271],{"class":844},[70,9778,9779],{"class":867},"Gate open for '",[70,9781,8713],{"class":1014},[70,9783,9784],{"class":867},"' for ",[70,9786,8261],{"class":1014},[70,9788,9753],{"class":867},[70,9790,4307],{"class":1014},[70,9792,6591],{"class":844},[70,9794,9795,9797,9800,9802],{"class":564,"line":1189},[70,9796,9727],{"class":844},[70,9798,9799],{"class":867},"Anyone who can authenticate as this account passes while the window lasts -",[70,9801,4307],{"class":1014},[70,9803,6591],{"class":844},[70,9805,9807,9809,9812,9814],{"class":564,"line":9806},81,[70,9808,9727],{"class":844},[70,9810,9811],{"class":867},"close it with \u002Fclose when you are done. Closing stops new logons only:",[70,9813,4307],{"class":1014},[70,9815,6591],{"class":844},[70,9817,9819,9821,9824,9826,9828],{"class":564,"line":9818},82,[70,9820,9727],{"class":844},[70,9822,9823],{"class":867},"a session that is already up stays up until it disconnects.",[70,9825,4307],{"class":1014},[70,9827,934],{"class":844},[70,9829,2945],{"class":844},[70,9831,9833,9836,9838,9840,9842,9844,9846,9848,9850],{"class":564,"line":9832},83,[70,9834,9835],{"class":1004},"            user",[70,9837,1536],{"class":844},[70,9839,7705],{"class":848},[70,9841,8270],{"class":956},[70,9843,8273],{"class":956},[70,9845,7711],{"class":848},[70,9847,957],{"class":844},[70,9849,3735],{"class":1004},[70,9851,2285],{"class":844},[70,9853,9855,9857,9859],{"class":564,"line":9854},84,[70,9856,6234],{"class":1587},[70,9858,1811],{"class":1014},[70,9860,1335],{"class":844},[70,9862,9864],{"class":564,"line":9863},85,[70,9865,5666],{"class":844},[17,9867,9868,9871,9872,9874],{},[41,9869,9870],{},"Open"," is short because ",[41,9873,6743],{}," does the work.",[17,9876,9877],{},"The code comes from the prompt by default. As an argument it ends up in the process command line, where other processes with enough rights can read it, in your shell history, and in 4688 records if command-line auditing is on. The code is single-use and burned a second later, so the exposure is small, but the tool says so every time you do it.",[17,9879,9880,9881,9884,9885,9887,9888,9890,9891,9893,9894,9897],{},"The code buffer gets ",[41,9882,9883],{},"SecureZeroMemory"," right after it's copied, and the ",[41,9886,511],{}," right after ",[41,9889,6743],{},". It has to be ",[41,9892,9883],{},", because the compiler may drop a ",[41,9895,9896],{},"memset"," on memory that nothing reads again.",[17,9899,9900,9901,9903,9904,9906],{},"Then the six results from ",[41,9902,6588],{},", each with a message that says what happened. ",[41,9905,9371],{}," gets its own text, because \"invalid code\" for a correct code you used at the tile a moment ago would be confusing. The message also says that retrying a used code counts as a failure.",[17,9908,9909,9910,9912,9913,9915],{},"The order at the end is deliberate: ",[41,9911,6743],{}," burns the code first by saving the time step, and then ",[41,9914,8556],{}," writes the timestamp. If the write fails, you run it again with the next code, which is annoying but never unsafe. The other order would leave an open gate behind a code that somebody could use again.",[17,9917,9918],{},"The message at the end also says what the gate doesn't do. The filter only runs when a logon is made, so a session that is already up stays up after the window closes. My first version only said \"close it with \u002Fclose when you are done\", which sounded like more than it does.",[250,9920,9922],{"id":9921},"one-shared-lockout","One shared lockout",[17,9924,9925,9926,438,9928,438,9930,438,9932,1221,9934,9936],{},"The gate tool doesn't reimplement the code check. It links Doppio's ",[41,9927,437],{},[41,9929,441],{},[41,9931,432],{},[41,9933,62],{},[41,9935,448],{}," unmodified, as a pinned submodule, and that's what keeps the lockout honest.",[17,9938,9939,9940,9942,9943,9946],{},"If Ristretto kept its own failure counter, an attacker would get two independent sets of guesses against the same secret: five at the tile and five at the gate, each with its own doubling lock. Sharing ",[41,9941,62],{}," means one counter, one ",[41,9944,9945],{},"lastStep",", one state file:",[105,9948,9949,9952,9958],{},[108,9950,9951],{},"Five wrong codes in a row lock the account, at the tile, at the gate or any mix of the two, and the lock doubles from 5 to 10, 20, 40 and at most 60 minutes.",[108,9953,9954,9955,9957],{},"A code burned at the logon screen is ",[41,9956,9371],{}," at the gate, and the other way round.",[108,9959,9960,9961,9963],{},"While the account is locked, ",[41,9962,6743],{}," doesn't check the code at all.",[17,9965,9966,9967,9975,9976,9978,9979,9982,9983,9986,9987,9990],{},"This only works if ",[49,9968,9969,9971,9972],{},[41,9970,302],{}," sits in the same folder as ",[41,9973,9974],{},"TacProvider.dll",", and that's easy to get wrong. Doppio's ",[41,9977,6818],{}," builds the path of ",[41,9980,9981],{},"state.lock"," from the folder of the module that runs the code (",[41,9984,9985],{},"GetModuleFileNameW"," on itself). Put the exe somewhere else and you get a second lock file. Then the tile and the gate can write the state at the same time, and the shared counter isn't shared anymore. The installer copies the exe to ",[41,9988,9989],{},"C:\\Program Files\\TheAdminCafe\\"," for this reason.",[250,9992,9994],{"id":9993},"the-compromise-i-made-on-purpose","The compromise I made on purpose",[17,9996,9997,9999],{},[41,9998,6588],{}," says, in so many words, to check the password before the code. Part 1 has a whole chapter on why: if the code comes first, anybody at the logon screen can lock out every enrolled account by typing garbage.",[17,10001,10002],{},"The gate checks the code without a password proof. I went back and forth on this one.",[17,10004,10005],{},"The argument for doing it: the gate grants nothing by itself. NTLM checks the password when you make the connection, so an open gate without the password or its NT hash is worth nothing. And the tool is admin-only three times over: the state lock file, the State key and the DPAPI secret are all SYSTEM and Administrators only. An attacker who can run it elevated can already switch the filter off.",[17,10007,10008,10009,8557,10012,10015,10016,10019],{},"The obvious fix is circular, and that settled it. To prove the password I'd call ",[41,10010,10011],{},"LogonUserW",[41,10013,10014],{},"LOGON32_LOGON_NETWORK",", and that is a network logon, which my own filter vetoes while the gate stays closed. ",[41,10017,10018],{},"LOGON32_LOGON_INTERACTIVE"," would work, but then every gate you open writes a 4624 type 2 into the Security log, which is a lie about what happened.",[17,10021,10022],{},"I left it as it is, and the lockout exposure is the price. An elevated attacker can burn the gate's guesses and lock the account out of its own logon screen. That's a denial of service by somebody who already is an administrator and could do worse with that right.",[250,10024,10026],{"id":10025},"status-close-and-close-all","Status, close and close all",[258,10028,10030],{"className":557,"code":10029,"filename":6429,"language":559,"meta":264,"style":264},"static int Status(const wchar_t* user)\n{\n    std::wstring sid; DWORD rid = 0;\n    if (!RidOf(user, sid, rid))\n    {\n        wprintf(L\"'%s' is not a local user account.\\n\", user);\n        return 1;\n    }\n\n    DWORD mode = 1, cb = sizeof(mode);\n    if (RegGetValueW(HKEY_LOCAL_MACHINE, kConfigPath, L\"Mode\",\n                     RRF_RT_REG_DWORD, nullptr, &mode, &cb) != ERROR_SUCCESS)\n        mode = 1;\n    if (!mode)\n        wprintf(L\"WARNING: Mode = 0, enforcement is off. The filter lets every network \"\n                L\"logon through regardless of the gate.\\n\");\n\n    \u002F\u002F The filter only gates accounts in Doppio's RID index. Without this\n    \u002F\u002F check, \u002Fstatus said \"gate closed\" for an account the filter lets\n    \u002F\u002F through anyway, for example after enrolling without enroll \u002Freindex.\n    switch (tac::EnrollmentByRid(rid))\n    {\n    case tac::Enrollment::No:\n        wprintf(L\"'%s' is not in Doppio's RID index, so the filter does not gate it at all. \"\n                L\"Run enroll \u002Freindex if it should be gated.\\n\", user);\n        return 0;\n    case tac::Enrollment::Unknown:\n        wprintf(L\"Doppio's RID index could not be read. The filter refuses every network \"\n                L\"logon for '%s' until it can.\\n\", user);\n        return 1;\n    case tac::Enrollment::Yes:\n    default:\n        break;\n    }\n\n    WCHAR value[16];\n    swprintf_s(value, L\"%lu\", static_cast\u003Cunsigned long>(rid));\n    ULONGLONG ts = 0; cb = sizeof(ts);\n    if (RegGetValueW(HKEY_LOCAL_MACHINE, kGatePath, value,\n                     RRF_RT_REG_QWORD, nullptr, &ts, &cb) != ERROR_SUCCESS)\n    {\n        wprintf(L\"'%s': gate closed (never opened).\\n\", user);\n        return 0;\n    }\n\n    const uint64_t now = NowUnix();\n    if (now \u003C ts)\n    {\n        wprintf(L\"'%s': clock skew, the filter treats this as closed.\\n\", user);\n        return 0;\n    }\n\n    const uint64_t age    = now - ts;\n    const DWORD    window = CurrentWindowSec();\n    if (age \u003C= window)\n        wprintf(L\"'%s': gate OPEN, %llu s remaining (opened %llu s ago, window %lu s).\\n\",\n                user, (unsigned long long)(window - age), (unsigned long long)age,\n                static_cast\u003Cunsigned long>(window));\n    else\n        wprintf(L\"'%s': gate closed (expired; last unlock %llu s ago, window %lu s).\\n\",\n                user, (unsigned long long)age, static_cast\u003Cunsigned long>(window));\n    return 0;\n}\n\nstatic int Close(const wchar_t* user)\n{\n    std::wstring sid; DWORD rid = 0;\n    if (!RidOf(user, sid, rid)) { wprintf(L\"unknown account\\n\"); return 1; }\n    bool ok = WriteTs(rid, 0);\n    if (ok)\n        GateLog(EVENTLOG_INFORMATION_TYPE, 302,\n                std::wstring(L\"Gate closed for '\") + user + L\"'.\");\n    wprintf(ok ? L\"closed. Sessions that are already up stay up.\\n\" : L\"close failed\\n\");\n    return ok ? 0 : 1;\n}\n\n\u002F\u002F Panic button: drop every open gate at once without having to remember who\n\u002F\u002F is enrolled. It always reads index 0 and deletes that value until the key\n\u002F\u002F is empty. Deleting while enumerating upwards shifts the indices, and the old\n\u002F\u002F fixed list of 256 names of up to 31 characters stopped early on a long name\n\u002F\u002F or a full list, then logged \"All gates closed\" anyway. The buffer takes the\n\u002F\u002F longest value name the registry allows, and any error is reported.\nstatic int CloseAll()\n{\n    HKEY k = nullptr;\n    if (OpenProtected(kGatePath, &k) != ERROR_SUCCESS)\n    {\n        wprintf(L\"Could not open the gate key (elevated?).\\n\");\n        return 1;\n    }\n\n    std::vector\u003CWCHAR> name(16384);        \u002F\u002F 16,383 characters + terminator\n    DWORD closed = 0;\n    LSTATUS st = ERROR_SUCCESS;\n    for (;;)\n    {\n        DWORD cch = static_cast\u003CDWORD>(name.size());\n        st = RegEnumValueW(k, 0, name.data(), &cch, nullptr, nullptr, nullptr, nullptr);\n        if (st != ERROR_SUCCESS)\n            break;\n        st = RegDeleteValueW(k, name.data());\n        if (st != ERROR_SUCCESS)\n            break;\n        ++closed;\n    }\n    RegCloseKey(k);\n\n    if (st != ERROR_NO_MORE_ITEMS)\n    {\n        GateLog(EVENTLOG_WARNING_TYPE, 302,\n                L\"Closing all gates stopped after \" + std::to_wstring(closed) +\n                L\" with error \" + std::to_wstring(st) + L\". Gates may still be open.\");\n        wprintf(L\"closed %lu gate(s), then stopped with error %ld. Gates may still be open,\\n\"\n                L\"check HKLM\\\\%s by hand.\\n\",\n                static_cast\u003Cunsigned long>(closed), static_cast\u003Clong>(st), kGatePath);\n        return 1;\n    }\n\n    GateLog(EVENTLOG_INFORMATION_TYPE, 302,\n            L\"All gates closed (\" + std::to_wstring(closed) + L\").\");\n    wprintf(L\"closed %lu gate(s). Sessions that are already up stay up.\\n\",\n            static_cast\u003Cunsigned long>(closed));\n    return 0;\n}\n",[41,10031,10032,10051,10055,10077,10101,10105,10130,10138,10142,10146,10172,10198,10226,10237,10249,10262,10275,10279,10284,10289,10294,10312,10316,10332,10349,10366,10374,10390,10403,10425,10433,10449,10455,10461,10465,10469,10481,10513,10537,10559,10588,10592,10617,10625,10629,10633,10647,10661,10665,10690,10698,10702,10706,10724,10739,10753,10790,10829,10846,10850,10879,10913,10921,10925,10929,10948,10952,10974,11024,11044,11054,11070,11105,11137,11153,11157,11161,11166,11171,11176,11181,11186,11191,11202,11206,11218,11245,11250,11268,11277,11282,11287,11315,11329,11342,11351,11356,11381,11432,11447,11454,11477,11492,11499,11510,11515,11526,11531,11547,11552,11567,11593,11628,11655,11675,11709,11718,11723,11728,11743,11777,11799,11817,11826],{"__ignoreMap":264},[70,10033,10034,10036,10038,10041,10043,10045,10047,10049],{"class":564,"line":565},[70,10035,7056],{"class":956},[70,10037,8622],{"class":956},[70,10039,10040],{"class":852}," Status",[70,10042,957],{"class":844},[70,10044,1641],{"class":956},[70,10046,7628],{"class":956},[70,10048,8423],{"class":1004},[70,10050,972],{"class":844},[70,10052,10053],{"class":564,"line":572},[70,10054,1292],{"class":844},[70,10056,10057,10059,10061,10063,10065,10067,10069,10071,10073,10075],{"class":564,"line":578},[70,10058,8653],{"class":852},[70,10060,5239],{"class":844},[70,10062,6772],{"class":1004},[70,10064,6777],{"class":1004},[70,10066,1402],{"class":844},[70,10068,1391],{"class":1004},[70,10070,2177],{"class":1004},[70,10072,1349],{"class":848},[70,10074,1811],{"class":1014},[70,10076,1335],{"class":844},[70,10078,10079,10081,10083,10085,10087,10089,10091,10093,10095,10097,10099],{"class":564,"line":584},[70,10080,5893],{"class":1587},[70,10082,1591],{"class":844},[70,10084,1664],{"class":1587},[70,10086,8582],{"class":852},[70,10088,957],{"class":844},[70,10090,8479],{"class":1004},[70,10092,1536],{"class":844},[70,10094,6777],{"class":1004},[70,10096,1536],{"class":844},[70,10098,2177],{"class":1004},[70,10100,5158],{"class":844},[70,10102,10103],{"class":564,"line":590},[70,10104,1509],{"class":844},[70,10106,10107,10109,10111,10113,10115,10117,10120,10122,10124,10126,10128],{"class":564,"line":596},[70,10108,8704],{"class":852},[70,10110,957],{"class":844},[70,10112,4271],{"class":844},[70,10114,1617],{"class":867},[70,10116,8713],{"class":1014},[70,10118,10119],{"class":867},"' is not a local user account.",[70,10121,4307],{"class":1014},[70,10123,934],{"class":844},[70,10125,1536],{"class":844},[70,10127,8423],{"class":1004},[70,10129,1900],{"class":844},[70,10131,10132,10134,10136],{"class":564,"line":602},[70,10133,2606],{"class":1587},[70,10135,1696],{"class":1014},[70,10137,1335],{"class":844},[70,10139,10140],{"class":564,"line":607},[70,10141,2405],{"class":844},[70,10143,10144],{"class":564,"line":613},[70,10145,838],{"emptyLinePlaceholder":837},[70,10147,10148,10150,10153,10155,10157,10159,10161,10163,10165,10167,10170],{"class":564,"line":619},[70,10149,7754],{"class":852},[70,10151,10152],{"class":1004}," mode",[70,10154,1349],{"class":848},[70,10156,1696],{"class":1014},[70,10158,1536],{"class":844},[70,10160,2905],{"class":1004},[70,10162,1349],{"class":848},[70,10164,2910],{"class":848},[70,10166,957],{"class":844},[70,10168,10169],{"class":1004},"mode",[70,10171,1900],{"class":844},[70,10173,10174,10176,10178,10180,10182,10184,10186,10188,10190,10192,10194,10196],{"class":564,"line":625},[70,10175,5893],{"class":1587},[70,10177,1591],{"class":844},[70,10179,2925],{"class":852},[70,10181,957],{"class":844},[70,10183,1467],{"class":1004},[70,10185,1536],{"class":844},[70,10187,1344],{"class":1004},[70,10189,1536],{"class":844},[70,10191,1312],{"class":844},[70,10193,3097],{"class":867},[70,10195,934],{"class":844},[70,10197,2945],{"class":844},[70,10199,10200,10202,10204,10206,10208,10210,10212,10214,10216,10218,10220,10222,10224],{"class":564,"line":631},[70,10201,8057],{"class":1004},[70,10203,1536],{"class":844},[70,10205,2955],{"class":1014},[70,10207,1536],{"class":844},[70,10209,2960],{"class":848},[70,10211,10169],{"class":1004},[70,10213,1536],{"class":844},[70,10215,2960],{"class":848},[70,10217,2969],{"class":1004},[70,10219,1008],{"class":844},[70,10221,2310],{"class":848},[70,10223,2977],{"class":1004},[70,10225,972],{"class":844},[70,10227,10228,10231,10233,10235],{"class":564,"line":636},[70,10229,10230],{"class":1004},"        mode",[70,10232,1349],{"class":848},[70,10234,1696],{"class":1014},[70,10236,1335],{"class":844},[70,10238,10239,10241,10243,10245,10247],{"class":564,"line":642},[70,10240,5893],{"class":1587},[70,10242,1591],{"class":844},[70,10244,1664],{"class":1587},[70,10246,10169],{"class":1004},[70,10248,972],{"class":844},[70,10250,10251,10253,10255,10257,10260],{"class":564,"line":648},[70,10252,8704],{"class":852},[70,10254,957],{"class":844},[70,10256,4271],{"class":844},[70,10258,10259],{"class":867},"WARNING: Mode = 0, enforcement is off. The filter lets every network ",[70,10261,6591],{"class":844},[70,10263,10264,10266,10269,10271,10273],{"class":564,"line":654},[70,10265,8818],{"class":844},[70,10267,10268],{"class":867},"logon through regardless of the gate.",[70,10270,4307],{"class":1014},[70,10272,934],{"class":844},[70,10274,1900],{"class":844},[70,10276,10277],{"class":564,"line":659},[70,10278,838],{"emptyLinePlaceholder":837},[70,10280,10281],{"class":564,"line":665},[70,10282,10283],{"class":568},"    \u002F\u002F The filter only gates accounts in Doppio's RID index. Without this\n",[70,10285,10286],{"class":564,"line":671},[70,10287,10288],{"class":568},"    \u002F\u002F check, \u002Fstatus said \"gate closed\" for an account the filter lets\n",[70,10290,10291],{"class":564,"line":677},[70,10292,10293],{"class":568},"    \u002F\u002F through anyway, for example after enrolling without enroll \u002Freindex.\n",[70,10295,10296,10298,10300,10302,10304,10306,10308,10310],{"class":564,"line":683},[70,10297,9162],{"class":1587},[70,10299,1591],{"class":844},[70,10301,5236],{"class":852},[70,10303,5239],{"class":844},[70,10305,5242],{"class":852},[70,10307,957],{"class":844},[70,10309,2278],{"class":1004},[70,10311,5158],{"class":844},[70,10313,10314],{"class":564,"line":689},[70,10315,1509],{"class":844},[70,10317,10318,10320,10322,10324,10326,10328,10330],{"class":564,"line":695},[70,10319,9178],{"class":1587},[70,10321,5259],{"class":852},[70,10323,5239],{"class":844},[70,10325,5264],{"class":852},[70,10327,5239],{"class":844},[70,10329,3333],{"class":1004},[70,10331,1517],{"class":844},[70,10333,10334,10336,10338,10340,10342,10344,10347],{"class":564,"line":701},[70,10335,8704],{"class":852},[70,10337,957],{"class":844},[70,10339,4271],{"class":844},[70,10341,1617],{"class":867},[70,10343,8713],{"class":1014},[70,10345,10346],{"class":867},"' is not in Doppio's RID index, so the filter does not gate it at all. ",[70,10348,6591],{"class":844},[70,10350,10351,10353,10356,10358,10360,10362,10364],{"class":564,"line":706},[70,10352,8818],{"class":844},[70,10354,10355],{"class":867},"Run enroll \u002Freindex if it should be gated.",[70,10357,4307],{"class":1014},[70,10359,934],{"class":844},[70,10361,1536],{"class":844},[70,10363,8423],{"class":1004},[70,10365,1900],{"class":844},[70,10367,10368,10370,10372],{"class":564,"line":712},[70,10369,2606],{"class":1587},[70,10371,1811],{"class":1014},[70,10373,1335],{"class":844},[70,10375,10376,10378,10380,10382,10384,10386,10388],{"class":564,"line":718},[70,10377,9178],{"class":1587},[70,10379,5259],{"class":852},[70,10381,5239],{"class":844},[70,10383,5264],{"class":852},[70,10385,5239],{"class":844},[70,10387,3358],{"class":1004},[70,10389,1517],{"class":844},[70,10391,10392,10394,10396,10398,10401],{"class":564,"line":723},[70,10393,8704],{"class":852},[70,10395,957],{"class":844},[70,10397,4271],{"class":844},[70,10399,10400],{"class":867},"Doppio's RID index could not be read. The filter refuses every network ",[70,10402,6591],{"class":844},[70,10404,10405,10407,10410,10412,10415,10417,10419,10421,10423],{"class":564,"line":729},[70,10406,8818],{"class":844},[70,10408,10409],{"class":867},"logon for '",[70,10411,8713],{"class":1014},[70,10413,10414],{"class":867},"' until it can.",[70,10416,4307],{"class":1014},[70,10418,934],{"class":844},[70,10420,1536],{"class":844},[70,10422,8423],{"class":1004},[70,10424,1900],{"class":844},[70,10426,10427,10429,10431],{"class":564,"line":735},[70,10428,2606],{"class":1587},[70,10430,1696],{"class":1014},[70,10432,1335],{"class":844},[70,10434,10435,10437,10439,10441,10443,10445,10447],{"class":564,"line":741},[70,10436,9178],{"class":1587},[70,10438,5259],{"class":852},[70,10440,5239],{"class":844},[70,10442,5264],{"class":852},[70,10444,5239],{"class":844},[70,10446,5366],{"class":1004},[70,10448,1517],{"class":844},[70,10450,10451,10453],{"class":564,"line":746},[70,10452,9456],{"class":1587},[70,10454,1517],{"class":844},[70,10456,10457,10459],{"class":564,"line":752},[70,10458,9196],{"class":1587},[70,10460,1335],{"class":844},[70,10462,10463],{"class":564,"line":758},[70,10464,2405],{"class":844},[70,10466,10467],{"class":564,"line":764},[70,10468,838],{"emptyLinePlaceholder":837},[70,10470,10471,10473,10475,10477,10479],{"class":564,"line":769},[70,10472,8235],{"class":852},[70,10474,3449],{"class":1004},[70,10476,1601],{"class":844},[70,10478,8242],{"class":1014},[70,10480,1798],{"class":844},[70,10482,10483,10485,10487,10489,10491,10493,10495,10497,10499,10501,10503,10505,10507,10509,10511],{"class":564,"line":775},[70,10484,8249],{"class":852},[70,10486,957],{"class":844},[70,10488,8254],{"class":1004},[70,10490,1536],{"class":844},[70,10492,1312],{"class":844},[70,10494,8261],{"class":1014},[70,10496,934],{"class":844},[70,10498,1536],{"class":844},[70,10500,7705],{"class":848},[70,10502,8270],{"class":956},[70,10504,8273],{"class":956},[70,10506,7711],{"class":848},[70,10508,957],{"class":844},[70,10510,2278],{"class":1004},[70,10512,2285],{"class":844},[70,10514,10515,10517,10519,10521,10523,10525,10527,10529,10531,10533,10535],{"class":564,"line":781},[70,10516,2458],{"class":852},[70,10518,3487],{"class":1004},[70,10520,1349],{"class":848},[70,10522,1811],{"class":1014},[70,10524,1402],{"class":844},[70,10526,2905],{"class":1004},[70,10528,1349],{"class":848},[70,10530,2910],{"class":848},[70,10532,957],{"class":844},[70,10534,3508],{"class":1004},[70,10536,1900],{"class":844},[70,10538,10539,10541,10543,10545,10547,10549,10551,10553,10555,10557],{"class":564,"line":787},[70,10540,5893],{"class":1587},[70,10542,1591],{"class":844},[70,10544,2925],{"class":852},[70,10546,957],{"class":844},[70,10548,1467],{"class":1004},[70,10550,1536],{"class":844},[70,10552,1303],{"class":1004},[70,10554,1536],{"class":844},[70,10556,3449],{"class":1004},[70,10558,2945],{"class":844},[70,10560,10561,10564,10566,10568,10570,10572,10574,10576,10578,10580,10582,10584,10586],{"class":564,"line":793},[70,10562,10563],{"class":1004},"                     RRF_RT_REG_QWORD",[70,10565,1536],{"class":844},[70,10567,2955],{"class":1014},[70,10569,1536],{"class":844},[70,10571,2960],{"class":848},[70,10573,3508],{"class":1004},[70,10575,1536],{"class":844},[70,10577,2960],{"class":848},[70,10579,2969],{"class":1004},[70,10581,1008],{"class":844},[70,10583,2310],{"class":848},[70,10585,2977],{"class":1004},[70,10587,972],{"class":844},[70,10589,10590],{"class":564,"line":799},[70,10591,1509],{"class":844},[70,10593,10594,10596,10598,10600,10602,10604,10607,10609,10611,10613,10615],{"class":564,"line":805},[70,10595,8704],{"class":852},[70,10597,957],{"class":844},[70,10599,4271],{"class":844},[70,10601,1617],{"class":867},[70,10603,8713],{"class":1014},[70,10605,10606],{"class":867},"': gate closed (never opened).",[70,10608,4307],{"class":1014},[70,10610,934],{"class":844},[70,10612,1536],{"class":844},[70,10614,8423],{"class":1004},[70,10616,1900],{"class":844},[70,10618,10619,10621,10623],{"class":564,"line":811},[70,10620,2606],{"class":1587},[70,10622,1811],{"class":1014},[70,10624,1335],{"class":844},[70,10626,10627],{"class":564,"line":817},[70,10628,2405],{"class":844},[70,10630,10631],{"class":564,"line":823},[70,10632,838],{"emptyLinePlaceholder":837},[70,10634,10635,10637,10639,10641,10643,10645],{"class":564,"line":829},[70,10636,1297],{"class":956},[70,10638,7059],{"class":956},[70,10640,2769],{"class":1004},[70,10642,1349],{"class":848},[70,10644,2461],{"class":852},[70,10646,2784],{"class":844},[70,10648,10649,10651,10653,10655,10657,10659],{"class":564,"line":834},[70,10650,5893],{"class":1587},[70,10652,1591],{"class":844},[70,10654,2824],{"class":1004},[70,10656,864],{"class":848},[70,10658,3487],{"class":1004},[70,10660,972],{"class":844},[70,10662,10663],{"class":564,"line":841},[70,10664,1509],{"class":844},[70,10666,10667,10669,10671,10673,10675,10677,10680,10682,10684,10686,10688],{"class":564,"line":856},[70,10668,8704],{"class":852},[70,10670,957],{"class":844},[70,10672,4271],{"class":844},[70,10674,1617],{"class":867},[70,10676,8713],{"class":1014},[70,10678,10679],{"class":867},"': clock skew, the filter treats this as closed.",[70,10681,4307],{"class":1014},[70,10683,934],{"class":844},[70,10685,1536],{"class":844},[70,10687,8423],{"class":1004},[70,10689,1900],{"class":844},[70,10691,10692,10694,10696],{"class":564,"line":874},[70,10693,2606],{"class":1587},[70,10695,1811],{"class":1014},[70,10697,1335],{"class":844},[70,10699,10700],{"class":564,"line":888},[70,10701,2405],{"class":844},[70,10703,10704],{"class":564,"line":902},[70,10705,838],{"emptyLinePlaceholder":837},[70,10707,10708,10710,10712,10714,10716,10718,10720,10722],{"class":564,"line":916},[70,10709,1297],{"class":956},[70,10711,7059],{"class":956},[70,10713,3627],{"class":1004},[70,10715,6924],{"class":848},[70,10717,2769],{"class":1004},[70,10719,2617],{"class":848},[70,10721,3487],{"class":1004},[70,10723,1335],{"class":844},[70,10725,10726,10728,10730,10733,10735,10737],{"class":564,"line":921},[70,10727,1297],{"class":956},[70,10729,1391],{"class":852},[70,10731,10732],{"class":1004},"    window",[70,10734,1349],{"class":848},[70,10736,7996],{"class":852},[70,10738,2784],{"class":844},[70,10740,10741,10743,10745,10747,10749,10751],{"class":564,"line":940},[70,10742,5893],{"class":1587},[70,10744,1591],{"class":844},[70,10746,3652],{"class":1004},[70,10748,3726],{"class":848},[70,10750,3690],{"class":1004},[70,10752,972],{"class":844},[70,10754,10755,10757,10759,10761,10763,10765,10768,10771,10774,10776,10779,10781,10784,10786,10788],{"class":564,"line":945},[70,10756,8704],{"class":852},[70,10758,957],{"class":844},[70,10760,4271],{"class":844},[70,10762,1617],{"class":867},[70,10764,8713],{"class":1014},[70,10766,10767],{"class":867},"': gate OPEN, ",[70,10769,10770],{"class":1014},"%llu",[70,10772,10773],{"class":867}," s remaining (opened ",[70,10775,10770],{"class":1014},[70,10777,10778],{"class":867}," s ago, window ",[70,10780,8261],{"class":1014},[70,10782,10783],{"class":867}," s).",[70,10785,4307],{"class":1014},[70,10787,934],{"class":844},[70,10789,2945],{"class":844},[70,10791,10792,10795,10797,10799,10801,10803,10805,10807,10809,10811,10813,10815,10817,10819,10821,10823,10825,10827],{"class":564,"line":975},[70,10793,10794],{"class":1004},"                user",[70,10796,1536],{"class":844},[70,10798,1591],{"class":844},[70,10800,8270],{"class":956},[70,10802,8273],{"class":956},[70,10804,8273],{"class":956},[70,10806,1841],{"class":844},[70,10808,3735],{"class":1004},[70,10810,2617],{"class":848},[70,10812,3627],{"class":1004},[70,10814,1557],{"class":844},[70,10816,1591],{"class":844},[70,10818,8270],{"class":956},[70,10820,8273],{"class":956},[70,10822,8273],{"class":956},[70,10824,1008],{"class":844},[70,10826,3652],{"class":1004},[70,10828,2945],{"class":844},[70,10830,10831,10834,10836,10838,10840,10842,10844],{"class":564,"line":980},[70,10832,10833],{"class":848},"                static_cast\u003C",[70,10835,8270],{"class":956},[70,10837,8273],{"class":956},[70,10839,7711],{"class":848},[70,10841,957],{"class":844},[70,10843,3735],{"class":1004},[70,10845,2285],{"class":844},[70,10847,10848],{"class":564,"line":991},[70,10849,8857],{"class":1587},[70,10851,10852,10854,10856,10858,10860,10862,10865,10867,10869,10871,10873,10875,10877],{"class":564,"line":1023},[70,10853,8704],{"class":852},[70,10855,957],{"class":844},[70,10857,4271],{"class":844},[70,10859,1617],{"class":867},[70,10861,8713],{"class":1014},[70,10863,10864],{"class":867},"': gate closed (expired; last unlock ",[70,10866,10770],{"class":1014},[70,10868,10778],{"class":867},[70,10870,8261],{"class":1014},[70,10872,10783],{"class":867},[70,10874,4307],{"class":1014},[70,10876,934],{"class":844},[70,10878,2945],{"class":844},[70,10880,10881,10883,10885,10887,10889,10891,10893,10895,10897,10899,10901,10903,10905,10907,10909,10911],{"class":564,"line":1031},[70,10882,10794],{"class":1004},[70,10884,1536],{"class":844},[70,10886,1591],{"class":844},[70,10888,8270],{"class":956},[70,10890,8273],{"class":956},[70,10892,8273],{"class":956},[70,10894,1008],{"class":844},[70,10896,3652],{"class":1004},[70,10898,1536],{"class":844},[70,10900,7705],{"class":848},[70,10902,8270],{"class":956},[70,10904,8273],{"class":956},[70,10906,7711],{"class":848},[70,10908,957],{"class":844},[70,10910,3735],{"class":1004},[70,10912,2285],{"class":844},[70,10914,10915,10917,10919],{"class":564,"line":1037},[70,10916,6234],{"class":1587},[70,10918,1811],{"class":1014},[70,10920,1335],{"class":844},[70,10922,10923],{"class":564,"line":1043},[70,10924,5666],{"class":844},[70,10926,10927],{"class":564,"line":1049},[70,10928,838],{"emptyLinePlaceholder":837},[70,10930,10931,10933,10935,10938,10940,10942,10944,10946],{"class":564,"line":1055},[70,10932,7056],{"class":956},[70,10934,8622],{"class":956},[70,10936,10937],{"class":852}," Close",[70,10939,957],{"class":844},[70,10941,1641],{"class":956},[70,10943,7628],{"class":956},[70,10945,8423],{"class":1004},[70,10947,972],{"class":844},[70,10949,10950],{"class":564,"line":1065},[70,10951,1292],{"class":844},[70,10953,10954,10956,10958,10960,10962,10964,10966,10968,10970,10972],{"class":564,"line":1091},[70,10955,8653],{"class":852},[70,10957,5239],{"class":844},[70,10959,6772],{"class":1004},[70,10961,6777],{"class":1004},[70,10963,1402],{"class":844},[70,10965,1391],{"class":1004},[70,10967,2177],{"class":1004},[70,10969,1349],{"class":848},[70,10971,1811],{"class":1014},[70,10973,1335],{"class":844},[70,10975,10976,10978,10980,10982,10984,10986,10988,10990,10992,10994,10996,10998,11000,11003,11005,11007,11010,11012,11014,11016,11018,11020,11022],{"class":564,"line":1098},[70,10977,5893],{"class":1587},[70,10979,1591],{"class":844},[70,10981,1664],{"class":1587},[70,10983,8582],{"class":852},[70,10985,957],{"class":844},[70,10987,8479],{"class":1004},[70,10989,1536],{"class":844},[70,10991,6777],{"class":1004},[70,10993,1536],{"class":844},[70,10995,2177],{"class":1004},[70,10997,3960],{"class":844},[70,10999,1821],{"class":844},[70,11001,11002],{"class":852}," wprintf",[70,11004,957],{"class":844},[70,11006,4271],{"class":844},[70,11008,11009],{"class":867},"unknown account",[70,11011,4307],{"class":1014},[70,11013,934],{"class":844},[70,11015,2493],{"class":844},[70,11017,1672],{"class":1587},[70,11019,1696],{"class":1014},[70,11021,1402],{"class":844},[70,11023,2011],{"class":844},[70,11025,11026,11028,11030,11032,11034,11036,11038,11040,11042],{"class":564,"line":1103},[70,11027,3402],{"class":956},[70,11029,5441],{"class":1004},[70,11031,1349],{"class":848},[70,11033,8158],{"class":852},[70,11035,957],{"class":844},[70,11037,2278],{"class":1004},[70,11039,1536],{"class":844},[70,11041,1811],{"class":1014},[70,11043,1900],{"class":844},[70,11045,11046,11048,11050,11052],{"class":564,"line":1113},[70,11047,5893],{"class":1587},[70,11049,1591],{"class":844},[70,11051,5460],{"class":1004},[70,11053,972],{"class":844},[70,11055,11056,11059,11061,11063,11065,11068],{"class":564,"line":1129},[70,11057,11058],{"class":852},"        GateLog",[70,11060,957],{"class":844},[70,11062,9666],{"class":1004},[70,11064,1536],{"class":844},[70,11066,11067],{"class":1014}," 302",[70,11069,2945],{"class":844},[70,11071,11072,11075,11077,11079,11081,11083,11086,11088,11090,11092,11094,11096,11098,11101,11103],{"class":564,"line":1136},[70,11073,11074],{"class":852},"                std",[70,11076,5239],{"class":844},[70,11078,6772],{"class":852},[70,11080,957],{"class":844},[70,11082,4271],{"class":844},[70,11084,11085],{"class":867},"Gate closed for '",[70,11087,934],{"class":844},[70,11089,1008],{"class":844},[70,11091,1693],{"class":848},[70,11093,8423],{"class":1004},[70,11095,1693],{"class":848},[70,11097,1312],{"class":844},[70,11099,11100],{"class":867},"'.",[70,11102,934],{"class":844},[70,11104,1900],{"class":844},[70,11106,11107,11109,11111,11113,11115,11117,11120,11122,11124,11126,11128,11131,11133,11135],{"class":564,"line":1141},[70,11108,9772],{"class":852},[70,11110,957],{"class":844},[70,11112,5460],{"class":1004},[70,11114,3147],{"class":848},[70,11116,1312],{"class":844},[70,11118,11119],{"class":867},"closed. Sessions that are already up stay up.",[70,11121,4307],{"class":1014},[70,11123,934],{"class":844},[70,11125,1547],{"class":848},[70,11127,1312],{"class":844},[70,11129,11130],{"class":867},"close failed",[70,11132,4307],{"class":1014},[70,11134,934],{"class":844},[70,11136,1900],{"class":844},[70,11138,11139,11141,11143,11145,11147,11149,11151],{"class":564,"line":1147},[70,11140,6234],{"class":1587},[70,11142,5441],{"class":1004},[70,11144,3147],{"class":848},[70,11146,1811],{"class":1014},[70,11148,1547],{"class":848},[70,11150,1696],{"class":1014},[70,11152,1335],{"class":844},[70,11154,11155],{"class":564,"line":1153},[70,11156,5666],{"class":844},[70,11158,11159],{"class":564,"line":1159},[70,11160,838],{"emptyLinePlaceholder":837},[70,11162,11163],{"class":564,"line":1172},[70,11164,11165],{"class":568},"\u002F\u002F Panic button: drop every open gate at once without having to remember who\n",[70,11167,11168],{"class":564,"line":1177},[70,11169,11170],{"class":568},"\u002F\u002F is enrolled. It always reads index 0 and deletes that value until the key\n",[70,11172,11173],{"class":564,"line":1183},[70,11174,11175],{"class":568},"\u002F\u002F is empty. Deleting while enumerating upwards shifts the indices, and the old\n",[70,11177,11178],{"class":564,"line":1189},[70,11179,11180],{"class":568},"\u002F\u002F fixed list of 256 names of up to 31 characters stopped early on a long name\n",[70,11182,11183],{"class":564,"line":9806},[70,11184,11185],{"class":568},"\u002F\u002F or a full list, then logged \"All gates closed\" anyway. The buffer takes the\n",[70,11187,11188],{"class":564,"line":9818},[70,11189,11190],{"class":568},"\u002F\u002F longest value name the registry allows, and any error is reported.\n",[70,11192,11193,11195,11197,11200],{"class":564,"line":9832},[70,11194,7056],{"class":956},[70,11196,8622],{"class":956},[70,11198,11199],{"class":852}," CloseAll",[70,11201,2464],{"class":844},[70,11203,11204],{"class":564,"line":9854},[70,11205,1292],{"class":844},[70,11207,11208,11210,11212,11214,11216],{"class":564,"line":9863},[70,11209,8184],{"class":852},[70,11211,8187],{"class":1004},[70,11213,1349],{"class":848},[70,11215,2955],{"class":1014},[70,11217,1335],{"class":844},[70,11219,11221,11223,11225,11227,11229,11231,11233,11235,11237,11239,11241,11243],{"class":564,"line":11220},86,[70,11222,5893],{"class":1587},[70,11224,1591],{"class":844},[70,11226,8202],{"class":852},[70,11228,957],{"class":844},[70,11230,8207],{"class":1004},[70,11232,1536],{"class":844},[70,11234,2960],{"class":848},[70,11236,8214],{"class":1004},[70,11238,1008],{"class":844},[70,11240,2310],{"class":848},[70,11242,2977],{"class":1004},[70,11244,972],{"class":844},[70,11246,11248],{"class":564,"line":11247},87,[70,11249,1509],{"class":844},[70,11251,11253,11255,11257,11259,11262,11264,11266],{"class":564,"line":11252},88,[70,11254,8704],{"class":852},[70,11256,957],{"class":844},[70,11258,4271],{"class":844},[70,11260,11261],{"class":867},"Could not open the gate key (elevated?).",[70,11263,4307],{"class":1014},[70,11265,934],{"class":844},[70,11267,1900],{"class":844},[70,11269,11271,11273,11275],{"class":564,"line":11270},89,[70,11272,2606],{"class":1587},[70,11274,1696],{"class":1014},[70,11276,1335],{"class":844},[70,11278,11280],{"class":564,"line":11279},90,[70,11281,2405],{"class":844},[70,11283,11285],{"class":564,"line":11284},91,[70,11286,838],{"emptyLinePlaceholder":837},[70,11288,11290,11292,11294,11296,11299,11301,11303,11305,11307,11310,11312],{"class":564,"line":11289},92,[70,11291,8653],{"class":852},[70,11293,5239],{"class":844},[70,11295,6705],{"class":852},[70,11297,11298],{"class":844},"\u003C",[70,11300,1527],{"class":852},[70,11302,7711],{"class":844},[70,11304,4583],{"class":852},[70,11306,957],{"class":844},[70,11308,11309],{"class":1014},"16384",[70,11311,2493],{"class":844},[70,11313,11314],{"class":568},"        \u002F\u002F 16,383 characters + terminator\n",[70,11316,11318,11320,11323,11325,11327],{"class":564,"line":11317},93,[70,11319,7754],{"class":852},[70,11321,11322],{"class":1004}," closed",[70,11324,1349],{"class":848},[70,11326,1811],{"class":1014},[70,11328,1335],{"class":844},[70,11330,11332,11334,11336,11338,11340],{"class":564,"line":11331},94,[70,11333,7768],{"class":852},[70,11335,7771],{"class":1004},[70,11337,1349],{"class":848},[70,11339,2977],{"class":1004},[70,11341,1335],{"class":844},[70,11343,11345,11348],{"class":564,"line":11344},95,[70,11346,11347],{"class":1587},"    for",[70,11349,11350],{"class":844}," (;;)\n",[70,11352,11354],{"class":564,"line":11353},96,[70,11355,1509],{"class":844},[70,11357,11359,11361,11363,11365,11367,11369,11371,11373,11375,11377,11379],{"class":564,"line":11358},97,[70,11360,2894],{"class":852},[70,11362,3983],{"class":1004},[70,11364,1349],{"class":848},[70,11366,7705],{"class":848},[70,11368,4947],{"class":1004},[70,11370,7711],{"class":848},[70,11372,957],{"class":844},[70,11374,4671],{"class":1004},[70,11376,25],{"class":844},[70,11378,9141],{"class":852},[70,11380,7719],{"class":844},[70,11382,11384,11386,11388,11391,11393,11395,11397,11399,11401,11403,11405,11408,11410,11412,11414,11416,11418,11420,11422,11424,11426,11428,11430],{"class":564,"line":11383},98,[70,11385,7869],{"class":1004},[70,11387,1349],{"class":848},[70,11389,11390],{"class":852}," RegEnumValueW",[70,11392,957],{"class":844},[70,11394,8214],{"class":1004},[70,11396,1536],{"class":844},[70,11398,1811],{"class":1014},[70,11400,1536],{"class":844},[70,11402,4583],{"class":1004},[70,11404,25],{"class":844},[70,11406,11407],{"class":852},"data",[70,11409,7256],{"class":844},[70,11411,2960],{"class":848},[70,11413,4010],{"class":1004},[70,11415,1536],{"class":844},[70,11417,2955],{"class":1014},[70,11419,1536],{"class":844},[70,11421,2955],{"class":1014},[70,11423,1536],{"class":844},[70,11425,2955],{"class":1014},[70,11427,1536],{"class":844},[70,11429,2955],{"class":1014},[70,11431,1900],{"class":844},[70,11433,11435,11437,11439,11441,11443,11445],{"class":564,"line":11434},99,[70,11436,2579],{"class":1587},[70,11438,1591],{"class":844},[70,11440,7845],{"class":1004},[70,11442,2310],{"class":848},[70,11444,2977],{"class":1004},[70,11446,972],{"class":844},[70,11448,11450,11452],{"class":564,"line":11449},100,[70,11451,5379],{"class":1587},[70,11453,1335],{"class":844},[70,11455,11457,11459,11461,11463,11465,11467,11469,11471,11473,11475],{"class":564,"line":11456},101,[70,11458,7869],{"class":1004},[70,11460,1349],{"class":848},[70,11462,8355],{"class":852},[70,11464,957],{"class":844},[70,11466,8214],{"class":1004},[70,11468,1536],{"class":844},[70,11470,4583],{"class":1004},[70,11472,25],{"class":844},[70,11474,11407],{"class":852},[70,11476,7719],{"class":844},[70,11478,11480,11482,11484,11486,11488,11490],{"class":564,"line":11479},102,[70,11481,2579],{"class":1587},[70,11483,1591],{"class":844},[70,11485,7845],{"class":1004},[70,11487,2310],{"class":848},[70,11489,2977],{"class":1004},[70,11491,972],{"class":844},[70,11493,11495,11497],{"class":564,"line":11494},103,[70,11496,5379],{"class":1587},[70,11498,1335],{"class":844},[70,11500,11502,11505,11508],{"class":564,"line":11501},104,[70,11503,11504],{"class":848},"        ++",[70,11506,11507],{"class":1004},"closed",[70,11509,1335],{"class":844},[70,11511,11513],{"class":564,"line":11512},105,[70,11514,2405],{"class":844},[70,11516,11518,11520,11522,11524],{"class":564,"line":11517},106,[70,11519,8370],{"class":852},[70,11521,957],{"class":844},[70,11523,8214],{"class":1004},[70,11525,1900],{"class":844},[70,11527,11529],{"class":564,"line":11528},107,[70,11530,838],{"emptyLinePlaceholder":837},[70,11532,11534,11536,11538,11540,11542,11545],{"class":564,"line":11533},108,[70,11535,5893],{"class":1587},[70,11537,1591],{"class":844},[70,11539,7845],{"class":1004},[70,11541,2310],{"class":848},[70,11543,11544],{"class":1004}," ERROR_NO_MORE_ITEMS",[70,11546,972],{"class":844},[70,11548,11550],{"class":564,"line":11549},109,[70,11551,1509],{"class":844},[70,11553,11555,11557,11559,11561,11563,11565],{"class":564,"line":11554},110,[70,11556,11058],{"class":852},[70,11558,957],{"class":844},[70,11560,4802],{"class":1004},[70,11562,1536],{"class":844},[70,11564,11067],{"class":1014},[70,11566,2945],{"class":844},[70,11568,11570,11572,11575,11577,11579,11581,11583,11585,11587,11589,11591],{"class":564,"line":11569},111,[70,11571,8818],{"class":844},[70,11573,11574],{"class":867},"Closing all gates stopped after ",[70,11576,934],{"class":844},[70,11578,1693],{"class":848},[70,11580,6767],{"class":852},[70,11582,5239],{"class":844},[70,11584,9713],{"class":852},[70,11586,957],{"class":844},[70,11588,11507],{"class":1004},[70,11590,1008],{"class":844},[70,11592,9722],{"class":848},[70,11594,11596,11598,11601,11603,11605,11607,11609,11611,11613,11615,11617,11619,11621,11624,11626],{"class":564,"line":11595},112,[70,11597,8818],{"class":844},[70,11599,11600],{"class":867}," with error ",[70,11602,934],{"class":844},[70,11604,1693],{"class":848},[70,11606,6767],{"class":852},[70,11608,5239],{"class":844},[70,11610,9713],{"class":852},[70,11612,957],{"class":844},[70,11614,7845],{"class":1004},[70,11616,1008],{"class":844},[70,11618,1693],{"class":848},[70,11620,1312],{"class":844},[70,11622,11623],{"class":867},". Gates may still be open.",[70,11625,934],{"class":844},[70,11627,1900],{"class":844},[70,11629,11631,11633,11635,11637,11640,11642,11645,11648,11651,11653],{"class":564,"line":11630},113,[70,11632,8704],{"class":852},[70,11634,957],{"class":844},[70,11636,4271],{"class":844},[70,11638,11639],{"class":867},"closed ",[70,11641,8261],{"class":1014},[70,11643,11644],{"class":867}," gate(s), then stopped with error ",[70,11646,11647],{"class":1014},"%ld",[70,11649,11650],{"class":867},". Gates may still be open,",[70,11652,4307],{"class":1014},[70,11654,6591],{"class":844},[70,11656,11658,11660,11663,11666,11669,11671,11673],{"class":564,"line":11657},114,[70,11659,8818],{"class":844},[70,11661,11662],{"class":867},"check HKLM",[70,11664,11665],{"class":1014},"\\\\%s",[70,11667,11668],{"class":867}," by hand.",[70,11670,4307],{"class":1014},[70,11672,934],{"class":844},[70,11674,2945],{"class":844},[70,11676,11678,11680,11682,11684,11686,11688,11690,11692,11694,11697,11699,11701,11703,11705,11707],{"class":564,"line":11677},115,[70,11679,10833],{"class":848},[70,11681,8270],{"class":956},[70,11683,8273],{"class":956},[70,11685,7711],{"class":848},[70,11687,957],{"class":844},[70,11689,11507],{"class":1004},[70,11691,1557],{"class":844},[70,11693,7705],{"class":848},[70,11695,11696],{"class":956},"long",[70,11698,7711],{"class":848},[70,11700,957],{"class":844},[70,11702,7845],{"class":1004},[70,11704,1557],{"class":844},[70,11706,1303],{"class":1004},[70,11708,1900],{"class":844},[70,11710,11712,11714,11716],{"class":564,"line":11711},116,[70,11713,2606],{"class":1587},[70,11715,1696],{"class":1014},[70,11717,1335],{"class":844},[70,11719,11721],{"class":564,"line":11720},117,[70,11722,2405],{"class":844},[70,11724,11726],{"class":564,"line":11725},118,[70,11727,838],{"emptyLinePlaceholder":837},[70,11729,11731,11733,11735,11737,11739,11741],{"class":564,"line":11730},119,[70,11732,9661],{"class":852},[70,11734,957],{"class":844},[70,11736,9666],{"class":1004},[70,11738,1536],{"class":844},[70,11740,11067],{"class":1014},[70,11742,2945],{"class":844},[70,11744,11746,11748,11751,11753,11755,11757,11759,11761,11763,11765,11767,11769,11771,11773,11775],{"class":564,"line":11745},120,[70,11747,9727],{"class":844},[70,11749,11750],{"class":867},"All gates closed (",[70,11752,934],{"class":844},[70,11754,1693],{"class":848},[70,11756,6767],{"class":852},[70,11758,5239],{"class":844},[70,11760,9713],{"class":852},[70,11762,957],{"class":844},[70,11764,11507],{"class":1004},[70,11766,1008],{"class":844},[70,11768,1693],{"class":848},[70,11770,1312],{"class":844},[70,11772,9403],{"class":867},[70,11774,934],{"class":844},[70,11776,1900],{"class":844},[70,11778,11780,11782,11784,11786,11788,11790,11793,11795,11797],{"class":564,"line":11779},121,[70,11781,9772],{"class":852},[70,11783,957],{"class":844},[70,11785,4271],{"class":844},[70,11787,11639],{"class":867},[70,11789,8261],{"class":1014},[70,11791,11792],{"class":867}," gate(s). Sessions that are already up stay up.",[70,11794,4307],{"class":1014},[70,11796,934],{"class":844},[70,11798,2945],{"class":844},[70,11800,11802,11805,11807,11809,11811,11813,11815],{"class":564,"line":11801},122,[70,11803,11804],{"class":848},"            static_cast\u003C",[70,11806,8270],{"class":956},[70,11808,8273],{"class":956},[70,11810,7711],{"class":848},[70,11812,957],{"class":844},[70,11814,11507],{"class":1004},[70,11816,2285],{"class":844},[70,11818,11820,11822,11824],{"class":564,"line":11819},123,[70,11821,6234],{"class":1587},[70,11823,1811],{"class":1014},[70,11825,1335],{"class":844},[70,11827,11829],{"class":564,"line":11828},124,[70,11830,5666],{"class":844},[17,11832,11833,11836,11837,11839,11840,11842,11843,25],{},[41,11834,11835],{},"Status"," does the same math as the filter and prints what the filter would decide right now, including the clock skew case. If the kill switch is off, it prints a warning first. An admin who forgot ",[41,11838,223],{}," from yesterday's test should notice before trusting the gate. Then it asks ",[41,11841,5242],{},", because the filter only gates accounts in Doppio's RID index. My first version said \"gate closed\" for an account the filter lets through anyway, for example after enrolling without ",[41,11844,140],{},[17,11846,11847,11850,11851,11853,11854,11857],{},[41,11848,11849],{},"CloseAll"," is the panic button. It doesn't need to know who is enrolled, it deletes every value under ",[41,11852,1330],{},". It always reads index 0 and deletes that value, until the key is empty. Deleting while you enumerate upwards with ",[41,11855,11856],{},"RegEnumValueW"," shifts the indices, and you'd skip every second value. My first version collected the names first, in a fixed list of 256 names with up to 31 characters each. A longer name or a full list stopped it early, and the event still said \"All gates closed\". Now the buffer takes the longest value name the registry allows, and an error shows up in the output and in the event log.",[250,11859,11861],{"id":11860},"window-and-mode","Window and mode",[258,11863,11865],{"className":557,"code":11864,"filename":6429,"language":559,"meta":264,"style":264},"static int SetWindow(const wchar_t* arg)\n{\n    long raw = _wtol(arg);\n    if (raw \u003C= 0)\n    {\n        wprintf(L\"Give the window in seconds, %lu-%lu.\\n\",\n                static_cast\u003Cunsigned long>(kWindowMin), static_cast\u003Cunsigned long>(kWindowMax));\n        return 1;\n    }\n    DWORD v = static_cast\u003CDWORD>(raw);\n    if (v \u003C kWindowMin || v > kWindowMax)\n    {\n        DWORD clamped = v \u003C kWindowMin ? kWindowMin : kWindowMax;\n        wprintf(L\"%lu s is outside the allowed range %lu-%lu; using %lu s.\\n\",\n                static_cast\u003Cunsigned long>(v),\n                static_cast\u003Cunsigned long>(kWindowMin),\n                static_cast\u003Cunsigned long>(kWindowMax),\n                static_cast\u003Cunsigned long>(clamped));\n        v = clamped;\n    }\n\n    HKEY k = nullptr;\n    if (OpenProtected(kConfigPath, &k) != ERROR_SUCCESS)\n    {\n        wprintf(L\"Could not open the config key (elevated?).\\n\");\n        return 1;\n    }\n    LSTATUS st = RegSetValueExW(k, L\"WindowSec\", 0, REG_DWORD,\n                                reinterpret_cast\u003Cconst BYTE*>(&v), sizeof(v));\n    RegCloseKey(k);\n    if (st != ERROR_SUCCESS) { wprintf(L\"write failed\\n\"); return 1; }\n    wprintf(L\"WindowSec = %lu (active within ~5 s, no reboot)\\n\",\n            static_cast\u003Cunsigned long>(v));\n    return 0;\n}\n\nstatic int SetMode(const wchar_t* arg)\n{\n    const bool gate = !_wcsicmp(arg, L\"gate\");\n    if (!gate && _wcsicmp(arg, L\"off\"))\n    {\n        wprintf(L\"Use 'gate' or 'off'.\\n\");\n        return 1;\n    }\n    DWORD v = gate ? 1u : 0u;\n\n    HKEY k = nullptr;\n    if (OpenProtected(kConfigPath, &k) != ERROR_SUCCESS)\n    {\n        wprintf(L\"Could not open the config key (elevated?).\\n\");\n        return 1;\n    }\n    LSTATUS st = RegSetValueExW(k, L\"Mode\", 0, REG_DWORD,\n                                reinterpret_cast\u003Cconst BYTE*>(&v), sizeof(v));\n    RegCloseKey(k);\n    if (st != ERROR_SUCCESS) { wprintf(L\"write failed\\n\"); return 1; }\n\n    GateLog(gate ? EVENTLOG_INFORMATION_TYPE : EVENTLOG_WARNING_TYPE, 304,\n            gate ? L\"Enforcement enabled (Mode = 1).\"\n                 : L\"Enforcement DISABLED (Mode = 0) - network logons are no longer gated.\");\n    if (gate)\n        wprintf(L\"Mode = gate (active within ~5 s, no reboot)\\n\");\n    else\n        wprintf(L\"Mode = off. Enforcement is DISABLED: every network logon passes, \"\n                L\"gate or not. Re-enable with \u002Fmode gate.\\n\");\n    return 0;\n}\n",[41,11866,11867,11887,11891,11911,11926,11930,11956,11988,11996,12000,12020,12042,12046,12071,12103,12120,12136,12152,12169,12180,12184,12188,12200,12227,12231,12248,12256,12260,12293,12320,12330,12369,12391,12407,12415,12419,12423,12442,12446,12478,12508,12512,12529,12537,12541,12565,12569,12581,12607,12611,12627,12635,12639,12671,12697,12707,12745,12749,12774,12788,12802,12812,12829,12833,12846,12859,12867],{"__ignoreMap":264},[70,11868,11869,11871,11873,11876,11878,11880,11882,11885],{"class":564,"line":565},[70,11870,7056],{"class":956},[70,11872,8622],{"class":956},[70,11874,11875],{"class":852}," SetWindow",[70,11877,957],{"class":844},[70,11879,1641],{"class":956},[70,11881,7628],{"class":956},[70,11883,11884],{"class":1004}," arg",[70,11886,972],{"class":844},[70,11888,11889],{"class":564,"line":572},[70,11890,1292],{"class":844},[70,11892,11893,11896,11899,11901,11904,11906,11909],{"class":564,"line":578},[70,11894,11895],{"class":956},"    long",[70,11897,11898],{"class":1004}," raw",[70,11900,1349],{"class":848},[70,11902,11903],{"class":852}," _wtol",[70,11905,957],{"class":844},[70,11907,11908],{"class":1004},"arg",[70,11910,1900],{"class":844},[70,11912,11913,11915,11917,11920,11922,11924],{"class":564,"line":584},[70,11914,5893],{"class":1587},[70,11916,1591],{"class":844},[70,11918,11919],{"class":1004},"raw",[70,11921,3726],{"class":848},[70,11923,1811],{"class":1014},[70,11925,972],{"class":844},[70,11927,11928],{"class":564,"line":590},[70,11929,1509],{"class":844},[70,11931,11932,11934,11936,11938,11941,11943,11946,11948,11950,11952,11954],{"class":564,"line":596},[70,11933,8704],{"class":852},[70,11935,957],{"class":844},[70,11937,4271],{"class":844},[70,11939,11940],{"class":867},"Give the window in seconds, ",[70,11942,8261],{"class":1014},[70,11944,11945],{"class":867},"-",[70,11947,8261],{"class":1014},[70,11949,25],{"class":867},[70,11951,4307],{"class":1014},[70,11953,934],{"class":844},[70,11955,2945],{"class":844},[70,11957,11958,11960,11962,11964,11966,11968,11971,11973,11975,11977,11979,11981,11983,11986],{"class":564,"line":602},[70,11959,10833],{"class":848},[70,11961,8270],{"class":956},[70,11963,8273],{"class":956},[70,11965,7711],{"class":848},[70,11967,957],{"class":844},[70,11969,11970],{"class":1004},"kWindowMin",[70,11972,1557],{"class":844},[70,11974,7705],{"class":848},[70,11976,8270],{"class":956},[70,11978,8273],{"class":956},[70,11980,7711],{"class":848},[70,11982,957],{"class":844},[70,11984,11985],{"class":1004},"kWindowMax",[70,11987,2285],{"class":844},[70,11989,11990,11992,11994],{"class":564,"line":607},[70,11991,2606],{"class":1587},[70,11993,1696],{"class":1014},[70,11995,1335],{"class":844},[70,11997,11998],{"class":564,"line":613},[70,11999,2405],{"class":844},[70,12001,12002,12004,12006,12008,12010,12012,12014,12016,12018],{"class":564,"line":619},[70,12003,7754],{"class":852},[70,12005,1776],{"class":1004},[70,12007,1349],{"class":848},[70,12009,7705],{"class":848},[70,12011,4947],{"class":1004},[70,12013,7711],{"class":848},[70,12015,957],{"class":844},[70,12017,11919],{"class":1004},[70,12019,1900],{"class":844},[70,12021,12022,12024,12026,12028,12030,12032,12034,12036,12038,12040],{"class":564,"line":625},[70,12023,5893],{"class":1587},[70,12025,1591],{"class":844},[70,12027,1860],{"class":1004},[70,12029,864],{"class":848},[70,12031,1414],{"class":1004},[70,12033,3928],{"class":1587},[70,12035,1776],{"class":1004},[70,12037,1911],{"class":848},[70,12039,1431],{"class":1004},[70,12041,972],{"class":844},[70,12043,12044],{"class":564,"line":631},[70,12045,1509],{"class":844},[70,12047,12048,12050,12053,12055,12057,12059,12061,12063,12065,12067,12069],{"class":564,"line":636},[70,12049,2894],{"class":852},[70,12051,12052],{"class":1004}," clamped",[70,12054,1349],{"class":848},[70,12056,1776],{"class":1004},[70,12058,864],{"class":848},[70,12060,1414],{"class":1004},[70,12062,3147],{"class":848},[70,12064,1414],{"class":1004},[70,12066,1547],{"class":848},[70,12068,1431],{"class":1004},[70,12070,1335],{"class":844},[70,12072,12073,12075,12077,12079,12081,12084,12086,12088,12090,12093,12095,12097,12099,12101],{"class":564,"line":642},[70,12074,8704],{"class":852},[70,12076,957],{"class":844},[70,12078,4271],{"class":844},[70,12080,8261],{"class":1014},[70,12082,12083],{"class":867}," s is outside the allowed range ",[70,12085,8261],{"class":1014},[70,12087,11945],{"class":867},[70,12089,8261],{"class":1014},[70,12091,12092],{"class":867},"; using ",[70,12094,8261],{"class":1014},[70,12096,9753],{"class":867},[70,12098,4307],{"class":1014},[70,12100,934],{"class":844},[70,12102,2945],{"class":844},[70,12104,12105,12107,12109,12111,12113,12115,12117],{"class":564,"line":648},[70,12106,10833],{"class":848},[70,12108,8270],{"class":956},[70,12110,8273],{"class":956},[70,12112,7711],{"class":848},[70,12114,957],{"class":844},[70,12116,1860],{"class":1004},[70,12118,12119],{"class":844},"),\n",[70,12121,12122,12124,12126,12128,12130,12132,12134],{"class":564,"line":654},[70,12123,10833],{"class":848},[70,12125,8270],{"class":956},[70,12127,8273],{"class":956},[70,12129,7711],{"class":848},[70,12131,957],{"class":844},[70,12133,11970],{"class":1004},[70,12135,12119],{"class":844},[70,12137,12138,12140,12142,12144,12146,12148,12150],{"class":564,"line":659},[70,12139,10833],{"class":848},[70,12141,8270],{"class":956},[70,12143,8273],{"class":956},[70,12145,7711],{"class":848},[70,12147,957],{"class":844},[70,12149,11985],{"class":1004},[70,12151,12119],{"class":844},[70,12153,12154,12156,12158,12160,12162,12164,12167],{"class":564,"line":665},[70,12155,10833],{"class":848},[70,12157,8270],{"class":956},[70,12159,8273],{"class":956},[70,12161,7711],{"class":848},[70,12163,957],{"class":844},[70,12165,12166],{"class":1004},"clamped",[70,12168,2285],{"class":844},[70,12170,12171,12174,12176,12178],{"class":564,"line":671},[70,12172,12173],{"class":1004},"        v",[70,12175,1349],{"class":848},[70,12177,12052],{"class":1004},[70,12179,1335],{"class":844},[70,12181,12182],{"class":564,"line":677},[70,12183,2405],{"class":844},[70,12185,12186],{"class":564,"line":683},[70,12187,838],{"emptyLinePlaceholder":837},[70,12189,12190,12192,12194,12196,12198],{"class":564,"line":689},[70,12191,8184],{"class":852},[70,12193,8187],{"class":1004},[70,12195,1349],{"class":848},[70,12197,2955],{"class":1014},[70,12199,1335],{"class":844},[70,12201,12202,12204,12206,12208,12210,12213,12215,12217,12219,12221,12223,12225],{"class":564,"line":695},[70,12203,5893],{"class":1587},[70,12205,1591],{"class":844},[70,12207,8202],{"class":852},[70,12209,957],{"class":844},[70,12211,12212],{"class":1004},"kConfigPath",[70,12214,1536],{"class":844},[70,12216,2960],{"class":848},[70,12218,8214],{"class":1004},[70,12220,1008],{"class":844},[70,12222,2310],{"class":848},[70,12224,2977],{"class":1004},[70,12226,972],{"class":844},[70,12228,12229],{"class":564,"line":701},[70,12230,1509],{"class":844},[70,12232,12233,12235,12237,12239,12242,12244,12246],{"class":564,"line":706},[70,12234,8704],{"class":852},[70,12236,957],{"class":844},[70,12238,4271],{"class":844},[70,12240,12241],{"class":867},"Could not open the config key (elevated?).",[70,12243,4307],{"class":1014},[70,12245,934],{"class":844},[70,12247,1900],{"class":844},[70,12249,12250,12252,12254],{"class":564,"line":712},[70,12251,2606],{"class":1587},[70,12253,1696],{"class":1014},[70,12255,1335],{"class":844},[70,12257,12258],{"class":564,"line":718},[70,12259,2405],{"class":844},[70,12261,12262,12264,12266,12268,12270,12272,12274,12276,12278,12280,12282,12284,12286,12288,12291],{"class":564,"line":723},[70,12263,7768],{"class":852},[70,12265,7771],{"class":1004},[70,12267,1349],{"class":848},[70,12269,8299],{"class":852},[70,12271,957],{"class":844},[70,12273,8214],{"class":1004},[70,12275,1536],{"class":844},[70,12277,1312],{"class":844},[70,12279,2940],{"class":867},[70,12281,934],{"class":844},[70,12283,1536],{"class":844},[70,12285,1811],{"class":1014},[70,12287,1536],{"class":844},[70,12289,12290],{"class":1004}," REG_DWORD",[70,12292,2945],{"class":844},[70,12294,12295,12298,12300,12302,12304,12306,12308,12310,12312,12314,12316,12318],{"class":564,"line":729},[70,12296,12297],{"class":848},"                                reinterpret_cast\u003C",[70,12299,1641],{"class":956},[70,12301,8328],{"class":1004},[70,12303,8331],{"class":848},[70,12305,957],{"class":844},[70,12307,2186],{"class":848},[70,12309,1860],{"class":1004},[70,12311,1557],{"class":844},[70,12313,2910],{"class":848},[70,12315,957],{"class":844},[70,12317,1860],{"class":1004},[70,12319,2285],{"class":844},[70,12321,12322,12324,12326,12328],{"class":564,"line":735},[70,12323,8370],{"class":852},[70,12325,957],{"class":844},[70,12327,8214],{"class":1004},[70,12329,1900],{"class":844},[70,12331,12332,12334,12336,12338,12340,12342,12344,12346,12348,12350,12352,12355,12357,12359,12361,12363,12365,12367],{"class":564,"line":741},[70,12333,5893],{"class":1587},[70,12335,1591],{"class":844},[70,12337,7845],{"class":1004},[70,12339,2310],{"class":848},[70,12341,2977],{"class":1004},[70,12343,1008],{"class":844},[70,12345,1821],{"class":844},[70,12347,11002],{"class":852},[70,12349,957],{"class":844},[70,12351,4271],{"class":844},[70,12353,12354],{"class":867},"write failed",[70,12356,4307],{"class":1014},[70,12358,934],{"class":844},[70,12360,2493],{"class":844},[70,12362,1672],{"class":1587},[70,12364,1696],{"class":1014},[70,12366,1402],{"class":844},[70,12368,2011],{"class":844},[70,12370,12371,12373,12375,12377,12380,12382,12385,12387,12389],{"class":564,"line":746},[70,12372,9772],{"class":852},[70,12374,957],{"class":844},[70,12376,4271],{"class":844},[70,12378,12379],{"class":867},"WindowSec = ",[70,12381,8261],{"class":1014},[70,12383,12384],{"class":867}," (active within ~5 s, no reboot)",[70,12386,4307],{"class":1014},[70,12388,934],{"class":844},[70,12390,2945],{"class":844},[70,12392,12393,12395,12397,12399,12401,12403,12405],{"class":564,"line":752},[70,12394,11804],{"class":848},[70,12396,8270],{"class":956},[70,12398,8273],{"class":956},[70,12400,7711],{"class":848},[70,12402,957],{"class":844},[70,12404,1860],{"class":1004},[70,12406,2285],{"class":844},[70,12408,12409,12411,12413],{"class":564,"line":758},[70,12410,6234],{"class":1587},[70,12412,1811],{"class":1014},[70,12414,1335],{"class":844},[70,12416,12417],{"class":564,"line":764},[70,12418,5666],{"class":844},[70,12420,12421],{"class":564,"line":769},[70,12422,838],{"emptyLinePlaceholder":837},[70,12424,12425,12427,12429,12432,12434,12436,12438,12440],{"class":564,"line":775},[70,12426,7056],{"class":956},[70,12428,8622],{"class":956},[70,12430,12431],{"class":852}," SetMode",[70,12433,957],{"class":844},[70,12435,1641],{"class":956},[70,12437,7628],{"class":956},[70,12439,11884],{"class":1004},[70,12441,972],{"class":844},[70,12443,12444],{"class":564,"line":781},[70,12445,1292],{"class":844},[70,12447,12448,12450,12452,12455,12457,12460,12463,12465,12467,12469,12471,12474,12476],{"class":564,"line":787},[70,12449,1297],{"class":956},[70,12451,5971],{"class":956},[70,12453,12454],{"class":1004}," gate",[70,12456,1349],{"class":848},[70,12458,12459],{"class":1587}," !",[70,12461,12462],{"class":852},"_wcsicmp",[70,12464,957],{"class":844},[70,12466,11908],{"class":1004},[70,12468,1536],{"class":844},[70,12470,1312],{"class":844},[70,12472,12473],{"class":867},"gate",[70,12475,934],{"class":844},[70,12477,1900],{"class":844},[70,12479,12480,12482,12484,12486,12488,12490,12493,12495,12497,12499,12501,12504,12506],{"class":564,"line":793},[70,12481,5893],{"class":1587},[70,12483,1591],{"class":844},[70,12485,1664],{"class":1587},[70,12487,12473],{"class":1004},[70,12489,1688],{"class":1587},[70,12491,12492],{"class":852}," _wcsicmp",[70,12494,957],{"class":844},[70,12496,11908],{"class":1004},[70,12498,1536],{"class":844},[70,12500,1312],{"class":844},[70,12502,12503],{"class":867},"off",[70,12505,934],{"class":844},[70,12507,5158],{"class":844},[70,12509,12510],{"class":564,"line":799},[70,12511,1509],{"class":844},[70,12513,12514,12516,12518,12520,12523,12525,12527],{"class":564,"line":805},[70,12515,8704],{"class":852},[70,12517,957],{"class":844},[70,12519,4271],{"class":844},[70,12521,12522],{"class":867},"Use 'gate' or 'off'.",[70,12524,4307],{"class":1014},[70,12526,934],{"class":844},[70,12528,1900],{"class":844},[70,12530,12531,12533,12535],{"class":564,"line":811},[70,12532,2606],{"class":1587},[70,12534,1696],{"class":1014},[70,12536,1335],{"class":844},[70,12538,12539],{"class":564,"line":817},[70,12540,2405],{"class":844},[70,12542,12543,12545,12547,12549,12551,12553,12555,12557,12559,12561,12563],{"class":564,"line":823},[70,12544,7754],{"class":852},[70,12546,1776],{"class":1004},[70,12548,1349],{"class":848},[70,12550,12454],{"class":1004},[70,12552,3147],{"class":848},[70,12554,1696],{"class":1014},[70,12556,2584],{"class":867},[70,12558,1547],{"class":848},[70,12560,1811],{"class":1014},[70,12562,2584],{"class":867},[70,12564,1335],{"class":844},[70,12566,12567],{"class":564,"line":829},[70,12568,838],{"emptyLinePlaceholder":837},[70,12570,12571,12573,12575,12577,12579],{"class":564,"line":834},[70,12572,8184],{"class":852},[70,12574,8187],{"class":1004},[70,12576,1349],{"class":848},[70,12578,2955],{"class":1014},[70,12580,1335],{"class":844},[70,12582,12583,12585,12587,12589,12591,12593,12595,12597,12599,12601,12603,12605],{"class":564,"line":841},[70,12584,5893],{"class":1587},[70,12586,1591],{"class":844},[70,12588,8202],{"class":852},[70,12590,957],{"class":844},[70,12592,12212],{"class":1004},[70,12594,1536],{"class":844},[70,12596,2960],{"class":848},[70,12598,8214],{"class":1004},[70,12600,1008],{"class":844},[70,12602,2310],{"class":848},[70,12604,2977],{"class":1004},[70,12606,972],{"class":844},[70,12608,12609],{"class":564,"line":856},[70,12610,1509],{"class":844},[70,12612,12613,12615,12617,12619,12621,12623,12625],{"class":564,"line":874},[70,12614,8704],{"class":852},[70,12616,957],{"class":844},[70,12618,4271],{"class":844},[70,12620,12241],{"class":867},[70,12622,4307],{"class":1014},[70,12624,934],{"class":844},[70,12626,1900],{"class":844},[70,12628,12629,12631,12633],{"class":564,"line":888},[70,12630,2606],{"class":1587},[70,12632,1696],{"class":1014},[70,12634,1335],{"class":844},[70,12636,12637],{"class":564,"line":902},[70,12638,2405],{"class":844},[70,12640,12641,12643,12645,12647,12649,12651,12653,12655,12657,12659,12661,12663,12665,12667,12669],{"class":564,"line":916},[70,12642,7768],{"class":852},[70,12644,7771],{"class":1004},[70,12646,1349],{"class":848},[70,12648,8299],{"class":852},[70,12650,957],{"class":844},[70,12652,8214],{"class":1004},[70,12654,1536],{"class":844},[70,12656,1312],{"class":844},[70,12658,3097],{"class":867},[70,12660,934],{"class":844},[70,12662,1536],{"class":844},[70,12664,1811],{"class":1014},[70,12666,1536],{"class":844},[70,12668,12290],{"class":1004},[70,12670,2945],{"class":844},[70,12672,12673,12675,12677,12679,12681,12683,12685,12687,12689,12691,12693,12695],{"class":564,"line":921},[70,12674,12297],{"class":848},[70,12676,1641],{"class":956},[70,12678,8328],{"class":1004},[70,12680,8331],{"class":848},[70,12682,957],{"class":844},[70,12684,2186],{"class":848},[70,12686,1860],{"class":1004},[70,12688,1557],{"class":844},[70,12690,2910],{"class":848},[70,12692,957],{"class":844},[70,12694,1860],{"class":1004},[70,12696,2285],{"class":844},[70,12698,12699,12701,12703,12705],{"class":564,"line":940},[70,12700,8370],{"class":852},[70,12702,957],{"class":844},[70,12704,8214],{"class":1004},[70,12706,1900],{"class":844},[70,12708,12709,12711,12713,12715,12717,12719,12721,12723,12725,12727,12729,12731,12733,12735,12737,12739,12741,12743],{"class":564,"line":945},[70,12710,5893],{"class":1587},[70,12712,1591],{"class":844},[70,12714,7845],{"class":1004},[70,12716,2310],{"class":848},[70,12718,2977],{"class":1004},[70,12720,1008],{"class":844},[70,12722,1821],{"class":844},[70,12724,11002],{"class":852},[70,12726,957],{"class":844},[70,12728,4271],{"class":844},[70,12730,12354],{"class":867},[70,12732,4307],{"class":1014},[70,12734,934],{"class":844},[70,12736,2493],{"class":844},[70,12738,1672],{"class":1587},[70,12740,1696],{"class":1014},[70,12742,1402],{"class":844},[70,12744,2011],{"class":844},[70,12746,12747],{"class":564,"line":975},[70,12748,838],{"emptyLinePlaceholder":837},[70,12750,12751,12753,12755,12757,12759,12762,12764,12767,12769,12772],{"class":564,"line":980},[70,12752,9661],{"class":852},[70,12754,957],{"class":844},[70,12756,12473],{"class":1004},[70,12758,3147],{"class":848},[70,12760,12761],{"class":1004}," EVENTLOG_INFORMATION_TYPE",[70,12763,1547],{"class":848},[70,12765,12766],{"class":1004}," EVENTLOG_WARNING_TYPE",[70,12768,1536],{"class":844},[70,12770,12771],{"class":1014}," 304",[70,12773,2945],{"class":844},[70,12775,12776,12779,12781,12783,12786],{"class":564,"line":991},[70,12777,12778],{"class":1004},"            gate",[70,12780,3147],{"class":848},[70,12782,1312],{"class":844},[70,12784,12785],{"class":867},"Enforcement enabled (Mode = 1).",[70,12787,6591],{"class":844},[70,12789,12790,12793,12795,12798,12800],{"class":564,"line":1023},[70,12791,12792],{"class":848},"                 :",[70,12794,1312],{"class":844},[70,12796,12797],{"class":867},"Enforcement DISABLED (Mode = 0) - network logons are no longer gated.",[70,12799,934],{"class":844},[70,12801,1900],{"class":844},[70,12803,12804,12806,12808,12810],{"class":564,"line":1031},[70,12805,5893],{"class":1587},[70,12807,1591],{"class":844},[70,12809,12473],{"class":1004},[70,12811,972],{"class":844},[70,12813,12814,12816,12818,12820,12823,12825,12827],{"class":564,"line":1037},[70,12815,8704],{"class":852},[70,12817,957],{"class":844},[70,12819,4271],{"class":844},[70,12821,12822],{"class":867},"Mode = gate (active within ~5 s, no reboot)",[70,12824,4307],{"class":1014},[70,12826,934],{"class":844},[70,12828,1900],{"class":844},[70,12830,12831],{"class":564,"line":1043},[70,12832,8857],{"class":1587},[70,12834,12835,12837,12839,12841,12844],{"class":564,"line":1049},[70,12836,8704],{"class":852},[70,12838,957],{"class":844},[70,12840,4271],{"class":844},[70,12842,12843],{"class":867},"Mode = off. Enforcement is DISABLED: every network logon passes, ",[70,12845,6591],{"class":844},[70,12847,12848,12850,12853,12855,12857],{"class":564,"line":1055},[70,12849,8818],{"class":844},[70,12851,12852],{"class":867},"gate or not. Re-enable with \u002Fmode gate.",[70,12854,4307],{"class":1014},[70,12856,934],{"class":844},[70,12858,1900],{"class":844},[70,12860,12861,12863,12865],{"class":564,"line":1065},[70,12862,6234],{"class":1587},[70,12864,1811],{"class":1014},[70,12866,1335],{"class":844},[70,12868,12869],{"class":564,"line":1091},[70,12870,5666],{"class":844},[17,12872,12873,12874,12876],{},"Both write into the config key through ",[41,12875,8202],{},", so the tool enforces the ACL again on every change. Both take effect within the filter's five-second refresh, no reboot.",[17,12878,12879,12882,12883,1221,12885,12887],{},[41,12880,12881],{},"SetMode"," accepts only ",[41,12884,12473],{},[41,12886,12503],{}," and rejects everything else. Parsing a number here would turn most typos into 0, and 0 is off. Switching off writes a warning event, switching on an information event, so the kill switch leaves a trace that's easy to find.",[250,12889,12890],{"id":12890},"wmain",[258,12892,12894],{"className":557,"code":12893,"filename":6429,"language":559,"meta":264,"style":264},"int wmain(int argc, wchar_t** argv)\n{\n    if (!Elevated())\n    {\n        wprintf(L\"Run elevated. The 2FA store, state and lock are SYSTEM\u002FAdministrators only.\\n\");\n        return 1;\n    }\n\n    if (argc >= 2 && argv[1][0] != L'\u002F')\n        return Open(argv[1], argc > 2 ? argv[2] : nullptr);\n\n    if (argc == 3 && !_wcsicmp(argv[1], L\"\u002Fstatus\"))  return Status(argv[2]);\n    if (argc == 3 && !_wcsicmp(argv[1], L\"\u002Fclose\"))   return Close(argv[2]);\n    if (argc == 2 && !_wcsicmp(argv[1], L\"\u002Fcloseall\"))return CloseAll();\n    if (argc == 3 && !_wcsicmp(argv[1], L\"\u002Fwindow\"))  return SetWindow(argv[2]);\n    if (argc == 3 && !_wcsicmp(argv[1], L\"\u002Fmode\"))    return SetMode(argv[2]);\n\n    wprintf(L\"ristretto \u003Cuser> [code]     open the network gate (TOTP check)\\n\"\n            L\"ristretto \u002Fstatus \u003Cuser>    show gate state and time remaining\\n\"\n            L\"ristretto \u002Fclose \u003Cuser>     close the gate now\\n\"\n            L\"ristretto \u002Fcloseall         close every open gate\\n\"\n            L\"ristretto \u002Fwindow \u003Csec>     gate lifetime, %lu-%lu (default %lu)\\n\"\n            L\"ristretto \u002Fmode gate|off    enforcement kill switch, no reboot\\n\",\n            static_cast\u003Cunsigned long>(kWindowMin),\n            static_cast\u003Cunsigned long>(kWindowMax),\n            static_cast\u003Cunsigned long>(kWindowDefault));\n    return argc == 1 ? 0 : 1;\n}\n",[41,12895,12896,12921,12925,12937,12941,12958,12966,12970,12974,13014,13054,13058,13112,13163,13206,13257,13308,13312,13327,13338,13349,13360,13384,13397,13413,13429,13445,13465],{"__ignoreMap":264},[70,12897,12898,12901,12904,12906,12908,12911,12913,12916,12919],{"class":564,"line":565},[70,12899,12900],{"class":956},"int",[70,12902,12903],{"class":852}," wmain",[70,12905,957],{"class":844},[70,12907,12900],{"class":956},[70,12909,12910],{"class":1004}," argc",[70,12912,1536],{"class":844},[70,12914,12915],{"class":956}," wchar_t**",[70,12917,12918],{"class":1004}," argv",[70,12920,972],{"class":844},[70,12922,12923],{"class":564,"line":572},[70,12924,1292],{"class":844},[70,12926,12927,12929,12931,12933,12935],{"class":564,"line":578},[70,12928,5893],{"class":1587},[70,12930,1591],{"class":844},[70,12932,1664],{"class":1587},[70,12934,7561],{"class":852},[70,12936,9116],{"class":844},[70,12938,12939],{"class":564,"line":584},[70,12940,1509],{"class":844},[70,12942,12943,12945,12947,12949,12952,12954,12956],{"class":564,"line":590},[70,12944,8704],{"class":852},[70,12946,957],{"class":844},[70,12948,4271],{"class":844},[70,12950,12951],{"class":867},"Run elevated. The 2FA store, state and lock are SYSTEM\u002FAdministrators only.",[70,12953,4307],{"class":1014},[70,12955,934],{"class":844},[70,12957,1900],{"class":844},[70,12959,12960,12962,12964],{"class":564,"line":596},[70,12961,2606],{"class":1587},[70,12963,1696],{"class":1014},[70,12965,1335],{"class":844},[70,12967,12968],{"class":564,"line":602},[70,12969,2405],{"class":844},[70,12971,12972],{"class":564,"line":607},[70,12973,838],{"emptyLinePlaceholder":837},[70,12975,12976,12978,12980,12983,12986,12989,12991,12993,12995,12997,13000,13002,13004,13006,13008,13010,13012],{"class":564,"line":613},[70,12977,5893],{"class":1587},[70,12979,1591],{"class":844},[70,12981,12982],{"class":1004},"argc",[70,12984,12985],{"class":848}," >=",[70,12987,12988],{"class":1014}," 2",[70,12990,1688],{"class":1587},[70,12992,12918],{"class":1004},[70,12994,1601],{"class":844},[70,12996,3852],{"class":1014},[70,12998,12999],{"class":844},"][",[70,13001,1575],{"class":1014},[70,13003,1606],{"class":844},[70,13005,2310],{"class":848},[70,13007,1611],{"class":844},[70,13009,1225],{"class":867},[70,13011,1617],{"class":844},[70,13013,972],{"class":844},[70,13015,13016,13018,13020,13022,13025,13027,13029,13031,13033,13035,13037,13039,13041,13043,13046,13048,13050,13052],{"class":564,"line":619},[70,13017,2606],{"class":1587},[70,13019,8625],{"class":852},[70,13021,957],{"class":844},[70,13023,13024],{"class":1004},"argv",[70,13026,1601],{"class":844},[70,13028,3852],{"class":1014},[70,13030,9134],{"class":844},[70,13032,12910],{"class":1004},[70,13034,1911],{"class":848},[70,13036,12988],{"class":1014},[70,13038,3147],{"class":848},[70,13040,12918],{"class":1004},[70,13042,1601],{"class":844},[70,13044,13045],{"class":1014},"2",[70,13047,1606],{"class":844},[70,13049,1547],{"class":848},[70,13051,2955],{"class":1014},[70,13053,1900],{"class":844},[70,13055,13056],{"class":564,"line":625},[70,13057,838],{"emptyLinePlaceholder":837},[70,13059,13060,13062,13064,13066,13068,13071,13073,13075,13077,13079,13081,13083,13085,13087,13089,13092,13094,13096,13099,13101,13103,13105,13107,13109],{"class":564,"line":631},[70,13061,5893],{"class":1587},[70,13063,1591],{"class":844},[70,13065,12982],{"class":1004},[70,13067,2974],{"class":848},[70,13069,13070],{"class":1014}," 3",[70,13072,1688],{"class":1587},[70,13074,12459],{"class":1587},[70,13076,12462],{"class":852},[70,13078,957],{"class":844},[70,13080,13024],{"class":1004},[70,13082,1601],{"class":844},[70,13084,3852],{"class":1014},[70,13086,9134],{"class":844},[70,13088,1312],{"class":844},[70,13090,13091],{"class":867},"\u002Fstatus",[70,13093,934],{"class":844},[70,13095,3960],{"class":844},[70,13097,13098],{"class":1587},"  return",[70,13100,10040],{"class":852},[70,13102,957],{"class":844},[70,13104,13024],{"class":1004},[70,13106,1601],{"class":844},[70,13108,13045],{"class":1014},[70,13110,13111],{"class":844},"]);\n",[70,13113,13114,13116,13118,13120,13122,13124,13126,13128,13130,13132,13134,13136,13138,13140,13142,13144,13146,13148,13151,13153,13155,13157,13159,13161],{"class":564,"line":636},[70,13115,5893],{"class":1587},[70,13117,1591],{"class":844},[70,13119,12982],{"class":1004},[70,13121,2974],{"class":848},[70,13123,13070],{"class":1014},[70,13125,1688],{"class":1587},[70,13127,12459],{"class":1587},[70,13129,12462],{"class":852},[70,13131,957],{"class":844},[70,13133,13024],{"class":1004},[70,13135,1601],{"class":844},[70,13137,3852],{"class":1014},[70,13139,9134],{"class":844},[70,13141,1312],{"class":844},[70,13143,240],{"class":867},[70,13145,934],{"class":844},[70,13147,3960],{"class":844},[70,13149,13150],{"class":1587},"   return",[70,13152,10937],{"class":852},[70,13154,957],{"class":844},[70,13156,13024],{"class":1004},[70,13158,1601],{"class":844},[70,13160,13045],{"class":1014},[70,13162,13111],{"class":844},[70,13164,13165,13167,13169,13171,13173,13175,13177,13179,13181,13183,13185,13187,13189,13191,13193,13195,13197,13199,13202,13204],{"class":564,"line":642},[70,13166,5893],{"class":1587},[70,13168,1591],{"class":844},[70,13170,12982],{"class":1004},[70,13172,2974],{"class":848},[70,13174,12988],{"class":1014},[70,13176,1688],{"class":1587},[70,13178,12459],{"class":1587},[70,13180,12462],{"class":852},[70,13182,957],{"class":844},[70,13184,13024],{"class":1004},[70,13186,1601],{"class":844},[70,13188,3852],{"class":1014},[70,13190,9134],{"class":844},[70,13192,1312],{"class":844},[70,13194,219],{"class":867},[70,13196,934],{"class":844},[70,13198,3960],{"class":844},[70,13200,13201],{"class":1587},"return",[70,13203,11199],{"class":852},[70,13205,2784],{"class":844},[70,13207,13208,13210,13212,13214,13216,13218,13220,13222,13224,13226,13228,13230,13232,13234,13236,13239,13241,13243,13245,13247,13249,13251,13253,13255],{"class":564,"line":648},[70,13209,5893],{"class":1587},[70,13211,1591],{"class":844},[70,13213,12982],{"class":1004},[70,13215,2974],{"class":848},[70,13217,13070],{"class":1014},[70,13219,1688],{"class":1587},[70,13221,12459],{"class":1587},[70,13223,12462],{"class":852},[70,13225,957],{"class":844},[70,13227,13024],{"class":1004},[70,13229,1601],{"class":844},[70,13231,3852],{"class":1014},[70,13233,9134],{"class":844},[70,13235,1312],{"class":844},[70,13237,13238],{"class":867},"\u002Fwindow",[70,13240,934],{"class":844},[70,13242,3960],{"class":844},[70,13244,13098],{"class":1587},[70,13246,11875],{"class":852},[70,13248,957],{"class":844},[70,13250,13024],{"class":1004},[70,13252,1601],{"class":844},[70,13254,13045],{"class":1014},[70,13256,13111],{"class":844},[70,13258,13259,13261,13263,13265,13267,13269,13271,13273,13275,13277,13279,13281,13283,13285,13287,13290,13292,13294,13296,13298,13300,13302,13304,13306],{"class":564,"line":654},[70,13260,5893],{"class":1587},[70,13262,1591],{"class":844},[70,13264,12982],{"class":1004},[70,13266,2974],{"class":848},[70,13268,13070],{"class":1014},[70,13270,1688],{"class":1587},[70,13272,12459],{"class":1587},[70,13274,12462],{"class":852},[70,13276,957],{"class":844},[70,13278,13024],{"class":1004},[70,13280,1601],{"class":844},[70,13282,3852],{"class":1014},[70,13284,9134],{"class":844},[70,13286,1312],{"class":844},[70,13288,13289],{"class":867},"\u002Fmode",[70,13291,934],{"class":844},[70,13293,3960],{"class":844},[70,13295,6234],{"class":1587},[70,13297,12431],{"class":852},[70,13299,957],{"class":844},[70,13301,13024],{"class":1004},[70,13303,1601],{"class":844},[70,13305,13045],{"class":1014},[70,13307,13111],{"class":844},[70,13309,13310],{"class":564,"line":659},[70,13311,838],{"emptyLinePlaceholder":837},[70,13313,13314,13316,13318,13320,13323,13325],{"class":564,"line":665},[70,13315,9772],{"class":852},[70,13317,957],{"class":844},[70,13319,4271],{"class":844},[70,13321,13322],{"class":867},"ristretto \u003Cuser> [code]     open the network gate (TOTP check)",[70,13324,4307],{"class":1014},[70,13326,6591],{"class":844},[70,13328,13329,13331,13334,13336],{"class":564,"line":671},[70,13330,9727],{"class":844},[70,13332,13333],{"class":867},"ristretto \u002Fstatus \u003Cuser>    show gate state and time remaining",[70,13335,4307],{"class":1014},[70,13337,6591],{"class":844},[70,13339,13340,13342,13345,13347],{"class":564,"line":677},[70,13341,9727],{"class":844},[70,13343,13344],{"class":867},"ristretto \u002Fclose \u003Cuser>     close the gate now",[70,13346,4307],{"class":1014},[70,13348,6591],{"class":844},[70,13350,13351,13353,13356,13358],{"class":564,"line":683},[70,13352,9727],{"class":844},[70,13354,13355],{"class":867},"ristretto \u002Fcloseall         close every open gate",[70,13357,4307],{"class":1014},[70,13359,6591],{"class":844},[70,13361,13362,13364,13367,13369,13371,13373,13376,13378,13380,13382],{"class":564,"line":689},[70,13363,9727],{"class":844},[70,13365,13366],{"class":867},"ristretto \u002Fwindow \u003Csec>     gate lifetime, ",[70,13368,8261],{"class":1014},[70,13370,11945],{"class":867},[70,13372,8261],{"class":1014},[70,13374,13375],{"class":867}," (default ",[70,13377,8261],{"class":1014},[70,13379,1008],{"class":867},[70,13381,4307],{"class":1014},[70,13383,6591],{"class":844},[70,13385,13386,13388,13391,13393,13395],{"class":564,"line":695},[70,13387,9727],{"class":844},[70,13389,13390],{"class":867},"ristretto \u002Fmode gate|off    enforcement kill switch, no reboot",[70,13392,4307],{"class":1014},[70,13394,934],{"class":844},[70,13396,2945],{"class":844},[70,13398,13399,13401,13403,13405,13407,13409,13411],{"class":564,"line":701},[70,13400,11804],{"class":848},[70,13402,8270],{"class":956},[70,13404,8273],{"class":956},[70,13406,7711],{"class":848},[70,13408,957],{"class":844},[70,13410,11970],{"class":1004},[70,13412,12119],{"class":844},[70,13414,13415,13417,13419,13421,13423,13425,13427],{"class":564,"line":706},[70,13416,11804],{"class":848},[70,13418,8270],{"class":956},[70,13420,8273],{"class":956},[70,13422,7711],{"class":848},[70,13424,957],{"class":844},[70,13426,11985],{"class":1004},[70,13428,12119],{"class":844},[70,13430,13431,13433,13435,13437,13439,13441,13443],{"class":564,"line":712},[70,13432,11804],{"class":848},[70,13434,8270],{"class":956},[70,13436,8273],{"class":956},[70,13438,7711],{"class":848},[70,13440,957],{"class":844},[70,13442,2723],{"class":1004},[70,13444,2285],{"class":844},[70,13446,13447,13449,13451,13453,13455,13457,13459,13461,13463],{"class":564,"line":718},[70,13448,6234],{"class":1587},[70,13450,12910],{"class":1004},[70,13452,2974],{"class":848},[70,13454,1696],{"class":1014},[70,13456,3147],{"class":848},[70,13458,1811],{"class":1014},[70,13460,1547],{"class":848},[70,13462,1696],{"class":1014},[70,13464,1335],{"class":844},[70,13466,13467],{"class":564,"line":723},[70,13468,5666],{"class":844},[17,13470,13471,13473,13474,13476,13477,13480],{},[41,13472,12890],{}," checks elevation first and then the commands. Anything that doesn't start with ",[41,13475,1225],{}," is a user name, so ",[41,13478,13479],{},"ristretto alice"," opens a gate. Without arguments the tool prints its help and returns 0, with wrong arguments the same help and 1.",[12,13482,13484],{"id":13483},"four-things-my-first-draft-got-wrong","Four things my first draft got wrong",[17,13486,13487],{},"I wrote the filter, read it twice, thought it was fine, and then had it audited. Four findings, and three of them were things I carried over from Part 2 without looking at them again. The code above is the fixed version. This chapter shows what it looked like before, and it's the part I learned the most from.",[250,13489,13491,13492,13494],{"id":13490},"_1-swprintf_s-can-take-lsass-down","1. ",[41,13493,2063],{}," can take lsass down",[17,13496,13497],{},"I built my log lines the way Part 2 builds them:",[258,13499,13501],{"className":557,"code":13500,"language":559,"meta":264,"style":264},"WCHAR text[256];\nswprintf_s(text, L\"Refused a network logon for enrolled account '%s' (RID %lu): \"\n                 L\"no fresh gate (last unlock %lu s ago or none).\", name, rid, ageSec);\n",[41,13502,13503,13515,13539],{"__ignoreMap":264},[70,13504,13505,13507,13509,13511,13513],{"class":564,"line":565},[70,13506,1527],{"class":1004},[70,13508,4214],{"class":1004},[70,13510,1601],{"class":844},[70,13512,5446],{"class":1014},[70,13514,1798],{"class":844},[70,13516,13517,13519,13521,13523,13525,13527,13529,13531,13533,13535,13537],{"class":564,"line":572},[70,13518,2063],{"class":852},[70,13520,957],{"class":844},[70,13522,263],{"class":1004},[70,13524,1536],{"class":844},[70,13526,1312],{"class":844},[70,13528,4654],{"class":867},[70,13530,8713],{"class":1014},[70,13532,4688],{"class":867},[70,13534,8261],{"class":1014},[70,13536,4721],{"class":867},[70,13538,6591],{"class":844},[70,13540,13541,13544,13547,13549,13551,13553,13555,13557,13559,13561,13563,13565],{"class":564,"line":578},[70,13542,13543],{"class":844},"                 L\"",[70,13545,13546],{"class":867},"no fresh gate (last unlock ",[70,13548,8261],{"class":1014},[70,13550,4788],{"class":867},[70,13552,934],{"class":844},[70,13554,1536],{"class":844},[70,13556,4583],{"class":1004},[70,13558,1536],{"class":844},[70,13560,2177],{"class":1004},[70,13562,1536],{"class":844},[70,13564,3421],{"class":1004},[70,13566,1900],{"class":844},[17,13568,13569,13570,13572],{},"On overflow, ",[41,13571,2063],{}," calls the invalid-parameter handler, and the default handler terminates the process, here lsass. That's a bugcheck, reached from the logon path, which is the one place where a bugcheck can turn into a boot loop.",[17,13574,13575],{},"It didn't overflow. With a 128-character account name and the largest possible RID and age, that line is 247 characters long, plus the terminator, in a 256-character buffer. So it worked every time I ran it. It also means the next person who adds three words to the format string ships a machine that bluescreens on the right username. Eight characters of headroom last until somebody edits the format string.",[17,13577,13578,13579,13581,13582,13585,13586,13588,13589,13592],{},"The fix is ",[41,13580,2059],{}," from above. The same rule took ",[41,13583,13584],{},"_time64"," out: it's CRT code on the logon path, in a file whose own header rules the CRT out. My first version of this paragraph said ",[41,13587,13584],{}," drags in locale machinery. It doesn't. It calls ",[41,13590,13591],{},"GetSystemTimeAsFileTime"," and converts, so the filter now calls that directly and gets the identical value.",[250,13594,13596,13597,13600],{"id":13595},"_2-the-catch-never-worked","2. The ",[41,13598,13599],{},"catch (...)"," never worked",[17,13602,13603],{},"Part 2's filter ends with a catch-all, and so did mine:",[258,13605,13607],{"className":557,"code":13606,"language":559,"meta":264,"style":264},"catch (...)\n{\n    \u002F\u002F Nothing above allocates; if that ever changes: fail closed.\n}\n",[41,13608,13609,13621,13625,13630],{"__ignoreMap":264},[70,13610,13611,13614,13616,13619],{"class":564,"line":565},[70,13612,13613],{"class":1587},"catch",[70,13615,1591],{"class":844},[70,13617,13618],{"class":5665},"...",[70,13620,972],{"class":844},[70,13622,13623],{"class":564,"line":572},[70,13624,1292],{"class":844},[70,13626,13627],{"class":564,"line":578},[70,13628,13629],{"class":568},"    \u002F\u002F Nothing above allocates; if that ever changes: fail closed.\n",[70,13631,13632],{"class":564,"line":584},[70,13633,5666],{"class":844},[17,13635,13636,13637,13640,13641,13643,13644,13646],{},"With ",[41,13638,13639],{},"\u002FEHsc",", which is in the build line of both articles, a C++ ",[41,13642,13599],{}," doesn't catch SEH exceptions. An access violation walks straight past it. And an access violation is the realistic failure here, because the one pointer we dereference is a ",[41,13645,4830],{}," from the caller.",[17,13648,13649,13650,13653],{},"The handler covered ",[41,13651,13652],{},"std::bad_alloc",", in a function without allocations. An access violation, the case I wrote it for, never reached it.",[17,13655,13578,13656,13658,13659,13661,13662,13665,13666,13668],{},[41,13657,5686],{}," \u002F ",[41,13660,5035],{}," around ",[41,13663,13664],{},"Decide()",", with ",[41,13667,5031],{}," deciding what to catch. In normal code, catching an access violation and carrying on is a bad idea, because you don't know what state you're in. Inside lsass the alternative is a bugcheck, so refusing one logon and staying up is the better of two bad options. That lowers the risk without removing it.",[250,13670,13672],{"id":13671},"_3-the-enrollment-check-failed-open","3. The enrollment check failed open",[17,13674,13675,13676,9459],{},"This finding mattered most, and it's four lines copied from ",[41,13677,441],{},[258,13679,13681],{"className":557,"code":13680,"language":559,"meta":264,"style":264},"return RegGetValueW(HKEY_LOCAL_MACHINE, kDoppioRids, value,\n                    RRF_RT_REG_DWORD, nullptr, nullptr, nullptr) == ERROR_SUCCESS;\n",[41,13682,13683,13705],{"__ignoreMap":264},[70,13684,13685,13687,13690,13692,13694,13696,13699,13701,13703],{"class":564,"line":565},[70,13686,13201],{"class":1587},[70,13688,13689],{"class":852}," RegGetValueW",[70,13691,957],{"class":844},[70,13693,1467],{"class":1004},[70,13695,1536],{"class":844},[70,13697,13698],{"class":1004}," kDoppioRids",[70,13700,1536],{"class":844},[70,13702,3449],{"class":1004},[70,13704,2945],{"class":844},[70,13706,13707,13710,13712,13714,13716,13718,13720,13722,13724,13726,13728],{"class":564,"line":572},[70,13708,13709],{"class":1004},"                    RRF_RT_REG_DWORD",[70,13711,1536],{"class":844},[70,13713,2955],{"class":1014},[70,13715,1536],{"class":844},[70,13717,2955],{"class":1014},[70,13719,1536],{"class":844},[70,13721,2955],{"class":1014},[70,13723,1008],{"class":844},[70,13725,2974],{"class":848},[70,13727,2977],{"class":1004},[70,13729,1335],{"class":844},[17,13731,13732,13733,438,13736,13738,13739,13741,13742,6744,13744,13746],{},"Every failure becomes ",[41,13734,13735],{},"false",[41,13737,13735],{}," means not enrolled, and not enrolled means ",[41,13740,3281],{},". For the credential provider that's fine, it has other checks behind it. In a filter whose only job is the veto, \"I couldn't tell\" must never come out as \"yes\". That's where the three outcomes come from. Doppio's own filter in Part 2 had the same hole. Both filters call Doppio's ",[41,13743,5242],{},[41,13745,432],{}," now.",[250,13748,13750,13751,13753],{"id":13749},"_4-windowsec-had-no-ceiling","4. ",[41,13752,2940],{}," had no ceiling",[17,13755,13756],{},"The config read accepted any non-zero DWORD:",[258,13758,13760],{"className":557,"code":13759,"language":559,"meta":264,"style":264},"if (RegGetValueW(..., L\"WindowSec\", ..., &v, &cb) == ERROR_SUCCESS && v)\n    g_windowSec = v;\n",[41,13761,13762,13811],{"__ignoreMap":264},[70,13763,13764,13766,13768,13770,13772,13774,13776,13778,13780,13782,13784,13787,13789,13791,13793,13795,13797,13799,13801,13803,13805,13807,13809],{"class":564,"line":565},[70,13765,4386],{"class":1587},[70,13767,1591],{"class":844},[70,13769,2925],{"class":852},[70,13771,957],{"class":844},[70,13773,13618],{"class":5665},[70,13775,1536],{"class":844},[70,13777,1312],{"class":844},[70,13779,2940],{"class":867},[70,13781,934],{"class":844},[70,13783,1536],{"class":844},[70,13785,13786],{"class":5665}," ...",[70,13788,1536],{"class":844},[70,13790,2960],{"class":848},[70,13792,1860],{"class":1004},[70,13794,1536],{"class":844},[70,13796,2960],{"class":848},[70,13798,2969],{"class":1004},[70,13800,1008],{"class":844},[70,13802,2974],{"class":848},[70,13804,2977],{"class":1004},[70,13806,1688],{"class":1587},[70,13808,1776],{"class":1004},[70,13810,972],{"class":844},[70,13812,13813,13816,13818,13820],{"class":564,"line":572},[70,13814,13815],{"class":1004},"    g_windowSec",[70,13817,1349],{"class":848},[70,13819,1776],{"class":1004},[70,13821,1335],{"class":844},[17,13823,13824,13826],{},[41,13825,3193],{}," seconds is about 136 years. A typo in regedit turns the gate into a door that stays propped open for the rest of the machine's life, and nothing anywhere says so. The key is admin-only, so nobody else can do this. The risk is an admin who gives themselves a permanent bypass by mistake and believes the gate works.",[17,13828,13829,13830,13833],{},"It's clamped to 30 to 3600 now, on both sides: in ",[41,13831,13832],{},"ristretto.exe \u002Fwindow"," when writing, and in the filter when reading. Only the second one matters. Clamping on write is a courtesy to the person using the tool. Clamping on read is what holds when the value didn't come from the tool.",[250,13835,13837],{"id":13836},"and-the-smaller-ones","And the smaller ones",[17,13839,13840,13841,13844],{},"There were more, all in ",[41,13842,13843],{},"SECURITY-FIXES.md"," in the repo:",[105,13846,13847,13850,13859,13869,13872,13875,13891],{},[108,13848,13849],{},"Concurrent logon threads wrote the config globals without synchronisation. They're interlocked now, see the config cache above.",[108,13851,13852,13853,13855,13856,13858],{},"The allow path wrote the account name to ",[41,13854,1244],{}," on every SMB connection. That's behind ",[41,13857,1240],{}," now, off by default.",[108,13860,13861,13862,13865,13866,13868],{},"The installer stored ",[41,13863,13864],{},"PrevAuth0 = 'TacSubAuth'"," when there had been no ",[41,13867,82],{}," at all. Uninstalling then pointed lsass at a DLL that was never there.",[108,13870,13871],{},"The config key with the kill switch kept inherited permissions instead of the hardened ACL.",[108,13873,13874],{},"The installer used paths relative to the current directory, and the uninstaller left open gates behind, so a reinstall could find a stale gate inside its window.",[108,13876,13877,13878,13880,13881,13884,13885,13887,13888,13890],{},"A review of this article found more, all fixed: the installer lost its restore record on a second run, it checked LSA protection through one registry value only, ",[41,13879,13091],{}," ignored the RID index, the tool read ",[41,13882,13883],{},"WindowSec = 0"," differently from the filter, and ",[41,13886,219],{}," could stop early. They're in ",[41,13889,13843],{}," as item 16.",[108,13892,13893,13894,13896],{},"After the second review of Part 2, the filter got the same three contract fixes as Doppio's: a veto code from Microsoft's list, all three out-parameters on every path, and no objection to pass-through logons. They're next to the code above, and in ",[41,13895,13843],{}," as item 15.",[17,13898,13899,13900,13902,13903,13905],{},"Part 2 taught me to check what Windows expects from my code. This round added a second lesson: ",[41,13901,2063],{},", the catch-all and the enrollment read looked fine in the code I copied them from. In lsass the same lines can take down the machine or let an enrolled account through, and that was true in the original too. Doppio's filter in ",[21,13904,24],{"href":23}," has the same three fixes now.",[12,13907,13909],{"id":13908},"build","Build",[258,13911,13914],{"className":13912,"code":13913,"language":263,"meta":264},[261],"git clone https:\u002F\u002Fgithub.com\u002FNobrac\u002FRistretto\ncd Ristretto\ngit submodule update --init --recursive\nbuild-ristretto.bat\n",[41,13915,13913],{"__ignoreMap":264},[17,13917,13918,13919,13922],{},"From the ",[49,13920,13921],{},"x64 Native Tools Command Prompt",", as always. The submodule pins Doppio to one commit, so a breaking change on Doppio's side fails this build until you move the pin on purpose.",[258,13924,13929],{"className":13925,"code":13926,"filename":13927,"language":13928,"meta":264,"style":264},"language-bat shiki shiki-themes gruvbox-dark-medium","@echo off\nsetlocal\nset DOPPIO=extern\\doppio\n\nif not exist %DOPPIO%\\verify.cpp (\n    echo Doppio sources missing. Run: git submodule update --init --recursive\n    exit \u002Fb 1\n)\n\nrem The filter runs inside lsass: \u002FQspectre and \u002Fguard:cf are not optional\nrem there, and \u002FMT keeps a CRT DLL dependency out of the logon path.\nrem \u002FWX so a new warning in lsass-resident code fails the build.\nrem\nrem Two separate passes, on purpose.\nrem\nrem The pass that produces the binaries treats COMPILER warnings as errors\nrem (\u002FW4 \u002FWX). That is the right bar for code that runs inside lsass.\nrem\nrem \u002Fanalyze is a different thing: a deeper static analyser that reasons about\nrem pointers and buffers. It finds real bugs, and it also reports things that\nrem are correct but that it cannot prove across a call, plus deliberate\nrem constructs like an SEH filter. Those are worth reading. They are not worth\nrem failing a build over, and the Windows SDK's own headers raise some of them\nrem (C28301, a PNTSTATUS annotation mismatch between bcrypt.h and ntsecapi.h).\nrem So analysis runs afterwards, advisory, and its findings do not stop the\nrem build. Read them; do not ignore them.\n\nrem The filter links one file of Doppio's: store_index.cpp, the enrollment\nrem index reads. It holds no DPAPI and no account lookups, so it is safe in\nrem lsass; store.cpp, with both, stays out of this DLL.\ncl \u002Fnologo \u002FLD \u002FMT \u002FO2 \u002Fguard:cf \u002FQspectre \u002FEHsc \u002Fstd:c++17 \u002FW4 \u002FWX \u002Fsdl ^\n   \u002FDUNICODE \u002FD_UNICODE ^\n   \u002FI%DOPPIO% src\\ristretto_filter.cpp %DOPPIO%\\store_index.cpp \u002FFe:RistrettoFilter.dll ^\n   \u002Flink \u002Fguard:cf \u002FCETCOMPAT \u002FDYNAMICBASE \u002FNXCOMPAT \u002FHIGHENTROPYVA ^\n   \u002FDEF:src\\RistrettoFilter.def advapi32.lib\nif errorlevel 1 goto :fail\n\ncl \u002Fnologo \u002FMT \u002FO2 \u002Fguard:cf \u002FQspectre \u002FEHsc \u002Fstd:c++17 \u002FW4 \u002FWX \u002Fsdl ^\n   \u002FDUNICODE \u002FD_UNICODE ^\n   \u002FI%DOPPIO% src\\ristretto.cpp %DOPPIO%\\totp.cpp %DOPPIO%\\store.cpp ^\n   %DOPPIO%\\store_index.cpp %DOPPIO%\\verify.cpp %DOPPIO%\\statelock.cpp \u002FFe:ristretto.exe ^\n   \u002Flink \u002Fguard:cf \u002FCETCOMPAT \u002FDYNAMICBASE \u002FNXCOMPAT \u002FHIGHENTROPYVA ^\n   bcrypt.lib crypt32.lib advapi32.lib\nif errorlevel 1 goto :fail\n\necho.\necho === Static analysis (advisory - does not fail the build) ===\ncl \u002Fnologo \u002Fc \u002Fanalyze \u002Fanalyze:only \u002Fanalyze:external- ^\n   \u002Fexternal:anglebrackets \u002Fexternal:W0 \u002Fwd28301 ^\n   \u002FEHsc \u002Fstd:c++17 \u002FW4 \u002FDUNICODE \u002FD_UNICODE \u002FI%DOPPIO% ^\n   src\\ristretto_filter.cpp src\\ristretto.cpp\necho === End of analysis ===\nrem Clear whatever the analysis pass left in errorlevel. Its findings are\nrem advisory, and the caller checks this script's exit code to decide whether\nrem the build worked - which it did, above.\nver >nul\necho.\n\ndel \u002Fq *.obj *.exp 2>nul\necho Built RistrettoFilter.dll and ristretto.exe\ngoto :eof\n:fail\necho BUILD FAILED\nexit \u002Fb 1\n","build-ristretto.bat","bat",[41,13930,13931,13942,13947,13961,13965,13988,13996,14007,14011,14015,14024,14031,14038,14043,14050,14054,14061,14068,14072,14079,14086,14093,14100,14107,14114,14121,14128,14132,14139,14146,14153,14161,14168,14193,14200,14205,14222,14226,14233,14239,14272,14303,14309,14314,14328,14332,14339,14352,14359,14366,14380,14385,14392,14399,14406,14413,14423,14429,14433,14447,14454,14464,14470,14477],{"__ignoreMap":264},[70,13932,13933,13936,13939],{"class":564,"line":565},[70,13934,13935],{"class":848},"@",[70,13937,13938],{"class":1587},"echo",[70,13940,13941],{"class":1587}," off\n",[70,13943,13944],{"class":564,"line":572},[70,13945,13946],{"class":1587},"setlocal\n",[70,13948,13949,13952,13955,13958],{"class":564,"line":578},[70,13950,13951],{"class":1587},"set",[70,13953,13954],{"class":1004}," DOPPIO",[70,13956,13957],{"class":848},"=",[70,13959,13960],{"class":5665},"extern\\doppio\n",[70,13962,13963],{"class":564,"line":584},[70,13964,838],{"emptyLinePlaceholder":837},[70,13966,13967,13969,13972,13975,13977,13980,13983,13986],{"class":564,"line":590},[70,13968,4386],{"class":1587},[70,13970,13971],{"class":1587}," not",[70,13973,13974],{"class":1587}," exist",[70,13976,1863],{"class":844},[70,13978,13979],{"class":1004},"DOPPIO",[70,13981,13982],{"class":844},"%",[70,13984,13985],{"class":5665},"\\verify.cpp ",[70,13987,5788],{"class":844},[70,13989,13990,13993],{"class":564,"line":596},[70,13991,13992],{"class":1587},"    echo",[70,13994,13995],{"class":5665}," Doppio sources missing. Run: git submodule update --init --recursive\n",[70,13997,13998,14001,14004],{"class":564,"line":602},[70,13999,14000],{"class":1587},"    exit",[70,14002,14003],{"class":5665}," \u002Fb ",[70,14005,14006],{"class":1014},"1\n",[70,14008,14009],{"class":564,"line":607},[70,14010,972],{"class":844},[70,14012,14013],{"class":564,"line":613},[70,14014,838],{"emptyLinePlaceholder":837},[70,14016,14017,14021],{"class":564,"line":619},[70,14018,14020],{"class":14019},"sUF24","rem",[70,14022,14023],{"class":568}," The filter runs inside lsass: \u002FQspectre and \u002Fguard:cf are not optional\n",[70,14025,14026,14028],{"class":564,"line":625},[70,14027,14020],{"class":14019},[70,14029,14030],{"class":568}," there, and \u002FMT keeps a CRT DLL dependency out of the logon path.\n",[70,14032,14033,14035],{"class":564,"line":631},[70,14034,14020],{"class":14019},[70,14036,14037],{"class":568}," \u002FWX so a new warning in lsass-resident code fails the build.\n",[70,14039,14040],{"class":564,"line":636},[70,14041,14042],{"class":14019},"rem\n",[70,14044,14045,14047],{"class":564,"line":642},[70,14046,14020],{"class":14019},[70,14048,14049],{"class":568}," Two separate passes, on purpose.\n",[70,14051,14052],{"class":564,"line":648},[70,14053,14042],{"class":14019},[70,14055,14056,14058],{"class":564,"line":654},[70,14057,14020],{"class":14019},[70,14059,14060],{"class":568}," The pass that produces the binaries treats COMPILER warnings as errors\n",[70,14062,14063,14065],{"class":564,"line":659},[70,14064,14020],{"class":14019},[70,14066,14067],{"class":568}," (\u002FW4 \u002FWX). That is the right bar for code that runs inside lsass.\n",[70,14069,14070],{"class":564,"line":665},[70,14071,14042],{"class":14019},[70,14073,14074,14076],{"class":564,"line":671},[70,14075,14020],{"class":14019},[70,14077,14078],{"class":568}," \u002Fanalyze is a different thing: a deeper static analyser that reasons about\n",[70,14080,14081,14083],{"class":564,"line":677},[70,14082,14020],{"class":14019},[70,14084,14085],{"class":568}," pointers and buffers. It finds real bugs, and it also reports things that\n",[70,14087,14088,14090],{"class":564,"line":683},[70,14089,14020],{"class":14019},[70,14091,14092],{"class":568}," are correct but that it cannot prove across a call, plus deliberate\n",[70,14094,14095,14097],{"class":564,"line":689},[70,14096,14020],{"class":14019},[70,14098,14099],{"class":568}," constructs like an SEH filter. Those are worth reading. They are not worth\n",[70,14101,14102,14104],{"class":564,"line":695},[70,14103,14020],{"class":14019},[70,14105,14106],{"class":568}," failing a build over, and the Windows SDK's own headers raise some of them\n",[70,14108,14109,14111],{"class":564,"line":701},[70,14110,14020],{"class":14019},[70,14112,14113],{"class":568}," (C28301, a PNTSTATUS annotation mismatch between bcrypt.h and ntsecapi.h).\n",[70,14115,14116,14118],{"class":564,"line":706},[70,14117,14020],{"class":14019},[70,14119,14120],{"class":568}," So analysis runs afterwards, advisory, and its findings do not stop the\n",[70,14122,14123,14125],{"class":564,"line":712},[70,14124,14020],{"class":14019},[70,14126,14127],{"class":568}," build. Read them; do not ignore them.\n",[70,14129,14130],{"class":564,"line":718},[70,14131,838],{"emptyLinePlaceholder":837},[70,14133,14134,14136],{"class":564,"line":723},[70,14135,14020],{"class":14019},[70,14137,14138],{"class":568}," The filter links one file of Doppio's: store_index.cpp, the enrollment\n",[70,14140,14141,14143],{"class":564,"line":729},[70,14142,14020],{"class":14019},[70,14144,14145],{"class":568}," index reads. It holds no DPAPI and no account lookups, so it is safe in\n",[70,14147,14148,14150],{"class":564,"line":735},[70,14149,14020],{"class":14019},[70,14151,14152],{"class":568}," lsass; store.cpp, with both, stays out of this DLL.\n",[70,14154,14155,14158],{"class":564,"line":741},[70,14156,14157],{"class":5665},"cl \u002Fnologo \u002FLD \u002FMT \u002FO2 \u002Fguard:cf \u002FQspectre \u002FEHsc \u002Fstd:c++17 \u002FW4 \u002FWX \u002Fsdl ",[70,14159,14160],{"class":1014},"^\n",[70,14162,14163,14166],{"class":564,"line":746},[70,14164,14165],{"class":5665},"   \u002FDUNICODE \u002FD_UNICODE ",[70,14167,14160],{"class":1014},[70,14169,14170,14173,14175,14177,14179,14182,14184,14186,14188,14191],{"class":564,"line":752},[70,14171,14172],{"class":5665},"   \u002FI",[70,14174,13982],{"class":844},[70,14176,13979],{"class":1004},[70,14178,13982],{"class":844},[70,14180,14181],{"class":5665}," src\\ristretto_filter.cpp ",[70,14183,13982],{"class":844},[70,14185,13979],{"class":1004},[70,14187,13982],{"class":844},[70,14189,14190],{"class":5665},"\\store_index.cpp \u002FFe:RistrettoFilter.dll ",[70,14192,14160],{"class":1014},[70,14194,14195,14198],{"class":564,"line":758},[70,14196,14197],{"class":5665},"   \u002Flink \u002Fguard:cf \u002FCETCOMPAT \u002FDYNAMICBASE \u002FNXCOMPAT \u002FHIGHENTROPYVA ",[70,14199,14160],{"class":1014},[70,14201,14202],{"class":564,"line":764},[70,14203,14204],{"class":5665},"   \u002FDEF:src\\RistrettoFilter.def advapi32.lib\n",[70,14206,14207,14209,14212,14214,14217,14219],{"class":564,"line":769},[70,14208,4386],{"class":1587},[70,14210,14211],{"class":1587}," errorlevel",[70,14213,1696],{"class":1014},[70,14215,14216],{"class":1587}," goto",[70,14218,1547],{"class":844},[70,14220,14221],{"class":1587},"fail\n",[70,14223,14224],{"class":564,"line":775},[70,14225,838],{"emptyLinePlaceholder":837},[70,14227,14228,14231],{"class":564,"line":781},[70,14229,14230],{"class":5665},"cl \u002Fnologo \u002FMT \u002FO2 \u002Fguard:cf \u002FQspectre \u002FEHsc \u002Fstd:c++17 \u002FW4 \u002FWX \u002Fsdl ",[70,14232,14160],{"class":1014},[70,14234,14235,14237],{"class":564,"line":787},[70,14236,14165],{"class":5665},[70,14238,14160],{"class":1014},[70,14240,14241,14243,14245,14247,14249,14252,14254,14256,14258,14261,14263,14265,14267,14270],{"class":564,"line":793},[70,14242,14172],{"class":5665},[70,14244,13982],{"class":844},[70,14246,13979],{"class":1004},[70,14248,13982],{"class":844},[70,14250,14251],{"class":5665}," src\\ristretto.cpp ",[70,14253,13982],{"class":844},[70,14255,13979],{"class":1004},[70,14257,13982],{"class":844},[70,14259,14260],{"class":5665},"\\totp.cpp ",[70,14262,13982],{"class":844},[70,14264,13979],{"class":1004},[70,14266,13982],{"class":844},[70,14268,14269],{"class":5665},"\\store.cpp ",[70,14271,14160],{"class":1014},[70,14273,14274,14277,14279,14281,14284,14286,14288,14290,14292,14294,14296,14298,14301],{"class":564,"line":799},[70,14275,14276],{"class":844},"   %",[70,14278,13979],{"class":1004},[70,14280,13982],{"class":844},[70,14282,14283],{"class":5665},"\\store_index.cpp ",[70,14285,13982],{"class":844},[70,14287,13979],{"class":1004},[70,14289,13982],{"class":844},[70,14291,13985],{"class":5665},[70,14293,13982],{"class":844},[70,14295,13979],{"class":1004},[70,14297,13982],{"class":844},[70,14299,14300],{"class":5665},"\\statelock.cpp \u002FFe:ristretto.exe ",[70,14302,14160],{"class":1014},[70,14304,14305,14307],{"class":564,"line":805},[70,14306,14197],{"class":5665},[70,14308,14160],{"class":1014},[70,14310,14311],{"class":564,"line":811},[70,14312,14313],{"class":5665},"   bcrypt.lib crypt32.lib advapi32.lib\n",[70,14315,14316,14318,14320,14322,14324,14326],{"class":564,"line":817},[70,14317,4386],{"class":1587},[70,14319,14211],{"class":1587},[70,14321,1696],{"class":1014},[70,14323,14216],{"class":1587},[70,14325,1547],{"class":844},[70,14327,14221],{"class":1587},[70,14329,14330],{"class":564,"line":823},[70,14331,838],{"emptyLinePlaceholder":837},[70,14333,14334,14336],{"class":564,"line":829},[70,14335,13938],{"class":1587},[70,14337,14338],{"class":5665},".\n",[70,14340,14341,14343,14346,14349],{"class":564,"line":834},[70,14342,13938],{"class":1587},[70,14344,14345],{"class":5665}," === Static analysis (advisory - does not fail the build) ",[70,14347,14348],{"class":848},"==",[70,14350,14351],{"class":5665},"=\n",[70,14353,14354,14357],{"class":564,"line":841},[70,14355,14356],{"class":5665},"cl \u002Fnologo \u002Fc \u002Fanalyze \u002Fanalyze:only \u002Fanalyze:external- ",[70,14358,14160],{"class":1014},[70,14360,14361,14364],{"class":564,"line":856},[70,14362,14363],{"class":5665},"   \u002Fexternal:anglebrackets \u002Fexternal:W0 \u002Fwd28301 ",[70,14365,14160],{"class":1014},[70,14367,14368,14371,14373,14375,14377],{"class":564,"line":874},[70,14369,14370],{"class":5665},"   \u002FEHsc \u002Fstd:c++17 \u002FW4 \u002FDUNICODE \u002FD_UNICODE \u002FI",[70,14372,13982],{"class":844},[70,14374,13979],{"class":1004},[70,14376,13982],{"class":844},[70,14378,14379],{"class":1014}," ^\n",[70,14381,14382],{"class":564,"line":888},[70,14383,14384],{"class":5665},"   src\\ristretto_filter.cpp src\\ristretto.cpp\n",[70,14386,14387,14389],{"class":564,"line":902},[70,14388,13938],{"class":1587},[70,14390,14391],{"class":5665}," === End of analysis ===\n",[70,14393,14394,14396],{"class":564,"line":916},[70,14395,14020],{"class":14019},[70,14397,14398],{"class":568}," Clear whatever the analysis pass left in errorlevel. Its findings are\n",[70,14400,14401,14403],{"class":564,"line":921},[70,14402,14020],{"class":14019},[70,14404,14405],{"class":568}," advisory, and the caller checks this script's exit code to decide whether\n",[70,14407,14408,14410],{"class":564,"line":940},[70,14409,14020],{"class":14019},[70,14411,14412],{"class":568}," the build worked - which it did, above.\n",[70,14414,14415,14418,14420],{"class":564,"line":945},[70,14416,14417],{"class":1587},"ver",[70,14419,1911],{"class":848},[70,14421,14422],{"class":1014},"nul\n",[70,14424,14425,14427],{"class":564,"line":975},[70,14426,13938],{"class":1587},[70,14428,14338],{"class":5665},[70,14430,14431],{"class":564,"line":980},[70,14432,838],{"emptyLinePlaceholder":837},[70,14434,14435,14438,14441,14443,14445],{"class":564,"line":991},[70,14436,14437],{"class":1587},"del",[70,14439,14440],{"class":5665}," \u002Fq *.obj *.exp ",[70,14442,13045],{"class":1014},[70,14444,7711],{"class":848},[70,14446,14422],{"class":1014},[70,14448,14449,14451],{"class":564,"line":1023},[70,14450,13938],{"class":1587},[70,14452,14453],{"class":5665}," Built RistrettoFilter.dll and ristretto.exe\n",[70,14455,14456,14459,14461],{"class":564,"line":1031},[70,14457,14458],{"class":1587},"goto",[70,14460,1547],{"class":844},[70,14462,14463],{"class":1587},"eof\n",[70,14465,14466,14468],{"class":564,"line":1037},[70,14467,9459],{"class":844},[70,14469,14221],{"class":1587},[70,14471,14472,14474],{"class":564,"line":1043},[70,14473,13938],{"class":1587},[70,14475,14476],{"class":5665}," BUILD FAILED\n",[70,14478,14479,14482,14484],{"class":564,"line":1049},[70,14480,14481],{"class":1587},"exit",[70,14483,14003],{"class":5665},[70,14485,14006],{"class":1014},[17,14487,14488],{},"The flags, from the outside in:",[105,14490,14491,14497,14507,14513,14519],{},[108,14492,14493,14496],{},[41,14494,14495],{},"\u002FMT"," links the C++ runtime statically. A DLL that lsass needs on the logon path and that can't find its runtime doesn't load, and you find out at boot or at the first network logon after it.",[108,14498,14499,14502,14503,14506],{},[41,14500,14501],{},"\u002Fguard:cf"," turns on Control Flow Guard, and ",[41,14504,14505],{},"\u002FDYNAMICBASE \u002FNXCOMPAT \u002FHIGHENTROPYVA"," give you ASLR with the full 64-bit range and DEP. Most of that is the linker default nowadays, but I'd rather see it written down for code in lsass.",[108,14508,14509,14512],{},[41,14510,14511],{},"\u002FCETCOMPAT"," marks both binaries compatible with CET shadow stacks, the same as Doppio's build.",[108,14514,14515,14518],{},[41,14516,14517],{},"\u002FQspectre"," adds the Spectre variant 1 mitigations. This DLL shares an address space with every secret on the machine.",[108,14520,14521,14524],{},[41,14522,14523],{},"\u002FW4 \u002FWX \u002Fsdl"," makes every compiler warning an error, plus the SDL checks. A new warning in lsass-resident code should stop the build.",[17,14526,14527,14529,14530,14533,14534,14537,14538,14540,14541,14543,14544,14546,14547,438,14550,14553,14554,14557,14558,14560],{},[41,14528,2420],{}," runs in a second pass, and that pass is advisory. The reason is a header from Microsoft: ",[41,14531,14532],{},"bcrypt.h"," declares ",[41,14535,14536],{},"PNTSTATUS"," without SAL annotations, ",[41,14539,897],{}," declares it again with them, and ",[41,14542,2420],{}," reports that as C28301 from inside the Windows SDK. Dropping ",[41,14545,2427],{}," because of it would be the wrong fix. The script keeps system headers out of the analysis (",[41,14548,14549],{},"\u002Fanalyze:external-",[41,14551,14552],{},"\u002Fexternal:anglebrackets \u002Fexternal:W0",", and ",[41,14555,14556],{},"\u002Fwd28301"," as a backstop), the real build keeps ",[41,14559,2427],{},", and the analysis prints its findings without failing anything. Read them. That pass found C6054 and C6320 from the filter chapter.",[17,14562,14563,14566,14567,14570],{},[41,14564,14565],{},"ver >nul"," at the end of the analysis pass looks strange. It's there to reset ",[41,14568,14569],{},"errorlevel",", so a CI job that checks the script's exit code sees the result of the build and ignores the advisory analysis.",[17,14572,14573,14574,14576,14577,14579],{},"Both passes link ",[41,14575,432],{},". The DLL needs it for the enrollment read, and the exe needs it because Doppio's ",[41,14578,441],{}," calls into it.",[17,14581,14582,14583,14586,14587,14589,14590,14592,14593,14595,14596,1225,14599,14601],{},"The repo also has a test for the filter. ",[41,14584,14585],{},"make -C tests"," builds the real ",[41,14588,549],{}," and Doppio's ",[41,14591,432],{}," on Linux, against fakes for the registry and the clock, under AddressSanitizer and UBSan. Then it calls the export the way MSV1_0 would. It checks the scope and pass-through, the gate and its edges, the window bounds, the kill switch, the fail-closed paths and the out-parameters. I put six of the old behaviours back one at a time, and the test failed for each of them. It can't show how MSV1_0 really calls the filter, and ",[41,14594,5686],{}," turns into ",[41,14597,14598],{},"try",[41,14600,13613],{}," there. That part is still the VM's job. CI runs the test on every push.",[12,14603,14605],{"id":14604},"install","Install",[17,14607,14608],{},"From an elevated PowerShell, in the build folder, after a snapshot:",[258,14610,14614],{"className":14611,"code":14612,"language":14613,"meta":264,"style":264},"language-powershell shiki shiki-themes gruvbox-dark-medium",".\\install-ristretto.ps1\n","powershell",[41,14615,14616],{"__ignoreMap":264},[70,14617,14618,14620],{"class":564,"line":565},[70,14619,25],{"class":5665},[70,14621,14622],{"class":852},"\\install-ristretto.ps1\n",[258,14624,14627],{"className":14611,"code":14625,"filename":14626,"language":14613,"meta":264,"style":264},"#requires -RunAsAdministrator\nparam(\n    # Skips the confirmation below, for a caller that has already asked.\n    # Barista passes it: it runs this through powershell.exe -NonInteractive,\n    # where Read-Host cannot prompt and throws instead, so without this the\n    # install never gets past the question. Run the script by hand and leave\n    # the switch off to get the prompt.\n    [switch]$Confirmed\n)\n$ErrorActionPreference = 'Stop'\n\n# ---------------------------------------------------------------------------\n# Installs RistrettoFilter into the MSV1_0 Auth0 slot and ristretto.exe next\n# to TacProvider.dll.\n#\n# Read this before running it on anything you care about:\n#   * The filter is an UNSIGNED DLL that lsass loads. A fault in it is a\n#     bugcheck, and a bugcheck on the logon path can mean a reboot loop.\n#   * It needs RunAsPPL off. RunAsPPL is what stops credential dumping from\n#     lsass, so turning it off makes the machine materially easier to loot.\n#     Net effect on a production box: likely negative.\n#   * Test VM only. Take a snapshot first.\n# ---------------------------------------------------------------------------\n\n$sddl = 'D:P(A;OICI;KA;;;SY)(A;OICI;KA;;;BA)'   # SYSTEM + Administrators, inheritance blocked\n$noPrev = '__none__'                            # sentinel: there was no Auth0 before us\n\nfunction Set-KeyAclHard {\n    param([Parameter(Mandatory)][string]$SubKey)\n    # The config key holds the Mode kill switch and WindowSec. Anyone who can\n    # write it can switch enforcement off, so it does not get to inherit\n    # whatever HKLM\\SOFTWARE happens to hand out.\n    $sec = New-Object System.Security.AccessControl.RegistrySecurity\n    $sec.SetSecurityDescriptorSddlForm($sddl)\n    $k = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey(\n            $SubKey, 'ReadWriteSubTree', 'ChangePermissions')\n    if (-not $k) { throw \"Could not open HKLM\\$SubKey to set its ACL.\" }\n    try { $k.SetAccessControl($sec) } finally { $k.Close() }\n}\n\n# --- preconditions ---------------------------------------------------------\n\n$dll = Join-Path $PSScriptRoot 'RistrettoFilter.dll'\n$exe = Join-Path $PSScriptRoot 'ristretto.exe'\nforeach ($f in @($dll, $exe)) {\n    if (-not (Test-Path -LiteralPath $f)) { throw \"Missing build output: $f. Run build-ristretto.bat first.\" }\n}\n\n$doppioKey = 'HKLM:\\SOFTWARE\\TheAdminCafe\\2FA'\nif (-not (Test-Path \"$doppioKey\\Rids\")) {\n    throw \"Doppio not found ($doppioKey\\Rids missing). Install and enroll with Doppio first.\"\n}\nif (-not (Test-Path 'C:\\Program Files\\TheAdminCafe\\TacProvider.dll')) {\n    throw \"TacProvider.dll not found - install Doppio first.\"\n}\n\n$lsaKey = 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Lsa'\n$msvKey = \"$lsaKey\\MSV1_0\"\n$cfgPathRel = 'SOFTWARE\\TheAdminCafe\\Ristretto'\n$cfg    = \"HKLM:\\$cfgPathRel\"\n\n# A domain controller keeps its accounts in the directory, not in a local SAM.\n# Doppio's RID index only knows local accounts, so on a DC the filter would\n# check domain RIDs against it. install-subauth.ps1 refuses for the same reason.\n# DomainRole 4 = backup DC, 5 = primary DC.\nif ((Get-CimInstance Win32_ComputerSystem).DomainRole -ge 4) {\n    throw \"This is a domain controller. The filter only knows local accounts by RID; on a DC it would check domain RIDs against Doppio's index. Refusing.\"\n}\n\n# With LSA protection on, lsass only loads plug-ins with a Microsoft LSA\n# signature, and this DLL has none. Without these checks the install looked\n# fine, Auth0 changed, and the filter simply never loaded.\n$lsaProps = Get-ItemProperty -Path $lsaKey\nif ($lsaProps.RunAsPPL -or $lsaProps.RunAsPPLBoot) {\n    throw \"LSA protection (RunAsPPL or RunAsPPLBoot) is on - the unsigned filter will never load. Turn it off in the test VM first.\"\n}\n\n# The registry values are not the whole story: a UEFI variable or Windows' own\n# default for new Windows 11 devices can turn LSA protection on as well. The\n# proof at runtime is WinInit event 12, \"LSASS.exe was started as a protected\n# process with level: 4\", in the System log. Event ID 12 is used by other\n# sources too, hence the provider check.\n$boot = (Get-CimInstance Win32_OperatingSystem).LastBootUpTime\n$pplEvent = Get-WinEvent -FilterHashtable @{ LogName = 'System'; Id = 12; StartTime = $boot } -ErrorAction SilentlyContinue |\n            Where-Object { $_.ProviderName -like '*Wininit*' } | Select-Object -First 1\nif ($pplEvent) {\n    throw \"lsass runs as a protected process since the last boot (WinInit event 12). It will not load the unsigned filter. Turn LSA protection off in the test VM first.\"\n}\n\nif (-not (Test-Path $msvKey)) { throw \"$msvKey does not exist. Wrong machine?\" }\n\n$cur = (Get-ItemProperty $msvKey -Name Auth0 -ErrorAction SilentlyContinue).Auth0\nif ($cur -and $cur -notin @('TacSubAuth', 'RistrettoFilter')) {\n    throw \"Auth0 belongs to '$cur'. Not touching another product's filter.\"\n}\n\n# What the uninstaller will restore. Checked here, before anything changes.\n# An older version of this script wiped the config key on a second run (see\n# below) and then recorded its own name, so the uninstaller \"restored\"\n# Ristretto and Doppio's TacSubAuth never came back.\n$prevRecorded = $null\nif (Test-Path $cfg) {\n    $prevRecorded = (Get-ItemProperty $cfg -Name PrevAuth0 -ErrorAction SilentlyContinue).PrevAuth0\n}\nif ($prevRecorded -eq 'RistrettoFilter') {\n    throw \"PrevAuth0 under $cfg is 'RistrettoFilter', left by an older version of this script. Set it to what Auth0 was before Ristretto ('TacSubAuth' for Doppio's filter, '$noPrev' for none) and run this again.\"\n}\nif ($cur -eq 'RistrettoFilter' -and -not $prevRecorded) {\n    throw \"Auth0 is already 'RistrettoFilter', but PrevAuth0 under $cfg is missing, so there is no record of what to restore. Set PrevAuth0 ('TacSubAuth' or '$noPrev') and run this again.\"\n}\n\nWrite-Host \"\"\nWrite-Host \"About to put an unsigned DLL in the lsass logon path on this machine.\" -ForegroundColor Yellow\nWrite-Host \"If it faults, this box may not boot cleanly. Snapshot taken?\" -ForegroundColor Yellow\n\nif ($Confirmed) {\n    # The two warnings above are printed either way, so they are in the\n    # caller's log next to whatever it asked its own user.\n    Write-Host \"Confirmed by the caller (-Confirmed).\"\n} else {\n    try {\n        $answer = Read-Host \"Type YES to continue\"\n    } catch {\n        # -NonInteractive without -Confirmed. The raw error from Read-Host\n        # says nothing about how to get past it.\n        throw \"No interactive console for the confirmation. Run this in a normal PowerShell window, or pass -Confirmed if the caller has already asked.\"\n    }\n    if ($answer -cne 'YES') { Write-Host \"Aborted. Nothing changed.\"; return }\n}\n\n# --- config key, hardened before anything is written into it ---------------\n\n# Create the keys only when they are missing. New-Item -Force on a registry\n# key that exists does not open it: the registry provider deletes the key with\n# all its values and subkeys and creates it again. That is how a second run\n# used to lose PrevAuth0, Mode, WindowSec and every open gate.\nif (-not (Test-Path $cfg)) { New-Item $cfg | Out-Null }\nSet-KeyAclHard -SubKey $cfgPathRel\nif (-not (Test-Path \"$cfg\\Gate\")) { New-Item \"$cfg\\Gate\" | Out-Null }\nSet-KeyAclHard -SubKey \"$cfgPathRel\\Gate\"\n\n# Remember what Auth0 was, including \"nothing\". The old code stored\n# 'TacSubAuth' when there was no Auth0 at all, so uninstalling invented a\n# filter that was never registered and pointed lsass at a DLL that does not\n# exist on this machine.\nif (-not (Get-ItemProperty $cfg -Name PrevAuth0 -ErrorAction SilentlyContinue)) {\n    if ($cur) { Set-ItemProperty $cfg -Name PrevAuth0 -Value $cur }\n    else      { Set-ItemProperty $cfg -Name PrevAuth0 -Value $noPrev }\n}\nif (-not (Get-ItemProperty $cfg -Name Mode -ErrorAction SilentlyContinue)) {\n    Set-ItemProperty $cfg -Name Mode -Value 1 -Type DWord\n}\nif (-not (Get-ItemProperty $cfg -Name WindowSec -ErrorAction SilentlyContinue)) {\n    Set-ItemProperty $cfg -Name WindowSec -Value 300 -Type DWord   # 5 min; filter clamps 30-3600\n}\n\n# --- event source ----------------------------------------------------------\n\n$src = 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\TheAdminCafe Ristretto'\nif (-not (Test-Path $src)) { New-Item $src | Out-Null }\nSet-ItemProperty $src -Name EventMessageFile -Value '%SystemRoot%\\System32\\EventCreate.exe' -Type ExpandString\nSet-ItemProperty $src -Name TypesSupported -Value 7 -Type DWord\n\n# --- payload, then the registry switch -------------------------------------\n# Order matters: the DLL has to be on disk before Auth0 names it, or the\n# next boot looks for a filter that is not there.\n\nCopy-Item $dll \"$env:SystemRoot\\System32\\RistrettoFilter.dll\" -Force\n\n# Must sit next to TacProvider.dll: shared state.lock (see README).\nCopy-Item $exe 'C:\\Program Files\\TheAdminCafe\\ristretto.exe' -Force\n\nSet-ItemProperty $msvKey -Name Auth0 -Value 'RistrettoFilter'\n\n$shown = if ($cur) { $cur } else { '(none)' }\nWrite-Host \"\"\nWrite-Host \"Auth0: '$shown' -> 'RistrettoFilter' (saved for restore). Reboot to load.\"\nWrite-Host \"Default window is 300 s. Open a gate with: ristretto \u003Cuser>\"\n","install-ristretto.ps1",[41,14628,14629,14636,14643,14648,14653,14658,14663,14668,14682,14686,14704,14708,14713,14718,14723,14728,14733,14738,14743,14748,14753,14758,14763,14767,14771,14789,14808,14812,14823,14852,14857,14862,14867,14883,14899,14920,14947,14984,15022,15026,15030,15035,15039,15062,15083,15114,15158,15162,15166,15182,15208,15227,15231,15254,15265,15269,15273,15289,15307,15323,15343,15347,15352,15357,15362,15367,15394,15405,15409,15413,15418,15423,15428,15450,15475,15486,15490,15494,15499,15504,15509,15514,15519,15540,15605,15644,15656,15667,15671,15675,15708,15712,15745,15786,15804,15808,15812,15817,15822,15827,15832,15846,15862,15892,15896,15917,15942,15946,15975,16000,16004,16008,16016,16032,16047,16051,16064,16069,16074,16086,16096,16102,16122,16132,16137,16143,16156,16161,16200,16205,16210,16216,16221,16227,16233,16239,16245,16280,16296,16338,16356,16361,16367,16373,16379,16385,16414,16449,16478,16483,16513,16538,16543,16573,16600,16605,16610,16616,16621,16638,16671,16702,16727,16732,16738,16744,16750,16755,16783,16788,16794,16814,16819,16842,16847,16887,16894,16913],{"__ignoreMap":264},[70,14630,14631,14633],{"class":564,"line":565},[70,14632,845],{"class":568},[70,14634,14635],{"class":14019},"requires -RunAsAdministrator\n",[70,14637,14638,14641],{"class":564,"line":572},[70,14639,14640],{"class":1587},"param",[70,14642,5788],{"class":844},[70,14644,14645],{"class":564,"line":578},[70,14646,14647],{"class":568},"    # Skips the confirmation below, for a caller that has already asked.\n",[70,14649,14650],{"class":564,"line":584},[70,14651,14652],{"class":568},"    # Barista passes it: it runs this through powershell.exe -NonInteractive,\n",[70,14654,14655],{"class":564,"line":590},[70,14656,14657],{"class":568},"    # where Read-Host cannot prompt and throws instead, so without this the\n",[70,14659,14660],{"class":564,"line":596},[70,14661,14662],{"class":568},"    # install never gets past the question. Run the script by hand and leave\n",[70,14664,14665],{"class":564,"line":602},[70,14666,14667],{"class":568},"    # the switch off to get the prompt.\n",[70,14669,14670,14673,14676,14679],{"class":564,"line":607},[70,14671,14672],{"class":844},"    [",[70,14674,14675],{"class":956},"switch",[70,14677,14678],{"class":844},"]$",[70,14680,14681],{"class":1004},"Confirmed\n",[70,14683,14684],{"class":564,"line":613},[70,14685,972],{"class":844},[70,14687,14688,14691,14694,14696,14698,14701],{"class":564,"line":619},[70,14689,14690],{"class":844},"$",[70,14692,14693],{"class":1004},"ErrorActionPreference",[70,14695,1349],{"class":848},[70,14697,9028],{"class":844},[70,14699,14700],{"class":867},"Stop",[70,14702,14703],{"class":844},"'\n",[70,14705,14706],{"class":564,"line":625},[70,14707,838],{"emptyLinePlaceholder":837},[70,14709,14710],{"class":564,"line":631},[70,14711,14712],{"class":568},"# ---------------------------------------------------------------------------\n",[70,14714,14715],{"class":564,"line":636},[70,14716,14717],{"class":568},"# Installs RistrettoFilter into the MSV1_0 Auth0 slot and ristretto.exe next\n",[70,14719,14720],{"class":564,"line":642},[70,14721,14722],{"class":568},"# to TacProvider.dll.\n",[70,14724,14725],{"class":564,"line":648},[70,14726,14727],{"class":568},"#\n",[70,14729,14730],{"class":564,"line":654},[70,14731,14732],{"class":568},"# Read this before running it on anything you care about:\n",[70,14734,14735],{"class":564,"line":659},[70,14736,14737],{"class":568},"#   * The filter is an UNSIGNED DLL that lsass loads. A fault in it is a\n",[70,14739,14740],{"class":564,"line":665},[70,14741,14742],{"class":568},"#     bugcheck, and a bugcheck on the logon path can mean a reboot loop.\n",[70,14744,14745],{"class":564,"line":671},[70,14746,14747],{"class":568},"#   * It needs RunAsPPL off. RunAsPPL is what stops credential dumping from\n",[70,14749,14750],{"class":564,"line":677},[70,14751,14752],{"class":568},"#     lsass, so turning it off makes the machine materially easier to loot.\n",[70,14754,14755],{"class":564,"line":683},[70,14756,14757],{"class":568},"#     Net effect on a production box: likely negative.\n",[70,14759,14760],{"class":564,"line":689},[70,14761,14762],{"class":568},"#   * Test VM only. Take a snapshot first.\n",[70,14764,14765],{"class":564,"line":695},[70,14766,14712],{"class":568},[70,14768,14769],{"class":564,"line":701},[70,14770,838],{"emptyLinePlaceholder":837},[70,14772,14773,14775,14778,14780,14782,14784,14786],{"class":564,"line":706},[70,14774,14690],{"class":844},[70,14776,14777],{"class":1004},"sddl",[70,14779,1349],{"class":848},[70,14781,9028],{"class":844},[70,14783,6929],{"class":867},[70,14785,1617],{"class":844},[70,14787,14788],{"class":568},"   # SYSTEM + Administrators, inheritance blocked\n",[70,14790,14791,14793,14796,14798,14800,14803,14805],{"class":564,"line":712},[70,14792,14690],{"class":844},[70,14794,14795],{"class":1004},"noPrev",[70,14797,1349],{"class":848},[70,14799,9028],{"class":844},[70,14801,14802],{"class":867},"__none__",[70,14804,1617],{"class":844},[70,14806,14807],{"class":568},"                            # sentinel: there was no Auth0 before us\n",[70,14809,14810],{"class":564,"line":718},[70,14811,838],{"emptyLinePlaceholder":837},[70,14813,14814,14817,14820],{"class":564,"line":723},[70,14815,14816],{"class":956},"function",[70,14818,14819],{"class":852}," Set-KeyAclHard",[70,14821,14822],{"class":844}," {\n",[70,14824,14825,14828,14831,14835,14837,14840,14843,14845,14847,14850],{"class":564,"line":729},[70,14826,14827],{"class":1587},"    param",[70,14829,14830],{"class":844},"([",[70,14832,14834],{"class":14833},"sgrX6","Parameter",[70,14836,957],{"class":844},[70,14838,14839],{"class":1004},"Mandatory",[70,14841,14842],{"class":844},")][",[70,14844,6692],{"class":956},[70,14846,14678],{"class":844},[70,14848,14849],{"class":1004},"SubKey",[70,14851,972],{"class":844},[70,14853,14854],{"class":564,"line":735},[70,14855,14856],{"class":568},"    # The config key holds the Mode kill switch and WindowSec. Anyone who can\n",[70,14858,14859],{"class":564,"line":741},[70,14860,14861],{"class":568},"    # write it can switch enforcement off, so it does not get to inherit\n",[70,14863,14864],{"class":564,"line":746},[70,14865,14866],{"class":568},"    # whatever HKLM\\SOFTWARE happens to hand out.\n",[70,14868,14869,14872,14875,14877,14880],{"class":564,"line":752},[70,14870,14871],{"class":844},"    $",[70,14873,14874],{"class":1004},"sec",[70,14876,1349],{"class":848},[70,14878,14879],{"class":852}," New-Object",[70,14881,14882],{"class":5665}," System.Security.AccessControl.RegistrySecurity\n",[70,14884,14885,14887,14889,14892,14895,14897],{"class":564,"line":758},[70,14886,14871],{"class":844},[70,14888,14874],{"class":1004},[70,14890,14891],{"class":956},".SetSecurityDescriptorSddlForm",[70,14893,14894],{"class":844},"($",[70,14896,14777],{"class":1004},[70,14898,972],{"class":844},[70,14900,14901,14903,14905,14907,14910,14913,14915,14918],{"class":564,"line":764},[70,14902,14871],{"class":844},[70,14904,8214],{"class":1004},[70,14906,1349],{"class":848},[70,14908,14909],{"class":844}," [",[70,14911,14912],{"class":956},"Microsoft.Win32.Registry",[70,14914,1606],{"class":844},[70,14916,14917],{"class":5665},"::LocalMachine.OpenSubKey",[70,14919,5788],{"class":844},[70,14921,14922,14925,14927,14929,14931,14934,14936,14938,14940,14943,14945],{"class":564,"line":769},[70,14923,14924],{"class":844},"            $",[70,14926,14849],{"class":1004},[70,14928,1536],{"class":848},[70,14930,9028],{"class":844},[70,14932,14933],{"class":867},"ReadWriteSubTree",[70,14935,1617],{"class":844},[70,14937,1536],{"class":848},[70,14939,9028],{"class":844},[70,14941,14942],{"class":867},"ChangePermissions",[70,14944,1617],{"class":844},[70,14946,972],{"class":844},[70,14948,14949,14951,14953,14956,14959,14961,14963,14965,14968,14970,14973,14975,14977,14980,14982],{"class":564,"line":775},[70,14950,5893],{"class":1587},[70,14952,1591],{"class":844},[70,14954,14955],{"class":1587},"-not",[70,14957,14958],{"class":844}," $",[70,14960,8214],{"class":1004},[70,14962,1008],{"class":844},[70,14964,1821],{"class":844},[70,14966,14967],{"class":1587}," throw",[70,14969,928],{"class":844},[70,14971,14972],{"class":867},"Could not open HKLM\\",[70,14974,14690],{"class":844},[70,14976,14849],{"class":1004},[70,14978,14979],{"class":867}," to set its ACL.",[70,14981,934],{"class":844},[70,14983,2011],{"class":844},[70,14985,14986,14989,14991,14993,14995,14998,15000,15002,15004,15006,15009,15011,15013,15015,15018,15020],{"class":564,"line":781},[70,14987,14988],{"class":1587},"    try",[70,14990,1821],{"class":844},[70,14992,14958],{"class":844},[70,14994,8214],{"class":1004},[70,14996,14997],{"class":956},".SetAccessControl",[70,14999,14894],{"class":844},[70,15001,14874],{"class":1004},[70,15003,1008],{"class":844},[70,15005,1881],{"class":844},[70,15007,15008],{"class":1587}," finally",[70,15010,1821],{"class":844},[70,15012,14958],{"class":844},[70,15014,8214],{"class":1004},[70,15016,15017],{"class":956},".Close",[70,15019,1997],{"class":844},[70,15021,2011],{"class":844},[70,15023,15024],{"class":564,"line":787},[70,15025,5666],{"class":844},[70,15027,15028],{"class":564,"line":793},[70,15029,838],{"emptyLinePlaceholder":837},[70,15031,15032],{"class":564,"line":799},[70,15033,15034],{"class":568},"# --- preconditions ---------------------------------------------------------\n",[70,15036,15037],{"class":564,"line":805},[70,15038,838],{"emptyLinePlaceholder":837},[70,15040,15041,15043,15046,15048,15051,15053,15056,15058,15060],{"class":564,"line":811},[70,15042,14690],{"class":844},[70,15044,15045],{"class":1004},"dll",[70,15047,1349],{"class":848},[70,15049,15050],{"class":852}," Join-Path",[70,15052,14958],{"class":844},[70,15054,15055],{"class":1014},"PSScriptRoot",[70,15057,9028],{"class":844},[70,15059,388],{"class":867},[70,15061,14703],{"class":844},[70,15063,15064,15066,15069,15071,15073,15075,15077,15079,15081],{"class":564,"line":817},[70,15065,14690],{"class":844},[70,15067,15068],{"class":1004},"exe",[70,15070,1349],{"class":848},[70,15072,15050],{"class":852},[70,15074,14958],{"class":844},[70,15076,15055],{"class":1014},[70,15078,9028],{"class":844},[70,15080,302],{"class":867},[70,15082,14703],{"class":844},[70,15084,15085,15088,15091,15094,15097,15100,15102,15104,15106,15108,15110,15112],{"class":564,"line":823},[70,15086,15087],{"class":1587},"foreach",[70,15089,15090],{"class":844}," ($",[70,15092,15093],{"class":1004},"f",[70,15095,15096],{"class":1587}," in",[70,15098,15099],{"class":1587}," @",[70,15101,14894],{"class":844},[70,15103,15045],{"class":1004},[70,15105,1536],{"class":848},[70,15107,14958],{"class":844},[70,15109,15068],{"class":1004},[70,15111,3960],{"class":844},[70,15113,14822],{"class":844},[70,15115,15116,15118,15120,15122,15124,15127,15129,15132,15134,15136,15138,15140,15142,15144,15147,15149,15151,15154,15156],{"class":564,"line":829},[70,15117,5893],{"class":1587},[70,15119,1591],{"class":844},[70,15121,14955],{"class":1587},[70,15123,1591],{"class":844},[70,15125,15126],{"class":852},"Test-Path",[70,15128,2617],{"class":848},[70,15130,15131],{"class":5665},"LiteralPath ",[70,15133,14690],{"class":844},[70,15135,15093],{"class":1004},[70,15137,3960],{"class":844},[70,15139,1821],{"class":844},[70,15141,14967],{"class":1587},[70,15143,928],{"class":844},[70,15145,15146],{"class":867},"Missing build output: ",[70,15148,14690],{"class":844},[70,15150,15093],{"class":1004},[70,15152,15153],{"class":867},". Run build-ristretto.bat first.",[70,15155,934],{"class":844},[70,15157,2011],{"class":844},[70,15159,15160],{"class":564,"line":834},[70,15161,5666],{"class":844},[70,15163,15164],{"class":564,"line":841},[70,15165,838],{"emptyLinePlaceholder":837},[70,15167,15168,15170,15173,15175,15177,15180],{"class":564,"line":856},[70,15169,14690],{"class":844},[70,15171,15172],{"class":1004},"doppioKey",[70,15174,1349],{"class":848},[70,15176,9028],{"class":844},[70,15178,15179],{"class":867},"HKLM:\\SOFTWARE\\TheAdminCafe\\2FA",[70,15181,14703],{"class":844},[70,15183,15184,15186,15188,15190,15192,15194,15197,15199,15202,15204,15206],{"class":564,"line":874},[70,15185,4386],{"class":1587},[70,15187,1591],{"class":844},[70,15189,14955],{"class":1587},[70,15191,1591],{"class":844},[70,15193,15126],{"class":852},[70,15195,15196],{"class":844}," \"$",[70,15198,15172],{"class":1004},[70,15200,15201],{"class":867},"\\Rids",[70,15203,934],{"class":844},[70,15205,3960],{"class":844},[70,15207,14822],{"class":844},[70,15209,15210,15213,15215,15218,15220,15222,15225],{"class":564,"line":888},[70,15211,15212],{"class":1587},"    throw",[70,15214,928],{"class":844},[70,15216,15217],{"class":867},"Doppio not found (",[70,15219,14690],{"class":844},[70,15221,15172],{"class":1004},[70,15223,15224],{"class":867},"\\Rids missing). Install and enroll with Doppio first.",[70,15226,6591],{"class":844},[70,15228,15229],{"class":564,"line":902},[70,15230,5666],{"class":844},[70,15232,15233,15235,15237,15239,15241,15243,15245,15248,15250,15252],{"class":564,"line":916},[70,15234,4386],{"class":1587},[70,15236,1591],{"class":844},[70,15238,14955],{"class":1587},[70,15240,1591],{"class":844},[70,15242,15126],{"class":852},[70,15244,9028],{"class":844},[70,15246,15247],{"class":867},"C:\\Program Files\\TheAdminCafe\\TacProvider.dll",[70,15249,1617],{"class":844},[70,15251,3960],{"class":844},[70,15253,14822],{"class":844},[70,15255,15256,15258,15260,15263],{"class":564,"line":921},[70,15257,15212],{"class":1587},[70,15259,928],{"class":844},[70,15261,15262],{"class":867},"TacProvider.dll not found - install Doppio first.",[70,15264,6591],{"class":844},[70,15266,15267],{"class":564,"line":940},[70,15268,5666],{"class":844},[70,15270,15271],{"class":564,"line":945},[70,15272,838],{"emptyLinePlaceholder":837},[70,15274,15275,15277,15280,15282,15284,15287],{"class":564,"line":975},[70,15276,14690],{"class":844},[70,15278,15279],{"class":1004},"lsaKey",[70,15281,1349],{"class":848},[70,15283,9028],{"class":844},[70,15285,15286],{"class":867},"HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Lsa",[70,15288,14703],{"class":844},[70,15290,15291,15293,15296,15298,15300,15302,15305],{"class":564,"line":980},[70,15292,14690],{"class":844},[70,15294,15295],{"class":1004},"msvKey",[70,15297,1349],{"class":848},[70,15299,15196],{"class":844},[70,15301,15279],{"class":1004},[70,15303,15304],{"class":867},"\\MSV1_0",[70,15306,6591],{"class":844},[70,15308,15309,15311,15314,15316,15318,15321],{"class":564,"line":991},[70,15310,14690],{"class":844},[70,15312,15313],{"class":1004},"cfgPathRel",[70,15315,1349],{"class":848},[70,15317,9028],{"class":844},[70,15319,15320],{"class":867},"SOFTWARE\\TheAdminCafe\\Ristretto",[70,15322,14703],{"class":844},[70,15324,15325,15327,15330,15332,15334,15337,15339,15341],{"class":564,"line":1023},[70,15326,14690],{"class":844},[70,15328,15329],{"class":1004},"cfg",[70,15331,6924],{"class":848},[70,15333,928],{"class":844},[70,15335,15336],{"class":867},"HKLM:\\",[70,15338,14690],{"class":844},[70,15340,15313],{"class":1004},[70,15342,6591],{"class":844},[70,15344,15345],{"class":564,"line":1031},[70,15346,838],{"emptyLinePlaceholder":837},[70,15348,15349],{"class":564,"line":1037},[70,15350,15351],{"class":568},"# A domain controller keeps its accounts in the directory, not in a local SAM.\n",[70,15353,15354],{"class":564,"line":1043},[70,15355,15356],{"class":568},"# Doppio's RID index only knows local accounts, so on a DC the filter would\n",[70,15358,15359],{"class":564,"line":1049},[70,15360,15361],{"class":568},"# check domain RIDs against it. install-subauth.ps1 refuses for the same reason.\n",[70,15363,15364],{"class":564,"line":1055},[70,15365,15366],{"class":568},"# DomainRole 4 = backup DC, 5 = primary DC.\n",[70,15368,15369,15371,15373,15376,15379,15381,15384,15387,15390,15392],{"class":564,"line":1065},[70,15370,4386],{"class":1587},[70,15372,1075],{"class":844},[70,15374,15375],{"class":852},"Get-CimInstance",[70,15377,15378],{"class":5665}," Win32_ComputerSystem",[70,15380,1008],{"class":844},[70,15382,15383],{"class":5665},".DomainRole ",[70,15385,15386],{"class":848},"-ge",[70,15388,15389],{"class":1014}," 4",[70,15391,1008],{"class":844},[70,15393,14822],{"class":844},[70,15395,15396,15398,15400,15403],{"class":564,"line":1091},[70,15397,15212],{"class":1587},[70,15399,928],{"class":844},[70,15401,15402],{"class":867},"This is a domain controller. The filter only knows local accounts by RID; on a DC it would check domain RIDs against Doppio's index. Refusing.",[70,15404,6591],{"class":844},[70,15406,15407],{"class":564,"line":1098},[70,15408,5666],{"class":844},[70,15410,15411],{"class":564,"line":1103},[70,15412,838],{"emptyLinePlaceholder":837},[70,15414,15415],{"class":564,"line":1113},[70,15416,15417],{"class":568},"# With LSA protection on, lsass only loads plug-ins with a Microsoft LSA\n",[70,15419,15420],{"class":564,"line":1129},[70,15421,15422],{"class":568},"# signature, and this DLL has none. Without these checks the install looked\n",[70,15424,15425],{"class":564,"line":1136},[70,15426,15427],{"class":568},"# fine, Auth0 changed, and the filter simply never loaded.\n",[70,15429,15430,15432,15435,15437,15440,15442,15445,15447],{"class":564,"line":1141},[70,15431,14690],{"class":844},[70,15433,15434],{"class":1004},"lsaProps",[70,15436,1349],{"class":848},[70,15438,15439],{"class":852}," Get-ItemProperty",[70,15441,2617],{"class":848},[70,15443,15444],{"class":5665},"Path ",[70,15446,14690],{"class":844},[70,15448,15449],{"class":1004},"lsaKey\n",[70,15451,15452,15454,15456,15458,15461,15464,15466,15468,15471,15473],{"class":564,"line":1147},[70,15453,4386],{"class":1587},[70,15455,15090],{"class":844},[70,15457,15434],{"class":1004},[70,15459,15460],{"class":956},".RunAsPPL",[70,15462,15463],{"class":1587}," -or",[70,15465,14958],{"class":844},[70,15467,15434],{"class":1004},[70,15469,15470],{"class":956},".RunAsPPLBoot",[70,15472,1008],{"class":844},[70,15474,14822],{"class":844},[70,15476,15477,15479,15481,15484],{"class":564,"line":1153},[70,15478,15212],{"class":1587},[70,15480,928],{"class":844},[70,15482,15483],{"class":867},"LSA protection (RunAsPPL or RunAsPPLBoot) is on - the unsigned filter will never load. Turn it off in the test VM first.",[70,15485,6591],{"class":844},[70,15487,15488],{"class":564,"line":1159},[70,15489,5666],{"class":844},[70,15491,15492],{"class":564,"line":1172},[70,15493,838],{"emptyLinePlaceholder":837},[70,15495,15496],{"class":564,"line":1177},[70,15497,15498],{"class":568},"# The registry values are not the whole story: a UEFI variable or Windows' own\n",[70,15500,15501],{"class":564,"line":1183},[70,15502,15503],{"class":568},"# default for new Windows 11 devices can turn LSA protection on as well. The\n",[70,15505,15506],{"class":564,"line":1189},[70,15507,15508],{"class":568},"# proof at runtime is WinInit event 12, \"LSASS.exe was started as a protected\n",[70,15510,15511],{"class":564,"line":9806},[70,15512,15513],{"class":568},"# process with level: 4\", in the System log. Event ID 12 is used by other\n",[70,15515,15516],{"class":564,"line":9818},[70,15517,15518],{"class":568},"# sources too, hence the provider check.\n",[70,15520,15521,15523,15526,15528,15530,15532,15535,15537],{"class":564,"line":9832},[70,15522,14690],{"class":844},[70,15524,15525],{"class":1004},"boot",[70,15527,1349],{"class":848},[70,15529,1591],{"class":844},[70,15531,15375],{"class":852},[70,15533,15534],{"class":5665}," Win32_OperatingSystem",[70,15536,1008],{"class":844},[70,15538,15539],{"class":5665},".LastBootUpTime\n",[70,15541,15542,15544,15547,15549,15552,15554,15557,15559,15562,15565,15567,15569,15572,15574,15576,15579,15581,15584,15586,15589,15591,15593,15595,15597,15599,15602],{"class":564,"line":9854},[70,15543,14690],{"class":844},[70,15545,15546],{"class":1004},"pplEvent",[70,15548,1349],{"class":848},[70,15550,15551],{"class":852}," Get-WinEvent",[70,15553,2617],{"class":848},[70,15555,15556],{"class":5665},"FilterHashtable ",[70,15558,13935],{"class":1587},[70,15560,15561],{"class":844},"{",[70,15563,15564],{"class":956}," LogName",[70,15566,1349],{"class":848},[70,15568,9028],{"class":844},[70,15570,15571],{"class":867},"System",[70,15573,1617],{"class":844},[70,15575,1402],{"class":844},[70,15577,15578],{"class":956}," Id",[70,15580,1349],{"class":848},[70,15582,15583],{"class":1014}," 12",[70,15585,1402],{"class":844},[70,15587,15588],{"class":956}," StartTime",[70,15590,1349],{"class":848},[70,15592,14958],{"class":844},[70,15594,15525],{"class":1004},[70,15596,1881],{"class":844},[70,15598,2617],{"class":848},[70,15600,15601],{"class":5665},"ErrorAction SilentlyContinue ",[70,15603,15604],{"class":848},"|\n",[70,15606,15607,15610,15612,15614,15617,15620,15623,15625,15628,15630,15632,15634,15637,15639,15642],{"class":564,"line":9863},[70,15608,15609],{"class":852},"            Where-Object",[70,15611,1821],{"class":844},[70,15613,14958],{"class":844},[70,15615,15616],{"class":1014},"_",[70,15618,15619],{"class":956},".ProviderName",[70,15621,15622],{"class":848}," -like",[70,15624,9028],{"class":844},[70,15626,15627],{"class":867},"*Wininit*",[70,15629,1617],{"class":844},[70,15631,1881],{"class":844},[70,15633,7806],{"class":848},[70,15635,15636],{"class":852}," Select-Object",[70,15638,2617],{"class":848},[70,15640,15641],{"class":5665},"First ",[70,15643,14006],{"class":1014},[70,15645,15646,15648,15650,15652,15654],{"class":564,"line":11220},[70,15647,4386],{"class":1587},[70,15649,15090],{"class":844},[70,15651,15546],{"class":1004},[70,15653,1008],{"class":844},[70,15655,14822],{"class":844},[70,15657,15658,15660,15662,15665],{"class":564,"line":11247},[70,15659,15212],{"class":1587},[70,15661,928],{"class":844},[70,15663,15664],{"class":867},"lsass runs as a protected process since the last boot (WinInit event 12). It will not load the unsigned filter. Turn LSA protection off in the test VM first.",[70,15666,6591],{"class":844},[70,15668,15669],{"class":564,"line":11252},[70,15670,5666],{"class":844},[70,15672,15673],{"class":564,"line":11270},[70,15674,838],{"emptyLinePlaceholder":837},[70,15676,15677,15679,15681,15683,15685,15687,15689,15691,15693,15695,15697,15699,15701,15704,15706],{"class":564,"line":11279},[70,15678,4386],{"class":1587},[70,15680,1591],{"class":844},[70,15682,14955],{"class":1587},[70,15684,1591],{"class":844},[70,15686,15126],{"class":852},[70,15688,14958],{"class":844},[70,15690,15295],{"class":1004},[70,15692,3960],{"class":844},[70,15694,1821],{"class":844},[70,15696,14967],{"class":1587},[70,15698,15196],{"class":844},[70,15700,15295],{"class":1004},[70,15702,15703],{"class":867}," does not exist. Wrong machine?",[70,15705,934],{"class":844},[70,15707,2011],{"class":844},[70,15709,15710],{"class":564,"line":11284},[70,15711,838],{"emptyLinePlaceholder":837},[70,15713,15714,15716,15719,15721,15723,15726,15728,15730,15732,15735,15737,15740,15742],{"class":564,"line":11289},[70,15715,14690],{"class":844},[70,15717,15718],{"class":1004},"cur",[70,15720,1349],{"class":848},[70,15722,1591],{"class":844},[70,15724,15725],{"class":852},"Get-ItemProperty",[70,15727,14958],{"class":844},[70,15729,15295],{"class":1004},[70,15731,2617],{"class":848},[70,15733,15734],{"class":5665},"Name Auth0 ",[70,15736,11945],{"class":848},[70,15738,15739],{"class":5665},"ErrorAction SilentlyContinue",[70,15741,1008],{"class":844},[70,15743,15744],{"class":5665},".Auth0\n",[70,15746,15747,15749,15751,15753,15756,15758,15760,15763,15765,15767,15769,15771,15773,15775,15777,15780,15782,15784],{"class":564,"line":11317},[70,15748,4386],{"class":1587},[70,15750,15090],{"class":844},[70,15752,15718],{"class":1004},[70,15754,15755],{"class":1587}," -and",[70,15757,14958],{"class":844},[70,15759,15718],{"class":1004},[70,15761,15762],{"class":848}," -notin",[70,15764,15099],{"class":1587},[70,15766,957],{"class":844},[70,15768,1617],{"class":844},[70,15770,86],{"class":867},[70,15772,1617],{"class":844},[70,15774,1536],{"class":848},[70,15776,9028],{"class":844},[70,15778,15779],{"class":867},"RistrettoFilter",[70,15781,1617],{"class":844},[70,15783,3960],{"class":844},[70,15785,14822],{"class":844},[70,15787,15788,15790,15792,15795,15797,15799,15802],{"class":564,"line":11331},[70,15789,15212],{"class":1587},[70,15791,928],{"class":844},[70,15793,15794],{"class":867},"Auth0 belongs to '",[70,15796,14690],{"class":844},[70,15798,15718],{"class":1004},[70,15800,15801],{"class":867},"'. Not touching another product's filter.",[70,15803,6591],{"class":844},[70,15805,15806],{"class":564,"line":11344},[70,15807,5666],{"class":844},[70,15809,15810],{"class":564,"line":11353},[70,15811,838],{"emptyLinePlaceholder":837},[70,15813,15814],{"class":564,"line":11358},[70,15815,15816],{"class":568},"# What the uninstaller will restore. Checked here, before anything changes.\n",[70,15818,15819],{"class":564,"line":11383},[70,15820,15821],{"class":568},"# An older version of this script wiped the config key on a second run (see\n",[70,15823,15824],{"class":564,"line":11434},[70,15825,15826],{"class":568},"# below) and then recorded its own name, so the uninstaller \"restored\"\n",[70,15828,15829],{"class":564,"line":11449},[70,15830,15831],{"class":568},"# Ristretto and Doppio's TacSubAuth never came back.\n",[70,15833,15834,15836,15839,15841,15843],{"class":564,"line":11456},[70,15835,14690],{"class":844},[70,15837,15838],{"class":1004},"prevRecorded",[70,15840,1349],{"class":848},[70,15842,14958],{"class":844},[70,15844,15845],{"class":1014},"null\n",[70,15847,15848,15850,15852,15854,15856,15858,15860],{"class":564,"line":11479},[70,15849,4386],{"class":1587},[70,15851,1591],{"class":844},[70,15853,15126],{"class":852},[70,15855,14958],{"class":844},[70,15857,15329],{"class":1004},[70,15859,1008],{"class":844},[70,15861,14822],{"class":844},[70,15863,15864,15866,15868,15870,15872,15874,15876,15878,15880,15883,15885,15887,15889],{"class":564,"line":11494},[70,15865,14871],{"class":844},[70,15867,15838],{"class":1004},[70,15869,1349],{"class":848},[70,15871,1591],{"class":844},[70,15873,15725],{"class":852},[70,15875,14958],{"class":844},[70,15877,15329],{"class":1004},[70,15879,2617],{"class":848},[70,15881,15882],{"class":5665},"Name PrevAuth0 ",[70,15884,11945],{"class":848},[70,15886,15739],{"class":5665},[70,15888,1008],{"class":844},[70,15890,15891],{"class":5665},".PrevAuth0\n",[70,15893,15894],{"class":564,"line":11501},[70,15895,5666],{"class":844},[70,15897,15898,15900,15902,15904,15907,15909,15911,15913,15915],{"class":564,"line":11512},[70,15899,4386],{"class":1587},[70,15901,15090],{"class":844},[70,15903,15838],{"class":1004},[70,15905,15906],{"class":848}," -eq",[70,15908,9028],{"class":844},[70,15910,15779],{"class":867},[70,15912,1617],{"class":844},[70,15914,1008],{"class":844},[70,15916,14822],{"class":844},[70,15918,15919,15921,15923,15926,15928,15930,15933,15935,15937,15940],{"class":564,"line":11517},[70,15920,15212],{"class":1587},[70,15922,928],{"class":844},[70,15924,15925],{"class":867},"PrevAuth0 under ",[70,15927,14690],{"class":844},[70,15929,15329],{"class":1004},[70,15931,15932],{"class":867}," is 'RistrettoFilter', left by an older version of this script. Set it to what Auth0 was before Ristretto ('TacSubAuth' for Doppio's filter, '",[70,15934,14690],{"class":844},[70,15936,14795],{"class":1004},[70,15938,15939],{"class":867},"' for none) and run this again.",[70,15941,6591],{"class":844},[70,15943,15944],{"class":564,"line":11528},[70,15945,5666],{"class":844},[70,15947,15948,15950,15952,15954,15956,15958,15960,15962,15964,15967,15969,15971,15973],{"class":564,"line":11533},[70,15949,4386],{"class":1587},[70,15951,15090],{"class":844},[70,15953,15718],{"class":1004},[70,15955,15906],{"class":848},[70,15957,9028],{"class":844},[70,15959,15779],{"class":867},[70,15961,1617],{"class":844},[70,15963,15755],{"class":1587},[70,15965,15966],{"class":1587}," -not",[70,15968,14958],{"class":844},[70,15970,15838],{"class":1004},[70,15972,1008],{"class":844},[70,15974,14822],{"class":844},[70,15976,15977,15979,15981,15984,15986,15988,15991,15993,15995,15998],{"class":564,"line":11549},[70,15978,15212],{"class":1587},[70,15980,928],{"class":844},[70,15982,15983],{"class":867},"Auth0 is already 'RistrettoFilter', but PrevAuth0 under ",[70,15985,14690],{"class":844},[70,15987,15329],{"class":1004},[70,15989,15990],{"class":867}," is missing, so there is no record of what to restore. Set PrevAuth0 ('TacSubAuth' or '",[70,15992,14690],{"class":844},[70,15994,14795],{"class":1004},[70,15996,15997],{"class":867},"') and run this again.",[70,15999,6591],{"class":844},[70,16001,16002],{"class":564,"line":11554},[70,16003,5666],{"class":844},[70,16005,16006],{"class":564,"line":11569},[70,16007,838],{"emptyLinePlaceholder":837},[70,16009,16010,16013],{"class":564,"line":11595},[70,16011,16012],{"class":852},"Write-Host",[70,16014,16015],{"class":844}," \"\"\n",[70,16017,16018,16020,16022,16025,16027,16029],{"class":564,"line":11630},[70,16019,16012],{"class":852},[70,16021,928],{"class":844},[70,16023,16024],{"class":867},"About to put an unsigned DLL in the lsass logon path on this machine.",[70,16026,934],{"class":844},[70,16028,2617],{"class":848},[70,16030,16031],{"class":5665},"ForegroundColor Yellow\n",[70,16033,16034,16036,16038,16041,16043,16045],{"class":564,"line":11657},[70,16035,16012],{"class":852},[70,16037,928],{"class":844},[70,16039,16040],{"class":867},"If it faults, this box may not boot cleanly. Snapshot taken?",[70,16042,934],{"class":844},[70,16044,2617],{"class":848},[70,16046,16031],{"class":5665},[70,16048,16049],{"class":564,"line":11677},[70,16050,838],{"emptyLinePlaceholder":837},[70,16052,16053,16055,16057,16060,16062],{"class":564,"line":11711},[70,16054,4386],{"class":1587},[70,16056,15090],{"class":844},[70,16058,16059],{"class":1004},"Confirmed",[70,16061,1008],{"class":844},[70,16063,14822],{"class":844},[70,16065,16066],{"class":564,"line":11720},[70,16067,16068],{"class":568},"    # The two warnings above are printed either way, so they are in the\n",[70,16070,16071],{"class":564,"line":11725},[70,16072,16073],{"class":568},"    # caller's log next to whatever it asked its own user.\n",[70,16075,16076,16079,16081,16084],{"class":564,"line":11730},[70,16077,16078],{"class":852},"    Write-Host",[70,16080,928],{"class":844},[70,16082,16083],{"class":867},"Confirmed by the caller (-Confirmed).",[70,16085,6591],{"class":844},[70,16087,16088,16091,16094],{"class":564,"line":11745},[70,16089,16090],{"class":844},"}",[70,16092,16093],{"class":1587}," else",[70,16095,14822],{"class":844},[70,16097,16098,16100],{"class":564,"line":11779},[70,16099,14988],{"class":1587},[70,16101,14822],{"class":844},[70,16103,16104,16107,16110,16112,16115,16117,16120],{"class":564,"line":11801},[70,16105,16106],{"class":844},"        $",[70,16108,16109],{"class":1004},"answer",[70,16111,1349],{"class":848},[70,16113,16114],{"class":852}," Read-Host",[70,16116,928],{"class":844},[70,16118,16119],{"class":867},"Type YES to continue",[70,16121,6591],{"class":844},[70,16123,16124,16127,16130],{"class":564,"line":11819},[70,16125,16126],{"class":844},"    }",[70,16128,16129],{"class":1587}," catch",[70,16131,14822],{"class":844},[70,16133,16134],{"class":564,"line":11828},[70,16135,16136],{"class":568},"        # -NonInteractive without -Confirmed. The raw error from Read-Host\n",[70,16138,16140],{"class":564,"line":16139},125,[70,16141,16142],{"class":568},"        # says nothing about how to get past it.\n",[70,16144,16146,16149,16151,16154],{"class":564,"line":16145},126,[70,16147,16148],{"class":1587},"        throw",[70,16150,928],{"class":844},[70,16152,16153],{"class":867},"No interactive console for the confirmation. Run this in a normal PowerShell window, or pass -Confirmed if the caller has already asked.",[70,16155,6591],{"class":844},[70,16157,16159],{"class":564,"line":16158},127,[70,16160,2405],{"class":844},[70,16162,16164,16166,16168,16170,16173,16175,16178,16180,16182,16184,16187,16189,16192,16194,16196,16198],{"class":564,"line":16163},128,[70,16165,5893],{"class":1587},[70,16167,15090],{"class":844},[70,16169,16109],{"class":1004},[70,16171,16172],{"class":848}," -cne",[70,16174,9028],{"class":844},[70,16176,16177],{"class":867},"YES",[70,16179,1617],{"class":844},[70,16181,1008],{"class":844},[70,16183,1821],{"class":844},[70,16185,16186],{"class":852}," Write-Host",[70,16188,928],{"class":844},[70,16190,16191],{"class":867},"Aborted. Nothing changed.",[70,16193,934],{"class":844},[70,16195,1402],{"class":844},[70,16197,1672],{"class":1587},[70,16199,2011],{"class":844},[70,16201,16203],{"class":564,"line":16202},129,[70,16204,5666],{"class":844},[70,16206,16208],{"class":564,"line":16207},130,[70,16209,838],{"emptyLinePlaceholder":837},[70,16211,16213],{"class":564,"line":16212},131,[70,16214,16215],{"class":568},"# --- config key, hardened before anything is written into it ---------------\n",[70,16217,16219],{"class":564,"line":16218},132,[70,16220,838],{"emptyLinePlaceholder":837},[70,16222,16224],{"class":564,"line":16223},133,[70,16225,16226],{"class":568},"# Create the keys only when they are missing. New-Item -Force on a registry\n",[70,16228,16230],{"class":564,"line":16229},134,[70,16231,16232],{"class":568},"# key that exists does not open it: the registry provider deletes the key with\n",[70,16234,16236],{"class":564,"line":16235},135,[70,16237,16238],{"class":568},"# all its values and subkeys and creates it again. That is how a second run\n",[70,16240,16242],{"class":564,"line":16241},136,[70,16243,16244],{"class":568},"# used to lose PrevAuth0, Mode, WindowSec and every open gate.\n",[70,16246,16248,16250,16252,16254,16256,16258,16260,16262,16264,16266,16269,16271,16273,16275,16278],{"class":564,"line":16247},137,[70,16249,4386],{"class":1587},[70,16251,1591],{"class":844},[70,16253,14955],{"class":1587},[70,16255,1591],{"class":844},[70,16257,15126],{"class":852},[70,16259,14958],{"class":844},[70,16261,15329],{"class":1004},[70,16263,3960],{"class":844},[70,16265,1821],{"class":844},[70,16267,16268],{"class":852}," New-Item",[70,16270,14958],{"class":844},[70,16272,15329],{"class":1004},[70,16274,7806],{"class":848},[70,16276,16277],{"class":852}," Out-Null",[70,16279,2011],{"class":844},[70,16281,16283,16286,16288,16291,16293],{"class":564,"line":16282},138,[70,16284,16285],{"class":852},"Set-KeyAclHard",[70,16287,2617],{"class":848},[70,16289,16290],{"class":5665},"SubKey ",[70,16292,14690],{"class":844},[70,16294,16295],{"class":1004},"cfgPathRel\n",[70,16297,16299,16301,16303,16305,16307,16309,16311,16313,16316,16318,16320,16322,16324,16326,16328,16330,16332,16334,16336],{"class":564,"line":16298},139,[70,16300,4386],{"class":1587},[70,16302,1591],{"class":844},[70,16304,14955],{"class":1587},[70,16306,1591],{"class":844},[70,16308,15126],{"class":852},[70,16310,15196],{"class":844},[70,16312,15329],{"class":1004},[70,16314,16315],{"class":867},"\\Gate",[70,16317,934],{"class":844},[70,16319,3960],{"class":844},[70,16321,1821],{"class":844},[70,16323,16268],{"class":852},[70,16325,15196],{"class":844},[70,16327,15329],{"class":1004},[70,16329,16315],{"class":867},[70,16331,934],{"class":844},[70,16333,7806],{"class":848},[70,16335,16277],{"class":852},[70,16337,2011],{"class":844},[70,16339,16341,16343,16345,16347,16350,16352,16354],{"class":564,"line":16340},140,[70,16342,16285],{"class":852},[70,16344,2617],{"class":848},[70,16346,16290],{"class":5665},[70,16348,16349],{"class":844},"\"$",[70,16351,15313],{"class":1004},[70,16353,16315],{"class":867},[70,16355,6591],{"class":844},[70,16357,16359],{"class":564,"line":16358},141,[70,16360,838],{"emptyLinePlaceholder":837},[70,16362,16364],{"class":564,"line":16363},142,[70,16365,16366],{"class":568},"# Remember what Auth0 was, including \"nothing\". The old code stored\n",[70,16368,16370],{"class":564,"line":16369},143,[70,16371,16372],{"class":568},"# 'TacSubAuth' when there was no Auth0 at all, so uninstalling invented a\n",[70,16374,16376],{"class":564,"line":16375},144,[70,16377,16378],{"class":568},"# filter that was never registered and pointed lsass at a DLL that does not\n",[70,16380,16382],{"class":564,"line":16381},145,[70,16383,16384],{"class":568},"# exist on this machine.\n",[70,16386,16388,16390,16392,16394,16396,16398,16400,16402,16404,16406,16408,16410,16412],{"class":564,"line":16387},146,[70,16389,4386],{"class":1587},[70,16391,1591],{"class":844},[70,16393,14955],{"class":1587},[70,16395,1591],{"class":844},[70,16397,15725],{"class":852},[70,16399,14958],{"class":844},[70,16401,15329],{"class":1004},[70,16403,2617],{"class":848},[70,16405,15882],{"class":5665},[70,16407,11945],{"class":848},[70,16409,15739],{"class":5665},[70,16411,3960],{"class":844},[70,16413,14822],{"class":844},[70,16415,16417,16419,16421,16423,16425,16427,16430,16432,16434,16436,16438,16440,16443,16445,16447],{"class":564,"line":16416},147,[70,16418,5893],{"class":1587},[70,16420,15090],{"class":844},[70,16422,15718],{"class":1004},[70,16424,1008],{"class":844},[70,16426,1821],{"class":844},[70,16428,16429],{"class":852}," Set-ItemProperty",[70,16431,14958],{"class":844},[70,16433,15329],{"class":1004},[70,16435,2617],{"class":848},[70,16437,15882],{"class":5665},[70,16439,11945],{"class":848},[70,16441,16442],{"class":5665},"Value ",[70,16444,14690],{"class":844},[70,16446,15718],{"class":1004},[70,16448,2011],{"class":844},[70,16450,16452,16455,16458,16460,16462,16464,16466,16468,16470,16472,16474,16476],{"class":564,"line":16451},148,[70,16453,16454],{"class":1587},"    else",[70,16456,16457],{"class":844},"      {",[70,16459,16429],{"class":852},[70,16461,14958],{"class":844},[70,16463,15329],{"class":1004},[70,16465,2617],{"class":848},[70,16467,15882],{"class":5665},[70,16469,11945],{"class":848},[70,16471,16442],{"class":5665},[70,16473,14690],{"class":844},[70,16475,14795],{"class":1004},[70,16477,2011],{"class":844},[70,16479,16481],{"class":564,"line":16480},149,[70,16482,5666],{"class":844},[70,16484,16486,16488,16490,16492,16494,16496,16498,16500,16502,16505,16507,16509,16511],{"class":564,"line":16485},150,[70,16487,4386],{"class":1587},[70,16489,1591],{"class":844},[70,16491,14955],{"class":1587},[70,16493,1591],{"class":844},[70,16495,15725],{"class":852},[70,16497,14958],{"class":844},[70,16499,15329],{"class":1004},[70,16501,2617],{"class":848},[70,16503,16504],{"class":5665},"Name Mode ",[70,16506,11945],{"class":848},[70,16508,15739],{"class":5665},[70,16510,3960],{"class":844},[70,16512,14822],{"class":844},[70,16514,16516,16519,16521,16523,16525,16527,16529,16531,16533,16535],{"class":564,"line":16515},151,[70,16517,16518],{"class":852},"    Set-ItemProperty",[70,16520,14958],{"class":844},[70,16522,15329],{"class":1004},[70,16524,2617],{"class":848},[70,16526,16504],{"class":5665},[70,16528,11945],{"class":848},[70,16530,16442],{"class":5665},[70,16532,3852],{"class":1014},[70,16534,2617],{"class":848},[70,16536,16537],{"class":5665},"Type DWord\n",[70,16539,16541],{"class":564,"line":16540},152,[70,16542,5666],{"class":844},[70,16544,16546,16548,16550,16552,16554,16556,16558,16560,16562,16565,16567,16569,16571],{"class":564,"line":16545},153,[70,16547,4386],{"class":1587},[70,16549,1591],{"class":844},[70,16551,14955],{"class":1587},[70,16553,1591],{"class":844},[70,16555,15725],{"class":852},[70,16557,14958],{"class":844},[70,16559,15329],{"class":1004},[70,16561,2617],{"class":848},[70,16563,16564],{"class":5665},"Name WindowSec ",[70,16566,11945],{"class":848},[70,16568,15739],{"class":5665},[70,16570,3960],{"class":844},[70,16572,14822],{"class":844},[70,16574,16576,16578,16580,16582,16584,16586,16588,16590,16592,16594,16597],{"class":564,"line":16575},154,[70,16577,16518],{"class":852},[70,16579,14958],{"class":844},[70,16581,15329],{"class":1004},[70,16583,2617],{"class":848},[70,16585,16564],{"class":5665},[70,16587,11945],{"class":848},[70,16589,16442],{"class":5665},[70,16591,297],{"class":1014},[70,16593,2617],{"class":848},[70,16595,16596],{"class":5665},"Type DWord   ",[70,16598,16599],{"class":568},"# 5 min; filter clamps 30-3600\n",[70,16601,16603],{"class":564,"line":16602},155,[70,16604,5666],{"class":844},[70,16606,16608],{"class":564,"line":16607},156,[70,16609,838],{"emptyLinePlaceholder":837},[70,16611,16613],{"class":564,"line":16612},157,[70,16614,16615],{"class":568},"# --- event source ----------------------------------------------------------\n",[70,16617,16619],{"class":564,"line":16618},158,[70,16620,838],{"emptyLinePlaceholder":837},[70,16622,16624,16626,16629,16631,16633,16636],{"class":564,"line":16623},159,[70,16625,14690],{"class":844},[70,16627,16628],{"class":1004},"src",[70,16630,1349],{"class":848},[70,16632,9028],{"class":844},[70,16634,16635],{"class":867},"HKLM:\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\TheAdminCafe Ristretto",[70,16637,14703],{"class":844},[70,16639,16641,16643,16645,16647,16649,16651,16653,16655,16657,16659,16661,16663,16665,16667,16669],{"class":564,"line":16640},160,[70,16642,4386],{"class":1587},[70,16644,1591],{"class":844},[70,16646,14955],{"class":1587},[70,16648,1591],{"class":844},[70,16650,15126],{"class":852},[70,16652,14958],{"class":844},[70,16654,16628],{"class":1004},[70,16656,3960],{"class":844},[70,16658,1821],{"class":844},[70,16660,16268],{"class":852},[70,16662,14958],{"class":844},[70,16664,16628],{"class":1004},[70,16666,7806],{"class":848},[70,16668,16277],{"class":852},[70,16670,2011],{"class":844},[70,16672,16674,16677,16679,16681,16683,16686,16688,16690,16692,16695,16697,16699],{"class":564,"line":16673},161,[70,16675,16676],{"class":852},"Set-ItemProperty",[70,16678,14958],{"class":844},[70,16680,16628],{"class":1004},[70,16682,2617],{"class":848},[70,16684,16685],{"class":5665},"Name EventMessageFile ",[70,16687,11945],{"class":848},[70,16689,16442],{"class":5665},[70,16691,1617],{"class":844},[70,16693,16694],{"class":867},"%SystemRoot%\\System32\\EventCreate.exe",[70,16696,1617],{"class":844},[70,16698,2617],{"class":848},[70,16700,16701],{"class":5665},"Type ExpandString\n",[70,16703,16705,16707,16709,16711,16713,16716,16718,16720,16723,16725],{"class":564,"line":16704},162,[70,16706,16676],{"class":852},[70,16708,14958],{"class":844},[70,16710,16628],{"class":1004},[70,16712,2617],{"class":848},[70,16714,16715],{"class":5665},"Name TypesSupported ",[70,16717,11945],{"class":848},[70,16719,16442],{"class":5665},[70,16721,16722],{"class":1014},"7",[70,16724,2617],{"class":848},[70,16726,16537],{"class":5665},[70,16728,16730],{"class":564,"line":16729},163,[70,16731,838],{"emptyLinePlaceholder":837},[70,16733,16735],{"class":564,"line":16734},164,[70,16736,16737],{"class":568},"# --- payload, then the registry switch -------------------------------------\n",[70,16739,16741],{"class":564,"line":16740},165,[70,16742,16743],{"class":568},"# Order matters: the DLL has to be on disk before Auth0 names it, or the\n",[70,16745,16747],{"class":564,"line":16746},166,[70,16748,16749],{"class":568},"# next boot looks for a filter that is not there.\n",[70,16751,16753],{"class":564,"line":16752},167,[70,16754,838],{"emptyLinePlaceholder":837},[70,16756,16758,16761,16763,16765,16767,16770,16773,16776,16778,16780],{"class":564,"line":16757},168,[70,16759,16760],{"class":852},"Copy-Item",[70,16762,14958],{"class":844},[70,16764,15045],{"class":1004},[70,16766,15196],{"class":844},[70,16768,16769],{"class":1014},"env:",[70,16771,16772],{"class":1004},"SystemRoot",[70,16774,16775],{"class":867},"\\System32\\RistrettoFilter.dll",[70,16777,934],{"class":844},[70,16779,2617],{"class":848},[70,16781,16782],{"class":5665},"Force\n",[70,16784,16786],{"class":564,"line":16785},169,[70,16787,838],{"emptyLinePlaceholder":837},[70,16789,16791],{"class":564,"line":16790},170,[70,16792,16793],{"class":568},"# Must sit next to TacProvider.dll: shared state.lock (see README).\n",[70,16795,16797,16799,16801,16803,16805,16808,16810,16812],{"class":564,"line":16796},171,[70,16798,16760],{"class":852},[70,16800,14958],{"class":844},[70,16802,15068],{"class":1004},[70,16804,9028],{"class":844},[70,16806,16807],{"class":867},"C:\\Program Files\\TheAdminCafe\\ristretto.exe",[70,16809,1617],{"class":844},[70,16811,2617],{"class":848},[70,16813,16782],{"class":5665},[70,16815,16817],{"class":564,"line":16816},172,[70,16818,838],{"emptyLinePlaceholder":837},[70,16820,16822,16824,16826,16828,16830,16832,16834,16836,16838,16840],{"class":564,"line":16821},173,[70,16823,16676],{"class":852},[70,16825,14958],{"class":844},[70,16827,15295],{"class":1004},[70,16829,2617],{"class":848},[70,16831,15734],{"class":5665},[70,16833,11945],{"class":848},[70,16835,16442],{"class":5665},[70,16837,1617],{"class":844},[70,16839,15779],{"class":867},[70,16841,14703],{"class":844},[70,16843,16845],{"class":564,"line":16844},174,[70,16846,838],{"emptyLinePlaceholder":837},[70,16848,16850,16852,16855,16857,16860,16862,16864,16866,16868,16870,16872,16874,16876,16878,16880,16883,16885],{"class":564,"line":16849},175,[70,16851,14690],{"class":844},[70,16853,16854],{"class":1004},"shown",[70,16856,1349],{"class":848},[70,16858,16859],{"class":1587}," if",[70,16861,15090],{"class":844},[70,16863,15718],{"class":1004},[70,16865,1008],{"class":844},[70,16867,1821],{"class":844},[70,16869,14958],{"class":844},[70,16871,15718],{"class":1004},[70,16873,1881],{"class":844},[70,16875,16093],{"class":1587},[70,16877,1821],{"class":844},[70,16879,9028],{"class":844},[70,16881,16882],{"class":867},"(none)",[70,16884,1617],{"class":844},[70,16886,2011],{"class":844},[70,16888,16890,16892],{"class":564,"line":16889},176,[70,16891,16012],{"class":852},[70,16893,16015],{"class":844},[70,16895,16897,16899,16901,16904,16906,16908,16911],{"class":564,"line":16896},177,[70,16898,16012],{"class":852},[70,16900,928],{"class":844},[70,16902,16903],{"class":867},"Auth0: '",[70,16905,14690],{"class":844},[70,16907,16854],{"class":1004},[70,16909,16910],{"class":867},"' -> 'RistrettoFilter' (saved for restore). Reboot to load.",[70,16912,6591],{"class":844},[70,16914,16916,16918,16920,16923],{"class":564,"line":16915},178,[70,16917,16012],{"class":852},[70,16919,928],{"class":844},[70,16921,16922],{"class":867},"Default window is 300 s. Open a gate with: ristretto \u003Cuser>",[70,16924,6591],{"class":844},[17,16926,16927],{},"In order, the script:",[187,16929,16930,16955,16985,16998,17019,17026],{},[108,16931,16932,16933,16936,16937,16939,16940,438,16942,16945,16946,16948,16949,16951,16952,16954],{},"It checks everything before it touches anything: both build outputs next to the script (",[41,16934,16935],{},"$PSScriptRoot",", not the current directory), Doppio's RID index, ",[41,16938,9974],{},", that the machine isn't a domain controller, that LSA protection is off (",[41,16941,78],{},[41,16943,16944],{},"RunAsPPLBoot",", and WinInit event 12 since the last boot), and that ",[41,16947,82],{}," belongs to ",[41,16950,86],{},", to Ristretto, or to nobody. It won't take the slot from another product's filter. My first version only looked at ",[41,16953,78],{},". With LSA protection on through any other route, the install looked fine and the filter never loaded.",[108,16956,16957,16958,16960,16961,16964,16965,16967,16968,2072,16971,16976,16977,16980,16981,16984],{},"It asks. You type ",[41,16959,16177],{},", in capitals. ",[41,16962,16963],{},"-cne"," is a case-sensitive compare, so ",[41,16966,494],{}," aborts. You're about to put an unsigned DLL into the logon path, so it wants a deliberate answer. The one exception is ",[41,16969,16970],{},"-Confirmed",[21,16972,16975],{"href":16973,"rel":16974},"https:\u002F\u002Fgithub.com\u002FNobrac\u002FBarista",[57],"Barista",", my installer for Doppio and Ristretto, asks its own user first and then runs this script with ",[41,16978,16979],{},"powershell.exe -NonInteractive",". In that mode ",[41,16982,16983],{},"Read-Host"," can't prompt and throws, so Barista passes the switch. The two warnings print either way and end up in Barista's log. Without a console and without the switch, the script stops with an error that tells you which of the two you need. Run it by hand and leave the switch off.",[108,16986,16987,16988,16991,16992,16994,16995,16997],{},"It creates the config keys if they're missing and hardens them before any value goes in. \"If they're missing\" matters: ",[41,16989,16990],{},"New-Item -Force"," on a registry key that exists doesn't open it. It deletes the key with everything in it and creates it again. My first version did exactly that. ",[41,16993,16285],{}," applies the same SDDL as the exe. A kill switch shouldn't depend on ",[41,16996,7008],{}," defaults staying what you remember.",[108,16999,17000,17001,17003,17004,17006,17007,17009,17010,17012,17013,17015,17016,17018],{},"It records what ",[41,17002,82],{}," was, including \"nothing\" as the ",[41,17005,14802],{}," sentinel. It does that only if there isn't a record yet, so a second run keeps the first record. If ",[41,17008,82],{}," already says ",[41,17011,15779],{}," and the record is gone, or the record itself says ",[41,17014,15779],{},", the script stops in step 1 and tells you how to fix it. My first version lost the record on a second run, see step 3, and then wrote down ",[41,17017,15779],{}," as the filter to restore. The uninstaller \"restored\" Ristretto, and Doppio's filter never came back.",[108,17020,17021,17022,17025],{},"It registers the event source. ",[41,17023,17024],{},"EventCreate.exe"," as message file is a trick: it has a message table that prints the string you pass, so the events are readable in the Event Viewer without a message DLL of our own.",[108,17027,17028,17029,17031,17032,17034],{},"It copies the DLL into System32 and the exe next to ",[41,17030,9974],{},", and sets ",[41,17033,82],{}," last. If the copy fails, lsass loads the old filter on the next boot.",[17,17036,17037],{},"Reboot to load it.",[250,17039,17041],{"id":17040},"using-it","Using it",[258,17043,17046],{"className":17044,"code":17045,"language":263,"meta":264},[261],"ristretto \u003Cuser> [code]     open the network gate (TOTP check)\nristretto \u002Fstatus \u003Cuser>    show gate state and time remaining\nristretto \u002Fclose \u003Cuser>     close the gate now\nristretto \u002Fcloseall         close every open gate\nristretto \u002Fwindow \u003Csec>     gate lifetime, 30-3600 (default 300)\nristretto \u002Fmode gate|off    enforcement kill switch, no reboot\n",[41,17047,17045],{"__ignoreMap":264},[17,17049,17050],{},"A session looks like this. On the protected machine, elevated:",[258,17052,17055],{"className":17053,"code":17054,"language":263,"meta":264},[261],"C:\\> \"C:\\Program Files\\TheAdminCafe\\ristretto.exe\" alice\nOne-time code: 482913\nGate open for 'alice' for 300 s.\nAnyone who can authenticate as this account passes while the window lasts -\nclose it with \u002Fclose when you are done. Closing stops new logons only:\na session that is already up stays up until it disconnects.\n",[41,17056,17054],{"__ignoreMap":264},[17,17058,17059,17060,17063,17064,17067,17068,17070],{},"Then from the second machine ",[41,17061,17062],{},"net use \\\\host\\share \u002Fuser:host\\alice"," connects for the next five minutes. The mapped share stays up after that, until it disconnects. ",[41,17065,17066],{},"ristretto \u002Fstatus alice"," tells you how much of the window is left, and ",[41,17069,215],{}," ends the window when you're done. Pass the code as an argument in scripts and nowhere else.",[250,17072,17074],{"id":17073},"the-first-thing-to-check","The first thing to check",[17,17076,17077,17078,17080,17081,1221,17083,17085],{},"Same as in Part 2: attach the kernel debugger and watch for ",[41,17079,4861],{}," lines on a network logon. Open a share from a second machine with the gate closed. You should see a \"Refused a network logon\" line and a logon error on the client, plus a failed 4625 in the Security log of the protected machine. Write down what it shows in Status and Sub Status. I expect ",[41,17082,363],{},[41,17084,359],{},". Open the gate and try again.",[17,17087,17088,17089,17092,17093,17096],{},"No lines at all has two possible reasons. Either lsass never loaded the DLL, or MSV1_0 doesn't call the filter on your build. Rule out the first one before you believe the second: in the System log, ",[41,17090,17091],{},"Wininit"," event 12 means lsass runs as a protected process, and under ",[41,17094,17095],{},"Microsoft\\Windows\\CodeIntegrity\\Operational"," an event 3033 or 3063 names the DLL that lsass refused. If neither is there and you still see no lines, the filter is loaded and never asked, and the gate does nothing.",[12,17098,17100],{"id":17099},"uninstall","Uninstall",[258,17102,17104],{"className":14611,"code":17103,"language":14613,"meta":264,"style":264},".\\uninstall-ristretto.ps1\n",[41,17105,17106],{"__ignoreMap":264},[70,17107,17108,17110],{"class":564,"line":565},[70,17109,25],{"class":5665},[70,17111,17112],{"class":852},"\\uninstall-ristretto.ps1\n",[258,17114,17117],{"className":14611,"code":17115,"filename":17116,"language":14613,"meta":264,"style":264},"#requires -RunAsAdministrator\n$ErrorActionPreference = 'Stop'\n\n$msvKey = 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0'\n$cfg    = 'HKLM:\\SOFTWARE\\TheAdminCafe\\Ristretto'\n$src    = 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\TheAdminCafe Ristretto'\n$noPrev = '__none__'\n\n$cur  = (Get-ItemProperty $msvKey -Name Auth0 -ErrorAction SilentlyContinue).Auth0\n$prev = (Get-ItemProperty $cfg -Name PrevAuth0 -ErrorAction SilentlyContinue).PrevAuth0\n\n# An older install-ristretto.ps1 recorded its own name when it ran twice.\n# Restoring that would leave Ristretto registered, so stop before anything\n# changes and say how to fix the record.\nif ($prev -eq 'RistrettoFilter') {\n    throw \"PrevAuth0 under $cfg is 'RistrettoFilter', left by an older install-ristretto.ps1. Set it to what Auth0 was before Ristretto ('TacSubAuth' for Doppio's filter, '$noPrev' for none) and run this again. Nothing was changed.\"\n}\n\n# Restore Auth0 first: if anything below fails, the machine is already back\n# on its original filter rather than half-uninstalled. Only touch it while it\n# is still ours: if somebody gave the slot to another filter after the\n# install, overwriting it with the old value would undo their change.\nif ($cur -ne 'RistrettoFilter') {\n    $shown = if ($cur) { \"'$cur'\" } else { '(none)' }\n    Write-Warning \"Auth0 is $shown, not 'RistrettoFilter'. Left as it is.\"\n    $restored = \"$shown (unchanged)\"\n} elseif ($prev -and $prev -ne $noPrev) {\n    Set-ItemProperty $msvKey -Name Auth0 -Value $prev\n    $restored = \"'$prev'\"\n} else {\n    # Either we recorded \"there was nothing here\", or the marker is gone.\n    # Removing the value is the safe answer - a dangling Auth0 pointing at a\n    # DLL that is not on disk is worse than no Auth0 at all.\n    Remove-ItemProperty $msvKey -Name Auth0 -ErrorAction SilentlyContinue\n    $restored = '(none, value removed)'\n}\n\n# Drop any open gates so a later reinstall cannot find a stale timestamp\n# still inside its window.\nRemove-Item \"$cfg\\Gate\" -Recurse -Force -ErrorAction SilentlyContinue\n\n# Clear the restore marker so a reinstall records the real previous state\n# instead of reusing ours.\nRemove-ItemProperty $cfg -Name PrevAuth0 -ErrorAction SilentlyContinue\n\nRemove-Item $src -Recurse -Force -ErrorAction SilentlyContinue\nRemove-Item 'C:\\Program Files\\TheAdminCafe\\ristretto.exe' -Force -ErrorAction SilentlyContinue\n\nWrite-Host \"Auth0: $restored. Open gates cleared.\"\nWrite-Host \"Reboot, THEN delete $env:SystemRoot\\System32\\RistrettoFilter.dll\"\nWrite-Host \"(lsass holds the DLL until the machine restarts).\"\n","uninstall-ristretto.ps1",[41,17118,17119,17125,17139,17143,17158,17173,17187,17201,17205,17233,17262,17266,17271,17276,17281,17301,17325,17329,17333,17338,17343,17348,17353,17374,17418,17437,17455,17482,17503,17523,17531,17536,17541,17546,17564,17579,17583,17587,17592,17597,17624,17628,17633,17638,17655,17659,17679,17697,17701,17719,17738],{"__ignoreMap":264},[70,17120,17121,17123],{"class":564,"line":565},[70,17122,845],{"class":568},[70,17124,14635],{"class":14019},[70,17126,17127,17129,17131,17133,17135,17137],{"class":564,"line":572},[70,17128,14690],{"class":844},[70,17130,14693],{"class":1004},[70,17132,1349],{"class":848},[70,17134,9028],{"class":844},[70,17136,14700],{"class":867},[70,17138,14703],{"class":844},[70,17140,17141],{"class":564,"line":578},[70,17142,838],{"emptyLinePlaceholder":837},[70,17144,17145,17147,17149,17151,17153,17156],{"class":564,"line":584},[70,17146,14690],{"class":844},[70,17148,15295],{"class":1004},[70,17150,1349],{"class":848},[70,17152,9028],{"class":844},[70,17154,17155],{"class":867},"HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Lsa\\MSV1_0",[70,17157,14703],{"class":844},[70,17159,17160,17162,17164,17166,17168,17171],{"class":564,"line":590},[70,17161,14690],{"class":844},[70,17163,15329],{"class":1004},[70,17165,6924],{"class":848},[70,17167,9028],{"class":844},[70,17169,17170],{"class":867},"HKLM:\\SOFTWARE\\TheAdminCafe\\Ristretto",[70,17172,14703],{"class":844},[70,17174,17175,17177,17179,17181,17183,17185],{"class":564,"line":596},[70,17176,14690],{"class":844},[70,17178,16628],{"class":1004},[70,17180,6924],{"class":848},[70,17182,9028],{"class":844},[70,17184,16635],{"class":867},[70,17186,14703],{"class":844},[70,17188,17189,17191,17193,17195,17197,17199],{"class":564,"line":602},[70,17190,14690],{"class":844},[70,17192,14795],{"class":1004},[70,17194,1349],{"class":848},[70,17196,9028],{"class":844},[70,17198,14802],{"class":867},[70,17200,14703],{"class":844},[70,17202,17203],{"class":564,"line":607},[70,17204,838],{"emptyLinePlaceholder":837},[70,17206,17207,17209,17211,17213,17215,17217,17219,17221,17223,17225,17227,17229,17231],{"class":564,"line":613},[70,17208,14690],{"class":844},[70,17210,15718],{"class":1004},[70,17212,2519],{"class":848},[70,17214,1591],{"class":844},[70,17216,15725],{"class":852},[70,17218,14958],{"class":844},[70,17220,15295],{"class":1004},[70,17222,2617],{"class":848},[70,17224,15734],{"class":5665},[70,17226,11945],{"class":848},[70,17228,15739],{"class":5665},[70,17230,1008],{"class":844},[70,17232,15744],{"class":5665},[70,17234,17235,17237,17240,17242,17244,17246,17248,17250,17252,17254,17256,17258,17260],{"class":564,"line":619},[70,17236,14690],{"class":844},[70,17238,17239],{"class":1004},"prev",[70,17241,1349],{"class":848},[70,17243,1591],{"class":844},[70,17245,15725],{"class":852},[70,17247,14958],{"class":844},[70,17249,15329],{"class":1004},[70,17251,2617],{"class":848},[70,17253,15882],{"class":5665},[70,17255,11945],{"class":848},[70,17257,15739],{"class":5665},[70,17259,1008],{"class":844},[70,17261,15891],{"class":5665},[70,17263,17264],{"class":564,"line":625},[70,17265,838],{"emptyLinePlaceholder":837},[70,17267,17268],{"class":564,"line":631},[70,17269,17270],{"class":568},"# An older install-ristretto.ps1 recorded its own name when it ran twice.\n",[70,17272,17273],{"class":564,"line":636},[70,17274,17275],{"class":568},"# Restoring that would leave Ristretto registered, so stop before anything\n",[70,17277,17278],{"class":564,"line":642},[70,17279,17280],{"class":568},"# changes and say how to fix the record.\n",[70,17282,17283,17285,17287,17289,17291,17293,17295,17297,17299],{"class":564,"line":648},[70,17284,4386],{"class":1587},[70,17286,15090],{"class":844},[70,17288,17239],{"class":1004},[70,17290,15906],{"class":848},[70,17292,9028],{"class":844},[70,17294,15779],{"class":867},[70,17296,1617],{"class":844},[70,17298,1008],{"class":844},[70,17300,14822],{"class":844},[70,17302,17303,17305,17307,17309,17311,17313,17316,17318,17320,17323],{"class":564,"line":654},[70,17304,15212],{"class":1587},[70,17306,928],{"class":844},[70,17308,15925],{"class":867},[70,17310,14690],{"class":844},[70,17312,15329],{"class":1004},[70,17314,17315],{"class":867}," is 'RistrettoFilter', left by an older install-ristretto.ps1. Set it to what Auth0 was before Ristretto ('TacSubAuth' for Doppio's filter, '",[70,17317,14690],{"class":844},[70,17319,14795],{"class":1004},[70,17321,17322],{"class":867},"' for none) and run this again. Nothing was changed.",[70,17324,6591],{"class":844},[70,17326,17327],{"class":564,"line":659},[70,17328,5666],{"class":844},[70,17330,17331],{"class":564,"line":665},[70,17332,838],{"emptyLinePlaceholder":837},[70,17334,17335],{"class":564,"line":671},[70,17336,17337],{"class":568},"# Restore Auth0 first: if anything below fails, the machine is already back\n",[70,17339,17340],{"class":564,"line":677},[70,17341,17342],{"class":568},"# on its original filter rather than half-uninstalled. Only touch it while it\n",[70,17344,17345],{"class":564,"line":683},[70,17346,17347],{"class":568},"# is still ours: if somebody gave the slot to another filter after the\n",[70,17349,17350],{"class":564,"line":689},[70,17351,17352],{"class":568},"# install, overwriting it with the old value would undo their change.\n",[70,17354,17355,17357,17359,17361,17364,17366,17368,17370,17372],{"class":564,"line":695},[70,17356,4386],{"class":1587},[70,17358,15090],{"class":844},[70,17360,15718],{"class":1004},[70,17362,17363],{"class":848}," -ne",[70,17365,9028],{"class":844},[70,17367,15779],{"class":867},[70,17369,1617],{"class":844},[70,17371,1008],{"class":844},[70,17373,14822],{"class":844},[70,17375,17376,17378,17380,17382,17384,17386,17388,17390,17392,17394,17396,17398,17400,17402,17404,17406,17408,17410,17412,17414,17416],{"class":564,"line":701},[70,17377,14871],{"class":844},[70,17379,16854],{"class":1004},[70,17381,1349],{"class":848},[70,17383,16859],{"class":1587},[70,17385,15090],{"class":844},[70,17387,15718],{"class":1004},[70,17389,1008],{"class":844},[70,17391,1821],{"class":844},[70,17393,928],{"class":844},[70,17395,1617],{"class":867},[70,17397,14690],{"class":844},[70,17399,15718],{"class":1004},[70,17401,1617],{"class":867},[70,17403,934],{"class":844},[70,17405,1881],{"class":844},[70,17407,16093],{"class":1587},[70,17409,1821],{"class":844},[70,17411,9028],{"class":844},[70,17413,16882],{"class":867},[70,17415,1617],{"class":844},[70,17417,2011],{"class":844},[70,17419,17420,17423,17425,17428,17430,17432,17435],{"class":564,"line":706},[70,17421,17422],{"class":852},"    Write-Warning",[70,17424,928],{"class":844},[70,17426,17427],{"class":867},"Auth0 is ",[70,17429,14690],{"class":844},[70,17431,16854],{"class":1004},[70,17433,17434],{"class":867},", not 'RistrettoFilter'. Left as it is.",[70,17436,6591],{"class":844},[70,17438,17439,17441,17444,17446,17448,17450,17453],{"class":564,"line":712},[70,17440,14871],{"class":844},[70,17442,17443],{"class":1004},"restored",[70,17445,1349],{"class":848},[70,17447,15196],{"class":844},[70,17449,16854],{"class":1004},[70,17451,17452],{"class":867}," (unchanged)",[70,17454,6591],{"class":844},[70,17456,17457,17459,17462,17464,17466,17468,17470,17472,17474,17476,17478,17480],{"class":564,"line":718},[70,17458,16090],{"class":844},[70,17460,17461],{"class":1587}," elseif",[70,17463,15090],{"class":844},[70,17465,17239],{"class":1004},[70,17467,15755],{"class":1587},[70,17469,14958],{"class":844},[70,17471,17239],{"class":1004},[70,17473,17363],{"class":848},[70,17475,14958],{"class":844},[70,17477,14795],{"class":1004},[70,17479,1008],{"class":844},[70,17481,14822],{"class":844},[70,17483,17484,17486,17488,17490,17492,17494,17496,17498,17500],{"class":564,"line":723},[70,17485,16518],{"class":852},[70,17487,14958],{"class":844},[70,17489,15295],{"class":1004},[70,17491,2617],{"class":848},[70,17493,15734],{"class":5665},[70,17495,11945],{"class":848},[70,17497,16442],{"class":5665},[70,17499,14690],{"class":844},[70,17501,17502],{"class":1004},"prev\n",[70,17504,17505,17507,17509,17511,17513,17515,17517,17519,17521],{"class":564,"line":729},[70,17506,14871],{"class":844},[70,17508,17443],{"class":1004},[70,17510,1349],{"class":848},[70,17512,928],{"class":844},[70,17514,1617],{"class":867},[70,17516,14690],{"class":844},[70,17518,17239],{"class":1004},[70,17520,1617],{"class":867},[70,17522,6591],{"class":844},[70,17524,17525,17527,17529],{"class":564,"line":735},[70,17526,16090],{"class":844},[70,17528,16093],{"class":1587},[70,17530,14822],{"class":844},[70,17532,17533],{"class":564,"line":741},[70,17534,17535],{"class":568},"    # Either we recorded \"there was nothing here\", or the marker is gone.\n",[70,17537,17538],{"class":564,"line":746},[70,17539,17540],{"class":568},"    # Removing the value is the safe answer - a dangling Auth0 pointing at a\n",[70,17542,17543],{"class":564,"line":752},[70,17544,17545],{"class":568},"    # DLL that is not on disk is worse than no Auth0 at all.\n",[70,17547,17548,17551,17553,17555,17557,17559,17561],{"class":564,"line":758},[70,17549,17550],{"class":852},"    Remove-ItemProperty",[70,17552,14958],{"class":844},[70,17554,15295],{"class":1004},[70,17556,2617],{"class":848},[70,17558,15734],{"class":5665},[70,17560,11945],{"class":848},[70,17562,17563],{"class":5665},"ErrorAction SilentlyContinue\n",[70,17565,17566,17568,17570,17572,17574,17577],{"class":564,"line":764},[70,17567,14871],{"class":844},[70,17569,17443],{"class":1004},[70,17571,1349],{"class":848},[70,17573,9028],{"class":844},[70,17575,17576],{"class":867},"(none, value removed)",[70,17578,14703],{"class":844},[70,17580,17581],{"class":564,"line":769},[70,17582,5666],{"class":844},[70,17584,17585],{"class":564,"line":775},[70,17586,838],{"emptyLinePlaceholder":837},[70,17588,17589],{"class":564,"line":781},[70,17590,17591],{"class":568},"# Drop any open gates so a later reinstall cannot find a stale timestamp\n",[70,17593,17594],{"class":564,"line":787},[70,17595,17596],{"class":568},"# still inside its window.\n",[70,17598,17599,17602,17604,17606,17608,17610,17612,17615,17617,17620,17622],{"class":564,"line":793},[70,17600,17601],{"class":852},"Remove-Item",[70,17603,15196],{"class":844},[70,17605,15329],{"class":1004},[70,17607,16315],{"class":867},[70,17609,934],{"class":844},[70,17611,2617],{"class":848},[70,17613,17614],{"class":5665},"Recurse ",[70,17616,11945],{"class":848},[70,17618,17619],{"class":5665},"Force ",[70,17621,11945],{"class":848},[70,17623,17563],{"class":5665},[70,17625,17626],{"class":564,"line":799},[70,17627,838],{"emptyLinePlaceholder":837},[70,17629,17630],{"class":564,"line":805},[70,17631,17632],{"class":568},"# Clear the restore marker so a reinstall records the real previous state\n",[70,17634,17635],{"class":564,"line":811},[70,17636,17637],{"class":568},"# instead of reusing ours.\n",[70,17639,17640,17643,17645,17647,17649,17651,17653],{"class":564,"line":817},[70,17641,17642],{"class":852},"Remove-ItemProperty",[70,17644,14958],{"class":844},[70,17646,15329],{"class":1004},[70,17648,2617],{"class":848},[70,17650,15882],{"class":5665},[70,17652,11945],{"class":848},[70,17654,17563],{"class":5665},[70,17656,17657],{"class":564,"line":823},[70,17658,838],{"emptyLinePlaceholder":837},[70,17660,17661,17663,17665,17667,17669,17671,17673,17675,17677],{"class":564,"line":829},[70,17662,17601],{"class":852},[70,17664,14958],{"class":844},[70,17666,16628],{"class":1004},[70,17668,2617],{"class":848},[70,17670,17614],{"class":5665},[70,17672,11945],{"class":848},[70,17674,17619],{"class":5665},[70,17676,11945],{"class":848},[70,17678,17563],{"class":5665},[70,17680,17681,17683,17685,17687,17689,17691,17693,17695],{"class":564,"line":834},[70,17682,17601],{"class":852},[70,17684,9028],{"class":844},[70,17686,16807],{"class":867},[70,17688,1617],{"class":844},[70,17690,2617],{"class":848},[70,17692,17619],{"class":5665},[70,17694,11945],{"class":848},[70,17696,17563],{"class":5665},[70,17698,17699],{"class":564,"line":841},[70,17700,838],{"emptyLinePlaceholder":837},[70,17702,17703,17705,17707,17710,17712,17714,17717],{"class":564,"line":856},[70,17704,16012],{"class":852},[70,17706,928],{"class":844},[70,17708,17709],{"class":867},"Auth0: ",[70,17711,14690],{"class":844},[70,17713,17443],{"class":1004},[70,17715,17716],{"class":867},". Open gates cleared.",[70,17718,6591],{"class":844},[70,17720,17721,17723,17725,17728,17730,17732,17734,17736],{"class":564,"line":874},[70,17722,16012],{"class":852},[70,17724,928],{"class":844},[70,17726,17727],{"class":867},"Reboot, THEN delete ",[70,17729,14690],{"class":844},[70,17731,16769],{"class":1014},[70,17733,16772],{"class":1004},[70,17735,16775],{"class":867},[70,17737,6591],{"class":844},[70,17739,17740,17742,17744,17747],{"class":564,"line":888},[70,17741,16012],{"class":852},[70,17743,928],{"class":844},[70,17745,17746],{"class":867},"(lsass holds the DLL until the machine restarts).",[70,17748,6591],{"class":844},[17,17750,17751,17753,17754,17756,17757,17759,17760,17762,17763,17765,17766,17768],{},[41,17752,82],{}," comes first. If anything after it fails, the machine is already back on its original filter (",[41,17755,86],{},", or none at all) instead of half uninstalled. A recorded ",[41,17758,14802],{}," removes the value instead of inventing a filter. The script only touches ",[41,17761,82],{}," while it still says ",[41,17764,15779],{},". If somebody gave the slot to another filter since, it warns and leaves it alone. If the record itself says ",[41,17767,15779],{},", which an older installer wrote on a second run, the script stops before it changes anything. Then the gates go, so a later reinstall can't find a timestamp inside its window, and the restore marker goes, so a reinstall records the real previous state.",[17,17770,17771,17772,17774],{},"Then reboot, and only then delete ",[41,17773,388],{}," from System32. lsass holds the DLL until the machine restarts.",[12,17776,17778],{"id":17777},"if-lsa-doesnt-start","If LSA doesn't start",[17,17780,17781],{},"The rescue works as in Part 2. Restore the snapshot. If you have none, boot WinRE, load the offline hive and remove the registration by hand:",[258,17783,17786],{"className":17784,"code":17785,"language":263,"meta":264},[261],"reg load HKLM\\OFF C:\\Windows\\System32\\config\\SYSTEM\nreg query HKLM\\OFF\\Select \u002Fv Current\nreg query HKLM\\OFF\\ControlSet001\\Control\\Lsa\\MSV1_0 \u002Fv Auth0\nreg delete HKLM\\OFF\\ControlSet001\\Control\\Lsa\\MSV1_0 \u002Fv Auth0\nreg unload HKLM\\OFF\n",[41,17787,17785],{"__ignoreMap":264},[17,17789,17790,17793,17794,17797,17798,17801,17802,17805,17806,17808],{},[41,17791,17792],{},"Select\\Current"," tells you which ",[41,17795,17796],{},"ControlSet00x"," Windows boots, and it isn't always ",[41,17799,17800],{},"001",". In WinRE the Windows partition is often not ",[41,17803,17804],{},"C:",", and with BitLocker you unlock it first, Part 2 has the details. If you want Doppio's filter back instead of none, set the value to ",[41,17807,86],{}," instead of deleting it.",[12,17810,17812],{"id":17811},"limits","Limits",[17,17814,17815],{},"Like in the earlier parts, read this chapter even if you skip everything else.",[105,17817,17818,17821,17832,17835,17840,17846,17860,17867,17870,17873,17876,17892],{},[108,17819,17820],{},"The window is account-wide. While a gate is open, anyone who can authenticate as that account gets in: somebody with the password, a pass-the-hash attack or a well-timed relay. A shorter window means less time for that, and the same kinds of attack. And it only covers new logons: a session opened inside the window stays up after it closes.",[108,17822,17823,17824,17827,17828,17831],{},"You open the gate on the machine it protects, elevated. This limit decides whether Ristretto is any use to you, and I didn't see it until somebody asked me what the thing is ",[36,17825,17826],{},"for",". You need an interactive session on the box to unlock network access ",[36,17829,17830],{},"to"," the box. On a headless server you reach over the network, that's a deadlock: you can't get in to open the gate that would let you in. Unattended access has the same problem from the other side. A backup job that pulls the share at 03:00 arrives as a network logon, and nobody is awake to open a gate for it. It also takes most of the RDP argument away: without a session you can't unlock the one NLA needs. The remaining case: you already have console or physical access and want network access from a second machine for a while. That case is real but narrow.",[108,17833,17834],{},"Nothing checks a second factor at connection time. The NTLM chapter explains why, and the whole design starts from that.",[108,17836,17837,17839],{},[41,17838,78],{}," has to be off, and that's a real downgrade. PPL is what stops credential-dumping tools from reading lsass memory. Turning it off to load an unsigned filter trades a strong, broad mitigation for a narrow one. On a machine that matters, I'd expect that trade to come out negative. This stays test-VM code until I sign the DLL and PPL can stay on.",[108,17841,17842,17843,17845],{},"A fault in the filter is a bugcheck. The ",[41,17844,5035],{}," helps, but lsass-resident code stays dangerous.",[108,17847,17848,17849,17856,17857,17859],{},"The open question from Part 2 remains: does MSV1_0 call the filter for local accounts? Microsoft ",[21,17850,17853,17854],{"href":17851,"rel":17852},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fwindows\u002Fwin32\u002Fapi\u002Fsubauth\u002Fnf-subauth-msv1_0subauthenticationfilter",[57],"documents ",[41,17855,82],{}," for domain controllers. If you never see a ",[41,17858,4861],{}," line in the debugger, the filter is loaded and never asked, and the gate does nothing.",[108,17861,17862,17863,17866],{},"The filter leaves batch, service, ",[41,17864,17865],{},"runas"," and UAC alone, as in Part 2. Keep the deny rights on for those.",[108,17868,17869],{},"The whole design sits on NTLM, and Microsoft is moving local accounts off it. LocalKDC, in preview since June 2026 and off by default, lets a local account authenticate over the network with Kerberos. That logon goes through the Kerberos package, not through the MSV1_0 path this filter hooks. I haven't tested what the gate does then. If you turn LocalKDC on, test that first.",[108,17871,17872],{},"The gate tool checks the code without a password, see above. An elevated attacker can burn the shared lockout.",[108,17874,17875],{},"Clock changes can only close the gate. A clock moved back reads as skew and the filter treats it as closed. A clock moved forward makes the gate expire early. Both directions fail closed, which also means an NTP correction or a VM snapshot restore can shut a gate you opened a minute ago.",[108,17877,17878,17879,17881,17882,17884,17885,438,17887,1221,17889,17891],{},"One function copies Doppio logic: ",[41,17880,8202],{}," copies ",[41,17883,8530],{},", and the two differ on purpose now. The submodule pin catches a changed signature, and it misses a changed meaning, in the copy and in the files the build links. Before you trust a build with a moved pin, read the diff of ",[41,17886,441],{},[41,17888,432],{},[41,17890,62],{},", and run the test.",[108,17893,17894],{},"I haven't tested any of it on a live LSA. It compiles for x64, it exports the one symbol, the decision path allocates nothing, and the test runs the real filter against a fake registry. That's the same honest position Part 2 ended on, and it hasn't improved.",[17,17896,17897],{},"And one limit of the idea itself: a gate never gives you a second factor on every network logon. For that you need the SSP\u002FAP, done right, which is the part I still owe you.",[12,17899,17901],{"id":17900},"where-this-goes-next","Where this goes next",[17,17903,17904,17905,17909,17910,17912],{},"Three parts in, local Windows accounts have a second factor at the console (",[21,17906,17908],{"href":17907},"\u002Fp\u002Fwindows-2fa-credential-provider\u002F","Part 1","), a veto inside LSA for everything that doesn't reach the tile (",[21,17911,24],{"href":23},"), and a way to open the network path again without giving up the factor (this one). None of it is production code. I built all of it to learn the layer.",[17,17914,17915],{},"My list for next, ordered by how much I want each one:",[105,17917,17918,17930,17933,17936],{},[108,17919,17920,17921,17923,17924,17929],{},"Sign the DLL. Then ",[41,17922,78],{}," can stay on, and the whole project stops being a VM exercise. This is the real blocker. Microsoft's page on ",[21,17925,17928],{"href":17926,"rel":17927},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fwindows-server\u002Fsecurity\u002Fcredentials-protection-and-management\u002Fconfiguring-additional-lsa-protection",[57],"configuring additional LSA protection"," explains what PPL protects and what a module needs to load under it.",[108,17931,17932],{},"The SSP\u002FAP, done right. That's the Part 3 I promised at the end of Part 2: signed, PPL-compatible, working with Credential Guard, making an enforcement decision and never keeping a credential. I built the gate because that one is hard, and it stays on the list.",[108,17934,17935],{},"Push approval instead of a gate. A connection that waits a few seconds for a tap on the phone is a real per-connection second factor. It needs a service and somewhere to send the push, and the filter can't block on the logon path, so I don't know the shape of it yet. I find that an interesting problem.",[108,17937,17938,17939,17942],{},"Bind the window to a client. I looked at binding a gate to the client workstation name from ",[41,17940,17941],{},"NETLOGON_NETWORK_INFO",". The client supplies that name and can write anything into it, so the check would protect nothing. I left it out on purpose.",[17,17944,17945,17946,17949],{},"All of it is on GitHub as ",[21,17947,51],{"href":55,"rel":17948},[57],", MIT licensed like Doppio. If you build on it, or if you find the fifth thing I got wrong, I'd like to hear about it.",[17951,17952,17953],"style",{},"html pre.shiki code .sM0OT, html code.shiki .sM0OT{--shiki-default:#EBDBB2}html pre.shiki code .s59W0, html code.shiki .s59W0{--shiki-default:#FABD2F}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s-UKz, html code.shiki .s-UKz{--shiki-default:#928374;--shiki-default-font-style:italic}html pre.shiki code .sUF24, html code.shiki .sUF24{--shiki-default:#FB4934;--shiki-default-font-style:italic}html pre.shiki code .sBQf5, html code.shiki .sBQf5{--shiki-default:#FB4934}html pre.shiki code .svnLg, html code.shiki .svnLg{--shiki-default:#A89984}html pre.shiki code .sG605, html code.shiki .sG605{--shiki-default:#FE8019}html pre.shiki code .sMWUE, html code.shiki .sMWUE{--shiki-default:#83A598}html pre.shiki code .sM2RB, html code.shiki .sM2RB{--shiki-default:#8EC07C}html pre.shiki code .sTqo2, html code.shiki .sTqo2{--shiki-default:#B8BB26}html pre.shiki code .sgrX6, html code.shiki .sgrX6{--shiki-default:#BDAE93}html pre.shiki code .ssIPD, html code.shiki .ssIPD{--shiki-default:#D3869B}",{"title":264,"searchDepth":578,"depth":578,"links":17955},[17956,17957,17958,17959,17962,17963,17977,17989,17999,18000,18004,18005,18006,18007],{"id":14,"depth":572,"text":15},{"id":129,"depth":572,"text":130},{"id":165,"depth":572,"text":166},{"id":181,"depth":572,"text":182,"children":17960},[17961],{"id":252,"depth":578,"text":253},{"id":371,"depth":572,"text":372},{"id":543,"depth":572,"text":544,"children":17964},[17965,17966,17967,17968,17969,17970,17971,17972,17973,17974,17975,17976],{"id":553,"depth":578,"text":554},{"id":1276,"depth":578,"text":1277},{"id":1485,"depth":578,"text":1486},{"id":2102,"depth":578,"text":2103},{"id":2437,"depth":578,"text":2438},{"id":2658,"depth":578,"text":2659},{"id":3208,"depth":578,"text":3209},{"id":3386,"depth":578,"text":3387},{"id":3771,"depth":578,"text":3772},{"id":4884,"depth":578,"text":4885},{"id":5049,"depth":578,"text":5050},{"id":5696,"depth":578,"text":5697},{"id":6412,"depth":572,"text":6413,"children":17978},[17979,17980,17981,17982,17983,17984,17985,17986,17987,17988],{"id":6424,"depth":578,"text":6425},{"id":6822,"depth":578,"text":6823},{"id":7030,"depth":578,"text":7031},{"id":7575,"depth":578,"text":7576},{"id":8609,"depth":578,"text":8610},{"id":9921,"depth":578,"text":9922},{"id":9993,"depth":578,"text":9994},{"id":10025,"depth":578,"text":10026},{"id":11860,"depth":578,"text":11861},{"id":12890,"depth":578,"text":12890},{"id":13483,"depth":572,"text":13484,"children":17990},[17991,17993,17995,17996,17998],{"id":13490,"depth":578,"text":17992},"1. swprintf_s can take lsass down",{"id":13595,"depth":578,"text":17994},"2. The catch (...) never worked",{"id":13671,"depth":578,"text":13672},{"id":13749,"depth":578,"text":17997},"4. WindowSec had no ceiling",{"id":13836,"depth":578,"text":13837},{"id":13908,"depth":572,"text":13909},{"id":14604,"depth":572,"text":14605,"children":18001},[18002,18003],{"id":17040,"depth":578,"text":17041},{"id":17073,"depth":578,"text":17074},{"id":17099,"depth":572,"text":17100},{"id":17777,"depth":572,"text":17778},{"id":17811,"depth":572,"text":17812},{"id":17900,"depth":572,"text":17901},"windows","2026-10-11","Part 2 refuses every network logon for an enrolled account, which makes the account safe and useless. Part 3 adds a gate you open once with a one-time code, so SMB, RPC and WinRM work again for a short window. In C++, with the full source.",false,"md","\u002Fimages\u002Fposts\u002Fwindows-2fa-gating-network-logons\u002Fcover.webp",[18015,18019],{"title":18016,"description":18017,"website":55,"image":18018},"Ristretto on GitHub","The complete source code of this part, plus SECURITY-FIXES.md. MIT licensed.","https:\u002F\u002Fgithub.githubassets.com\u002Fimages\u002Fmodules\u002Flogos_page\u002FGitHub-Mark.png",{"title":18020,"description":18021,"website":18022,"image":18018},"Doppio on GitHub","The credential provider, the store and verify.cpp from Part 1 and 2.","https:\u002F\u002Fgithub.com\u002FNobrac\u002Fdoppio",{},[18025,18026,18027],"A gate with a one-time code","One lockout for tile and gate","Four things the audit found","\u002Fposts\u002Fwindows-2fa-gating-network-logons",{"title":5,"description":18010},"Doppio · 3-part series","posts\u002Fwindows-2fa-gating-network-logons","3",[18008,18034,18035,18036,18037,18038],"lsa","ntlm","smb","2fa","security",null,"upLr6q9EDnaM6_yufCwSW89c8_uBQWyaOt5Ok_MevUo",[18042,18045,18055,18062,18069,18077,18085,18090,18098,18104,18113,18120,18127,18134,18140,18147,18153,18159,18167,18178,18184,18192,18198,18204,18210,18216,18222,18228,18234,18240,18246,18253,18259,18265,18273,18279,18286],{"path":18028,"title":5,"description":18010,"date":18009,"category":18008,"alsoIn":18043,"series":18030,"tags":18044,"readingTime":764},[7],[18008,18034,18035,18036,18037,18038],{"path":18046,"title":18047,"description":18048,"date":18049,"category":7,"alsoIn":18039,"series":18039,"tags":18050,"readingTime":590},"\u002Fposts\u002Fadhardenkit","ADHardenKit: Hardening AD Without Breaking It","LDAP signing, SMB signing, NTLM, Kerberos with AES only. Every hardening checklist has them. I wrote a tool that turns them on in an order that doesn't break the domain.","2026-10-07",[18051,18052,14613,18053,18035,18054],"active-directory","windows-server","hardening","kerberos",{"path":18056,"title":18057,"description":18058,"date":18059,"category":7,"alsoIn":18039,"series":18039,"tags":18060,"readingTime":596},"\u002Fposts\u002Fntlm-analyzer","NTLM-Analyzer: Find Out Who Still Uses NTLM","Windows logs a lot of NTLM activity, but you have to piece it together yourself. I built a tool that shows which users and programs still use NTLMv1 or NTLMv2, and what already runs over Kerberos.","2026-10-03",[18051,18035,18054,18061,18052],"auditing",{"path":18063,"title":18064,"description":18065,"date":18066,"category":18008,"alsoIn":18039,"series":18030,"tags":18067,"readingTime":746},"\u002Fposts\u002Fwindows-2fa-inside-lsa","Build Your Own 2FA for the Windows Logon, Part 2: Inside LSA","The credential provider from Part 1 only sees the logon screen. Part 2 goes into LSA to close the paths it can't reach, with an authentication package and an MSV1_0 sub-authentication filter in C++.","2026-10-01",[18008,18068,18034,18037,18038],"credential-provider",{"path":18070,"title":18071,"description":18072,"date":18073,"category":18008,"alsoIn":18074,"series":18030,"tags":18075,"readingTime":1023},"\u002Fposts\u002Fwindows-2fa-credential-provider","Build Your Own 2FA for the Windows Logon","How a Credential Provider works and how to build a working TOTP 2FA for local Windows accounts in C++. With rate limiting, replay protection and logging.","2026-09-30",[7],[18008,18068,18037,18076,18038],"totp",{"path":18078,"title":18079,"description":18080,"date":18081,"category":7,"alsoIn":18039,"series":18039,"tags":18082,"readingTime":578},"\u002Fposts\u002Fadtierkit-active-directory-tiering","ADTierKit: Automatic AD-Tiering","I got tired of building tier models by hand, so I wrote a tool for it. One PowerShell script, one JSON file.","2026-08-06",[18051,18052,14613,18083,18084],"tiering","laps",{"path":18086,"title":18087,"description":18088,"date":18081,"category":18051,"alsoIn":18039,"series":18039,"tags":18089,"readingTime":625},"\u002Fposts\u002Fldap-ldaps-and-active-directory","The thing with LDAP, LDAPS and Active Directory","Why LDAPS rarely fixes what security reports worry about, why you can't just block port 389, and what actually protects LDAP in Active Directory.",[],{"path":18091,"title":18092,"description":18093,"date":18094,"category":18095,"alsoIn":18039,"series":18039,"tags":18096,"readingTime":578},"\u002Fposts\u002Finstall-tor-exit-node","Install Tor Exit Node","How to run a Tor exit node on an anonymously rented VPS: paying with Monero, picking the provider and the country, and installing and configuring Tor.","2025-09-30","linux",[18097],"tor",{"path":18099,"title":18100,"description":18101,"date":18102,"category":18051,"alsoIn":18039,"series":18039,"tags":18103,"readingTime":565},"\u002Fposts\u002Fconfigure-kerberos-armoring","Configure Kerberos Armoring","Kerberos Armoring (FAST) protects the Kerberos exchange against offline brute force, replay and tampering. How to enable it in your domain with a GPO.","2025-07-28",[18054],{"path":18105,"title":18106,"description":18107,"date":18108,"category":18109,"alsoIn":18039,"series":18039,"tags":18110,"readingTime":834},"\u002Fposts\u002Fexchange-2019-install","Installation guide for Exchange SE and Exchange 2019","Install Exchange Server Subscription Edition (SE) or Exchange Server 2019 step by step and harden it, with WAF rules on the Sophos XG, SPF, DMARC, DKIM and more. The installation is the same for both versions.","2025-06-26","exchange",[18109,18111,18112],"exchange-se","exchange-2019",{"path":18114,"title":18115,"description":18116,"date":18117,"category":18051,"alsoIn":18039,"series":18039,"tags":18118,"readingTime":613},"\u002Fposts\u002Fhow-ntlm-works-active-directory","How NTLM works in Active Directory","How NTLM proves that you know a password without sending it, what the domain controller does, the difference between NTLMv1 and NTLMv2, and why Microsoft is phasing it out.","2025-05-07",[18035,18051,18119],"authentication",{"path":18121,"title":18122,"description":18123,"date":18124,"category":18051,"alsoIn":18039,"series":18039,"tags":18125,"readingTime":584},"\u002Fposts\u002Fadfs-installation","ADFS Installation","Basic installation guide for Active Directory Federation Services","2025-01-27",[18126],"adfs",{"path":18128,"title":18129,"description":18130,"date":18131,"category":18051,"alsoIn":18039,"series":18039,"tags":18132,"readingTime":578},"\u002Fposts\u002Fexample-pass-the-hash-attack","Pass the Hash Attack Example","Example of a pass the hash attack","2025-01-22",[18133],"pass-the-hash",{"path":18135,"title":18136,"description":18137,"date":18138,"category":18051,"alsoIn":18039,"series":18039,"tags":18139,"readingTime":596},"\u002Fposts\u002Fhow-kerberos-works-active-directory","How Kerberos works in Active Directory","How Kerberos works in Active Directory, step by step: the TGT, the service ticket and the request to the service, and why no password crosses the network.","2025-01-16",[18054],{"path":18141,"title":18142,"description":18143,"date":18144,"category":18008,"alsoIn":18039,"series":18039,"tags":18145,"readingTime":578},"\u002Fposts\u002Fconfigure-credential-guard-computer-accounts","Configure Credential Guard for Computer Accounts","Windows Server 2025 can move computer account passwords into Credential Guard. How to enable it by GPO, and why turning it off means a new domain join.","2025-01-15",[18146],"credential-guard",{"path":18148,"title":18149,"description":18150,"date":18151,"category":18008,"alsoIn":18039,"series":18039,"tags":18152,"readingTime":584},"\u002Fposts\u002Fconfigure-credential-guard-windows","Configure Credential Guard","How Credential Guard shields NTLM hashes and Kerberos tickets, how to enable it by GPO on older clients and servers, and how to check that it runs.","2025-01-13",[18146],{"path":18154,"title":18155,"description":18156,"date":18151,"category":18008,"alsoIn":18039,"series":18039,"tags":18157,"readingTime":578},"\u002Fposts\u002Fosconfig-windows-server-2025-security","OSConfig - Security Settings for Windows Server 2025","OSConfig applies Microsoft's recommended security settings on Windows Server 2025 without GPOs. Install the module, list templates, enable a baseline.",[18158],"osconfig",{"path":18160,"title":18161,"description":18162,"date":18163,"category":18008,"alsoIn":18039,"series":18039,"tags":18164,"readingTime":578},"\u002Fposts\u002Fsecure-iis-modsecurity","Secure IIS with Modsecurity","Installation guide to secure IIS with Modsecurity","2025-01-12",[18165,18166],"iis","modsecurity",{"path":18168,"title":18169,"description":18170,"date":18171,"category":18051,"alsoIn":18039,"series":18039,"tags":18172,"readingTime":619},"\u002Fposts\u002Fvcsa-certificate-windows-pki","Issue VCSA Certificate with Windows PKI","Replace the self-signed vCenter (VCSA) certificate with a SubCA certificate from your Windows PKI, using the VCSA Certificate Manager and WinSCP.","2025-01-09",[18173,18174,18175,18176,18177],"ad-cs","pki","vmware","vcsa","vcenter",{"path":18179,"title":18180,"description":18181,"date":18182,"category":18051,"alsoIn":18039,"series":18039,"tags":18183,"readingTime":769},"\u002Fposts\u002Fwindows-pki-installation","Installation guide for a Windows PKI","Step-by-step guide for a two-tier Windows PKI with root CA, sub CA and a separate CRL server, plus certificate templates, OCSP and web enrollment.","2024-12-20",[18173,18174],{"path":18185,"title":18186,"description":18187,"date":18188,"category":18189,"alsoIn":18039,"series":18039,"tags":18190,"readingTime":619},"\u002Fposts\u002Fhack-the-box-administrator","Hack The Box: Administrator","Walkthrough for the \"Administrator\" Hack the Box Challenge","2024-11-19","hack-the-box",[18191,18008,18051],"writeup",{"path":18193,"title":18194,"description":18195,"date":18196,"category":18051,"alsoIn":18039,"series":18039,"tags":18197,"readingTime":572},"\u002Fposts\u002Fsecure-active-directory-part-1","Secure Active Directory - Part 1","Part 1 of the guide to a secure Active Directory","2024-09-09",[],{"path":18199,"title":18200,"description":18201,"date":18202,"category":18189,"alsoIn":18039,"series":18039,"tags":18203,"readingTime":631},"\u002Fposts\u002Fhack-the-box-greenhorn","Hack The Box: Greenhorn","Walkthrough for the \"Greenhorn\" Hack the Box Challenge","2024-07-24",[18191,18095],{"path":18205,"title":18206,"description":18207,"date":18208,"category":18189,"alsoIn":18039,"series":18039,"tags":18209,"readingTime":613},"\u002Fposts\u002Fhack-the-box-runner","Hack The Box: Runner","Walkthrough for the \"Runner\" Hack the Box Challenge","2024-07-20",[18191,18095],{"path":18211,"title":18212,"description":18213,"date":18214,"category":18189,"alsoIn":18039,"series":18039,"tags":18215,"readingTime":565},"\u002Fposts\u002Fhack-the-box-ghost","Hack The Box: Ghost","Walkthrough for the \"Ghost\" Hack the Box Challenge","2024-07-18",[18191,18008,18051],{"path":18217,"title":18218,"description":18219,"date":18220,"category":18189,"alsoIn":18039,"series":18039,"tags":18221,"readingTime":602},"\u002Fposts\u002Fhack-the-box-knife","Hack The Box: Knife","Walkthrough for the \"Knife\" Hack the Box Challenge","2024-07-15",[18191,18095],{"path":18223,"title":18224,"description":18225,"date":18226,"category":18189,"alsoIn":18039,"series":18039,"tags":18227,"readingTime":578},"\u002Fposts\u002Fhack-the-box-broker","Hack The Box: Broker","Walkthrough for the \"Broker\" Hack the Box Challenge","2024-07-10",[18191,18095],{"path":18229,"title":18230,"description":18231,"date":18232,"category":18189,"alsoIn":18039,"series":18039,"tags":18233,"readingTime":565},"\u002Fposts\u002Fhack-the-box-permx","Hack The Box: PermX","Walkthrough zur \"PermX\" Hack the Box Challenge","2024-07-08",[18191,18095],{"path":18235,"title":18236,"description":18237,"date":18238,"category":18189,"alsoIn":18039,"series":18039,"tags":18239,"readingTime":578},"\u002Fposts\u002Fhack-the-box-lame","Hack The Box: Lame","Walkthrough zur \"Lame\" Hack the Box Challenge","2024-07-06",[18191,18095],{"path":18241,"title":18242,"description":18243,"date":18244,"category":18189,"alsoIn":18039,"series":18039,"tags":18245,"readingTime":584},"\u002Fposts\u002Fhack-the-box-analytics","Hack The Box: Analytics","Walkthrough zur \"Analytics\" Hack the Box Challenge","2024-06-30",[18191,18095],{"path":18247,"title":18248,"description":18249,"date":18250,"category":18189,"alsoIn":18039,"series":18039,"tags":18251,"readingTime":590},"\u002Fposts\u002Fhack-the-box-sense","Hack The Box: Sense","Walkthrough zur \"Sense\" Hack the Box Challenge","2024-06-28",[18191,18252],"freebsd",{"path":18254,"title":18255,"description":18256,"date":18257,"category":18189,"alsoIn":18039,"series":18039,"tags":18258,"readingTime":625},"\u002Fposts\u002Fhack-the-box-mailing","Hack The Box: Mailing","Walkthrough zur \"Mailing\" Hack the Box Challenge","2024-06-26",[18191,18008],{"path":18260,"title":18261,"description":18262,"date":18263,"category":18189,"alsoIn":18039,"series":18039,"tags":18264,"readingTime":578},"\u002Fposts\u002Fhack-the-box-jerry","Hack The Box: Jerry","Walkthrough zur \"Jerry\" Hack the Box Challenge","2024-06-20",[18191,18008],{"path":18266,"title":18267,"description":18268,"date":18269,"category":18095,"alsoIn":18039,"series":18039,"tags":18270,"readingTime":565},"\u002Fposts\u002Flinux-ssh-telegram-notifications","Setting up notifications via Telegram for SSH login\u002Flogoff","Get a Telegram message every time someone logs in to or out of your Linux server over SSH, with a Telegram bot, a small script and PAM.","2024-06-01",[18271,18272],"ssh","monitoring",{"path":18274,"title":18275,"description":18276,"date":18277,"category":18189,"alsoIn":18039,"series":18039,"tags":18278,"readingTime":654},"\u002Fposts\u002Fhack-the-box-forest","Hack The Box: Forest","Walkthrough zur \"Forest\" Hack the Box Challenge","2024-05-20",[18191,18008,18051],{"path":18280,"title":18281,"description":18282,"date":18283,"category":18109,"alsoIn":18039,"series":18039,"tags":18284,"readingTime":565},"\u002Fposts\u002Fpowershell-connect-to-exchange-online","Verbindung zu Exchange Online mit Powershell","Wie man sich mit PowerShell mit Exchange Online verbindet: das Modul ExchangeOnlineManagement installieren, aktuell halten und die Verbindung herstellen.","2023-10-23",[18285,14613],"m365",{"path":18287,"title":18288,"description":18289,"date":18290,"category":18109,"alsoIn":18039,"series":18039,"tags":18291,"readingTime":565},"\u002Fposts\u002Fexchange-october-2023-updates","Exchange Server","Neue Sicherheitsupdates (Oktober 2023)","2023-10-18",[],1791749238965]