diff --git a/README.md b/README.md index 32c9f62..9b4ae6c 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,4 @@ Convert OpenVPN config files to the ONC ChromeOS network config files. ## How to use -Download the code and open index.html in Chrome. Follow the instructions there. +Download the the `index.html` file and open it in Chrome. Follow the instructions there. diff --git a/index.html b/index.html index 0606be4..4fda698 100644 --- a/index.html +++ b/index.html @@ -7,23 +7,306 @@ OpenVPN to ONC - - + + - +

ovpn2onc

-

Output

- +

Output (copy this into a new file and load it in ChromeOS)

+
diff --git a/js/converter.js b/js/converter.js deleted file mode 100644 index 95e27be..0000000 --- a/js/converter.js +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Convert the parsed OpenVPN config to ONC. - */ - -const oncBasics = { - 'Type': 'UnencryptedConfiguration', - 'Certificates': [], - 'NetworkConfigurations': [] -} - -export function convert(name, ovpn, keys) { - if (!ovpn.client) { - console.warn('Is this a server file?') - } - 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['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']) { - 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, - 'Type': 'VPN', - 'VPN': { - 'Type': 'OpenVPN', - 'Host': host, - 'OpenVPN': params - } - } - - // Put everything together - let onc = Object.assign({}, oncBasics) // create copy - onc.NetworkConfigurations = [config] - onc.Certificates = certs - return onc -} - -/** - * Create UUID (from Stackoverflow). - */ -function uuidv4() { - return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c=> - (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) - ) -} - -function convertKey(key) { - 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 deleted file mode 100644 index 84deba1..0000000 --- a/js/main.js +++ /dev/null @@ -1,41 +0,0 @@ -import {decode} from './parser.js' -import {convert} from './converter.js' - -let clickButton = document.getElementById('clickbutton') -clickButton.addEventListener('click', handler, false) - -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 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 deleted file mode 100644 index ac1b986..0000000 --- a/js/parser.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Parse *.ovpn file. - */ -export function decode (str) { - let ovpn = {} - let keys = {} - const re = /^([^ ]+)( (.*))?$/i - const xmlOpen = /^<([^\/].*)>$/i - const xmlClose = /^<\/(.*)>$/i - let xmlTag = '' - let inXml = false - let xmlContent = '' - let lines = str.split(/[\r\n]+/g) - - for (let line of lines) { - if (!line || line.match(/^\s*[;#]/)) continue - if (inXml) { - const xmlMatch = line.match(xmlClose) - if (!xmlMatch) { - xmlContent += line + '\n' - continue - } - const tag = xmlMatch[1] - if (tag !== xmlTag) { - throw 'bad xml tag' - } - const name = unsafe(xmlTag) - const value = unsafe(xmlContent) - keys[name] = value - ovpn[name] = name - xmlContent = '' - inXml = false - continue - } - const xmlMatch = line.match(xmlOpen) - if (xmlMatch) { - inXml = true - xmlTag = xmlMatch[1] - continue - } - const match = line.match(re) - if (!match) continue - const key = unsafe(match[1]) - const value = match[2] ? unsafe((match[3] || '')) : true - ovpn[key] = value - } - - return [ovpn, keys] -} - -function isQuoted (val) { - return ((val.charAt(0) === '"' && val.slice(-1) === '"') || - (val.charAt(0) === "'" && val.slice(-1) === "'")) -} - -function unsafe (val, doUnesc) { - val = (val || '').trim() - if (isQuoted(val)) { - // remove the single quotes before calling JSON.parse - if (val.charAt(0) === "'") { - val = val.substr(1, val.length - 2) - } - try { val = JSON.parse(val) } catch (_) {} - } else { - // walk the val to find the first not-escaped ; character - var esc = false - var unesc = '' - for (var i = 0, l = val.length; i < l; i++) { - var c = val.charAt(i) - if (esc) { - if ('\\;#'.indexOf(c) !== -1) { - unesc += c - } else { - unesc += '\\' + c - } - esc = false - } else if (';#'.indexOf(c) !== -1) { - break - } else if (c === '\\') { - esc = true - } else { - unesc += c - } - } - if (esc) { - unesc += '\\' - } - return unesc - } - return val -} diff --git a/style.css b/style.css deleted file mode 100644 index a1d72b3..0000000 --- a/style.css +++ /dev/null @@ -1,3 +0,0 @@ -#output { - background-color: lightgray; -}