--- sbbs/src/sbbs3/sbbscon.c 2018/04/24 16:41:23 1.1 +++ sbbs/src/sbbs3/sbbscon.c 2018/04/24 16:43:27 1.1.1.2 @@ -2,13 +2,13 @@ /* Synchronet vanilla/console-mode "front-end" */ -/* $Id: sbbscon.c,v 1.1 2018/04/24 16:41:23 root Exp $ */ +/* $Id: sbbscon.c,v 1.1.1.2 2018/04/24 16:43:27 root Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * @@ -58,6 +58,10 @@ #ifdef __unix__ +#ifdef USE_LINUX_CAPS +#include +#endif + #include #include #include @@ -101,13 +105,15 @@ BOOL web_running=FALSE; BOOL web_stopped=FALSE; BOOL has_web=FALSE; web_startup_t web_startup; -uint thread_count=1; -uint socket_count=0; -uint client_count=0; +ulong thread_count=1; +ulong socket_count=0; +ulong error_count=0; int prompt_len=0; static scfg_t scfg; /* To allow rerun */ static ulong served=0; char ini_file[MAX_PATH+1]; +link_list_t login_attempt_list; +link_list_t client_list; #ifdef __unix__ char new_uid_name[32]; @@ -122,6 +128,27 @@ char log_ident[128]; BOOL std_facilities=FALSE; FILE * pidf; char pid_fname[MAX_PATH+1]; +BOOL capabilities_set=FALSE; + +#ifdef USE_LINUX_CAPS +/* + * If the value of PR_SET_KEEPCAPS is not in , define it + * here. This allows setuid() to work on systems running a new enough + * kernel but with /usr/include/linux pointing to "standard" kernel + * headers. + */ +#ifndef PR_SET_KEEPCAPS +#define PR_SET_KEEPCAPS 8 +#endif + +#ifndef SYS_capset +#ifndef __NR_capset +#include /* Slackware 4.0 needs this. */ +#endif +#define SYS_capset __NR_capset +#endif +#endif /* USE_LINUX_CAPS */ + #endif static const char* prompt; @@ -148,22 +175,21 @@ static const char* usage = "\nusage: %s #ifdef __unix__ "\tnd do not read run as daemon - overrides .ini file\n" #endif - "\tlt use local timezone (do not force UTC/GMT)\n" "\tdefaults show default settings and options\n" "\n" ; -static const char* telnet_usage = "Telnet server settings:\n\n" - "\ttf set first Telnet node number\n" - "\ttl set last Telnet node number\n" +static const char* telnet_usage = "Terminal server settings:\n\n" + "\ttf set first node number\n" + "\ttl set last node number\n" "\ttp set Telnet server port\n" "\trp set RLogin server port (and enable RLogin server)\n" "\tr2 use second RLogin name in BSD RLogin\n" - "\tto set Telnet server options value (advanced)\n" + "\tto set Terminal server options value (advanced)\n" "\tta enable auto-logon via IP address\n" "\ttd enable Telnet command debug output\n" "\ttc emabble sysop availability for chat\n" "\ttq disable QWK events\n" - "\tt- disable Telnet/RLogin server\n" + "\tt- disable Terminal server\n" "\n" ; static const char* ftp_usage = "FTP server settings:\n" @@ -226,7 +252,7 @@ static int lputs(int level, char *str) printf("\r%*s\r",prompt_len,""); if(str!=NULL) { for(p=str; *p; p++) { - if(iscntrl(*p)) + if(iscntrl((unsigned char)*p)) printf("^%c",'@'+*p); else printf("%c",*p); @@ -235,14 +261,19 @@ static int lputs(int level, char *str) } /* re-display prompt with current stats */ if(prompt!=NULL) - prompt_len = printf(prompt, thread_count, socket_count, client_count, served); + prompt_len = printf(prompt, thread_count, socket_count, client_list.count, served, error_count); fflush(stdout); pthread_mutex_unlock(&mutex); return(prompt_len); } -static int lprintf(int level, char *fmt, ...) +static void errormsg(void* cbdata, int level, const char* fmt) +{ + error_count++; +} + +static int lprintf(int level, const char *fmt, ...) { va_list argptr; char sbuf[1024]; @@ -264,8 +295,11 @@ static BOOL do_seteuid(BOOL to_new) { BOOL result=FALSE; - if(new_uid_name[0]==0) /* not set? */ - return(TRUE); /* do nothing */ + if(capabilities_set) + return(TRUE); /* do nothing */ + + if(new_uid_name[0]==0) /* not set? */ + return(TRUE); /* do nothing */ if(old_uid==new_uid && old_gid==new_gid) return(TRUE); /* do nothing */ @@ -341,6 +375,11 @@ BOOL do_setuid(BOOL force) } if(getuid() != new_uid || geteuid() != new_uid) { + if(initgroups(new_uid_name, new_gid)) { + lputs(LOG_ERR,"!initgroups FAILED"); + lputs(LOG_ERR,strerror(errno)); + result=FALSE; + } if(setreuid(new_uid,new_uid)) { lputs(LOG_ERR,"!setuid FAILED"); @@ -356,6 +395,120 @@ BOOL do_setuid(BOOL force) return(result); } + +int change_user(void) +{ + if(!do_setuid(FALSE)) { + /* actually try to change the uid of this process */ + lputs(LOG_ERR,"!Setting new user_id failed! (Does the user exist?)"); + return(-1); + } else { + char str[256]; + struct passwd *pwent; + + pwent=getpwnam(new_uid_name); + if(pwent != NULL) { + char uenv[128]; + char henv[MAX_PATH+6]; + sprintf(uenv,"USER=%s",pwent->pw_name); + putenv(uenv); + sprintf(henv,"HOME=%s",pwent->pw_dir); + putenv(henv); + } + if(new_gid_name[0]) { + char genv[128]; + sprintf(genv,"GROUP=%s",new_gid_name); + putenv(genv); + } + lprintf(LOG_INFO,"Successfully changed user_id to %s", new_uid_name); + } + return(0); +} + +#ifdef USE_LINUX_CAPS +/********************************************************** +* Set system capabilities on Linux. Allows non root user +* to make calls to bind +* **********************************************************/ +void whoami(void) +{ + uid_t a, b, c; + getresuid(&a, &b, &c); + lprintf(LOG_DEBUG,"Current uids: ruid - %d, euid - %d, suid - %d", a, b, c); + getresgid(&a, &b, &c); + lprintf(LOG_DEBUG,"Current gids: rgid - %d, egid - %d, sgid - %d", a, b, c); +} + +void list_caps(void) +{ + cap_t caps = cap_get_proc(); + ssize_t y = 0; + lprintf(LOG_DEBUG, "The process %d was given capabilities %s", (int) getpid(), cap_to_text(caps, &y)); + fflush(0); + cap_free(caps); +} + +static int linux_keepcaps(void) +{ + char strbuf[100]; + /* + * Ask the kernel to allow us to keep our capabilities after we + * setuid(). + */ + if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) { + if (errno != EINVAL) { + lputs(LOG_ERR,"linux_keepcaps FAILED"); + lputs(LOG_ERR,strerror(errno)); + } + return(-1); + } + return(0); +} + +static int linux_setcaps(unsigned int caps) +{ + struct __user_cap_header_struct caphead; + struct __user_cap_data_struct cap; + + memset(&caphead, 0, sizeof(caphead)); + caphead.version = _LINUX_CAPABILITY_VERSION; + caphead.pid = 0; + memset(&cap, 0, sizeof(cap)); + cap.effective = caps; + cap.permitted = caps; + cap.inheritable = 0; + return(syscall(SYS_capset, &caphead, &cap)); +} + +static int linux_initialprivs(void) +{ + unsigned int caps; + + caps = 0; + caps |= (1 << CAP_NET_BIND_SERVICE); + caps |= (1 << CAP_SETUID); + caps |= (1 << CAP_SETGID); + caps |= (1 << CAP_DAC_READ_SEARCH); + caps |= (1 << CAP_SYS_RESOURCE); + printf("Setting initial privileges\n"); + return(linux_setcaps(caps)); +} + +static int linux_minprivs(void) +{ + unsigned int caps; + + caps = 0; + caps |= (1 << CAP_NET_BIND_SERVICE); + caps |= (1 << CAP_SYS_RESOURCE); + printf("Setting minimum privileges\n"); + return(linux_setcaps(caps)); +} +/********************************************************** +* End capabilities section +* **********************************************************/ +#endif /* USE_LINUX_CAPS */ + #endif /* __unix__ */ #ifdef _WINSOCKAPI_ @@ -369,7 +522,7 @@ static BOOL winsock_startup(void) if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) return(TRUE); - lprintf(LOG_ERR,"!WinSock startup ERROR %d", status); + lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status); return(FALSE); } @@ -437,28 +590,28 @@ static void socket_open(void* p, BOOL op static void client_on(void* p, BOOL on, int sock, client_t* client, BOOL update) { - static pthread_mutex_t mutex; - static BOOL mutex_initialized; - - if(!mutex_initialized) { - pthread_mutex_init(&mutex,NULL); - mutex_initialized=TRUE; - } + if(on) { + if(update) { + list_node_t* node; + + listLock(&client_list); + if((node=listFindTaggedNode(&client_list, sock)) != NULL) + memcpy(node->data, client, sizeof(client_t)); + listUnlock(&client_list); + } else { + served++; + listAddNodeData(&client_list, client, sizeof(client_t), sock, LAST_NODE); + } + } else + listRemoveTaggedNode(&client_list, sock, /* free_data: */TRUE); - pthread_mutex_lock(&mutex); - if(on && !update) { - client_count++; - served++; - } else if(!on && client_count>0) - client_count--; - pthread_mutex_unlock(&mutex); lputs(LOG_INFO,NULL); /* update displayed stats */ } /****************************************************************************/ /* BBS local/log print routine */ /****************************************************************************/ -static int bbs_lputs(void* p, int level, char *str) +static int bbs_lputs(void* p, int level, const char *str) { char logline[512]; char tstr[64]; @@ -475,7 +628,7 @@ static int bbs_lputs(void* p, int level, if (std_facilities) syslog(level|LOG_AUTH,"%s",str); else - syslog(level," %s",str); + syslog(level,"term %s",str); return(strlen(str)); } #endif @@ -488,7 +641,7 @@ static int bbs_lputs(void* p, int level, ,tm.tm_mon+1,tm.tm_mday ,tm.tm_hour,tm.tm_min,tm.tm_sec); - sprintf(logline,"%s %.*s",tstr,(int)sizeof(logline)-32,str); + sprintf(logline,"%sterm %.*s",tstr,(int)sizeof(logline)-32,str); truncsp(logline); lputs(level,logline); @@ -499,12 +652,12 @@ static void bbs_started(void* p) { bbs_running=TRUE; bbs_stopped=FALSE; - #ifdef _THREAD_SUID_BROKEN - if(thread_suid_broken) { - do_seteuid(FALSE); - do_setuid(FALSE); - } - #endif + #ifdef _THREAD_SUID_BROKEN + if(thread_suid_broken) { + do_seteuid(FALSE); + do_setuid(FALSE); + } + #endif } static void bbs_terminated(void* p, int code) @@ -516,7 +669,7 @@ static void bbs_terminated(void* p, int /****************************************************************************/ /* FTP local/log print routine */ /****************************************************************************/ -static int ftp_lputs(void* p, int level, char *str) +static int ftp_lputs(void* p, int level, const char *str) { char logline[512]; char tstr[64]; @@ -578,7 +731,7 @@ static void ftp_terminated(void* p, int /****************************************************************************/ /* Mail Server local/log print routine */ /****************************************************************************/ -static int mail_lputs(void* p, int level, char *str) +static int mail_lputs(void* p, int level, const char *str) { char logline[512]; char tstr[64]; @@ -636,7 +789,7 @@ static void mail_terminated(void* p, int /****************************************************************************/ /* Services local/log print routine */ /****************************************************************************/ -static int services_lputs(void* p, int level, char *str) +static int services_lputs(void* p, int level, const char *str) { char logline[512]; char tstr[64]; @@ -694,7 +847,7 @@ static void services_terminated(void* p, /****************************************************************************/ /* Event thread local/log print routine */ /****************************************************************************/ -static int event_lputs(int level, char *str) +static int event_lputs(void* p, int level, const char *str) { char logline[512]; char tstr[64]; @@ -734,7 +887,7 @@ static int event_lputs(int level, char * /****************************************************************************/ /* web local/log print routine */ /****************************************************************************/ -static int web_lputs(void* p, int level, char *str) +static int web_lputs(void* p, int level, const char *str) { char logline[512]; char tstr[64]; @@ -811,7 +964,7 @@ static void terminate(void) while(bbs_running || ftp_running || web_running || mail_running || services_running) { if(count && (count%10)==0) { if(bbs_running) - lputs(LOG_INFO,"BBS System thread still running"); + lputs(LOG_INFO,"Terminal Server thread still running"); if(ftp_running) lputs(LOG_INFO,"FTP Server thread still running"); if(web_running) @@ -903,7 +1056,7 @@ void cleanup(void) } #if defined(_WIN32) -BOOL WINAPI ControlHandler(DWORD CtrlType) +BOOL WINAPI ControlHandler(unsigned long CtrlType) { terminated=TRUE; return TRUE; @@ -949,6 +1102,7 @@ static void handle_sigs(void) int sig=0; sigset_t sigs; + SetThreadName("Signal Handler"); thread_up(NULL,TRUE,TRUE); if (is_daemon) { @@ -1037,6 +1191,7 @@ int main(int argc, char** argv) char error[256]; char host_name[128]=""; node_t node; + unsigned count; #ifdef __unix__ struct passwd* pw_entry; struct group* gr_entry; @@ -1056,6 +1211,9 @@ int main(int argc, char** argv) printf("\nSynchronet Console for %s Version %s%c %s\n\n" ,PLATFORM_DESC,VERSION,REVISION,COPYRIGHT_NOTICE); + SetThreadName("Main"); + listInit(&client_list, LINK_LIST_MUTEX); + loginAttemptListInit(&login_attempt_list); atexit(cleanup); ctrl_dir=getenv("SBBSCTRL"); /* read from environment variable */ @@ -1082,6 +1240,7 @@ int main(int argc, char** argv) bbs_startup.log_level = LOG_DEBUG; bbs_startup.lputs=bbs_lputs; bbs_startup.event_lputs=event_lputs; + bbs_startup.errormsg=errormsg; bbs_startup.started=bbs_started; bbs_startup.recycle=recycle; bbs_startup.terminated=bbs_terminated; @@ -1096,6 +1255,7 @@ int main(int argc, char** argv) bbs_startup.status=bbs_status; bbs_startup.clients=bbs_clients; */ + bbs_startup.login_attempt_list=&login_attempt_list; strcpy(bbs_startup.ctrl_dir,ctrl_dir); /* Initialize FTP startup structure */ @@ -1104,6 +1264,7 @@ int main(int argc, char** argv) ftp_startup.cbdata=&ftp_startup; ftp_startup.log_level = LOG_DEBUG; ftp_startup.lputs=ftp_lputs; + ftp_startup.errormsg=errormsg; ftp_startup.started=ftp_started; ftp_startup.recycle=recycle; ftp_startup.terminated=ftp_terminated; @@ -1114,6 +1275,7 @@ int main(int argc, char** argv) ftp_startup.seteuid=do_seteuid; ftp_startup.setuid=do_setuid; #endif + ftp_startup.login_attempt_list=&login_attempt_list; strcpy(ftp_startup.index_file_name,"00index"); strcpy(ftp_startup.ctrl_dir,ctrl_dir); @@ -1123,6 +1285,7 @@ int main(int argc, char** argv) web_startup.cbdata=&web_startup; web_startup.log_level = LOG_DEBUG; web_startup.lputs=web_lputs; + web_startup.errormsg=errormsg; web_startup.started=web_started; web_startup.recycle=recycle; web_startup.terminated=web_terminated; @@ -1133,6 +1296,7 @@ int main(int argc, char** argv) web_startup.seteuid=do_seteuid; web_startup.setuid=do_setuid; #endif + web_startup.login_attempt_list=&login_attempt_list; strcpy(web_startup.ctrl_dir,ctrl_dir); /* Initialize Mail Server startup structure */ @@ -1141,6 +1305,7 @@ int main(int argc, char** argv) mail_startup.cbdata=&mail_startup; mail_startup.log_level = LOG_DEBUG; mail_startup.lputs=mail_lputs; + mail_startup.errormsg=errormsg; mail_startup.started=mail_started; mail_startup.recycle=recycle; mail_startup.terminated=mail_terminated; @@ -1151,6 +1316,7 @@ int main(int argc, char** argv) mail_startup.seteuid=do_seteuid; mail_startup.setuid=do_setuid; #endif + mail_startup.login_attempt_list=&login_attempt_list; strcpy(mail_startup.ctrl_dir,ctrl_dir); /* Initialize Services startup structure */ @@ -1159,6 +1325,7 @@ int main(int argc, char** argv) services_startup.cbdata=&services_startup; services_startup.log_level = LOG_DEBUG; services_startup.lputs=services_lputs; + services_startup.errormsg=errormsg; services_startup.started=services_started; services_startup.recycle=recycle; services_startup.terminated=services_terminated; @@ -1169,6 +1336,7 @@ int main(int argc, char** argv) services_startup.seteuid=do_seteuid; services_startup.setuid=do_setuid; #endif + services_startup.login_attempt_list=&login_attempt_list; strcpy(services_startup.ctrl_dir,ctrl_dir); /* Pre-INI command-line switches */ @@ -1238,9 +1406,9 @@ int main(int argc, char** argv) printf("Default settings:\n"); printf("\n"); printf("Telnet server port:\t%u\n",bbs_startup.telnet_port); - printf("Telnet first node:\t%u\n",bbs_startup.first_node); - printf("Telnet last node:\t%u\n",bbs_startup.last_node); - printf("Telnet server options:\t0x%08lX\n",bbs_startup.options); + printf("Terminal first node:\t%u\n",bbs_startup.first_node); + printf("Terminal last node:\t%u\n",bbs_startup.last_node); + printf("Terminal server options:\t0x%08lX\n",bbs_startup.options); printf("FTP server port:\t%u\n",ftp_startup.port); printf("FTP server options:\t0x%08lX\n",ftp_startup.options); printf("Mail SMTP server port:\t%u\n",mail_startup.smtp_port); @@ -1260,7 +1428,7 @@ int main(int argc, char** argv) SAFECOPY(log_facility,arg++); break; #endif - case 'T': /* Telnet settings */ + case 'T': /* Terminal server settings */ switch(toupper(*(arg++))) { case '-': run_bbs=FALSE; @@ -1409,7 +1577,6 @@ int main(int argc, char** argv) return(1); } break; - break; case 'G': /* GET */ switch(toupper(*(arg++))) { case 'I': /* Identity */ @@ -1481,7 +1648,7 @@ int main(int argc, char** argv) case 'S': /* Services */ run_services=FALSE; break; - case 'T': /* Telnet */ + case 'T': /* Terminal Server */ run_bbs=FALSE; break; case 'E': /* No Events */ @@ -1517,19 +1684,6 @@ int main(int argc, char** argv) return(1); } break; - case 'L': /* Local */ - switch(toupper(*(arg++))) { - case 'T': /* timezone */ - bbs_startup.options |=BBS_OPT_LOCAL_TIMEZONE; - ftp_startup.options |=BBS_OPT_LOCAL_TIMEZONE; - mail_startup.options |=BBS_OPT_LOCAL_TIMEZONE; - services_startup.options|=BBS_OPT_LOCAL_TIMEZONE; - break; - default: - show_usage(argv[0]); - return(1); - } - break; default: show_usage(argv[0]); @@ -1635,7 +1789,42 @@ int main(int argc, char** argv) #if defined(_WIN32) SetConsoleCtrlHandler(ControlHandler, TRUE /* Add */); #elif defined(__unix__) - /* Set up blocked signals */ + +#ifdef USE_LINUX_CAPS /* set capabilities and change user before we start threads */ + whoami(); + list_caps(); + if(linux_initialprivs() < 0) { + lputs(LOG_ERR,"linux_initialprivs() FAILED"); + /* assuming if we pass here the module is loaded so no further module messages are needed */ + lputs(LOG_ERR,"Verify the following kernel module is loaded [See insmod(8)]: capability"); + lputs(LOG_ERR,strerror(errno)); + } + else { + list_caps(); + if(linux_keepcaps() < 0) { + lputs(LOG_ERR,"linux_keepcaps() FAILED"); + lputs(LOG_ERR,strerror(errno)); + } + else { + if(change_user() < 0) { + lputs(LOG_ERR,"change_user() FAILED"); + } + else { + if(linux_minprivs() < 0) { + lputs(LOG_ERR,"linux_minprivs() FAILED"); + lputs(LOG_ERR,strerror(errno)); + } + else { + capabilities_set=TRUE; + } + } + } + } + whoami(); + list_caps(); +#endif /* USE_LINUX_CAPS */ + + /* Set up blocked signals */ sigemptyset(&sigs); sigaddset(&sigs,SIGINT); sigaddset(&sigs,SIGQUIT); @@ -1648,34 +1837,36 @@ int main(int argc, char** argv) signal(SIGPIPE, SIG_IGN); /* Ignore "Broken Pipe" signal (Also used for broken socket etc.) */ signal(SIGALRM, SIG_IGN); /* Ignore "Alarm" signal */ _beginthread((void(*)(void*))handle_sigs,0,NULL); - if(new_uid_name[0]!=0) { /* check the user arg, if we have uid 0 */ - /* Can't recycle servers (re-bind ports) as non-root user */ - /* If DONT_BLAME_SYNCHRONET is set, keeps root credentials laying around */ + if(!capabilities_set) { /* capabilities were NOT set, fallback to original handling of thread options */ + if(new_uid_name[0]!=0) { /* check the user arg, if we have uid 0 */ + /* Can't recycle servers (re-bind ports) as non-root user */ + /* If DONT_BLAME_SYNCHRONET is set, keeps root credentials laying around */ #if !defined(DONT_BLAME_SYNCHRONET) - if(!thread_suid_broken) { - if(bbs_startup.telnet_port < IPPORT_RESERVED - || (bbs_startup.options & BBS_OPT_ALLOW_RLOGIN - && bbs_startup.rlogin_port < IPPORT_RESERVED) + if(!thread_suid_broken) { + if(bbs_startup.telnet_port < IPPORT_RESERVED + || (bbs_startup.options & BBS_OPT_ALLOW_RLOGIN + && bbs_startup.rlogin_port < IPPORT_RESERVED) #ifdef USE_CRYPTLIB - || (bbs_startup.options & BBS_OPT_ALLOW_SSH - && bbs_startup.ssh_port < IPPORT_RESERVED) -#endif - ) - bbs_startup.options|=BBS_OPT_NO_RECYCLE; - if(ftp_startup.port < IPPORT_RESERVED) - ftp_startup.options|=FTP_OPT_NO_RECYCLE; - if(web_startup.port < IPPORT_RESERVED) - web_startup.options|=BBS_OPT_NO_RECYCLE; - if((mail_startup.options & MAIL_OPT_ALLOW_POP3 - && mail_startup.pop3_port < IPPORT_RESERVED) - || mail_startup.smtp_port < IPPORT_RESERVED) - mail_startup.options|=MAIL_OPT_NO_RECYCLE; - /* Perhaps a BBS_OPT_NO_RECYCLE_LOW option? */ - services_startup.options|=BBS_OPT_NO_RECYCLE; - } -#endif - } + || (bbs_startup.options & BBS_OPT_ALLOW_SSH + && bbs_startup.ssh_port < IPPORT_RESERVED) #endif + ) + bbs_startup.options|=BBS_OPT_NO_RECYCLE; + if(ftp_startup.port < IPPORT_RESERVED) + ftp_startup.options|=FTP_OPT_NO_RECYCLE; + if(web_startup.port < IPPORT_RESERVED) + web_startup.options|=BBS_OPT_NO_RECYCLE; + if((mail_startup.options & MAIL_OPT_ALLOW_POP3 + && mail_startup.pop3_port < IPPORT_RESERVED) + || mail_startup.smtp_port < IPPORT_RESERVED) + mail_startup.options|=MAIL_OPT_NO_RECYCLE; + /* Perhaps a BBS_OPT_NO_RECYCLE_LOW option? */ + services_startup.options|=BBS_OPT_NO_RECYCLE; + } +#endif /* !defined(DONT_BLAME_SYNCHRONET) */ + } + } /* end if(!capabilities_set) */ +#endif /* defined(__unix__) */ if(run_bbs) _beginthread((void(*)(void*))bbs_thread,0,&bbs_startup); @@ -1697,69 +1888,50 @@ int main(int argc, char** argv) #endif #ifdef __unix__ - if(getuid()) { /* are we running as a normal user? */ - lprintf(LOG_WARNING - ,"!Started as non-root user. Cannot bind() to ports below %u.", IPPORT_RESERVED); - } - - else if(new_uid_name[0]==0) /* check the user arg, if we have uid 0 */ - lputs(LOG_WARNING,"WARNING: No user account specified, running as root."); - - else - { - lputs(LOG_INFO,"Waiting for child threads to bind ports..."); - while((run_bbs && !(bbs_running || bbs_stopped)) - || (run_ftp && !(ftp_running || ftp_stopped)) - || (run_web && !(web_running || web_stopped)) - || (run_mail && !(mail_running || mail_stopped)) - || (run_services && !(services_running || services_stopped))) { - mswait(1000); - if(run_bbs && !(bbs_running || bbs_stopped)) - lputs(LOG_INFO,"Waiting for BBS thread"); - if(run_web && !(web_running || web_stopped)) - lputs(LOG_INFO,"Waiting for Web thread"); - if(run_ftp && !(ftp_running || ftp_stopped)) - lputs(LOG_INFO,"Waiting for FTP thread"); - if(run_mail && !(mail_running || mail_stopped)) - lputs(LOG_INFO,"Waiting for Mail thread"); - if(run_services && !(services_running || services_stopped)) - lputs(LOG_INFO,"Waiting for Services thread"); - } - - if(!do_setuid(FALSE)) - /* actually try to change the uid of this process */ - lputs(LOG_ERR,"!Setting new user_id failed! (Does the user exist?)"); + if(getuid() && !capabilities_set) { /* are we running as a normal user? */ + lprintf(LOG_WARNING + ,"!Started as non-root user. Cannot bind() to ports below %u.", IPPORT_RESERVED); + } + else if(new_uid_name[0]==0) /* check the user arg, if we have uid 0 */ + lputs(LOG_WARNING,"WARNING: No user account specified, running as root."); - else { - char str[256]; - struct passwd *pwent; - - pwent=getpwnam(new_uid_name); - if(pwent != NULL) { - char uenv[128]; - char henv[MAX_PATH+6]; - sprintf(uenv,"USER=%s",pwent->pw_name); - putenv(uenv); - sprintf(henv,"HOME=%s",pwent->pw_dir); - putenv(henv); - } - if(new_gid_name[0]) { - char genv[128]; - sprintf(genv,"GROUP=%s",new_gid_name); - putenv(genv); - } - lprintf(LOG_INFO,"Successfully changed user_id to %s", new_uid_name); - } + else + { + lputs(LOG_INFO,"Waiting for child threads to bind ports..."); + while((run_bbs && !(bbs_running || bbs_stopped)) + || (run_ftp && !(ftp_running || ftp_stopped)) + || (run_web && !(web_running || web_stopped)) + || (run_mail && !(mail_running || mail_stopped)) + || (run_services && !(services_running || services_stopped))) { + mswait(1000); + if(run_bbs && !(bbs_running || bbs_stopped)) + lputs(LOG_INFO,"Waiting for BBS thread"); + if(run_web && !(web_running || web_stopped)) + lputs(LOG_INFO,"Waiting for Web thread"); + if(run_ftp && !(ftp_running || ftp_stopped)) + lputs(LOG_INFO,"Waiting for FTP thread"); + if(run_mail && !(mail_running || mail_stopped)) + lputs(LOG_INFO,"Waiting for Mail thread"); + if(run_services && !(services_running || services_stopped)) + lputs(LOG_INFO,"Waiting for Services thread"); + } + + if(!capabilities_set) { /* if using capabilities user should already have changed */ + if(change_user() < 0) + lputs(LOG_ERR,"change_user FAILED"); + } + } + + if(!isatty(fileno(stdin))) /* redirected */ + while(1) { + select(0,NULL,NULL,NULL,NULL); /* Sleep forever - Should this just exit the thread? */ + lputs(LOG_WARNING,"select(NULL) returned!"); } - - if(!isatty(fileno(stdin))) /* redirected */ - while(1) - select(0,NULL,NULL,NULL,NULL); /* Sleep forever - Should this just exit the thread? */ else /* interactive */ #endif { - prompt = "[Threads: %d Sockets: %d Clients: %d Served: %lu] (?=Help): "; - lputs(LOG_INFO,NULL); /* display prompt */ + prompt = "[Threads: %d Sockets: %d Clients: %d Served: %lu Errors: %lu] (?=Help): "; + lputs(LOG_INFO,NULL); /* display prompt */ while(!terminated) { #ifdef __unix__ @@ -1787,22 +1959,42 @@ int main(int argc, char** argv) printf("%c\n",ch); switch(ch) { case 'q': - terminated=TRUE; - break; +#ifdef SBBSCON_PROMPT_ON_QUIT /* This part of Quicksilver's mod doesn't work right on an active BBS (the prompt is usually quickly erased) */ + /* default to no, prevent accidental quit */ + printf("Confirm quit [y/N]: "); + fflush(stdout); + switch (toupper(getch())) { + case 'Y': + terminated = TRUE; + break; + default: + break; + } +#else + terminated = TRUE; +#endif + break; case 'w': /* who's online */ printf("\nNodes in use:\n"); case 'n': /* nodelist */ printf("\n"); + count=0; for(i=1;i<=scfg.sys_nodes;i++) { getnodedat(&scfg,i,&node,NULL /* file */); if(ch=='w' && node.status!=NODE_INUSE && node.status!=NODE_QUIET) continue; printnodedat(&scfg, i,&node); + count++; } + if(ch=='w') + printf("%u nodes in use\n", count); break; case 'l': /* lock node */ case 'd': /* down node */ case 'i': /* interrupt node */ +#ifdef __unix__ + _echo_on(); /* turn on echoing so user can see what they type */ +#endif printf("\nNode number: "); if((n=atoi(fgets(str,sizeof(str),stdin)))<1) break; @@ -1825,12 +2017,16 @@ int main(int argc, char** argv) } putnodedat(&scfg,n,&node,file); printnodedat(&scfg,n,&node); +#ifdef __unix__ + _echo_off(); /* turn off echoing - failsafe */ +#endif break; case 'r': /* recycle */ case 's': /* shutdown */ case 't': /* terminate */ - printf("BBS, FTP, Web, Mail, Services, or [All] ? "); - switch(toupper(getch())) { + printf("BBS, FTP, Web, Mail, Services, All, or [Cancel] ? "); + fflush(stdout); + switch(toupper(getch())) { case 'B': printf("BBS\n"); if(ch=='t') @@ -1876,7 +2072,7 @@ int main(int argc, char** argv) else services_startup.recycle_now=TRUE; break; - default: + case 'A': printf("All\n"); if(ch=='t') terminate(); @@ -1895,30 +2091,106 @@ int main(int argc, char** argv) services_startup.recycle_now=TRUE; } break; + case 'C': + default: + break; } break; case '!': /* execute */ +#ifdef __unix__ + _echo_on(); /* turn on echoing so user can see what they type */ +#endif printf("Command line: "); fgets(str,sizeof(str),stdin); system(str); +#ifdef __unix__ + _echo_off(); /* turn off echoing - failsafe */ +#endif break; - default: + case 'a': /* Show failed login attempts: */ + printf("\nFailed login attempts:\n\n"); + { + unsigned long total=0; + struct tm tm; + list_node_t* node; + login_attempt_t* login_attempt; + + listLock(&login_attempt_list); + count=0; + for(node=login_attempt_list.first; node!=NULL; node=node->next) { + login_attempt=node->data; + localtime_r(&login_attempt->time,&tm); + printf("%u attempts (%u duplicate) from %s, last via %s on %u/%u %02u:%02u:%02u (user: %s, password: %s)\n" + ,login_attempt->count + ,login_attempt->dupes + ,inet_ntoa(login_attempt->addr) + ,login_attempt->prot + ,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec + ,login_attempt->user + ,login_attempt->pass + ); + count++; + total+=(login_attempt->count-login_attempt->dupes); + } + listUnlock(&login_attempt_list); + if(count) + printf("==\n"); + printf("%u failed login attempters (potential password hackers)\n", count); + printf("%u total unique failed login attempts (potential password hack attempts)\n", total); + } + break; + case 'A': + printf("\n%u login attempts cleared\n", loginAttemptListClear(&login_attempt_list)); + break; + case 'c': /* Show connected clients: */ + printf("\nConnected clients:\n\n"); + { + unsigned long total=0; + struct tm tm; + list_node_t* node; + client_t* client; + + listLock(&client_list); + count=0; + for(node=client_list.first; node!=NULL; node=node->next) { + client=node->data; + localtime_r(&client->time,&tm); + printf("%04d %s %s %s %s port %u since %u/%u %02u:%02u:%02u\n" + ,node->tag + ,client->protocol + ,client->user + ,client->addr + ,client->host + ,client->port + ,tm.tm_mon+1,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec + ); + count++; + } + listUnlock(&client_list); + } + break; + case '?': /* only print help if user requests it */ printf("\nSynchronet Console Version %s%c Help\n\n",VERSION,REVISION); printf("q = quit\n"); printf("n = node list\n"); - printf("w = who's online\n"); + printf("w = who's online (node's in-use)\n"); printf("l = lock node (toggle)\n"); printf("d = down node (toggle)\n"); printf("i = interrupt node (toggle)\n"); + printf("a = show failed login attempts\n"); + printf("c = show connected clients\n"); printf("r = recycle servers (when not in use)\n"); printf("s = shutdown servers (when not in use)\n"); printf("t = terminate servers (immediately)\n"); printf("! = execute external command\n"); + printf("? = print this help information\n"); #if 0 /* to do */ printf("c# = chat with node #\n"); printf("s# = spy on node #\n"); #endif break; + default: + break; } lputs(LOG_INFO,""); /* redisplay prompt */ }