diff --git a/index.html b/index.html index d79cb3f..0606be4 100644 --- a/index.html +++ b/index.html @@ -1,4 +1,4 @@ - + @@ -17,7 +17,7 @@ diff --git a/js/converter.js b/js/converter.js index 5b8d75a..95e27be 100644 --- a/js/converter.js +++ b/js/converter.js @@ -8,25 +8,72 @@ const oncBasics = { 'NetworkConfigurations': [] } -export function convert(name, ovpn) { +export function convert(name, ovpn, keys) { if (!ovpn.client) { console.warn('Is this a server file?') } - - // Check parameters let params = {} + + // Add certificates + let certs = [] + let [cas, caGuids] = createCerts(keys, ovpn['ca'], 'Authority') + params['ServerCARefs'] = caGuids + certs = certs.concat(cas) + let [clientCerts, clientCertGuids] = createCerts(keys, ovpn['cert'], 'Client') + if (clientCerts[0]) { + params['ClientCertType'] = 'Ref' + params['ClientCertRef'] = clientCertGuids[0] + certs.push(clientCerts[0]) + } else { + params['ClientCertType'] = 'None' + } + + // Add parameters let remote = ovpn.remote.split(' ') const host = remote[0] - if (remote[1]) { - params['Port'] = remote[1] - } - if (ovpn.proto) { - params['Proto'] = ovpn.proto + if (remote[1]) params['Port'] = remote[1] + if (ovpn['auth-user-pass']) params['UserAuthenticationType'] = 'Password' + if (ovpn['comp-lzo'] && ovpn['comp-lzo'] !== 'no') { + params['CompLZO'] = 'true' + } else { + params['CompLZO'] = 'false' } + if (ovpn['persist-key']) params['SaveCredentials'] = true if (ovpn['tls-auth']) { - params['TLSAuthContents'] = convertKey(ovpn['tls-auth']) + let authKey = ovpn['tls-auth'].split(' ') + params['TLSAuthContents'] = convertKey(keys[authKey[0]]) + if (authKey[1]) params['KeyDirection'] = authKey[1] } + if (ovpn['verify-x509-name']) { + params['VerifyX509'] = { + 'Name': ovpn['verify-x509-name'] + } + } + // set parameters if they exist in the ovpn config + let conditionalSet = (ovpnName, oncName, type='str') => { + if (ovpn[ovpnName]) { + const raw = ovpn[ovpnName] + let value + switch (type) { + case 'int': + value = Number(raw) + break + default: + value = raw + } + params[oncName] = value + } + } + conditionalSet('port', 'Port', 'int') + conditionalSet('proto', 'Proto') + conditionalSet('key-direction', 'KeyDirection') + conditionalSet('remote-cert-tls', 'RemoteCertTLS') + conditionalSet('cipher', 'Cipher') + conditionalSet('auth', 'Auth') + conditionalSet('auth-retry', 'AuthRetry') + conditionalSet('reneg-sec', 'RenegSec', 'int') + // Put together network configuration let config = { 'GUID': `{${uuidv4()}}`, 'Name': name, @@ -37,8 +84,11 @@ export function convert(name, ovpn) { 'OpenVPN': params } } + + // Put everything together let onc = Object.assign({}, oncBasics) // create copy onc.NetworkConfigurations = [config] + onc.Certificates = certs return onc } @@ -52,5 +102,42 @@ function uuidv4() { } function convertKey(key) { - return key.replace(/\n/g, '\n') + '\n' + let lines = key.split(/\n/g) + let out = '' + for (let line of lines) { + // filter out empty lines and lines with comments + if (!line || line.match(/^\s*[;#]/)) continue + out += line + '\n' + } + return out +} + +function extractCas(str) { + let splits = str.replace(/\n/g, '').split('-----BEGIN CERTIFICATE-----') + console.log(splits) + let cas = [] + for (const s of splits) { + if (s.includes('-----END CERTIFICATE-----')) { + cas.push(s.split('-----END CERTIFICATE-----')[0]) + } + } + return cas +} + +function createCerts(keys, certName, certType) { + let certs = [] + let certGuids = [] + if (certName) { + let rawCerts = extractCas(keys[certName]) + for (const cert of rawCerts) { + const guid = `{${uuidv4()}}` + certGuids.push(guid) + certs.push({ + 'GUID': guid, + 'Type': certType, + 'X509': cert + }) + } + } + return [certs, certGuids] } diff --git a/js/main.js b/js/main.js index 55a5133..84deba1 100644 --- a/js/main.js +++ b/js/main.js @@ -2,28 +2,40 @@ import {decode} from './parser.js' import {convert} from './converter.js' let clickButton = document.getElementById('clickbutton') -clickButton.addEventListener('click', main, false) +clickButton.addEventListener('click', handler, false) -function main() { +function handler() { let selectedFile = document.getElementById('inputopenvpn').files[0] + let certificates = document.getElementById('inputcertificates').files let connName = document.getElementById('connname').value + let output = document.getElementById('output') + main(connName, selectedFile, certificates, output) +} + +async function main(connName, selectedFile, certificateFiles, output) { if (connName === '') { alert('Please specify a name for the connection.') return } console.log(selectedFile.size + ' bytes') - let reader = new FileReader() - // callback for when reader is done - reader.onload = (e => { - let content = e.target.result - // remove windows-style newlines - content = content.replace(/\r/g, '') - let parsed = decode(content) - console.log(parsed) - let onc = convert(connName, parsed) - let output = document.getElementById('output') - output.value = JSON.stringify(onc, null, 2) - }); - // start reading - reader.readAsText(selectedFile) + let content = await readFile(selectedFile) + let [ovpn, keys] = decode(content) + console.log(ovpn) + for (const certificateFile of certificateFiles) { + keys[certificateFile.name] = await readFile(certificateFile) + } + let onc = convert(connName, ovpn, keys) + output.value = JSON.stringify(onc, null, 2) +} + +function readFile(file) { + return new Promise(resolve => { + let reader = new FileReader() + reader.onload = (e => { + // callback and remove windows-style newlines + resolve(e.target.result.replace(/\r/g, '')) + }) + // start reading + reader.readAsText(file) + }) } diff --git a/js/parser.js b/js/parser.js index 387328f..ac1b986 100644 --- a/js/parser.js +++ b/js/parser.js @@ -1,5 +1,9 @@ +/** + * Parse *.ovpn file. + */ export function decode (str) { - let out = {} + let ovpn = {} + let keys = {} const re = /^([^ ]+)( (.*))?$/i const xmlOpen = /^<([^\/].*)>$/i const xmlClose = /^<\/(.*)>$/i @@ -20,9 +24,10 @@ export function decode (str) { if (tag !== xmlTag) { throw 'bad xml tag' } - const key = unsafe(xmlTag) + const name = unsafe(xmlTag) const value = unsafe(xmlContent) - out[key] = value + keys[name] = value + ovpn[name] = name xmlContent = '' inXml = false continue @@ -37,10 +42,10 @@ export function decode (str) { if (!match) continue const key = unsafe(match[1]) const value = match[2] ? unsafe((match[3] || '')) : true - out[key] = value + ovpn[key] = value } - return out + return [ovpn, keys] } function isQuoted (val) {