mirror of https://github.com/sudo-project/sudo.git
208 lines
5.3 KiB
C
208 lines
5.3 KiB
C
/*
|
|
* Copyright (c) 2010 Philip Guenther <guenther@openbsd.org>
|
|
*
|
|
* Public domain.
|
|
*
|
|
* Verify that mkdtemp() and mkstemps() doesn't overrun or underrun
|
|
* the template buffer and that it can generate names that don't
|
|
* contain any X's
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
#define SUDO_ERROR_WRAP 0
|
|
|
|
#include <sudo_compat.h>
|
|
#include <sudo_util.h>
|
|
#include <sudo_fatal.h>
|
|
|
|
#ifndef MAP_ANON
|
|
# if defined(MAP_ANONYMOUS)
|
|
# define MAP_ANON MAP_ANONYMOUS
|
|
# endif
|
|
#endif
|
|
|
|
#ifndef MAP_FAILED
|
|
# define MAP_FAILED ((void *) -1)
|
|
#endif
|
|
|
|
#define MAX_TEMPLATE_LEN 10
|
|
#define MAX_TRIES 100
|
|
#define MIN_Xs 6
|
|
|
|
#define SUFFIX ".suff"
|
|
#define SLEN (sizeof SUFFIX - 1)
|
|
|
|
sudo_dso_public int main(int argc, char *argv[]);
|
|
|
|
/*
|
|
* verify that a path generated by mkdtemp() or mkstemp() looks like a
|
|
* reasonable expansion of the template and matches the fd. Returns true
|
|
* if all the X's were replaced with non-X's
|
|
*/
|
|
static int
|
|
check(int fd, char const *kind, char const *path, char const *prefix,
|
|
size_t plen, char const *suffix, size_t slen, size_t tlen)
|
|
{
|
|
struct stat sb, fsb;
|
|
char const *p;
|
|
|
|
if (tlen < MIN_Xs) {
|
|
if (fd != -1)
|
|
sudo_fatalx("%s(%s) succeed with too few Xs", kind, path);
|
|
if (errno != EINVAL)
|
|
sudo_fatal("%s(%s) failed with wrong errno: %d", kind, path, errno);
|
|
return 1;
|
|
}
|
|
if (fd == -1)
|
|
sudo_fatal("%s(%s)", kind, path);
|
|
if (stat(path, &sb))
|
|
sudo_fatal("%s: stat(%s)", kind, path);
|
|
if (fd >= 0) {
|
|
if (fstat(fd, &fsb))
|
|
sudo_fatal("%s: fstat(%d==%s)", kind, fd, path);
|
|
if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino)
|
|
sudo_fatalx("%s: stat mismatch", kind);
|
|
}
|
|
if (memcmp(path, prefix, plen) != 0)
|
|
sudo_fatalx("%s: prefix changed! %s vs %s", kind, prefix, path);
|
|
if (memcmp(path + plen + tlen, suffix, slen + 1) != 0)
|
|
sudo_fatalx("%s: suffix changed! %s vs %s", kind, suffix, path);
|
|
for (p = path + plen; p < path + plen + tlen; p++)
|
|
if (*p == '\0')
|
|
sudo_fatalx("%s: unexpected truncation", kind);
|
|
else if (*p == 'X')
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
try_mkdtemp(char *p, char const *prefix, size_t len)
|
|
{
|
|
size_t plen = strlen(prefix);
|
|
int fd, tries, ok;
|
|
|
|
for (tries = 0; tries < MAX_TRIES; tries++) {
|
|
memcpy(p, prefix, plen);
|
|
memset(p + plen, 'X', len);
|
|
p[plen + len] = '\0';
|
|
fd = mkdtemp(p) ? -2 : -1;
|
|
ok = check(fd, "mkdtemp", p, prefix, plen, "", 0, len);
|
|
rmdir(p);
|
|
if (ok)
|
|
return;
|
|
}
|
|
sudo_fatalx("mkdtemp: exceeded MAX_TRIES");
|
|
}
|
|
|
|
static void
|
|
try_mkstemps(char *p, char const *prefix, size_t len, char const *suffix)
|
|
{
|
|
size_t plen = strlen(prefix);
|
|
size_t slen = strlen(suffix);
|
|
int tries, fd, ok;
|
|
|
|
for (tries = 0; tries < MAX_TRIES; tries++) {
|
|
memcpy(p, prefix, plen);
|
|
memset(p + plen, 'X', len);
|
|
memcpy(p + plen + len, suffix, slen + 1);
|
|
fd = mkstemps(p, (int)slen);
|
|
ok = check(fd, "mkstemp", p, prefix, plen, suffix, slen, len);
|
|
close(fd);
|
|
unlink(p);
|
|
if (ok)
|
|
return;
|
|
}
|
|
sudo_fatalx("mkstemps: exceeded MAX_TRIES");
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char cwd[PATH_MAX + 1];
|
|
char *p;
|
|
size_t clen, i;
|
|
size_t pg;
|
|
int ch;
|
|
|
|
initprogname(argc > 0 ? argv[0] : "mktemp_test");
|
|
|
|
while ((ch = getopt(argc, argv, "v")) != -1) {
|
|
switch (ch) {
|
|
case 'v':
|
|
/* ignore */
|
|
break;
|
|
default:
|
|
fprintf(stderr, "usage: %s [-v]\n", getprogname());
|
|
return EXIT_FAILURE;
|
|
}
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
pg = (size_t)sysconf(_SC_PAGESIZE);
|
|
if (pg == (size_t)-1)
|
|
sudo_fatal("sysconf(_SC_PAGESIZE)");
|
|
if (getcwd(cwd, sizeof cwd - 1) == NULL)
|
|
sudo_fatal("getcwd");
|
|
clen = strlen(cwd);
|
|
cwd[clen++] = '/';
|
|
cwd[clen] = '\0';
|
|
#ifdef MAP_ANON
|
|
p = mmap(NULL, pg * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
|
|
#else
|
|
ch = open("/dev/zero", O_RDWR);
|
|
if (ch == -1)
|
|
sudo_fatal("/dev/zero");
|
|
p = mmap(NULL, pg * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE, ch, 0);
|
|
#endif
|
|
if (p == MAP_FAILED)
|
|
sudo_fatal("mmap");
|
|
if (mprotect(p, pg, PROT_NONE) || mprotect(p + pg * 2, pg, PROT_NONE))
|
|
sudo_fatal("mprotect");
|
|
p += pg;
|
|
|
|
for (i = MAX_TEMPLATE_LEN; i != 0; i--) {
|
|
/* try first at the start of a page, no prefix */
|
|
try_mkdtemp(p, "", i);
|
|
/* now at the end of the page, no prefix */
|
|
try_mkdtemp(p + pg - i - 1, "", i);
|
|
/* start of the page, prefixed with the cwd */
|
|
try_mkdtemp(p, cwd, i);
|
|
/* how about at the end of the page, prefixed with cwd? */
|
|
try_mkdtemp(p + pg - clen - i - 1, cwd, i);
|
|
|
|
/* again, with mkstemps() and an empty suffix */
|
|
/* try first at the start of a page, no prefix */
|
|
try_mkstemps(p, "", i, "");
|
|
/* now at the end of the page, no prefix */
|
|
try_mkstemps(p + pg - i - 1, "", i, "");
|
|
/* start of the page, prefixed with the cwd */
|
|
try_mkstemps(p, cwd, i, "");
|
|
/* how about at the end of the page, prefixed with cwd? */
|
|
try_mkstemps(p + pg - clen - i - 1, cwd, i, "");
|
|
|
|
/* mkstemps() and a non-empty suffix */
|
|
/* try first at the start of a page, no prefix */
|
|
try_mkstemps(p, "", i, SUFFIX);
|
|
/* now at the end of the page, no prefix */
|
|
try_mkstemps(p + pg - i - SLEN - 1, "", i, SUFFIX);
|
|
/* start of the page, prefixed with the cwd */
|
|
try_mkstemps(p, cwd, i, SUFFIX);
|
|
/* how about at the end of the page, prefixed with cwd? */
|
|
try_mkstemps(p + pg - clen - i - SLEN - 1, cwd, i, SUFFIX);
|
|
}
|
|
|
|
return 0;
|
|
}
|