/* Return a free entry in the socket entry for a tcl socket * * alloctclsock() can be called by Tcl threads */ int alloctclsock(register int sock, int mask, Tcl_FileProc *proc, ClientData cd) { int f = -1; register int i; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) { if (td->socklist[i].flags & SOCK_UNUSED) { if (f == -1) f = i; } else if ((td->socklist[i].flags & SOCK_TCL) && td->socklist[i].sock == sock) { f = i; break; } } if (f != -1) { td->socklist[f].sock = sock; td->socklist[f].flags = SOCK_TCL; td->socklist[f].handler.tclsock.mask = mask; td->socklist[f].handler.tclsock.proc = proc; td->socklist[f].handler.tclsock.cd = cd; return f; } /* Try again if enlarging socketlist works */ if (increase_socks_max()) return -1; else return alloctclsock(sock, mask, proc, cd); }
/* Get the certificate, corresponding to the connection * identified by sock. * * Return value: pointer to a X509 certificate or NULL if we couldn't * look up the certificate. */ static X509 *ssl_getcert(int sock) { int i; struct threaddata *td = threaddata(); i = findsock(sock); if (i == -1 || !td->socklist[i].ssl) return NULL; return SSL_get_peer_certificate(td->socklist[i].ssl); }
/* Count allocated memory for SSL. This excludes memory allocated by OpenSSL's * family of malloc functions. */ int expmem_tls() { int i, tot; struct threaddata *td = threaddata(); /* currently it's only the appdata structs allocated by ssl_handshake() */ for (i = 0, tot = 0; i < td->MAXSOCKS; i++) if (!(td->socklist[i].flags & (SOCK_UNUSED | SOCK_TCL))) if (td->socklist[i].ssl && SSL_get_app_data(td->socklist[i].ssl)) tot += sizeof(ssl_appdata); return tot; }
int expmem_dccutil() { int tot, i; tot = sizeof(struct dcc_t) * max_dcc; tot += sizeof(sock_list) * threaddata()->MAXSOCKS; for (i = 0; i < dcc_total; i++) { if (dcc[i].type && dcc[i].type->expmem) tot += dcc[i].type->expmem(dcc[i].u.other); } return tot; }
int expmem_net() { int i, tot = 0; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) { if (!(td->socklist[i].flags & (SOCK_UNUSED | SOCK_TCL))) { if (td->socklist[i].handler.sock.inbuf != NULL) tot += strlen(td->socklist[i].handler.sock.inbuf) + 1; if (td->socklist[i].handler.sock.outbuf != NULL) tot += td->socklist[i].handler.sock.outbuflen; } } return tot; }
/* Done with a tcl socket * * killtclsock() can be called by Tcl threads */ void killtclsock(register int sock) { register int i; struct threaddata *td = threaddata(); if (sock < 0) return; for (i = 0; i < td->MAXSOCKS; i++) { if ((td->socklist[i].flags & SOCK_TCL) && td->socklist[i].sock == sock) { td->socklist[i].flags = SOCK_UNUSED; return; } } }
/* Sets/Unsets options for a specific socket. * * Returns: 0 - on success * -1 - socket not found * -2 - illegal operation */ int sockoptions(int sock, int operation, int sock_options) { int i; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) if ((td->socklist[i].sock == sock) && !(td->socklist[i].flags & SOCK_UNUSED)) { if (operation == EGG_OPTION_SET) td->socklist[i].flags |= sock_options; else if (operation == EGG_OPTION_UNSET) td->socklist[i].flags &= ~sock_options; else return -2; return 0; } return -1; }
/* Return a free entry in the socket entry */ int allocsock(int sock, int options) { int i; struct threaddata *td = threaddata(); for (i = 0; i < td->MAXSOCKS; i++) { if (td->socklist[i].flags & SOCK_UNUSED) { /* yay! there is table space */ td->socklist[i].handler.sock.inbuf = NULL; td->socklist[i].handler.sock.outbuf = NULL; td->socklist[i].handler.sock.inbuflen = 0; td->socklist[i].handler.sock.outbuflen = 0; td->socklist[i].flags = options; td->socklist[i].sock = sock; return i; } } /* Try again if enlarging socketlist works */ if (increase_socks_max()) return -1; else return allocsock(sock, options); }
/* This function is called to enlarge the static sockettable in a thread. * It keeps the mainthread dcc table enlarging with the main thread sockettable * If this fails because the upper limit max_socks is reached, -1 is returned. * If this was called from the main thread, it updates the socklist variable * * increase_socks_max() can be called by Tcl threads */ int increase_socks_max() { struct threaddata *td = threaddata(); int osock = td->MAXSOCKS; if (max_socks < 1) max_socks = 1; if (td->MAXSOCKS == max_socks) { putlog(LOG_MISC, "*", "Maximum socket limit reached. Consider raising max-socks."); return -1; } td->MAXSOCKS += 10; if (td->MAXSOCKS > max_socks) td->MAXSOCKS = max_socks; if (td->socklist) td->socklist = nrealloc(td->socklist, sizeof(sock_list) * td->MAXSOCKS); else td->socklist = nmalloc(sizeof(sock_list) * td->MAXSOCKS); for (; osock < td->MAXSOCKS; osock++) td->socklist[osock].flags = SOCK_UNUSED; if (td->mainthread) { max_dcc = td->MAXSOCKS - 10; if (max_dcc < 1) max_dcc = 1; if (dcc) dcc = nrealloc(dcc, sizeof(struct dcc_t) * max_dcc); else dcc = nmalloc(sizeof(struct dcc_t) * max_dcc); socklist = td->socklist; } return 0; }
/* Request a normal socket for i/o */ void setsock(int sock, int options) { int i = allocsock(sock, options), parm; struct threaddata *td = threaddata(); if (i == -1) { putlog(LOG_MISC, "*", "Sockettable full."); return; } if (((sock != STDOUT) || backgrd) && !(td->socklist[i].flags & SOCK_NONSOCK)) { parm = 1; setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *) &parm, sizeof(int)); parm = 0; setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &parm, sizeof(int)); } if (options & SOCK_LISTEN) { /* Tris says this lets us grab the same port again next time */ parm = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &parm, sizeof(int)); } /* Yay async i/o ! */ fcntl(sock, F_SETFL, O_NONBLOCK); }
/* Dump status info out to dcc */ void tell_verbose_status(int idx) { char s[256], s1[121], s2[81]; char *vers_t, *uni_t; int i; time_t now2 = now - online_since, hr, min; float cputime; #ifdef HAVE_UNAME struct utsname un; if (uname(&un) < 0) { #endif vers_t = " "; uni_t = "*unknown*"; #ifdef HAVE_UNAME } else { vers_t = un.release; uni_t = un.sysname; } #endif i = count_users(userlist); dprintf(idx, "I am %s, running %s: %d user%s (mem: %uk).\n", botnetnick, ver, i, i == 1 ? "" : "s", (int) (expected_memory() / 1024)); s[0] = 0; if (now2 > 86400) { /* days */ sprintf(s, "%d day", (int) (now2 / 86400)); if ((int) (now2 / 86400) >= 2) strcat(s, "s"); strcat(s, ", "); now2 -= (((int) (now2 / 86400)) * 86400); } hr = (time_t) ((int) now2 / 3600); now2 -= (hr * 3600); min = (time_t) ((int) now2 / 60); sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min); s1[0] = 0; if (backgrd) strcpy(s1, MISC_BACKGROUND); else { if (term_z) strcpy(s1, MISC_TERMMODE); else if (con_chan) strcpy(s1, MISC_STATMODE); else strcpy(s1, MISC_LOGMODE); } cputime = getcputime(); if (cputime < 0) sprintf(s2, "CPU: unknown"); else { hr = cputime / 60; cputime -= hr * 60; sprintf(s2, "CPU: %02d:%05.2f", (int) hr, cputime); /* Actally min/sec */ } dprintf(idx, "%s %s (%s) - %s - %s: %4.1f%%\n", MISC_ONLINEFOR, s, s1, s2, MISC_CACHEHIT, 100.0 * ((float) cache_hit) / ((float) (cache_hit + cache_miss))); dprintf(idx, "Configured with: " EGG_AC_ARGS "\n"); if (admin[0]) dprintf(idx, "Admin: %s\n", admin); dprintf(idx, "Config file: %s\n", configfile); dprintf(idx, "OS: %s %s\n", uni_t, vers_t); dprintf(idx, "Process ID: %d (parent %d)\n", getpid(), getppid()); /* info library */ dprintf(idx, "%s %s\n", MISC_TCLLIBRARY, ((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ? tcl_resultstring() : "*unknown*"); /* info tclversion/patchlevel */ dprintf(idx, "%s %s (%s %s)\n", MISC_TCLVERSION, ((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ? tcl_resultstring() : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ? tcl_resultstring() : "*unknown*", MISC_TCLHVERSION, TCL_PATCH_LEVEL ? TCL_PATCH_LEVEL : "*unknown*"); if (tcl_threaded()) dprintf(idx, "Tcl is threaded.\n"); #ifdef TLS dprintf(idx, "TLS support is enabled.\n"); dprintf(idx, "TLS library: %s\n", SSLeay_version(SSLEAY_VERSION)); #else dprintf(idx, "TLS support is not available.\n"); #endif #ifdef IPV6 dprintf(idx, "IPv6 support is enabled.\n"); #else dprintf(idx, "IPv6 support is not available.\n"); #endif dprintf(idx, "Socket table: %d/%d\n", threaddata()->MAXSOCKS, max_socks); }
/* Dump status info out to dcc */ void tell_verbose_status(int idx) { char s[256], s1[121], s2[81]; char *vers_t, *uni_t; int i; time_t now2 = now - online_since, hr, min; #ifdef HAVE_GETRUSAGE struct rusage ru; #else # ifdef HAVE_CLOCK clock_t cl; # endif #endif #ifdef HAVE_UNAME struct utsname un; if (!uname(&un) < 0) { #endif vers_t = " "; uni_t = "*unknown*"; #ifdef HAVE_UNAME } else { vers_t = un.release; uni_t = un.sysname; } #endif i = count_users(userlist); dprintf(idx, "I am %s, running %s: %d user%s (mem: %uk).\n", botnetnick, ver, i, i == 1 ? "" : "s", (int) (expected_memory() / 1024)); #ifdef IPV6 dprintf(idx, "Unofficial IPv6 patch for v1.6.21 based on the v1.6.18 IPv6 patch \n"); #endif s[0] = 0; if (now2 > 86400) { /* days */ sprintf(s, "%d day", (int) (now2 / 86400)); if ((int) (now2 / 86400) >= 2) strcat(s, "s"); strcat(s, ", "); now2 -= (((int) (now2 / 86400)) * 86400); } hr = (time_t) ((int) now2 / 3600); now2 -= (hr * 3600); min = (time_t) ((int) now2 / 60); sprintf(&s[strlen(s)], "%02d:%02d", (int) hr, (int) min); s1[0] = 0; if (backgrd) strcpy(s1, MISC_BACKGROUND); else { if (term_z) strcpy(s1, MISC_TERMMODE); else if (con_chan) strcpy(s1, MISC_STATMODE); else strcpy(s1, MISC_LOGMODE); } #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &ru); hr = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) / 60); min = (int) ((ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) - (hr * 60)); sprintf(s2, "CPU: %02d:%02d", (int) hr, (int) min); /* Actally min/sec */ #else # ifdef HAVE_CLOCK cl = (clock() / CLOCKS_PER_SEC); hr = (int) (cl / 60); min = (int) (cl - (hr * 60)); sprintf(s2, "CPU: %02d:%02d", (int) hr, (int) min); /* Actually min/sec */ # else sprintf(s2, "CPU: unknown"); # endif #endif dprintf(idx, "%s %s (%s) - %s - %s: %4.1f%%\n", MISC_ONLINEFOR, s, s1, s2, MISC_CACHEHIT, 100.0 * ((float) cache_hit) / ((float) (cache_hit + cache_miss))); dprintf(idx, "Configured with: " EGG_AC_ARGS "\n"); if (admin[0]) dprintf(idx, "Admin: %s\n", admin); dprintf(idx, "Config file: %s\n", configfile); dprintf(idx, "OS: %s %s\n", uni_t, vers_t); /* info library */ dprintf(idx, "%s %s\n", MISC_TCLLIBRARY, ((interp) && (Tcl_Eval(interp, "info library") == TCL_OK)) ? tcl_resultstring() : "*unknown*"); /* info tclversion/patchlevel */ dprintf(idx, "%s %s (%s %s)\n", MISC_TCLVERSION, ((interp) && (Tcl_Eval(interp, "info patchlevel") == TCL_OK)) ? tcl_resultstring() : (Tcl_Eval(interp, "info tclversion") == TCL_OK) ? tcl_resultstring() : "*unknown*", MISC_TCLHVERSION, TCL_PATCH_LEVEL ? TCL_PATCH_LEVEL : "*unknown*"); if (tcl_threaded()) dprintf(idx, "Tcl is threaded.\n"); dprintf(idx, "Socket table: %d/%d\n", threaddata()->MAXSOCKS, max_socks); }
/* Switch a socket to SSL communication * * Creates a SSL data structure for the connection; * Sets up callbacks and initiates a SSL handshake with the peer; * Reports error conditions and performs cleanup upon failure. * * flags: ssl flags, i.e connect or listen * verify: peer certificate verification flags * loglevel: is the level to output information about the connection * and certificates. * host: contains the dns name or ip address of the peer. Used for * verification. * cb: optional callback, this function will be called after the * handshake completes. * * Return value: 0 on success, !=0 on failure. */ int ssl_handshake(int sock, int flags, int verify, int loglevel, char *host, IntFunc cb) { int i, err, ret; ssl_appdata *data; struct threaddata *td = threaddata(); debug0("TLS: attempting SSL negotiation..."); if (!ssl_ctx && ssl_init()) { debug0("TLS: Failed. OpenSSL not initialized properly."); return -1; } /* find the socket in the list */ i = findsock(sock); if (i == -1) { debug0("TLS: socket not in socklist"); return -2; } if (td->socklist[i].ssl) { debug0("TLS: handshake not required - SSL session already established"); return 0; } td->socklist[i].ssl = SSL_new(ssl_ctx); if (!td->socklist[i].ssl || !SSL_set_fd(td->socklist[i].ssl, td->socklist[i].sock)) { debug1("TLS: cannot initiate SSL session - %s", ERR_error_string(ERR_get_error(), 0)); return -3; } /* Prepare a ssl appdata struct for the verify callback */ data = nmalloc(sizeof(ssl_appdata)); egg_bzero(data, sizeof(ssl_appdata)); data->flags = flags & (TLS_LISTEN | TLS_CONNECT); data->verify = flags & ~(TLS_LISTEN | TLS_CONNECT); data->loglevel = loglevel; data->cb = cb; strncpyz(data->host, host ? host : "", sizeof(data->host)); SSL_set_app_data(td->socklist[i].ssl, data); SSL_set_info_callback(td->socklist[i].ssl, (void *) ssl_info); /* We set this +1 to be able to report extra long chains properly. * Otherwise, OpenSSL will break the verification reporting about * missing certificates instead. The rest of the fix is in * ssl_verify() */ SSL_set_verify_depth(td->socklist[i].ssl, tls_maxdepth + 1); SSL_set_mode(td->socklist[i].ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); if (data->flags & TLS_CONNECT) { SSL_set_verify(td->socklist[i].ssl, SSL_VERIFY_PEER, ssl_verify); ret = SSL_connect(td->socklist[i].ssl); if (!ret) debug0("TLS: connect handshake failed."); } else { if (data->flags & TLS_VERIFYPEER) SSL_set_verify(td->socklist[i].ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify); else SSL_set_verify(td->socklist[i].ssl, SSL_VERIFY_PEER, ssl_verify); ret = SSL_accept(td->socklist[i].ssl); if (!ret) debug0("TLS: accept handshake failed"); } err = SSL_get_error(td->socklist[i].ssl, ret); /* Normal condition for async I/O, similar to EAGAIN */ if (ret > 0 || err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) { debug0("TLS: handshake in progress"); return 0; } if (ERR_peek_error()) debug0("TLS: handshake failed due to the following errors: "); while ((err = ERR_get_error())) debug1("TLS: %s", ERR_error_string(err, NULL)); /* Attempt failed, cleanup and abort */ SSL_shutdown(td->socklist[i].ssl); SSL_free(td->socklist[i].ssl); td->socklist[i].ssl = NULL; nfree(data); return -4; }