ip/sol: add intel AMT serial-over-lan console program

This commit is contained in:
cinap_lenrek 2022-04-01 21:24:42 +00:00
parent 4daf4ffdbe
commit ca60e03c5b
3 changed files with 324 additions and 0 deletions

46
sys/man/8/sol Normal file
View file

@ -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)

View file

@ -27,6 +27,7 @@ TARG = 6in4\
torrent\ torrent\
udpecho\ udpecho\
socksd\ socksd\
sol\
wol\ wol\
DIRS=ftpfs cifsd dhcpd httpd ipconfig ppp snoopy DIRS=ftpfs cifsd dhcpd httpd ipconfig ppp snoopy

277
sys/src/cmd/ip/sol.c Normal file
View file

@ -0,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);
}