Vishal Mishra 7bfe72
--- a/session.c
Vishal Mishra 7bfe72
+++ b/session.c
Vishal Mishra 7bfe72
@@ -98,6 +98,7 @@
Vishal Mishra 7bfe72
 #include "atomicio.h"
Vishal Mishra 7bfe72
 #include "slog.h"
Vishal Mishra 7bfe72
 
Vishal Mishra 7bfe72
+#define SSH_MAX_PUBKEY_BYTES 16384
Vishal Mishra 7bfe72
 
Vishal Mishra 7bfe72
 #if defined(KRB5) && defined(USE_AFS)
Vishal Mishra 7bfe72
 #include <kafs.h>
Vishal Mishra 7bfe72
@@ -1054,11 +1055,18 @@ copy_environment(char **source, char ***
Vishal Mishra 7bfe72
 static char **
Vishal Mishra 7bfe72
 do_setup_env(struct ssh *ssh, Session *s, const char *shell)
Vishal Mishra 7bfe72
 {
Vishal Mishra 7bfe72
-	char buf[256];
Vishal Mishra 7bfe72
+	char buf[SSH_MAX_PUBKEY_BYTES];
Vishal Mishra 7bfe72
+	char *pbuf = &buf[0];
Vishal Mishra 7bfe72
 	size_t n;
Vishal Mishra 7bfe72
 	u_int i, envsize;
Vishal Mishra 7bfe72
 	char *ocp, *cp, *value, **env, *laddr;
Vishal Mishra 7bfe72
 	struct passwd *pw = s->pw;
Vishal Mishra 7bfe72
+	Authctxt *authctxt = s->authctxt;
Vishal Mishra 7bfe72
+	struct sshkey *key;
Vishal Mishra 7bfe72
+	size_t len = 0;
Vishal Mishra 7bfe72
+	ssize_t total = 0;
Vishal Mishra 7bfe72
+	struct sshkey_cert *cert;
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
 #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)
Vishal Mishra 7bfe72
 	char *path = NULL;
Vishal Mishra 7bfe72
 #endif
Vishal Mishra 7bfe72
@@ -1255,9 +1263,57 @@ do_setup_env(struct ssh *ssh, Session *s
Vishal Mishra 7bfe72
 		child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file);
Vishal Mishra 7bfe72
 	if (s->ttyfd != -1)
Vishal Mishra 7bfe72
 		child_set_env(&env, &envsize, "SSH_TTY", s->tty);
Vishal Mishra 7bfe72
-	if (original_command)
Vishal Mishra 7bfe72
+	if (original_command) {
Vishal Mishra 7bfe72
 		child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",
Vishal Mishra 7bfe72
 		    original_command);
Vishal Mishra 7bfe72
+		/*
Vishal Mishra 7bfe72
+		* Set SSH_CERT_PRINCIPALS to be the principals on the ssh certificate.
Vishal Mishra 7bfe72
+		* Only do so when a force command is present to prevent the client
Vishal Mishra 7bfe72
+		* from changing the value of SSH_CERT_PRINCIPALS. For example, when a
Vishal Mishra 7bfe72
+		* client is given shell access, the client can easily change the
Vishal Mishra 7bfe72
+		* value of an environment variable by running, e.g.,
Vishal Mishra 7bfe72
+		* ssh user@host.address 'SSH_CERT_PRINCIPALS=attacker env'
Vishal Mishra 7bfe72
+		*/
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+		if (authctxt->nprev_keys > 0) {
Vishal Mishra 7bfe72
+			key = authctxt->prev_keys[authctxt->nprev_keys-1];
Vishal Mishra 7bfe72
+			/* If a user was authorized by a certificate, set SSH_CERT_PRINCIPALS */
Vishal Mishra 7bfe72
+			if (sshkey_is_cert(key)) {
Vishal Mishra 7bfe72
+				cert = key->cert;
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+				for (i = 0; i < cert->nprincipals - 1; ++i) {
Vishal Mishra 7bfe72
+					/*
Vishal Mishra 7bfe72
+					* total: bytes written to buf so far
Vishal Mishra 7bfe72
+					* 2: one for comma and one for '\0' to be added by snprintf
Vishal Mishra 7bfe72
+					* We stop at the first principal overflowing buf.
Vishal Mishra 7bfe72
+					*/
Vishal Mishra 7bfe72
+					if (total + strlen(cert->principals[i]) + 2 > SSH_MAX_PUBKEY_BYTES)
Vishal Mishra 7bfe72
+						break;
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+					len = snprintf(pbuf, SSH_MAX_PUBKEY_BYTES-total, "%s,",
Vishal Mishra 7bfe72
+					    cert->principals[i]);
Vishal Mishra 7bfe72
+					/* pbuf advances by len, the '\0' at the end will be overwritten */
Vishal Mishra 7bfe72
+					pbuf += len;
Vishal Mishra 7bfe72
+					total += len;
Vishal Mishra 7bfe72
+				}
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+				if (total + strlen(cert->principals[i]) + 1 <= SSH_MAX_PUBKEY_BYTES) {
Vishal Mishra 7bfe72
+					len = snprintf(pbuf, SSH_MAX_PUBKEY_BYTES-total, "%s",
Vishal Mishra 7bfe72
+					    cert->principals[i]);
Vishal Mishra 7bfe72
+					total += len;
Vishal Mishra 7bfe72
+				} else if (total > 0)
Vishal Mishra 7bfe72
+					/*
Vishal Mishra 7bfe72
+					* If we hit the overflow condition, remove the trailing comma.
Vishal Mishra 7bfe72
+					* We only do so if the overflowing principal is not the first one on the
Vishal Mishra 7bfe72
+					* certificate so that there is at least one principal in buf
Vishal Mishra 7bfe72
+					*/
Vishal Mishra 7bfe72
+					buf[total-1] = '\0';
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+				if (total > 0)
Vishal Mishra 7bfe72
+					child_set_env(&env, &envsize, "SSH_CERT_PRINCIPALS", buf);
Vishal Mishra 7bfe72
+			}
Vishal Mishra 7bfe72
+		}
Vishal Mishra 7bfe72
+	}
Vishal Mishra 7bfe72
 
Vishal Mishra 7bfe72
   /* set LOG_SESSION_ID for child */
Vishal Mishra 7bfe72
   child_set_env(&env, &envsize, "LOG_SESSION_ID", get_log_session_id());
Vishal Mishra 7bfe72
--- /dev/null
Vishal Mishra 7bfe72
+++ b/regress/cert-princ-env.sh
Vishal Mishra 7bfe72
@@ -0,0 +1,129 @@
Vishal Mishra 7bfe72
+tid="cert principal env"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+# change to ecdsa
Vishal Mishra 7bfe72
+CERT_ID="$USER"
Vishal Mishra 7bfe72
+AUTH_PRINC_FILE="$OBJ/auth_principals"
Vishal Mishra 7bfe72
+CA_FILE="$OBJ/ca-rsa"
Vishal Mishra 7bfe72
+IDENTITY_FILE="$OBJ/$USER-rsa"
Vishal Mishra 7bfe72
+SSH_MAX_PUBKEY_BYTES=16384
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+cat << EOF >> $OBJ/sshd_config
Vishal Mishra 7bfe72
+TrustedUserCAKeys $CA_FILE.pub
Vishal Mishra 7bfe72
+Protocol 2
Vishal Mishra 7bfe72
+PubkeyAuthentication yes
Vishal Mishra 7bfe72
+AuthenticationMethods publickey
Vishal Mishra 7bfe72
+AuthorizedPrincipalsFile $AUTH_PRINC_FILE
Vishal Mishra 7bfe72
+ForceCommand=/bin/env
Vishal Mishra 7bfe72
+EOF
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+cleanup() {
Vishal Mishra 7bfe72
+	rm -f $CA_FILE{.pub,}
Vishal Mishra 7bfe72
+	rm -f $IDENTITY_FILE{-cert.pub,.pub,}
Vishal Mishra 7bfe72
+	rm -f $AUTH_PRINC_FILE
Vishal Mishra 7bfe72
+}
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+make_keys_and_certs() {
Vishal Mishra 7bfe72
+	rm -f $CA_FILE{.pub,}
Vishal Mishra 7bfe72
+	rm -f $IDENTITY_FILE{-cert.pub,.pub,}
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+  local princs=$1
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+	${SSHKEYGEN} -q -t rsa -C '' -N '' -f $CA_FILE ||
Vishal Mishra 7bfe72
+	    fatal 'Could not create CA key'
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+	${SSHKEYGEN} -q -t rsa -C '' -N '' -f $IDENTITY_FILE ||
Vishal Mishra 7bfe72
+	    fatal 'Could not create keypair'
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+	${SSHKEYGEN} -q -s $CA_FILE -I $CERT_ID -n "$princs" -z "42" "$IDENTITY_FILE.pub" ||
Vishal Mishra 7bfe72
+	    fatal "Could not create SSH cert"
Vishal Mishra 7bfe72
+}
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+test_with_expected_principals() {
Vishal Mishra 7bfe72
+	local princs=$1
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+	out=$(${SSH} -E thlog -F $OBJ/ssh_config -i "$IDENTITY_FILE" somehost false) ||
Vishal Mishra 7bfe72
+	    fatal "SSH failed"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+	echo "$out" | grep -q "SSH_CERT_PRINCIPALS=$princs$" ||
Vishal Mishra 7bfe72
+	    fatal "SSH_CERT_PRINCIPALS has incorrect value"
Vishal Mishra 7bfe72
+}
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+test_with_no_expected_principals() {
Vishal Mishra 7bfe72
+	local princs=$1
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+	out=$(${SSH} -E thlog -F $OBJ/ssh_config -i "$IDENTITY_FILE" somehost false) ||
Vishal Mishra 7bfe72
+	    fatal "SSH failed"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+	echo "$out" | grep -vq "SSH_CERT_PRINCIPALS" ||
Vishal Mishra 7bfe72
+	    fatal "SSH_CERT_PRINCIPALS env should not be set"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+	echo "$out" | grep -vq "SSH_CERT_PRINCIPALS=$princs" ||
Vishal Mishra 7bfe72
+	    fatal "SSH_CERT_PRINCIPALS has incorrect value"
Vishal Mishra 7bfe72
+}
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+echo 'a' > $AUTH_PRINC_FILE
Vishal Mishra 7bfe72
+start_sshd
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+principals="a,b,c,d"
Vishal Mishra 7bfe72
+make_keys_and_certs "$principals"
Vishal Mishra 7bfe72
+test_with_expected_principals "$principals"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+big_princ=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16381 | head -n 1)
Vishal Mishra 7bfe72
+make_keys_and_certs "a,$big_princ"
Vishal Mishra 7bfe72
+test_with_expected_principals "a,$big_princ"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+# No room for two principals
Vishal Mishra 7bfe72
+big_princ=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16382 | head -n 1)
Vishal Mishra 7bfe72
+make_keys_and_certs "a,$big_princ"
Vishal Mishra 7bfe72
+test_with_expected_principals "a"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+make_keys_and_certs "$big_princ,a"
Vishal Mishra 7bfe72
+test_with_expected_principals "$big_princ"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+big_princ=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16384 | head -n 1)
Vishal Mishra 7bfe72
+make_keys_and_certs "a,$big_princ"
Vishal Mishra 7bfe72
+test_with_expected_principals "a"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+# principal too big for buffer
Vishal Mishra 7bfe72
+big_princ=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $SSH_MAX_PUBKEY_BYTES | head -n 1)
Vishal Mishra 7bfe72
+make_keys_and_certs "$big_princ"
Vishal Mishra 7bfe72
+test_with_no_expected_principals "$big_princ"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+# no matching principals in certificate and auth princ file
Vishal Mishra 7bfe72
+principals="b,c,d"
Vishal Mishra 7bfe72
+make_keys_and_certs "$principals"
Vishal Mishra 7bfe72
+test_with_no_expected_principals "$principals"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+stop_sshd
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+cat << EOF >> $OBJ/sshd_config
Vishal Mishra 7bfe72
+TrustedUserCAKeys $CA_FILE.pub
Vishal Mishra 7bfe72
+Protocol 2
Vishal Mishra 7bfe72
+PubkeyAuthentication yes
Vishal Mishra 7bfe72
+AuthenticationMethods publickey
Vishal Mishra 7bfe72
+AuthorizedPrincipalsFile $AUTH_PRINC_FILE
Vishal Mishra 7bfe72
+EOF
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+start_sshd
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+# no force command, no princpals
Vishal Mishra 7bfe72
+principals="a,b,c,d"
Vishal Mishra 7bfe72
+make_keys_and_certs "$principals"
Vishal Mishra 7bfe72
+test_with_no_expected_principals "$principals"
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+stop_sshd
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+cat << EOF >> $OBJ/sshd_config
Vishal Mishra 7bfe72
+Protocol 2
Vishal Mishra 7bfe72
+PubkeyAuthentication yes
Vishal Mishra 7bfe72
+AuthenticationMethods publickey
Vishal Mishra 7bfe72
+AuthorizedPrincipalsFile $AUTH_PRINC_FILE
Vishal Mishra 7bfe72
+EOF
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+start_sshd
Vishal Mishra 7bfe72
+
Vishal Mishra 7bfe72
+# No TrustedUserCAKeys causes pubkey auth, no principals
Vishal Mishra 7bfe72
+principals="a,b,c,d"
Vishal Mishra 7bfe72
+make_keys_and_certs "$principals"
Vishal Mishra 7bfe72
+test_with_no_expected_principals "$principals"