diff --git a/src/sbbs3/answer.cpp b/src/sbbs3/answer.cpp index 4da4569825d40da29ad74b791ed0b05fc69e7b9b..9049256e1e546bdb623bb0459d54e67297ffd2d4 100644 --- a/src/sbbs3/answer.cpp +++ b/src/sbbs3/answer.cpp @@ -25,6 +25,20 @@ extern "C" void client_on(SOCKET sock, client_t* client, BOOL update); +bool +sbbs_t::set_authresponse(bool activate_ssh) +{ + int status; + + lprintf(LOG_DEBUG, "%04d SSH Setting attribute: SESSINFO_AUTHRESPONSE", client_socket); + status = cryptSetAttribute(ssh_session, CRYPT_SESSINFO_AUTHRESPONSE, activate_ssh); + if(cryptStatusError(status)) { + log_crypt_error_status_sock(status, "setting auth response"); + return false; + } + return true; +} + bool sbbs_t::answer() { char str[MAX_PATH+1],str2[MAX_PATH+1],c; @@ -177,29 +191,47 @@ bool sbbs_t::answer() } #ifdef USE_CRYPTLIB if(sys_status&SS_SSH) { + int ssh_failed=0; + bool activate_ssh = false; + tmp[0]=0; pthread_mutex_lock(&ssh_mutex); - ctmp = get_crypt_attribute(ssh_session, CRYPT_SESSINFO_USERNAME); - if (ctmp) { - SAFECOPY(rlogin_name, parse_login(ctmp)); - free_crypt_attrstr(ctmp); - ctmp = get_crypt_attribute(ssh_session, CRYPT_SESSINFO_PASSWORD); + for(ssh_failed=0; ssh_failed < 3; ssh_failed++) { + lprintf(LOG_DEBUG, "%04d SSH Setting attribute: SESSINFO_ACTIVE", client_socket); + if(cryptStatusError(i=cryptSetAttribute(ssh_session, CRYPT_SESSINFO_ACTIVE, 1))) { + log_crypt_error_status_sock(i, "setting session active"); + activate_ssh = false; + // TODO: Add private key here... + if(i != CRYPT_ENVELOPE_RESOURCE) { + break; + } + } + else { + break; + } + ctmp = get_crypt_attribute(ssh_session, CRYPT_SESSINFO_USERNAME); if (ctmp) { - SAFECOPY(tmp, ctmp); + SAFECOPY(rlogin_name, parse_login(ctmp)); free_crypt_attrstr(ctmp); + ctmp = get_crypt_attribute(ssh_session, CRYPT_SESSINFO_PASSWORD); + if (ctmp) { + SAFECOPY(tmp, ctmp); + free_crypt_attrstr(ctmp); + } + lprintf(LOG_DEBUG,"SSH login: '%s'", rlogin_name); } - pthread_mutex_unlock(&ssh_mutex); - lprintf(LOG_DEBUG,"SSH login: '%s'", rlogin_name); - } - else { - rlogin_name[0] = 0; - pthread_mutex_unlock(&ssh_mutex); - } - useron.number = find_login_id(&cfg, rlogin_name); - if(useron.number) { - getuserdat(&cfg,&useron); - for(i=0;i<3 && online;i++) { - if(stricmp(tmp,useron.pass)) { + else { + rlogin_name[0] = 0; + continue; + } + useron.number = find_login_id(&cfg, rlogin_name); + if(useron.number) { + getuserdat(&cfg,&useron); + if (stricmp(tmp, useron.pass) == 0) { + SAFECOPY(rlogin_pass, tmp); + activate_ssh = set_authresponse(true); + } + else if(ssh_failed) { if(cfg.sys_misc&SM_ECHO_PW) safe_snprintf(str,sizeof(str),"(%04u) %-25s FAILED Password attempt: '%s'" ,useron.number,useron.alias,tmp); @@ -208,53 +240,68 @@ bool sbbs_t::answer() ,useron.number,useron.alias); logline(LOG_NOTICE,"+!",str); badlogin(useron.alias, tmp); - rioctl(IOFI); /* flush input buffer */ - bputs(text[InvalidLogon]); - bputs(text[PasswordPrompt]); - console|=CON_R_ECHOX; - getstr(tmp,LEN_PASS*2,K_UPPER|K_LOWPRIO|K_TAB); - console&=~(CON_R_ECHOX|CON_L_ECHOX); - } - else { - SAFECOPY(rlogin_pass, tmp); - if(REALSYSOP && (cfg.sys_misc&SM_SYSPASSLOGIN) && (cfg.sys_misc&SM_R_SYSOP)) { - rioctl(IOFI); /* flush input buffer */ - if(!chksyspass()) - bputs(text[InvalidLogon]); - else { - i=0; - break; - } - } - else { - i = 0; - break; - } + useron.number=0; } } - if(i) { - if(stricmp(tmp,useron.pass)) { - if(cfg.sys_misc&SM_ECHO_PW) - safe_snprintf(str,sizeof(str),"(%04u) %-25s FAILED Password attempt: '%s'" - ,useron.number,useron.alias,tmp); + else { + if(cfg.sys_misc&SM_ECHO_PW) + lprintf(LOG_NOTICE, "SSH !UNKNOWN USER: '%s' (password: %s)", rlogin_name, truncsp(tmp)); + else + lprintf(LOG_NOTICE, "SSH !UNKNOWN USER: '%s'", rlogin_name); + badlogin(rlogin_name, tmp); + // Enable SSH so we can create a new user... + activate_ssh = set_authresponse(true); + } + } + if (activate_ssh) { + int cid; + char tname[1024]; + int tnamelen; + + ssh_failed=0; + // Check the channel ID and name... + if (cryptStatusOK(i=cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &cid))) { + if (i == CRYPT_OK) { + tnamelen = 0; + i=cryptGetAttributeString(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TYPE, tname, &tnamelen); + log_crypt_error_status_sock(i, "getting channel type"); + if (tnamelen != 7 || strnicmp(tname, "session", 7)) { + lprintf(LOG_NOTICE, "%04d SSH [%s] active channel '%.*s' is not 'session', disconnecting.", client_socket, client_ipaddr, tnamelen, tname); + badlogin(/* user: */NULL, /* passwd: */NULL, "SSH", &client_addr, /* delay: */false); + // Fail because there's no session. + activate_ssh = false; + } else - safe_snprintf(str,sizeof(str),"(%04u) %-25s FAILED Password attempt" - ,useron.number,useron.alias); - logline(LOG_NOTICE,"+!",str); - badlogin(useron.alias, tmp); - bputs(text[InvalidLogon]); + session_channel = cid; } - useron.number=0; - hangup(); + } + else { + log_crypt_error_status_sock(i, "getting channel id"); + if (i == CRYPT_ERROR_PERMISSION) + lprintf(LOG_CRIT, "!Your cryptlib build is obsolete, please update"); } } - else { - if(cfg.sys_misc&SM_ECHO_PW) - lprintf(LOG_NOTICE, "SSH !UNKNOWN USER: '%s' (password: %s)", rlogin_name, truncsp(tmp)); - else - lprintf(LOG_NOTICE, "SSH !UNKNOWN USER: '%s'", rlogin_name); - badlogin(rlogin_name, tmp); + if (activate_ssh) { + if(cryptStatusError(i=cryptSetAttribute(ssh_session, CRYPT_PROPERTY_OWNER, CRYPT_UNUSED))) { + log_crypt_error_status_sock(i, "clearing owner"); + activate_ssh = false; + } + } + if(!activate_ssh) { + int status; + lprintf(LOG_NOTICE, "%04d SSH [%s] session establishment failed", client_socket, client_ipaddr); + if (cryptStatusError(status = cryptDestroySession(ssh_session))) { + lprintf(LOG_ERR, "%04d SSH ERROR %d destroying Cryptlib Session %d from %s line %d" + , client_socket, status, ssh_session, __FILE__, __LINE__); + } + ssh_mode = false; + pthread_mutex_unlock(&ssh_mutex); + close_socket(client_socket); + useron.number = 0; + return false; } + SetEvent(ssh_active); + if (cryptStatusOK(cryptGetAttribute(ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_WIDTH, &l)) && l > 0) { cols = l; lprintf(LOG_DEBUG, "%04d SSH [%s] height %d", client_socket, client.addr, cols); @@ -271,6 +318,7 @@ bool sbbs_t::answer() terminal[sizeof(terminal)-1] = 0; lprintf(LOG_DEBUG, "%04d SSH [%s] term: %s", client_socket, client.addr, terminal); } + pthread_mutex_unlock(&ssh_mutex); } #endif diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp index c6302f9c1a7bd36e6640573753f6a96f8a356435..d4b698ecd85788798a60243f6e210b9ac501616d 100644 --- a/src/sbbs3/main.cpp +++ b/src/sbbs3/main.cpp @@ -274,6 +274,20 @@ int lprintf(int level, const char *fmt, ...) return(lputs(level,sbuf)); } +void +sbbs_t::log_crypt_error_status_sock(int status, const char *action) +{ + char *estr; + int level; + get_crypt_error_string(status, ssh_session, &estr, action, &level); + if (estr) { + if (level < startup->ssh_error_level) + level = startup->ssh_error_level; + lprintf(level, "%04d SSH %s", client_socket, estr); + free_crypt_attrstr(estr); + } +} + /* Picks the right log callback function (event or term) based on the sbbs->cfg.node_num value */ /* Prepends the current node number and user alias (if applicable) */ int sbbs_t::lputs(int level, const char* str) @@ -1929,7 +1943,7 @@ static int crypt_pop_channel_data(sbbs_t *sbbs, char *inbuf, int want, int *got) continue; if (cid != sbbs->session_channel) { if (cryptStatusError(status = cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, cid))) { - GCESS(status, sbbs->client_socket, sbbs->ssh_session, "setting channel"); + sbbs->log_crypt_error_status_sock(status, "setting channel"); return status; } cname = get_crypt_attribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TYPE); @@ -1939,7 +1953,7 @@ static int crypt_pop_channel_data(sbbs_t *sbbs, char *inbuf, int want, int *got) free_crypt_attrstr(cname); closing_channel = cid; if (cryptStatusError(status = cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_ACTIVE, 0))) { - GCESS(status, sbbs->client_socket, sbbs->ssh_session, "closing channel"); + sbbs->log_crypt_error_status_sock(status, "closing channel"); return status; } continue; @@ -1951,7 +1965,7 @@ static int crypt_pop_channel_data(sbbs_t *sbbs, char *inbuf, int want, int *got) * and it was destroyed, so it's no longer possible to get the channel id. */ if (status != CRYPT_ERROR_NOTFOUND) - GCESS(status, sbbs->client_socket, sbbs->ssh_session, "getting channel id"); + sbbs->log_crypt_error_status_sock(status, "getting channel id"); closing_channel = -1; } } @@ -2090,6 +2104,10 @@ void input_thread(void *arg) #ifdef USE_CRYPTLIB if(sbbs->ssh_mode && sock==sbbs->client_socket) { int err; + if (WaitForEvent(sbbs->ssh_active, 1000) == WAIT_TIMEOUT) { + pthread_mutex_unlock(&sbbs->input_thread_mutex); + continue; + } pthread_mutex_lock(&sbbs->ssh_mutex); if(cryptStatusError((err=crypt_pop_channel_data(sbbs, (char*)inbuf, rd, &i)))) { pthread_mutex_unlock(&sbbs->ssh_mutex); @@ -3661,6 +3679,7 @@ bool sbbs_t::init() #ifdef USE_CRYPTLIB pthread_mutex_init(&ssh_mutex,NULL); ssh_mutex_created = true; + ssh_active = CreateEvent(NULL, TRUE, FALSE, (void *)"ssh_active"); #endif pthread_mutex_init(&input_thread_mutex,NULL); input_thread_mutex_created = true; @@ -3779,6 +3798,12 @@ sbbs_t::~sbbs_t() #ifdef USE_CRYPTLIB while(ssh_mutex_created && pthread_mutex_destroy(&ssh_mutex)==EBUSY) mswait(1); + if (ssh_active) { + SetEvent(ssh_active); + while ((!CloseEvent(ssh_active)) && errno == EBUSY) + mswait(1); + ssh_active = nullptr; + } #endif while(input_thread_mutex_created && pthread_mutex_destroy(&input_thread_mutex)==EBUSY) mswait(1); @@ -5389,7 +5414,6 @@ NO_SSH: /* Do SSH stuff here */ #ifdef USE_CRYPTLIB if(ssh) { - int ssh_failed=0; BOOL nodelay = TRUE; ulong nb = 0; @@ -5403,10 +5427,10 @@ NO_SSH: sbbs->ssh_mode = true; if(cryptStatusError(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_OPTION_NET_CONNECTTIMEOUT, startup->ssh_connect_timeout))) - GCESS(i, client_socket, sbbs->ssh_session, "setting connect timeout"); + sbbs->log_crypt_error_status_sock(i, "setting connect timeout"); if(cryptStatusError(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_PRIVATEKEY, ssh_context))) { - GCESS(i, client_socket, sbbs->ssh_session, "setting private key"); + sbbs->log_crypt_error_status_sock(i, "setting private key"); SSH_END(client_socket); close_socket(client_socket); continue; @@ -5414,81 +5438,22 @@ NO_SSH: setsockopt(client_socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay)); ioctlsocket(client_socket,FIONBIO,&nb); if(cryptStatusError(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_NETWORKSOCKET, client_socket))) { - GCESS(i, client_socket, sbbs->ssh_session, "setting network socket"); - SSH_END(client_socket); - close_socket(client_socket); - continue; - } - for(ssh_failed=0; ssh_failed < 2; ssh_failed++) { - /* Accept any credentials */ - lprintf(LOG_DEBUG, "%04d SSH Setting attribute: SESSINFO_AUTHRESPONSE", client_socket); - if(cryptStatusError(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_AUTHRESPONSE, 1))) { - GCESS(i, client_socket, sbbs->ssh_session, "setting auth response"); - ssh_failed=1; - break; - } - lprintf(LOG_DEBUG, "%04d SSH Setting attribute: SESSINFO_ACTIVE", client_socket); - if(cryptStatusError(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_ACTIVE, 1))) { - GCESS(i, client_socket, sbbs->ssh_session, "setting session active"); - if(i != CRYPT_ENVELOPE_RESOURCE) { - ssh_failed=2; - break; - } - } - else { - int cid; - char tname[1024]; - int tnamelen; - - ssh_failed=0; - // Check the channel ID and name... - if (cryptStatusOK(i=cryptGetAttribute(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL, &cid))) { - if (i == CRYPT_OK) { - tnamelen = 0; - i=cryptGetAttributeString(sbbs->ssh_session, CRYPT_SESSINFO_SSH_CHANNEL_TYPE, tname, &tnamelen); - GCESS(i, client_socket, sbbs->ssh_session, "getting channel type"); - if (tnamelen != 7 || strnicmp(tname, "session", 7)) { - lprintf(LOG_NOTICE, "%04d SSH [%s] active channel '%.*s' is not 'session', disconnecting.", client_socket, host_ip, tnamelen, tname); - sbbs->badlogin(/* user: */NULL, /* passwd: */NULL, "SSH", &client_addr, /* delay: */false); - // Fail because there's no session. - ssh_failed = 3; - } - else - sbbs->session_channel = cid; - } - } - else { - GCESS(i, client_socket, sbbs->ssh_session, "getting channel id"); - if (i == CRYPT_ERROR_PERMISSION) - lprintf(LOG_CRIT, "!Your cryptlib build is obsolete, please update"); - } - break; - } - } - if (!ssh_failed) { - if(cryptStatusError(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_PROPERTY_OWNER, CRYPT_UNUSED))) { - GCESS(i, client_socket, sbbs->ssh_session, "clearing owner"); - ssh_failed = 2; - } - } - if(ssh_failed) { - lprintf(LOG_NOTICE, "%04d SSH [%s] session establishment failed", client_socket, host_ip); + sbbs->log_crypt_error_status_sock(i, "setting network socket"); SSH_END(client_socket); close_socket(client_socket); continue; } if(cryptStatusError(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_OPTION_NET_READTIMEOUT, 0))) - GCESS(i, sbbs->client_socket, sbbs->ssh_session, "setting read timeout"); + sbbs->log_crypt_error_status_sock(i, "setting read timeout"); // READ = WRITE TIMEOUT HACK... REMOVE WHEN FIXED if(cryptStatusError(i=cryptSetAttribute(sbbs->ssh_session, CRYPT_OPTION_NET_WRITETIMEOUT, 0))) - GCESS(i, sbbs->client_socket, sbbs->ssh_session, "setting write timeout"); + sbbs->log_crypt_error_status_sock(i, "setting write timeout"); #if 0 if(cryptStatusError(err=crypt_pop_channel_data(sbbs, str, sizeof(str), &i))) { GCES(i, sbbs->cfg.node_num, sbbs->ssh_session, "popping data"); i=0; } #endif - sbbs->online=ON_REMOTE; } #endif @@ -5780,6 +5745,7 @@ NO_SSH: new_node->sys_status|=SS_SSH; new_node->telnet_mode|=TELNET_MODE_OFF; // SSH does not use Telnet commands new_node->ssh_session=sbbs->ssh_session; + new_node->online = ON_REMOTE; } /* Wait for pending data to be sent then turn off ssh_mode for uber-output */ while(sbbs->output_thread_running && RingBufFull(&sbbs->outbuf)) @@ -5791,8 +5757,8 @@ NO_SSH: } protected_uint32_adjust(&node_threads_running, 1); - new_node->input_thread_running = true; - new_node->input_thread=(HANDLE)_beginthread(input_thread,0, new_node); + new_node->input_thread_running = true; + new_node->input_thread=(HANDLE)_beginthread(input_thread, 0, new_node); new_node->output_thread_running = true; new_node->autoterm = sbbs->autoterm; new_node->cols = sbbs->cols; diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h index 58462a8f84a12c5450567c27a923a4073b696bcb..7a848b7237053f88f3268ebe55e4afb81f24c7e1 100644 --- a/src/sbbs3/sbbs.h +++ b/src/sbbs3/sbbs.h @@ -451,6 +451,7 @@ public: bool input_thread_mutex_created = false; pthread_mutex_t ssh_mutex; bool ssh_mutex_created = false; + xpevent_t ssh_active = nullptr; #define OUTCOM_RETRY_DELAY 80 // milliseconds #define OUTCOM_RETRY_ATTEMPTS 1000 // 80 seconds @@ -1020,6 +1021,7 @@ public: const char* parse_login(const char*); /* answer.cpp */ + bool set_authresponse(bool activate_ssh); bool answer(void); /* logon.ccp */ @@ -1066,6 +1068,7 @@ public: int getnodetopage(int all, int telegram); /* main.cpp */ + void log_crypt_error_status_sock(int status, const char *action); int lputs(int level, const char* str); int lprintf(int level, const char *fmt, ...) #if defined(__GNUC__) // Catch printf-format errors diff --git a/src/sbbs3/ssl.c b/src/sbbs3/ssl.c index e6b81825eeee3cb96702ee9d456e0c5beea5c213..2eb68917a0e391fcef0e2c97de38aca33efc3449 100644 --- a/src/sbbs3/ssl.c +++ b/src/sbbs3/ssl.c @@ -15,10 +15,12 @@ void free_crypt_attrstr(char *attr) char* get_crypt_attribute(CRYPT_HANDLE sess, C_IN CRYPT_ATTRIBUTE_TYPE attr) { - int len = 0; - char *estr = NULL; + int len = 0; + char *estr = NULL; + int status; - if (cryptStatusOK(cryptGetAttributeString(sess, attr, NULL, &len))) { + status = cryptGetAttributeString(sess, attr, NULL, &len); + if (cryptStatusOK(status)) { estr = malloc(len + 1); if (estr) { if (cryptStatusError(cryptGetAttributeString(sess, attr, estr, &len))) {