diff --git a/fb87_070_logging_reverse_port_forward.patch b/fb87_070_logging_reverse_port_forward.patch
new file mode 100644
index 0000000..339fd35
--- /dev/null
+++ b/fb87_070_logging_reverse_port_forward.patch
@@ -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);
diff --git a/fb87_080_logging_certificates.patch b/fb87_080_logging_certificates.patch
new file mode 100644
index 0000000..46cc276
--- /dev/null
+++ b/fb87_080_logging_certificates.patch
@@ -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
diff --git a/fb87_090_logging_shell_cmd_pty.patch b/fb87_090_logging_shell_cmd_pty.patch
new file mode 100644
index 0000000..6017884
--- /dev/null
+++ b/fb87_090_logging_shell_cmd_pty.patch
@@ -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
diff --git a/fb87_810_increase_ssh_cert_max_principals.patch b/fb87_810_increase_ssh_cert_max_principals.patch
new file mode 100644
index 0000000..2abe0e6
--- /dev/null
+++ b/fb87_810_increase_ssh_cert_max_principals.patch
@@ -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 */
diff --git a/fb87_log_accept_env.patch b/fb87_log_accept_env.patch
new file mode 100644
index 0000000..24ba1fc
--- /dev/null
+++ b/fb87_log_accept_env.patch
@@ -0,0 +1,22 @@
+diff --git a/session.c b/session.c
+--- a/session.c
++++ b/session.c
+@@ -2106,16 +2106,16 @@
+ 
+ 	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;
+ 			s->env[s->num_env].val = val;
+ 			s->num_env++;
+ 			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);
diff --git a/fb87_log_auth_info.patch b/fb87_log_auth_info.patch
new file mode 100644
index 0000000..45f320a
--- /dev/null
+++ b/fb87_log_auth_info.patch
@@ -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);
diff --git a/fb87_log_port_forwards.patch b/fb87_log_port_forwards.patch
new file mode 100644
index 0000000..8a7b030
--- /dev/null
+++ b/fb87_log_port_forwards.patch
@@ -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);
+ 
diff --git a/fb87_log_session_id.patch b/fb87_log_session_id.patch
new file mode 100644
index 0000000..ac9d6a3
--- /dev/null
+++ b/fb87_log_session_id.patch
@@ -0,0 +1,141 @@
+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/auth-pam.c
+===================================================================
+--- b.orig/auth-pam.c
++++ b/auth-pam.c
+@@ -766,7 +766,20 @@ sshpam_init(struct ssh *ssh, Authctxt *a
+ 		return (-1);
+ 	}
+ #endif
+-	return (0);
++	debug("PAM: setting PAM LOG_SESSION_ID to \"%s\"", get_log_session_id());
++	{
++		char log_session_id_env[HOST_NAME_MAX + 50];
++		snprintf(log_session_id_env, sizeof(log_session_id_env),
++				"LOG_SESSION_ID=%s", get_log_session_id());
++		sshpam_err = pam_putenv(sshpam_handle, log_session_id_env);
++		if (sshpam_err != PAM_SUCCESS) {
++			pam_end(sshpam_handle, sshpam_err);
++			sshpam_handle = NULL;
++			return (-1);
++		}
++	}
++
++        return (0);
+ }
+ 
+ static void
+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)));
diff --git a/fb87_pass_principals_to_child.patch b/fb87_pass_principals_to_child.patch
new file mode 100644
index 0000000..27bb7ca
--- /dev/null
+++ b/fb87_pass_principals_to_child.patch
@@ -0,0 +1,224 @@
+diff --git a/session.c b/session.c
+--- a/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>
+@@ -990,11 +991,18 @@
+ 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
+@@ -1188,9 +1196,57 @@
+ 		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);
++			}
++		}
++	}
+ 
+ 	if (debug_flag) {
+ 		/* dump the environment */
+diff --git a/regress/cert-princ-env.sh b/regress/cert-princ-env.sh
+new file mode 100644
+--- /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"
diff --git a/fb87_slog.patch b/fb87_slog.patch
new file mode 100644
index 0000000..427cdda
--- /dev/null
+++ b/fb87_slog.patch
@@ -0,0 +1,1148 @@
+Index: b/slog.c
+===================================================================
+--- /dev/null
++++ b/slog.c
+@@ -0,0 +1,619 @@
++/*
++ * Copyright 2004-present Facebook. All Rights Reserved.
++ */
++
++ /* When using slogctxt in any module perform a NULL pointer check */
++
++#include <sys/types.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <stdio.h>
++#include <string.h>
++#include <syslog.h>
++#include <pwd.h>
++#include <time.h>
++
++#include "includes.h"
++#include "slog.h"
++#include "log.h"
++#include "misc.h"
++#include "servconf.h"
++#include "xmalloc.h"
++
++typedef struct Structuredlogctxt Structuredlogctxt;
++
++struct Structuredlogctxt { /* items we care about logging */
++	char		server_ip[SLOG_SHORT_STRING_LEN];		// server_ip
++	int		server_port;		// server_port
++	char		remote_ip[SLOG_SHORT_STRING_LEN];		// remote_ip
++	int		remote_port;		// remote_port
++	pid_t		pam_process_pid;		// pam_pid
++	char		session_id[SLOG_STRING_LEN];		// session_id
++	char		method[SLOG_STRING_LEN];		// method
++	char		cert_id[SLOG_STRING_LEN];		// cert_id
++	unsigned long long		cert_serial;		// cert_serial
++	char		principal[SLOG_STRING_LEN];		// principal
++	char		user[SLOG_STRING_LEN];		// user
++	char		command[SLOG_LONG_STRING_LEN];		// command
++	SLOG_SESSION_STATE		session_state;		// session_state
++	SLOG_AUTHENTICATED		auth_successful;		// auth_successful
++	time_t		start_time;		// start_time
++	time_t		end_time;		// end_time
++	int		duration;		// duration
++	pid_t		main_pid;		// main_pid
++	char		auth_info[SLOG_MEDIUM_STRING_LEN];		// auth_info
++	char		client_version[SLOG_STRING_LEN];		// client_version
++};
++
++Structuredlogctxt *slogctxt;
++extern ServerOptions options;
++
++// Define token constants and default syntax format
++static const char *server_ip_token         = "server_ip";
++static const char *server_port_token       = "server_port";
++static const char *remote_ip_token         = "remote_ip";
++static const char *remote_port_token       = "remote_port";
++static const char *pam_pid_token           = "pam_pid";
++static const char *process_pid_token       = "pid";
++static const char *session_id_token        = "session_id";
++static const char *method_token            = "method";
++static const char *cert_id_token           = "cert_id";
++static const char *cert_serial_token       = "cert_serial";
++static const char *principal_token         = "principal";
++static const char *user_token              = "user";
++static const char *command_token           = "command";
++static const char *session_state_token     = "session_state";
++static const char *auth_successful_token   = "auth_successful";
++static const char *start_time_token        = "start_time";
++static const char *end_time_token          = "end_time";
++static const char *duration_token          = "duration";
++static const char *main_pid_token          = "main_pid";
++static const char *auth_info_token         = "auth_info";
++static const char *client_version          = "client_version";
++
++/* Example log format sshd_config
++ * LogFormatPrefix sshd_auth_msg:
++ * LogFormatKeys server_ip server_port remote_ip remote_port pid session_id /
++   method cert_id cert_serial principal user session_state auth_successful /
++   start_time command # NO LINE BREAKS
++ * LogFormatJson yes # no makes this output an json array vs json object
++ */
++
++// Set a arbitrary large size so we can feed a potentially
++// large json object to the logger
++#define SLOG_BUF_SIZE 8192
++#define SLOG_TRUNCATED_MESSAGE_JSON ", \"incomplete\": \"true\"}"
++#define SLOG_TRUNCATED_MESSAGE_ARRAY ", \"incomplete\"]"
++#define SLOG_TRUNCATED_SIZE 25
++/* size of format for JSON for quotes_colon_space around key and comma_space
++   after value or closure_null */
++#define SLOG_JSON_FORMAT_SIZE 6
++#define SLOG_BUF_CALC_SIZE  SLOG_BUF_SIZE - SLOG_TRUNCATED_SIZE
++
++/* private declarations */
++static void slog_log(void);
++static void slog_cleanup(void);
++static void slog_generate_auth_payload(char *);
++static void slog_escape_value(char *, char *, size_t);
++static void slog_get_safe_from_token(char *, const char *);
++static const char* slog_get_state_text(void);
++
++/* public functions */
++
++void
++slog_init(void)
++{
++	/* initialize only if we have log_format_keys */
++	if (options.num_log_format_keys != 0) {
++		slogctxt = xcalloc(1, sizeof(Structuredlogctxt));
++		if (slogctxt != NULL)
++			slog_cleanup();
++	}
++}
++
++void
++slog_pam_session_opened(void)
++{
++	if (slogctxt != NULL) {
++		slogctxt->session_state = SLOG_SESSION_OPEN;
++		slogctxt->pam_process_pid = getpid();
++	}
++}
++
++void
++slog_set_auth_data(int authenticated, const char *method, const char *user)
++{
++	if (slogctxt != NULL) {
++		slogctxt->auth_successful =
++		    authenticated ? SLOG_AUTHORIZED : SLOG_UNAUTHORIZED;
++		strlcpy(slogctxt->method, method, SLOG_SHORT_STRING_LEN);
++		strlcpy(slogctxt->user, user, SLOG_STRING_LEN);
++	}
++}
++
++void
++slog_set_cert_id(const char *id)
++{
++	if (slogctxt != NULL)
++		strlcpy(slogctxt->cert_id, id, SLOG_STRING_LEN);
++}
++
++
++void
++slog_set_cert_serial(unsigned long long serial)
++{
++	if (slogctxt != NULL)
++		slogctxt->cert_serial = serial;
++}
++
++void
++slog_set_connection(const char *remote_ip, int remote_port,
++    const char *server_ip, int server_port, const char *session)
++{
++	if (slogctxt != NULL) {
++		strlcpy(slogctxt->remote_ip, remote_ip, SLOG_SHORT_STRING_LEN);
++		slogctxt->remote_port = remote_port;
++		strlcpy(slogctxt->server_ip, server_ip, SLOG_SHORT_STRING_LEN);
++		slogctxt->server_port = server_port;
++		strlcpy(slogctxt->session_id, session, SLOG_STRING_LEN);
++		slogctxt->start_time = time(NULL);
++		slogctxt->main_pid = getpid();
++	}
++}
++
++void
++slog_set_client_version(const char *version)
++{
++	if (slogctxt != NULL) {
++		if (strlen(version) < SLOG_STRING_LEN)
++			strlcpy(slogctxt->client_version, version, SLOG_STRING_LEN);
++		else {
++			// version can be up to 256 bytes, truncate to 95 and add ' ...'
++			// which will fit in SLOG_STRING_LEN
++			snprintf(slogctxt->client_version, SLOG_STRING_LEN, "%.95s ...", version);
++		}
++	}
++}
++
++void
++slog_set_command(const char *command)
++{
++	if (slogctxt != NULL) {
++		if (strlen(command) < SLOG_LONG_STRING_LEN)
++			strlcpy(slogctxt->command, command, SLOG_LONG_STRING_LEN);
++		else {
++			// If command is longer than allowed we truncate it to
++			// 1995 (SLOG_LONG_STRING_LEN - 5) characters and add ' ...\0' to
++			// the end of the command.
++			snprintf(slogctxt->command, SLOG_LONG_STRING_LEN, "%.1995s ...", command);
++		}
++	}
++}
++
++void
++slog_set_principal(const char *principal)
++{
++	if (slogctxt != NULL)
++		strlcpy(slogctxt->principal, principal, SLOG_STRING_LEN);
++}
++
++void
++slog_set_user(const char *user)
++{
++	if (slogctxt != NULL)
++		strlcpy(slogctxt->user, user, SLOG_STRING_LEN);
++}
++
++void
++slog_set_auth_info(const char *auth_info)
++{
++	if (slogctxt != NULL)
++		strlcpy(slogctxt->auth_info, auth_info, SLOG_MEDIUM_STRING_LEN);
++}
++
++void
++slog_exit_handler(void)
++{
++	/* to prevent duplicate logging we only log based on the pid set */
++	if (slogctxt != NULL) {
++		if (slogctxt->server_ip[0] == 0)
++			return; // not initialized
++		if (slogctxt->main_pid != getpid())
++			return; // not main process
++		if (slogctxt->session_state == SLOG_SESSION_INIT)
++			slog_log();
++		else {
++			slogctxt->session_state = SLOG_SESSION_CLOSED;
++			slogctxt->end_time = time(NULL);
++			slogctxt->duration = slogctxt->end_time - slogctxt->start_time;
++			slog_log();
++			slog_cleanup();
++		}
++  }
++}
++
++void
++slog_log_session(void)
++{
++	if (slogctxt != NULL) {
++		slogctxt->session_state = SLOG_SESSION_OPEN;
++		slog_log();
++	}
++}
++
++/* private function scope begin */
++
++static void
++slog_log(void)
++{
++	char *buffer = xmalloc(SLOG_BUF_SIZE);
++
++	if (buffer == NULL)
++		return;
++
++	memset(buffer, 0, SLOG_BUF_SIZE);
++
++	if (options.num_log_format_keys > 0
++	    && slogctxt != NULL
++	    && slogctxt->server_ip[0] != 0
++	    && slogctxt->user[0] != 0) {
++		slog_generate_auth_payload(buffer);
++		do_log_slog_payload(buffer);
++	}
++
++	free(buffer);
++}
++
++static void
++slog_cleanup(void)
++{
++	// Reset the log context values
++	if (slogctxt != NULL) {
++		memset(slogctxt, 0, sizeof(Structuredlogctxt));
++		slogctxt->session_state = SLOG_SESSION_INIT;
++		slogctxt->auth_successful = SLOG_UNAUTHORIZED;
++	}
++}
++
++/* We use debug3 since the debug is very noisy */
++static void
++slog_generate_auth_payload(char *buf)
++{
++	if (buf == NULL)
++		return;
++
++	// Create large buffer so don't risk overflow
++	char *safe = xmalloc(SLOG_BUF_SIZE);
++	memset(safe, 0, SLOG_BUF_SIZE);
++
++	if (safe == NULL)
++		return;
++
++	int i;
++	size_t remaining;
++	int json = options.log_format_json;
++	int keys = options.num_log_format_keys;
++	int truncated = 0;
++	char *tmpbuf = buf;
++	char *key;
++
++	debug3("JSON format is %d with %d tokens.", json, keys);
++
++	if (options.log_format_prefix != NULL
++	    && strlen(options.log_format_prefix) < SLOG_BUF_CALC_SIZE-1) {
++		tmpbuf += snprintf(tmpbuf, SLOG_BUF_CALC_SIZE,
++	    "%s ", options.log_format_prefix);
++	}
++	*tmpbuf++ = (json) ? '{' : '[';
++	debug3("current buffer after prefix: %s", buf);
++
++	// Loop through the keys filling out the output string
++	for (i = 0; i < keys; i++) {
++		safe[0] = 0;  // clear safe string
++		key = options.log_format_keys[i];
++		remaining = SLOG_BUF_CALC_SIZE - (tmpbuf - buf);
++
++		if (key == NULL)
++			continue;  // Shouldn't happen but if null go to next key
++
++		slog_get_safe_from_token(safe, key);
++		debug3("token: %s, value: %s", key, safe);
++
++		if (json) {
++			if (*safe == '\0')
++				continue; // No value since we are using key pairs skip
++			if (remaining <= SLOG_JSON_FORMAT_SIZE + strlen(key) + strlen(safe)) {
++				debug("Log would exceed buffer size %u, %zu, %zu at key: %s",
++				    (unsigned int)remaining, strlen(key), strlen(safe), key);
++				truncated = 1;
++				break;
++			}
++			tmpbuf += snprintf(tmpbuf, remaining, "%s\"%s\": %s",
++			    i > 0 ? ", " : "", key, safe);
++		} else {
++			if (*safe == '\0')
++				strlcpy(safe, "\"\"", SLOG_SHORT_STRING_LEN);
++			if (remaining < SLOG_JSON_FORMAT_SIZE + strlen(safe)) {
++				debug("Log would exceed remaining buffer size %d, %zu, at key: %s",
++				    (unsigned int)remaining, strlen(safe), key);
++				truncated = 1;
++				break;
++			}
++			tmpbuf += snprintf(tmpbuf, remaining, "%s%s", i > 0 ? ", " : "", safe);
++		}
++		debug3("current buffer after token: %s", buf);
++		debug3("end of loop key: %s, %d out of %d keys", key, i + 1, keys);
++	}
++
++	// Close the log string. If truncated set truncated message and close string
++	if (truncated == 1)
++		strlcpy(tmpbuf, json ? SLOG_TRUNCATED_MESSAGE_JSON :
++		    SLOG_TRUNCATED_MESSAGE_ARRAY, SLOG_TRUNCATED_SIZE);
++	else {
++		*tmpbuf++ = (json) ? '}' : ']';
++	}
++
++	free(safe);
++}
++
++// buffer size is input string * 2 +1
++static void
++slog_escape_value(char *output, char *input, size_t buffer_size)
++{
++	int i;
++	buffer_size -= 2;
++	if (input != NULL) {
++		int input_size = strlen(input);
++		char *temp = output;
++		*temp++ = '"';
++		buffer_size--;
++		for (i = 0; i < input_size && buffer_size > 0; i++) {
++			switch(input[i]) {
++			// characters escaped are the same as folly::json::escapeString
++			case 27: // <escape> ascii control character
++				if (buffer_size > 6) {
++					*temp++ = '\\';
++					*temp++ = 'u';
++					*temp++ = '0';
++					*temp++ = '0';
++					*temp++ = '1';
++					*temp++ = 'b';
++					buffer_size -= 6;
++				}
++			case '\b':
++				if (buffer_size > 1) {
++					*temp++ = '\\';
++					*temp++ = 'b';
++					buffer_size -= 2;
++				}
++				break;
++			case '\f':
++				if (buffer_size > 1) {
++					*temp++ = '\\';
++					*temp++ = 'f';
++					buffer_size -= 2;
++				}
++				break;
++			case '\n':
++				if (buffer_size > 1) {
++					*temp++ = '\\';
++					*temp++ = 'n';
++					buffer_size -= 2;
++					}
++				break;
++			case '\r':
++				if (buffer_size > 1) {
++					*temp++ = '\\';
++					*temp++ = 'r';
++					buffer_size -= 2;
++				}
++				break;
++			case '\t':
++				if (buffer_size > 1) {
++					*temp++ = '\\';
++					*temp++ = 't';
++					buffer_size -= 2;
++				}
++				break;
++			case '\"':
++			case '\\':
++				if (buffer_size > 1) {
++					*temp++ = '\\';
++					buffer_size--;
++			}
++			default:  // Non-escape char
++				*temp++ = input[i];
++				buffer_size--;
++			}
++		}
++		*temp++ = '"';
++		*temp++ = '\0';
++	}
++}
++
++static void
++slog_get_safe_from_token(char *output, const char *token)
++{
++	if (output == NULL || token == NULL || slogctxt == NULL)
++		return;
++
++	if (strcmp(token, server_ip_token) == 0) {
++		if (slogctxt->server_ip[0] != 0) {
++			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%s\"",
++			    slogctxt->server_ip);
++		}
++		return;
++	}
++
++	if (strcmp(token, server_port_token) == 0) {
++		if (slogctxt->server_port > 0) {
++			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"",
++			slogctxt->server_port);
++		}
++		return;
++	}
++
++	if (strcmp(token, remote_ip_token) == 0) {
++		if (slogctxt->remote_ip[0] != 0) {
++			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%s\"",
++			    slogctxt->remote_ip);
++		}
++		return;
++	}
++
++	if (strcmp(token, remote_port_token) == 0) {
++		if (slogctxt->remote_port > 0) {
++			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"",
++			    slogctxt->remote_port);
++		}
++		return;
++	}
++
++	if (strcmp(token, pam_pid_token) == 0) {
++		if (slogctxt->pam_process_pid > 0) {
++			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%ld\"",
++			    (long)slogctxt->pam_process_pid);
++		}
++		return;
++	}
++
++	if (strcmp(token, process_pid_token) == 0) {
++		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%ld\"", (long)getpid());
++		return;
++	}
++
++	if (strcmp(token, session_id_token) == 0) {
++		if (slogctxt->session_id[0] != 0) {
++				snprintf(output, SLOG_STRING_LEN, "\"%s\"",
++				    slogctxt->session_id);
++		}
++		return;
++	}
++
++	if (strcmp(token, method_token) == 0) {
++		if (slogctxt->method[0] != 0) {
++				snprintf(output, SLOG_STRING_LEN, "\"%s\"",
++				    slogctxt->method);
++		}
++		return;
++	}
++
++	// Arbitrary input
++	if (strcmp(token, cert_id_token) == 0) {
++		if (slogctxt->cert_id[0] != 0 &&
++		    strcmp(slogctxt->method, "publickey") == 0) {
++				slog_escape_value(output, slogctxt->cert_id,
++				    SLOG_STRING_LEN * 2 + 1);
++		}
++		return;
++	}
++
++	if (strcmp(token, cert_serial_token) == 0) {
++		if (slogctxt->cert_serial > 0 &&
++		    strcmp(slogctxt->method, "publickey") == 0) {
++			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%llu\"",
++			    slogctxt->cert_serial);
++		}
++		return;
++	}
++
++	// Arbitrary input
++	if (strcmp(token, principal_token) == 0) {
++		if (slogctxt->principal[0] != 0) {
++			slog_escape_value(output, slogctxt->principal,
++			    SLOG_STRING_LEN * 2 + 1);
++		}
++	return;
++	}
++
++	// Arbitrary input
++	if (strcmp(token, user_token) == 0) {
++		if (slogctxt->user[0] != 0) {
++			slog_escape_value(output, slogctxt->user,
++		    SLOG_STRING_LEN * 2 + 1);
++		}
++		return;
++	}
++
++	// Arbitrary input
++	if (strcmp(token, auth_info_token) == 0) {
++		if (slogctxt->auth_info[0] != 0) {
++			slog_escape_value(output, slogctxt->auth_info,
++			    SLOG_MEDIUM_STRING_LEN * 2 + 1);
++		}
++		return;
++	}
++
++	// Arbitrary input
++	if (strcmp(token, command_token) == 0) {
++		if (slogctxt->command[0] != 0) {
++			slog_escape_value(output, slogctxt->command,
++			    SLOG_LONG_STRING_LEN * 2 + 1);
++		}
++		return;
++	}
++
++	if (strcmp(token, auth_successful_token) == 0) {
++		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%s\"",
++		    slogctxt->auth_successful == SLOG_AUTHORIZED ? "true" : "false");
++		return;
++	}
++
++	if (strcmp(token, session_state_token) == 0) {
++		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%s\"",
++		    slog_get_state_text());
++		return;
++	}
++
++	if (strcmp(token, start_time_token) == 0) {
++		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"",
++		    (int)slogctxt->start_time);
++		return;
++	}
++
++	if (strcmp(token, end_time_token) == 0 && slogctxt->end_time > 0) {
++		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"",
++		    (int)slogctxt->end_time);
++		return;
++	}
++
++	if (strcmp(token, duration_token) == 0 && slogctxt->end_time > 0) {
++		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"", slogctxt->duration);
++		return;
++	}
++
++	if (strcmp(token, main_pid_token) == 0) {
++		if (slogctxt->main_pid > 0) {
++			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%ld\"",
++			    (long)slogctxt->main_pid);
++		}
++		return;
++	}
++
++	// Arbitrary input
++	if (strncmp(token, client_version, strlen(client_version)) == 0) {
++		if (slogctxt->client_version[0] != 0) {
++			slog_escape_value(output, slogctxt->client_version,
++			    SLOG_STRING_LEN + 2);
++		}
++		return;
++	}
++}
++
++static const char *
++slog_get_state_text(void)
++{
++	if (slogctxt == NULL)
++		return "";
++
++	switch (slogctxt->session_state) {
++		case SLOG_SESSION_INIT:
++			return "Session failed";
++		case SLOG_SESSION_OPEN:
++			return "Session opened";
++		case SLOG_SESSION_CLOSED:
++			return "Session closed";
++		default:
++			return "Unknown session state";  // Should never happen
++	}
++}
+Index: b/servconf.c
+===================================================================
+--- b.orig/servconf.c
++++ b/servconf.c
+@@ -204,6 +204,9 @@ initialize_server_options(ServerOptions
+ 	options->disable_forwarding = -1;
+ 	options->expose_userauth_info = -1;
+ 	options->rsa_min_size = -1;
++	options->log_format_prefix = NULL;
++	options->num_log_format_keys = 0;
++	options->log_format_json = -1;
+ }
+ 
+ /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
+@@ -473,6 +476,8 @@ fill_default_server_options(ServerOption
+ 		options->sk_provider = xstrdup("internal");
+ 	if (options->rsa_min_size == -1)
+ 		options->rsa_min_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
++	if (options->log_format_json == -1)
++		options->log_format_json = 0;
+ 
+ 	assemble_algorithms(options);
+ 
+@@ -553,6 +558,10 @@ typedef enum {
+ 	sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
+ 	sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
+ 	sRSAMinSize,
++	/* Structured Logging options.  Unless sLogFormatKeys is set,
++	    structured logging is disabled */
++	sLogFormatPrefix, sLogFormatKeys, sLogFormatJson,
++
+ 	sDeprecated, sIgnore, sUnsupported
+ } ServerOpCodes;
+ 
+@@ -730,6 +739,9 @@ static struct {
+ 	{ "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
+ 	{ "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
+ 	{ "rsaminsize", sRSAMinSize, SSHCFG_ALL },
++	{ "logformatprefix", sLogFormatPrefix, SSHCFG_GLOBAL },
++	{ "logformatkeys", sLogFormatKeys, SSHCFG_GLOBAL },
++	{ "logformatjson", sLogFormatJson, SSHCFG_GLOBAL },
+ 	{ NULL, sBadOption, 0 }
+ };
+ 
+@@ -2367,6 +2379,30 @@ process_server_config_line_depth(ServerO
+ 		}
+ 		break;
+ 
++	case sLogFormatPrefix:
++		arg = strdelim(&str);
++		if (!arg || *arg == '\0') {
++			fatal("%.200s line %d: invalid log format prefix",
++			    filename, linenum);
++		}
++		options->log_format_prefix = xstrdup(arg);
++		break;
++
++	case sLogFormatKeys:
++		while ((arg = strdelim(&str)) && *arg != '\0') {
++			if (options->num_log_format_keys >= MAX_LOGFORMAT_KEYS)
++				fatal("%s line %d: too long format keys.",
++				    filename, linenum);
++			if (!*activep)
++				continue;
++			options->log_format_keys[options->num_log_format_keys++] = xstrdup(arg);
++		}
++		break;
++
++	case sLogFormatJson:
++		intptr = &options->log_format_json;
++		goto parse_flag;
++
+ 	case sIPQoS:
+ 		arg = argv_next(&ac, &av);
+ 		if (!arg || *arg == '\0')
+@@ -3024,6 +3060,7 @@ dump_config(ServerOptions *o)
+ 	dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
+ 	dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash);
+ 	dump_cfg_fmtint(sExposeAuthInfo, o->expose_userauth_info);
++	dump_cfg_fmtint(sLogFormatJson, o->log_format_json);
+ 
+ 	/* string arguments */
+ 	dump_cfg_string(sPidFile, o->pid_file);
+@@ -3054,6 +3091,7 @@ dump_config(ServerOptions *o)
+ #if defined(__OpenBSD__) || defined(HAVE_SYS_SET_PROCESS_RDOMAIN)
+ 	dump_cfg_string(sRDomain, o->routing_domain);
+ #endif
++	dump_cfg_string(sLogFormatPrefix, o->log_format_prefix);
+ 
+ 	/* string arguments requiring a lookup */
+ 	dump_cfg_string(sLogLevel, log_level_name(o->log_level));
+@@ -3076,6 +3114,7 @@ dump_config(ServerOptions *o)
+ 	    o->num_auth_methods, o->auth_methods);
+ 	dump_cfg_strarray_oneline(sLogVerbose,
+ 	    o->num_log_verbose, o->log_verbose);
++	dump_cfg_strarray(sLogFormatKeys, o->num_log_format_keys, o->log_format_keys);
+ 
+ 	/* other arguments */
+ 	for (i = 0; i < o->num_subsystems; i++)
+Index: b/auth2-pubkey.c
+===================================================================
+--- b.orig/auth2-pubkey.c
++++ b/auth2-pubkey.c
+@@ -66,6 +66,7 @@
+ #include "monitor_wrap.h"
+ #include "authfile.h"
+ #include "match.h"
++#include "slog.h"
+ #include "ssherr.h"
+ #include "channels.h" /* XXX for session.h */
+ #include "session.h" /* XXX for child_set_env(); refactor? */
+@@ -389,6 +390,7 @@ check_principals_line(struct ssh *ssh, c
+ 		debug3("%s: matched principal \"%.100s\"",
+ 		    loc, cert->principals[i]);
+ 		found = 1;
++		slog_set_principal(cp);
+ 	}
+ 	if (found && authoptsp != NULL) {
+ 		*authoptsp = opts;
+@@ -714,6 +716,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);
+  success:
+ 	if (finalopts == NULL)
+ 		fatal_f("internal error: missing options");
+@@ -864,6 +867,7 @@ user_cert_trusted_ca(struct ssh *ssh, st
+ 		*authoptsp = final_opts;
+ 		final_opts = NULL;
+ 	}
++	slog_set_cert_id(key->cert->key_id);
+ 	ret = 1;
+  out:
+ 	sshauthopt_free(principals_opts);
+Index: b/regress/test-exec.sh
+===================================================================
+--- b.orig/regress/test-exec.sh
++++ b/regress/test-exec.sh
+@@ -689,7 +689,7 @@ start_sshd ()
+ 
+ 	trace "wait for sshd"
+ 	i=0;
+-	while [ ! -f $PIDFILE -a $i -lt 10 ]; do
++	while [ ! -f $PIDFILE -a $i -lt 3 ]; do
+ 		i=`expr $i + 1`
+ 		sleep $i
+ 	done
+Index: b/session.c
+===================================================================
+--- b.orig/session.c
++++ b/session.c
+@@ -96,6 +96,8 @@
+ #include "monitor_wrap.h"
+ #include "sftp.h"
+ #include "atomicio.h"
++#include "slog.h"
++
+ 
+ #if defined(KRB5) && defined(USE_AFS)
+ #include <kafs.h>
+@@ -753,6 +755,7 @@ do_exec(struct ssh *ssh, Session *s, con
+ 	    ssh_remote_ipaddr(ssh),
+ 	    ssh_remote_port(ssh),
+ 	    s->self);
++	    slog_log_session();
+ 
+ #ifdef SSH_AUDIT_EVENTS
+ 	if (s->command != NULL || s->command_handle != -1)
+@@ -1482,7 +1485,7 @@ do_setusercontext(struct passwd *pw)
+ 			perror("unable to set user context (setuser)");
+ 			exit(1);
+ 		}
+-		/* 
++		/*
+ 		 * FreeBSD's setusercontext() will not apply the user's
+ 		 * own umask setting unless running with the user's UID.
+ 		 */
+@@ -2159,6 +2162,7 @@ session_exec_req(struct ssh *ssh, Sessio
+ 	    (r = sshpkt_get_end(ssh)) != 0)
+ 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
+ 
++	slog_set_command(command);
+ 	success = do_exec(ssh, s, command) == 0;
+ 	free(command);
+ 	return success;
+@@ -2869,4 +2873,3 @@ session_get_remote_name_or_ip(struct ssh
+ 		remote = ssh_remote_ipaddr(ssh);
+ 	return remote;
+ }
+-
+Index: b/log.h
+===================================================================
+--- b.orig/log.h
++++ b/log.h
+@@ -133,4 +133,6 @@ void	 sshlogdirect(LogLevel, int, const
+ #define logdie_fr(r, ...)	sshlogdie(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__)
+ #define sigdie_fr(r, ...)	sshsigdie(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__)
+ 
++void     do_log_slog_payload(const char *);
++
+ #endif
+Index: b/log.c
+===================================================================
+--- b.orig/log.c
++++ b/log.c
+@@ -529,3 +529,39 @@ sshlogdirect(LogLevel level, int forced,
+ 	do_log(level, forced, NULL, fmt, args);
+ 	va_end(args);
+ }
++
++/* Custom function to log to syslog, to handle the session logging code
++ */
++void
++do_log_slog_payload(const char *payload)
++{
++#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
++	struct syslog_data sdata = SYSLOG_DATA_INIT;
++#endif
++	int pri = LOG_INFO;
++	int saved_errno = errno;
++	LogLevel level = SYSLOG_LEVEL_INFO;
++	log_handler_fn *tmp_handler;
++
++	if (log_handler != NULL) {
++		/* Avoid recursion */
++		tmp_handler = log_handler;
++		log_handler = NULL;
++		tmp_handler(level, 0, payload, log_handler_ctx);
++		log_handler = tmp_handler;
++	} else if (log_on_stderr) {
++		(void)write(log_stderr_fd, payload, strlen(payload));
++		(void)write(log_stderr_fd, "\r\n", 2);
++	} else {
++#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
++		openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata);
++		syslog_r(pri, &sdata, "%s", payload);
++		closelog_r(&sdata);
++#else
++		openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility);
++		syslog(pri, "%s", payload);
++		closelog();
++#endif
++	}
++	errno = saved_errno;
++}
+Index: b/slog.h
+===================================================================
+--- /dev/null
++++ b/slog.h
+@@ -0,0 +1,41 @@
++/*
++ * Copyright 2004-present Facebook. All Rights Reserved.
++ */
++#ifndef USE_SLOG
++#define USE_SLOG
++
++#define SLOG_STRING_LEN        100
++#define SLOG_MEDIUM_STRING_LEN 1000
++#define SLOG_SHORT_STRING_LEN  50
++#define SLOG_LONG_STRING_LEN   2000
++
++typedef enum {
++	SLOG_SESSION_INIT,
++	SLOG_SESSION_OPEN,
++	SLOG_SESSION_CLOSED,
++} SLOG_SESSION_STATE;
++
++typedef enum {
++	SLOG_UNAUTHORIZED = 0,
++	SLOG_AUTHORIZED = 1
++} SLOG_AUTHENTICATED;
++
++void	slog_init(void);
++
++// setters
++void	slog_pam_session_opened(void);
++void	slog_set_auth_data(int , const char *, const char *);
++void	slog_set_cert_id(const char *);
++void	slog_set_cert_serial(unsigned long long );
++void	slog_set_connection(const char *, int, const char *, int, const char *);
++void	slog_set_command(const char *);
++void	slog_set_principal(const char *);
++void	slog_set_user(const char *);
++void	slog_set_auth_info(const char *);
++void	slog_set_client_version(const char *);
++
++// loggers
++void	slog_exit_handler(void);
++void	slog_log_session(void);
++
++#endif
+Index: b/sshd.c
+===================================================================
+--- b.orig/sshd.c
++++ b/sshd.c
+@@ -132,6 +132,8 @@
+ #include "sk-api.h"
+ #include "srclimit.h"
+ #include "dh.h"
++#include "slog.h"
++#include <time.h>
+ 
+ /* Re-exec fds */
+ #define REEXEC_DEVCRYPTO_RESERVED_FD	(STDERR_FILENO + 1)
+@@ -2149,6 +2151,7 @@ main(int ac, char **av)
+ 	}
+ 	/* Reinitialize the log (because of the fork above). */
+ 	log_init(__progname, options.log_level, options.log_facility, log_stderr);
++	slog_init();
+ 
+ 	if (FIPS_mode()) {
+ 		debug("FIPS mode initialized");
+@@ -2315,8 +2318,15 @@ main(int ac, char **av)
+ 	    rdomain == NULL ? "" : " rdomain \"",
+ 	    rdomain == NULL ? "" : rdomain,
+ 	    rdomain == NULL ? "" : "\"");
+-	free(laddr);
+ 
++
++	slog_set_connection(remote_ip,
++	    remote_port,
++	    laddr,
++	    ssh_local_port(ssh),
++	    get_log_session_id());
++
++	free(laddr);
+ 	/*
+ 	 * We don't want to listen forever unless the other side
+ 	 * successfully authenticates itself.  So we set up an alarm which is
+@@ -2638,6 +2648,7 @@ cleanup_exit(int i)
+ 	if (in_cleanup)
+ 		_exit(i);
+ 	in_cleanup = 1;
++	slog_exit_handler();
+ 	if (the_active_state != NULL && the_authctxt != NULL) {
+ 		do_cleanup(the_active_state, the_authctxt);
+ 		if (use_privsep && privsep_is_preauth &&
+Index: b/sshd_config
+===================================================================
+--- b.orig/sshd_config
++++ b/sshd_config
+@@ -33,6 +33,15 @@ Include /etc/ssh/sshd_config.d/*.conf
+ # Logging
+ #SyslogFacility AUTH
+ #LogLevel INFO
++# Structured logging
++# The default is no structured logging, to enable structured logging at least
++# one LogFormatKeys must be set.  LogFormatJson defaults to no (array) to keep
++# the log line size smaller, if keys are desired set LogFormatJson to yes
++LogFormatPrefix sshd_auth_msg:
++LogFormatKeys server_ip server_port remote_ip remote_port pid session_id method
++LogFormatKeys cert_id cert_serial principal user session_state auth_successful
++LogFormatKeys start_time command
++LogFormatJson yes
+ 
+ # Authentication:
+ 
+Index: b/auth-pam.c
+===================================================================
+--- b.orig/auth-pam.c
++++ b/auth-pam.c
+@@ -94,6 +94,7 @@ extern char *__progname;
+ #include "auth-pam.h"
+ #include "canohost.h"
+ #include "log.h"
++#include "slog.h"
+ #include "msg.h"
+ #include "packet.h"
+ #include "misc.h"
+@@ -1223,9 +1224,12 @@ do_pam_session(struct ssh *ssh)
+ 	if (sshpam_err != PAM_SUCCESS)
+ 		fatal("PAM: failed to set PAM_CONV: %s",
+ 		    pam_strerror(sshpam_handle, sshpam_err));
++
+ 	sshpam_err = pam_open_session(sshpam_handle, 0);
+-	if (sshpam_err == PAM_SUCCESS)
++	if (sshpam_err == PAM_SUCCESS) {
++		slog_pam_session_opened();
+ 		sshpam_session_open = 1;
++	}
+ 	else {
+ 		sshpam_session_open = 0;
+ 		auth_restrict_session(ssh);
+Index: b/servconf.h
+===================================================================
+--- b.orig/servconf.h
++++ b/servconf.h
+@@ -22,6 +22,8 @@
+ 
+ #define MAX_SUBSYSTEMS		256	/* Max # subsystems. */
+ 
++#define MAX_LOGFORMAT_KEYS      256     /* Max # LogFormatKeys */
++
+ /* permit_root_login */
+ #define	PERMIT_NOT_SET		-1
+ #define	PERMIT_NO		0
+@@ -239,6 +241,12 @@ typedef struct {
+ 	u_int64_t timing_secret;
+ 	char   *sk_provider;
+ 	int	rsa_min_size;	/* minimum size of RSA keys */
++
++	char   *log_format_prefix;
++	u_int  num_log_format_keys;
++	char   *log_format_keys[MAX_LOGFORMAT_KEYS];
++	int log_format_json;	/* 1 to return "token": "token_val" in log format */
++
+ }       ServerOptions;
+ 
+ /* Information about the incoming connection as used by Match */
+Index: b/regress/slog.sh
+===================================================================
+--- /dev/null
++++ b/regress/slog.sh
+@@ -0,0 +1,59 @@
++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"
++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
++EOF
++
++
++cp $test_config $old_config
++start_sshd
++
++${SSH} -F $OBJ/ssh_config somehost true
++if [ $? -ne 0 ]; then
++	fail "ssh connect with failed"
++fi
++
++test_log_counts() {
++	cnt=$(grep -c "$log_prefix" "$TEST_SSHD_LOGFILE")
++	if [ $cnt -ne 2 ]; then
++		fail "expected 2 structured logging lines, got $cnt"
++	fi
++}
++
++test_json_valid() {
++	which python &>/dev/null || echo 'python not found in path, skipping tests'
++
++	loglines=$(cat "$TEST_SSHD_LOGFILE" | grep "$log_prefix")
++	first=$(echo "$loglines" | head -n1)
++	last=$(echo "$loglines" | tail -n1)
++
++	echo ${first:$(expr length $log_prefix)} | python -m json.tool &>/dev/null \
++	    || fail "invalid json structure $first"
++	echo ${last:$(expr length $log_prefix)} | python -m json.tool &>/dev/null  \
++	    || fail "invalid json structure $last"
++}
++
++test_log_counts
++test_json_valid
+Index: b/Makefile.in
+===================================================================
+--- b.orig/Makefile.in
++++ b/Makefile.in
+@@ -129,7 +129,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw
+ 	monitor.o monitor_wrap.o auth-krb5.o \
+ 	auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
+ 	loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
+-	srclimit.o sftp-server.o sftp-common.o \
++	srclimit.o sftp-server.o sftp-common.o sftp-realpath.o slog.o \
+ 	sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
+ 	sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \
+ 	sandbox-solaris.o uidswap.o $(SKOBJS)
+@@ -313,7 +313,7 @@ distclean:	regressclean
+ 	rm -f *.o *.a $(TARGETS) logintest config.cache config.log
+ 	rm -f *.out core opensshd.init openssh.xml
+ 	rm -f Makefile buildpkg.sh config.h config.status
+-	rm -f survey.sh openbsd-compat/regress/Makefile *~ 
++	rm -f survey.sh openbsd-compat/regress/Makefile *~
+ 	rm -rf autom4te.cache
+ 	rm -f regress/check-perm
+ 	rm -f regress/mkdtemp
+Index: b/auth.c
+===================================================================
+--- b.orig/auth.c
++++ b/auth.c
+@@ -76,6 +76,7 @@
+ #include "ssherr.h"
+ #include "compat.h"
+ #include "channels.h"
++#include "slog.h"
+ 
+ /* import */
+ extern ServerOptions options;
+@@ -351,6 +352,7 @@ auth_log(struct ssh *ssh, int authentica
+ 	    extra != NULL ? extra : "");
+ 
+ 	free(extra);
++	slog_set_auth_data(authenticated, method, authctxt->user);
+ 
+ #if defined(CUSTOM_FAILED_LOGIN) || defined(SSH_AUDIT_EVENTS)
+ 	if (authenticated == 0 && !(authctxt->postponed || partial)) {
+@@ -564,6 +566,7 @@ auth_openprincipals(const char *file, st
+ struct passwd *
+ getpwnamallow(struct ssh *ssh, const char *user)
+ {
++	slog_set_user(user);
+ #ifdef HAVE_LOGIN_CAP
+ 	extern login_cap_t *lc;
+ #ifdef BSD_AUTH
diff --git a/fbpatches/fb87_070_logging_reverse_port_forward.patch b/fbpatches/fb87_070_logging_reverse_port_forward.patch
deleted file mode 100644
index 339fd35..0000000
--- a/fbpatches/fb87_070_logging_reverse_port_forward.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-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);
diff --git a/fbpatches/fb87_080_logging_certificates.patch b/fbpatches/fb87_080_logging_certificates.patch
deleted file mode 100644
index 46cc276..0000000
--- a/fbpatches/fb87_080_logging_certificates.patch
+++ /dev/null
@@ -1,182 +0,0 @@
-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
diff --git a/fbpatches/fb87_090_logging_shell_cmd_pty.patch b/fbpatches/fb87_090_logging_shell_cmd_pty.patch
deleted file mode 100644
index 6017884..0000000
--- a/fbpatches/fb87_090_logging_shell_cmd_pty.patch
+++ /dev/null
@@ -1,73 +0,0 @@
-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
diff --git a/fbpatches/fb87_810_increase_ssh_cert_max_principals.patch b/fbpatches/fb87_810_increase_ssh_cert_max_principals.patch
deleted file mode 100644
index 2abe0e6..0000000
--- a/fbpatches/fb87_810_increase_ssh_cert_max_principals.patch
+++ /dev/null
@@ -1,13 +0,0 @@
-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 */
diff --git a/fbpatches/fb87_log_accept_env.patch b/fbpatches/fb87_log_accept_env.patch
deleted file mode 100644
index 24ba1fc..0000000
--- a/fbpatches/fb87_log_accept_env.patch
+++ /dev/null
@@ -1,22 +0,0 @@
-diff --git a/session.c b/session.c
---- a/session.c
-+++ b/session.c
-@@ -2106,16 +2106,16 @@
- 
- 	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;
- 			s->env[s->num_env].val = val;
- 			s->num_env++;
- 			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);
diff --git a/fbpatches/fb87_log_auth_info.patch b/fbpatches/fb87_log_auth_info.patch
deleted file mode 100644
index 45f320a..0000000
--- a/fbpatches/fb87_log_auth_info.patch
+++ /dev/null
@@ -1,216 +0,0 @@
-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);
diff --git a/fbpatches/fb87_log_port_forwards.patch b/fbpatches/fb87_log_port_forwards.patch
deleted file mode 100644
index 8a7b030..0000000
--- a/fbpatches/fb87_log_port_forwards.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-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);
- 
diff --git a/fbpatches/fb87_log_session_id.patch b/fbpatches/fb87_log_session_id.patch
deleted file mode 100644
index ac9d6a3..0000000
--- a/fbpatches/fb87_log_session_id.patch
+++ /dev/null
@@ -1,141 +0,0 @@
-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/auth-pam.c
-===================================================================
---- b.orig/auth-pam.c
-+++ b/auth-pam.c
-@@ -766,7 +766,20 @@ sshpam_init(struct ssh *ssh, Authctxt *a
- 		return (-1);
- 	}
- #endif
--	return (0);
-+	debug("PAM: setting PAM LOG_SESSION_ID to \"%s\"", get_log_session_id());
-+	{
-+		char log_session_id_env[HOST_NAME_MAX + 50];
-+		snprintf(log_session_id_env, sizeof(log_session_id_env),
-+				"LOG_SESSION_ID=%s", get_log_session_id());
-+		sshpam_err = pam_putenv(sshpam_handle, log_session_id_env);
-+		if (sshpam_err != PAM_SUCCESS) {
-+			pam_end(sshpam_handle, sshpam_err);
-+			sshpam_handle = NULL;
-+			return (-1);
-+		}
-+	}
-+
-+        return (0);
- }
- 
- static void
-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)));
diff --git a/fbpatches/fb87_pass_principals_to_child.patch b/fbpatches/fb87_pass_principals_to_child.patch
deleted file mode 100644
index 27bb7ca..0000000
--- a/fbpatches/fb87_pass_principals_to_child.patch
+++ /dev/null
@@ -1,224 +0,0 @@
-diff --git a/session.c b/session.c
---- a/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>
-@@ -990,11 +991,18 @@
- 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
-@@ -1188,9 +1196,57 @@
- 		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);
-+			}
-+		}
-+	}
- 
- 	if (debug_flag) {
- 		/* dump the environment */
-diff --git a/regress/cert-princ-env.sh b/regress/cert-princ-env.sh
-new file mode 100644
---- /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"
diff --git a/fbpatches/fb87_slog.patch b/fbpatches/fb87_slog.patch
deleted file mode 100644
index 427cdda..0000000
--- a/fbpatches/fb87_slog.patch
+++ /dev/null
@@ -1,1148 +0,0 @@
-Index: b/slog.c
-===================================================================
---- /dev/null
-+++ b/slog.c
-@@ -0,0 +1,619 @@
-+/*
-+ * Copyright 2004-present Facebook. All Rights Reserved.
-+ */
-+
-+ /* When using slogctxt in any module perform a NULL pointer check */
-+
-+#include <sys/types.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <stdio.h>
-+#include <string.h>
-+#include <syslog.h>
-+#include <pwd.h>
-+#include <time.h>
-+
-+#include "includes.h"
-+#include "slog.h"
-+#include "log.h"
-+#include "misc.h"
-+#include "servconf.h"
-+#include "xmalloc.h"
-+
-+typedef struct Structuredlogctxt Structuredlogctxt;
-+
-+struct Structuredlogctxt { /* items we care about logging */
-+	char		server_ip[SLOG_SHORT_STRING_LEN];		// server_ip
-+	int		server_port;		// server_port
-+	char		remote_ip[SLOG_SHORT_STRING_LEN];		// remote_ip
-+	int		remote_port;		// remote_port
-+	pid_t		pam_process_pid;		// pam_pid
-+	char		session_id[SLOG_STRING_LEN];		// session_id
-+	char		method[SLOG_STRING_LEN];		// method
-+	char		cert_id[SLOG_STRING_LEN];		// cert_id
-+	unsigned long long		cert_serial;		// cert_serial
-+	char		principal[SLOG_STRING_LEN];		// principal
-+	char		user[SLOG_STRING_LEN];		// user
-+	char		command[SLOG_LONG_STRING_LEN];		// command
-+	SLOG_SESSION_STATE		session_state;		// session_state
-+	SLOG_AUTHENTICATED		auth_successful;		// auth_successful
-+	time_t		start_time;		// start_time
-+	time_t		end_time;		// end_time
-+	int		duration;		// duration
-+	pid_t		main_pid;		// main_pid
-+	char		auth_info[SLOG_MEDIUM_STRING_LEN];		// auth_info
-+	char		client_version[SLOG_STRING_LEN];		// client_version
-+};
-+
-+Structuredlogctxt *slogctxt;
-+extern ServerOptions options;
-+
-+// Define token constants and default syntax format
-+static const char *server_ip_token         = "server_ip";
-+static const char *server_port_token       = "server_port";
-+static const char *remote_ip_token         = "remote_ip";
-+static const char *remote_port_token       = "remote_port";
-+static const char *pam_pid_token           = "pam_pid";
-+static const char *process_pid_token       = "pid";
-+static const char *session_id_token        = "session_id";
-+static const char *method_token            = "method";
-+static const char *cert_id_token           = "cert_id";
-+static const char *cert_serial_token       = "cert_serial";
-+static const char *principal_token         = "principal";
-+static const char *user_token              = "user";
-+static const char *command_token           = "command";
-+static const char *session_state_token     = "session_state";
-+static const char *auth_successful_token   = "auth_successful";
-+static const char *start_time_token        = "start_time";
-+static const char *end_time_token          = "end_time";
-+static const char *duration_token          = "duration";
-+static const char *main_pid_token          = "main_pid";
-+static const char *auth_info_token         = "auth_info";
-+static const char *client_version          = "client_version";
-+
-+/* Example log format sshd_config
-+ * LogFormatPrefix sshd_auth_msg:
-+ * LogFormatKeys server_ip server_port remote_ip remote_port pid session_id /
-+   method cert_id cert_serial principal user session_state auth_successful /
-+   start_time command # NO LINE BREAKS
-+ * LogFormatJson yes # no makes this output an json array vs json object
-+ */
-+
-+// Set a arbitrary large size so we can feed a potentially
-+// large json object to the logger
-+#define SLOG_BUF_SIZE 8192
-+#define SLOG_TRUNCATED_MESSAGE_JSON ", \"incomplete\": \"true\"}"
-+#define SLOG_TRUNCATED_MESSAGE_ARRAY ", \"incomplete\"]"
-+#define SLOG_TRUNCATED_SIZE 25
-+/* size of format for JSON for quotes_colon_space around key and comma_space
-+   after value or closure_null */
-+#define SLOG_JSON_FORMAT_SIZE 6
-+#define SLOG_BUF_CALC_SIZE  SLOG_BUF_SIZE - SLOG_TRUNCATED_SIZE
-+
-+/* private declarations */
-+static void slog_log(void);
-+static void slog_cleanup(void);
-+static void slog_generate_auth_payload(char *);
-+static void slog_escape_value(char *, char *, size_t);
-+static void slog_get_safe_from_token(char *, const char *);
-+static const char* slog_get_state_text(void);
-+
-+/* public functions */
-+
-+void
-+slog_init(void)
-+{
-+	/* initialize only if we have log_format_keys */
-+	if (options.num_log_format_keys != 0) {
-+		slogctxt = xcalloc(1, sizeof(Structuredlogctxt));
-+		if (slogctxt != NULL)
-+			slog_cleanup();
-+	}
-+}
-+
-+void
-+slog_pam_session_opened(void)
-+{
-+	if (slogctxt != NULL) {
-+		slogctxt->session_state = SLOG_SESSION_OPEN;
-+		slogctxt->pam_process_pid = getpid();
-+	}
-+}
-+
-+void
-+slog_set_auth_data(int authenticated, const char *method, const char *user)
-+{
-+	if (slogctxt != NULL) {
-+		slogctxt->auth_successful =
-+		    authenticated ? SLOG_AUTHORIZED : SLOG_UNAUTHORIZED;
-+		strlcpy(slogctxt->method, method, SLOG_SHORT_STRING_LEN);
-+		strlcpy(slogctxt->user, user, SLOG_STRING_LEN);
-+	}
-+}
-+
-+void
-+slog_set_cert_id(const char *id)
-+{
-+	if (slogctxt != NULL)
-+		strlcpy(slogctxt->cert_id, id, SLOG_STRING_LEN);
-+}
-+
-+
-+void
-+slog_set_cert_serial(unsigned long long serial)
-+{
-+	if (slogctxt != NULL)
-+		slogctxt->cert_serial = serial;
-+}
-+
-+void
-+slog_set_connection(const char *remote_ip, int remote_port,
-+    const char *server_ip, int server_port, const char *session)
-+{
-+	if (slogctxt != NULL) {
-+		strlcpy(slogctxt->remote_ip, remote_ip, SLOG_SHORT_STRING_LEN);
-+		slogctxt->remote_port = remote_port;
-+		strlcpy(slogctxt->server_ip, server_ip, SLOG_SHORT_STRING_LEN);
-+		slogctxt->server_port = server_port;
-+		strlcpy(slogctxt->session_id, session, SLOG_STRING_LEN);
-+		slogctxt->start_time = time(NULL);
-+		slogctxt->main_pid = getpid();
-+	}
-+}
-+
-+void
-+slog_set_client_version(const char *version)
-+{
-+	if (slogctxt != NULL) {
-+		if (strlen(version) < SLOG_STRING_LEN)
-+			strlcpy(slogctxt->client_version, version, SLOG_STRING_LEN);
-+		else {
-+			// version can be up to 256 bytes, truncate to 95 and add ' ...'
-+			// which will fit in SLOG_STRING_LEN
-+			snprintf(slogctxt->client_version, SLOG_STRING_LEN, "%.95s ...", version);
-+		}
-+	}
-+}
-+
-+void
-+slog_set_command(const char *command)
-+{
-+	if (slogctxt != NULL) {
-+		if (strlen(command) < SLOG_LONG_STRING_LEN)
-+			strlcpy(slogctxt->command, command, SLOG_LONG_STRING_LEN);
-+		else {
-+			// If command is longer than allowed we truncate it to
-+			// 1995 (SLOG_LONG_STRING_LEN - 5) characters and add ' ...\0' to
-+			// the end of the command.
-+			snprintf(slogctxt->command, SLOG_LONG_STRING_LEN, "%.1995s ...", command);
-+		}
-+	}
-+}
-+
-+void
-+slog_set_principal(const char *principal)
-+{
-+	if (slogctxt != NULL)
-+		strlcpy(slogctxt->principal, principal, SLOG_STRING_LEN);
-+}
-+
-+void
-+slog_set_user(const char *user)
-+{
-+	if (slogctxt != NULL)
-+		strlcpy(slogctxt->user, user, SLOG_STRING_LEN);
-+}
-+
-+void
-+slog_set_auth_info(const char *auth_info)
-+{
-+	if (slogctxt != NULL)
-+		strlcpy(slogctxt->auth_info, auth_info, SLOG_MEDIUM_STRING_LEN);
-+}
-+
-+void
-+slog_exit_handler(void)
-+{
-+	/* to prevent duplicate logging we only log based on the pid set */
-+	if (slogctxt != NULL) {
-+		if (slogctxt->server_ip[0] == 0)
-+			return; // not initialized
-+		if (slogctxt->main_pid != getpid())
-+			return; // not main process
-+		if (slogctxt->session_state == SLOG_SESSION_INIT)
-+			slog_log();
-+		else {
-+			slogctxt->session_state = SLOG_SESSION_CLOSED;
-+			slogctxt->end_time = time(NULL);
-+			slogctxt->duration = slogctxt->end_time - slogctxt->start_time;
-+			slog_log();
-+			slog_cleanup();
-+		}
-+  }
-+}
-+
-+void
-+slog_log_session(void)
-+{
-+	if (slogctxt != NULL) {
-+		slogctxt->session_state = SLOG_SESSION_OPEN;
-+		slog_log();
-+	}
-+}
-+
-+/* private function scope begin */
-+
-+static void
-+slog_log(void)
-+{
-+	char *buffer = xmalloc(SLOG_BUF_SIZE);
-+
-+	if (buffer == NULL)
-+		return;
-+
-+	memset(buffer, 0, SLOG_BUF_SIZE);
-+
-+	if (options.num_log_format_keys > 0
-+	    && slogctxt != NULL
-+	    && slogctxt->server_ip[0] != 0
-+	    && slogctxt->user[0] != 0) {
-+		slog_generate_auth_payload(buffer);
-+		do_log_slog_payload(buffer);
-+	}
-+
-+	free(buffer);
-+}
-+
-+static void
-+slog_cleanup(void)
-+{
-+	// Reset the log context values
-+	if (slogctxt != NULL) {
-+		memset(slogctxt, 0, sizeof(Structuredlogctxt));
-+		slogctxt->session_state = SLOG_SESSION_INIT;
-+		slogctxt->auth_successful = SLOG_UNAUTHORIZED;
-+	}
-+}
-+
-+/* We use debug3 since the debug is very noisy */
-+static void
-+slog_generate_auth_payload(char *buf)
-+{
-+	if (buf == NULL)
-+		return;
-+
-+	// Create large buffer so don't risk overflow
-+	char *safe = xmalloc(SLOG_BUF_SIZE);
-+	memset(safe, 0, SLOG_BUF_SIZE);
-+
-+	if (safe == NULL)
-+		return;
-+
-+	int i;
-+	size_t remaining;
-+	int json = options.log_format_json;
-+	int keys = options.num_log_format_keys;
-+	int truncated = 0;
-+	char *tmpbuf = buf;
-+	char *key;
-+
-+	debug3("JSON format is %d with %d tokens.", json, keys);
-+
-+	if (options.log_format_prefix != NULL
-+	    && strlen(options.log_format_prefix) < SLOG_BUF_CALC_SIZE-1) {
-+		tmpbuf += snprintf(tmpbuf, SLOG_BUF_CALC_SIZE,
-+	    "%s ", options.log_format_prefix);
-+	}
-+	*tmpbuf++ = (json) ? '{' : '[';
-+	debug3("current buffer after prefix: %s", buf);
-+
-+	// Loop through the keys filling out the output string
-+	for (i = 0; i < keys; i++) {
-+		safe[0] = 0;  // clear safe string
-+		key = options.log_format_keys[i];
-+		remaining = SLOG_BUF_CALC_SIZE - (tmpbuf - buf);
-+
-+		if (key == NULL)
-+			continue;  // Shouldn't happen but if null go to next key
-+
-+		slog_get_safe_from_token(safe, key);
-+		debug3("token: %s, value: %s", key, safe);
-+
-+		if (json) {
-+			if (*safe == '\0')
-+				continue; // No value since we are using key pairs skip
-+			if (remaining <= SLOG_JSON_FORMAT_SIZE + strlen(key) + strlen(safe)) {
-+				debug("Log would exceed buffer size %u, %zu, %zu at key: %s",
-+				    (unsigned int)remaining, strlen(key), strlen(safe), key);
-+				truncated = 1;
-+				break;
-+			}
-+			tmpbuf += snprintf(tmpbuf, remaining, "%s\"%s\": %s",
-+			    i > 0 ? ", " : "", key, safe);
-+		} else {
-+			if (*safe == '\0')
-+				strlcpy(safe, "\"\"", SLOG_SHORT_STRING_LEN);
-+			if (remaining < SLOG_JSON_FORMAT_SIZE + strlen(safe)) {
-+				debug("Log would exceed remaining buffer size %d, %zu, at key: %s",
-+				    (unsigned int)remaining, strlen(safe), key);
-+				truncated = 1;
-+				break;
-+			}
-+			tmpbuf += snprintf(tmpbuf, remaining, "%s%s", i > 0 ? ", " : "", safe);
-+		}
-+		debug3("current buffer after token: %s", buf);
-+		debug3("end of loop key: %s, %d out of %d keys", key, i + 1, keys);
-+	}
-+
-+	// Close the log string. If truncated set truncated message and close string
-+	if (truncated == 1)
-+		strlcpy(tmpbuf, json ? SLOG_TRUNCATED_MESSAGE_JSON :
-+		    SLOG_TRUNCATED_MESSAGE_ARRAY, SLOG_TRUNCATED_SIZE);
-+	else {
-+		*tmpbuf++ = (json) ? '}' : ']';
-+	}
-+
-+	free(safe);
-+}
-+
-+// buffer size is input string * 2 +1
-+static void
-+slog_escape_value(char *output, char *input, size_t buffer_size)
-+{
-+	int i;
-+	buffer_size -= 2;
-+	if (input != NULL) {
-+		int input_size = strlen(input);
-+		char *temp = output;
-+		*temp++ = '"';
-+		buffer_size--;
-+		for (i = 0; i < input_size && buffer_size > 0; i++) {
-+			switch(input[i]) {
-+			// characters escaped are the same as folly::json::escapeString
-+			case 27: // <escape> ascii control character
-+				if (buffer_size > 6) {
-+					*temp++ = '\\';
-+					*temp++ = 'u';
-+					*temp++ = '0';
-+					*temp++ = '0';
-+					*temp++ = '1';
-+					*temp++ = 'b';
-+					buffer_size -= 6;
-+				}
-+			case '\b':
-+				if (buffer_size > 1) {
-+					*temp++ = '\\';
-+					*temp++ = 'b';
-+					buffer_size -= 2;
-+				}
-+				break;
-+			case '\f':
-+				if (buffer_size > 1) {
-+					*temp++ = '\\';
-+					*temp++ = 'f';
-+					buffer_size -= 2;
-+				}
-+				break;
-+			case '\n':
-+				if (buffer_size > 1) {
-+					*temp++ = '\\';
-+					*temp++ = 'n';
-+					buffer_size -= 2;
-+					}
-+				break;
-+			case '\r':
-+				if (buffer_size > 1) {
-+					*temp++ = '\\';
-+					*temp++ = 'r';
-+					buffer_size -= 2;
-+				}
-+				break;
-+			case '\t':
-+				if (buffer_size > 1) {
-+					*temp++ = '\\';
-+					*temp++ = 't';
-+					buffer_size -= 2;
-+				}
-+				break;
-+			case '\"':
-+			case '\\':
-+				if (buffer_size > 1) {
-+					*temp++ = '\\';
-+					buffer_size--;
-+			}
-+			default:  // Non-escape char
-+				*temp++ = input[i];
-+				buffer_size--;
-+			}
-+		}
-+		*temp++ = '"';
-+		*temp++ = '\0';
-+	}
-+}
-+
-+static void
-+slog_get_safe_from_token(char *output, const char *token)
-+{
-+	if (output == NULL || token == NULL || slogctxt == NULL)
-+		return;
-+
-+	if (strcmp(token, server_ip_token) == 0) {
-+		if (slogctxt->server_ip[0] != 0) {
-+			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%s\"",
-+			    slogctxt->server_ip);
-+		}
-+		return;
-+	}
-+
-+	if (strcmp(token, server_port_token) == 0) {
-+		if (slogctxt->server_port > 0) {
-+			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"",
-+			slogctxt->server_port);
-+		}
-+		return;
-+	}
-+
-+	if (strcmp(token, remote_ip_token) == 0) {
-+		if (slogctxt->remote_ip[0] != 0) {
-+			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%s\"",
-+			    slogctxt->remote_ip);
-+		}
-+		return;
-+	}
-+
-+	if (strcmp(token, remote_port_token) == 0) {
-+		if (slogctxt->remote_port > 0) {
-+			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"",
-+			    slogctxt->remote_port);
-+		}
-+		return;
-+	}
-+
-+	if (strcmp(token, pam_pid_token) == 0) {
-+		if (slogctxt->pam_process_pid > 0) {
-+			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%ld\"",
-+			    (long)slogctxt->pam_process_pid);
-+		}
-+		return;
-+	}
-+
-+	if (strcmp(token, process_pid_token) == 0) {
-+		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%ld\"", (long)getpid());
-+		return;
-+	}
-+
-+	if (strcmp(token, session_id_token) == 0) {
-+		if (slogctxt->session_id[0] != 0) {
-+				snprintf(output, SLOG_STRING_LEN, "\"%s\"",
-+				    slogctxt->session_id);
-+		}
-+		return;
-+	}
-+
-+	if (strcmp(token, method_token) == 0) {
-+		if (slogctxt->method[0] != 0) {
-+				snprintf(output, SLOG_STRING_LEN, "\"%s\"",
-+				    slogctxt->method);
-+		}
-+		return;
-+	}
-+
-+	// Arbitrary input
-+	if (strcmp(token, cert_id_token) == 0) {
-+		if (slogctxt->cert_id[0] != 0 &&
-+		    strcmp(slogctxt->method, "publickey") == 0) {
-+				slog_escape_value(output, slogctxt->cert_id,
-+				    SLOG_STRING_LEN * 2 + 1);
-+		}
-+		return;
-+	}
-+
-+	if (strcmp(token, cert_serial_token) == 0) {
-+		if (slogctxt->cert_serial > 0 &&
-+		    strcmp(slogctxt->method, "publickey") == 0) {
-+			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%llu\"",
-+			    slogctxt->cert_serial);
-+		}
-+		return;
-+	}
-+
-+	// Arbitrary input
-+	if (strcmp(token, principal_token) == 0) {
-+		if (slogctxt->principal[0] != 0) {
-+			slog_escape_value(output, slogctxt->principal,
-+			    SLOG_STRING_LEN * 2 + 1);
-+		}
-+	return;
-+	}
-+
-+	// Arbitrary input
-+	if (strcmp(token, user_token) == 0) {
-+		if (slogctxt->user[0] != 0) {
-+			slog_escape_value(output, slogctxt->user,
-+		    SLOG_STRING_LEN * 2 + 1);
-+		}
-+		return;
-+	}
-+
-+	// Arbitrary input
-+	if (strcmp(token, auth_info_token) == 0) {
-+		if (slogctxt->auth_info[0] != 0) {
-+			slog_escape_value(output, slogctxt->auth_info,
-+			    SLOG_MEDIUM_STRING_LEN * 2 + 1);
-+		}
-+		return;
-+	}
-+
-+	// Arbitrary input
-+	if (strcmp(token, command_token) == 0) {
-+		if (slogctxt->command[0] != 0) {
-+			slog_escape_value(output, slogctxt->command,
-+			    SLOG_LONG_STRING_LEN * 2 + 1);
-+		}
-+		return;
-+	}
-+
-+	if (strcmp(token, auth_successful_token) == 0) {
-+		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%s\"",
-+		    slogctxt->auth_successful == SLOG_AUTHORIZED ? "true" : "false");
-+		return;
-+	}
-+
-+	if (strcmp(token, session_state_token) == 0) {
-+		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%s\"",
-+		    slog_get_state_text());
-+		return;
-+	}
-+
-+	if (strcmp(token, start_time_token) == 0) {
-+		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"",
-+		    (int)slogctxt->start_time);
-+		return;
-+	}
-+
-+	if (strcmp(token, end_time_token) == 0 && slogctxt->end_time > 0) {
-+		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"",
-+		    (int)slogctxt->end_time);
-+		return;
-+	}
-+
-+	if (strcmp(token, duration_token) == 0 && slogctxt->end_time > 0) {
-+		snprintf(output, SLOG_SHORT_STRING_LEN, "\"%d\"", slogctxt->duration);
-+		return;
-+	}
-+
-+	if (strcmp(token, main_pid_token) == 0) {
-+		if (slogctxt->main_pid > 0) {
-+			snprintf(output, SLOG_SHORT_STRING_LEN, "\"%ld\"",
-+			    (long)slogctxt->main_pid);
-+		}
-+		return;
-+	}
-+
-+	// Arbitrary input
-+	if (strncmp(token, client_version, strlen(client_version)) == 0) {
-+		if (slogctxt->client_version[0] != 0) {
-+			slog_escape_value(output, slogctxt->client_version,
-+			    SLOG_STRING_LEN + 2);
-+		}
-+		return;
-+	}
-+}
-+
-+static const char *
-+slog_get_state_text(void)
-+{
-+	if (slogctxt == NULL)
-+		return "";
-+
-+	switch (slogctxt->session_state) {
-+		case SLOG_SESSION_INIT:
-+			return "Session failed";
-+		case SLOG_SESSION_OPEN:
-+			return "Session opened";
-+		case SLOG_SESSION_CLOSED:
-+			return "Session closed";
-+		default:
-+			return "Unknown session state";  // Should never happen
-+	}
-+}
-Index: b/servconf.c
-===================================================================
---- b.orig/servconf.c
-+++ b/servconf.c
-@@ -204,6 +204,9 @@ initialize_server_options(ServerOptions
- 	options->disable_forwarding = -1;
- 	options->expose_userauth_info = -1;
- 	options->rsa_min_size = -1;
-+	options->log_format_prefix = NULL;
-+	options->num_log_format_keys = 0;
-+	options->log_format_json = -1;
- }
- 
- /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
-@@ -473,6 +476,8 @@ fill_default_server_options(ServerOption
- 		options->sk_provider = xstrdup("internal");
- 	if (options->rsa_min_size == -1)
- 		options->rsa_min_size = SSH_RSA_MINIMUM_MODULUS_SIZE;
-+	if (options->log_format_json == -1)
-+		options->log_format_json = 0;
- 
- 	assemble_algorithms(options);
- 
-@@ -553,6 +558,10 @@ typedef enum {
- 	sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
- 	sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
- 	sRSAMinSize,
-+	/* Structured Logging options.  Unless sLogFormatKeys is set,
-+	    structured logging is disabled */
-+	sLogFormatPrefix, sLogFormatKeys, sLogFormatJson,
-+
- 	sDeprecated, sIgnore, sUnsupported
- } ServerOpCodes;
- 
-@@ -730,6 +739,9 @@ static struct {
- 	{ "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
- 	{ "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
- 	{ "rsaminsize", sRSAMinSize, SSHCFG_ALL },
-+	{ "logformatprefix", sLogFormatPrefix, SSHCFG_GLOBAL },
-+	{ "logformatkeys", sLogFormatKeys, SSHCFG_GLOBAL },
-+	{ "logformatjson", sLogFormatJson, SSHCFG_GLOBAL },
- 	{ NULL, sBadOption, 0 }
- };
- 
-@@ -2367,6 +2379,30 @@ process_server_config_line_depth(ServerO
- 		}
- 		break;
- 
-+	case sLogFormatPrefix:
-+		arg = strdelim(&str);
-+		if (!arg || *arg == '\0') {
-+			fatal("%.200s line %d: invalid log format prefix",
-+			    filename, linenum);
-+		}
-+		options->log_format_prefix = xstrdup(arg);
-+		break;
-+
-+	case sLogFormatKeys:
-+		while ((arg = strdelim(&str)) && *arg != '\0') {
-+			if (options->num_log_format_keys >= MAX_LOGFORMAT_KEYS)
-+				fatal("%s line %d: too long format keys.",
-+				    filename, linenum);
-+			if (!*activep)
-+				continue;
-+			options->log_format_keys[options->num_log_format_keys++] = xstrdup(arg);
-+		}
-+		break;
-+
-+	case sLogFormatJson:
-+		intptr = &options->log_format_json;
-+		goto parse_flag;
-+
- 	case sIPQoS:
- 		arg = argv_next(&ac, &av);
- 		if (!arg || *arg == '\0')
-@@ -3024,6 +3060,7 @@ dump_config(ServerOptions *o)
- 	dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
- 	dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash);
- 	dump_cfg_fmtint(sExposeAuthInfo, o->expose_userauth_info);
-+	dump_cfg_fmtint(sLogFormatJson, o->log_format_json);
- 
- 	/* string arguments */
- 	dump_cfg_string(sPidFile, o->pid_file);
-@@ -3054,6 +3091,7 @@ dump_config(ServerOptions *o)
- #if defined(__OpenBSD__) || defined(HAVE_SYS_SET_PROCESS_RDOMAIN)
- 	dump_cfg_string(sRDomain, o->routing_domain);
- #endif
-+	dump_cfg_string(sLogFormatPrefix, o->log_format_prefix);
- 
- 	/* string arguments requiring a lookup */
- 	dump_cfg_string(sLogLevel, log_level_name(o->log_level));
-@@ -3076,6 +3114,7 @@ dump_config(ServerOptions *o)
- 	    o->num_auth_methods, o->auth_methods);
- 	dump_cfg_strarray_oneline(sLogVerbose,
- 	    o->num_log_verbose, o->log_verbose);
-+	dump_cfg_strarray(sLogFormatKeys, o->num_log_format_keys, o->log_format_keys);
- 
- 	/* other arguments */
- 	for (i = 0; i < o->num_subsystems; i++)
-Index: b/auth2-pubkey.c
-===================================================================
---- b.orig/auth2-pubkey.c
-+++ b/auth2-pubkey.c
-@@ -66,6 +66,7 @@
- #include "monitor_wrap.h"
- #include "authfile.h"
- #include "match.h"
-+#include "slog.h"
- #include "ssherr.h"
- #include "channels.h" /* XXX for session.h */
- #include "session.h" /* XXX for child_set_env(); refactor? */
-@@ -389,6 +390,7 @@ check_principals_line(struct ssh *ssh, c
- 		debug3("%s: matched principal \"%.100s\"",
- 		    loc, cert->principals[i]);
- 		found = 1;
-+		slog_set_principal(cp);
- 	}
- 	if (found && authoptsp != NULL) {
- 		*authoptsp = opts;
-@@ -714,6 +716,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);
-  success:
- 	if (finalopts == NULL)
- 		fatal_f("internal error: missing options");
-@@ -864,6 +867,7 @@ user_cert_trusted_ca(struct ssh *ssh, st
- 		*authoptsp = final_opts;
- 		final_opts = NULL;
- 	}
-+	slog_set_cert_id(key->cert->key_id);
- 	ret = 1;
-  out:
- 	sshauthopt_free(principals_opts);
-Index: b/regress/test-exec.sh
-===================================================================
---- b.orig/regress/test-exec.sh
-+++ b/regress/test-exec.sh
-@@ -689,7 +689,7 @@ start_sshd ()
- 
- 	trace "wait for sshd"
- 	i=0;
--	while [ ! -f $PIDFILE -a $i -lt 10 ]; do
-+	while [ ! -f $PIDFILE -a $i -lt 3 ]; do
- 		i=`expr $i + 1`
- 		sleep $i
- 	done
-Index: b/session.c
-===================================================================
---- b.orig/session.c
-+++ b/session.c
-@@ -96,6 +96,8 @@
- #include "monitor_wrap.h"
- #include "sftp.h"
- #include "atomicio.h"
-+#include "slog.h"
-+
- 
- #if defined(KRB5) && defined(USE_AFS)
- #include <kafs.h>
-@@ -753,6 +755,7 @@ do_exec(struct ssh *ssh, Session *s, con
- 	    ssh_remote_ipaddr(ssh),
- 	    ssh_remote_port(ssh),
- 	    s->self);
-+	    slog_log_session();
- 
- #ifdef SSH_AUDIT_EVENTS
- 	if (s->command != NULL || s->command_handle != -1)
-@@ -1482,7 +1485,7 @@ do_setusercontext(struct passwd *pw)
- 			perror("unable to set user context (setuser)");
- 			exit(1);
- 		}
--		/* 
-+		/*
- 		 * FreeBSD's setusercontext() will not apply the user's
- 		 * own umask setting unless running with the user's UID.
- 		 */
-@@ -2159,6 +2162,7 @@ session_exec_req(struct ssh *ssh, Sessio
- 	    (r = sshpkt_get_end(ssh)) != 0)
- 		sshpkt_fatal(ssh, r, "%s: parse packet", __func__);
- 
-+	slog_set_command(command);
- 	success = do_exec(ssh, s, command) == 0;
- 	free(command);
- 	return success;
-@@ -2869,4 +2873,3 @@ session_get_remote_name_or_ip(struct ssh
- 		remote = ssh_remote_ipaddr(ssh);
- 	return remote;
- }
--
-Index: b/log.h
-===================================================================
---- b.orig/log.h
-+++ b/log.h
-@@ -133,4 +133,6 @@ void	 sshlogdirect(LogLevel, int, const
- #define logdie_fr(r, ...)	sshlogdie(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__)
- #define sigdie_fr(r, ...)	sshsigdie(__FILE__, __func__, __LINE__, 1, SYSLOG_LEVEL_ERROR, ssh_err(r), __VA_ARGS__)
- 
-+void     do_log_slog_payload(const char *);
-+
- #endif
-Index: b/log.c
-===================================================================
---- b.orig/log.c
-+++ b/log.c
-@@ -529,3 +529,39 @@ sshlogdirect(LogLevel level, int forced,
- 	do_log(level, forced, NULL, fmt, args);
- 	va_end(args);
- }
-+
-+/* Custom function to log to syslog, to handle the session logging code
-+ */
-+void
-+do_log_slog_payload(const char *payload)
-+{
-+#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
-+	struct syslog_data sdata = SYSLOG_DATA_INIT;
-+#endif
-+	int pri = LOG_INFO;
-+	int saved_errno = errno;
-+	LogLevel level = SYSLOG_LEVEL_INFO;
-+	log_handler_fn *tmp_handler;
-+
-+	if (log_handler != NULL) {
-+		/* Avoid recursion */
-+		tmp_handler = log_handler;
-+		log_handler = NULL;
-+		tmp_handler(level, 0, payload, log_handler_ctx);
-+		log_handler = tmp_handler;
-+	} else if (log_on_stderr) {
-+		(void)write(log_stderr_fd, payload, strlen(payload));
-+		(void)write(log_stderr_fd, "\r\n", 2);
-+	} else {
-+#if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT)
-+		openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata);
-+		syslog_r(pri, &sdata, "%s", payload);
-+		closelog_r(&sdata);
-+#else
-+		openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility);
-+		syslog(pri, "%s", payload);
-+		closelog();
-+#endif
-+	}
-+	errno = saved_errno;
-+}
-Index: b/slog.h
-===================================================================
---- /dev/null
-+++ b/slog.h
-@@ -0,0 +1,41 @@
-+/*
-+ * Copyright 2004-present Facebook. All Rights Reserved.
-+ */
-+#ifndef USE_SLOG
-+#define USE_SLOG
-+
-+#define SLOG_STRING_LEN        100
-+#define SLOG_MEDIUM_STRING_LEN 1000
-+#define SLOG_SHORT_STRING_LEN  50
-+#define SLOG_LONG_STRING_LEN   2000
-+
-+typedef enum {
-+	SLOG_SESSION_INIT,
-+	SLOG_SESSION_OPEN,
-+	SLOG_SESSION_CLOSED,
-+} SLOG_SESSION_STATE;
-+
-+typedef enum {
-+	SLOG_UNAUTHORIZED = 0,
-+	SLOG_AUTHORIZED = 1
-+} SLOG_AUTHENTICATED;
-+
-+void	slog_init(void);
-+
-+// setters
-+void	slog_pam_session_opened(void);
-+void	slog_set_auth_data(int , const char *, const char *);
-+void	slog_set_cert_id(const char *);
-+void	slog_set_cert_serial(unsigned long long );
-+void	slog_set_connection(const char *, int, const char *, int, const char *);
-+void	slog_set_command(const char *);
-+void	slog_set_principal(const char *);
-+void	slog_set_user(const char *);
-+void	slog_set_auth_info(const char *);
-+void	slog_set_client_version(const char *);
-+
-+// loggers
-+void	slog_exit_handler(void);
-+void	slog_log_session(void);
-+
-+#endif
-Index: b/sshd.c
-===================================================================
---- b.orig/sshd.c
-+++ b/sshd.c
-@@ -132,6 +132,8 @@
- #include "sk-api.h"
- #include "srclimit.h"
- #include "dh.h"
-+#include "slog.h"
-+#include <time.h>
- 
- /* Re-exec fds */
- #define REEXEC_DEVCRYPTO_RESERVED_FD	(STDERR_FILENO + 1)
-@@ -2149,6 +2151,7 @@ main(int ac, char **av)
- 	}
- 	/* Reinitialize the log (because of the fork above). */
- 	log_init(__progname, options.log_level, options.log_facility, log_stderr);
-+	slog_init();
- 
- 	if (FIPS_mode()) {
- 		debug("FIPS mode initialized");
-@@ -2315,8 +2318,15 @@ main(int ac, char **av)
- 	    rdomain == NULL ? "" : " rdomain \"",
- 	    rdomain == NULL ? "" : rdomain,
- 	    rdomain == NULL ? "" : "\"");
--	free(laddr);
- 
-+
-+	slog_set_connection(remote_ip,
-+	    remote_port,
-+	    laddr,
-+	    ssh_local_port(ssh),
-+	    get_log_session_id());
-+
-+	free(laddr);
- 	/*
- 	 * We don't want to listen forever unless the other side
- 	 * successfully authenticates itself.  So we set up an alarm which is
-@@ -2638,6 +2648,7 @@ cleanup_exit(int i)
- 	if (in_cleanup)
- 		_exit(i);
- 	in_cleanup = 1;
-+	slog_exit_handler();
- 	if (the_active_state != NULL && the_authctxt != NULL) {
- 		do_cleanup(the_active_state, the_authctxt);
- 		if (use_privsep && privsep_is_preauth &&
-Index: b/sshd_config
-===================================================================
---- b.orig/sshd_config
-+++ b/sshd_config
-@@ -33,6 +33,15 @@ Include /etc/ssh/sshd_config.d/*.conf
- # Logging
- #SyslogFacility AUTH
- #LogLevel INFO
-+# Structured logging
-+# The default is no structured logging, to enable structured logging at least
-+# one LogFormatKeys must be set.  LogFormatJson defaults to no (array) to keep
-+# the log line size smaller, if keys are desired set LogFormatJson to yes
-+LogFormatPrefix sshd_auth_msg:
-+LogFormatKeys server_ip server_port remote_ip remote_port pid session_id method
-+LogFormatKeys cert_id cert_serial principal user session_state auth_successful
-+LogFormatKeys start_time command
-+LogFormatJson yes
- 
- # Authentication:
- 
-Index: b/auth-pam.c
-===================================================================
---- b.orig/auth-pam.c
-+++ b/auth-pam.c
-@@ -94,6 +94,7 @@ extern char *__progname;
- #include "auth-pam.h"
- #include "canohost.h"
- #include "log.h"
-+#include "slog.h"
- #include "msg.h"
- #include "packet.h"
- #include "misc.h"
-@@ -1223,9 +1224,12 @@ do_pam_session(struct ssh *ssh)
- 	if (sshpam_err != PAM_SUCCESS)
- 		fatal("PAM: failed to set PAM_CONV: %s",
- 		    pam_strerror(sshpam_handle, sshpam_err));
-+
- 	sshpam_err = pam_open_session(sshpam_handle, 0);
--	if (sshpam_err == PAM_SUCCESS)
-+	if (sshpam_err == PAM_SUCCESS) {
-+		slog_pam_session_opened();
- 		sshpam_session_open = 1;
-+	}
- 	else {
- 		sshpam_session_open = 0;
- 		auth_restrict_session(ssh);
-Index: b/servconf.h
-===================================================================
---- b.orig/servconf.h
-+++ b/servconf.h
-@@ -22,6 +22,8 @@
- 
- #define MAX_SUBSYSTEMS		256	/* Max # subsystems. */
- 
-+#define MAX_LOGFORMAT_KEYS      256     /* Max # LogFormatKeys */
-+
- /* permit_root_login */
- #define	PERMIT_NOT_SET		-1
- #define	PERMIT_NO		0
-@@ -239,6 +241,12 @@ typedef struct {
- 	u_int64_t timing_secret;
- 	char   *sk_provider;
- 	int	rsa_min_size;	/* minimum size of RSA keys */
-+
-+	char   *log_format_prefix;
-+	u_int  num_log_format_keys;
-+	char   *log_format_keys[MAX_LOGFORMAT_KEYS];
-+	int log_format_json;	/* 1 to return "token": "token_val" in log format */
-+
- }       ServerOptions;
- 
- /* Information about the incoming connection as used by Match */
-Index: b/regress/slog.sh
-===================================================================
---- /dev/null
-+++ b/regress/slog.sh
-@@ -0,0 +1,59 @@
-+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"
-+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
-+EOF
-+
-+
-+cp $test_config $old_config
-+start_sshd
-+
-+${SSH} -F $OBJ/ssh_config somehost true
-+if [ $? -ne 0 ]; then
-+	fail "ssh connect with failed"
-+fi
-+
-+test_log_counts() {
-+	cnt=$(grep -c "$log_prefix" "$TEST_SSHD_LOGFILE")
-+	if [ $cnt -ne 2 ]; then
-+		fail "expected 2 structured logging lines, got $cnt"
-+	fi
-+}
-+
-+test_json_valid() {
-+	which python &>/dev/null || echo 'python not found in path, skipping tests'
-+
-+	loglines=$(cat "$TEST_SSHD_LOGFILE" | grep "$log_prefix")
-+	first=$(echo "$loglines" | head -n1)
-+	last=$(echo "$loglines" | tail -n1)
-+
-+	echo ${first:$(expr length $log_prefix)} | python -m json.tool &>/dev/null \
-+	    || fail "invalid json structure $first"
-+	echo ${last:$(expr length $log_prefix)} | python -m json.tool &>/dev/null  \
-+	    || fail "invalid json structure $last"
-+}
-+
-+test_log_counts
-+test_json_valid
-Index: b/Makefile.in
-===================================================================
---- b.orig/Makefile.in
-+++ b/Makefile.in
-@@ -129,7 +129,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw
- 	monitor.o monitor_wrap.o auth-krb5.o \
- 	auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \
- 	loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \
--	srclimit.o sftp-server.o sftp-common.o \
-+	srclimit.o sftp-server.o sftp-common.o sftp-realpath.o slog.o \
- 	sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
- 	sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \
- 	sandbox-solaris.o uidswap.o $(SKOBJS)
-@@ -313,7 +313,7 @@ distclean:	regressclean
- 	rm -f *.o *.a $(TARGETS) logintest config.cache config.log
- 	rm -f *.out core opensshd.init openssh.xml
- 	rm -f Makefile buildpkg.sh config.h config.status
--	rm -f survey.sh openbsd-compat/regress/Makefile *~ 
-+	rm -f survey.sh openbsd-compat/regress/Makefile *~
- 	rm -rf autom4te.cache
- 	rm -f regress/check-perm
- 	rm -f regress/mkdtemp
-Index: b/auth.c
-===================================================================
---- b.orig/auth.c
-+++ b/auth.c
-@@ -76,6 +76,7 @@
- #include "ssherr.h"
- #include "compat.h"
- #include "channels.h"
-+#include "slog.h"
- 
- /* import */
- extern ServerOptions options;
-@@ -351,6 +352,7 @@ auth_log(struct ssh *ssh, int authentica
- 	    extra != NULL ? extra : "");
- 
- 	free(extra);
-+	slog_set_auth_data(authenticated, method, authctxt->user);
- 
- #if defined(CUSTOM_FAILED_LOGIN) || defined(SSH_AUDIT_EVENTS)
- 	if (authenticated == 0 && !(authctxt->postponed || partial)) {
-@@ -564,6 +566,7 @@ auth_openprincipals(const char *file, st
- struct passwd *
- getpwnamallow(struct ssh *ssh, const char *user)
- {
-+	slog_set_user(user);
- #ifdef HAVE_LOGIN_CAP
- 	extern login_cap_t *lc;
- #ifdef BSD_AUTH
diff --git a/fbpatches/series b/fbpatches/series
deleted file mode 100644
index 8fe0e6e..0000000
--- a/fbpatches/series
+++ /dev/null
@@ -1,10 +0,0 @@
-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
diff --git a/openssh.spec b/openssh.spec
index 1d42e2b..2f166f0 100644
--- a/openssh.spec
+++ b/openssh.spec
@@ -7,7 +7,7 @@
 
 # Useful development mode for porting patches from
 # a different release
-%global use_quilt 0
+%global use_quilt 1
 
 %global _hardened_build 1
 
@@ -81,6 +81,9 @@ Source12: sshd-keygen@.service
 Source13: sshd-keygen
 Source15: sshd-keygen.target
 Source16: ssh-agent.service
+%if 0%{facebook} && 0%{use_quilt}
+Source17: series
+%endif
 
 #https://bugzilla.mindrot.org/show_bug.cgi?id=2581
 Patch100: openssh-6.7p1-coverity.patch
@@ -261,33 +264,33 @@ Patch1006: openssh-8.7p1-negotiate-supported-algs.patch
 # c9s specific logic factored out of openssh-7.7p1-fips.patch
 Patch2000: openssh-7.7p1-fips-warning.patch
 
-%if %{facebook} && !%{use_quilt}
+%if 0%{facebook} && !0%{use_quilt}
 # Add a unique log session identifier to output messages for
 # each sshd process and its children.
-Patch2010: fbpatches/fb87_log_session_id.patch
+Patch2010: fb87_log_session_id.patch
 # Add structured logging
-Patch2011: fbpatches/fb87_slog.patch
+Patch2011: fb87_slog.patch
 # Add a log entry when a session is started over a local forward port.
-Patch2012: fbpatches/fb87_log_port_forwards.patch
+Patch2012: fb87_log_port_forwards.patch
 # Add a log line when a session is started over a reverse port forward.
-Patch2013: fbpatches/fb87_070_logging_reverse_port_forward.patch
+Patch2013: fb87_070_logging_reverse_port_forward.patch
 # Increase ssh cert max principals from 256 to 1024.
-Patch2014: fbpatches/fb87_810_increase_ssh_cert_max_principals.patch
+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: fbpatches/fb87_090_logging_shell_cmd_pty.patch
+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: fbpatches/fb87_080_logging_certificates.patch
+Patch2016: fb87_080_logging_certificates.patch
 # Add verbose logging for setting env variables.
-Patch2017: fbpatches/fb87_log_accept_env.patch
+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
 # ommand is present and the user is authenticated by the certificate.
-Patch2018: fbpatches/fb87_pass_principals_to_child.patch
+Patch2018: fb87_pass_principals_to_child.patch
 # Log extra authenticaton informatino to the auth_info structured
 # logging field, and add tests for pubkey and cert auth.
-Patch2019: fbpatches/fb87_log_auth_info.patch
+Patch2019: fb87_log_auth_info.patch
 %endif
 
 License: BSD
@@ -495,7 +498,7 @@ popd
 
 %patch100 -p1 -b .coverity
 
-%if %{facebook} && !%{use_quilt}
+%if 0%{facebook} && !0%{use_quilt}
 %patch2010 -p1 -b .log_session_id
 %patch2011 -p1 -b .slog
 %patch2012 -p1 -b .log_port_forwards
@@ -508,8 +511,8 @@ popd
 %patch2019 -p1 -b .log_auth_info
 %endif
 
-%if %{facebook} && %{use_quilt}
-ln -sf ../../fbpatches patches
+%if 0%{facebook} && 0%{use_quilt}
+ln -sf %{_sourcedir} patches
 quilt push -a
 %endif
 
diff --git a/series b/series
new file mode 100644
index 0000000..8fe0e6e
--- /dev/null
+++ b/series
@@ -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