Files
wlan-ap/feeds/tip/cloud_discovery/files/usr/bin/est_client
2025-08-29 22:52:28 -04:00

229 lines
6.1 KiB
Plaintext
Executable File

#!/usr/bin/ucode
'use strict';
import { ulog_open, ulog, ULOG_SYSLOG, ULOG_STDIO, LOG_DAEMON, LOG_INFO } from 'log';
import * as fs from 'fs';
import * as libuci from 'uci';
let store_operational_pem = false;
let store_operational_ca = false;
let est_server = 'est.certificates.open-lan.org';
let cert_prefix = 'operational';
function set_est_server() {
let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`);
let issuer = pipe.read("all");
pipe.close();
if (match(issuer, /OpenLAN Demo Birth CA/)) {
ulog(LOG_INFO, 'Certificate type is "Demo" \n');
est_server = 'qaest.certificates.open-lan.org:8001';
} else if (match(issuer, /OpenLAN Birth Issuing CA/)) {
ulog(LOG_INFO, 'Certificate type is "Production"\n');
} else {
ulog(LOG_INFO, 'Certificate type is "TIP"\n');
}
}
if (getenv('EST_SERVER'))
est_server = getenv('EST_SERVER');
if (getenv('CERT_PREFIX'))
cert_prefix = getenv('CERT_PREFIX');
ulog_open(ULOG_SYSLOG | ULOG_STDIO, LOG_DAEMON, "est_client");
function generate_csr(cert) {
if (!fs.stat('/tmp/csr.nohdr.p10')) {
let pipe = fs.popen(`openssl x509 -in ${cert} -noout -subject`);
let subject = pipe.read("all");
pipe.close();
subject = rtrim(subject);
subject = replace(subject, 'subject=', '/');
subject = replace(subject, ' = ', '=');
subject = replace(subject, ', ', '/');
let ret = system(`openssl req -subj "${subject}" -new -key /etc/ucentral/key.pem -out /tmp/csr.p10`);
if (ret) {
ulog(LOG_INFO, 'Failed to generate CSR\n');
return 1;
}
let input = fs.open('/tmp/csr.p10', 'r');
let output = fs.open('/tmp/csr.nohdr.p10', 'w');
let line;
while (line = input.read('line')) {
if (substr(line, 0, 4) == '----')
continue;
output.write(line);
}
input.close();
output.close();
ulog(LOG_INFO, 'Generated CSR\n');
}
return 0;
}
function store_operational_cert(path, target) {
system('mount_certs');
system(`cp ${path} /certificates/${target}`);
ulog(LOG_INFO, `Persistently stored ${target}\n`);
}
function p7_too_pem(src, dst) {
let input = fs.readfile(src);
let output = fs.open('/tmp/convert.p7', 'w');
output.write('-----BEGIN PKCS #7 SIGNED DATA-----\n');
output.write(`${input}\n-----END PKCS #7 SIGNED DATA-----`);
output.close();
let ret = system(`openssl pkcs7 -outform PEM -print_certs -in /tmp/convert.p7 -out ${dst}`);
if (ret) {
ulog(LOG_INFO, 'Failed to convert P7 to PEM\n');
return 1;
}
ulog(LOG_INFO, 'Converted P7 to PEM\n');
return 0;
}
function call_est_server(path, cert, target) {
if (generate_csr(cert))
return 1;
set_est_server();
let ret = system('curl -m 10 -X POST https://' + est_server + '/.well-known/est/' + path + ' -d @/tmp/csr.nohdr.p10 -H "Content-Type: application/pkcs10" --cert ' + cert + ' --key /etc/ucentral/key.pem --cacert /etc/ucentral/insta.pem -o /tmp/operational.nohdr.p7');
if (ret) {
ulog(LOG_INFO, 'Failed to request operational certificate\n');
return 1;
}
ulog(LOG_INFO, 'EST succeeded\n');
return p7_too_pem('/tmp/operational.nohdr.p7', target);
}
function simpleenroll() {
if (fs.stat('/etc/ucentral/' + cert_prefix + '.pem')) {
ulog(LOG_INFO, 'Operational certificate is present\n');
return 0;
}
if (call_est_server('simpleenroll', '/etc/ucentral/cert.pem', '/etc/ucentral/' + cert_prefix + '.pem'))
return 1;
ulog(LOG_INFO, 'Operational cert acquired\n');
store_operational_pem = true;
return 0;
}
function simplereenroll() {
if (!fs.stat('/etc/ucentral/' + cert_prefix + '.pem')) {
ulog(LOG_INFO, 'Operational certificate was not found\n');
return 0;
}
if (call_est_server('simplereenroll', '/etc/ucentral/' + cert_prefix + '.pem', '/tmp/' + cert_prefix + '.pem'))
return 1;
ulog(LOG_INFO, 'Operational cert updated\n');
store_operational_cert('/tmp/' + cert_prefix + '.pem', cert_prefix + '.pem');
system('cp /tmp/' + cert_prefix + '.pem /etc/ucentral/');
system('store_certs');
return 0;
}
function load_operational_ca() {
if (fs.stat('/etc/ucentral/' + cert_prefix + '.ca')) {
ulog(LOG_INFO, 'Operational CA is present\n');
return 0;
}
set_est_server();
let ret = system('curl -m 10 -X GET https://' + est_server + '/.well-known/est/cacerts --cert /etc/ucentral/' + cert_prefix + '.pem --key /etc/ucentral/key.pem --cacert /etc/ucentral/insta.pem -o /tmp/' + cert_prefix + '.ca.nohdr.p7');
if (!ret)
ret = p7_too_pem('/tmp/' + cert_prefix + '.ca.nohdr.p7', '/etc/ucentral/' + cert_prefix + '.ca');
if (ret) {
ulog(LOG_INFO, 'Failed to load CA\n');
return 1;
}
system('cat /etc/ucentral/openlan.pem >> /etc/ucentral/' + cert_prefix + '.ca');
ulog(LOG_INFO, 'Acquired CA\n');
store_operational_ca = true;
return 0;
}
function fwtool() {
if (!fs.stat('/etc/ucentral/cert.pem'))
return 0;
let pipe = fs.popen(`openssl x509 -in /etc/ucentral/cert.pem -noout -issuer`);
let issuer = pipe.read("all");
pipe.close();
if (!(match(issuer, /OpenLAN/) && match(issuer, /Birth/)))
return 0;
ulog(LOG_INFO, 'The issuer is insta\n');
let metadata = fs.readfile('/tmp/sysupgrade.meta');
if (metadata)
metadata = json(metadata);
if (!metadata)
return 0;
if (!metadata.est_supported) {
ulog(LOG_INFO, 'The image does not support EST\n');
return 1;
}
ulog(LOG_INFO, 'The image supports EST\n');
return 0;
}
function check_cert() {
if (!fs.stat('/etc/ucentral/cert.pem'))
return 0;
let pipe = fs.popen("openssl x509 -in /etc/ucentral/cert.pem -noout -subject -nameopt multiline | grep commonName | awk '{ print $3 }'");
let cn = pipe.read("all");
pipe.close();
if (!cn)
return 0;
cn = lc(trim(cn));
let uci = libuci.cursor();
let serial = uci.get('ucentral', 'config', 'serial');
return cn != serial;
}
switch(ARGV[0]) {
case 'enroll':
let ret = simpleenroll();
if (!ret)
ret = load_operational_ca();
if (store_operational_pem)
store_operational_cert('/etc/ucentral/' + cert_prefix + '.pem', cert_prefix + '.pem');
if (store_operational_ca)
store_operational_cert('/etc/ucentral/' + cert_prefix + '.ca', cert_prefix + '.ca');
if (store_operational_pem || store_operational_ca)
system('store_certs');
exit(ret);
case 'reenroll':
if (simplereenroll())
exit(1);
exit(0);
case 'fwtool':
exit(fwtool());
case 'check':
exit(check_cert());
}