diff options
Diffstat (limited to '')
-rw-r--r-- | regress/check-perm.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/regress/check-perm.c b/regress/check-perm.c new file mode 100644 index 0000000..dac307d --- /dev/null +++ b/regress/check-perm.c @@ -0,0 +1,205 @@ +/* + * Placed in the public domain + */ + +/* $OpenBSD: modpipe.c,v 1.6 2013/11/21 03:16:47 djm Exp $ */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> +#include <pwd.h> +#ifdef HAVE_LIBGEN_H +#include <libgen.h> +#endif + +static void +fatal(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + va_end(args); + exit(1); +} +/* Based on session.c. NB. keep tests in sync */ +static void +safely_chroot(const char *path, uid_t uid) +{ + const char *cp; + char component[PATH_MAX]; + struct stat st; + + if (*path != '/') + fatal("chroot path does not begin at root"); + if (strlen(path) >= sizeof(component)) + fatal("chroot path too long"); + + /* + * Descend the path, checking that each component is a + * root-owned directory with strict permissions. + */ + for (cp = path; cp != NULL;) { + if ((cp = strchr(cp, '/')) == NULL) + strlcpy(component, path, sizeof(component)); + else { + cp++; + memcpy(component, path, cp - path); + component[cp - path] = '\0'; + } + + /* debug3("%s: checking '%s'", __func__, component); */ + + if (stat(component, &st) != 0) + fatal("%s: stat(\"%s\"): %s", __func__, + component, strerror(errno)); + if (st.st_uid != 0 || (st.st_mode & 022) != 0) + fatal("bad ownership or modes for chroot " + "directory %s\"%s\"", + cp == NULL ? "" : "component ", component); + if (!S_ISDIR(st.st_mode)) + fatal("chroot path %s\"%s\" is not a directory", + cp == NULL ? "" : "component ", component); + + } + + if (chdir(path) == -1) + fatal("Unable to chdir to chroot path \"%s\": " + "%s", path, strerror(errno)); +} + +/* from platform.c */ +int +platform_sys_dir_uid(uid_t uid) +{ + if (uid == 0) + return 1; +#ifdef PLATFORM_SYS_DIR_UID + if (uid == PLATFORM_SYS_DIR_UID) + return 1; +#endif + return 0; +} + +/* from auth.c */ +int +auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, + uid_t uid, char *err, size_t errlen) +{ + char buf[PATH_MAX], homedir[PATH_MAX]; + char *cp; + int comparehome = 0; + struct stat st; + + if (realpath(name, buf) == NULL) { + snprintf(err, errlen, "realpath %s failed: %s", name, + strerror(errno)); + return -1; + } + if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) + comparehome = 1; + + if (!S_ISREG(stp->st_mode)) { + snprintf(err, errlen, "%s is not a regular file", buf); + return -1; + } + if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || + (stp->st_mode & 022) != 0) { + snprintf(err, errlen, "bad ownership or modes for file %s", + buf); + return -1; + } + + /* for each component of the canonical path, walking upwards */ + for (;;) { + if ((cp = dirname(buf)) == NULL) { + snprintf(err, errlen, "dirname() failed"); + return -1; + } + strlcpy(buf, cp, sizeof(buf)); + + if (stat(buf, &st) < 0 || + (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || + (st.st_mode & 022) != 0) { + snprintf(err, errlen, + "bad ownership or modes for directory %s", buf); + return -1; + } + + /* If are past the homedir then we can stop */ + if (comparehome && strcmp(homedir, buf) == 0) + break; + + /* + * dirname should always complete with a "/" path, + * but we can be paranoid and check for "." too + */ + if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) + break; + } + return 0; +} + +static void +usage(void) +{ + fprintf(stderr, "check-perm -m [chroot | keys-command] [path]\n"); + exit(1); +} + +int +main(int argc, char **argv) +{ + const char *path = "."; + char errmsg[256]; + int ch, mode = -1; + extern char *optarg; + extern int optind; + struct stat st; + + while ((ch = getopt(argc, argv, "hm:")) != -1) { + switch (ch) { + case 'm': + if (strcasecmp(optarg, "chroot") == 0) + mode = 1; + else if (strcasecmp(optarg, "keys-command") == 0) + mode = 2; + else { + fprintf(stderr, "Invalid -m option\n"), + usage(); + } + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + else if (argc == 1) + path = argv[0]; + + if (mode == 1) + safely_chroot(path, getuid()); + else if (mode == 2) { + if (stat(path, &st) < 0) + fatal("Could not stat %s: %s", path, strerror(errno)); + if (auth_secure_path(path, &st, NULL, 0, + errmsg, sizeof(errmsg)) != 0) + fatal("Unsafe %s: %s", path, errmsg); + } else { + fprintf(stderr, "Invalid mode\n"); + usage(); + } + return 0; +} |