ref: ca60e03c5bf96cff818857079335d1e6517080f7
parent: 4daf4ffdbed20d10585c8da987d200b7a200fcc4
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Apr 1 17:24:42 EDT 2022
ip/sol: add intel AMT serial-over-lan console program
--- /dev/null
+++ b/sys/man/8/sol
@@ -1,0 +1,46 @@
+.TH SOL 8
+.SH NAME
+sol - Intel AMT serial-over-lan console
+.SH SYNOPSIS
+.B ip/sol
+[
+.B -rR
+] [
+.B -u
+.I user
+]
+.I host
+.SH DESCRIPTION
+This program provides remote access to the
+serial-over-lan interface of intel AMT
+enabled machines.
+.PP
+The protocol runs over tcp port 16995 and
+and is protected using TLS.
+.PP
+For authentication, a username and password
+is required. The default username, unless
+given by the
+.B -u
+.I user
+argument, is "admin".
+The password will be prompted and kept in
+factotum, tagged with the servers x509
+crtificate thumbprint.
+.PP
+The
+.B -r
+and
+.B -R
+flags enable or disable the use of /dev/cons
+in raw mode instead of just reading and writing
+standard input and output.
+By default, raw mode is enabled when the
+.B $TERM
+environment variable has been set.
+.SH SOURCE
+.B /sys/src/cmd/ip/sol.c
+.SH "SEE ALSO"
+.IR ssh (1),
+.IR consolefs (4),
+.IR factotum (4)
--- a/sys/src/cmd/ip/mkfile
+++ b/sys/src/cmd/ip/mkfile
@@ -27,6 +27,7 @@
torrent\
udpecho\
socksd\
+ sol\
wol\
DIRS=ftpfs cifsd dhcpd httpd ipconfig ppp snoopy
--- /dev/null
+++ b/sys/src/cmd/ip/sol.c
@@ -1,0 +1,277 @@
+/* intel amt serial-over-lan console */
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <libsec.h>
+#include <auth.h>
+
+enum {
+ MAX_TRANSMIT_BUFFER = 15000,
+ TRANSMIT_BUFFER_TIMEOUT = 100,
+ TRANSMIT_OVERFLOW_TIMEOUT = 0,
+ HOST_SESSION_RX_TIMEOUT = 60000,
+ HOST_FIFO_RX_FLUSH_TIMEOUT = 0,
+ HEATBEAT_INTERVAL = 5000,
+};
+
+int pid = 0, raw = -1, fd = -1;
+char thumbfile[] = "/sys/lib/tls/amt";
+
+Biobuf bin, bout;
+jmp_buf reconnect;
+
+void
+killpid(void)
+{
+ postnote(PNPROC, pid, "kill");
+}
+
+void
+eof(void)
+{
+ if(pid == 0) sysfatal("eof: %r");
+ killpid();
+ pid = 0;
+ longjmp(reconnect, 1);
+}
+
+void
+recv(char *fmt, ...)
+{
+ uchar buf[4];
+ va_list a;
+ void *s;
+ int n;
+
+ va_start(a, fmt);
+ for(;;){
+ switch(*fmt++){
+ case '\0':
+ va_end(a);
+ return;
+ case '*':
+ Bflush(&bin);
+ break;
+ case '_':
+ if(Bread(&bin, buf, 1) != 1) eof();
+ break;
+ case 'b':
+ if(Bread(&bin, buf, 1) != 1) eof();
+ *va_arg(a, int*) = (uint)buf[0];
+ break;
+ case 'w':
+ if(Bread(&bin, buf, 2) != 2) eof();
+ *va_arg(a, int*) = (uint)buf[0] | (uint)buf[1] << 8;
+ break;
+ case 'l':
+ if(Bread(&bin, buf, 4) != 4) eof();
+ *va_arg(a, int*) = (uint)buf[0] | (uint)buf[1] << 8 | (uint)buf[2] << 16 | (uint)buf[3] << 24;
+ break;
+ case '[':
+ s = va_arg(a, void*);
+ n = va_arg(a, int);
+ if(n && Bread(&bin, s, n) != n) eof();
+ break;
+ }
+ }
+}
+
+void
+send(char *fmt, ...)
+{
+ uchar buf[4];
+ va_list a;
+ void *s;
+ int n;
+
+ va_start(a, fmt);
+ for(;;){
+ switch(*fmt++){
+ case '\0':
+ Bflush(&bout);
+ va_end(a);
+ return;
+ case '_':
+ buf[0] = 0;
+ Bwrite(&bout, buf, 1);
+ break;
+ case 'b':
+ buf[0] = va_arg(a, int);
+ Bwrite(&bout, buf, 1);
+ break;
+ case 'w':
+ n = va_arg(a, int);
+ buf[0] = n;
+ buf[1] = n >> 8;
+ Bwrite(&bout, buf, 2);
+ break;
+ case 'l':
+ n = va_arg(a, int);
+ buf[0] = n;
+ buf[1] = n >> 8;
+ buf[2] = n >> 16;
+ buf[3] = n >> 24;
+ Bwrite(&bout, buf, 4);
+ break;
+ case '[':
+ s = va_arg(a, char*);
+ n = va_arg(a, int);
+ if(n) Bwrite(&bout, s, n);
+ break;
+ }
+ }
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-Rr] [-u user] host\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+ uchar thumb[SHA2_256dlen], buf[MAX_TRANSMIT_BUFFER];
+ TLSconn tls;
+ char *user = "admin";
+ UserPasswd *up = nil;
+ int reply, ok, n;
+
+ fmtinstall('[', encodefmt);
+ fmtinstall('H', encodefmt);
+
+ ARGBEGIN {
+ case 'u':
+ user = EARGF(usage());
+ break;
+ case 'R':
+ raw = 0;
+ break;
+ case 'r':
+ raw = 1;
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if(argc != 1)
+ usage();
+
+ if(raw < 0) {
+ char *term = getenv("TERM");
+ raw = term && *term;
+ free(term);
+ }
+
+ if(raw){
+ close(0);
+ if(open("/dev/cons", OREAD) != 0)
+ sysfatal("open: %r");
+ close(1);
+ if(open("/dev/cons", OWRITE) != 1)
+ sysfatal("open: %r");
+ dup(1, 2);
+ fd = open("/dev/consctl", OWRITE);
+ if(fd >= 0)
+ write(fd, "rawon", 5);
+ }
+
+Reconnect:
+ fd = dial(netmkaddr(argv[0], "tcp", "16995"), nil, nil, nil);
+ if(fd < 0)
+ sysfatal("dial: %r");
+
+ memset(&tls, 0, sizeof(tls));
+ fd = tlsClient(fd, &tls);
+ if(fd < 0)
+ sysfatal("tls client: %r");
+ sha2_256(tls.cert, tls.certlen, buf, nil);
+ free(tls.cert);
+ free(tls.sessionID);
+ memset(&tls, 0, sizeof(tls));
+
+ if(up == nil){
+ memmove(thumb, buf, sizeof(thumb));
+ up = auth_getuserpasswd(auth_getkey,
+ "proto=pass service=sol user=%q server=%q thumb='%.*['",
+ user, argv[0], sizeof(thumb), thumb);
+ if(up == nil)
+ sysfatal("auth_getuserpasswd: %r");
+ close(fd);
+ goto Reconnect;
+ }
+
+ if(memcmp(buf, thumb, sizeof(thumb)) != 0)
+ sysfatal("certificate thumb changed: %.*[, expected %.*[",
+ sizeof(thumb), buf, sizeof(thumb), thumb);
+
+ Binit(&bin, fd, OREAD);
+ Binit(&bout, fd, OWRITE);
+ if(setjmp(reconnect)){
+ Bterm(&bin);
+ Bterm(&bout);
+ close(fd);
+ goto Reconnect;
+ }
+
+ send("l[", 0x10, "SOL ", 4);
+ recv("lb*", &reply, &ok);
+ if(reply != 0x11 || ok != 1)
+ sysfatal("bad session reply: %x %x", reply, ok);
+
+ send("lblb[b[", 0x13, 1,
+ strlen(up->user)+1+strlen(up->passwd)+1,
+ strlen(up->user), up->user, strlen(up->user),
+ strlen(up->passwd), up->passwd, strlen(up->passwd));
+ recv("lb*", &reply, &ok);
+ if(reply != 0x14 || ok != 1)
+ sysfatal("bad auth reply: %x %x", reply, ok);
+
+ send("l____wwwwww____", 0x20,
+ MAX_TRANSMIT_BUFFER,
+ TRANSMIT_BUFFER_TIMEOUT,
+ TRANSMIT_OVERFLOW_TIMEOUT,
+ HOST_SESSION_RX_TIMEOUT,
+ HOST_FIFO_RX_FLUSH_TIMEOUT,
+ HEATBEAT_INTERVAL);
+ recv("lb*", &reply, &ok);
+ if(reply != 0x21 || ok != 1)
+ sysfatal("bad redirection reply: %x %x", reply, ok);
+
+ pid = fork();
+ if(pid == 0){
+ pid = getppid();
+ atexit(killpid);
+ for(;;){
+ n = read(0, buf, sizeof(buf));
+ if(n < 0)
+ sysfatal("read: %r");
+ if(n == 0)
+ break;
+ send("l____w[", 0x28, n, buf, n);
+ }
+ } else {
+ atexit(killpid);
+ for(;;){
+ recv("l", &reply);
+ switch(reply){
+ default:
+ sysfatal("unknown reply %x\n", reply);
+ break;
+ case 0x2a:
+ recv("____w", &n);
+ recv("[", buf, n);
+ if(write(1, buf, n) != n)
+ break;
+ break;
+ case 0x24:
+ case 0x2b:
+ recv("*");
+ break;
+ }
+ }
+ }
+ exits(nil);
+}