123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- /*
- * 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;
- }
|