bhyveload: use a dirfd to support -h

Don't allow lookups from the loader scripts, which in rare cases may be
in guest control depending on the setup, to leave the specified host
root.  Open the root dir and strictly do RESOLVE_BENEATH lookups from
there.

cb_open() has been restructured a bit to work nicely with this, using
fdopendir() in the directory case and just using the fd we already
opened in the regular file case.

hostbase_open() was split out to provide an obvious place to apply
rights(4) if that's something we care to do.

Reviewed by:	allanjude (earlier version), markj
Differential Revision:	https://reviews.freebsd.org/D43284
This commit is contained in:
Kyle Evans 2024-01-03 16:17:59 -06:00
parent ee7d5ba1b5
commit 6779d44bd8
1 changed files with 57 additions and 27 deletions

View File

@ -88,11 +88,11 @@
#define NDISKS 32
static char *host_base;
static struct termios term, oldterm;
static int disk_fd[NDISKS];
static int ndisks;
static int consin_fd, consout_fd;
static int hostbase_fd = -1;
static int need_reinit;
@ -159,42 +159,61 @@ static int
cb_open(void *arg __unused, const char *filename, void **hp)
{
struct cb_file *cf;
char path[PATH_MAX];
struct stat sb;
int fd, flags;
if (!host_base)
cf = NULL;
fd = -1;
flags = O_RDONLY | O_RESOLVE_BENEATH;
if (hostbase_fd == -1)
return (ENOENT);
strlcpy(path, host_base, PATH_MAX);
if (path[strlen(path) - 1] == '/')
path[strlen(path) - 1] = 0;
strlcat(path, filename, PATH_MAX);
cf = malloc(sizeof(struct cb_file));
if (stat(path, &cf->cf_stat) < 0) {
free(cf);
/* Absolute paths are relative to our hostbase, chop off leading /. */
if (filename[0] == '/')
filename++;
/* Lookup of /, use . instead. */
if (filename[0] == '\0')
filename = ".";
if (fstatat(hostbase_fd, filename, &sb, AT_RESOLVE_BENEATH) < 0)
return (errno);
if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode))
return (EINVAL);
if (S_ISDIR(sb.st_mode))
flags |= O_DIRECTORY;
/* May be opening the root dir */
fd = openat(hostbase_fd, filename, flags);
if (fd < 0)
return (errno);
cf = malloc(sizeof(struct cb_file));
if (cf == NULL) {
close(fd);
return (ENOMEM);
}
cf->cf_stat = sb;
cf->cf_size = cf->cf_stat.st_size;
if (S_ISDIR(cf->cf_stat.st_mode)) {
cf->cf_isdir = 1;
cf->cf_u.dir = opendir(path);
if (!cf->cf_u.dir)
goto out;
*hp = cf;
return (0);
}
if (S_ISREG(cf->cf_stat.st_mode)) {
cf->cf_u.dir = fdopendir(fd);
if (cf->cf_u.dir == NULL) {
close(fd);
free(cf);
return (ENOMEM);
}
} else {
assert(S_ISREG(cf->cf_stat.st_mode));
cf->cf_isdir = 0;
cf->cf_u.fd = open(path, O_RDONLY);
if (cf->cf_u.fd < 0)
goto out;
*hp = cf;
return (0);
cf->cf_u.fd = fd;
}
out:
free(cf);
return (EINVAL);
*hp = cf;
return (0);
}
static int
@ -714,6 +733,17 @@ usage(void)
exit(1);
}
static void
hostbase_open(const char *base)
{
if (hostbase_fd != -1)
close(hostbase_fd);
hostbase_fd = open(base, O_DIRECTORY | O_PATH);
if (hostbase_fd == -1)
err(EX_OSERR, "open");
}
int
main(int argc, char** argv)
{
@ -748,7 +778,7 @@ main(int argc, char** argv)
break;
case 'h':
host_base = optarg;
hostbase_open(optarg);
break;
case 'l':