From 0c6d04259ac11abe30cbdbbbcbc2e3943e3be251 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Tue, 16 Mar 2021 08:15:25 -0400
Subject: [PATCH] Add support for the SSH "none" auth method.

This is used by some BBSs to enable encryption without needing to
integrate the BBS user base into their SSH server (and presumably
so they don't need to run multiple SSH servers).  All users log in
with the same username (ie: "bbs") and no password is requested or
required.

Once the BBS starts, it prompts for the BBS user name and password
as normal.

In SyncTERM, the user/password/syspass fields are redefined as
SSHuser/BBSuser/BBSpassword and they are moved around when you
change the connection type.  This means that if you change a listing
that has a syspass to SSH (no auth) and back, the syspass is lost.

I'm not sure if I plan to fix this or not.
---
 3rdp/build/GNUmakefile              |   3 +-
 3rdp/build/cl-allow-none-auth.patch | 158 ++++++++++++++++++++++++++++
 src/syncterm/bbslist.c              |  68 +++++++++---
 src/syncterm/conn.c                 |   7 +-
 src/syncterm/conn.h                 |   1 +
 src/syncterm/ssh.c                  |  49 ++++++---
 src/syncterm/st_crypt.c             |   5 +
 src/syncterm/st_crypt.h             |   2 +
 src/syncterm/term.c                 |  10 +-
 9 files changed, 263 insertions(+), 40 deletions(-)
 create mode 100644 3rdp/build/cl-allow-none-auth.patch

diff --git a/3rdp/build/GNUmakefile b/3rdp/build/GNUmakefile
index 8e615d93a1..8f21d156b1 100644
--- a/3rdp/build/GNUmakefile
+++ b/3rdp/build/GNUmakefile
@@ -81,7 +81,7 @@ $(CRYPT_SRC): | $(3RDPSRCDIR)
 $(CRYPT_IDIR): | $(3RDPODIR)
 	$(QUIET)$(IFNOTEXIST) mkdir $(CRYPT_IDIR)
 
-$(CRYPTLIB_BUILD): $(3RDP_ROOT)$(DIRSEP)dist/cryptlib.zip $(3RDP_ROOT)$(DIRSEP)build/terminal-params.patch $(3RDP_ROOT)$(DIRSEP)build/cl-mingw32-static.patch $(3RDP_ROOT)$(DIRSEP)build/cl-ranlib.patch $(3RDP_ROOT)$(DIRSEP)build/cl-win32-noasm.patch $(3RDP_ROOT)$(DIRSEP)build/cl-zz-country.patch $(3RDP_ROOT)$(DIRSEP)build/cl-algorithms.patch $(3RDP_ROOT)$(DIRSEP)build/cl-allow-duplicate-ext.patch $(3RDP_ROOT)$(DIRSEP)build/cl-macosx-minver.patch $(3RDP_ROOT)$(DIRSEP)build/cl-endian.patch $(3RDP_ROOT)$(DIRSEP)build/cl-cryptodev.patch $(3RDP_ROOT)$(DIRSEP)build/cl-posix-me-gently.patch $(3RDP_ROOT)$(DIRSEP)build/cl-tpm-linux.patch $(3RDP_ROOT)$(DIRSEP)build/cl-PAM-noprompts.patch $(3RDP_ROOT)$(DIRSEP)build/cl-zlib.patch $(3RDP_ROOT)$(DIRSEP)build/Dynamic-linked-static-lib.patch $(3RDP_ROOT)$(DIRSEP)build/SSL-fix.patch $(3RDP_ROOT)$(DIRSEP)build/cl-bigger-maxattribute.patch $(3RDP_ROOT)$(DIRSEP)build/cl-vcxproj.patch $(3RDP_ROOT)$(DIRSEP)build/cl-mingw-vcver.patch $(3RDP_ROOT)$(DIRSEP)build/cl-win32-build-fix.patch $(3RDP_ROOT)$(DIRSEP)build/cl-gcc-non-const-time-val.patch $(3RDP_ROOT)$(DIRSEP)build/cl-no-odbc.patch $(3RDP_ROOT)$(DIRSEP)build/cl-noasm-defines.patch $(3RDP_ROOT)$(DIRSEP)build/cl-bn-noasm64-fix.patch $(3RDP_ROOT)$(DIRSEP)build/cl-no-RSA-suites.patch $(3RDP_ROOT)$(DIRSEP)build/cl-fix-ECC-RSA.patch $(3RDP_ROOT)$(DIRSEP)build/cl-prefer-ECC.patch $(3RDP_ROOT)$(DIRSEP)build/cl-prefer-ECC-harder.patch $(3RDP_ROOT)$(DIRSEP)build/cl-more-RSA-ECC-fixes.patch $(3RDP_ROOT)$(DIRSEP)build/cl-DH-key-init.patch $(3RDP_ROOT)$(DIRSEP)build/cl-clear-GCM-flag.patch $(3RDP_ROOT)$(DIRSEP)build/cl-use-ssh-ctr.patch $(3RDP_ROOT)$(DIRSEP)build/cl-ssh-list-ctr-modes.patch $(3RDP_ROOT)$(DIRSEP)build/cl-ssh-incCtr.patch $(3RDP_ROOT)$(DIRSEP)build/cl-ssl-suite-blocksizes.patch $(3RDP_ROOT)$(DIRSEP)build/cl-no-tpm.patch $(3RDP_ROOT)$(DIRSEP)build/cl-no-via-aes.patch $(3RDP_ROOT)$(DIRSEP)build/cl-fix-ssh-ecc-ephemeral.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-just-use-cc.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-learn-numbers.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-no-safe-stack.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-allow-pkcs12.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-uint64_t-redefine.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-random-openbsd.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-openbsd-threads.patch | $(CRYPT_SRC) $(CRYPT_IDIR)
+$(CRYPTLIB_BUILD): $(3RDP_ROOT)$(DIRSEP)dist/cryptlib.zip $(3RDP_ROOT)$(DIRSEP)build/terminal-params.patch $(3RDP_ROOT)$(DIRSEP)build/cl-mingw32-static.patch $(3RDP_ROOT)$(DIRSEP)build/cl-ranlib.patch $(3RDP_ROOT)$(DIRSEP)build/cl-win32-noasm.patch $(3RDP_ROOT)$(DIRSEP)build/cl-zz-country.patch $(3RDP_ROOT)$(DIRSEP)build/cl-algorithms.patch $(3RDP_ROOT)$(DIRSEP)build/cl-allow-duplicate-ext.patch $(3RDP_ROOT)$(DIRSEP)build/cl-macosx-minver.patch $(3RDP_ROOT)$(DIRSEP)build/cl-endian.patch $(3RDP_ROOT)$(DIRSEP)build/cl-cryptodev.patch $(3RDP_ROOT)$(DIRSEP)build/cl-posix-me-gently.patch $(3RDP_ROOT)$(DIRSEP)build/cl-tpm-linux.patch $(3RDP_ROOT)$(DIRSEP)build/cl-PAM-noprompts.patch $(3RDP_ROOT)$(DIRSEP)build/cl-zlib.patch $(3RDP_ROOT)$(DIRSEP)build/Dynamic-linked-static-lib.patch $(3RDP_ROOT)$(DIRSEP)build/SSL-fix.patch $(3RDP_ROOT)$(DIRSEP)build/cl-bigger-maxattribute.patch $(3RDP_ROOT)$(DIRSEP)build/cl-vcxproj.patch $(3RDP_ROOT)$(DIRSEP)build/cl-mingw-vcver.patch $(3RDP_ROOT)$(DIRSEP)build/cl-win32-build-fix.patch $(3RDP_ROOT)$(DIRSEP)build/cl-gcc-non-const-time-val.patch $(3RDP_ROOT)$(DIRSEP)build/cl-no-odbc.patch $(3RDP_ROOT)$(DIRSEP)build/cl-noasm-defines.patch $(3RDP_ROOT)$(DIRSEP)build/cl-bn-noasm64-fix.patch $(3RDP_ROOT)$(DIRSEP)build/cl-no-RSA-suites.patch $(3RDP_ROOT)$(DIRSEP)build/cl-fix-ECC-RSA.patch $(3RDP_ROOT)$(DIRSEP)build/cl-prefer-ECC.patch $(3RDP_ROOT)$(DIRSEP)build/cl-prefer-ECC-harder.patch $(3RDP_ROOT)$(DIRSEP)build/cl-more-RSA-ECC-fixes.patch $(3RDP_ROOT)$(DIRSEP)build/cl-DH-key-init.patch $(3RDP_ROOT)$(DIRSEP)build/cl-clear-GCM-flag.patch $(3RDP_ROOT)$(DIRSEP)build/cl-use-ssh-ctr.patch $(3RDP_ROOT)$(DIRSEP)build/cl-ssh-list-ctr-modes.patch $(3RDP_ROOT)$(DIRSEP)build/cl-ssh-incCtr.patch $(3RDP_ROOT)$(DIRSEP)build/cl-ssl-suite-blocksizes.patch $(3RDP_ROOT)$(DIRSEP)build/cl-no-tpm.patch $(3RDP_ROOT)$(DIRSEP)build/cl-no-via-aes.patch $(3RDP_ROOT)$(DIRSEP)build/cl-fix-ssh-ecc-ephemeral.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-just-use-cc.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-learn-numbers.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-no-safe-stack.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-allow-pkcs12.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-uint64_t-redefine.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-random-openbsd.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-openbsd-threads.patch $(3RDP_ROOT)$(DIRSEP)/build/cl-allow-none-auth.patch | $(CRYPT_SRC) $(CRYPT_IDIR)
 	@echo Creating $@ ...
 	$(QUIET)-rm -rf $(CRYPT_SRC)/*
 	$(QUIET)unzip -oa $(3RDPDISTDIR)$(DIRSEP)cryptlib.zip -d $(CRYPT_SRC)
@@ -131,6 +131,7 @@ $(CRYPTLIB_BUILD): $(3RDP_ROOT)$(DIRSEP)dist/cryptlib.zip $(3RDP_ROOT)$(DIRSEP)b
 	$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-uint64_t-redefine.patch
 	$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-random-openbsd.patch
 	$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-openbsd-threads.patch
+	$(QUIET)patch -b -p0 -d $(CRYPT_SRC) < cl-allow-none-auth.patch
 ifeq ($(CC),mingw32-gcc)
 	$(QUIET)cd $(CRYPT_SRC) && env - PATH="$(PATH)" CC="$(CC)" AR="$(AR)" RANLIB="$(RANLIB)" make directories
 	$(QUIET)cd $(CRYPT_SRC) && env - PATH="$(PATH)" CC="$(CC)" AR="$(AR)" RANLIB="$(RANLIB)" make toolscripts
diff --git a/3rdp/build/cl-allow-none-auth.patch b/3rdp/build/cl-allow-none-auth.patch
new file mode 100644
index 0000000000..95ab0fd698
--- /dev/null
+++ b/3rdp/build/cl-allow-none-auth.patch
@@ -0,0 +1,158 @@
+--- ../cl-old/cryptlib.h	2021-03-16 04:15:50.265534000 -0400
++++ ./cryptlib.h	2021-03-16 06:53:47.582168000 -0400
+@@ -1215,6 +1215,7 @@
+ 	CRYPT_SESSINFO_SSH_CHANNEL_ARG1,/* SSH channel argument 1 */
+ 	CRYPT_SESSINFO_SSH_CHANNEL_ARG2,/* SSH channel argument 2 */
+ 	CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE,/* SSH channel active */
++	CRYPT_SESSINFO_SSH_OPTIONS,		/* SSH protocol options */
+ 	CRYPT_SESSINFO_SSL_OPTIONS,		/* SSL/TLS protocol options */
+ 	CRYPT_SESSINFO_SSL_SUBPROTOCOL,	/* SSL/TLS additional sub-protocol */
+ 	CRYPT_SESSINFO_SSL_WSPROTOCOL,	/* SSL/TLS WebSockets sub-protocol */
+@@ -1680,6 +1681,14 @@
+ #define CRYPT_SSLOPTION_SUITEB_256			0x200	/*  vanish in future releases) */
+ #ifdef _CRYPT_DEFINED
+ #define CRYPT_SSLOPTION_MAX					0x7F	/* Defines for range checking */
++#endif /* _CRYPT_DEFINED */
++
++/* SSH protocol options. */
++
++#define CRYPT_SSHOPTION_NONE				0x000
++#define CRYPT_SSHOPTION_NONE_AUTH			0x001	/* Try none authentication */
++#ifdef _CRYPT_DEFINED
++#define CRYPT_SSHOPTION_MAX					0x01	/* Defines for range checking */
+ #endif /* _CRYPT_DEFINED */
+ 
+ /****************************************************************************
+diff -ur ../cl-old/kernel/attr_acl.c ./kernel/attr_acl.c
+--- ../cl-old/kernel/attr_acl.c	2021-03-16 04:15:50.398060000 -0400
++++ ./kernel/attr_acl.c	2021-03-16 06:53:47.606473000 -0400
+@@ -3731,6 +3731,12 @@
+ 		ST_NONE, ST_NONE, ST_SESS_SSH | ST_SESS_SSH_SVR, 
+ 		MKPERM_SSH_EXT( RWx_xxx ),
+ 		ROUTE( OBJECT_TYPE_SESSION ) ),
++	MKACL_N(	/* SSH protocol options */
++		CRYPT_SESSINFO_SSH_OPTIONS,
++		ST_NONE, ST_NONE, ST_SESS_SSH, 
++		MKPERM_SSH_EXT( Rxx_RWx ),
++		ROUTE( OBJECT_TYPE_SESSION ),
++		RANGE( CRYPT_SSHOPTION_NONE, CRYPT_SSHOPTION_MAX ) ),
+ 
+ 	MKACL_N(	/* SSL/TLS protocol options */
+ 		CRYPT_SESSINFO_SSL_OPTIONS,
+@@ -4653,7 +4659,7 @@
+ 	static_assert( CRYPT_CERTINFO_FIRST_EXTENSION == 2200, "Attribute value" );
+ 	static_assert( CRYPT_CERTINFO_FIRST_CMS == 2500, "Attribute value" );
+ 	static_assert( CRYPT_SESSINFO_FIRST_SPECIFIC == 6016, "Attribute value" );
+-	static_assert( CRYPT_SESSINFO_LAST_SPECIFIC == 6031, "Attribute value" );
++	static_assert( CRYPT_SESSINFO_LAST_SPECIFIC == 6032, "Attribute value" );
+ 	static_assert( CRYPT_CERTFORMAT_LAST == 12, "Attribute value" );
+ 
+ 	/* Perform a consistency check on the attribute ACLs.  The ACLs are
+diff -ur ../cl-old/session/ssh.c ./session/ssh.c
+--- ../cl-old/session/ssh.c	2021-03-16 04:15:50.257467000 -0400
++++ ./session/ssh.c	2021-03-16 06:53:47.638940000 -0400
+@@ -980,6 +980,18 @@
+ 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ARG2 || \
+ 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE );
+ 
++	if( type == CRYPT_SESSINFO_SSH_OPTIONS )
++		{
++			int *valuePtr = ( int * ) data;
++
++			*valuePtr = CRYPT_SSHOPTION_NONE;
++			if( TEST_FLAG( sessionInfoPtr->protocolFlags, 
++						   SSH_PFLAG_DUMMYUSERAUTH ) )
++				*valuePtr |= CRYPT_SSHOPTION_NONE_AUTH;
++
++			return( CRYPT_OK );
++		}
++
+ 	if( type == CRYPT_SESSINFO_SSH_CHANNEL || \
+ 		type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE )
+ 		{
+@@ -1010,11 +1022,13 @@
+ 			  type == CRYPT_SESSINFO_SSH_CHANNEL_TYPE || \
+ 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ARG1 || \
+ 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ARG2 || \
++			  type == CRYPT_SESSINFO_SSH_OPTIONS || \
+ 			  type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE );
+ 
+ 	/* Get the data value if it's an integer parameter */
+ 	if( type == CRYPT_SESSINFO_SSH_CHANNEL || \
+-		type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE )
++		type == CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE || \
++		type == CRYPT_SESSINFO_SSH_OPTIONS)
+ 		value = *( ( int * ) data );
+ 
+ 	/* If we're selecting a channel and there's unwritten data from a
+@@ -1038,6 +1052,13 @@
+ 		return( createChannel( sessionInfoPtr ) );
+ 		}
+ 
++	if( type == CRYPT_SESSINFO_SSH_OPTIONS )
++		{
++		if (value & CRYPT_SSHOPTION_NONE_AUTH)
++			SET_FLAG( sessionInfoPtr->protocolFlags, SSH_PFLAG_DUMMYUSERAUTH );
++		return( CRYPT_OK );
++		}
++
+ 	/* If we 're setting the channel-active attribute, this implicitly
+ 	   activates or deactivates the channel rather than setting any
+ 	   attribute value */
+@@ -1165,8 +1186,6 @@
+ 		 SESSION_FLAG_NONE,			/* Flags */
+ 		SSH_PORT,					/* SSH port */
+ 		SESSION_NEEDS_USERID |		/* Client attributes */
+-			SESSION_NEEDS_PASSWORD | \
+-			SESSION_NEEDS_KEYORPASSWORD | \
+ 			SESSION_NEEDS_PRIVKEYSIGN,
+ 				/* The client private key is optional, but if present it has
+ 				   to be signature-capable */
+diff -ur ../cl-old/session/ssh2_authc.c ./session/ssh2_authc.c
+--- ../cl-old/session/ssh2_authc.c	2021-03-16 04:15:50.264206000 -0400
++++ ./session/ssh2_authc.c	2021-03-16 07:46:47.873769000 -0400
+@@ -315,13 +315,22 @@
+ 	assert( isWritePtr( authAlgo, sizeof( CRYPT_ALGO_TYPE ) ) );
+ 	assert( isWritePtr( furtherAuthRequired, sizeof( BOOLEAN ) ) );
+ 
+-	REQUIRES( isShortIntegerRangeNZ( length ) );
+ 	REQUIRES( usedPasswordAuth == TRUE || usedPasswordAuth == FALSE );
+ 
+ 	/* Clear return values */
+ 	*authAlgo = CRYPT_ALGO_NONE;
+ 	*furtherAuthRequired = FALSE;
+ 
++	if (length == 0 && GET_FLAG( sessionInfoPtr->protocolFlags, SSH_PFLAG_DUMMYUSERAUTH ) && !usedPasswordAuth)
++		{
++		CLEAR_FLAG( sessionInfoPtr->protocolFlags, SSH_PFLAG_DUMMYUSERAUTH );
++		*furtherAuthRequired = TRUE;
++		*authAlgo = CRYPT_PSEUDOALGO_PASSWORD;
++		return( CRYPT_OK );
++		}
++
++	REQUIRES( isShortIntegerRangeNZ( length ) );
++
+ 	/* Before we can try and interpret the response, we have to check for an
+ 	   empty response */
+ 	if( length >= LENGTH_SIZE && \
+@@ -647,6 +656,8 @@
+ 								  SSH_MSG_USERAUTH_REQUEST );
+ 	if( cryptStatusError( status ) )
+ 		return( status );
++	if (passwordPtr == NULL && GET_FLAG( sessionInfoPtr->protocolFlags, SSH_PFLAG_DUMMYUSERAUTH ))
++		return( OK_SPECIAL );
+ 	if( usePasswordAuth )
+ 		{
+ 		/*	byte	type = SSH_MSG_USERAUTH_REQUEST
+@@ -1220,6 +1231,11 @@
+ 	   auth required */
+ 	if( !hasPassword )
+ 		{
++		if (length == 0)
++			{
++			return( reportAuthFailure( sessionInfoPtr, CRYPT_PSEUDOALGO_PASSWORD, 
++									   requiredAuthAlgo, TRUE ) );
++			}
+ 		return( reportAuthFailure( sessionInfoPtr, CRYPT_ALGO_RSA, 
+ 								   requiredAuthAlgo, TRUE ) );
+ 		}
diff --git a/src/syncterm/bbslist.c b/src/syncterm/bbslist.c
index 17695425cb..4fe5b12f30 100644
--- a/src/syncterm/bbslist.c
+++ b/src/syncterm/bbslist.c
@@ -966,10 +966,18 @@ int edit_list(struct bbslist **list, struct bbslist *item,char *listpath,int isd
             fc_str(opt[i++], item->flow_control);
         else if (item->conn_type != CONN_TYPE_SHELL)
             sprintf(opt[i++], "TCP Port          %hu",item->port);
-        printf_trunc(opt[i], sizeof(opt[i]), "Username          %s",item->user);
-	i++;
-        sprintf(opt[i++], "Password          %s",item->password[0]?"********":"<none>");
-        sprintf(opt[i++], "System Password   %s",item->syspass[0]?"********":"<none>");
+        if (item->conn_type == CONN_TYPE_SSHNA) {
+		printf_trunc(opt[i], sizeof(opt[i]), "SSH Username      %s",item->user);
+		i++;
+		sprintf(opt[i++], "BBS Username      %s",item->password);
+		sprintf(opt[i++], "BBS Password      %s",item->syspass[0]?"********":"<none>");
+	}
+	else {
+		printf_trunc(opt[i], sizeof(opt[i]), "Username          %s",item->user);
+		i++;
+		sprintf(opt[i++], "Password          %s",item->password[0]?"********":"<none>");
+		sprintf(opt[i++], "System Password   %s",item->syspass[0]?"********":"<none>");
+	}
         sprintf(opt[i++], "Screen Mode       %s",screen_modes[item->screen_mode]);
         sprintf(opt[i++], "Hide Status Line  %s",item->nostatus?"Yes":"No");
         printf_trunc(opt[i], sizeof(opt[i]), "Download Path     %s", item->dldir);
@@ -1179,27 +1187,45 @@ int edit_list(struct bbslist **list, struct bbslist *item,char *listpath,int isd
                 }
                 break;
             case 4:
-                uifc.helpbuf=   "`Username`\n\n"
-                                "Enter the username to attempt auto-login to the remote with.\n"
-                                "For SSH, this must be the SSH user name.";
+		if (item->conn_type == CONN_TYPE_SSHNA) {
+			uifc.helpbuf=   "`SSH Username`\n\n"
+					"Enter the username for passwordless SSH authentication.";
+		}
+		else {
+			uifc.helpbuf=   "`Username`\n\n"
+					"Enter the username to attempt auto-login to the remote with.\n"
+					"For SSH, this must be the SSH user name.";
+		}
                 uifc.input(WIN_MID|WIN_SAV,0,0,"Username",item->user,MAX_USER_LEN,K_EDIT);
                 check_exit(FALSE);
                 iniSetString(&inifile,itemname,"UserName",item->user,&ini_style);
                 break;
             case 5:
-                uifc.helpbuf=   "`Password`\n\n"
-                                "Enter your password for auto-login.\n"
-                                "For SSH, this must be the SSH password if it exists.\n";
+		if (item->conn_type == CONN_TYPE_SSHNA) {
+			uifc.helpbuf=   "`BBS Username`\n\n"
+					"Enter the username to be sent for auto-login (ALT-L).";
+		}
+		else {
+			uifc.helpbuf=   "`Password`\n\n"
+					"Enter your password for auto-login.\n"
+					"For SSH, this must be the SSH password if it exists.\n";
+		}
                 uifc.input(WIN_MID|WIN_SAV,0,0,"Password",item->password,MAX_PASSWD_LEN,K_EDIT);
                 check_exit(FALSE);
                 iniSetString(&inifile,itemname,"Password",item->password,&ini_style);
                 break;
             case 6:
-                uifc.helpbuf=   "`System Password`\n\n"
-                                "Enter your System password for auto-login.\n"
-                                "This password is sent after the username and password, so for non-\n"
-                                "Synchronet, or non-sysop accounts, this can be used for simple\n"
-                                "scripting.";
+		if (item->conn_type == CONN_TYPE_SSHNA) {
+			uifc.helpbuf=   "`BBS Password`\n\n"
+					"Enter your password for auto-login. (ALT-L)\n";
+		}
+		else {
+			uifc.helpbuf=   "`System Password`\n\n"
+					"Enter your System password for auto-login.\n"
+					"This password is sent after the username and password, so for non-\n"
+					"Synchronet, or non-sysop accounts, this can be used for simple\n"
+					"scripting.";
+		}
                 uifc.input(WIN_MID|WIN_SAV,0,0,"System Password",item->syspass,MAX_SYSPASS_LEN,K_EDIT);
                 check_exit(FALSE);
                 iniSetString(&inifile,itemname,"SystemPassword",item->syspass,&ini_style);
@@ -1217,6 +1243,18 @@ int edit_list(struct bbslist **list, struct bbslist *item,char *listpath,int isd
                         item->conn_type++;
                         iniSetEnum(&inifile,itemname,"ConnectionType",conn_types_enum,item->conn_type,&ini_style);
 
+			// TODO: NOTE: This is destructive!  Beware!  Ooooooo....
+			if (i == CONN_TYPE_SSHNA && item->conn_type != CONN_TYPE_SSHNA) {
+				SAFECOPY(item->user, item->password);
+				SAFECOPY(item->password, item->syspass);
+				item->syspass[0] = 0;
+			}
+			if (i != CONN_TYPE_SSHNA && item->conn_type == CONN_TYPE_SSHNA) {
+				SAFECOPY(item->syspass, item->password);
+				SAFECOPY(item->password, item->user);
+				item->user[0] = 0;
+			}
+
                         if(item->conn_type!=CONN_TYPE_MODEM && item->conn_type!=CONN_TYPE_SERIAL
                                 && item->conn_type!=CONN_TYPE_SHELL
                                 ) {
diff --git a/src/syncterm/conn.c b/src/syncterm/conn.c
index 6a04211ebd..572dab1c3e 100644
--- a/src/syncterm/conn.c
+++ b/src/syncterm/conn.c
@@ -45,9 +45,9 @@
 #include "conn_telnet.h"
 
 struct conn_api conn_api;
-char *conn_types_enum[]={"Unknown","RLogin","RLoginReversed","Telnet","Raw","SSH","Modem","Serial","Shell","MBBSGhost","TelnetS", NULL};
-char *conn_types[]={"Unknown","RLogin","RLogin Reversed","Telnet","Raw","SSH","Modem","Serial","Shell","MBBS GHost","TelnetS",NULL};
-short unsigned int conn_ports[]={0,513,513,23,0,22,0,0,0,65535,992,0};
+char *conn_types_enum[]={"Unknown","RLogin","RLoginReversed","Telnet","Raw","SSH","SSHNA","Modem","Serial","Shell","MBBSGhost","TelnetS", NULL};
+char *conn_types[]={"Unknown","RLogin","RLogin Reversed","Telnet","Raw","SSH","SSH (no auth)","Modem","Serial","Shell","MBBS GHost","TelnetS",NULL};
+short unsigned int conn_ports[]={0,513,513,23,0,22,22,0,0,0,65535,992,0};
 
 struct conn_buffer conn_inbuf;
 struct conn_buffer conn_outbuf;
@@ -367,6 +367,7 @@ int conn_connect(struct bbslist *bbs)
 			conn_api.binary_mode_on=telnet_binary_mode_on;
 			conn_api.binary_mode_off=telnet_binary_mode_off;
 			break;
+		case CONN_TYPE_SSHNA:
 		case CONN_TYPE_SSH:
 			conn_api.connect=ssh_connect;
 			conn_api.close=ssh_close;
diff --git a/src/syncterm/conn.h b/src/syncterm/conn.h
index 0356e15fa7..2178e3708f 100644
--- a/src/syncterm/conn.h
+++ b/src/syncterm/conn.h
@@ -21,6 +21,7 @@ enum {
 	,CONN_TYPE_TELNET
 	,CONN_TYPE_RAW
 	,CONN_TYPE_SSH
+	,CONN_TYPE_SSHNA
 	,CONN_TYPE_MODEM
 	,CONN_TYPE_SERIAL
 	,CONN_TYPE_SHELL
diff --git a/src/syncterm/ssh.c b/src/syncterm/ssh.c
index ad9ad0bb44..1dcaae8b8e 100644
--- a/src/syncterm/ssh.c
+++ b/src/syncterm/ssh.c
@@ -229,26 +229,41 @@ int ssh_connect(struct bbslist *bbs)
 
 	if (!bbs->hidepopups)
 		uifc.pop(NULL);
-	if(!password[0]) {
-		if (bbs->hidepopups)
-			init_uifc(FALSE, FALSE);
-		uifcinput("Password",MAX_PASSWD_LEN,password,K_PASSWORD,"Incorrect password.  Try again.");
-		if (bbs->hidepopups)
-			uifcbail();
+	if (bbs->conn_type == CONN_TYPE_SSHNA) {
+		status = cl.SetAttribute(ssh_session, CRYPT_SESSINFO_SSH_OPTIONS, CRYPT_SSHOPTION_NONE_AUTH);
+		if(cryptStatusError(status)) {
+			char	str[1024];
+			sprintf(str,"Error %d disabling password auth",status);
+			if (!bbs->hidepopups)
+				uifcmsg("Error disabling password auth",str);
+			conn_api.terminate=1;
+			if (!bbs->hidepopups)
+				uifc.pop(NULL);
+			return(-1);
+		}
 	}
+	else {
+		if(!password[0]) {
+			if (bbs->hidepopups)
+				init_uifc(FALSE, FALSE);
+			uifcinput("Password",MAX_PASSWD_LEN,password,K_PASSWORD,"Incorrect password.  Try again.");
+			if (bbs->hidepopups)
+				uifcbail();
+		}
 
-	if (!bbs->hidepopups)
-		uifc.pop("Setting Password");
-	status=cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_PASSWORD, password, strlen(password));
-	if(cryptStatusError(status)) {
-		char	str[1024];
-		sprintf(str,"Error %d setting password",status);
-		if (!bbs->hidepopups)
-			uifcmsg("Error setting password",str);
-		conn_api.terminate=1;
 		if (!bbs->hidepopups)
-			uifc.pop(NULL);
-		return(-1);
+			uifc.pop("Setting Password");
+		status=cl.SetAttributeString(ssh_session, CRYPT_SESSINFO_PASSWORD, password, strlen(password));
+		if(cryptStatusError(status)) {
+			char	str[1024];
+			sprintf(str,"Error %d setting password",status);
+			if (!bbs->hidepopups)
+				uifcmsg("Error setting password",str);
+			conn_api.terminate=1;
+			if (!bbs->hidepopups)
+				uifc.pop(NULL);
+			return(-1);
+		}
 	}
 
 	if (!bbs->hidepopups) {
diff --git a/src/syncterm/st_crypt.c b/src/syncterm/st_crypt.c
index 90cdc7f634..e46c51373b 100644
--- a/src/syncterm/st_crypt.c
+++ b/src/syncterm/st_crypt.c
@@ -49,6 +49,7 @@ int init_crypt(void)
 	cl.SetAttributeString=cryptSetAttributeString;
 	cl.DestroySession=cryptDestroySession;
 	cl.AddRandom=cryptAddRandom;
+	cl.DeleteAttribute=cryptDeleteAttribute;
 #else
 	cryptlib=xp_dlopen(libnames,RTLD_LAZY, CRYPTLIB_VERSION/1000);
 	if(cryptlib==NULL)
@@ -101,6 +102,10 @@ int init_crypt(void)
 		xp_dlclose(cryptlib);
 		return(-1);
 	}
+	if((cl.DeleteAttribute=xp_dlsym(cryptlib,cryptDeleteAttribute))==NULL) {
+		xp_dlclose(cryptlib);
+		return(-1);
+	}
 #endif
 	if(cryptStatusOK(cl.Init())) {
 		if(cryptStatusOK(cl.AddRandom(NULL, CRYPT_RANDOM_SLOWPOLL))) {
diff --git a/src/syncterm/st_crypt.h b/src/syncterm/st_crypt.h
index d9a704fecd..323b9a1f23 100644
--- a/src/syncterm/st_crypt.h
+++ b/src/syncterm/st_crypt.h
@@ -37,6 +37,8 @@ struct crypt_funcs {
 		C_IN void C_PTR value, C_IN int valueLength );
 	int (*DestroySession)( C_IN CRYPT_SESSION session );
 	int (*AddRandom)( C_IN void C_PTR randomData, C_IN int randomDataLength );
+	int (*DeleteAttribute)( C_IN CRYPT_HANDLE cryptHandle,
+		C_IN CRYPT_ATTRIBUTE_TYPE attributeType);
 };
 
 #endif
diff --git a/src/syncterm/term.c b/src/syncterm/term.c
index bbb608a014..6e5db091b2 100644
--- a/src/syncterm/term.c
+++ b/src/syncterm/term.c
@@ -2824,10 +2824,12 @@ BOOL doterm(struct bbslist *bbs)
 					break;
 				case 0x2600:	/* ALT-L */
 					if(bbs->conn_type != CONN_TYPE_RLOGIN && bbs->conn_type != CONN_TYPE_RLOGIN_REVERSED && bbs->conn_type != CONN_TYPE_SSH) {
-						if(bbs->user[0]) {
-							conn_send(bbs->user,strlen(bbs->user),0);
-							conn_send(cterm->emulation==CTERM_EMULATION_ATASCII?"\x9b":"\r",1,0);
-							SLEEP(10);
+						if (bbs->conn_type != CONN_TYPE_SSHNA) {
+							if(bbs->user[0]) {
+								conn_send(bbs->user,strlen(bbs->user),0);
+								conn_send(cterm->emulation==CTERM_EMULATION_ATASCII?"\x9b":"\r",1,0);
+								SLEEP(10);
+							}
 						}
 						if(bbs->password[0]) {
 							conn_send(bbs->password,strlen(bbs->password),0);
-- 
GitLab