diff options
Diffstat (limited to 'crypto/openssh/regress/check-perm.c')
| -rw-r--r-- | crypto/openssh/regress/check-perm.c | 205 | 
1 files changed, 205 insertions, 0 deletions
| diff --git a/crypto/openssh/regress/check-perm.c b/crypto/openssh/regress/check-perm.c new file mode 100644 index 000000000000..dac307d24464 --- /dev/null +++ b/crypto/openssh/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; +} | 
