diff --git a/protocols/luci-proto-openconnect/htdocs/luci-static/resources/protocol/openconnect.js b/protocols/luci-proto-openconnect/htdocs/luci-static/resources/protocol/openconnect.js index 05c559ce84..86ff009029 100644 --- a/protocols/luci-proto-openconnect/htdocs/luci-static/resources/protocol/openconnect.js +++ b/protocols/luci-proto-openconnect/htdocs/luci-static/resources/protocol/openconnect.js @@ -4,14 +4,14 @@ 'require network'; 'require validation'; -var callGetCertificateFiles = rpc.declare({ +const callGetCertificateFiles = rpc.declare({ object: 'luci.openconnect', method: 'getCertificates', params: [ 'interface' ], expect: { '': {} } }); -var callSetCertificateFiles = rpc.declare({ +const callSetCertificateFiles = rpc.declare({ object: 'luci.openconnect', method: 'setCertificates', params: [ 'interface', 'user_certificate', 'user_privatekey', 'ca_certificate' ], @@ -22,14 +22,14 @@ network.registerPatternVirtual(/^vpn-.+$/); function sanitizeCert(s) { if (typeof(s) != 'string') - return null; + return ''; s = s.trim(); if (s == '') - return null; + return s; - s = s.replace(/\r\n?/g, '\n'); + s = s.replace(/\r?\n/g, '\n'); if (!s.match(/\n$/)) s += '\n'; @@ -38,24 +38,22 @@ function sanitizeCert(s) { } function validateCert(priv, section_id, value) { - var beg = priv ? /^-----BEGIN (RSA )?PRIVATE KEY-----$/ : /^-----BEGIN CERTIFICATE-----$/, - end = priv ? /^-----END (RSA )?PRIVATE KEY-----$/ : /^-----END CERTIFICATE-----$/, - lines = value.trim().split(/[\r\n]/), - start = false, - i; - - if (value === null || value === '') + if (!value?.trim()) return true; - for (i = 0; i < lines.length; i++) { - if (lines[i].match(beg)) - start = true; - else if (start && !lines[i].match(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/)) - break; - } + const beg = priv ? /^-----BEGIN (RSA )?PRIVATE KEY-----$/ : /^-----BEGIN CERTIFICATE-----$/; + const end = priv ? /^-----END (RSA )?PRIVATE KEY-----$/ : /^-----END CERTIFICATE-----$/; + const lines = value.trim().split(/[\r?\n]/); + const base64 = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/; + const errmsg = _('This does not look like a valid PEM file'); - if (!start || i < lines.length - 1 || !lines[i].match(end)) - return _('This does not look like a valid PEM file'); + + if (!lines?.[0].match(beg) || !lines.at(-1).match(end)) + return errmsg; + + for (let i = 1; i < lines.length - 1; i++) + if (!base64.test(lines[i])) + return errmsg; return true; } @@ -90,9 +88,9 @@ return network.registerProtocol('openconnect', { }, renderFormOptions: function(s) { - var dev = this.getDevice().getName(), - certLoadPromise = null, - o; + const dev = this.getDevice().getName(); + let certLoadPromise = null; + let o; o = s.taboption('general', form.ListValue, 'vpn_protocol', _('VPN Protocol')); o.value('anyconnect', 'OpenConnect or Cisco AnyConnect SSL VPN'); @@ -106,7 +104,7 @@ return network.registerProtocol('openconnect', { o = s.taboption('general', form.Value, 'uri', _('VPN Server')); o.placeholder = 'https://example.com:443/usergroup'; o.validate = function(section_id, value) { - var m = String(value).match(/^(?:(\w+):\/\/|)(?:\[([0-9a-f:.]{2,45})\]|([^\/:]+))(?::([0-9]{1,5}))?(?:\/.*)?$/i); + const m = String(value).match(/^(?:(\w+):\/\/|)(?:\[([0-9a-f:.]{2,45})\]|([^\/:]+))(?::([0-9]{1,5}))?(?:\/.*)?$/i); if (!m) return _('Invalid server URL'); @@ -163,7 +161,7 @@ return network.registerProtocol('openconnect', { return certLoadPromise.then(function(certs) { return certs.user_certificate }); }; o.write = function(section_id, value) { - return callSetCertificateFiles(section_id, sanitizeCert(value), null, null); + return callSetCertificateFiles(section_id, sanitizeCert(value), '', ''); }; o = s.taboption('general', form.TextValue, 'userkey', _('User key (PEM encoded)')); @@ -175,7 +173,7 @@ return network.registerProtocol('openconnect', { return certLoadPromise.then(function(certs) { return certs.user_privatekey }); }; o.write = function(section_id, value) { - return callSetCertificateFiles(section_id, null, sanitizeCert(value), null); + return callSetCertificateFiles(section_id, '', sanitizeCert(value), ''); }; o = s.taboption('general', form.TextValue, 'ca', _('CA certificate; if empty it will be saved after the first connection.')); @@ -187,7 +185,7 @@ return network.registerProtocol('openconnect', { return certLoadPromise.then(function(certs) { return certs.ca_certificate }); }; o.write = function(section_id, value) { - return callSetCertificateFiles(section_id, null, null, sanitizeCert(value)); + return callSetCertificateFiles(section_id, '', '', sanitizeCert(value)); }; o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU')); diff --git a/protocols/luci-proto-openconnect/root/usr/share/rpcd/ucode/luci.openconnect b/protocols/luci-proto-openconnect/root/usr/share/rpcd/ucode/luci.openconnect index 8e728e6bd0..1bc13a1706 100644 --- a/protocols/luci-proto-openconnect/root/usr/share/rpcd/ucode/luci.openconnect +++ b/protocols/luci-proto-openconnect/root/usr/share/rpcd/ucode/luci.openconnect @@ -1,41 +1,32 @@ -#!/usr/bin/env ucode - 'use strict'; import { readfile, writefile, stat } from 'fs'; const interfaceregex = /^[a-zA-Z0-9_]+$/; -const user_certificate_string = "/etc/openconnect/user-cert-vpn-%s.pem"; -const user_privatekey_string = "/etc/openconnect/user-key-vpn-%s.pem"; -const ca_certificate_string = "/etc/openconnect/ca-vpn-%s.pem"; +const paths = { + user_certificate: "/etc/openconnect/user-cert-vpn-%s.pem", + user_privatekey: "/etc/openconnect/user-key-vpn-%s.pem", + ca_certificate: "/etc/openconnect/ca-vpn-%s.pem" +}; - -// Utility to read a file function _readfile(path) { - let _stat = stat(path); - if (_stat && _stat.type == "file") { - let content = readfile(path); - return content ? trim(content) : 'File empty'; - } - return 'File not found'; + let s = stat(path); + return (s?.type == 'file') ? trim(readfile(path) ?? '') || 'File empty' : null; } -// Utility to write a file function _writefile(path, data) { - if (!data) { - return false; - } - return writefile(path, data) == length(data); + return data ? writefile(path, data) == length(data) : false; +} + +function is_valid_iface(ifname) { + return ifname && match(ifname, interfaceregex); } const methods = { - - list:{ + list: { call: function() { return { - getCertificates: { - interface: "interface" - }, + getCertificates: { interface: "interface" }, setCertificates: { interface: "interface", user_certificate: "user_certificate", @@ -47,29 +38,16 @@ const methods = { }, getCertificates: { - args: { - interface: "interface", - }, + args: { interface: "interface" }, call: function(req) { + let iface = req.args?.interface; + if (!is_valid_iface(iface)) return; - const _interface = req.args?.interface; - if (!_interface || !match(_interface, interfaceregex)) { - // printf("Invalid interface name"); - return; - } - - const user_certificate_pem = _readfile(sprintf(user_certificate_string, _interface)); - const user_privatekey_pem = _readfile(sprintf(user_privatekey_string, _interface)); - const ca_certificate_pem = _readfile(sprintf(ca_certificate_string, _interface)); - - if(user_certificate_pem && user_privatekey_pem && ca_certificate_pem){ - return { - user_certificate: user_certificate_pem, - user_privatekey: user_privatekey_pem, - ca_certificate: ca_certificate_pem, - }; - } + let result = {}; + for (let k in paths) + result[k] = _readfile(sprintf(paths[k], iface)); + return result; } }, @@ -81,35 +59,17 @@ const methods = { ca_certificate: "ca_certificate", }, call: function(req) { + let iface = req.args?.interface; + if (!is_valid_iface(iface)) return; let result = false; - let _interface = req.args?.interface; - - if (!_interface || !match(_interface, interfaceregex)) { - // printf("Invalid interface name"); - return; + for (let k in paths) { + if (req.args?.[k]) + result = _writefile(sprintf(paths[k], iface), req.args[k]); } - - /* the interface is set up to call 1 write per certificate, - with only one of the following arguments not null */ - if (req.args?.user_certificate) { - result = _writefile(sprintf(user_certificate_string, _interface), req.args?.user_certificate); - } - if (req.args?.user_privatekey) { - result = _writefile(sprintf(user_privatekey_string, _interface), req.args?.user_privatekey); - } - if (req.args?.ca_certificate) { - result = _writefile(sprintf(ca_certificate_string, _interface), req.args?.ca_certificate); - } - - return { - result: result, - }; - + return { result: result }; } } - }; return { 'luci.openconnect': methods }; -