mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 04:55:42 +09:00
fix(desktop): trust CAs in the windows intermediate store (#3494)
This commit is contained in:
1 parent
4f88872a3c
commit
4b8ce8bf63
8 files changed
+290
-9
No files matched your search
@@ -66,6 +66,17 @@ describe('desktop system trust verifier', () => {
|
||||
assert.equal(buildDesktopTrustAnchors([read('kz_root')]).size, 0);
|
||||
});
|
||||
|
||||
test('anchors on DER roots read from the Windows intermediate store', () => {
|
||||
const der = (name) => new X509Certificate(read(name)).raw;
|
||||
const store = buildDesktopTrustAnchors([der('root')]);
|
||||
const request = {hostname: 'host.example', certificate: chain(['leaf', 'int']), errorCode: AUTHORITY_INVALID};
|
||||
assert.equal(
|
||||
verifyAgainstDesktopSystemTrust(request, () => store),
|
||||
ACCEPT,
|
||||
);
|
||||
assert.equal(buildDesktopTrustAnchors([der('kz_root'), Buffer.from('not a certificate')]).size, 0);
|
||||
});
|
||||
|
||||
test('anchors only on admin added CAs that the distro bundle still trusts', () => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-trust-'));
|
||||
const write = (file, content) => {
|
||||
|
||||
@@ -5,6 +5,7 @@ import net from 'node:net';
|
||||
import {createChildLogger} from '@electron/common/Logger';
|
||||
import {CHROMIUM_SPKI_BLOCKLIST, CHROMIUM_SPKI_BLOCKLIST_SOURCE} from '@electron/main/DesktopChromiumSPKIBlocklist';
|
||||
import {resolveDesktopLocallyAddedCertificates} from '@electron/main/DesktopTrustedCertificates';
|
||||
import {readWindowsIntermediateStoreRoots} from '@electron/main/GatewaySocketNativeBoundary';
|
||||
import type {Certificate, Session} from 'electron';
|
||||
|
||||
const logger = createChildLogger('DesktopSystemTrustVerifier');
|
||||
@@ -398,14 +399,14 @@ function chainsToDesktopTrustAnchor(
|
||||
}
|
||||
|
||||
export function buildDesktopTrustAnchors(
|
||||
certificates: ReadonlyArray<string>,
|
||||
certificates: ReadonlyArray<string | Buffer>,
|
||||
now: number = Date.now(),
|
||||
): DesktopTrustAnchors {
|
||||
const anchors = new Map<string, Array<ParsedCertificate>>();
|
||||
for (const pem of certificates) {
|
||||
for (const certificate of certificates) {
|
||||
let anchor: ParsedCertificate;
|
||||
try {
|
||||
anchor = parseCertificate(new X509Certificate(pem));
|
||||
anchor = parseCertificate(new X509Certificate(certificate));
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
@@ -434,12 +435,23 @@ export function verifyAgainstDesktopSystemTrust(
|
||||
return CHROMIUM_DEFAULT_RESULT;
|
||||
}
|
||||
|
||||
function resolvePlatformAnchorCertificates(): ReadonlyArray<string | Buffer> {
|
||||
switch (process.platform) {
|
||||
case 'linux':
|
||||
return resolveDesktopLocallyAddedCertificates();
|
||||
case 'win32':
|
||||
return readWindowsIntermediateStoreRoots();
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function installDesktopSystemTrustVerifier(target: Session): void {
|
||||
if (process.platform !== 'linux') return;
|
||||
const anchors = buildDesktopTrustAnchors(resolveDesktopLocallyAddedCertificates());
|
||||
const anchors = buildDesktopTrustAnchors(resolvePlatformAnchorCertificates());
|
||||
if (anchors.size === 0) return;
|
||||
logger.info('Trusting locally added CAs for Chromium requests', {
|
||||
count: anchors.size,
|
||||
platform: process.platform,
|
||||
blocklist: CHROMIUM_SPKI_BLOCKLIST_SOURCE,
|
||||
});
|
||||
target.setCertificateVerifyProc((request, callback) => {
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface GatewaySocketBoundary {
|
||||
type GatewaySocketNativeModule = {
|
||||
connect: GatewaySocketConnectBinding | null;
|
||||
warmup?: (() => void) | null;
|
||||
windowsIntermediateStoreRoots?: (() => Array<Buffer>) | null;
|
||||
loadError: Error | null;
|
||||
};
|
||||
|
||||
@@ -34,6 +35,7 @@ const requireModule = createRequire(import.meta.url);
|
||||
|
||||
let bindingCache: GatewaySocketConnectBinding | null | undefined;
|
||||
let warmupCache: (() => void) | null = null;
|
||||
let windowsIntermediateStoreRootsCache: (() => Array<Buffer>) | null = null;
|
||||
|
||||
export function loadGatewaySocketBinding(): GatewaySocketConnectBinding | null {
|
||||
if (bindingCache !== undefined) return bindingCache;
|
||||
@@ -47,6 +49,8 @@ export function loadGatewaySocketBinding(): GatewaySocketConnectBinding | null {
|
||||
const nativeModule = requireModule(GATEWAY_SOCKET_ADDON_PACKAGE) as GatewaySocketNativeModule;
|
||||
bindingCache = nativeModule.connect ?? null;
|
||||
warmupCache = bindingCache === null ? null : (nativeModule.warmup ?? null);
|
||||
windowsIntermediateStoreRootsCache =
|
||||
bindingCache === null ? null : (nativeModule.windowsIntermediateStoreRoots ?? null);
|
||||
} catch (error) {
|
||||
log.warn('[NativeGateway] The native gateway addon is unavailable, using the browser transport', error);
|
||||
bindingCache = null;
|
||||
@@ -63,6 +67,16 @@ export function warmGatewaySocketTransport(): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function readWindowsIntermediateStoreRoots(): ReadonlyArray<Buffer> {
|
||||
if (loadGatewaySocketBinding() === null || windowsIntermediateStoreRootsCache === null) return [];
|
||||
try {
|
||||
return windowsIntermediateStoreRootsCache();
|
||||
} catch (error) {
|
||||
log.warn('[NativeGateway] Failed to read the Windows intermediate certificate store', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function isNativeGatewayAvailable(): boolean {
|
||||
return loadGatewaySocketBinding() !== null;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user