The branch main has been updated by sjg:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=5bfb3045d25b3e097f1e55fdc5b3f929f2b7da55

commit 5bfb3045d25b3e097f1e55fdc5b3f929f2b7da55
Author:     Simon J. Gerraty <s...@freebsd.org>
AuthorDate: 2025-08-20 22:49:53 +0000
Commit:     Simon J. Gerraty <s...@freebsd.org>
CommitDate: 2025-08-20 22:49:53 +0000

    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
---
 stand/common/dev_net.c |  32 +++++++----
 stand/defs.mk          |   2 +
 stand/libsa/globals.c  |   1 +
 stand/libsa/libsa.3    |   4 ++
 stand/libsa/net.h      |   1 +
 stand/libsa/tftp.c     | 153 +++++++++++++++++++++++++++++++++++--------------
 stand/loader.mk        |   2 -
 7 files changed, 140 insertions(+), 55 deletions(-)

diff --git a/stand/common/dev_net.c b/stand/common/dev_net.c
index 10389db27b99..d1c48d40691a 100644
--- a/stand/common/dev_net.c
+++ b/stand/common/dev_net.c
@@ -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);
 }
diff --git a/stand/defs.mk b/stand/defs.mk
index 8ef84267b198..eb4133b604eb 100644
--- a/stand/defs.mk
+++ b/stand/defs.mk
@@ -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
diff --git a/stand/libsa/globals.c b/stand/libsa/globals.c
index 2797045d4faf..6bd3a4243d73 100644
--- a/stand/libsa/globals.c
+++ b/stand/libsa/globals.c
@@ -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;
diff --git a/stand/libsa/libsa.3 b/stand/libsa/libsa.3
index 3e3f70610516..0947f97a0a1f 100644
--- a/stand/libsa/libsa.3
+++ b/stand/libsa/libsa.3
@@ -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
diff --git a/stand/libsa/net.h b/stand/libsa/net.h
index d4823d88f58b..945b6b9ea45f 100644
--- a/stand/libsa/net.h
+++ b/stand/libsa/net.h
@@ -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;
diff --git a/stand/libsa/tftp.c b/stand/libsa/tftp.c
index 0584246a6dea..656c402683bb 100644
--- a/stand/libsa/tftp.c
+++ b/stand/libsa/tftp.c
@@ -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);
 }
diff --git a/stand/loader.mk b/stand/loader.mk
index 0f2ff31a5343..4073e523e552 100644
--- a/stand/loader.mk
+++ b/stand/loader.mk
@@ -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

Reply via email to