mirror of
https://github.com/opnsense/src.git
synced 2026-06-11 09:41:03 -04:00
Allow secure-netboot
When doing file verification, tftp needs to be able to handle multiple open files concurrently. We also need tftp_stat() to provide useful values for st_dev and st_ino. Allow an architecture to define NETPROTO_DEFAULT. The default is NET_NFS for backwards compatability. In net_parse_rootpath() fix parsing of <scheme>://<ip>[:<port]/<path> and ensure we return INADDR_NONE unless we successfully parsed an addr, so we don't end up clobbering rootip obtained from bootp(). Sponsored by: Juniper Networks, Inc. Differential Revision: https://reviews.freebsd.org/D51187
This commit is contained in:
parent
b44cc1b479
commit
5bfb3045d2
7 changed files with 140 additions and 55 deletions
|
|
@ -66,6 +66,10 @@
|
|||
#include "dev_net.h"
|
||||
#include "bootstrap.h"
|
||||
|
||||
#ifndef NETPROTO_DEFAULT
|
||||
# define NETPROTO_DEFAULT NET_NFS
|
||||
#endif
|
||||
|
||||
static char *netdev_name;
|
||||
static int netdev_sock = -1;
|
||||
static int netdev_opens;
|
||||
|
|
@ -304,7 +308,7 @@ net_getparams(int sock)
|
|||
return (EIO);
|
||||
}
|
||||
exit:
|
||||
if ((rootaddr = net_parse_rootpath()) != INADDR_NONE)
|
||||
if ((rootaddr = net_parse_rootpath()) != htonl(INADDR_NONE))
|
||||
rootip.s_addr = rootaddr;
|
||||
|
||||
DEBUG_PRINTF(1,("%s: proto: %d\n", __func__, netproto));
|
||||
|
|
@ -355,7 +359,7 @@ is_tftp(void)
|
|||
* Parses the rootpath if present
|
||||
*
|
||||
* The rootpath format can be in the form
|
||||
* <scheme>://ip/path
|
||||
* <scheme>://ip[:port]/path
|
||||
* <scheme>:/path
|
||||
*
|
||||
* For compatibility with previous behaviour it also accepts as an NFS scheme
|
||||
|
|
@ -370,10 +374,10 @@ is_tftp(void)
|
|||
uint32_t
|
||||
net_parse_rootpath(void)
|
||||
{
|
||||
n_long addr = htonl(INADDR_NONE);
|
||||
n_long addr = 0;
|
||||
size_t i;
|
||||
char ip[FNAME_SIZE];
|
||||
char *ptr, *val;
|
||||
char *ptr, *portp, *val;
|
||||
|
||||
netproto = NET_NONE;
|
||||
|
||||
|
|
@ -388,7 +392,7 @@ net_parse_rootpath(void)
|
|||
ptr = rootpath;
|
||||
/* Fallback for compatibility mode */
|
||||
if (netproto == NET_NONE) {
|
||||
netproto = NET_NFS;
|
||||
netproto = NETPROTO_DEFAULT;
|
||||
(void)strsep(&ptr, ":");
|
||||
if (ptr != NULL) {
|
||||
addr = inet_addr(rootpath);
|
||||
|
|
@ -401,16 +405,21 @@ net_parse_rootpath(void)
|
|||
if (*ptr == '/') {
|
||||
/* we are in the form <scheme>://, we do expect an ip */
|
||||
ptr++;
|
||||
/*
|
||||
* XXX when http will be there we will need to check for
|
||||
* a port, but right now we do not need it yet
|
||||
*/
|
||||
portp = val = strchr(ptr, ':');
|
||||
if (val != NULL) {
|
||||
val++;
|
||||
rootport = strtol(val, NULL, 10);
|
||||
}
|
||||
val = strchr(ptr, '/');
|
||||
if (val != NULL) {
|
||||
if (portp == NULL)
|
||||
portp = val;
|
||||
snprintf(ip, sizeof(ip), "%.*s",
|
||||
(int)((uintptr_t)val - (uintptr_t)ptr),
|
||||
(int)(portp - ptr),
|
||||
ptr);
|
||||
addr = inet_addr(ip);
|
||||
DEBUG_PRINTF(1,("ip=%s addr=%#x\n",
|
||||
ip, addr));
|
||||
bcopy(val, rootpath, strlen(val) + 1);
|
||||
}
|
||||
} else {
|
||||
|
|
@ -418,6 +427,7 @@ net_parse_rootpath(void)
|
|||
bcopy(ptr, rootpath, strlen(ptr) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (addr == 0)
|
||||
addr = htonl(INADDR_NONE);
|
||||
return (addr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,6 +207,8 @@ LOADER_INTERP?=${LOADER_DEFAULT_INTERP}
|
|||
# Make sure we use the machine link we're about to create
|
||||
CFLAGS+=-I.
|
||||
|
||||
.include "${BOOTSRC}/veriexec.mk"
|
||||
|
||||
all: ${PROG}
|
||||
|
||||
CLEANFILES+= teken_state.h
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
u_char bcea[6] = BA; /* broadcast ethernet address */
|
||||
|
||||
char rootpath[FNAME_SIZE] = "/"; /* root mount path */
|
||||
int rootport; /* port for rootpath server */
|
||||
char bootfile[FNAME_SIZE]; /* bootp says to boot this */
|
||||
char hostname[FNAME_SIZE]; /* our hostname */
|
||||
int hostnamelen;
|
||||
|
|
|
|||
|
|
@ -781,6 +781,10 @@ The same as
|
|||
but for
|
||||
.Xr bzip2 1 Ns -compressed
|
||||
files.
|
||||
.It Va pkgfs_fsops
|
||||
File access from a tar file typically streamed via TFTP.
|
||||
The order of files in the tar file must match the order they are
|
||||
to be consumed as rewind is not practical.
|
||||
.El
|
||||
.Pp
|
||||
The array of
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ enum net_proto {
|
|||
|
||||
extern u_char bcea[6];
|
||||
extern char rootpath[FNAME_SIZE];
|
||||
extern int rootport;
|
||||
extern char bootfile[FNAME_SIZE];
|
||||
extern char hostname[FNAME_SIZE];
|
||||
extern int hostnamelen;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@
|
|||
#include <netinet/in_systm.h>
|
||||
#include <arpa/tftp.h>
|
||||
|
||||
#ifdef LOADER_VERIEXEC
|
||||
#include <verify_file.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "stand.h"
|
||||
|
|
@ -85,7 +89,6 @@ struct fs_ops tftp_fsops = {
|
|||
};
|
||||
|
||||
static int tftpport = 2000;
|
||||
static int is_open = 0;
|
||||
|
||||
/*
|
||||
* The legacy TFTP_BLKSIZE value was SEGSIZE(512).
|
||||
|
|
@ -99,10 +102,14 @@ static int is_open = 0;
|
|||
* Jumbo frames in the future.
|
||||
*/
|
||||
#define TFTP_MAX_BLKSIZE 9008
|
||||
#define TFTP_TRIES 2
|
||||
#define TFTP_TRIES 3
|
||||
|
||||
struct tftp_handle {
|
||||
struct iodesc *iodesc;
|
||||
struct iodesc io;
|
||||
int id;
|
||||
ino_t ino;
|
||||
int port;
|
||||
int currblock; /* contents of lastdata */
|
||||
unsigned int islastblock:1; /* flag */
|
||||
unsigned int tries:4; /* number of read attempts */
|
||||
|
|
@ -178,6 +185,9 @@ tftp_sendack(struct tftp_handle *h, u_short block)
|
|||
wbuf.t.th_block = htons(block);
|
||||
wtail += 2;
|
||||
|
||||
DEBUG_PRINTF(5,("%s: myport=%hu xid=%lu, block=%hu\n",
|
||||
__func__, h->iodesc->myport, h->iodesc->xid, block));
|
||||
|
||||
sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
|
||||
}
|
||||
|
||||
|
|
@ -191,6 +201,7 @@ recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft,
|
|||
void *ptr = NULL;
|
||||
ssize_t len;
|
||||
int tftp_error;
|
||||
unsigned short block;
|
||||
|
||||
errno = 0;
|
||||
extra = recv_extra;
|
||||
|
|
@ -204,19 +215,22 @@ recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft,
|
|||
}
|
||||
|
||||
extra->rtype = ntohs(t->th_opcode);
|
||||
switch (ntohs(t->th_opcode)) {
|
||||
block = ntohs(t->th_block);
|
||||
DEBUG_PRINTF(6,("%s: myport=%hu xid=%lu, block=%hu, opcode=%hu\n",
|
||||
__func__, d->myport, d->xid, block, extra->rtype));
|
||||
switch (extra->rtype) {
|
||||
case DATA: {
|
||||
int got;
|
||||
|
||||
if (htons(t->th_block) < (u_short)d->xid) {
|
||||
if (block < (u_short)d->xid) {
|
||||
/*
|
||||
* Apparently our ACK was missed, re-send.
|
||||
*/
|
||||
tftp_sendack(h, htons(t->th_block));
|
||||
tftp_sendack(h, block);
|
||||
free(ptr);
|
||||
return (-1);
|
||||
}
|
||||
if (htons(t->th_block) != (u_short)d->xid) {
|
||||
if (block != (u_short)d->xid) {
|
||||
/*
|
||||
* Packet from the future, drop this.
|
||||
*/
|
||||
|
|
@ -242,9 +256,7 @@ recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft,
|
|||
printf("illegal tftp error %d\n", tftp_error);
|
||||
errno = EIO;
|
||||
} else {
|
||||
#ifdef TFTP_DEBUG
|
||||
printf("tftp-error %d\n", tftp_error);
|
||||
#endif
|
||||
DEBUG_PRINTF(0, ("tftp-error %d\n", tftp_error));
|
||||
errno = tftperrors[tftp_error];
|
||||
}
|
||||
free(ptr);
|
||||
|
|
@ -285,9 +297,7 @@ recvtftp(struct iodesc *d, void **pkt, void **payload, time_t tleft,
|
|||
return (0);
|
||||
}
|
||||
default:
|
||||
#ifdef TFTP_DEBUG
|
||||
printf("tftp type %d not handled\n", ntohs(t->th_opcode));
|
||||
#endif
|
||||
DEBUG_PRINTF(0, ("tftp type %hu not handled\n", extra->rtype));
|
||||
free(ptr);
|
||||
return (-1);
|
||||
}
|
||||
|
|
@ -344,7 +354,7 @@ tftp_makereq(struct tftp_handle *h)
|
|||
bcopy("0", wtail, 2);
|
||||
wtail += 2;
|
||||
|
||||
h->iodesc->myport = htons(tftpport + (getsecs() & 0x3ff));
|
||||
h->iodesc->myport = htons(h->port + (getsecs() & 0x3ff));
|
||||
h->iodesc->destport = htons(IPPORT_TFTP);
|
||||
h->iodesc->xid = 1; /* expected block */
|
||||
|
||||
|
|
@ -352,11 +362,15 @@ tftp_makereq(struct tftp_handle *h)
|
|||
h->islastblock = 0;
|
||||
h->validsize = 0;
|
||||
|
||||
DEBUG_PRINTF(5,("%s: %s: id=%d port=%d myport=%hu xid=1\n",
|
||||
__func__, h->path, h->id, h->port, ntohs(h->iodesc->myport)));
|
||||
pkt = NULL;
|
||||
recv_extra.tftp_handle = h;
|
||||
res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *)&wbuf.t,
|
||||
&recvtftp, &pkt, (void **)&t, &recv_extra);
|
||||
if (res == -1) {
|
||||
DEBUG_PRINTF(3,("%s: %s: id=%d errno=%d\n",
|
||||
__func__, h->path, h->id, errno));
|
||||
free(pkt);
|
||||
return (errno);
|
||||
}
|
||||
|
|
@ -411,12 +425,18 @@ tftp_getnextblock(struct tftp_handle *h)
|
|||
|
||||
h->iodesc->xid = h->currblock + 1; /* expected block */
|
||||
|
||||
DEBUG_PRINTF(5,("%s: %s: id=%d port=%d myport=%hu xid=%lu\n",
|
||||
__func__, h->path, h->id, h->port,
|
||||
ntohs(h->iodesc->myport), h->iodesc->xid));
|
||||
|
||||
pkt = NULL;
|
||||
recv_extra.tftp_handle = h;
|
||||
res = sendrecv(h->iodesc, &sendudp, &wbuf.t, wtail - (char *)&wbuf.t,
|
||||
&recvtftp, &pkt, (void **)&t, &recv_extra);
|
||||
|
||||
if (res == -1) { /* 0 is OK! */
|
||||
DEBUG_PRINTF(3,("%s: %s: id=%d errno=%d\n",
|
||||
__func__, h->path, h->id, errno));
|
||||
free(pkt);
|
||||
return (errno);
|
||||
}
|
||||
|
|
@ -429,21 +449,32 @@ tftp_getnextblock(struct tftp_handle *h)
|
|||
if (res < h->tftp_blksize)
|
||||
h->islastblock = 1; /* EOF */
|
||||
|
||||
if (h->islastblock == 1) {
|
||||
DEBUG_PRINTF(5,("%s: %s: id=%d res=%d blksz=%d last=%d\n",
|
||||
__func__, h->path, h->id, res, h->tftp_blksize, h->islastblock));
|
||||
|
||||
if (h->islastblock) {
|
||||
/* Send an ACK for the last block */
|
||||
wbuf.t.th_block = htons((u_short)h->currblock);
|
||||
sendudp(h->iodesc, &wbuf.t, wtail - (char *)&wbuf.t);
|
||||
tftp_sendack(h, h->currblock);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* If doing verification we need to handle multiple
|
||||
* files at the same time.
|
||||
*/
|
||||
#define TOPEN_MAX 8
|
||||
static struct tftp_handle *handles[TOPEN_MAX];
|
||||
|
||||
static int
|
||||
tftp_open(const char *path, struct open_file *f)
|
||||
{
|
||||
struct devdesc *dev;
|
||||
struct tftp_handle *tftpfile;
|
||||
struct iodesc *io;
|
||||
static int lx = 0;
|
||||
int i, x;
|
||||
int res;
|
||||
size_t pathsize;
|
||||
const char *extraslash;
|
||||
|
|
@ -451,24 +482,39 @@ tftp_open(const char *path, struct open_file *f)
|
|||
if (netproto != NET_TFTP)
|
||||
return (EINVAL);
|
||||
|
||||
if (f->f_dev->dv_type != DEVT_NET)
|
||||
if (f->f_dev == NULL || f->f_dev->dv_type != DEVT_NET)
|
||||
return (EINVAL);
|
||||
|
||||
if (is_open)
|
||||
tftpfile = NULL;
|
||||
for (x = lx + 1, i = 0; i < TOPEN_MAX; i++, x++) {
|
||||
x %= TOPEN_MAX;
|
||||
if (handles[x] == NULL) {
|
||||
handles[x] = tftpfile = calloc(1, sizeof(*tftpfile));
|
||||
if (tftpfile == NULL)
|
||||
return (ENOMEM);
|
||||
/* id allows us to clear the slot on close */
|
||||
tftpfile->id = lx = x;
|
||||
/* port ensures a different session with server */
|
||||
tftpfile->port = (tftpport + (x * tftpport)) & 0xffff;
|
||||
DEBUG_PRINTF(1, ("%s(%s) id=%d port=%d\n",
|
||||
__func__, path, tftpfile->id, tftpfile->port));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tftpfile == NULL) {
|
||||
DEBUG_PRINTF(1, ("%s: EBUSY\n", __func__));
|
||||
return (EBUSY);
|
||||
|
||||
tftpfile = calloc(1, sizeof(*tftpfile));
|
||||
if (!tftpfile)
|
||||
return (ENOMEM);
|
||||
|
||||
}
|
||||
tftpfile->tftp_blksize = TFTP_REQUESTED_BLKSIZE;
|
||||
dev = f->f_devdata;
|
||||
tftpfile->iodesc = io = socktodesc(*(int *)(dev->d_opendata));
|
||||
io = socktodesc(*(int *)(dev->d_opendata));
|
||||
if (io == NULL) {
|
||||
free(tftpfile);
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
memcpy(&tftpfile->io, io, sizeof(tftpfile->io));
|
||||
io = tftpfile->iodesc = &tftpfile->io;
|
||||
io->destip = rootip;
|
||||
tftpfile->off = 0;
|
||||
pathsize = (strlen(rootpath) + 1 + strlen(path) + 1) * sizeof(char);
|
||||
|
|
@ -481,8 +527,11 @@ tftp_open(const char *path, struct open_file *f)
|
|||
extraslash = "";
|
||||
else
|
||||
extraslash = "/";
|
||||
res = snprintf(tftpfile->path, pathsize, "%s%s%s",
|
||||
rootpath, extraslash, path);
|
||||
if (rootpath[0] == '/' && rootpath[1] == '\0' && path[0] == '/')
|
||||
res = strlcpy(tftpfile->path, path, pathsize);
|
||||
else
|
||||
res = snprintf(tftpfile->path, pathsize, "%s%s%s",
|
||||
rootpath, extraslash, path);
|
||||
if (res < 0 || res > pathsize) {
|
||||
free(tftpfile->path);
|
||||
free(tftpfile);
|
||||
|
|
@ -492,13 +541,13 @@ tftp_open(const char *path, struct open_file *f)
|
|||
res = tftp_makereq(tftpfile);
|
||||
|
||||
if (res) {
|
||||
handles[tftpfile->id] = NULL;
|
||||
free(tftpfile->path);
|
||||
free(tftpfile->pkt);
|
||||
free(tftpfile);
|
||||
return (res);
|
||||
}
|
||||
f->f_fsdata = tftpfile;
|
||||
is_open = 1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
|
@ -548,9 +597,7 @@ tftp_read(struct open_file *f, void *addr, size_t size,
|
|||
|
||||
rc = tftp_getnextblock(tftpfile);
|
||||
if (rc) { /* no answer */
|
||||
#ifdef TFTP_DEBUG
|
||||
printf("tftp: read error\n");
|
||||
#endif
|
||||
DEBUG_PRINTF(0, ("tftp: read error\n"));
|
||||
if (tftpfile->tries > TFTP_TRIES) {
|
||||
return (rc);
|
||||
} else {
|
||||
|
|
@ -569,10 +616,8 @@ tftp_read(struct open_file *f, void *addr, size_t size,
|
|||
|
||||
inbuffer = tftpfile->validsize - offinblock;
|
||||
if (inbuffer < 0) {
|
||||
#ifdef TFTP_DEBUG
|
||||
printf("tftp: invalid offset %d\n",
|
||||
tftpfile->off);
|
||||
#endif
|
||||
DEBUG_PRINTF(0, ("tftp: invalid offset %d\n",
|
||||
tftpfile->off));
|
||||
return (EINVAL);
|
||||
}
|
||||
count = (size < inbuffer ? size : inbuffer);
|
||||
|
|
@ -587,15 +632,15 @@ tftp_read(struct open_file *f, void *addr, size_t size,
|
|||
if ((tftpfile->islastblock) && (count == inbuffer))
|
||||
break; /* EOF */
|
||||
} else {
|
||||
#ifdef TFTP_DEBUG
|
||||
printf("tftp: block %d not found\n", needblock);
|
||||
#endif
|
||||
DEBUG_PRINTF(0, ("tftp: block %d not found\n", needblock));
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
DEBUG_PRINTF(4, ("%s(%s) res=%ld\n", __func__, tftpfile->path,
|
||||
(tftpfile->tftp_tsize - tftpfile->off)));
|
||||
if (resid != NULL)
|
||||
*resid = res;
|
||||
return (rc);
|
||||
|
|
@ -611,15 +656,18 @@ tftp_close(struct open_file *f)
|
|||
tftp_senderr(tftpfile, 0, "No error: file closed");
|
||||
|
||||
if (tftpfile) {
|
||||
DEBUG_PRINTF(1, ("%s(%d): %s\n", __func__,
|
||||
tftpfile->id, tftpfile->path));
|
||||
handles[tftpfile->id] = NULL;
|
||||
free(tftpfile->path);
|
||||
free(tftpfile->pkt);
|
||||
free(tftpfile->tftp_cache);
|
||||
free(tftpfile);
|
||||
}
|
||||
is_open = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
tftp_stat(struct open_file *f, struct stat *sb)
|
||||
{
|
||||
|
|
@ -631,6 +679,29 @@ tftp_stat(struct open_file *f, struct stat *sb)
|
|||
sb->st_uid = 0;
|
||||
sb->st_gid = 0;
|
||||
sb->st_size = tftpfile->tftp_tsize;
|
||||
sb->st_mtime = 0;
|
||||
#ifdef LOADER_VERIEXEC
|
||||
/* libsecureboot needs st_dev and st_ino at minimum;
|
||||
* we need to fake something that will be close enough to
|
||||
* unique.
|
||||
*/
|
||||
sb->st_dev = (dev_t)tftpfile->iodesc->destip.s_addr;
|
||||
/* we don't want to compute this more than once */
|
||||
if (tftpfile->ino == 0) {
|
||||
union {
|
||||
unsigned char digest[SHA_DIGEST_LENGTH];
|
||||
ino_t ino;
|
||||
} u;
|
||||
|
||||
hash_string(tftpfile->path, 0, u.digest, sizeof(u.digest));
|
||||
|
||||
tftpfile->ino = u.ino & 0x7fffffff;
|
||||
DEBUG_PRINTF(2,("%s(%s) dev=%lu ino=%lu\n", __func__,
|
||||
tftpfile->path, (unsigned long)sb->st_dev,
|
||||
(unsigned long)tftpfile->ino));
|
||||
}
|
||||
sb->st_ino = tftpfile->ino;
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
|
@ -828,9 +899,7 @@ tftp_parse_oack(struct tftp_handle *h, char *buf, size_t len)
|
|||
return (-1);
|
||||
}
|
||||
|
||||
#ifdef TFTP_DEBUG
|
||||
printf("tftp_blksize: %u\n", h->tftp_blksize);
|
||||
printf("tftp_tsize: %lu\n", h->tftp_tsize);
|
||||
#endif
|
||||
DEBUG_PRINTF(2, ("tftp_blksize: %u\n", h->tftp_blksize));
|
||||
DEBUG_PRINTF(2, ("tftp_tsize: %lu\n", h->tftp_tsize));
|
||||
return (0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,8 +101,6 @@ SRCS+= interp_simple.c
|
|||
.error Unknown interpreter ${LOADER_INTERP}
|
||||
.endif
|
||||
|
||||
.include "${BOOTSRC}/veriexec.mk"
|
||||
|
||||
.if defined(BOOT_PROMPT_123)
|
||||
CFLAGS+= -DBOOT_PROMPT_123
|
||||
.endif
|
||||
|
|
|
|||
Loading…
Reference in a new issue