upstream: switch over to the new authorized_keys options API and

remove the legacy one.

Includes a fairly big refactor of auth2-pubkey.c to retain less state
between key file lines.

feedback and ok markus@

OpenBSD-Commit-ID: dece6cae0f47751b9892080eb13d6625599573df
diff --git a/.depend b/.depend
index 4c6b49d..0893a87 100644
--- a/.depend
+++ b/.depend
@@ -7,8 +7,7 @@
 audit.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h
 auth-bsdauth.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h
 auth-krb5.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h xmalloc.h ssh.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h log.h misc.h servconf.h uidswap.h key.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h
-auth-options.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h openbsd-compat/sys-queue.h key.h sshkey.h xmalloc.h match.h ssherr.h log.h canohost.h packet.h dispatch.h opacket.h misc.h channels.h servconf.h auth-options.h hostfile.h auth.h auth-pam.h
-auth-options.o: audit.h loginrec.h
+auth-options.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h openbsd-compat/sys-queue.h xmalloc.h ssherr.h log.h misc.h sshkey.h match.h ssh2.h auth-options.h
 auth-pam.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h
 auth-passwd.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h log.h misc.h servconf.h key.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h auth-options.h
 auth-rhosts.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h uidswap.h pathnames.h log.h misc.h key.h sshkey.h servconf.h canohost.h hostfile.h auth.h auth-pam.h audit.h loginrec.h
@@ -16,7 +15,7 @@
 auth-sia.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h
 auth-skey.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h
 auth.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h xmalloc.h match.h groupaccess.h log.h misc.h servconf.h key.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h auth-options.h canohost.h uidswap.h packet.h openbsd-compat/sys-queue.h
-auth.o: dispatch.h opacket.h authfile.h monitor_wrap.h ssherr.h compat.h
+auth.o: dispatch.h opacket.h authfile.h monitor_wrap.h ssherr.h compat.h channels.h
 auth2-chall.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h xmalloc.h ssh2.h key.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h log.h misc.h servconf.h
 auth2-gss.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h
 auth2-hostbased.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h xmalloc.h ssh2.h packet.h openbsd-compat/sys-queue.h dispatch.h opacket.h log.h misc.h servconf.h compat.h sshkey.h hostfile.h auth.h auth-pam.h audit.h loginrec.h canohost.h
@@ -161,7 +160,7 @@
 sshconnect2.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h packet.h dispatch.h opacket.h compat.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h key.h sshkey.h kex.h mac.h
 sshconnect2.o: myproposal.h sshconnect.h authfile.h dh.h authfd.h log.h misc.h readconf.h match.h canohost.h msg.h pathnames.h uidswap.h hostfile.h ssherr.h utf8.h
 sshd.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h ./openbsd-compat/sys-tree.h openbsd-compat/sys-queue.h xmalloc.h ssh.h ssh2.h sshpty.h packet.h dispatch.h opacket.h log.h misc.h match.h servconf.h uidswap.h compat.h cipher.h cipher-chachapoly.h
-sshd.o: chacha.h poly1305.h cipher-aesctr.h rijndael.h digest.h key.h sshkey.h kex.h mac.h myproposal.h authfile.h pathnames.h atomicio.h canohost.h hostfile.h auth.h auth-pam.h audit.h loginrec.h authfd.h msg.h channels.h session.h monitor.h monitor_wrap.h ssh-sandbox.h version.h ssherr.h
+sshd.o: chacha.h poly1305.h cipher-aesctr.h rijndael.h digest.h key.h sshkey.h kex.h mac.h myproposal.h authfile.h pathnames.h atomicio.h canohost.h hostfile.h auth.h auth-pam.h audit.h loginrec.h authfd.h msg.h channels.h session.h monitor.h monitor_wrap.h ssh-sandbox.h auth-options.h version.h ssherr.h
 ssherr.o: ssherr.h
 sshkey-xmss.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h
 sshkey.o: includes.h config.h defines.h platform.h openbsd-compat/openbsd-compat.h openbsd-compat/base64.h openbsd-compat/sigact.h openbsd-compat/readpassphrase.h openbsd-compat/vis.h openbsd-compat/getrrsetbyname.h openbsd-compat/sha1.h openbsd-compat/sha2.h openbsd-compat/rmd160.h openbsd-compat/md5.h openbsd-compat/blf.h openbsd-compat/getopt.h openbsd-compat/bsd-misc.h openbsd-compat/bsd-setres_id.h openbsd-compat/bsd-signal.h openbsd-compat/bsd-statvfs.h openbsd-compat/bsd-waitpid.h openbsd-compat/bsd-poll.h openbsd-compat/fake-rfc2553.h openbsd-compat/bsd-cygwin_util.h openbsd-compat/port-aix.h openbsd-compat/port-irix.h openbsd-compat/port-linux.h openbsd-compat/port-solaris.h openbsd-compat/port-net.h openbsd-compat/port-uw.h openbsd-compat/bsd-nextstep.h entropy.h buffer.h sshbuf.h crypto_api.h ssh2.h ssherr.h misc.h cipher.h cipher-chachapoly.h chacha.h poly1305.h cipher-aesctr.h rijndael.h digest.h sshkey.h sshkey-xmss.h match.h xmss_fast.h
diff --git a/auth-options.c b/auth-options.c
index 8b93b51..484e44b 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -1,14 +1,4 @@
-/* $OpenBSD: auth-options.c,v 1.75 2018/03/03 03:06:02 djm Exp $ */
-/*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
- */
+/* $OpenBSD: auth-options.c,v 1.76 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
  *
@@ -39,649 +29,15 @@
 
 #include "openbsd-compat/sys-queue.h"
 
-#include "key.h"	/* XXX for typedef */
-#include "buffer.h"	/* XXX for typedef */
 #include "xmalloc.h"
-#include "match.h"
 #include "ssherr.h"
-#include "ssh2.h"
 #include "log.h"
-#include "canohost.h"
-#include "packet.h"
 #include "sshbuf.h"
 #include "misc.h"
-#include "channels.h"
-#include "servconf.h"
 #include "sshkey.h"
+#include "match.h"
+#include "ssh2.h"
 #include "auth-options.h"
-#include "hostfile.h"
-#include "auth.h"
-
-/* Flags set authorized_keys flags */
-int no_port_forwarding_flag = 0;
-int no_agent_forwarding_flag = 0;
-int no_x11_forwarding_flag = 0;
-int no_pty_flag = 0;
-int no_user_rc = 0;
-int key_is_cert_authority = 0;
-
-/* "command=" option. */
-char *forced_command = NULL;
-
-/* "environment=" options. */
-struct envstring *custom_environment = NULL;
-
-/* "tunnel=" option. */
-int forced_tun_device = -1;
-
-/* "principals=" option. */
-char *authorized_principals = NULL;
-
-extern ServerOptions options;
-
-/* XXX refactor to be stateless */
-
-void
-auth_clear_options(void)
-{
-	struct ssh *ssh = active_state;		/* XXX */
-
-	no_agent_forwarding_flag = 0;
-	no_port_forwarding_flag = 0;
-	no_pty_flag = 0;
-	no_x11_forwarding_flag = 0;
-	no_user_rc = 0;
-	key_is_cert_authority = 0;
-	while (custom_environment) {
-		struct envstring *ce = custom_environment;
-		custom_environment = ce->next;
-		free(ce->s);
-		free(ce);
-	}
-	free(forced_command);
-	forced_command = NULL;
-	free(authorized_principals);
-	authorized_principals = NULL;
-	forced_tun_device = -1;
-	channel_clear_permitted_opens(ssh);
-}
-
-/*
- * Match flag 'opt' in *optsp, and if allow_negate is set then also match
- * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
- * if negated option matches. 
- * If the option or negated option matches, then *optsp is updated to
- * point to the first character after the option and, if 'msg' is not NULL
- * then a message based on it added via auth_debug_add().
- */
-static int
-match_flag(const char *opt, int allow_negate, char **optsp, const char *msg)
-{
-	size_t opt_len = strlen(opt);
-	char *opts = *optsp;
-	int negate = 0;
-
-	if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
-		opts += 3;
-		negate = 1;
-	}
-	if (strncasecmp(opts, opt, opt_len) == 0) {
-		*optsp = opts + opt_len;
-		if (msg != NULL) {
-			auth_debug_add("%s %s.", msg,
-			    negate ? "disabled" : "enabled");
-		}
-		return negate ? 0 : 1;
-	}
-	return -1;
-}
-
-/*
- * return 1 if access is granted, 0 if not.
- * side effect: sets key option flags
- * XXX remove side effects; fill structure instead.
- */
-int
-auth_parse_options(struct passwd *pw, char *opts, const char *file,
-    u_long linenum)
-{
-	struct ssh *ssh = active_state;		/* XXX */
-	const char *cp;
-	int i, r;
-
-	/* reset options */
-	auth_clear_options();
-
-	if (!opts)
-		return 1;
-
-	while (*opts && *opts != ' ' && *opts != '\t') {
-		if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) {
-			key_is_cert_authority = r;
-			goto next_option;
-		}
-		if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) {
-			auth_debug_add("Key is restricted.");
-			no_port_forwarding_flag = 1;
-			no_agent_forwarding_flag = 1;
-			no_x11_forwarding_flag = 1;
-			no_pty_flag = 1;
-			no_user_rc = 1;
-			goto next_option;
-		}
-		if ((r = match_flag("port-forwarding", 1, &opts,
-		    "Port forwarding")) != -1) {
-			no_port_forwarding_flag = r != 1;
-			goto next_option;
-		}
-		if ((r = match_flag("agent-forwarding", 1, &opts,
-		    "Agent forwarding")) != -1) {
-			no_agent_forwarding_flag = r != 1;
-			goto next_option;
-		}
-		if ((r = match_flag("x11-forwarding", 1, &opts,
-		    "X11 forwarding")) != -1) {
-			no_x11_forwarding_flag = r != 1;
-			goto next_option;
-		}
-		if ((r = match_flag("pty", 1, &opts,
-		    "PTY allocation")) != -1) {
-			no_pty_flag = r != 1;
-			goto next_option;
-		}
-		if ((r = match_flag("user-rc", 1, &opts,
-		    "User rc execution")) != -1) {
-			no_user_rc = r != 1;
-			goto next_option;
-		}
-		cp = "command=\"";
-		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
-			opts += strlen(cp);
-			free(forced_command);
-			forced_command = xmalloc(strlen(opts) + 1);
-			i = 0;
-			while (*opts) {
-				if (*opts == '"')
-					break;
-				if (*opts == '\\' && opts[1] == '"') {
-					opts += 2;
-					forced_command[i++] = '"';
-					continue;
-				}
-				forced_command[i++] = *opts++;
-			}
-			if (!*opts) {
-				debug("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				auth_debug_add("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				free(forced_command);
-				forced_command = NULL;
-				goto bad_option;
-			}
-			forced_command[i] = '\0';
-			auth_debug_add("Forced command.");
-			opts++;
-			goto next_option;
-		}
-		cp = "principals=\"";
-		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
-			opts += strlen(cp);
-			free(authorized_principals);
-			authorized_principals = xmalloc(strlen(opts) + 1);
-			i = 0;
-			while (*opts) {
-				if (*opts == '"')
-					break;
-				if (*opts == '\\' && opts[1] == '"') {
-					opts += 2;
-					authorized_principals[i++] = '"';
-					continue;
-				}
-				authorized_principals[i++] = *opts++;
-			}
-			if (!*opts) {
-				debug("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				auth_debug_add("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				free(authorized_principals);
-				authorized_principals = NULL;
-				goto bad_option;
-			}
-			authorized_principals[i] = '\0';
-			auth_debug_add("principals: %.900s",
-			    authorized_principals);
-			opts++;
-			goto next_option;
-		}
-		cp = "environment=\"";
-		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
-			char *s;
-			struct envstring *new_envstring;
-
-			opts += strlen(cp);
-			s = xmalloc(strlen(opts) + 1);
-			i = 0;
-			while (*opts) {
-				if (*opts == '"')
-					break;
-				if (*opts == '\\' && opts[1] == '"') {
-					opts += 2;
-					s[i++] = '"';
-					continue;
-				}
-				s[i++] = *opts++;
-			}
-			if (!*opts) {
-				debug("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				auth_debug_add("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				free(s);
-				goto bad_option;
-			}
-			s[i] = '\0';
-			opts++;
-			if (options.permit_user_env) {
-				auth_debug_add("Adding to environment: "
-				    "%.900s", s);
-				debug("Adding to environment: %.900s", s);
-				new_envstring = xcalloc(1,
-				    sizeof(*new_envstring));
-				new_envstring->s = s;
-				new_envstring->next = custom_environment;
-				custom_environment = new_envstring;
-				s = NULL;
-			}
-			free(s);
-			goto next_option;
-		}
-		cp = "from=\"";
-		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
-			const char *remote_ip = ssh_remote_ipaddr(ssh);
-			const char *remote_host = auth_get_canonical_hostname(
-			    ssh, options.use_dns);
-			char *patterns = xmalloc(strlen(opts) + 1);
-
-			opts += strlen(cp);
-			i = 0;
-			while (*opts) {
-				if (*opts == '"')
-					break;
-				if (*opts == '\\' && opts[1] == '"') {
-					opts += 2;
-					patterns[i++] = '"';
-					continue;
-				}
-				patterns[i++] = *opts++;
-			}
-			if (!*opts) {
-				debug("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				auth_debug_add("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				free(patterns);
-				goto bad_option;
-			}
-			patterns[i] = '\0';
-			opts++;
-			switch (match_host_and_ip(remote_host, remote_ip,
-			    patterns)) {
-			case 1:
-				free(patterns);
-				/* Host name matches. */
-				goto next_option;
-			case -1:
-				debug("%.100s, line %lu: invalid criteria",
-				    file, linenum);
-				auth_debug_add("%.100s, line %lu: "
-				    "invalid criteria", file, linenum);
-				/* FALLTHROUGH */
-			case 0:
-				free(patterns);
-				logit("Authentication tried for %.100s with "
-				    "correct key but not from a permitted "
-				    "host (host=%.200s, ip=%.200s).",
-				    pw->pw_name, remote_host, remote_ip);
-				auth_debug_add("Your host '%.200s' is not "
-				    "permitted to use this key for login.",
-				    remote_host);
-				break;
-			}
-			/* deny access */
-			return 0;
-		}
-		cp = "permitopen=\"";
-		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
-			char *host, *p;
-			int port;
-			char *patterns = xmalloc(strlen(opts) + 1);
-
-			opts += strlen(cp);
-			i = 0;
-			while (*opts) {
-				if (*opts == '"')
-					break;
-				if (*opts == '\\' && opts[1] == '"') {
-					opts += 2;
-					patterns[i++] = '"';
-					continue;
-				}
-				patterns[i++] = *opts++;
-			}
-			if (!*opts) {
-				debug("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				auth_debug_add("%.100s, line %lu: missing "
-				    "end quote", file, linenum);
-				free(patterns);
-				goto bad_option;
-			}
-			patterns[i] = '\0';
-			opts++;
-			p = patterns;
-			/* XXX - add streamlocal support */
-			host = hpdelim(&p);
-			if (host == NULL || strlen(host) >= NI_MAXHOST) {
-				debug("%.100s, line %lu: Bad permitopen "
-				    "specification <%.100s>", file, linenum,
-				    patterns);
-				auth_debug_add("%.100s, line %lu: "
-				    "Bad permitopen specification", file,
-				    linenum);
-				free(patterns);
-				goto bad_option;
-			}
-			host = cleanhostname(host);
-			if (p == NULL || (port = permitopen_port(p)) < 0) {
-				debug("%.100s, line %lu: Bad permitopen port "
-				    "<%.100s>", file, linenum, p ? p : "");
-				auth_debug_add("%.100s, line %lu: "
-				    "Bad permitopen port", file, linenum);
-				free(patterns);
-				goto bad_option;
-			}
-			if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
-				channel_add_permitted_opens(ssh, host, port);
-			free(patterns);
-			goto next_option;
-		}
-		cp = "tunnel=\"";
-		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
-			char *tun = NULL;
-			opts += strlen(cp);
-			tun = xmalloc(strlen(opts) + 1);
-			i = 0;
-			while (*opts) {
-				if (*opts == '"')
-					break;
-				tun[i++] = *opts++;
-			}
-			if (!*opts) {
-				debug("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				auth_debug_add("%.100s, line %lu: missing end quote",
-				    file, linenum);
-				free(tun);
-				forced_tun_device = -1;
-				goto bad_option;
-			}
-			tun[i] = '\0';
-			forced_tun_device = a2tun(tun, NULL);
-			free(tun);
-			if (forced_tun_device == SSH_TUNID_ERR) {
-				debug("%.100s, line %lu: invalid tun device",
-				    file, linenum);
-				auth_debug_add("%.100s, line %lu: invalid tun device",
-				    file, linenum);
-				forced_tun_device = -1;
-				goto bad_option;
-			}
-			auth_debug_add("Forced tun device: %d", forced_tun_device);
-			opts++;
-			goto next_option;
-		}
-next_option:
-		/*
-		 * Skip the comma, and move to the next option
-		 * (or break out if there are no more).
-		 */
-		if (!*opts)
-			fatal("Bugs in auth-options.c option processing.");
-		if (*opts == ' ' || *opts == '\t')
-			break;		/* End of options. */
-		if (*opts != ',')
-			goto bad_option;
-		opts++;
-		/* Process the next option. */
-	}
-
-	/* grant access */
-	return 1;
-
-bad_option:
-	logit("Bad options in %.100s file, line %lu: %.50s",
-	    file, linenum, opts);
-	auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
-	    file, linenum, opts);
-
-	/* deny access */
-	return 0;
-}
-
-#define OPTIONS_CRITICAL	1
-#define OPTIONS_EXTENSIONS	2
-static int
-parse_option_list(struct sshbuf *oblob, struct passwd *pw,
-    u_int which, int crit,
-    int *cert_no_port_forwarding_flag,
-    int *cert_no_agent_forwarding_flag,
-    int *cert_no_x11_forwarding_flag,
-    int *cert_no_pty_flag,
-    int *cert_no_user_rc,
-    char **cert_forced_command,
-    int *cert_source_address_done)
-{
-	struct ssh *ssh = active_state;		/* XXX */
-	char *command, *allowed;
-	const char *remote_ip;
-	char *name = NULL;
-	struct sshbuf *c = NULL, *data = NULL;
-	int r, ret = -1, result, found;
-
-	if ((c = sshbuf_fromb(oblob)) == NULL) {
-		error("%s: sshbuf_fromb failed", __func__);
-		goto out;
-	}
-
-	while (sshbuf_len(c) > 0) {
-		sshbuf_free(data);
-		data = NULL;
-		if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
-		    (r = sshbuf_froms(c, &data)) != 0) {
-			error("Unable to parse certificate options: %s",
-			    ssh_err(r));
-			goto out;
-		}
-		debug3("found certificate option \"%.100s\" len %zu",
-		    name, sshbuf_len(data));
-		found = 0;
-		if ((which & OPTIONS_EXTENSIONS) != 0) {
-			if (strcmp(name, "permit-X11-forwarding") == 0) {
-				*cert_no_x11_forwarding_flag = 0;
-				found = 1;
-			} else if (strcmp(name,
-			    "permit-agent-forwarding") == 0) {
-				*cert_no_agent_forwarding_flag = 0;
-				found = 1;
-			} else if (strcmp(name,
-			    "permit-port-forwarding") == 0) {
-				*cert_no_port_forwarding_flag = 0;
-				found = 1;
-			} else if (strcmp(name, "permit-pty") == 0) {
-				*cert_no_pty_flag = 0;
-				found = 1;
-			} else if (strcmp(name, "permit-user-rc") == 0) {
-				*cert_no_user_rc = 0;
-				found = 1;
-			}
-		}
-		if (!found && (which & OPTIONS_CRITICAL) != 0) {
-			if (strcmp(name, "force-command") == 0) {
-				if ((r = sshbuf_get_cstring(data, &command,
-				    NULL)) != 0) {
-					error("Unable to parse \"%s\" "
-					    "section: %s", name, ssh_err(r));
-					goto out;
-				}
-				if (*cert_forced_command != NULL) {
-					error("Certificate has multiple "
-					    "force-command options");
-					free(command);
-					goto out;
-				}
-				*cert_forced_command = command;
-				found = 1;
-			}
-			if (strcmp(name, "source-address") == 0) {
-				if ((r = sshbuf_get_cstring(data, &allowed,
-				    NULL)) != 0) {
-					error("Unable to parse \"%s\" "
-					    "section: %s", name, ssh_err(r));
-					goto out;
-				}
-				if ((*cert_source_address_done)++) {
-					error("Certificate has multiple "
-					    "source-address options");
-					free(allowed);
-					goto out;
-				}
-				remote_ip = ssh_remote_ipaddr(ssh);
-				result = addr_match_cidr_list(remote_ip,
-				    allowed);
-				free(allowed);
-				switch (result) {
-				case 1:
-					/* accepted */
-					break;
-				case 0:
-					/* no match */
-					logit("Authentication tried for %.100s "
-					    "with valid certificate but not "
-					    "from a permitted host "
-					    "(ip=%.200s).", pw->pw_name,
-					    remote_ip);
-					auth_debug_add("Your address '%.200s' "
-					    "is not permitted to use this "
-					    "certificate for login.",
-					    remote_ip);
-					goto out;
-				case -1:
-				default:
-					error("Certificate source-address "
-					    "contents invalid");
-					goto out;
-				}
-				found = 1;
-			}
-		}
-
-		if (!found) {
-			if (crit) {
-				error("Certificate critical option \"%s\" "
-				    "is not supported", name);
-				goto out;
-			} else {
-				logit("Certificate extension \"%s\" "
-				    "is not supported", name);
-			}
-		} else if (sshbuf_len(data) != 0) {
-			error("Certificate option \"%s\" corrupt "
-			    "(extra data)", name);
-			goto out;
-		}
-		free(name);
-		name = NULL;
-	}
-	/* successfully parsed all options */
-	ret = 0;
-
- out:
-	if (ret != 0 &&
-	    cert_forced_command != NULL &&
-	    *cert_forced_command != NULL) {
-		free(*cert_forced_command);
-		*cert_forced_command = NULL;
-	}
-	free(name);
-	sshbuf_free(data);
-	sshbuf_free(c);
-	return ret;
-}
-
-/*
- * Set options from critical certificate options. These supersede user key
- * options so this must be called after auth_parse_options().
- */
-int
-auth_cert_options(struct sshkey *k, struct passwd *pw, const char **reason)
-{
-	int cert_no_port_forwarding_flag = 1;
-	int cert_no_agent_forwarding_flag = 1;
-	int cert_no_x11_forwarding_flag = 1;
-	int cert_no_pty_flag = 1;
-	int cert_no_user_rc = 1;
-	char *cert_forced_command = NULL;
-	int cert_source_address_done = 0;
-
-	*reason = "invalid certificate options";
-
-	/* Separate options and extensions for v01 certs */
-	if (parse_option_list(k->cert->critical, pw,
-	    OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
-	    &cert_forced_command,
-	    &cert_source_address_done) == -1)
-		return -1;
-	if (parse_option_list(k->cert->extensions, pw,
-	    OPTIONS_EXTENSIONS, 0,
-	    &cert_no_port_forwarding_flag,
-	    &cert_no_agent_forwarding_flag,
-	    &cert_no_x11_forwarding_flag,
-	    &cert_no_pty_flag,
-	    &cert_no_user_rc,
-	    NULL, NULL) == -1)
-		return -1;
-
-	no_port_forwarding_flag |= cert_no_port_forwarding_flag;
-	no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
-	no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
-	no_pty_flag |= cert_no_pty_flag;
-	no_user_rc |= cert_no_user_rc;
-	/*
-	 * Only permit both CA and key option forced-command if they match.
-	 * Otherwise refuse the certificate.
-	 */
-	if (cert_forced_command != NULL && forced_command != NULL) {
-		if (strcmp(forced_command, cert_forced_command) == 0) {
-			free(forced_command);
-			forced_command = cert_forced_command;
-		} else {
-			*reason = "certificate and key options forced command "
-			    "do not match";
-			free(cert_forced_command);
-			return -1;
-		}
-	} else if (cert_forced_command != NULL)
-		forced_command = cert_forced_command;
-	/* success */
-	*reason = NULL;
-	return 0;
-}
-
-/*
- * authorized_keys options processing.
- */
 
 /*
  * Match flag 'opt' in *optsp, and if allow_negate is set then also match
diff --git a/auth-options.h b/auth-options.h
index 0dbfc32..16871d7 100644
--- a/auth-options.h
+++ b/auth-options.h
@@ -1,15 +1,19 @@
-/* $OpenBSD: auth-options.h,v 1.24 2018/03/03 03:06:02 djm Exp $ */
+/* $OpenBSD: auth-options.h,v 1.25 2018/03/03 03:15:51 djm Exp $ */
 
 /*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
+ * Copyright (c) 2018 Damien Miller <djm@mindrot.org>
  *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 #ifndef AUTH_OPTIONS_H
@@ -18,30 +22,6 @@
 struct passwd;
 struct sshkey;
 
-/* Linked list of custom environment strings */
-struct envstring {
-	struct envstring *next;
-	char   *s;
-};
-
-/* Flags that may be set in authorized_keys options. */
-extern int no_port_forwarding_flag;
-extern int no_agent_forwarding_flag;
-extern int no_x11_forwarding_flag;
-extern int no_pty_flag;
-extern int no_user_rc;
-extern char *forced_command;
-extern struct envstring *custom_environment;
-extern int forced_tun_device;
-extern int key_is_cert_authority;
-extern char *authorized_principals;
-
-int	auth_parse_options(struct passwd *, char *, const char *, u_long);
-void	auth_clear_options(void);
-int	auth_cert_options(struct sshkey *, struct passwd *, const char **);
-
-/* authorized_keys options handling */
-
 /*
  * sshauthopt represents key options parsed from authorized_keys or
  * from certificate extensions/options.
diff --git a/auth-pam.c b/auth-pam.c
index de29c04..50d2073 100644
--- a/auth-pam.c
+++ b/auth-pam.c
@@ -1077,7 +1077,7 @@
 }
 
 void
-do_pam_session(void)
+do_pam_session(struct ssh *ssh)
 {
 	debug3("PAM: opening session");
 
@@ -1093,7 +1093,7 @@
 		sshpam_session_open = 1;
 	else {
 		sshpam_session_open = 0;
-		disable_forwarding();
+		auth_restrict_session(ssh);
 		error("PAM: pam_open_session(): %s",
 		    pam_strerror(sshpam_handle, sshpam_err));
 	}
diff --git a/auth-pam.h b/auth-pam.h
index c47b442..4198607 100644
--- a/auth-pam.h
+++ b/auth-pam.h
@@ -25,10 +25,12 @@
 #include "includes.h"
 #ifdef USE_PAM
 
+struct ssh;
+
 void start_pam(Authctxt *);
 void finish_pam(void);
 u_int do_pam_account(void);
-void do_pam_session(void);
+void do_pam_session(struct ssh *);
 void do_pam_setcred(int );
 void do_pam_chauthtok(void);
 int do_pam_putenv(char *, char *);
diff --git a/auth-passwd.c b/auth-passwd.c
index 996c2cf..6097fdd 100644
--- a/auth-passwd.c
+++ b/auth-passwd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-passwd.c,v 1.45 2016/07/21 01:39:35 dtucker Exp $ */
+/* $OpenBSD: auth-passwd.c,v 1.46 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -68,22 +68,15 @@
 
 #define MAX_PASSWORD_LEN	1024
 
-void
-disable_forwarding(void)
-{
-	no_port_forwarding_flag = 1;
-	no_agent_forwarding_flag = 1;
-	no_x11_forwarding_flag = 1;
-}
-
 /*
  * Tries to authenticate the user using password.  Returns true if
  * authentication succeeds.
  */
 int
-auth_password(Authctxt *authctxt, const char *password)
+auth_password(struct ssh *ssh, const char *password)
 {
-	struct passwd * pw = authctxt->pw;
+	Authctxt *authctxt = ssh->authctxt;
+	struct passwd *pw = authctxt->pw;
 	int result, ok = authctxt->valid;
 #if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
 	static int expire_checked = 0;
@@ -128,9 +121,9 @@
 			authctxt->force_pwchange = 1;
 	}
 #endif
-	result = sys_auth_passwd(authctxt, password);
+	result = sys_auth_passwd(ssh, password);
 	if (authctxt->force_pwchange)
-		disable_forwarding();
+		auth_restrict_session(ssh);
 	return (result && ok);
 }
 
@@ -170,19 +163,19 @@
 }
 
 int
-sys_auth_passwd(Authctxt *authctxt, const char *password)
+sys_auth_passwd(struct ssh *ssh, const char *password)
 {
-	struct passwd *pw = authctxt->pw;
+	Authctxt *authctxt = ssh->authctxt;
 	auth_session_t *as;
 	static int expire_checked = 0;
 
-	as = auth_usercheck(pw->pw_name, authctxt->style, "auth-ssh",
+	as = auth_usercheck(authctxt->pw->pw_name, authctxt->style, "auth-ssh",
 	    (char *)password);
 	if (as == NULL)
 		return (0);
 	if (auth_getstate(as) & AUTH_PWEXPIRED) {
 		auth_close(as);
-		disable_forwarding();
+		auth_restrict_session(ssh);
 		authctxt->force_pwchange = 1;
 		return (1);
 	} else {
@@ -195,8 +188,9 @@
 }
 #elif !defined(CUSTOM_SYS_AUTH_PASSWD)
 int
-sys_auth_passwd(Authctxt *authctxt, const char *password)
+sys_auth_passwd(struct ssh *ssh, const char *password)
 {
+	Authctxt *authctxt = ssh->authctxt;
 	struct passwd *pw = authctxt->pw;
 	char *encrypted_password, *salt = NULL;
 
diff --git a/auth.c b/auth.c
index fd02eff..041a09e 100644
--- a/auth.c
+++ b/auth.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.c,v 1.125 2018/01/08 15:21:49 markus Exp $ */
+/* $OpenBSD: auth.c,v 1.126 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -74,12 +74,14 @@
 #include "authfile.h"
 #include "ssherr.h"
 #include "compat.h"
+#include "channels.h"
 
 /* import */
 extern ServerOptions options;
 extern int use_privsep;
 extern Buffer loginmsg;
 extern struct passwd *privsep_pw;
+extern struct sshauthopt *auth_opts;
 
 /* Debugging messages */
 Buffer auth_debug;
@@ -386,10 +388,8 @@
  * Check whether root logins are disallowed.
  */
 int
-auth_root_allowed(const char *method)
+auth_root_allowed(struct ssh *ssh, const char *method)
 {
-	struct ssh *ssh = active_state; /* XXX */
-
 	switch (options.permit_root_login) {
 	case PERMIT_YES:
 		return 1;
@@ -400,7 +400,7 @@
 			return 1;
 		break;
 	case PERMIT_FORCED_ONLY:
-		if (forced_command) {
+		if (auth_opts->force_command != NULL) {
 			logit("Root login accepted for forced command.");
 			return 1;
 		}
@@ -993,3 +993,173 @@
 		*child = f;
 	return pid;
 }
+
+/* These functions link key/cert options to the auth framework */
+
+/* Log sshauthopt options locally and (optionally) for remote transmission */
+void
+auth_log_authopts(const char *loc, const struct sshauthopt *opts, int do_remote)
+{
+	int do_env = options.permit_user_env && opts->nenv > 0;
+	int do_permitopen = opts->npermitopen > 0 &&
+	    (options.allow_tcp_forwarding & FORWARD_LOCAL) != 0;
+	size_t i;
+	char msg[1024], tbuf[32];
+
+	snprintf(tbuf, sizeof(tbuf), "%d", opts->force_tun_device);
+	/* Try to keep this alphabetically sorted */
+	snprintf(msg, sizeof(msg), "key options:%s%s%s%s%s%s%s%s%s%s%s",
+	    opts->permit_agent_forwarding_flag ? " agent-forwarding" : "",
+	    opts->force_command == NULL ? "" : " command",
+	    do_env ?  " environment" : "",
+	    do_permitopen ?  " permitopen" : "",
+	    opts->permit_port_forwarding_flag ? " port-forwarding" : "",
+	    opts->cert_principals == NULL ? "" : " principals",
+	    opts->permit_pty_flag ? " pty" : "",
+	    opts->force_tun_device == -1 ? "" : " tun=",
+	    opts->force_tun_device == -1 ? "" : tbuf,
+	    opts->permit_user_rc ? " user-rc" : "",
+	    opts->permit_x11_forwarding_flag ? " x11-forwarding" : "");
+
+	debug("%s: %s", loc, msg);
+	if (do_remote)
+		auth_debug_add("%s: %s", loc, msg);
+
+	if (options.permit_user_env) {
+		for (i = 0; i < opts->nenv; i++) {
+			debug("%s: environment: %s", loc, opts->env[i]);
+			if (do_remote) {
+				auth_debug_add("%s: environment: %s",
+				    loc, opts->env[i]);
+			}
+		}
+	}
+
+	/* Go into a little more details for the local logs. */
+	if (opts->cert_principals != NULL) {
+		debug("%s: authorized principals: \"%s\"",
+		    loc, opts->cert_principals);
+	}
+	if (opts->force_command != NULL)
+		debug("%s: forced command: \"%s\"", loc, opts->force_command);
+	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) {
+		for (i = 0; i < opts->npermitopen; i++) {
+			debug("%s: permitted open: %s",
+			    loc, opts->permitopen[i]);
+		}
+	}
+}
+
+/* Activate a new set of key/cert options; merging with what is there. */
+int
+auth_activate_options(struct ssh *ssh, struct sshauthopt *opts)
+{
+	struct sshauthopt *old = auth_opts;
+	const char *emsg = NULL;
+
+	debug("%s: setting new authentication options", __func__);
+	if ((auth_opts = sshauthopt_merge(old, opts, &emsg)) == NULL) {
+		error("Inconsistent authentication options: %s", emsg);
+		return -1;
+	}
+	return 0;
+}
+
+/* Disable forwarding, etc for the session */
+void
+auth_restrict_session(struct ssh *ssh)
+{
+	struct sshauthopt *restricted;
+
+	debug("%s: restricting session", __func__);
+
+	/* A blank sshauthopt defaults to permitting nothing */
+	restricted = sshauthopt_new();
+	restricted->restricted = 1;
+
+	if (auth_activate_options(ssh, restricted) != 0)
+		fatal("%s: failed to restrict session", __func__);
+	sshauthopt_free(restricted);
+}
+
+int
+auth_authorise_keyopts(struct ssh *ssh, struct passwd *pw,
+    struct sshauthopt *opts, int allow_cert_authority, const char *loc)
+{
+	const char *remote_ip = ssh_remote_ipaddr(ssh);
+	const char *remote_host = auth_get_canonical_hostname(ssh,
+	    options.use_dns);
+
+	/* Consistency checks */
+	if (opts->cert_principals != NULL && !opts->cert_authority) {
+		debug("%s: principals on non-CA key", loc);
+		auth_debug_add("%s: principals on non-CA key", loc);
+		/* deny access */
+		return -1;
+	}
+	/* cert-authority flag isn't valid in authorized_principals files */
+	if (!allow_cert_authority && opts->cert_authority) {
+		debug("%s: cert-authority flag invalid here", loc);
+		auth_debug_add("%s: cert-authority flag invalid here", loc);
+		/* deny access */
+		return -1;
+	}
+
+	/* Perform from= checks */
+	if (opts->required_from_host_keys != NULL) {
+		switch (match_host_and_ip(remote_host, remote_ip,
+		    opts->required_from_host_keys )) {
+		case 1:
+			/* Host name matches. */
+			break;
+		case -1:
+		default:
+			debug("%s: invalid from criteria", loc);
+			auth_debug_add("%s: invalid from criteria", loc);
+			/* FALLTHROUGH */
+		case 0:
+			logit("%s: Authentication tried for %.100s with "
+			    "correct key but not from a permitted "
+			    "host (host=%.200s, ip=%.200s, required=%.200s).",
+			    loc, pw->pw_name, remote_host, remote_ip,
+			    opts->required_from_host_keys);
+			auth_debug_add("%s: Your host '%.200s' is not "
+			    "permitted to use this key for login.",
+			    loc, remote_host);
+			/* deny access */
+			return -1;
+		}
+	}
+	/* Check source-address restriction from certificate */
+	if (opts->required_from_host_cert != NULL) {
+		switch (addr_match_cidr_list(remote_ip,
+		    opts->required_from_host_cert)) {
+		case 1:
+			/* accepted */
+			break;
+		case -1:
+		default:
+			/* invalid */
+			error("%s: Certificate source-address invalid",
+			    loc);
+			/* FALLTHROUGH */
+		case 0:
+			logit("%s: Authentication tried for %.100s with valid "
+			    "certificate but not from a permitted source "
+			    "address (%.200s).", loc, pw->pw_name, remote_ip);
+			auth_debug_add("%s: Your address '%.200s' is not "
+			    "permitted to use this certificate for login.",
+			    loc, remote_ip);
+			return -1;
+		}
+	}
+	/*
+	 *
+	 * XXX this is spammy. We should report remotely only for keys
+	 *     that are successful in actual auth attempts, and not PK_OK
+	 *     tests.
+	 */
+	auth_log_authopts(loc, opts, 1);
+
+	return 0;
+}
diff --git a/auth.h b/auth.h
index 64f3c2e..23ce67c 100644
--- a/auth.h
+++ b/auth.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth.h,v 1.94 2018/01/08 15:21:49 markus Exp $ */
+/* $OpenBSD: auth.h,v 1.95 2018/03/03 03:15:51 djm Exp $ */
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -42,9 +42,11 @@
 #include <krb5.h>
 #endif
 
+struct passwd;
 struct ssh;
-struct sshkey;
 struct sshbuf;
+struct sshkey;
+struct sshauthopt;
 
 typedef struct Authctxt Authctxt;
 typedef struct Authmethod Authmethod;
@@ -128,11 +130,12 @@
 int
 auth_rhosts2(struct passwd *, const char *, const char *, const char *);
 
-int      auth_password(Authctxt *, const char *);
+int      auth_password(struct ssh *, const char *);
 
 int	 hostbased_key_allowed(struct passwd *, const char *, char *,
 	    struct sshkey *);
-int	 user_key_allowed(struct passwd *, struct sshkey *, int);
+int	 user_key_allowed(struct ssh *, struct passwd *, struct sshkey *, int,
+    struct sshauthopt **);
 int	 auth2_key_already_used(Authctxt *, const struct sshkey *);
 
 /*
@@ -163,14 +166,12 @@
 #include "audit.h"
 void remove_kbdint_device(const char *);
 
-void disable_forwarding(void);
-
 void	do_authentication2(Authctxt *);
 
 void	auth_log(Authctxt *, int, int, const char *, const char *);
 void	auth_maxtries_exceeded(Authctxt *) __attribute__((noreturn));
 void	userauth_finish(struct ssh *, int, const char *, const char *);
-int	auth_root_allowed(const char *);
+int	auth_root_allowed(struct ssh *, const char *);
 
 void	userauth_send_banner(const char *);
 
@@ -214,8 +215,17 @@
 int	 sshd_hostkey_sign(struct sshkey *, struct sshkey *, u_char **,
 	     size_t *, const u_char *, size_t, const char *, u_int);
 
+/* Key / cert options linkage to auth layer */
+const struct sshauthopt *auth_options(struct ssh *);
+int	 auth_activate_options(struct ssh *, struct sshauthopt *);
+void	 auth_restrict_session(struct ssh *);
+int	 auth_authorise_keyopts(struct ssh *, struct passwd *pw,
+    struct sshauthopt *, int, const char *);
+void	 auth_log_authopts(const char *, const struct sshauthopt *, int);
+
 /* debug messages during authentication */
-void	 auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2)));
+void	 auth_debug_add(const char *fmt,...)
+    __attribute__((format(printf, 1, 2)));
 void	 auth_debug_send(void);
 void	 auth_debug_reset(void);
 
@@ -227,7 +237,7 @@
 pid_t	subprocess(const char *, struct passwd *,
     const char *, int, char **, FILE **, u_int flags);
 
-int	 sys_auth_passwd(Authctxt *, const char *);
+int	 sys_auth_passwd(struct ssh *, const char *);
 
 #define SKEY_PROMPT "\nS/Key Password: "
 
diff --git a/auth2-none.c b/auth2-none.c
index 35d25fa..8d4e9bb 100644
--- a/auth2-none.c
+++ b/auth2-none.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-none.c,v 1.20 2017/05/30 14:29:59 markus Exp $ */
+/* $OpenBSD: auth2-none.c,v 1.21 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -68,7 +68,7 @@
 	if ((r = sshpkt_get_end(ssh)) != 0)
 		fatal("%s: %s", __func__, ssh_err(r));
 	if (options.permit_empty_passwd && options.password_authentication)
-		return (PRIVSEP(auth_password(ssh->authctxt, "")));
+		return (PRIVSEP(auth_password(ssh, "")));
 	return (0);
 }
 
diff --git a/auth2-passwd.c b/auth2-passwd.c
index 5f7ba32..445016a 100644
--- a/auth2-passwd.c
+++ b/auth2-passwd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-passwd.c,v 1.14 2017/05/30 14:29:59 markus Exp $ */
+/* $OpenBSD: auth2-passwd.c,v 1.15 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -63,7 +63,7 @@
 
 	if (change)
 		logit("password change not supported");
-	else if (PRIVSEP(auth_password(ssh->authctxt, password)) == 1)
+	else if (PRIVSEP(auth_password(ssh, password)) == 1)
 		authenticated = 1;
 	explicit_bzero(password, len);
 	free(password);
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index 8fb7ffe..8024b1d 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.76 2018/02/07 22:52:45 dtucker Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.77 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -88,6 +88,7 @@
 userauth_pubkey(struct ssh *ssh)
 {
 	Authctxt *authctxt = ssh->authctxt;
+	struct passwd *pw = authctxt->pw;
 	struct sshbuf *b;
 	struct sshkey *key = NULL;
 	char *pkalg, *userstyle = NULL, *key_s = NULL, *ca_s = NULL;
@@ -95,6 +96,7 @@
 	size_t blen, slen;
 	int r, pktype;
 	int authenticated = 0;
+	struct sshauthopt *authopts = NULL;
 
 	if (!authctxt->valid) {
 		debug2("%s: disabled because of invalid user", __func__);
@@ -185,7 +187,7 @@
 
 		/* test for correct signature */
 		authenticated = 0;
-		if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) &&
+		if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) &&
 		    PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b),
 		    sshbuf_len(b), NULL, ssh->compat)) == 0) {
 			authenticated = 1;
@@ -210,7 +212,7 @@
 		 * if a user is not allowed to login. is this an
 		 * issue? -markus
 		 */
-		if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) {
+		if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) {
 			if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK))
 			    != 0 ||
 			    (r = sshpkt_put_cstring(ssh, pkalg)) != 0 ||
@@ -221,10 +223,14 @@
 			authctxt->postponed = 1;
 		}
 	}
-	if (authenticated != 1)
-		auth_clear_options();
 done:
+	if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) {
+		debug("%s: key options inconsistent with existing", __func__);
+		authenticated = 0;
+	}
 	debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg);
+
+	sshauthopt_free(authopts);
 	sshkey_free(key);
 	free(userstyle);
 	free(pkalg);
@@ -254,18 +260,77 @@
 	return 0;
 }
 
+/*
+ * Process a single authorized_principals format line. Returns 0 and sets
+ * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a
+ * log preamble for file/line information.
+ */
 static int
-process_principals(FILE *f, const char *file, struct passwd *pw,
-    const struct sshkey_cert *cert)
+check_principals_line(struct ssh *ssh, char *cp, const struct sshkey_cert *cert,
+    const char *loc, struct sshauthopt **authoptsp)
 {
-	char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
+	u_int i, found = 0;
+	char *ep, *line_opts;
+	const char *reason = NULL;
+	struct sshauthopt *opts = NULL;
+
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
+
+	/* Trim trailing whitespace. */
+	ep = cp + strlen(cp) - 1;
+	while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
+		*ep-- = '\0';
+
+	/*
+	 * If the line has internal whitespace then assume it has
+	 * key options.
+	 */
+	line_opts = NULL;
+	if ((ep = strrchr(cp, ' ')) != NULL ||
+	    (ep = strrchr(cp, '\t')) != NULL) {
+		for (; *ep == ' ' || *ep == '\t'; ep++)
+			;
+		line_opts = cp;
+		cp = ep;
+	}
+	if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {
+		debug("%s: bad principals options: %s", loc, reason);
+		auth_debug_add("%s: bad principals options: %s", loc, reason);
+		return -1;
+	}
+	/* Check principals in cert against those on line */
+	for (i = 0; i < cert->nprincipals; i++) {
+		if (strcmp(cp, cert->principals[i]) != 0)
+			continue;
+		debug3("%s: matched principal \"%.100s\"",
+		    loc, cert->principals[i]);
+		found = 1;
+	}
+	if (found && authoptsp != NULL) {
+		*authoptsp = opts;
+		opts = NULL;
+	}
+	sshauthopt_free(opts);
+	return found ? 0 : -1;
+}
+
+static int
+process_principals(struct ssh *ssh, FILE *f, const char *file,
+    const struct sshkey_cert *cert, struct sshauthopt **authoptsp)
+{
+	char loc[256], line[SSH_MAX_PUBKEY_BYTES], *cp, *ep;
 	u_long linenum = 0;
-	u_int i, found_principal = 0;
+	u_int found_principal = 0;
+
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
 
 	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
 		/* Always consume entire input */
 		if (found_principal)
 			continue;
+
 		/* Skip leading whitespace. */
 		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
 			;
@@ -274,50 +339,33 @@
 			*ep = '\0';
 		if (!*cp || *cp == '\n')
 			continue;
-		/* Trim trailing whitespace. */
-		ep = cp + strlen(cp) - 1;
-		while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
-			*ep-- = '\0';
-		/*
-		 * If the line has internal whitespace then assume it has
-		 * key options.
-		 */
-		line_opts = NULL;
-		if ((ep = strrchr(cp, ' ')) != NULL ||
-		    (ep = strrchr(cp, '\t')) != NULL) {
-			for (; *ep == ' ' || *ep == '\t'; ep++)
-				;
-			line_opts = cp;
-			cp = ep;
-		}
-		for (i = 0; i < cert->nprincipals; i++) {
-			if (strcmp(cp, cert->principals[i]) == 0) {
-				debug3("%s:%lu: matched principal \"%.100s\"",
-				    file, linenum, cert->principals[i]);
-				if (auth_parse_options(pw, line_opts,
-				    file, linenum) != 1)
-					continue;
-				found_principal = 1;
-				continue;
-			}
-		}
+
+		snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
+		if (check_principals_line(ssh, cp, cert, loc, authoptsp) == 0)
+			found_principal = 1;
 	}
 	return found_principal;
 }
 
+/* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */
+
 static int
-match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
+match_principals_file(struct ssh *ssh, struct passwd *pw, char *file,
+    struct sshkey_cert *cert, struct sshauthopt **authoptsp)
 {
 	FILE *f;
 	int success;
 
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
+
 	temporarily_use_uid(pw);
 	debug("trying authorized principals file %s", file);
 	if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) {
 		restore_uid();
 		return 0;
 	}
-	success = process_principals(f, file, pw, cert);
+	success = process_principals(ssh, f, file, cert, authoptsp);
 	fclose(f);
 	restore_uid();
 	return success;
@@ -328,12 +376,13 @@
  * returns 1 if the principal is allowed or 0 otherwise.
  */
 static int
-match_principals_command(struct passwd *user_pw, const struct sshkey *key)
+match_principals_command(struct ssh *ssh, struct passwd *user_pw,
+    const struct sshkey *key, struct sshauthopt **authoptsp)
 {
+	struct passwd *runas_pw = NULL;
 	const struct sshkey_cert *cert = key->cert;
 	FILE *f = NULL;
 	int r, ok, found_principal = 0;
-	struct passwd *pw;
 	int i, ac = 0, uid_swapped = 0;
 	pid_t pid;
 	char *tmp, *username = NULL, *command = NULL, **av = NULL;
@@ -341,6 +390,8 @@
 	char serial_s[16];
 	void (*osigchld)(int);
 
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
 	if (options.authorized_principals_command == NULL)
 		return 0;
 	if (options.authorized_principals_command_user == NULL) {
@@ -358,8 +409,8 @@
 	/* Prepare and verify the user for the command */
 	username = percent_expand(options.authorized_principals_command_user,
 	    "u", user_pw->pw_name, (char *)NULL);
-	pw = getpwnam(username);
-	if (pw == NULL) {
+	runas_pw = getpwnam(username);
+	if (runas_pw == NULL) {
 		error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s",
 		    username, strerror(errno));
 		goto out;
@@ -417,15 +468,15 @@
 	/* Prepare a printable command for logs, etc. */
 	command = argv_assemble(ac, av);
 
-	if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command,
+	if ((pid = subprocess("AuthorizedPrincipalsCommand", runas_pw, command,
 	    ac, av, &f,
 	    SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0)
 		goto out;
 
 	uid_swapped = 1;
-	temporarily_use_uid(pw);
+	temporarily_use_uid(runas_pw);
 
-	ok = process_principals(f, "(command)", pw, cert);
+	ok = process_principals(ssh, f, "(command)", cert, authoptsp);
 
 	fclose(f);
 	f = NULL;
@@ -452,130 +503,225 @@
 	free(keytext);
 	return found_principal;
 }
+
+static void
+skip_space(char **cpp)
+{
+	char *cp;
+
+	for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++)
+		;
+	*cpp = cp;
+}
+
+/*
+ * Advanced *cpp past the end of key options, defined as the first unquoted
+ * whitespace character. Returns 0 on success or -1 on failure (e.g.
+ * unterminated quotes).
+ */
+static int
+advance_past_options(char **cpp)
+{
+	char *cp = *cpp;
+	int quoted = 0;
+
+	for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
+		if (*cp == '\\' && cp[1] == '"')
+			cp++;	/* Skip both */
+		else if (*cp == '"')
+			quoted = !quoted;
+	}
+	*cpp = cp;
+	/* return failure for unterminated quotes */
+	return (*cp == '\0' && quoted) ? -1 : 0;
+}
+
+/*
+ * Check a single line of an authorized_keys-format file. Returns 0 if key
+ * matches, -1 otherwise. Will return key/cert options via *authoptsp
+ * on success. "loc" is used as file/line location in log messages.
+ */
+static int
+check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
+    char *cp, const char *loc, struct sshauthopt **authoptsp)
+{
+	int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
+	struct sshkey *found = NULL;
+	struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
+	char *key_options = NULL, *fp = NULL;
+	const char *reason = NULL;
+	int ret = -1;
+
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
+
+	if ((found = sshkey_new(want_keytype)) == NULL) {
+		debug3("%s: keytype %d failed", __func__, want_keytype);
+		goto out;
+	}
+
+	/* XXX djm: peek at key type in line and skip if unwanted */
+
+	if (sshkey_read(found, &cp) != 0) {
+		/* no key?  check for options */
+		debug2("%s: check options: '%s'", loc, cp);
+		key_options = cp;
+		if (advance_past_options(&cp) != 0) {
+			reason = "invalid key option string";
+			goto fail_reason;
+		}
+		skip_space(&cp);
+		if (sshkey_read(found, &cp) != 0) {
+			/* still no key?  advance to next line*/
+			debug2("%s: advance: '%s'", loc, cp);
+			goto out;
+		}
+	}
+	/* Parse key options now; we need to know if this is a CA key */
+	if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
+		debug("%s: bad key options: %s", loc, reason);
+		auth_debug_add("%s: bad key options: %s", loc, reason);
+		goto out;
+	}
+	/* Ignore keys that don't match or incorrectly marked as CAs */
+	if (sshkey_is_cert(key)) {
+		/* Certificate; check signature key against CA */
+		if (!sshkey_equal(found, key->cert->signature_key) ||
+		    !keyopts->cert_authority)
+			goto out;
+	} else {
+		/* Plain key: check it against key found in file */
+		if (!sshkey_equal(found, key) || keyopts->cert_authority)
+			goto out;
+	}
+
+	/* We have a candidate key, perform authorisation checks */
+	if ((fp = sshkey_fingerprint(found,
+	    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
+		fatal("%s: fingerprint failed", __func__);
+
+	debug("%s: matching %s found: %s %s", loc,
+	    sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
+
+	if (auth_authorise_keyopts(ssh, pw, keyopts,
+	    sshkey_is_cert(key), loc) != 0) {
+		reason = "Refused by key options";
+		goto fail_reason;
+	}
+	/* That's all we need for plain keys. */
+	if (!sshkey_is_cert(key)) {
+		verbose("Accepted key %s %s found at %s",
+		    sshkey_type(found), fp, loc);
+		finalopts = keyopts;
+		keyopts = NULL;
+		goto success;
+	}
+
+	/*
+	 * Additional authorisation for certificates.
+	 */
+
+	/* Parse and check options present in certificate */
+	if ((certopts = sshauthopt_from_cert(key)) == NULL) {
+		reason = "Invalid certificate options";
+		goto fail_reason;
+	}
+	if (auth_authorise_keyopts(ssh, pw, certopts, 0, loc) != 0) {
+		reason = "Refused by certificate options";
+		goto fail_reason;
+	}
+	if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
+		goto fail_reason;
+
+	/*
+	 * If the user has specified a list of principals as
+	 * a key option, then prefer that list to matching
+	 * their username in the certificate principals list.
+	 */
+	if (keyopts->cert_principals != NULL &&
+	    !match_principals_option(keyopts->cert_principals, key->cert)) {
+		reason = "Certificate does not contain an authorized principal";
+		goto fail_reason;
+	}
+	if (sshkey_cert_check_authority(key, 0, 0,
+	   keyopts->cert_principals == NULL ? pw->pw_name : NULL, &reason) != 0)
+		goto fail_reason;
+
+	verbose("Accepted certificate ID \"%s\" (serial %llu) "
+	    "signed by CA %s %s found at %s",
+	    key->cert->key_id,
+	    (unsigned long long)key->cert->serial,
+	    sshkey_type(found), fp, loc);
+
+ success:
+	if (finalopts == NULL)
+		fatal("%s: internal error: missing options", __func__);
+	if (authoptsp != NULL) {
+		*authoptsp = finalopts;
+		finalopts = NULL;
+	}
+	/* success */
+	ret = 0;
+	goto out;
+
+ fail_reason:
+	error("%s", reason);
+	auth_debug_add("%s", reason);
+ out:
+	free(fp);
+	sshauthopt_free(keyopts);
+	sshauthopt_free(certopts);
+	sshauthopt_free(finalopts);
+	sshkey_free(found);
+	return ret;
+}
+
 /*
  * Checks whether key is allowed in authorized_keys-format file,
  * returns 1 if the key is allowed or 0 otherwise.
  */
 static int
-check_authkeys_file(FILE *f, char *file, struct sshkey *key, struct passwd *pw)
+check_authkeys_file(struct ssh *ssh, struct passwd *pw, FILE *f,
+    char *file, struct sshkey *key, struct sshauthopt **authoptsp)
 {
-	char line[SSH_MAX_PUBKEY_BYTES];
+	char *cp, line[SSH_MAX_PUBKEY_BYTES], loc[256];
 	int found_key = 0;
 	u_long linenum = 0;
-	struct sshkey *found = NULL;
+
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
 
 	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
-		char *cp, *key_options = NULL, *fp = NULL;
-		const char *reason = NULL;
-
 		/* Always consume entire file */
 		if (found_key)
 			continue;
-		sshkey_free(found);
-		found = sshkey_new(sshkey_is_cert(key) ? KEY_UNSPEC : key->type);
-		if (found == NULL)
-			goto done;
-		auth_clear_options();
 
 		/* Skip leading whitespace, empty and comment lines. */
-		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
-			;
+		cp = line;
+		skip_space(&cp);
 		if (!*cp || *cp == '\n' || *cp == '#')
 			continue;
-
-		if (sshkey_read(found, &cp) != 0) {
-			/* no key?  check if there are options for this key */
-			int quoted = 0;
-			debug2("user_key_allowed: check options: '%s'", cp);
-			key_options = cp;
-			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
-				if (*cp == '\\' && cp[1] == '"')
-					cp++;	/* Skip both */
-				else if (*cp == '"')
-					quoted = !quoted;
-			}
-			/* Skip remaining whitespace. */
-			for (; *cp == ' ' || *cp == '\t'; cp++)
-				;
-			if (sshkey_read(found, &cp) != 0) {
-				debug2("user_key_allowed: advance: '%s'", cp);
-				/* still no key?  advance to next line*/
-				continue;
-			}
-		}
-		if (sshkey_is_cert(key)) {
-			if (!sshkey_equal(found, key->cert->signature_key))
-				continue;
-			if (auth_parse_options(pw, key_options, file,
-			    linenum) != 1)
-				continue;
-			if (!key_is_cert_authority)
-				continue;
-			if ((fp = sshkey_fingerprint(found,
-			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
-				continue;
-			debug("matching CA found: file %s, line %lu, %s %s",
-			    file, linenum, sshkey_type(found), fp);
-			/*
-			 * If the user has specified a list of principals as
-			 * a key option, then prefer that list to matching
-			 * their username in the certificate principals list.
-			 */
-			if (authorized_principals != NULL &&
-			    !match_principals_option(authorized_principals,
-			    key->cert)) {
-				reason = "Certificate does not contain an "
-				    "authorized principal";
- fail_reason:
-				free(fp);
-				error("%s", reason);
-				auth_debug_add("%s", reason);
-				continue;
-			}
-			if (sshkey_cert_check_authority(key, 0, 0,
-			    authorized_principals == NULL ? pw->pw_name : NULL,
-			    &reason) != 0)
-				goto fail_reason;
-			if (auth_cert_options(key, pw, &reason) != 0)
-				goto fail_reason;
-			verbose("Accepted certificate ID \"%s\" (serial %llu) "
-			    "signed by %s CA %s via %s", key->cert->key_id,
-			    (unsigned long long)key->cert->serial,
-			    sshkey_type(found), fp, file);
-			free(fp);
+		snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
+		if (check_authkey_line(ssh, pw, key, cp, loc, authoptsp) == 0)
 			found_key = 1;
-			break;
-		} else if (sshkey_equal(found, key)) {
-			if (auth_parse_options(pw, key_options, file,
-			    linenum) != 1)
-				continue;
-			if (key_is_cert_authority)
-				continue;
-			if ((fp = sshkey_fingerprint(found,
-			    options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL)
-				continue;
-			debug("matching key found: file %s, line %lu %s %s",
-			    file, linenum, sshkey_type(found), fp);
-			free(fp);
-			found_key = 1;
-			continue;
-		}
 	}
- done:
-	sshkey_free(found);
-	if (!found_key)
-		debug2("key not found");
 	return found_key;
 }
 
 /* Authenticate a certificate key against TrustedUserCAKeys */
 static int
-user_cert_trusted_ca(struct passwd *pw, struct sshkey *key)
+user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
+    struct sshauthopt **authoptsp)
 {
 	char *ca_fp, *principals_file = NULL;
 	const char *reason;
+	struct sshauthopt *principals_opts = NULL, *cert_opts = NULL;
+	struct sshauthopt *final_opts = NULL;
 	int r, ret = 0, found_principal = 0, use_authorized_principals;
 
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
+
 	if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL)
 		return 0;
 
@@ -596,36 +742,69 @@
 	 * against the username.
 	 */
 	if ((principals_file = authorized_principals_file(pw)) != NULL) {
-		if (match_principals_file(principals_file, pw, key->cert))
+		if (match_principals_file(ssh, pw, principals_file,
+		    key->cert, &principals_opts))
 			found_principal = 1;
 	}
 	/* Try querying command if specified */
-	if (!found_principal && match_principals_command(pw, key))
+	if (!found_principal && match_principals_command(ssh, pw, key,
+	    &principals_opts))
 		found_principal = 1;
 	/* If principals file or command is specified, then require a match */
 	use_authorized_principals = principals_file != NULL ||
             options.authorized_principals_command != NULL;
 	if (!found_principal && use_authorized_principals) {
 		reason = "Certificate does not contain an authorized principal";
- fail_reason:
-		error("%s", reason);
-		auth_debug_add("%s", reason);
-		goto out;
+		goto fail_reason;
 	}
+	if (use_authorized_principals && principals_opts == NULL)
+		fatal("%s: internal error: missing principals_opts", __func__);
 	if (sshkey_cert_check_authority(key, 0, 1,
 	    use_authorized_principals ? NULL : pw->pw_name, &reason) != 0)
 		goto fail_reason;
-	if (auth_cert_options(key, pw, &reason) != 0)
-		goto fail_reason;
 
+	/* Check authority from options in key and from principals file/cmd */
+	if ((cert_opts = sshauthopt_from_cert(key)) == NULL) {
+		reason = "Invalid certificate options";
+		goto fail_reason;
+	}
+	if (auth_authorise_keyopts(ssh, pw, cert_opts, 0, "cert") != 0) {
+		reason = "Refused by certificate options";
+		goto fail_reason;
+	}
+	if (principals_opts == NULL) {
+		final_opts = cert_opts;
+		cert_opts = NULL;
+	} else {
+		if (auth_authorise_keyopts(ssh, pw, principals_opts, 0,
+		    "principals") != 0) {
+			reason = "Refused by certificate principals options";
+			goto fail_reason;
+		}
+		if ((final_opts = sshauthopt_merge(principals_opts,
+		    cert_opts, &reason)) == NULL) {
+ fail_reason:
+			error("%s", reason);
+			auth_debug_add("%s", reason);
+			goto out;
+		}
+	}
+
+	/* 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,
 	    sshkey_type(key->cert->signature_key), ca_fp,
 	    options.trusted_user_ca_keys);
+	if (authoptsp != NULL) {
+		*authoptsp = final_opts;
+		final_opts = NULL;
+	}
 	ret = 1;
-
  out:
+	sshauthopt_free(principals_opts);
+	sshauthopt_free(cert_opts);
+	sshauthopt_free(final_opts);
 	free(principals_file);
 	free(ca_fp);
 	return ret;
@@ -636,17 +815,22 @@
  * returns 1 if the key is allowed or 0 otherwise.
  */
 static int
-user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file)
+user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
+    char *file, struct sshauthopt **authoptsp)
 {
 	FILE *f;
 	int found_key = 0;
 
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
+
 	/* Temporarily use the user's uid. */
 	temporarily_use_uid(pw);
 
 	debug("trying public key file %s", file);
 	if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) {
-		found_key = check_authkeys_file(f, file, key, pw);
+		found_key = check_authkeys_file(ssh, pw, f, file,
+		    key, authoptsp);
 		fclose(f);
 	}
 
@@ -659,17 +843,20 @@
  * returns 1 if the key is allowed or 0 otherwise.
  */
 static int
-user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key)
+user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw,
+    struct sshkey *key, struct sshauthopt **authoptsp)
 {
+	struct passwd *runas_pw = NULL;
 	FILE *f = NULL;
 	int r, ok, found_key = 0;
-	struct passwd *pw;
 	int i, uid_swapped = 0, ac = 0;
 	pid_t pid;
 	char *username = NULL, *key_fp = NULL, *keytext = NULL;
 	char *tmp, *command = NULL, **av = NULL;
 	void (*osigchld)(int);
 
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
 	if (options.authorized_keys_command == NULL)
 		return 0;
 	if (options.authorized_keys_command_user == NULL) {
@@ -686,8 +873,8 @@
 	/* Prepare and verify the user for the command */
 	username = percent_expand(options.authorized_keys_command_user,
 	    "u", user_pw->pw_name, (char *)NULL);
-	pw = getpwnam(username);
-	if (pw == NULL) {
+	runas_pw = getpwnam(username);
+	if (runas_pw == NULL) {
 		error("AuthorizedKeysCommandUser \"%s\" not found: %s",
 		    username, strerror(errno));
 		goto out;
@@ -745,15 +932,16 @@
 		xasprintf(&command, "%s %s", av[0], av[1]);
 	}
 
-	if ((pid = subprocess("AuthorizedKeysCommand", pw, command,
+	if ((pid = subprocess("AuthorizedKeysCommand", runas_pw, command,
 	    ac, av, &f,
 	    SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0)
 		goto out;
 
 	uid_swapped = 1;
-	temporarily_use_uid(pw);
+	temporarily_use_uid(runas_pw);
 
-	ok = check_authkeys_file(f, options.authorized_keys_command, key, pw);
+	ok = check_authkeys_file(ssh, user_pw, f,
+	    options.authorized_keys_command, key, authoptsp);
 
 	fclose(f);
 	f = NULL;
@@ -783,10 +971,14 @@
  * Check whether key authenticates and authorises the user.
  */
 int
-user_key_allowed(struct passwd *pw, struct sshkey *key, int auth_attempt)
+user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
+    int auth_attempt, struct sshauthopt **authoptsp)
 {
 	u_int success, i;
 	char *file;
+	struct sshauthopt *opts = NULL;
+	if (authoptsp != NULL)
+		*authoptsp = NULL;
 
 	if (auth_key_is_revoked(key))
 		return 0;
@@ -794,25 +986,31 @@
 	    auth_key_is_revoked(key->cert->signature_key))
 		return 0;
 
-	success = user_cert_trusted_ca(pw, key);
-	if (success)
-		return success;
+	if ((success = user_cert_trusted_ca(ssh, pw, key, &opts)) != 0)
+		goto out;
+	sshauthopt_free(opts);
+	opts = NULL;
 
-	success = user_key_command_allowed2(pw, key);
-	if (success > 0)
-		return success;
+	if ((success = user_key_command_allowed2(ssh, pw, key, &opts)) != 0)
+		goto out;
+	sshauthopt_free(opts);
+	opts = NULL;
 
 	for (i = 0; !success && i < options.num_authkeys_files; i++) {
-
 		if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
 			continue;
 		file = expand_authorized_keys(
 		    options.authorized_keys_files[i], pw);
-
-		success = user_key_allowed2(pw, key, file);
+		success = user_key_allowed2(ssh, pw, key, file, &opts);
 		free(file);
 	}
 
+ out:
+	if (success && authoptsp != NULL) {
+		*authoptsp = opts;
+		opts = NULL;
+	}
+	sshauthopt_free(opts);
 	return success;
 }
 
diff --git a/auth2.c b/auth2.c
index c80911a..e003422 100644
--- a/auth2.c
+++ b/auth2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2.c,v 1.144 2018/01/23 05:27:21 djm Exp $ */
+/* $OpenBSD: auth2.c,v 1.145 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  *
@@ -310,7 +310,7 @@
 
 	/* Special handling for root */
 	if (authenticated && authctxt->pw->pw_uid == 0 &&
-	    !auth_root_allowed(method)) {
+	    !auth_root_allowed(ssh, method)) {
 		authenticated = 0;
 #ifdef SSH_AUDIT_EVENTS
 		PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED));
diff --git a/misc.c b/misc.c
index b45a9ec..1e660b0 100644
--- a/misc.c
+++ b/misc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: misc.c,v 1.124 2018/03/02 03:02:11 djm Exp $ */
+/* $OpenBSD: misc.c,v 1.125 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
  * Copyright (c) 2005,2006 Damien Miller.  All rights reserved.
@@ -1921,6 +1921,7 @@
 	}
 
 	/* Allocate space and format the variable in the appropriate slot. */
+	/* XXX xasprintf */
 	env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
 	snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
 }
diff --git a/monitor.c b/monitor.c
index e4ac3cc..c68e1b0 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.179 2018/02/05 05:37:46 tb Exp $ */
+/* $OpenBSD: monitor.c,v 1.180 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -116,6 +116,7 @@
 extern Buffer auth_debug;
 extern int auth_debug_init;
 extern Buffer loginmsg;
+extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */
 
 /* State exported from the child */
 static struct sshbuf *child_state;
@@ -172,6 +173,7 @@
 static u_char *key_blob = NULL;
 static u_int key_bloblen = 0;
 static int key_blobtype = MM_NOKEY;
+static struct sshauthopt *key_opts = NULL;
 static char *hostbased_cuser = NULL;
 static char *hostbased_chost = NULL;
 static char *auth_method = "unknown";
@@ -252,7 +254,6 @@
 struct mon_table *mon_dispatch;
 
 /* Specifies if a certain message is allowed at the moment */
-
 static void
 monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit)
 {
@@ -297,6 +298,7 @@
 
 	authctxt = _authctxt;
 	memset(authctxt, 0, sizeof(*authctxt));
+	ssh->authctxt = authctxt;
 
 	authctxt->loginmsg = &loginmsg;
 
@@ -331,7 +333,7 @@
 				fatal("%s: unexpected authentication from %d",
 				    __func__, ent->type);
 			if (authctxt->pw->pw_uid == 0 &&
-			    !auth_root_allowed(auth_method))
+			    !auth_root_allowed(ssh, auth_method))
 				authenticated = 0;
 #ifdef USE_PAM
 			/* PAM needs to perform account checks after auth */
@@ -365,6 +367,7 @@
 
 	debug("%s: %s has been authenticated by privileged process",
 	    __func__, authctxt->user);
+	ssh->authctxt = NULL;
 	ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user);
 
 	mm_get_keystate(pmonitor);
@@ -413,7 +416,7 @@
 	monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1);
 	monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1);
 
-	if (!no_pty_flag) {
+	if (auth_opts->permit_pty_flag) {
 		monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1);
 		monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1);
 	}
@@ -558,9 +561,11 @@
 	free(key_blob);
 	free(hostbased_cuser);
 	free(hostbased_chost);
+	sshauthopt_free(key_opts);
 	key_blob = NULL;
 	key_bloblen = 0;
 	key_blobtype = MM_NOKEY;
+	key_opts = NULL;
 	hostbased_cuser = NULL;
 	hostbased_chost = NULL;
 }
@@ -828,6 +833,7 @@
 int
 mm_answer_authpassword(int sock, Buffer *m)
 {
+	struct ssh *ssh = active_state;	/* XXX */
 	static int call_count;
 	char *passwd;
 	int authenticated;
@@ -838,7 +844,7 @@
 	passwd = buffer_get_string(m, &plen);
 	/* Only authenticate if the context is valid */
 	authenticated = options.password_authentication &&
-	    auth_password(authctxt, passwd);
+	    auth_password(ssh, passwd);
 	explicit_bzero(passwd, strlen(passwd));
 	free(passwd);
 
@@ -1129,15 +1135,16 @@
 int
 mm_answer_keyallowed(int sock, Buffer *m)
 {
+	struct ssh *ssh = active_state;	/* XXX */
 	struct sshkey *key;
 	char *cuser, *chost;
 	u_char *blob;
 	u_int bloblen, pubkey_auth_attempt;
 	enum mm_keytype type = 0;
-	int allowed = 0;
+	int r, allowed = 0;
+	struct sshauthopt *opts = NULL;
 
 	debug3("%s entering", __func__);
-
 	type = buffer_get_int(m);
 	cuser = buffer_get_string(m, NULL);
 	chost = buffer_get_string(m, NULL);
@@ -1156,28 +1163,31 @@
 
 		switch (type) {
 		case MM_USERKEY:
-			allowed = options.pubkey_authentication &&
-			    !auth2_key_already_used(authctxt, key) &&
-			    match_pattern_list(sshkey_ssh_name(key),
-			    options.pubkey_key_types, 0) == 1 &&
-			    user_key_allowed(authctxt->pw, key,
-			    pubkey_auth_attempt);
 			auth_method = "publickey";
-			if (options.pubkey_authentication &&
-			    (!pubkey_auth_attempt || allowed != 1))
-				auth_clear_options();
+			if (!options.pubkey_authentication)
+				break;
+			if (auth2_key_already_used(authctxt, key))
+				break;
+			if (match_pattern_list(sshkey_ssh_name(key),
+			    options.pubkey_key_types, 0) != 1)
+				break;
+			allowed = user_key_allowed(ssh, authctxt->pw, key,
+			    pubkey_auth_attempt, &opts);
 			break;
 		case MM_HOSTKEY:
-			allowed = options.hostbased_authentication &&
-			    !auth2_key_already_used(authctxt, key) &&
-			    match_pattern_list(sshkey_ssh_name(key),
-			    options.hostbased_key_types, 0) == 1 &&
-			    hostbased_key_allowed(authctxt->pw,
+			auth_method = "hostbased";
+			if (!options.hostbased_authentication)
+				break;
+			if (auth2_key_already_used(authctxt, key))
+				break;
+			if (match_pattern_list(sshkey_ssh_name(key),
+			    options.hostbased_key_types, 0) != 1)
+				break;
+			allowed = hostbased_key_allowed(authctxt->pw,
 			    cuser, chost, key);
 			auth2_record_info(authctxt,
 			    "client user \"%.100s\", client host \"%.100s\"",
 			    cuser, chost);
-			auth_method = "hostbased";
 			break;
 		default:
 			fatal("%s: unknown key type %d", __func__, type);
@@ -1185,7 +1195,10 @@
 		}
 	}
 
-	debug3("%s: key is %s", __func__, allowed ? "allowed" : "not allowed");
+	debug3("%s: %s authentication%s: %s key is %s", __func__,
+	    auth_method, pubkey_auth_attempt ? "" : " test",
+	    (key == NULL || !authctxt->valid) ? "invalid" : sshkey_type(key),
+	    allowed ? "allowed" : "not allowed");
 
 	auth2_record_key(authctxt, 0, key);
 	sshkey_free(key);
@@ -1198,6 +1211,7 @@
 		key_blob = blob;
 		key_bloblen = bloblen;
 		key_blobtype = type;
+		key_opts = opts;
 		hostbased_cuser = cuser;
 		hostbased_chost = chost;
 	} else {
@@ -1210,10 +1224,13 @@
 
 	buffer_clear(m);
 	buffer_put_int(m, allowed);
-	buffer_put_int(m, forced_command != NULL);
-
+	if (opts != NULL && (r = sshauthopt_serialise(opts, m, 1)) != 0)
+		fatal("%s: sshauthopt_serialise: %s", __func__, ssh_err(r));
 	mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m);
 
+	if (!allowed)
+		sshauthopt_free(opts);
+
 	return (0);
 }
 
@@ -1336,6 +1353,7 @@
 int
 mm_answer_keyverify(int sock, struct sshbuf *m)
 {
+	struct ssh *ssh = active_state;	/* XXX */
 	struct sshkey *key;
 	u_char *signature, *data, *blob;
 	char *sigalg;
@@ -1390,6 +1408,8 @@
 	free(data);
 	free(sigalg);
 
+	if (key_blobtype == MM_USERKEY)
+		auth_activate_options(ssh, key_opts);
 	monitor_reset_key_state();
 
 	sshkey_free(key);
diff --git a/monitor_wrap.c b/monitor_wrap.c
index cce318b..9666bda 100644
--- a/monitor_wrap.c
+++ b/monitor_wrap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.c,v 1.98 2018/01/08 15:14:44 markus Exp $ */
+/* $OpenBSD: monitor_wrap.c,v 1.99 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -351,7 +351,7 @@
 
 /* Do the password authentication */
 int
-mm_auth_password(Authctxt *authctxt, char *password)
+mm_auth_password(struct ssh *ssh, char *password)
 {
 	Buffer m;
 	int authenticated = 0;
@@ -378,34 +378,38 @@
 }
 
 int
-mm_user_key_allowed(struct passwd *pw, struct sshkey *key,
-    int pubkey_auth_attempt)
+mm_user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key,
+    int pubkey_auth_attempt, struct sshauthopt **authoptp)
 {
 	return (mm_key_allowed(MM_USERKEY, NULL, NULL, key,
-	    pubkey_auth_attempt));
+	    pubkey_auth_attempt, authoptp));
 }
 
 int
 mm_hostbased_key_allowed(struct passwd *pw, const char *user, const char *host,
     struct sshkey *key)
 {
-	return (mm_key_allowed(MM_HOSTKEY, user, host, key, 0));
+	return (mm_key_allowed(MM_HOSTKEY, user, host, key, 0, NULL));
 }
 
 int
 mm_key_allowed(enum mm_keytype type, const char *user, const char *host,
-    struct sshkey *key, int pubkey_auth_attempt)
+    struct sshkey *key, int pubkey_auth_attempt, struct sshauthopt **authoptp)
 {
 	Buffer m;
 	u_char *blob;
 	u_int len;
-	int allowed = 0, have_forced = 0;
+	int r, allowed = 0;
+	struct sshauthopt *opts = NULL;
 
 	debug3("%s entering", __func__);
 
+	if (authoptp != NULL)
+		*authoptp = NULL;
+
 	/* Convert the key to a blob and the pass it over */
 	if (!key_to_blob(key, &blob, &len))
-		return (0);
+		return 0;
 
 	buffer_init(&m);
 	buffer_put_int(&m, type);
@@ -418,18 +422,24 @@
 	mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYALLOWED, &m);
 
 	debug3("%s: waiting for MONITOR_ANS_KEYALLOWED", __func__);
-	mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_KEYALLOWED, &m);
+	mm_request_receive_expect(pmonitor->m_recvfd,
+	    MONITOR_ANS_KEYALLOWED, &m);
 
 	allowed = buffer_get_int(&m);
-
-	/* fake forced command */
-	auth_clear_options();
-	have_forced = buffer_get_int(&m);
-	forced_command = have_forced ? xstrdup("true") : NULL;
-
+	if (allowed && type == MM_USERKEY) {
+		if ((r = sshauthopt_deserialise(&m, &opts)) != 0)
+			fatal("%s: sshauthopt_deserialise: %s",
+			    __func__, ssh_err(r));
+	}
 	buffer_free(&m);
 
-	return (allowed);
+	if (authoptp != NULL) {
+		*authoptp = opts;
+		opts = NULL;
+	}
+	sshauthopt_free(opts);
+
+	return allowed;
 }
 
 /*
diff --git a/monitor_wrap.h b/monitor_wrap.h
index f5af1e8..7623327 100644
--- a/monitor_wrap.h
+++ b/monitor_wrap.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor_wrap.h,v 1.36 2017/12/18 02:25:15 djm Exp $ */
+/* $OpenBSD: monitor_wrap.h,v 1.37 2018/03/03 03:15:51 djm Exp $ */
 
 /*
  * Copyright 2002 Niels Provos <provos@citi.umich.edu>
@@ -35,6 +35,8 @@
 
 struct monitor;
 struct Authctxt;
+struct sshkey;
+struct sshauthopt;
 
 void mm_log_handler(LogLevel, const char *, void *);
 int mm_is_monitor(void);
@@ -44,10 +46,11 @@
 void mm_inform_authserv(char *, char *);
 struct passwd *mm_getpwnamallow(const char *);
 char *mm_auth2_read_banner(void);
-int mm_auth_password(struct Authctxt *, char *);
+int mm_auth_password(struct ssh *, char *);
 int mm_key_allowed(enum mm_keytype, const char *, const char *, struct sshkey *,
-    int);
-int mm_user_key_allowed(struct passwd *, struct sshkey *, int);
+    int, struct sshauthopt **);
+int mm_user_key_allowed(struct ssh *, struct passwd *, struct sshkey *, int,
+    struct sshauthopt **);
 int mm_hostbased_key_allowed(struct passwd *, const char *,
     const char *, struct sshkey *);
 int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t,
diff --git a/serverloop.c b/serverloop.c
index 7e2abd5..d6fe24c 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: serverloop.c,v 1.204 2018/02/11 21:16:56 dtucker Exp $ */
+/* $OpenBSD: serverloop.c,v 1.205 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -82,6 +82,7 @@
 
 /* XXX */
 extern Authctxt *the_authctxt;
+extern struct sshauthopt *auth_opts;
 extern int use_privsep;
 
 static int no_more_sessions = 0; /* Disallow further sessions. */
@@ -456,12 +457,13 @@
 	originator_port = packet_get_int();
 	packet_check_eom();
 
-	debug("server_request_direct_tcpip: originator %s port %d, target %s "
-	    "port %d", originator, originator_port, target, target_port);
+	debug("%s: originator %s port %d, target %s port %d", __func__,
+	    originator, originator_port, target, target_port);
 
 	/* XXX fine grained permissions */
 	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 &&
-	    !no_port_forwarding_flag && !options.disable_forwarding) {
+	    auth_opts->permit_port_forwarding_flag &&
+	    !options.disable_forwarding) {
 		c = channel_connect_to_port(ssh, target, target_port,
 		    "direct-tcpip", "direct-tcpip", reason, errmsg);
 	} else {
@@ -487,20 +489,20 @@
 	struct passwd *pw = the_authctxt->pw;
 
 	if (pw == NULL || !the_authctxt->valid)
-		fatal("server_input_global_request: no/invalid user");
+		fatal("%s: no/invalid user", __func__);
 
 	target = packet_get_string(NULL);
 	originator = packet_get_string(NULL);
 	originator_port = packet_get_int();
 	packet_check_eom();
 
-	debug("server_request_direct_streamlocal: originator %s port %d, target %s",
+	debug("%s: originator %s port %d, target %s", __func__,
 	    originator, originator_port, target);
 
 	/* XXX fine grained permissions */
 	if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 &&
-	    !no_port_forwarding_flag && !options.disable_forwarding &&
-	    (pw->pw_uid == 0 || use_privsep)) {
+	    auth_opts->permit_port_forwarding_flag &&
+	    !options.disable_forwarding && (pw->pw_uid == 0 || use_privsep)) {
 		c = channel_connect_to_path(ssh, target,
 		    "direct-streamlocal@openssh.com", "direct-streamlocal");
 	} else {
@@ -519,8 +521,7 @@
 server_request_tun(struct ssh *ssh)
 {
 	Channel *c = NULL;
-	int mode, tun;
-	int sock;
+	int mode, tun, sock;
 	char *tmp, *ifname = NULL;
 
 	mode = packet_get_int();
@@ -539,10 +540,10 @@
 	}
 
 	tun = packet_get_int();
-	if (forced_tun_device != -1) {
-		if (tun != SSH_TUNID_ANY && forced_tun_device != tun)
+	if (auth_opts->force_tun_device != -1) {
+		if (tun != SSH_TUNID_ANY && auth_opts->force_tun_device != tun)
 			goto done;
-		tun = forced_tun_device;
+		tun = auth_opts->force_tun_device;
 	}
 	sock = tun_open(tun, mode, &ifname);
 	if (sock < 0)
@@ -767,7 +768,8 @@
 
 		/* check permissions */
 		if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 ||
-		    no_port_forwarding_flag || options.disable_forwarding ||
+		    !auth_opts->permit_port_forwarding_flag ||
+		    options.disable_forwarding ||
 		    (!want_reply && fwd.listen_port == 0) ||
 		    (fwd.listen_port != 0 &&
 		     !bind_permitted(fwd.listen_port, pw->pw_uid))) {
@@ -805,7 +807,8 @@
 
 		/* check permissions */
 		if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0
-		    || no_port_forwarding_flag || options.disable_forwarding ||
+		    || !auth_opts->permit_port_forwarding_flag ||
+		    options.disable_forwarding ||
 		    (pw->pw_uid != 0 && !use_privsep)) {
 			success = 0;
 			packet_send_debug("Server has disabled "
diff --git a/session.c b/session.c
index 51c5ea0..58826db 100644
--- a/session.c
+++ b/session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: session.c,v 1.293 2017/10/23 05:08:00 djm Exp $ */
+/* $OpenBSD: session.c,v 1.294 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -140,6 +140,7 @@
 extern int startup_pipe;
 extern void destroy_sensitive_data(void);
 extern Buffer loginmsg;
+extern struct sshauthopt *auth_opts;
 char *tun_fwd_ifnames; /* serverloop.c */
 
 /* original command from peer. */
@@ -288,14 +289,42 @@
 	restore_uid();
 }
 
+static void
+set_permitopen_from_authopts(struct ssh *ssh, const struct sshauthopt *opts)
+{
+	char *tmp, *cp, *host;
+	int port;
+	size_t i;
+
+	if ((options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
+		return;
+	channel_clear_permitted_opens(ssh);
+	for (i = 0; i < auth_opts->npermitopen; i++) {
+		tmp = cp = xstrdup(auth_opts->permitopen[i]);
+		/* This shouldn't fail as it has already been checked */
+		if ((host = hpdelim(&cp)) == NULL)
+			fatal("%s: internal error: hpdelim", __func__);
+		host = cleanhostname(host);
+		if (cp == NULL || (port = permitopen_port(cp)) < 0)
+			fatal("%s: internal error: permitopen port",
+			    __func__);
+		channel_add_permitted_opens(ssh, host, port);
+		free(tmp);
+	}
+}
+
 void
 do_authenticated(struct ssh *ssh, Authctxt *authctxt)
 {
 	setproctitle("%s", authctxt->pw->pw_name);
 
+	auth_log_authopts("active", auth_opts, 0);
+
 	/* setup the channel layer */
 	/* XXX - streamlocal? */
-	if (no_port_forwarding_flag || options.disable_forwarding ||
+	set_permitopen_from_authopts(ssh, auth_opts);
+	if (!auth_opts->permit_port_forwarding_flag ||
+	    options.disable_forwarding ||
 	    (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0)
 		channel_disable_adm_local_opens(ssh);
 	else
@@ -642,9 +671,9 @@
 		original_command = command;
 		command = options.adm_forced_command;
 		forced = "(config)";
-	} else if (forced_command) {
+	} else if (auth_opts->force_command != NULL) {
 		original_command = command;
-		command = forced_command;
+		command = auth_opts->force_command;
 		forced = "(key-option)";
 	}
 	if (forced != NULL) {
@@ -947,8 +976,9 @@
 do_setup_env(struct ssh *ssh, Session *s, const char *shell)
 {
 	char buf[256];
+	size_t n;
 	u_int i, envsize;
-	char **env, *laddr;
+	char *ocp, *cp, **env, *laddr;
 	struct passwd *pw = s->pw;
 #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN)
 	char *path = NULL;
@@ -1023,20 +1053,17 @@
 	if (getenv("TZ"))
 		child_set_env(&env, &envsize, "TZ", getenv("TZ"));
 
-	/* Set custom environment options from RSA authentication. */
-	while (custom_environment) {
-		struct envstring *ce = custom_environment;
-		char *str = ce->s;
-
-		for (i = 0; str[i] != '=' && str[i]; i++)
-			;
-		if (str[i] == '=') {
-			str[i] = 0;
-			child_set_env(&env, &envsize, str, str + i + 1);
+	/* Set custom environment options from pubkey authentication. */
+	if (options.permit_user_env) {
+		for (n = 0 ; n < auth_opts->nenv; n++) {
+			ocp = xstrdup(auth_opts->env[n]);
+			cp = strchr(ocp, '=');
+			if (*cp == '=') {
+				*cp = '\0';
+				child_set_env(&env, &envsize, ocp, cp + 1);
+			}
+			free(ocp);
 		}
-		custom_environment = ce->next;
-		free(ce->s);
-		free(ce);
 	}
 
 	/* SSH_CLIENT deprecated */
@@ -1138,7 +1165,7 @@
  * first in this order).
  */
 static void
-do_rc_files(Session *s, const char *shell)
+do_rc_files(struct ssh *ssh, Session *s, const char *shell)
 {
 	FILE *f = NULL;
 	char cmd[1024];
@@ -1150,7 +1177,7 @@
 
 	/* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */
 	if (!s->is_subsystem && options.adm_forced_command == NULL &&
-	    !no_user_rc && options.permit_user_rc &&
+	    auth_opts->permit_user_rc && options.permit_user_rc &&
 	    stat(_PATH_SSH_USER_RC, &st) >= 0) {
 		snprintf(cmd, sizeof cmd, "%s -c '%s %s'",
 		    shell, _PATH_BSHELL, _PATH_SSH_USER_RC);
@@ -1570,7 +1597,7 @@
 
 	closefrom(STDERR_FILENO + 1);
 
-	do_rc_files(s, shell);
+	do_rc_files(ssh, s, shell);
 
 	/* restore SIGPIPE for child */
 	signal(SIGPIPE, SIG_DFL);
@@ -1833,8 +1860,8 @@
 	u_int len;
 	int n_bytes;
 
-	if (no_pty_flag || !options.permit_tty) {
-		debug("Allocating a pty not permitted for this authentication.");
+	if (!auth_opts->permit_pty_flag || !options.permit_tty) {
+		debug("Allocating a pty not permitted for this connection.");
 		return 0;
 	}
 	if (s->ttyfd != -1) {
@@ -2022,9 +2049,11 @@
 session_auth_agent_req(struct ssh *ssh, Session *s)
 {
 	static int called = 0;
+
 	packet_check_eom();
-	if (no_agent_forwarding_flag || !options.allow_agent_forwarding) {
-		debug("session_auth_agent_req: no_agent_forwarding_flag");
+	if (!auth_opts->permit_agent_forwarding_flag ||
+	    !options.allow_agent_forwarding) {
+		debug("%s: agent forwarding disabled", __func__);
 		return 0;
 	}
 	if (called) {
@@ -2402,8 +2431,8 @@
 	char hostname[NI_MAXHOST];
 	u_int i;
 
-	if (no_x11_forwarding_flag) {
-		packet_send_debug("X11 forwarding disabled in user configuration file.");
+	if (!auth_opts->permit_x11_forwarding_flag) {
+		packet_send_debug("X11 forwarding disabled by key options.");
 		return 0;
 	}
 	if (!options.x11_forwarding) {
@@ -2412,7 +2441,7 @@
 	}
 	if (options.xauth_location == NULL ||
 	    (stat(options.xauth_location, &st) == -1)) {
-		packet_send_debug("No xauth program; cannot forward with spoofing.");
+		packet_send_debug("No xauth program; cannot forward X11.");
 		return 0;
 	}
 	if (s->display != NULL) {
diff --git a/sshd.c b/sshd.c
index 0b9a7ec..fd95b68 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.505 2018/02/23 15:58:38 markus Exp $ */
+/* $OpenBSD: sshd.c,v 1.506 2018/03/03 03:15:51 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -119,6 +119,7 @@
 #endif
 #include "monitor_wrap.h"
 #include "ssh-sandbox.h"
+#include "auth-options.h"
 #include "version.h"
 #include "ssherr.h"
 
@@ -232,6 +233,9 @@
 /* global authentication context */
 Authctxt *the_authctxt = NULL;
 
+/* global key/cert auth options. XXX move to permanent ssh->authctxt? */
+struct sshauthopt *auth_opts = NULL;
+
 /* sshd_config buffer */
 Buffer cfg;
 
@@ -2066,6 +2070,10 @@
 	/* XXX global for cleanup, access from other modules */
 	the_authctxt = authctxt;
 
+	/* Set default key authentication options */
+	if ((auth_opts = sshauthopt_new_with_keys_defaults()) == NULL)
+		fatal("allocation failed");
+
 	/* prepare buffer to collect messages to display to user after login */
 	buffer_init(&loginmsg);
 	auth_debug_reset();
@@ -2122,7 +2130,7 @@
 #ifdef USE_PAM
 	if (options.use_pam) {
 		do_pam_setcred(1);
-		do_pam_session();
+		do_pam_session(ssh);
 	}
 #endif