Adding a chroot-directory option per key in authorized_keys file

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

 



I'm trying to create a temporary sftp "inbox", so users can share
files more easily.
To do that I want the sender to generate a temporary key pair, send me
the public key securely (perhaps over TLS or a trusted third party),
then I can add a line in authorized_keys like this:

restrict,command="internal-sftp",chroot-directory="/run/ssh-inbox/1000/05b475...a592b2"
ssh-rsa AAAAB3NzaC...kIQX3jyJ2oM=

Which allows only sftp access to the following key, chrooted to the
given directory (which is owned by root, created by a daemon/suid
binary/etc), which is /run/ssh-inbox/<UID>/<SHA256(pubkey)>/
My patch verifies that the key has restrict and
command="internal-sftp" set before accepting the key.

I tried to stick to the surrounding code style as much as I could, let
me know if i need to fix anything.

Thanks,
- David
diff --git a/.gitignore b/.gitignore
index 34a95721..6f227a7f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ survey.sh
 **/*.out
 **/*.a
 autom4te.cache/
+.idea/
 scp
 sftp
 sftp-server
diff --git a/auth-options.c b/auth-options.c
index 2d200944..7a9515a4 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -135,6 +135,22 @@ cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob,
 				opts->force_command = command;
 				found = 1;
 			}
+			if (strcmp(name, "chroot-directory") == 0) {
+				if ((r = sshbuf_get_cstring(data, &command,
+				    NULL)) != 0) {
+					error("Unable to parse \"%s\" "
+					    "section: %s", name, ssh_err(r));
+					goto out;
+				}
+				if (opts->chroot_directory != NULL) {
+					error("Certificate has multiple "
+					    "chroot-directory options");
+					free(command);
+					goto out;
+				}
+				opts->chroot_directory = command;
+				found = 1;
+			}
 			if (strcmp(name, "source-address") == 0) {
 				if ((r = sshbuf_get_cstring(data, &allowed,
 				    NULL)) != 0) {
@@ -207,6 +223,7 @@ sshauthopt_free(struct sshauthopt *opts)
 
 	free(opts->cert_principals);
 	free(opts->force_command);
+	free(opts->chroot_directory);
 	free(opts->required_from_host_cert);
 	free(opts->required_from_host_keys);
 
@@ -364,6 +381,14 @@ sshauthopt_parse(const char *opts, const char **errstrp)
 			ret->force_command = opt_dequote(&opts, &errstr);
 			if (ret->force_command == NULL)
 				goto fail;
+		} else if (opt_match(&opts, "chroot-directory")) {
+			if (ret->chroot_directory != NULL) {
+				errstr = "multiple \"chroot-directory\" clauses";
+				goto fail;
+			}
+			ret->chroot_directory = opt_dequote(&opts, &errstr);
+			if (ret->chroot_directory == NULL)
+				goto fail;
 		} else if (opt_match(&opts, "principals")) {
 			if (ret->cert_principals != NULL) {
 				errstr = "multiple \"principals\" clauses";
@@ -614,6 +639,32 @@ sshauthopt_merge(const struct sshauthopt *primary,
 		    additional->force_command)) == NULL)
 			goto alloc_fail;
 	}
+
+	/*
+	 * When both multiple chroot-directory are specified, only
+	 * proceed if they are identical, otherwise fail.
+	 */
+	if (primary->chroot_directory != NULL &&
+	    additional->chroot_directory != NULL) {
+		if (strcmp(primary->chroot_directory,
+		    additional->chroot_directory) == 0) {
+			/* ok */
+			ret->chroot_directory = strdup(primary->chroot_directory);
+			if (ret->chroot_directory == NULL)
+				goto alloc_fail;
+		} else {
+			errstr = "chroot directory options do not match";
+			goto fail;
+		}
+	} else if (primary->chroot_directory != NULL) {
+		if ((ret->chroot_directory = strdup(
+		    primary->chroot_directory)) == NULL)
+			goto alloc_fail;
+	} else if (additional->chroot_directory != NULL) {
+		if ((ret->chroot_directory = strdup(
+		    additional->chroot_directory)) == NULL)
+			goto alloc_fail;
+	}
 	/* success */
 	if (errstrp != NULL)
 		*errstrp = NULL;
@@ -660,6 +711,7 @@ sshauthopt_copy(const struct sshauthopt *orig)
 	} while (0)
 	OPTSTRING(cert_principals);
 	OPTSTRING(force_command);
+	OPTSTRING(chroot_directory);
 	OPTSTRING(required_from_host_cert);
 	OPTSTRING(required_from_host_keys);
 #undef OPTSTRING
@@ -799,6 +851,8 @@ sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m,
 	    (r = serialise_nullable_string(m,
 	    untrusted ? "true" : opts->force_command)) != 0 ||
 	    (r = serialise_nullable_string(m,
+	    untrusted ? "true" : opts->chroot_directory)) != 0 ||
+	    (r = serialise_nullable_string(m,
 	    untrusted ? NULL : opts->required_from_host_cert)) != 0 ||
 	    (r = serialise_nullable_string(m,
 	     untrusted ? NULL : opts->required_from_host_keys)) != 0)
diff --git a/auth-options.h b/auth-options.h
index d96ffede..12d4bdd6 100644
--- a/auth-options.h
+++ b/auth-options.h
@@ -49,6 +49,7 @@ struct sshauthopt {
 
 	int force_tun_device;
 	char *force_command;
+	char *chroot_directory;
 
 	/* Custom environment */
 	size_t nenv;
diff --git a/auth.c b/auth.c
index 086b8ebb..b5e09edc 100644
--- a/auth.c
+++ b/auth.c
@@ -1006,9 +1006,10 @@ auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote)
 
 	snprintf(buf, sizeof(buf), "%d", opts->force_tun_device);
 	/* Try to keep this alphabetically sorted */
-	snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+	snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
 	    opts->permit_agent_forwarding_flag ? " agent-forwarding" : "",
 	    opts->force_command == NULL ? "" : " command",
+	    opts->chroot_directory == NULL ? "" : " chroot-directory",
 	    do_env ?  " environment" : "",
 	    opts->valid_before == 0 ? "" : "expires",
 	    do_permitopen ?  " permitopen" : "",
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index 815ea0f2..25577f8f 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -611,6 +611,20 @@ check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
 		auth_debug_add("%s: bad key options: %s", loc, reason);
 		goto out;
 	}
+	/* Allowing users to have a shell which is chrooted may allow them to escape
+	 * with SUID binaries
+	 */
+	if (keyopts->chroot_directory &&
+		!(keyopts->restricted
+		    && keyopts->force_command
+		    && strcmp(keyopts->force_command, INTERNAL_SFTP_NAME) == 0)) {
+		debug("%s: cannot use 'chroot-directory' without 'restrict' and "
+		    "'command=\"" INTERNAL_SFTP_NAME "\"'", loc);
+		auth_debug_add("%s: cannot use 'chroot-directory' without "
+		    "'restrict' and 'command=\"" INTERNAL_SFTP_NAME "\"'", loc);
+		goto out;
+	}
+
 	/* Ignore keys that don't match or incorrectly marked as CAs */
 	if (sshkey_is_cert(key)) {
 		/* Certificate; check signature key against CA */
diff --git a/session.c b/session.c
index 8c0e54f7..4ad7eb18 100644
--- a/session.c
+++ b/session.c
@@ -1362,7 +1362,7 @@ safely_chroot(const char *path, uid_t uid)
 void
 do_setusercontext(struct passwd *pw)
 {
-	char uidstr[32], *chroot_path, *tmp;
+	char uidstr[32], *chroot_path, *tmp, *chroot_directory = NULL;
 
 	platform_setusercontext(pw);
 
@@ -1390,9 +1390,15 @@ do_setusercontext(struct passwd *pw)
 
 		platform_setusercontext_post_groups(pw);
 
-		if (!in_chroot && options.chroot_directory != NULL &&
-		    strcasecmp(options.chroot_directory, "none") != 0) {
-                        tmp = tilde_expand_filename(options.chroot_directory,
+		if (options.chroot_directory) {
+			chroot_directory = options.chroot_directory;
+		} else if (auth_opts->chroot_directory != NULL) {
+			chroot_directory = auth_opts->chroot_directory;
+		}
+
+		if (!in_chroot && chroot_directory != NULL &&
+		    strcasecmp(chroot_directory, "none") != 0) {
+                        tmp = tilde_expand_filename(chroot_directory,
 			    pw->pw_uid);
 			snprintf(uidstr, sizeof(uidstr), "%llu",
 			    (unsigned long long)pw->pw_uid);
@@ -1404,6 +1410,8 @@ do_setusercontext(struct passwd *pw)
 			/* Make sure we don't attempt to chroot again */
 			free(options.chroot_directory);
 			options.chroot_directory = NULL;
+			free(auth_opts->chroot_directory);
+			auth_opts->chroot_directory = NULL;
 			in_chroot = 1;
 		}
 
_______________________________________________
openssh-unix-dev mailing list
openssh-unix-dev@xxxxxxxxxxx
https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev

[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux