shithub: riscv

Download patch

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);
+}