From ca60e03c5bf96cff818857079335d1e6517080f7 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Fri, 1 Apr 2022 21:24:42 +0000 Subject: [PATCH] ip/sol: add intel AMT serial-over-lan console program --- sys/man/8/sol | 46 +++++++ sys/src/cmd/ip/mkfile | 1 + sys/src/cmd/ip/sol.c | 277 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 sys/man/8/sol create mode 100644 sys/src/cmd/ip/sol.c diff --git a/sys/man/8/sol b/sys/man/8/sol new file mode 100644 index 000000000..3da91db4a --- /dev/null +++ b/sys/man/8/sol @@ -0,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) diff --git a/sys/src/cmd/ip/mkfile b/sys/src/cmd/ip/mkfile index 894576fb1..0584668b0 100644 --- a/sys/src/cmd/ip/mkfile +++ b/sys/src/cmd/ip/mkfile @@ -27,6 +27,7 @@ TARG = 6in4\ torrent\ udpecho\ socksd\ + sol\ wol\ DIRS=ftpfs cifsd dhcpd httpd ipconfig ppp snoopy diff --git a/sys/src/cmd/ip/sol.c b/sys/src/cmd/ip/sol.c new file mode 100644 index 000000000..5d72321e0 --- /dev/null +++ b/sys/src/cmd/ip/sol.c @@ -0,0 +1,277 @@ +/* intel amt serial-over-lan console */ + +#include +#include +#include +#include +#include + +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); +}