diff options
| author | Andrej Mihajlov <and@mullvad.net> | 2018-06-20 14:43:51 +0200 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2018-07-03 13:37:54 +0200 |
| commit | 67e82627564f8e4a2d8da4bcf5f0fd00867876bc (patch) | |
| tree | 814494d59045d8b3f02cb310dc7728aa818be509 /app/lib | |
| parent | be096ee87bb1256b67c3b24b61be77d523aff9cc (diff) | |
| download | mullvadvpn-67e82627564f8e4a2d8da4bcf5f0fd00867876bc.tar.xz mullvadvpn-67e82627564f8e4a2d8da4bcf5f0fd00867876bc.zip | |
Move RPC file related stuff into RpcAddressFile
Diffstat (limited to 'app/lib')
| -rw-r--r-- | app/lib/backend.js | 23 | ||||
| -rw-r--r-- | app/lib/rpc-address-file.js | 120 | ||||
| -rw-r--r-- | app/lib/rpc-file-security.js | 36 |
3 files changed, 124 insertions, 55 deletions
diff --git a/app/lib/backend.js b/app/lib/backend.js index b24b495449..39521d8edb 100644 --- a/app/lib/backend.js +++ b/app/lib/backend.js @@ -8,6 +8,7 @@ import connectionActions from '../redux/connection/actions'; import settingsActions from '../redux/settings/actions'; import { push } from 'react-router-redux'; +import type { RpcCredentials } from './rpc-address-file'; import type { ReduxStore } from '../redux/store'; import type { AccountToken, BackendState, RelaySettingsUpdate } from './ipc-facade'; import type { ConnectionState } from '../redux/connection/reducers'; @@ -74,32 +75,16 @@ export class UnknownError extends Error { } } -export type IpcCredentials = { - connectionString: string, - sharedSecret: string, -}; -export function parseIpcCredentials(data: string): ?IpcCredentials { - const [connectionString, sharedSecret] = data.split('\n', 2); - if (connectionString && sharedSecret !== undefined) { - return { - connectionString, - sharedSecret, - }; - } else { - return null; - } -} - /** * Backend implementation */ export class Backend { _ipc: IpcFacade; - _credentials: ?IpcCredentials; + _credentials: ?RpcCredentials; _authenticationPromise: ?Promise<void>; _store: ReduxStore; - constructor(store: ReduxStore, credentials?: IpcCredentials, ipc: ?IpcFacade) { + constructor(store: ReduxStore, credentials?: RpcCredentials, ipc: ?IpcFacade) { this._store = store; this._credentials = credentials; @@ -116,7 +101,7 @@ export class Backend { } } - setCredentials(credentials: IpcCredentials) { + setCredentials(credentials: RpcCredentials) { log.debug('Got connection info to backend', credentials.connectionString); this._credentials = credentials; diff --git a/app/lib/rpc-address-file.js b/app/lib/rpc-address-file.js new file mode 100644 index 0000000000..5185ddc749 --- /dev/null +++ b/app/lib/rpc-address-file.js @@ -0,0 +1,120 @@ +// @flow + +import fs from 'fs'; +import path from 'path'; +import { promisify } from 'util'; +import { getSystemTemporaryDirectory } from './tempdir'; + +const fsReadFileAsync = promisify(fs.readFile); + +const POLL_INTERVAL = 200; + +const appDirectoryName = 'Mullvad VPN'; + +export type RpcCredentials = { + connectionString: string, + sharedSecret: string, +}; + +export class RpcAddressFile { + _filePath: string; + _pollIntervalId: ?IntervalID; + _pollPromise: ?Promise<void>; + + constructor() { + this._filePath = getRpcAddressFilePath(); + } + + get filePath(): string { + return this._filePath; + } + + poll(): Promise<void> { + let promise = this._pollPromise; + + if (!promise) { + promise = new Promise((resolve, _reject) => { + const timer = setInterval(() => { + fs.exists(this._filePath, (exists) => { + if (exists) { + clearInterval(timer); + resolve(); + + this._pollPromise = null; + } + }); + }, POLL_INTERVAL); + }); + + this._pollPromise = promise; + } + + return promise; + } + + isTrusted() { + const filePath = this._filePath; + switch (process.platform) { + case 'win32': + return isOwnedByLocalSystem(filePath); + case 'darwin': + case 'linux': + return isOwnedAndOnlyWritableByRoot(filePath); + default: + throw new Error(`Unknown platform: ${process.platform}`); + } + } + + async waitUntilExists(): Promise<RpcCredentials> { + const data = await fsReadFileAsync(this._filePath, 'utf8'); + const [connectionString, sharedSecret] = data.split('\n', 2); + + if (connectionString && sharedSecret !== undefined) { + return { + connectionString, + sharedSecret, + }; + } else { + throw new Error('Cannot parse the RPC address file'); + } + } +} + +function getRpcAddressFilePath() { + const rpcAddressFileName = '.mullvad_rpc_address'; + + switch (process.platform) { + case 'win32': { + // Windows: %ALLUSERSPROFILE%\{appname} + const programDataDirectory = process.env.ALLUSERSPROFILE; + if (programDataDirectory) { + const appDataDirectory = path.join(programDataDirectory, appDirectoryName); + return path.join(appDataDirectory, rpcAddressFileName); + } else { + throw new Error('Missing %ALLUSERSPROFILE% environment variable'); + } + } + default: + return path.join(getSystemTemporaryDirectory(), rpcAddressFileName); + } +} + +function isOwnedAndOnlyWritableByRoot(path: string): boolean { + const stat = fs.statSync(path); + const isOwnedByRoot = stat.uid === 0; + const isOnlyWritableByOwner = (stat.mode & parseInt('022', 8)) === 0; + + return isOwnedByRoot && isOnlyWritableByOwner; +} + +function isOwnedByLocalSystem(path: string): boolean { + // $FlowFixMe: this module is only available on Windows + const winsec = require('windows-security'); + const ownerSid = winsec.getFileOwnerSid(path, null); + const isWellKnownSid = winsec.isWellKnownSid( + ownerSid, + winsec.WellKnownSid.BuiltinAdministratorsSid, + ); + + return isWellKnownSid; +} diff --git a/app/lib/rpc-file-security.js b/app/lib/rpc-file-security.js deleted file mode 100644 index fa88111c02..0000000000 --- a/app/lib/rpc-file-security.js +++ /dev/null @@ -1,36 +0,0 @@ -// @flow - -import fs from 'fs'; - -export function canTrustRpcAddressFile(path: string): boolean { - const platform = process.platform; - switch (platform) { - case 'win32': - return isOwnedByLocalSystem(path); - case 'darwin': - case 'linux': - return isOwnedAndOnlyWritableByRoot(path); - default: - throw new Error(`Unknown platform: ${platform}`); - } -} - -function isOwnedAndOnlyWritableByRoot(path: string): boolean { - const stat = fs.statSync(path); - const isOwnedByRoot = stat.uid === 0; - const isOnlyWritableByOwner = (stat.mode & parseInt('022', 8)) === 0; - - return isOwnedByRoot && isOnlyWritableByOwner; -} - -function isOwnedByLocalSystem(path: string): boolean { - // $FlowFixMe: this module is only available on Windows - const winsec = require('windows-security'); - const ownerSid = winsec.getFileOwnerSid(path, null); - const isWellKnownSid = winsec.isWellKnownSid( - ownerSid, - winsec.WellKnownSid.BuiltinAdministratorsSid, - ); - - return isWellKnownSid; -} |
