#6 Improved session logging patch, disable keyperm patch for hs+fb, and merge upstream changes
Merged 6 months ago by dcavalca. Opened 6 months ago by rcolebaugh.
rpms/ rcolebaugh/openssh c9s-sig-hyperscale  into  c9s-sig-hyperscale

@@ -0,0 +1,48 @@ 

+ Index: b/channels.c

+ ===================================================================

+ --- b.orig/channels.c

+ +++ b/channels.c

+ @@ -3774,6 +3774,7 @@ int

+  channel_setup_remote_fwd_listener(struct ssh *ssh, struct Forward *fwd,

+      int *allocated_listen_port, struct ForwardOptions *fwd_opts)

+  {

+ +  int success = 0;

+  	if (!check_rfwd_permission(ssh, fwd)) {

+  		ssh_packet_send_debug(ssh, "port forwarding refused");

+  		if (fwd->listen_path != NULL)

+ @@ -3795,14 +3796,23 @@ channel_setup_remote_fwd_listener(struct

+  			    ssh_remote_ipaddr(ssh), ssh_remote_port(ssh));

+  		return 0;

+  	}

+ -	if (fwd->listen_path != NULL) {

+ -		return channel_setup_fwd_listener_streamlocal(ssh,

+ +  if (fwd->listen_path != NULL) {

+ +		success = channel_setup_fwd_listener_streamlocal(ssh,

+  		    SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts);

+  	} else {

+ -		return channel_setup_fwd_listener_tcpip(ssh,

+ +		success = channel_setup_fwd_listener_tcpip(ssh,

+  		    SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port,

+  		    fwd_opts);

+  	}

+ +  logit("Remote forward request %s: listen=%s:%d connect=%s:%d"

+ +         " uid=%d",

+ +         success ? "succeeded" : "failed",

+ +         fwd->listen_host,

+ +         fwd->listen_port,

+ +         ssh_remote_ipaddr(ssh),

+ +         ssh_remote_port(ssh),

+ +         getuid());

+ +  return success;

+  }

+  

+  /*

+ @@ -4593,7 +4603,7 @@ x11_create_display_inet(struct ssh *ssh,

+  				if ((errno != EINVAL) && (errno != EAFNOSUPPORT)

+  #ifdef EPFNOSUPPORT

+  				    && (errno != EPFNOSUPPORT)

+ -#endif 

+ +#endif

+  				    ) {

+  					error("socket: %.100s", strerror(errno));

+  					freeaddrinfo(aitop);

@@ -0,0 +1,182 @@ 

+ Index: b/auth2-pubkey.c

+ ===================================================================

+ --- b.orig/auth2-pubkey.c

+ +++ b/auth2-pubkey.c

+ @@ -389,6 +389,10 @@ check_principals_line(struct ssh *ssh, c

+  			continue;

+  		debug3("%s: matched principal \"%.100s\"",

+  		    loc, cert->principals[i]);

+ +		verbose("Matched principal \"%.100s\" from %s against \"%.100s\" "

+ +		    "from cert",

+ +		    cp, loc, cert->principals[i]);

+ +

+  		found = 1;

+  		slog_set_principal(cp);

+  	}

+ @@ -432,6 +436,8 @@ process_principals(struct ssh *ssh, FILE

+  			found_principal = 1;

+  	}

+  	free(line);

+ +	if (!found_principal)

+ +		verbose("Did not match any principals from auth_principals_* files");

+  	return found_principal;

+  }

+  

+ @@ -710,7 +716,7 @@ check_authkey_line(struct ssh *ssh, stru

+  	    &reason) != 0)

+  		goto fail_reason;

+  

+ -	verbose("Accepted certificate ID \"%s\" (serial %llu) "

+ +	verbose("Accepted cert ID \"%s\" (serial %llu) "

+  	    "signed by CA %s %s found at %s",

+  	    key->cert->key_id,

+  	    (unsigned long long)key->cert->serial,

+ @@ -780,7 +786,7 @@ static int

+  user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key,

+      struct sshauthopt **authoptsp)

+  {

+ -	char *ca_fp, *principals_file = NULL;

+ +	char *ca_fp, *key_fp, *principals_file = NULL;

+  	const char *reason;

+  	struct sshauthopt *principals_opts = NULL, *cert_opts = NULL;

+  	struct sshauthopt *final_opts = NULL;

+ @@ -796,11 +802,16 @@ user_cert_trusted_ca(struct ssh *ssh, st

+  	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)

+  		return 0;

+  

+ +	key_fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT);

+ +

+  	if ((r = sshkey_in_file(key->cert->signature_key,

+  	    options.trusted_user_ca_keys, 1, 0)) != 0) {

+  		debug2_fr(r, "CA %s %s is not listed in %s",

+  		    sshkey_type(key->cert->signature_key), ca_fp,

+  		    options.trusted_user_ca_keys);

+ +		verbose("CA %s %s is not listed in %s",

+ +		    sshkey_type(key->cert->signature_key), ca_fp,

+ +		    options.trusted_user_ca_keys);

+  		goto out;

+  	}

+  	/*

+ @@ -851,6 +862,11 @@ user_cert_trusted_ca(struct ssh *ssh, st

+  		if ((final_opts = sshauthopt_merge(principals_opts,

+  		    cert_opts, &reason)) == NULL) {

+   fail_reason:

+ +			verbose("Rejected cert ID \"%s\" with signature "

+ +			    "%s signed by %s CA %s via %s",

+ +			    key->cert->key_id, key_fp,

+ +			    sshkey_type(key->cert->signature_key), ca_fp,

+ +			    options.trusted_user_ca_keys);

+  			error("%s", reason);

+  			auth_debug_add("%s", reason);

+  			goto out;

+ @@ -858,9 +874,10 @@ user_cert_trusted_ca(struct ssh *ssh, st

+  	}

+  

+  	/* Success */

+ -	verbose("Accepted certificate ID \"%s\" (serial %llu) signed by "

+ -	    "%s CA %s via %s", key->cert->key_id,

+ -	    (unsigned long long)key->cert->serial,

+ +	verbose("Accepted cert ID \"%s\" (serial %llu) with signature %s "

+ +	    "signed by %s CA %s via %s",

+ +	    key->cert->key_id,

+ +	    (unsigned long long)key->cert->serial, key_fp,

+  	    sshkey_type(key->cert->signature_key), ca_fp,

+  	    options.trusted_user_ca_keys);

+  	if (authoptsp != NULL) {

+ @@ -875,6 +892,7 @@ user_cert_trusted_ca(struct ssh *ssh, st

+  	sshauthopt_free(final_opts);

+  	free(principals_file);

+  	free(ca_fp);

+ +	free(key_fp);

+  	return ret;

+  }

+  

+ Index: b/regress/cert-logging.sh

+ ===================================================================

+ --- /dev/null

+ +++ b/regress/cert-logging.sh

+ @@ -0,0 +1,84 @@

+ +tid="cert logging"

+ +

+ +CERT_ID="cert_id"

+ +PRINCIPAL=$USER

+ +SERIAL=0

+ +

+ +log_grep() {

+ +    if [ "$(grep -c -G "$1" "$TEST_SSHD_LOGFILE")" == "0" ]; then

+ +        return 1;

+ +    else

+ +        return 0;

+ +    fi

+ +}

+ +

+ +cat << EOF >> $OBJ/sshd_config

+ +TrustedUserCAKeys $OBJ/ssh-rsa.pub

+ +Protocol 2

+ +PubkeyAuthentication yes

+ +AuthenticationMethods publickey

+ +AuthorizedPrincipalsFile $OBJ/auth_principals

+ +EOF

+ +

+ +if [ ! -f $OBJ/trusted_rsa ]; then

+ +    ${SSHKEYGEN} -q -t rsa -C '' -N '' -f $OBJ/trusted_rsa

+ +fi

+ +if [ ! -f $OBJ/untrusted_rsa ]; then

+ +    ${SSHKEYGEN} -q -t rsa -C '' -N '' -f $OBJ/untrusted_rsa

+ +fi

+ +

+ +${SSHKEYGEN} -q -s $OBJ/ssh-rsa -I $CERT_ID -n $PRINCIPAL -z $SERIAL $OBJ/trusted_rsa.pub ||

+ +    fatal "Could not create trusted SSH cert"

+ +

+ +${SSHKEYGEN} -q -s $OBJ/untrusted_rsa -I $CERT_ID -n $PRINCIPAL -z $SERIAL $OBJ/untrusted_rsa.pub ||

+ +    fatal "Could not create untrusted SSH cert"

+ +

+ +CA_FP="$(${SSHKEYGEN} -l -E sha256 -f ssh-rsa | cut -d' ' -f2)"

+ +KEY_FP="$(${SSHKEYGEN} -l -E sha256 -f trusted_rsa | cut -d' ' -f2)"

+ +UNTRUSTED_CA_FP="$(${SSHKEYGEN} -l -E sha256 -f untrusted_rsa | cut -d' ' -f2)"

+ +

+ +start_sshd

+ +

+ +

+ +test_no_principals() {

+ +    echo > $OBJ/auth_principals

+ +    ${SSH} -F $OBJ/ssh_config -i $OBJ/trusted_rsa-cert.pub somehost true ||

+ +        fatal "SSH failed"

+ +

+ +    if ! log_grep 'Did not match any principals from auth_principals_\* files'; then

+ +        fail "No 'Did not match any principals' message"

+ +    fi

+ +

+ +    if ! log_grep "Rejected cert ID \"$CERT_ID\" with signature $KEY_FP signed by RSA CA $CA_FP via $OBJ/ssh-rsa.pub"; then

+ +        fail "No 'Rejected cert ID' message"

+ +    fi

+ +}

+ +

+ +

+ +test_with_principals() {

+ +    echo $USER > $OBJ/auth_principals

+ +    ${SSH} -F $OBJ/ssh_config -i $OBJ/trusted_rsa-cert.pub somehost true ||

+ +        fatal "SSH failed"

+ +

+ +    if ! log_grep "Matched principal \"$PRINCIPAL\" from $OBJ/auth_principals:1 against \"$PRINCIPAL\" from cert"; then

+ +        fail "No 'Matched principal' message"

+ +    fi

+ +    if ! log_grep "Accepted cert ID \"$CERT_ID\" (serial $SERIAL) with signature $KEY_FP signed by RSA CA $CA_FP via $OBJ/ssh-rsa.pub"; then

+ +        fail "No 'Accepted cert ID' message"

+ +    fi

+ +}

+ +

+ +

+ +test_untrusted_cert() {

+ +    ${SSH} -F $OBJ/ssh_config -i $OBJ/untrusted_rsa-cert.pub somehost true ||

+ +        fatal "SSH failed"

+ +

+ +    if ! log_grep "CA RSA $UNTRUSTED_CA_FP is not listed in $OBJ/ssh-rsa.pub"; then

+ +        fail "No 'CA is not listed' message"

+ +    fi

+ +}

+ +

+ +

+ +test_no_principals

+ +test_with_principals

+ +test_untrusted_cert

@@ -0,0 +1,73 @@ 

+ Index: b/session.c

+ ===================================================================

+ --- b.orig/session.c

+ +++ b/session.c

+ @@ -2049,6 +2049,8 @@ session_pty_req(struct ssh *ssh, Session

+  		return 0;

+  	}

+  	debug("session_pty_req: session %d alloc %s", s->self, s->tty);

+ +	verbose("Allocated pty %s for user %s session %d",

+ +					s->tty, s->pw->pw_name, s->self);

+  

+  	ssh_tty_parse_modes(ssh, s->ttyfd);

+  

+ @@ -2148,6 +2150,7 @@ session_shell_req(struct ssh *ssh, Sessi

+  

+  	if ((r = sshpkt_get_end(ssh)) != 0)

+  		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);

+ +	verbose("Shell Request for user %s", s->pw->pw_name);

+  	return do_exec(ssh, s, NULL) == 0;

+  }

+  

+ @@ -2163,6 +2166,7 @@ session_exec_req(struct ssh *ssh, Sessio

+  		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);

+  

+  	slog_set_command(command);

+ +	verbose("Exec Request for user %s with command %s", s->pw->pw_name, command);

+  	success = do_exec(ssh, s, command) == 0;

+  	free(command);

+  	return success;

+ Index: b/regress/session-req.sh

+ ===================================================================

+ --- /dev/null

+ +++ b/regress/session-req.sh

+ @@ -0,0 +1,39 @@

+ +tid="session req"

+ +

+ +start_sshd

+ +

+ +test_user_shell_exec_req() {

+ +  session_shell_req_expected="Exec Request for user $USER with command true"

+ +  cnt=$(grep -c "$session_shell_req_expected" "$TEST_SSHD_LOGFILE")

+ +  if [ $cnt == "0" ]; then

+ +  	fail "No exec request for user log lines found"

+ +  fi

+ +}

+ +

+ +test_user_pty() {

+ +  session_pty_req_expected="Allocated pty .* for user $USER session .*"

+ +  line_count=$(grep -c "$session_req_expected" "$TEST_SSHD_LOGFILE")

+ +  if [ $line_count == "0" ]; then

+ +  	fail "No Allocated pty for user session found in log lines"

+ +  fi

+ +}

+ +

+ +test_user_shell_req() {

+ +  exit | ${SSH} -F $OBJ/ssh_config somehost

+ +  if [ $? -ne 0 ]; then

+ +  	fail "ssh connect with failed"

+ +  fi

+ +  session_shell_req_expected="Shell Request for user $USER"

+ +  line_count=$(grep -c "$session_shell_req_expected" "$TEST_SSHD_LOGFILE")

+ +  if [ $line_count == "0" ]; then

+ +  	fail "No session request for user log lines found"

+ +  fi

+ +}

+ +

+ +${SSH} -F $OBJ/ssh_config somehost true

+ +if [ $? -ne 0 ]; then

+ +	fail "ssh connect with failed"

+ +fi

+ +test_user_shell_exec_req

+ +test_user_pty

+ +test_user_shell_req

@@ -0,0 +1,13 @@ 

+ Index: b/sshkey.h

+ ===================================================================

+ --- b.orig/sshkey.h

+ +++ b/sshkey.h

+ @@ -106,7 +106,7 @@ enum sshkey_private_format {

+  /* key is stored in external hardware */

+  #define SSHKEY_FLAG_EXT		0x0001

+  

+ -#define SSHKEY_CERT_MAX_PRINCIPALS	256

+ +#define SSHKEY_CERT_MAX_PRINCIPALS	1024

+  /* XXX opaquify? */

+  struct sshkey_cert {

+  	struct sshbuf	*certblob; /* Kept around for use on wire */

@@ -0,0 +1,22 @@ 

+ Index: b/session.c

+ ===================================================================

+ --- b.orig/session.c

+ +++ b/session.c

+ @@ -2206,7 +2206,7 @@ session_env_req(struct ssh *ssh, Session

+  

+  	for (i = 0; i < options.num_accept_env; i++) {

+  		if (match_pattern(name, options.accept_env[i])) {

+ -			debug2("Setting env %d: %s=%s", s->num_env, name, val);

+ +			verbose("Setting env %d: %s=%s user=%s", s->num_env, name, val, s->pw->pw_name);

+  			s->env = xrecallocarray(s->env, s->num_env,

+  			    s->num_env + 1, sizeof(*s->env));

+  			s->env[s->num_env].name = name;

+ @@ -2215,7 +2215,7 @@ session_env_req(struct ssh *ssh, Session

+  			return (1);

+  		}

+  	}

+ -	debug2("Ignoring env request %s: disallowed name", name);

+ +	verbose("Ignoring env request %s user=%s : disallowed name", name, s->pw->pw_name);

+  

+   fail:

+  	free(name);

@@ -0,0 +1,216 @@ 

+ Index: b/regress/slog.sh

+ ===================================================================

+ --- b.orig/regress/slog.sh

+ +++ b/regress/slog.sh

+ @@ -1,41 +1,60 @@

+  tid='structured log'

+  

+ -port="4242"

+  log_prefix="sshd_auth_msg:"

+ -log_keys="server_ip server_port remote_ip remote_port pid session_id method cert_id cert_serial principal user session_state auth_successful _time command end_time duration auth_info client_version"

+ +log_keys="server_ip server_port remote_ip remote_port pid session_id method cert_id cert_serial principal user session_state auth_successful command end_time duration auth_info client_version"

+  do_log_json="yes"

+ -test_config="$OBJ/sshd2_config"

+ -old_config="$OBJ/sshd_config"

+ -PIDFILE=$OBJ/pidfile

+ -

+ -cat << EOF > $test_config

+ -	#*:

+ -	StrictModes             no

+ -	Port                    $port

+ -	AddressFamily           inet

+ -	ListenAddress           127.0.0.1

+ -	#ListenAddress          ::1

+ -	PidFile                 $PIDFILE

+ -	AuthorizedKeysFile      $OBJ/authorized_keys_%u

+ -	LogLevel                ERROR

+ -	AcceptEnv               _XXX_TEST_*

+ -	AcceptEnv               _XXX_TEST

+ -	HostKey $OBJ/host.ssh-ed25519

+ -	LogFormatPrefix $log_prefix

+ -	LogFormatJson $do_log_json

+ -	LogFormatKeys $log_keys

+ +

+ +AUTH_PRINC_FILE="$OBJ/auth_principals"

+ +CA_FILE="$OBJ/ca-rsa"

+ +IDENTITY_FILE="$OBJ/$USER-rsa"

+ +CERT_ID=$USER

+ +

+ +cat << EOF >>	$OBJ/sshd_config

+ +TrustedUserCAKeys $CA_FILE.pub

+ +PubkeyAuthentication yes

+ +AuthenticationMethods publickey

+ +AuthorizedPrincipalsFile $AUTH_PRINC_FILE

+ +LogFormatPrefix $log_prefix

+ +LogFormatJson $do_log_json

+ +LogFormatKeys $log_keys

+  EOF

+  

+ +sed -i 's/DEBUG3/VERBOSE/g' $OBJ/sshd_config

+  

+ -cp $test_config $old_config

+ -start_sshd

+ +cleanup() {

+ +	rm -f $CA_FILE{.pub,}

+ +	rm -f $IDENTITY_FILE{-cert.pub,.pub,}

+ +	rm -f $AUTH_PRINC_FILE

+ +	rm -f $TEST_SSHD_LOGFILE

+ +}

+ +

+ +make_keys() {

+ +	local keytype=$1

+ +

+ +	rm -f $IDENTITY_FILE{.pub,}

+ +	${SSHKEYGEN} -q -t $keytype -C '' -N '' -f $IDENTITY_FILE ||

+ +	    fatal 'Could not create keypair'

+ +

+ +	cat $IDENTITY_FILE.pub > authorized_keys_$USER

+ +	${SSHKEYGEN} -lf $IDENTITY_FILE

+ +}

+  

+ -${SSH} -F $OBJ/ssh_config somehost true

+ -if [ $? -ne 0 ]; then

+ -	fail "ssh connect with failed"

+ -fi

+ +make_cert() {

+ +	local princs=$1

+ +	local certtype=$2

+ +	local serial=$3

+  

+ -test_log_counts() {

+ +	rm -f $CA_FILE

+ +	rm -f "$IDENTITY_FILE-cert.pub"

+ +

+ +	${SSHKEYGEN} -q -t $certtype -C '' -N '' -f $CA_FILE ||

+ +	    fatal 'Could not create CA key'

+ +

+ +	${SSHKEYGEN} -q -s $CA_FILE -I $CERT_ID -n "$princs" -z $serial "$IDENTITY_FILE.pub" ||

+ +	    fatal "Could not create SSH cert"

+ +}

+ +

+ +do_test_log_counts() {

+  	cnt=$(grep -c "$log_prefix" "$TEST_SSHD_LOGFILE")

+  	if [ $cnt -ne 2 ]; then

+  		fail "expected 2 structured logging lines, got $cnt"

+ @@ -43,7 +62,10 @@ test_log_counts() {

+  }

+  

+  test_json_valid() {

+ -	which python &>/dev/null || echo 'python not found in path, skipping tests'

+ +	if ! $(which python &>/dev/null) ; then

+ +		 echo 'python not found in path, skipping JSON tests'

+ +		 return 1

+ +	fi

+  

+  	loglines=$(cat "$TEST_SSHD_LOGFILE" | grep "$log_prefix")

+  	first=$(echo "$loglines" | head -n1)

+ @@ -55,5 +77,72 @@ test_json_valid() {

+  	    || fail "invalid json structure $last"

+  }

+  

+ -test_log_counts

+ -test_json_valid

+ +# todo: first/last line

+ +extract_key() {

+ +	local key=$1

+ +	loglines=$(cat "$TEST_SSHD_LOGFILE" | grep "$log_prefix")

+ +	last=$(echo "$loglines" | tail -n1)

+ +	json=${last:$(expr length $log_prefix)}

+ +

+ +	val=$(echo $json | python -c "import sys, json; print(json.load(sys.stdin)[\"$key\"])") ||

+ +	    fail "error extracting $key from $json"

+ +	echo "$val"

+ +}

+ +

+ +test_basic_logging() {

+ +	${SSH} -F $OBJ/ssh_config -v -i "$IDENTITY_FILE" somehost true ||

+ +		    fatal "SSH failed"

+ +

+ +	do_test_log_counts

+ +	test_json_valid || return 1

+ +}

+ +

+ +extract_hash() {

+ +	local source=$1

+ +	echo $source | sed "s/.*\(SHA256:[[:print:]]\{43\}\).*$/\1/"

+ +}

+ +

+ +test_auth_info() {

+ +	local keyfp=$1

+ +	local keytype=$2

+ +	local princ=$3

+ +	local serial=$4

+ +

+ +	${SSH} -F $OBJ/ssh_config -v -i "$IDENTITY_FILE" somehost true ||

+ +	    fatal "SSH failed"

+ +

+ +	auth_info=$(extract_key 'auth_info')

+ +	digest=$(extract_hash "$keyfp")

+ +

+ +	[ -z "$keyfp" ] || echo "$auth_info" | grep -q "$digest" ||

+ +		echo "hash digest not found"

+ +	[ -z "$keytype" ] || echo "$auth_info" | grep -q "$keytype" ||

+ +		echo "keytype not found"

+ +	[ -z "$princ" ] || echo "$auth_info" | grep -q "$princ" ||

+ +		echo "princ not found"

+ +	[ -z "$serial" ] || echo "$auth_info" | grep -q "$serial" ||

+ +		echo "serial not found"

+ +}

+ +

+ +test_cert_serial() {

+ +	local serial=$1

+ +	logged_serial=$(extract_key 'cert_serial')

+ +	 [ $serial = $logged_serial ] || fail 'cert serial mismatch'

+ +}

+ +

+ +start_sshd

+ +

+ +keytype="RSA"

+ +keyfp=$(make_keys $keytype)

+ +test_basic_logging || return

+ +test_auth_info "$keyfp" "$keytype"

+ +

+ +rm authorized_keys_$USER # force cert auth

+ +

+ +princ="$USER"

+ +echo $princ > $AUTH_PRINC_FILE

+ +

+ +serial='42'

+ +make_cert "$princ" "$keytype" "$serial"

+ +test_auth_info "$keyfp" "$keytype" "$princ" "$serial"

+ +test_cert_serial "$serial"

+ Index: b/auth.c

+ ===================================================================

+ --- b.orig/auth.c

+ +++ b/auth.c

+ @@ -351,6 +351,8 @@ auth_log(struct ssh *ssh, int authentica

+  	    extra != NULL ? ": " : "",

+  	    extra != NULL ? extra : "");

+  

+ +	if (extra != NULL)

+ +		slog_set_auth_info(extra);

+  	free(extra);

+  	slog_set_auth_data(authenticated, method, authctxt->user);

+  

+ Index: b/auth2-pubkey.c

+ ===================================================================

+ --- b.orig/auth2-pubkey.c

+ +++ b/auth2-pubkey.c

+ @@ -722,7 +722,7 @@ check_authkey_line(struct ssh *ssh, stru

+  	    (unsigned long long)key->cert->serial,

+  	    sshkey_type(found), fp, loc);

+  

+ -	    slog_set_cert_serial(key->cert->serial);

+ +	slog_set_cert_serial(key->cert->serial);

+   success:

+  	if (finalopts == NULL)

+  		fatal_f("internal error: missing options");

+ @@ -885,6 +885,8 @@ user_cert_trusted_ca(struct ssh *ssh, st

+  		final_opts = NULL;

+  	}

+  	slog_set_cert_id(key->cert->key_id);

+ +	slog_set_cert_serial(key->cert->serial);

+ +

+  	ret = 1;

+   out:

+  	sshauthopt_free(principals_opts);

@@ -0,0 +1,24 @@ 

+ Index: b/serverloop.c

+ ===================================================================

+ --- b.orig/serverloop.c

+ +++ b/serverloop.c

+ @@ -433,6 +433,7 @@ server_request_direct_tcpip(struct ssh *

+  	char *target = NULL, *originator = NULL;

+  	u_int target_port = 0, originator_port = 0;

+  	int r;

+ +	uid_t user;

+  

+  	if ((r = sshpkt_get_cstring(ssh, &target, NULL)) != 0 ||

+  	    (r = sshpkt_get_u32(ssh, &target_port)) != 0 ||

+ @@ -451,6 +452,11 @@ server_request_direct_tcpip(struct ssh *

+  		goto out;

+  	}

+  

+ +	user = getuid();

+ +	logit("Tunnel: %s:%d -> %s:%d UID(%d) user %s",

+ +	    originator, originator_port, target, target_port, user,

+ +	    getpwuid(user)->pw_name);

+ +

+  	debug_f("originator %s port %u, target %s port %u",

+  	    originator, originator_port, target, target_port);

+  

@@ -0,0 +1,130 @@ 

+ Index: b/sshd.c

+ ===================================================================

+ --- b.orig/sshd.c

+ +++ b/sshd.c

+ @@ -1420,6 +1420,8 @@ server_accept_loop(struct ssh *ssh, int

+  				    options.log_level,

+  				    options.log_facility,

+  				    log_stderr);

+ +

+ +				set_log_session_id();  // Set log session ID for this session

+  				if (rexec_flag)

+  					close(config_s[0]);

+  				else {

+ Index: b/log.c

+ ===================================================================

+ --- b.orig/log.c

+ +++ b/log.c

+ @@ -410,17 +410,17 @@ do_log(LogLevel level, int force, const

+  		tmp_handler(level, force, fmtbuf, log_handler_ctx);

+  		log_handler = tmp_handler;

+  	} else if (log_on_stderr) {

+ -		snprintf(msgbuf, sizeof msgbuf, "%.*s\r\n",

+ -		    (int)sizeof msgbuf - 3, fmtbuf);

+ +		snprintf(msgbuf, sizeof msgbuf, "%.*s session=%s\r\n",

+ +		    (int)sizeof msgbuf - 3, fmtbuf, get_log_session_id());

+  		(void)write(log_stderr_fd, msgbuf, strlen(msgbuf));

+  	} else {

+  #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)

+  		openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata);

+ -		syslog_r(pri, &sdata, "%.500s", fmtbuf);

+ +		syslog_r(pri, &sdata, "%.500s session=%s", fmtbuf, get_log_session_id());

+  		closelog_r(&sdata);

+  #else

+  		openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility);

+ -		syslog(pri, "%.500s", fmtbuf);

+ +		syslog(pri, "%.500s session=%s", fmtbuf, get_log_session_id());

+  		closelog();

+  #endif

+  	}

+ @@ -452,6 +452,33 @@ sshlogdie(const char *file, const char *

+  }

+  

+  void

+ +set_log_session_id()

+ +{

+ +        struct timeval tv;

+ +        char hostname[HOST_NAME_MAX + 1];

+ +        char session_id[HOST_NAME_MAX + 20];

+ +        char *s;

+ +        if (gethostname(hostname, sizeof(hostname)) != 0) {

+ +                *hostname = '\0';

+ +        }

+ +        gettimeofday(&tv, NULL);

+ +        snprintf(session_id, sizeof(session_id), "%s:%x.%x",

+ +                 hostname, tv.tv_sec, tv.tv_usec);

+ +        setenv("LOG_SESSION_ID", session_id, 1);

+ +}

+ +

+ +const char *

+ +get_log_session_id()

+ +{

+ +        const char *id = getenv("LOG_SESSION_ID");

+ +        if (!id) {

+ +                 set_log_session_id();

+ +                 id = getenv("LOG_SESSION_ID");

+ +        }

+ +        return id;

+ +}

+ +

+ +void

+  sshsigdie(const char *file, const char *func, int line, int showfunc,

+      LogLevel level, const char *suffix, const char *fmt, ...)

+  {

+ Index: b/regress/session-id.sh

+ ===================================================================

+ --- /dev/null

+ +++ b/regress/session-id.sh

+ @@ -0,0 +1,23 @@

+ +tid="session id"

+ +

+ +start_sshd

+ +

+ +${SSH} -F $OBJ/ssh_config somehost true

+ +if [ $? -ne 0 ]; then

+ +	fail "ssh connect with failed"

+ +fi

+ +

+ +expected="session=$(hostname)"

+ +

+ +# grab the first session ID which will be stable across session

+ +sessionid=$(grep -m1 $expected $TEST_SSHD_LOGFILE | sed -E 's/.*(session=.*)/\1/')

+ +

+ +line_count=$(grep -c $expected $TEST_SSHD_LOGFILE)

+ +if [ $line_count == "0" ]; then

+ +	fail "No session ID lines found"

+ +fi

+ +

+ +stable_id_count=$(grep -c $sessionid $TEST_SSHD_LOGFILE)

+ +if [ $line_count != $stable_id_count ]; then

+ +	fail 'Mismatching session ids found'

+ +fi

+ Index: b/log.h

+ ===================================================================

+ --- b.orig/log.h

+ +++ b/log.h

+ @@ -68,6 +68,9 @@ const char *	log_level_name(LogLevel);

+  void	 set_log_handler(log_handler_fn *, void *);

+  void	 cleanup_exit(int) __attribute__((noreturn));

+  

+ +void		set_log_session_id();

+ +const char *	get_log_session_id();

+ +

+  void	 sshlog(const char *, const char *, int, int,

+      LogLevel, const char *, const char *, ...)

+      __attribute__((format(printf, 7, 8)));

+ Index: b/session.c

+ ===================================================================

+ --- b.orig/session.c

+ +++ b/session.c

+ @@ -1256,6 +1256,10 @@ do_setup_env(struct ssh *ssh, Session *s

+  		child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",

+  		    original_command);

+  

+ +  /* set LOG_SESSION_ID for child */

+ +  child_set_env(&env, &envsize, "LOG_SESSION_ID", get_log_session_id());

+ +  debug("set LOG_SESION_ID to: %s", get_log_session_id());

+ +

+  	if (debug_flag) {

+  		/* dump the environment */

+  		fprintf(stderr, "Environment:\n");

@@ -0,0 +1,225 @@ 

+ Index: b/session.c

+ ===================================================================

+ --- b.orig/session.c

+ +++ b/session.c

+ @@ -98,6 +98,7 @@

+  #include "atomicio.h"

+  #include "slog.h"

+  

+ +#define SSH_MAX_PUBKEY_BYTES 16384

+  

+  #if defined(KRB5) && defined(USE_AFS)

+  #include <kafs.h>

+ @@ -1054,11 +1055,18 @@ copy_environment(char **source, char ***

+  static char **

+  do_setup_env(struct ssh *ssh, Session *s, const char *shell)

+  {

+ -	char buf[256];

+ +	char buf[SSH_MAX_PUBKEY_BYTES];

+ +	char *pbuf = &buf[0];

+  	size_t n;

+  	u_int i, envsize;

+  	char *ocp, *cp, *value, **env, *laddr;

+  	struct passwd *pw = s->pw;

+ +	Authctxt *authctxt = s->authctxt;

+ +	struct sshkey *key;

+ +	size_t len = 0;

+ +	ssize_t total = 0;

+ +	struct sshkey_cert *cert;

+ +

+  #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)

+  	char *path = NULL;

+  #endif

+ @@ -1255,9 +1263,57 @@ do_setup_env(struct ssh *ssh, Session *s

+  		child_set_env(&env, &envsize, "SSH_USER_AUTH", auth_info_file);

+  	if (s->ttyfd != -1)

+  		child_set_env(&env, &envsize, "SSH_TTY", s->tty);

+ -	if (original_command)

+ +	if (original_command) {

+  		child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND",

+  		    original_command);

+ +		/*

+ +		* Set SSH_CERT_PRINCIPALS to be the principals on the ssh certificate.

+ +		* Only do so when a force command is present to prevent the client

+ +		* from changing the value of SSH_CERT_PRINCIPALS. For example, when a

+ +		* client is given shell access, the client can easily change the

+ +		* value of an environment variable by running, e.g.,

+ +		* ssh user@host.address 'SSH_CERT_PRINCIPALS=attacker env'

+ +		*/

+ +

+ +		if (authctxt->nprev_keys > 0) {

+ +			key = authctxt->prev_keys[authctxt->nprev_keys-1];

+ +			/* If a user was authorized by a certificate, set SSH_CERT_PRINCIPALS */

+ +			if (sshkey_is_cert(key)) {

+ +				cert = key->cert;

+ +

+ +				for (i = 0; i < cert->nprincipals - 1; ++i) {

+ +					/*

+ +					* total: bytes written to buf so far

+ +					* 2: one for comma and one for '\0' to be added by snprintf

+ +					* We stop at the first principal overflowing buf.

+ +					*/

+ +					if (total + strlen(cert->principals[i]) + 2 > SSH_MAX_PUBKEY_BYTES)

+ +						break;

+ +

+ +					len = snprintf(pbuf, SSH_MAX_PUBKEY_BYTES-total, "%s,",

+ +					    cert->principals[i]);

+ +					/* pbuf advances by len, the '\0' at the end will be overwritten */

+ +					pbuf += len;

+ +					total += len;

+ +				}

+ +

+ +				if (total + strlen(cert->principals[i]) + 1 <= SSH_MAX_PUBKEY_BYTES) {

+ +					len = snprintf(pbuf, SSH_MAX_PUBKEY_BYTES-total, "%s",

+ +					    cert->principals[i]);

+ +					total += len;

+ +				} else if (total > 0)

+ +					/*

+ +					* If we hit the overflow condition, remove the trailing comma.

+ +					* We only do so if the overflowing principal is not the first one on the

+ +					* certificate so that there is at least one principal in buf

+ +					*/

+ +					buf[total-1] = '\0';

+ +

+ +				if (total > 0)

+ +					child_set_env(&env, &envsize, "SSH_CERT_PRINCIPALS", buf);

+ +			}

+ +		}

+ +	}

+  

+    /* set LOG_SESSION_ID for child */

+    child_set_env(&env, &envsize, "LOG_SESSION_ID", get_log_session_id());

+ Index: b/regress/cert-princ-env.sh

+ ===================================================================

+ --- /dev/null

+ +++ b/regress/cert-princ-env.sh

+ @@ -0,0 +1,129 @@

+ +tid="cert principal env"

+ +

+ +# change to ecdsa

+ +CERT_ID="$USER"

+ +AUTH_PRINC_FILE="$OBJ/auth_principals"

+ +CA_FILE="$OBJ/ca-rsa"

+ +IDENTITY_FILE="$OBJ/$USER-rsa"

+ +SSH_MAX_PUBKEY_BYTES=16384

+ +

+ +cat << EOF >> $OBJ/sshd_config

+ +TrustedUserCAKeys $CA_FILE.pub

+ +Protocol 2

+ +PubkeyAuthentication yes

+ +AuthenticationMethods publickey

+ +AuthorizedPrincipalsFile $AUTH_PRINC_FILE

+ +ForceCommand=/bin/env

+ +EOF

+ +

+ +cleanup() {

+ +	rm -f $CA_FILE{.pub,}

+ +	rm -f $IDENTITY_FILE{-cert.pub,.pub,}

+ +	rm -f $AUTH_PRINC_FILE

+ +}

+ +

+ +make_keys_and_certs() {

+ +	rm -f $CA_FILE{.pub,}

+ +	rm -f $IDENTITY_FILE{-cert.pub,.pub,}

+ +

+ +  local princs=$1

+ +

+ +	${SSHKEYGEN} -q -t rsa -C '' -N '' -f $CA_FILE ||

+ +	    fatal 'Could not create CA key'

+ +

+ +	${SSHKEYGEN} -q -t rsa -C '' -N '' -f $IDENTITY_FILE ||

+ +	    fatal 'Could not create keypair'

+ +

+ +	${SSHKEYGEN} -q -s $CA_FILE -I $CERT_ID -n "$princs" -z "42" "$IDENTITY_FILE.pub" ||

+ +	    fatal "Could not create SSH cert"

+ +}

+ +

+ +test_with_expected_principals() {

+ +	local princs=$1

+ +

+ +	out=$(${SSH} -E thlog -F $OBJ/ssh_config -i "$IDENTITY_FILE" somehost false) ||

+ +	    fatal "SSH failed"

+ +

+ +	echo "$out" | grep -q "SSH_CERT_PRINCIPALS=$princs$" ||

+ +	    fatal "SSH_CERT_PRINCIPALS has incorrect value"

+ +}

+ +

+ +test_with_no_expected_principals() {

+ +	local princs=$1

+ +

+ +	out=$(${SSH} -E thlog -F $OBJ/ssh_config -i "$IDENTITY_FILE" somehost false) ||

+ +	    fatal "SSH failed"

+ +

+ +	echo "$out" | grep -vq "SSH_CERT_PRINCIPALS" ||

+ +	    fatal "SSH_CERT_PRINCIPALS env should not be set"

+ +

+ +	echo "$out" | grep -vq "SSH_CERT_PRINCIPALS=$princs" ||

+ +	    fatal "SSH_CERT_PRINCIPALS has incorrect value"

+ +}

+ +

+ +

+ +echo 'a' > $AUTH_PRINC_FILE

+ +start_sshd

+ +

+ +principals="a,b,c,d"

+ +make_keys_and_certs "$principals"

+ +test_with_expected_principals "$principals"

+ +

+ +big_princ=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16381 | head -n 1)

+ +make_keys_and_certs "a,$big_princ"

+ +test_with_expected_principals "a,$big_princ"

+ +

+ +# No room for two principals

+ +big_princ=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16382 | head -n 1)

+ +make_keys_and_certs "a,$big_princ"

+ +test_with_expected_principals "a"

+ +

+ +make_keys_and_certs "$big_princ,a"

+ +test_with_expected_principals "$big_princ"

+ +

+ +big_princ=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16384 | head -n 1)

+ +make_keys_and_certs "a,$big_princ"

+ +test_with_expected_principals "a"

+ +

+ +# principal too big for buffer

+ +big_princ=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w $SSH_MAX_PUBKEY_BYTES | head -n 1)

+ +make_keys_and_certs "$big_princ"

+ +test_with_no_expected_principals "$big_princ"

+ +

+ +# no matching principals in certificate and auth princ file

+ +principals="b,c,d"

+ +make_keys_and_certs "$principals"

+ +test_with_no_expected_principals "$principals"

+ +

+ +stop_sshd

+ +

+ +cat << EOF >> $OBJ/sshd_config

+ +TrustedUserCAKeys $CA_FILE.pub

+ +Protocol 2

+ +PubkeyAuthentication yes

+ +AuthenticationMethods publickey

+ +AuthorizedPrincipalsFile $AUTH_PRINC_FILE

+ +EOF

+ +

+ +start_sshd

+ +

+ +# no force command, no princpals

+ +principals="a,b,c,d"

+ +make_keys_and_certs "$principals"

+ +test_with_no_expected_principals "$principals"

+ +

+ +stop_sshd

+ +

+ +cat << EOF >> $OBJ/sshd_config

+ +Protocol 2

+ +PubkeyAuthentication yes

+ +AuthenticationMethods publickey

+ +AuthorizedPrincipalsFile $AUTH_PRINC_FILE

+ +EOF

+ +

+ +start_sshd

+ +

+ +# No TrustedUserCAKeys causes pubkey auth, no principals

+ +principals="a,b,c,d"

+ +make_keys_and_certs "$principals"

+ +test_with_no_expected_principals "$principals"

file added
+1148
The added file is too large to be shown here, see it at: fb87_slog.patch
@@ -0,0 +1,12 @@ 

+ diff -up openssh-8.6p1/sshkey.c.fips openssh-8.6p1/sshkey.c

+ --- openssh-8.6p1/sshkey.c.fips	2021-05-06 12:08:36.493926838 +0200

+ +++ openssh-8.6p1/sshkey.c	2021-05-06 12:08:36.502926908 +0200

+ @@ -1705,6 +1707,8 @@ rsa_generate_private_key(u_int bits, RSA

+ 		goto out;

+ 

+ 	if (EVP_PKEY_keygen(ctx, &res) <= 0) {

+ +		if (FIPS_mode())

+ +			logit_f("the key length might be unsupported by FIPS mode approved key generation method");

+  		ret = SSH_ERR_LIBCRYPTO_ERROR;

+  		goto out;

+  	}

@@ -479,15 +479,6 @@ 

   	/* Fill in ret from parsed key */

   	ret->type = type;

   	if (sshkey_is_cert(ret)) {

- @@ -1705,6 +1707,8 @@ rsa_generate_private_key(u_int bits, RSA

- 		goto out;

- 

- 	if (EVP_PKEY_keygen(ctx, &res) <= 0) {

- +		if (FIPS_mode())

- +			logit_f("the key length might be unsupported by FIPS mode approved key generation method");

-  		ret = SSH_ERR_LIBCRYPTO_ERROR;

-  		goto out;

-  	}

  @@ -2916,6 +2916,11 @@ sshkey_sign(struct sshkey *key,

   		break;

   	case KEY_ED25519_SK:

@@ -0,0 +1,52 @@ 

+ --- openssh-9.3p1/openbsd-compat/openssl-compat.c	2023-03-15 22:28:19.000000000 +0100

+ +++ /home/dbelyavs/work/upstream/openssh-portable/openbsd-compat/openssl-compat.c	2023-05-25 14:19:42.870841944 +0200

+ @@ -33,10 +33,10 @@

+  

+  /*

+   * OpenSSL version numbers: MNNFFPPS: major minor fix patch status

+ - * We match major, minor, fix and status (not patch) for <1.0.0.

+ - * After that, we acceptable compatible fix versions (so we

+ - * allow 1.0.1 to work with 1.0.0). Going backwards is only allowed

+ - * within a patch series.

+ + * Versions >=3 require only major versions to match.

+ + * For versions <3, we accept compatible fix versions (so we allow 1.0.1

+ + * to work with 1.0.0). Going backwards is only allowed within a patch series.

+ + * See https://www.openssl.org/policies/releasestrat.html

+   */

+  

+  int

+ @@ -48,15 +48,17 @@

+  	if (headerver == libver)

+  		return 1;

+  

+ -	/* for versions < 1.0.0, major,minor,fix,status must match */

+ -	if (headerver < 0x1000000f) {

+ -		mask = 0xfffff00fL; /* major,minor,fix,status */

+ +	/*

+ +	 * For versions >= 3.0, only the major and status must match.

+ +	 */

+ +	if (headerver >= 0x3000000f) {

+ +		mask = 0xf000000fL; /* major,status */

+  		return (headerver & mask) == (libver & mask);

+  	}

+  

+  	/*

+ -	 * For versions >= 1.0.0, major,minor,status must match and library

+ -	 * fix version must be equal to or newer than the header.

+ +	 * For versions >= 1.0.0, but <3, major,minor,status must match and

+ +	 * library fix version must be equal to or newer than the header.

+  	 */

+  	mask = 0xfff0000fL; /* major,minor,status */

+  	hfix = (headerver & 0x000ff000) >> 12;

+ diff -up openssh-8.7p1/configure.ac.check openssh-8.7p1/configure.ac

+ --- openssh-8.7p1/configure.ac.check	2023-11-27 14:54:32.959113758 +0100

+ +++ openssh-8.7p1/configure.ac	2023-11-27 14:54:49.467500523 +0100

+ @@ -2821,7 +2821,7 @@ if test "x$openssl" = "xyes" ; then

+  				;;

+  			101*)   ;; # 1.1.x

+  			200*)   ;; # LibreSSL

+ -			300*)   ;; # OpenSSL development branch.

+ +			30*)   ;; # OpenSSL 3.x series

+  		        *)

+  				AC_MSG_ERROR([Unknown/unsupported OpenSSL version ("$ssl_library_ver")])

+  		                ;;

@@ -0,0 +1,33 @@ 

+ diff -u -p -r1.166 auth2.c

+ --- a/auth2.c	8 Mar 2023 04:43:12 -0000	1.166

+ +++ b/auth2.c	28 Aug 2023 08:32:44 -0000

+ @@ -208,6 +208,7 @@ input_service_request(int type, u_int32_

+  }

+  

+  #define MIN_FAIL_DELAY_SECONDS 0.005

+ +#define MAX_FAIL_DELAY_SECONDS 5.0

+  static double

+  user_specific_delay(const char *user)

+  {

+ @@ -233,6 +234,12 @@ ensure_minimum_time_since(double start, 

+  	struct timespec ts;

+  	double elapsed = monotime_double() - start, req = seconds, remain;

+  

+ +	if (elapsed > MAX_FAIL_DELAY_SECONDS) {

+ +		debug3_f("elapsed %0.3lfms exceeded the max delay "

+ +		    "requested %0.3lfms)", elapsed*1000, req*1000);

+ +		return;

+ +	}

+ +

+  	/* if we've already passed the requested time, scale up */

+  	while ((remain = seconds - elapsed) < 0.0)

+  		seconds *= 2;

+ @@ -317,7 +324,7 @@ input_userauth_request(int type, u_int32

+  		debug2("input_userauth_request: try method %s", method);

+  		authenticated =	m->userauth(ssh);

+  	}

+ -	if (!authctxt->authenticated)

+ +	if (!authctxt->authenticated && strcmp(method, "none") != 0)

+  		ensure_minimum_time_since(tstart,

+  		    user_specific_delay(authctxt->user));

+  	userauth_finish(ssh, authenticated, method, NULL);

@@ -0,0 +1,447 @@ 

+ diff --git a/PROTOCOL b/PROTOCOL

+ index d453c779..ded935eb 100644

+ --- a/PROTOCOL

+ +++ b/PROTOCOL

+ @@ -137,6 +137,32 @@ than as a named global or channel request to allow pings with very

+  described at:

+  http://git.libssh.org/users/aris/libssh.git/plain/doc/curve25519-sha256@libssh.org.txt?h=curve25519

+  

+ +1.9 transport: strict key exchange extension

+ +

+ +OpenSSH supports a number of transport-layer hardening measures under

+ +a "strict KEX" feature. This feature is signalled similarly to the

+ +RFC8308 ext-info feature: by including a additional algorithm in the

+ +initiial SSH2_MSG_KEXINIT kex_algorithms field. The client may append

+ +"kex-strict-c-v00@openssh.com" to its kex_algorithms and the server

+ +may append "kex-strict-s-v00@openssh.com". These pseudo-algorithms

+ +are only valid in the initial SSH2_MSG_KEXINIT and MUST be ignored

+ +if they are present in subsequent SSH2_MSG_KEXINIT packets.

+ +

+ +When an endpoint that supports this extension observes this algorithm

+ +name in a peer's KEXINIT packet, it MUST make the following changes to

+ +the the protocol:

+ +

+ +a) During initial KEX, terminate the connection if any unexpected or

+ +   out-of-sequence packet is received. This includes terminating the

+ +   connection if the first packet received is not SSH2_MSG_KEXINIT.

+ +   Unexpected packets for the purpose of strict KEX include messages

+ +   that are otherwise valid at any time during the connection such as

+ +   SSH2_MSG_DEBUG and SSH2_MSG_IGNORE.

+ +b) After sending or receiving a SSH2_MSG_NEWKEYS message, reset the

+ +   packet sequence number to zero. This behaviour persists for the

+ +   duration of the connection (i.e. not just the first

+ +   SSH2_MSG_NEWKEYS).

+ +

+  2. Connection protocol changes

+  

+  2.1. connection: Channel write close extension "eow@openssh.com"

+ diff --git a/kex.c b/kex.c

+ index aa5e792d..d478ff6e 100644

+ --- a/kex.c

+ +++ b/kex.c

+ @@ -65,7 +65,7 @@

+  #endif

+  

+  /* prototype */

+ -static int kex_choose_conf(struct ssh *);

+ +static int kex_choose_conf(struct ssh *, uint32_t seq);

+  static int kex_input_newkeys(int, u_int32_t, struct ssh *);

+  

+  static const char *proposal_names[PROPOSAL_MAX] = {

+ @@ -177,6 +177,18 @@ kex_names_valid(const char *names)

+  	return 1;

+  }

+  

+ +/* returns non-zero if proposal contains any algorithm from algs */

+ +static int

+ +has_any_alg(const char *proposal, const char *algs)

+ +{

+ +	char *cp;

+ +

+ +	if ((cp = match_list(proposal, algs, NULL)) == NULL)

+ +		return 0;

+ +	free(cp);

+ +	return 1;

+ +}

+ +

+  /*

+   * Concatenate algorithm names, avoiding duplicates in the process.

+   * Caller must free returned string.

+ @@ -184,7 +196,7 @@ kex_names_valid(const char *names)

+  char *

+  kex_names_cat(const char *a, const char *b)

+  {

+ -	char *ret = NULL, *tmp = NULL, *cp, *p, *m;

+ +	char *ret = NULL, *tmp = NULL, *cp, *p;

+  	size_t len;

+  

+  	if (a == NULL || *a == '\0')

+ @@ -201,10 +213,8 @@ kex_names_cat(const char *a, const char *b)

+  	}

+  	strlcpy(ret, a, len);

+  	for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) {

+ -		if ((m = match_list(ret, p, NULL)) != NULL) {

+ -			free(m);

+ +		if (has_any_alg(ret, p))

+  			continue; /* Algorithm already present */

+ -		}

+  		if (strlcat(ret, ",", len) >= len ||

+  		    strlcat(ret, p, len) >= len) {

+  			free(tmp);

+ @@ -466,7 +485,12 @@ kex_protocol_error(int type, u_int32_t seq, struct ssh *ssh)

+  {

+  	int r;

+  

+ -	error("kex protocol error: type %d seq %u", type, seq);

+ +	/* If in strict mode, any unexpected message is an error */

+ +	if ((ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict) {

+ +		ssh_packet_disconnect(ssh, "strict KEX violation: "

+ +		    "unexpected packet type %u (seqnr %u)", type, seq);

+ +	}

+ +	error_f("type %u seq %u", type, seq);

+  	if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 ||

+  	    (r = sshpkt_put_u32(ssh, seq)) != 0 ||

+  	    (r = sshpkt_send(ssh)) != 0)

+ @@ -548,6 +572,11 @@ kex_input_ext_info(int type, u_int32_t seq, struct ssh *ssh)

+  	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error);

+  	if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0)

+  		return r;

+ + 	if (ninfo >= 1024) {

+ + 		error("SSH2_MSG_EXT_INFO with too many entries, expected "

+ + 		    "<=1024, received %u", ninfo);

+ +		return dispatch_protocol_error(type, seq, ssh);

+ + 	}

+  	for (i = 0; i < ninfo; i++) {

+  		if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0)

+  			return r;

+ @@ -681,7 +705,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)

+  		error_f("no kex");

+  		return SSH_ERR_INTERNAL_ERROR;

+  	}

+ -	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL);

+ +	ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_protocol_error);

+  	ptr = sshpkt_ptr(ssh, &dlen);

+  	if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0)

+  		return r;

+ @@ -717,7 +741,7 @@ kex_input_kexinit(int type, u_int32_t seq, struct ssh *ssh)

+  	if (!(kex->flags & KEX_INIT_SENT))

+  		if ((r = kex_send_kexinit(ssh)) != 0)

+  			return r;

+ -	if ((r = kex_choose_conf(ssh)) != 0)

+ +	if ((r = kex_choose_conf(ssh, seq)) != 0)

+  		return r;

+  

+  	if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL)

+ @@ -981,20 +1005,14 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])

+  	return (1);

+  }

+  

+ -/* returns non-zero if proposal contains any algorithm from algs */

+  static int

+ -has_any_alg(const char *proposal, const char *algs)

+ +kexalgs_contains(char **peer, const char *ext)

+  {

+ -	char *cp;

+ -

+ -	if ((cp = match_list(proposal, algs, NULL)) == NULL)

+ -		return 0;

+ -	free(cp);

+ -	return 1;

+ +	return has_any_alg(peer[PROPOSAL_KEX_ALGS], ext);

+  }

+  

+  static int

+ -kex_choose_conf(struct ssh *ssh)

+ +kex_choose_conf(struct ssh *ssh, uint32_t seq)

+  {

+  	struct kex *kex = ssh->kex;

+  	struct newkeys *newkeys;

+ @@ -1019,13 +1037,23 @@ kex_choose_conf(struct ssh *ssh)

+  		sprop=peer;

+  	}

+  

+ -	/* Check whether client supports ext_info_c */

+ -	if (kex->server && (kex->flags & KEX_INITIAL)) {

+ -		char *ext;

+ -

+ -		ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL);

+ -		kex->ext_info_c = (ext != NULL);

+ -		free(ext);

+ +	/* Check whether peer supports ext_info/kex_strict */

+ +	if ((kex->flags & KEX_INITIAL) != 0) {

+ +		if (kex->server) {

+ +			kex->ext_info_c = kexalgs_contains(peer, "ext-info-c");

+ +			kex->kex_strict = kexalgs_contains(peer,

+ +			    "kex-strict-c-v00@openssh.com");

+ +		} else {

+ +			kex->kex_strict = kexalgs_contains(peer,

+ +			    "kex-strict-s-v00@openssh.com");

+ +		}

+ +		if (kex->kex_strict) {

+ +			debug3_f("will use strict KEX ordering");

+ +			if (seq != 0)

+ +				ssh_packet_disconnect(ssh,

+ +				    "strict KEX violation: "

+ +				    "KEXINIT was not the first packet");

+ +		}

+  	}

+  

+  	/* Check whether client supports rsa-sha2 algorithms */

+ diff --git a/kex.h b/kex.h

+ index 5f7ef784..272ebb43 100644

+ --- a/kex.h

+ +++ b/kex.h

+ @@ -149,6 +149,7 @@ struct kex {

+  	u_int	kex_type;

+  	char	*server_sig_algs;

+  	int	ext_info_c;

+ +	int	kex_strict;

+  	struct sshbuf *my;

+  	struct sshbuf *peer;

+  	struct sshbuf *client_version;

+ diff --git a/packet.c b/packet.c

+ index 52017def..beb214f9 100644

+ --- a/packet.c

+ +++ b/packet.c

+ @@ -1207,8 +1207,13 @@ ssh_packet_send2_wrapped(struct ssh *ssh)

+  	sshbuf_dump(state->output, stderr);

+  #endif

+  	/* increment sequence number for outgoing packets */

+ -	if (++state->p_send.seqnr == 0)

+ +	if (++state->p_send.seqnr == 0) {

+ +		if ((ssh->kex->flags & KEX_INITIAL) != 0) {

+ +			ssh_packet_disconnect(ssh, "outgoing sequence number "

+ +			    "wrapped during initial key exchange");

+ +		}

+  		logit("outgoing seqnr wraps around");

+ +	}

+  	if (++state->p_send.packets == 0)

+  		if (!(ssh->compat & SSH_BUG_NOREKEY))

+  			return SSH_ERR_NEED_REKEY;

+ @@ -1216,6 +1221,11 @@ ssh_packet_send2_wrapped(struct ssh *ssh)

+  	state->p_send.bytes += len;

+  	sshbuf_reset(state->outgoing_packet);

+  

+ +	if (type == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) {

+ +		debug_f("resetting send seqnr %u", state->p_send.seqnr);

+ +		state->p_send.seqnr = 0;

+ +	}

+ +

+  	if (type == SSH2_MSG_NEWKEYS)

+  		r = ssh_set_newkeys(ssh, MODE_OUT);

+  	else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side)

+ @@ -1344,8 +1354,7 @@ ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)

+  	/* Stay in the loop until we have received a complete packet. */

+  	for (;;) {

+  		/* Try to read a packet from the buffer. */

+ -		r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p);

+ -		if (r != 0)

+ +		if ((r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p)) != 0)

+  			break;

+  		/* If we got a packet, return it. */

+  		if (*typep != SSH_MSG_NONE)

+ @@ -1629,10 +1615,16 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)

+  		if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0)

+  			goto out;

+  	}

+ +

+  	if (seqnr_p != NULL)

+  		*seqnr_p = state->p_read.seqnr;

+ -	if (++state->p_read.seqnr == 0)

+ +	if (++state->p_read.seqnr == 0) {

+ +		if ((ssh->kex->flags & KEX_INITIAL) != 0) {

+ +			ssh_packet_disconnect(ssh, "incoming sequence number "

+ +			    "wrapped during initial key exchange");

+ +		}

+  		logit("incoming seqnr wraps around");

+ +	}

+  	if (++state->p_read.packets == 0)

+  		if (!(ssh->compat & SSH_BUG_NOREKEY))

+  			return SSH_ERR_NEED_REKEY;

+ @@ -1698,6 +1690,10 @@ ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)

+  #endif

+  	/* reset for next packet */

+  	state->packlen = 0;

+ +	if (*typep == SSH2_MSG_NEWKEYS && ssh->kex->kex_strict) {

+ +		debug_f("resetting read seqnr %u", state->p_read.seqnr);

+ +		state->p_read.seqnr = 0;

+ +	}

+  

+  	if ((r = ssh_packet_check_rekey(ssh)) != 0)

+  		return r;

+ @@ -1720,10 +1716,39 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)

+  		r = ssh_packet_read_poll2(ssh, typep, seqnr_p);

+  		if (r != 0)

+  			return r;

+ -		if (*typep) {

+ -			state->keep_alive_timeouts = 0;

+ -			DBG(debug("received packet type %d", *typep));

+ +		if (*typep == 0) {

+ +			/* no message ready */

+ +			return 0;

+  		}

+ +		state->keep_alive_timeouts = 0;

+ +		DBG(debug("received packet type %d", *typep));

+ +

+ +		/* Always process disconnect messages */

+ +		if (*typep == SSH2_MSG_DISCONNECT) {

+ +			if ((r = sshpkt_get_u32(ssh, &reason)) != 0 ||

+ +			    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0)

+ +				return r;

+ +			/* Ignore normal client exit notifications */

+ +			do_log2(ssh->state->server_side &&

+ +			    reason == SSH2_DISCONNECT_BY_APPLICATION ?

+ +			    SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR,

+ +			    "Received disconnect from %s port %d:"

+ +			    "%u: %.400s", ssh_remote_ipaddr(ssh),

+ +			    ssh_remote_port(ssh), reason, msg);

+ +			free(msg);

+ +			return SSH_ERR_DISCONNECTED;

+ +		}

+ +

+ +		/*

+ +		 * Do not implicitly handle any messages here during initial

+ +		 * KEX when in strict mode. They will be need to be allowed

+ +		 * explicitly by the KEX dispatch table or they will generate

+ +		 * protocol errors.

+ +		 */

+ +		if (ssh->kex != NULL &&

+ +		    (ssh->kex->flags & KEX_INITIAL) && ssh->kex->kex_strict)

+ +			return 0;

+ +		/* Implicitly handle transport-level messages */

+  		switch (*typep) {

+  		case SSH2_MSG_IGNORE:

+  			debug3("Received SSH2_MSG_IGNORE");

+ @@ -1738,19 +1763,6 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)

+  			debug("Remote: %.900s", msg);

+  			free(msg);

+  			break;

+ -		case SSH2_MSG_DISCONNECT:

+ -			if ((r = sshpkt_get_u32(ssh, &reason)) != 0 ||

+ -			    (r = sshpkt_get_string(ssh, &msg, NULL)) != 0)

+ -				return r;

+ -			/* Ignore normal client exit notifications */

+ -			do_log2(ssh->state->server_side &&

+ -			    reason == SSH2_DISCONNECT_BY_APPLICATION ?

+ -			    SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR,

+ -			    "Received disconnect from %s port %d:"

+ -			    "%u: %.400s", ssh_remote_ipaddr(ssh),

+ -			    ssh_remote_port(ssh), reason, msg);

+ -			free(msg);

+ -			return SSH_ERR_DISCONNECTED;

+  		case SSH2_MSG_UNIMPLEMENTED:

+  			if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0)

+  				return r;

+ @@ -2242,6 +2254,7 @@ kex_to_blob(struct sshbuf *m, struct kex *kex)

+  	    (r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 ||

+  	    (r = sshbuf_put_u32(m, kex->hostkey_nid)) != 0 ||

+  	    (r = sshbuf_put_u32(m, kex->kex_type)) != 0 ||

+ +	    (r = sshbuf_put_u32(m, kex->kex_strict)) != 0 ||

+  	    (r = sshbuf_put_stringb(m, kex->my)) != 0 ||

+  	    (r = sshbuf_put_stringb(m, kex->peer)) != 0 ||

+  	    (r = sshbuf_put_stringb(m, kex->client_version)) != 0 ||

+ @@ -2404,6 +2417,7 @@ kex_from_blob(struct sshbuf *m, struct kex **kexp)

+  	    (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 ||

+  	    (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_nid)) != 0 ||

+  	    (r = sshbuf_get_u32(m, &kex->kex_type)) != 0 ||

+ +	    (r = sshbuf_get_u32(m, &kex->kex_strict)) != 0 ||

+  	    (r = sshbuf_get_stringb(m, kex->my)) != 0 ||

+  	    (r = sshbuf_get_stringb(m, kex->peer)) != 0 ||

+  	    (r = sshbuf_get_stringb(m, kex->client_version)) != 0 ||

+ @@ -2732,6 +2746,7 @@ sshpkt_disconnect(struct ssh *ssh, const char *fmt,...)

+  	vsnprintf(buf, sizeof(buf), fmt, args);

+  	va_end(args);

+  

+ +	debug2_f("sending SSH2_MSG_DISCONNECT: %s", buf);

+  	if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 ||

+  	    (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 ||

+  	    (r = sshpkt_put_cstring(ssh, buf)) != 0 ||

+ diff --git a/sshconnect2.c b/sshconnect2.c

+ index df6caf81..0cccbcc4 100644

+ --- a/sshconnect2.c

+ +++ b/sshconnect2.c

+ @@ -253,7 +253,8 @@ ssh_kex2(struct ssh *ssh, char *host, st

+  		fatal_fr(r, "kex_assemble_namelist");

+  	free(all_key);

+  

+ -	if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL)

+ +	if ((s = kex_names_cat(options.kex_algorithms,

+ +	    "ext-info-c,kex-strict-c-v00@openssh.com")) == NULL)

+  		fatal_f("kex_names_cat");

+  	myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, s);

+  	myproposal[PROPOSAL_ENC_ALGS_CTOS] =

+ @@ -358,7 +358,6 @@ struct cauthmethod {

+  };

+  

+  static int input_userauth_service_accept(int, u_int32_t, struct ssh *);

+ -static int input_userauth_ext_info(int, u_int32_t, struct ssh *);

+  static int input_userauth_success(int, u_int32_t, struct ssh *);

+  static int input_userauth_failure(int, u_int32_t, struct ssh *);

+  static int input_userauth_banner(int, u_int32_t, struct ssh *);

+ @@ -472,7 +471,7 @@ ssh_userauth2(struct ssh *ssh, const char *local_user,

+  

+  	ssh->authctxt = &authctxt;

+  	ssh_dispatch_init(ssh, &input_userauth_error);

+ -	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info);

+ +	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, kex_input_ext_info);

+  	ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept);

+  	ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &authctxt.success);	/* loop until success */

+  	pubkey_cleanup(ssh);

+ @@ -531,12 +530,6 @@ input_userauth_service_accept(int type, u_int32_t seq, struct ssh *ssh)

+  }

+  

+  /* ARGSUSED */

+ -static int

+ -input_userauth_ext_info(int type, u_int32_t seqnr, struct ssh *ssh)

+ -{

+ -	return kex_input_ext_info(type, seqnr, ssh);

+ -}

+ -

+  void

+  userauth(struct ssh *ssh, char *authlist)

+  {

+ @@ -615,6 +608,7 @@ input_userauth_success(int type, u_int32_t seq, struct ssh *ssh)

+  	free(authctxt->methoddata);

+  	authctxt->methoddata = NULL;

+  	authctxt->success = 1;			/* break out */

+ +	ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, dispatch_protocol_error);

+  	return 0;

+  }

+  

+ diff -up openssh-8.7p1/sshd.c.kexstrict openssh-8.7p1/sshd.c

+ --- openssh-8.7p1/sshd.c.kexstrict	2023-11-27 13:19:18.855433602 +0100

+ +++ openssh-8.7p1/sshd.c	2023-11-27 13:28:10.441325314 +0100

+ @@ -2531,10 +2531,14 @@ do_ssh2_kex(struct ssh *ssh)

+  	struct kex *kex;

+  	char *hostkey_types = NULL;

+  	char *prop_kex = NULL, *prop_enc = NULL, *prop_hostkey = NULL;

+ +	char *cp;

+  	int r;

+  

+ -	myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh,

+ -	    options.kex_algorithms);

+ +	if ((cp = kex_names_cat(options.kex_algorithms, 

+ +	   "kex-strict-s-v00@openssh.com")) == NULL)

+ +		fatal_f("kex_names_cat");

+ +

+ +	myproposal[PROPOSAL_KEX_ALGS] = prop_kex = compat_kex_proposal(ssh, cp);

+  	myproposal[PROPOSAL_ENC_ALGS_CTOS] =

+  	    myproposal[PROPOSAL_ENC_ALGS_STOC] = prop_enc =

+  	    compat_cipher_proposal(ssh, options.ciphers);

+ @@ -2586,7 +2586,7 @@ do_ssh2_kex(struct ssh *ssh)

+  	if (gss && orig)

+  		xasprintf(&newstr, "%s,%s", gss, orig);

+  	else if (gss)

+ -		newstr = gss;

+ +		xasprintf(&newstr, "%s,%s", gss, "kex-strict-s-v00@openssh.com");

+  	else if (orig)

+  		newstr = orig;

+  

+ @@ -2650,6 +2654,7 @@ do_ssh2_kex(struct ssh *ssh)

+  #endif

+  	free(prop_kex);

+  	free(prop_enc);

+ +	free(cp);

+  	free(prop_hostkey);

+  	debug("KEX done");

+  }

@@ -0,0 +1,57 @@ 

+ diff --git a/ssh.c b/ssh.c

+ index 35c48e62..48d93ddf 100644

+ --- a/ssh.c

+ +++ b/ssh.c

+ @@ -626,6 +626,41 @@ ssh_conn_info_free(struct ssh_conn_info *cinfo)

+  	free(cinfo);

+  }

+  

+ +static int

+ +valid_hostname(const char *s)

+ +{

+ +	size_t i;

+ +

+ +	if (*s == '-')

+ +		return 0;

+ +	for (i = 0; s[i] != 0; i++) {

+ +		if (strchr("'`\"$\\;&<>|(){}", s[i]) != NULL ||

+ +		    isspace((u_char)s[i]) || iscntrl((u_char)s[i]))

+ +			return 0;

+ +	}

+ +	return 1;

+ +}

+ +

+ +static int

+ +valid_ruser(const char *s)

+ +{

+ +	size_t i;

+ +

+ +	if (*s == '-')

+ +		return 0;

+ +	for (i = 0; s[i] != 0; i++) {

+ +		if (strchr("'`\";&<>|(){}", s[i]) != NULL)

+ +			return 0;

+ +		/* Disallow '-' after whitespace */

+ +		if (isspace((u_char)s[i]) && s[i + 1] == '-')

+ +			return 0;

+ +		/* Disallow \ in last position */

+ +		if (s[i] == '\\' && s[i + 1] == '\0')

+ +			return 0;

+ +	}

+ +	return 1;

+ +}

+ +

+  /*

+   * Main program for the ssh client.

+   */

+ @@ -1118,6 +1153,10 @@ main(int ac, char **av)

+  	if (!host)

+  		usage();

+  

+ +	if (!valid_hostname(host))

+ +		fatal("hostname contains invalid characters");

+ +	if (options.user != NULL && !valid_ruser(options.user))

+ +		fatal("remote username contains invalid characters");

+  	host_arg = xstrdup(host);

+  

+  	/* Initialize the command to execute on remote host. */

@@ -0,0 +1,2 @@ 

+ #Type Name ID  GECOS                     Home directory        Shell

+ u     sshd 74  "Privilege-separated SSH" /usr/share/empty.sshd -

@@ -0,0 +1,2 @@ 

+ #Type Name     ID

+ g     ssh_keys 101

file modified
+148 -12
@@ -5,11 +5,11 @@ 

  %global WITH_SELINUX 0

  %endif

  

- %global _hardened_build 1

+ # Useful development mode for porting patches from

+ # a different release

+ %global use_quilt 0

  

- # OpenSSH privilege separation requires a user & group ID

- %global sshd_uid    74

- %global sshd_gid    74

+ %global _hardened_build 1

  

  # Do we want to disable building of gnome-askpass? (1=yes 0=no)

  %global no_gnome_askpass 0
@@ -51,14 +51,15 @@ 

  

  # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1

  %global openssh_ver 8.7p1

- %global openssh_rel 34

+ %global openssh_rel 38

+ %global hyperscale_rel 8

  %global pam_ssh_agent_ver 0.10.4

  %global pam_ssh_agent_rel 5

  

  Summary: An open source implementation of SSH protocol version 2

  Name: openssh

  Version: %{openssh_ver}

- Release: %{openssh_rel}%{?dist}

+ Release: %{openssh_rel}.%{hyperscale_rel}%{?dist}

  URL: http://www.openssh.com/portable.html

  #URL1: https://github.com/jbeverly/pam_ssh_agent_auth/

  Source0: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz
@@ -76,6 +77,12 @@ 

  Source13: sshd-keygen

  Source15: sshd-keygen.target

  Source16: ssh-agent.service

+ Source17: openssh-systemd-sysusers.conf

+ Source18: openssh-server-systemd-sysusers.conf

+ %if 0%{?facebook} && 0%{?use_quilt}

+ Source19: series

+ BuildRequires: quilt

+ %endif

  

  #https://bugzilla.mindrot.org/show_bug.cgi?id=2581

  Patch100: openssh-6.7p1-coverity.patch
@@ -115,7 +122,9 @@ 

  #https://bugzilla.mindrot.org/show_bug.cgi?id=1644

  Patch601: openssh-6.6p1-allow-ip-opts.patch

  #https://bugzilla.mindrot.org/show_bug.cgi?id=1893 (WONTFIX)

+ %if ! 0%{?facebook}

  Patch604: openssh-6.6p1-keyperm.patch

+ %endif

  #(drop?) https://bugzilla.mindrot.org/show_bug.cgi?id=1925

  Patch606: openssh-5.9p1-ipv6man.patch

  #?
@@ -282,6 +291,46 @@ 

  # upsream commit

  # b23fe83f06ee7e721033769cfa03ae840476d280

  Patch1015: openssh-9.3p1-upstream-cve-2023-38408.patch

+ #upstream commit b7afd8a4ecaca8afd3179b55e9db79c0ff210237

+ Patch1016: openssh-9.3p1-openssl-compat.patch

+ #upstream commit 01dbf3d46651b7d6ddf5e45d233839bbfffaeaec

+ Patch1017: openssh-9.4p2-limit-delay.patch

+ #upstream commit 1edb00c58f8a6875fad6a497aa2bacf37f9e6cd5

+ Patch1018: openssh-9.6p1-CVE-2023-48795.patch

+ #upstream commit 7ef3787c84b6b524501211b11a26c742f829af1a

+ Patch1019: openssh-9.6p1-CVE-2023-51385.patch

+ 

+ # c9s specific logic factored out of openssh-7.7p1-fips.patch

+ Patch2000: openssh-7.7p1-fips-warning.patch

+ 

+ %if 0%{?facebook} && !0%{?use_quilt}

+ # Add a unique log session identifier to output messages for

+ # each sshd process and its children.

+ Patch2010: fb87_log_session_id.patch

+ # Add structured logging

+ Patch2011: fb87_slog.patch

+ # Add a log entry when a session is started over a local forward port.

+ Patch2012: fb87_log_port_forwards.patch

+ # Add a log line when a session is started over a reverse port forward.

+ Patch2013: fb87_070_logging_reverse_port_forward.patch

+ # Increase ssh cert max principals from 256 to 1024.

+ Patch2014: fb87_810_increase_ssh_cert_max_principals.patch

+ # Output a line in the logs showing the command run, or shell request

+ # and the user.

+ Patch2015: fb87_090_logging_shell_cmd_pty.patch

+ # Output a line in the logs showing which principal was matched when

+ # certificate authentication was used.

+ Patch2016: fb87_080_logging_certificates.patch

+ # Add verbose logging for setting env variables.

+ Patch2017: fb87_log_accept_env.patch

+ # Set an environment variable SSH_CERT_PRINCIPALS in the child process

+ # to be the full principal list of a user's SSH certificate when forced

+ # command is present and the user is authenticated by the certificate.

+ Patch2018: fb87_pass_principals_to_child.patch

+ # Log extra authentication information to the auth_info structured

+ # logging field, and add tests for pubkey and cert auth.

+ Patch2019: fb87_log_auth_info.patch

+ %endif

  

  License: BSD

  Requires: /sbin/nologin
@@ -356,7 +405,7 @@ 

  %package -n pam_ssh_agent_auth

  Summary: PAM module for authentication with ssh-agent

  Version: %{pam_ssh_agent_ver}

- Release: %{pam_ssh_agent_rel}.%{openssh_rel}%{?dist}

+ Release: %{pam_ssh_agent_rel}.%{openssh_rel}.%{hyperscale_rel}%{?dist}

  License: BSD

  

  %description
@@ -428,7 +477,9 @@ 

  %patch502 -p1 -b .keycat

  

  %patch601 -p1 -b .ip-opts

+ %if ! 0%{?facebook}

  %patch604 -p1 -b .keyperm

+ %endif

  %patch606 -p1 -b .ipv6man

  %patch607 -p1 -b .sigpipe

  %patch609 -p1 -b .x11
@@ -472,13 +523,20 @@ 

  %patch980 -p1 -b .sftpdirs

  %patch981 -p1 -b .scp-sftpdirs

  %patch982 -p1 -b .minrsabits

+ %if 0%{?rhel} > 8

+ # This patch results in build failures on el8

  %patch983 -p1 -b .evpgenrsa

+ %endif

  %patch984 -p1 -b .ibmca

  %patch986 -p1 -b .91cleanup

  

  %patch200 -p1 -b .audit

  %patch201 -p1 -b .audit-race

  %patch700 -p1 -b .fips

+ %if 0%{?rhel} > 8

+ # This patch is based onto evpgenrsa and doesn't apply cleanly without it

+ %patch2000 -p1 -b .fips-warning

+ %endif

  

  %patch1000 -p1 -b .minimize-sha1-use

  %patch1001 -p1 -b .scp-clears-file
@@ -501,6 +559,28 @@ 

  %patch1013 -p1 -b .man-hostkeyalgos

  %patch1014 -p1 -b .utc_parse

  %patch1015 -p1 -b .cve-2023-38408

+ %patch1016 -p1 -b .openssl3compat

+ %patch1017 -p1 -b .limitdelay

+ %patch1018 -p1 -b .cve-2023-48795

+ %patch1019 -p1 -b .cve-2023-51385

+ 

+ %if 0%{?facebook} && !0%{?use_quilt}

+ %patch2010 -p1 -b .log_session_id

+ %patch2011 -p1 -b .slog

+ %patch2012 -p1 -b .log_port_forwards

+ %patch2013 -p1 -b .logging_reverse_port_forward

+ %patch2014 -p1 -b .increase_ssh_cert_max_principals

+ %patch2015 -p1 -b .logging_shell_cmd_pty

+ %patch2016 -p1 -b .logging_certificates

+ %patch2017 -p1 -b .log_accept_env

+ %patch2018 -p1 -b .pass_principals_to_child

+ %patch2019 -p1 -b .log_auth_info

+ %endif

+ 

+ %if 0%{?facebook} && 0%{?use_quilt}

+ ln -sf %{_sourcedir} patches

+ quilt push -a

+ %endif

  

  autoreconf

  pushd pam_ssh_agent_auth-pam_ssh_agent_auth-%{pam_ssh_agent_ver}
@@ -646,6 +726,8 @@ 

  install -m755 contrib/ssh-copy-id $RPM_BUILD_ROOT%{_bindir}/

  install contrib/ssh-copy-id.1 $RPM_BUILD_ROOT%{_mandir}/man1/

  install -d -m711 ${RPM_BUILD_ROOT}/%{_datadir}/empty.sshd

+ install -p -D -m 0644 %{SOURCE17} %{buildroot}%{_sysusersdir}/openssh.conf

+ install -p -D -m 0644 %{SOURCE18} %{buildroot}%{_sysusersdir}/openssh-server.conf

  

  %if ! %{no_gnome_askpass}

  install contrib/gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/gnome-ssh-askpass
@@ -674,13 +756,10 @@ 

  install -m 755 regress/misc/sk-dummy/sk-dummy.so $RPM_BUILD_ROOT%{_libdir}/sshtest

  

  %pre

- getent group ssh_keys >/dev/null || groupadd -r ssh_keys || :

+ %sysusers_create_compat %{SOURCE17}

  

  %pre server

- getent group sshd >/dev/null || groupadd -g %{sshd_uid} -r sshd || :

- getent passwd sshd >/dev/null || \

-   useradd -c "Privilege-separated SSH" -u %{sshd_uid} -g sshd \

-   -s /sbin/nologin -r -d /usr/share/empty.sshd sshd 2> /dev/null || :

+ %sysusers_create_compat %{SOURCE18}

  

  %post server

  %systemd_post sshd.service sshd.socket
@@ -718,6 +797,7 @@ 

  %attr(0755,root,root) %dir %{_libexecdir}/openssh

  %attr(2555,root,ssh_keys) %{_libexecdir}/openssh/ssh-keysign

  %attr(0644,root,root) %{_mandir}/man8/ssh-keysign.8*

+ %attr(0644,root,root) %{_sysusersdir}/openssh.conf

  

  %files clients

  %attr(0755,root,root) %{_bindir}/ssh
@@ -763,6 +843,7 @@ 

  %attr(0644,root,root) %{_unitdir}/sshd.socket

  %attr(0644,root,root) %{_unitdir}/sshd-keygen@.service

  %attr(0644,root,root) %{_unitdir}/sshd-keygen.target

+ %attr(0644,root,root) %{_sysusersdir}/openssh-server.conf

  

  %files keycat

  %doc HOWTO.ssh-keycat
@@ -787,6 +868,43 @@ 

  %endif

  

  %changelog

+ * Fri May 3 2024 Raymond Colebaugh <raymondcolebaugh@gmail.com> - 8.7p1-38.8 + 0.10.4-5.7

+ - Merge "Add LOG_SESSION_ID for everyone" patch into fb87_log_session_id.patch,

+   update fb87_pass_principals_to_child.patch to account for this change

+ 

+ * Mon Apr 15 2024 Raymond Colebaugh <raymondcolebaugh@gmail.com> - 8.7p1-38.8 + 0.10.4-5.7

+ - Disable openssh-6.6p1-keyperm.patch in hs+fb builds

+ 

+ * Mon Apr 15 2024 Raymond Colebaugh <raymondcolebaugh@gmail.com> - 8.7p1-38.8 + 0.10.4-5.7

+ - Merge new changes from upstream

+ 

+ * Fri Jan 05 2024 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-38

+ - Fix Terrapin attack

+   Resolves: CVE-2023-48795

+ 

+ * Fri Jan 05 2024 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-37

+ - Fix Terrapin attack

+   Resolves: CVE-2023-48795

+ 

+ * Wed Dec 20 2023 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-36

+ - Fix Terrapin attack

+   Resolves: CVE-2023-48795

+ - Relax OpenSSH build-time checks for OpenSSL version

+   Related: RHEL-4734

+ - Forbid shell metasymbols in username/hostname

+   Resolves: CVE-2023-51385

+ 

+ * Mon Oct 23 2023 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-35

+ - Relax OpenSSH checks for OpenSSL version

+   Resolves: RHEL-4734

+ - Limit artificial delays in sshd while login using AD user

+   Resolves: RHEL-2469

+ - Move users/groups creation logic to sysusers.d fragments

+   Resolves: RHEL-5222

+ 

+ * Thu Aug 3 2023 Raymond Colebaugh <raymondcolebaugh@gmail.com> - 8.7p1-34.7 + 0.10.4-5.7

+ - Merge new changes from upstream

+ 

  * Thu Jul 20 2023 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-34

  - Avoid remote code execution in ssh-agent PKCS#11 support

    Resolves: CVE-2023-38408
@@ -808,6 +926,9 @@ 

  - Add some log debug lines

  - Related: rhbz#2091694

  

+ * Mon Apr 24 2023 Raymond Colebaugh <raymondcolebaugh@gmail.com> - 8.7p1-29.6 + 0.10.4-5.6

+ - Merge new changes from upstream

+ 

  * Thu Apr 20 2023 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-30

  - Some non-terminating processes were listening on ports.

    Resolves: rhbz#2177768
@@ -848,6 +969,9 @@ 

  - Build fix after OpenSSL rebase

    Resolves: rhbz#2153626

  

+ * Mon Oct 17 2022 Raymond Colebaugh <raymondcolebaugh@gmail.com> - 8.7p1-24.5 + 0.10.4-5.5

+ - Merge new changes from upstream

+ 

  * Fri Sep 23 2022 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-24

  - Set minimal value of RSA key length via configuration option - support both names

    Resolves: rhbz#2128352
@@ -856,6 +980,12 @@ 

  - Set minimal value of RSA key length via configuration option

    Resolves: rhbz#2128352

  

+ * Wed Sep 21 2022 Raymond Colebaugh <raymondcolebaugh@gmail.com> - 8.7p1-22.4 + 0.10.4-5.4

+ - Merge new changes from upstream

+ 

+ * Wed Aug 24 2022 Kent Peacock <kentp@fb.com> - 8.7p1-19.3 + 0.10.4-5.3

+ - Set up local developer strategy using quilt and incorporate Meta patches

+ 

  * Tue Aug 16 2022 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-22

  - Avoid spirous message on connecting to the machine with ssh-rsa keys

    Related: rhbz#2115246
@@ -870,6 +1000,12 @@ 

  - Fix openssh-8.7p1-scp-clears-file.patch

    Related: rhbz#2056884

  

+ * Wed Jul 20 2022 Davide Cavalca <dcavalca@centosproject.org> - 8.7p1-19.2 + 0.10.4-5.2

+ - Refactor and reinstate FIPS patch for el8

+ 

+ * Wed Jul 20 2022 Davide Cavalca <dcavalca@centosproject.org> - 8.7p1-19.1 + 0.10.4-5.1

+ - Make it build on el8 as well for Hyperscale

+ 

  * Fri Jul 15 2022 Dmitry Belyavskiy <dbelyavs@redhat.com> - 8.7p1-19 + 0.10.4-5

  - FIX pam_ssh_agent_auth auth for RSA keys

    Related: rhbz#2070113

file added
+10
@@ -0,0 +1,10 @@ 

+ fb87_log_session_id.patch

+ fb87_slog.patch

+ fb87_log_port_forwards.patch

+ fb87_070_logging_reverse_port_forward.patch

+ fb87_810_increase_ssh_cert_max_principals.patch

+ fb87_090_logging_shell_cmd_pty.patch

+ fb87_080_logging_certificates.patch

+ fb87_log_accept_env.patch

+ fb87_pass_principals_to_child.patch

+ fb87_log_auth_info.patch