ip/sol: add intel AMT serial-over-lan console program
This commit is contained in:
parent
4daf4ffdbe
commit
ca60e03c5b
3 changed files with 324 additions and 0 deletions
46
sys/man/8/sol
Normal file
46
sys/man/8/sol
Normal 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)
|
|
@ -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
277
sys/src/cmd/ip/sol.c
Normal 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);
|
||||||
|
}
|
Loading…
Reference in a new issue