Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ecbf333
Fix response status
michaelortmann May 16, 2026
b0ee5e0
Fix use after free / fingerprint logging
michaelortmann May 17, 2026
f4ee142
Init function pointer to null_func instead of 0
michaelortmann May 17, 2026
b3a08ff
Removed comment, because connection is properly closed with SO_LINGER…
michaelortmann May 17, 2026
01e85f7
Write SSL_R_HTTP_REQUEST error msg only to webui ports
michaelortmann May 17, 2026
590675d
Write SSL_R_HTTP_REQUEST error msg only to webui ports
michaelortmann May 17, 2026
b566633
Merge remote-tracking branch 'upstream/develop' into webui.better
michaelortmann May 17, 2026
31f779f
killsock(): dont kill stdout, so that we keep stdout logging until th…
michaelortmann May 17, 2026
4e5065f
cleanup
michaelortmann May 17, 2026
76fc966
Revert "cleanup"
michaelortmann May 17, 2026
2c96b4b
Revert "killsock(): dont kill stdout, so that we keep stdout logging …
michaelortmann May 17, 2026
70b5c28
killsock(): dont kill stdout, so that we keep stdout logging until th…
michaelortmann May 17, 2026
8f1f62f
tputs(): Fix openssl error logging
michaelortmann May 17, 2026
c0c987f
tputs(): Fix memleak
michaelortmann May 18, 2026
d4cac15
More ERR_clear_error()
michaelortmann May 18, 2026
cecea56
Stop writing to sock after close notify during write and enhance logging
michaelortmann May 18, 2026
5883021
Fix fatal(): end all logging to dcc for killed sock
michaelortmann May 18, 2026
fa6d9ef
Fix memleak
michaelortmann May 18, 2026
16bdb16
Proper memleak fix
michaelortmann May 18, 2026
e30327f
Fix more issues
thommey May 23, 2026
610eba3
Fix compiler warning
thommey May 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/dcc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1284,7 +1284,10 @@ static void dcc_telnet(int idx, char *buf, int i)
return;
}
/* Buffer data received on this socket. */
sockoptions(sock, EGG_OPTION_SET, SOCK_BUFFER);
if (!strcmp(dcc[idx].nick, "(webui)"))
sockoptions(sock, EGG_OPTION_SET, SOCK_BUFFER | SOCK_WEBUI);
else
sockoptions(sock, EGG_OPTION_SET, SOCK_BUFFER);

if (port < 1024) {
putlog(LOG_BOTS, "*", DCC_BADSRC, iptostr(&dcc[i].sockname.addr.sa), port);
Expand Down Expand Up @@ -1343,14 +1346,15 @@ void dcc_telnet_hostresolved2(int i, int idx) {
ident_target_port = getenv("EGGDROP_TEST") ? 1113 : 113;

snprintf(userhost, sizeof userhost, "telnet@%s", dcc[i].host);
changeover_dcc(i, &DCC_IDENTWAIT, 0);

/* Skip ident lookup if disabled */
if (identtimeout <= 0) {
dcc[i].u.ident_sock = dcc[idx].sock;
dcc_telnet_got_ident(i, userhost);
return;
}

changeover_dcc(i, &DCC_IDENTWAIT, 0);
dcc[i].timeval = now;
dcc[i].u.ident_sock = dcc[idx].sock;
sock = -1;
Expand Down
18 changes: 18 additions & 0 deletions src/dccutil.c
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,18 @@ void lostdcc(int n)
dcc[n].type = &DCC_LOST;
}

/* Mark an entry as lost, to be reaped in the mainloop asynchronously.
* Avoids reentrancy issues
*/
void lostdcc_deferred(int n)
{
/* sanity check */
if (n < 0 || n >= max_dcc) {
return;
}
dcc[n].status |= STAT_LOSTDCC;
}

/* Remove entry from dcc list. Think twice before using this function,
* because it invalidates any variables that point to a specific dcc
* entry!
Expand Down Expand Up @@ -385,6 +397,12 @@ void dcc_remove_lost(void)
dcc[i].sock = -1;
removedcc(i);
i--;
} else if (dcc[i].status & STAT_LOSTDCC) {
/* intentionally after DCC_LOST cleanup,
* so it doesn't clean immediately
*/
dcc[i].status &= ~STAT_LOSTDCC;
lostdcc(i);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/eggdrop.h
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,7 @@ struct dupwait_info {
#define STAT_PAGE 0x00080 /* page output to the user */
#define STAT_SERV 0x00100 /* this is a server connection */
#define STAT_WS 0x00200 /* webui websocket */
#define STAT_LOSTDCC 0x00400 /* closed by remote, call lostdcc() */

/* For stripping out mIRC codes. */
#define STRIP_COLOR 0x00001 /* remove mIRC color codes */
Expand Down Expand Up @@ -594,7 +595,8 @@ typedef struct {
#define SOCK_VIRTUAL 0x0200 /* not-connected socket (dont read it!) */
#define SOCK_BUFFER 0x0400 /* buffer data; don't notify dcc funcs */
#define SOCK_TCL 0x0800 /* tcl socket, don't do anything on it */
#define SOCK_WS 0x1000 /* webui websocket */
#define SOCK_WEBUI 0x1000 /* webui websocket pre-upgrade */
#define SOCK_WS 0x2000 /* webui websocket after framed upgrade */

/* Flags to sock_has_data
*/
Expand Down
4 changes: 3 additions & 1 deletion src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,10 @@ void fatal(const char *s, int recoverable)

putlog(LOG_MISC, "*", "* %s", s);
for (i = 0; i < dcc_total; i++)
if (dcc[i].sock >= 0)
if (dcc[i].sock >= 0) {
killsock(dcc[i].sock);
dcc[i].sock = -1;
}
#ifdef TLS
ssl_cleanup();
#endif
Expand Down
6 changes: 3 additions & 3 deletions src/modules.c
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,9 @@ int (*rfc_toupper) (int) = _rfc_toupper;
int (*rfc_tolower) (int) = _rfc_tolower;
void (*dns_hostbyip) (sockname_t *) = core_dns_hostbyip;
void (*dns_ipbyhost) (char *) = core_dns_ipbyhost;
void (*webui_dcc_telnet_hostresolved) (int, int) = 0;
size_t (*webui_frame) (char **, char *, size_t) = 0;
void (*webui_unframe) (int, char *, int *) = 0;
void (*webui_dcc_telnet_hostresolved) (int, int) = (void (*)(int, int)) null_func;
size_t (*webui_frame) (char **, char *, size_t) = (size_t (*)(char **, char * ,size_t)) null_func;
void (*webui_unframe) (int, char *, int *) = (void (*)(int, char *, int *)) null_func;

module_entry *module_list;
dependancy *dependancy_list = NULL;
Expand Down
14 changes: 10 additions & 4 deletions src/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,8 @@ void killsock(int sock)
int i;
struct threaddata *td = threaddata();

/* Ignore invalid sockets. */
if (sock < 0)
/* Ignore invalid sockets and stdout/stderr. */
if ((sock < 0) || (sock == STDOUT) || (sock == STDERR))
return;

for (i = 0; i < td->MAXSOCKS; i++) {
Expand Down Expand Up @@ -1366,12 +1366,17 @@ void tputs(int z, char *s, unsigned int len)
else
len = webui_frame(&s2, s, len);
if (socklist[i].ssl) {
ERR_clear_error();
x = SSL_write(socklist[i].ssl, s2, len);
if (x < 0) {
int err = SSL_get_error(socklist[i].ssl, x);
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ)
if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ) {
errno = EAGAIN;
else if (!inhere) { /* Out there, somewhere */
} else if (err == SSL_ERROR_ZERO_RETURN) {
/* Peer sent close notify, lostdcc_deferred() was already
* scheduled from ssl_info(). Don't queue more data. */
return;
} else if (!inhere) { /* Out there, somewhere */
inhere = 1;
debug1("tputs(): SSL error = %s",
ERR_error_string(ERR_get_error(), 0));
Expand Down Expand Up @@ -1469,6 +1474,7 @@ void dequeue_sockets()
errno = 0;
#ifdef TLS
if (socklist[i].ssl) {
ERR_clear_error();
x = SSL_write(socklist[i].ssl, socklist[i].handler.sock.outbuf,
socklist[i].handler.sock.outbuflen);
if (x < 0) {
Expand Down
1 change: 1 addition & 0 deletions src/proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ extern void (*sharein) (int, char *);
void chanout_but(int x, int chan, const char *format, ...) ATTRIBUTE_FORMAT(printf,3,4);
void dcc_chatter(int);
void lostdcc(int);
void lostdcc_deferred(int);
void killtransfer(int);
void removedcc(int);
void makepass(char *);
Expand Down
59 changes: 28 additions & 31 deletions src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,21 +136,19 @@ static X509 *ssl_getcert(int sock)
* Return value: ptr to the hexadecimal representation of the fingerprint or
* NULL in case of error.
*/
static char *ssl_getfp_from_cert(X509 *cert)
static char *ssl_getfp_from_cert(X509 *cert, const EVP_MD *type)
{
char *p;
unsigned int i;
static char fp[SHA_DIGEST_LENGTH * 3];
unsigned char md[SHA_DIGEST_LENGTH];
static char fp[EVP_MAX_MD_SIZE * 3];
unsigned char md[EVP_MAX_MD_SIZE];

if (!X509_digest(cert, EVP_sha1(), md, &i)) {
if (!X509_digest(cert, type, md, &i)) {
putlog(LOG_MISC, "*", "ERROR: TLS: ssl_getfp_from_cert(): X509_digest()");
X509_free(cert);
return NULL;
}
if (!(p = OPENSSL_buf2hexstr(md, i))) {
putlog(LOG_MISC, "*", "ERROR: TLS: ssl_getfp_from_cert(): OPENSSL_buf2hexstr()");
X509_free(cert);
return NULL;
}
strlcpy(fp, p, sizeof fp);
Expand All @@ -173,15 +171,17 @@ char *ssl_getfp(int sock)

if (!(cert = ssl_getcert(sock)))
return NULL;
fp = ssl_getfp_from_cert(cert);
fp = ssl_getfp_from_cert(cert, EVP_sha1());
#if OPENSSL_VERSION_NUMBER < 0x30000000L /* 3.0.0 */
X509_free(cert);
#endif
return fp;
}

// FIXME: Assumption is fingerprint stays the same if path doesn't change
void verify_cert_expiry(int idx) {
X509 *x509;
static char last_tls_certfile[sizeof tls_certfile];
#if OPENSSL_VERSION_NUMBER >= 0x10002000L /* 1.0.2 */
x509 = SSL_CTX_get0_certificate(ssl_ctx); /* The returned pointer must not be freed by the caller. */
#else
Expand All @@ -191,6 +191,12 @@ void verify_cert_expiry(int idx) {
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
#endif
if (x509) {
if (strcmp(tls_certfile, last_tls_certfile)) {
const char *fp = ssl_getfp_from_cert(x509, EVP_sha256());
putlog(LOG_MISC, "*", "Certificate loaded: %s (sha256 fingerprint %s)",
tls_certfile, fp ? fp : "(error getting fp)");
strlcpy(last_tls_certfile, tls_certfile, sizeof last_tls_certfile);
}
#if OPENSSL_VERSION_NUMBER >= 0x40000000L /* 4.0.0 */
int e;
if (!X509_check_certificate_times(NULL, x509, &e) && (e == X509_V_ERR_CERT_HAS_EXPIRED)) {
Expand Down Expand Up @@ -264,23 +270,6 @@ int ssl_init()
tls_certfile, ERR_error_string(ERR_get_error(), NULL));
fatal("Unable to load TLS certificate (ssl-certificate config setting)!", 0);
}

/* TODO: sha256 fingerprint
* print this fingerprint to every user / every partyline login
* maybe only print it when webui is enabled
* compatibility to older openssl is possible, similar to #1411
* this functionality could be sepped into a side-PR
* or functionality of #1411 can be reused once merged
*
* for now, just disable the fingerprint for openssl < 1.0.2
*/
#if OPENSSL_VERSION_NUMBER >= 0x10002000L /* 1.0.2 */
// TODO: ssl_getfp_from_cert() must not free the cert from SSL_CTX_get0_certificate()
putlog(LOG_MISC, "*", "Certificate loaded: %s (sha1 fingerprint %s)",
tls_certfile,
ssl_getfp_from_cert(SSL_CTX_get0_certificate(ssl_ctx)));
#endif

verify_cert_expiry(0);
if (SSL_CTX_use_PrivateKey_file(ssl_ctx, tls_keyfile, SSL_FILETYPE_PEM) != 1) {
putlog(LOG_MISC, "*", "ERROR: TLS: unable to load private key from %s: %s",
Expand Down Expand Up @@ -950,9 +939,17 @@ static void ssl_info(const SSL *ssl, int where, int ret)
!strcmp(SSL_alert_desc_string(ret), "RO"))
putlog(LOG_MISC, "*", "TLS: Long TLSCiphertext field received, connection failed. Is this really a TLS port?");
} else {
/* Ignore close notify warnings */
debug1("TLS: Received close notify during %s",
(where & SSL_CB_READ) ? "read" : "write");
/* Ignore close notify warnings and stop writing to sock */
sock = SSL_get_fd(ssl);
debug2("TLS: Received close notify during %s sock %i",
(where & SSL_CB_READ) ? "read" : "write", sock);
if (where & SSL_CB_WRITE) {
int i = findsock(sock);
if (i >= 0 && (threaddata()->socklist[i].flags & SOCK_WEBUI)) {
int idx = findidx(sock);
lostdcc_deferred(idx);
}
}
}
} else if (where & SSL_CB_EXIT) {
/* SSL_CB_EXIT may point to soft error for non-blocking! */
Expand Down Expand Up @@ -1097,15 +1094,16 @@ int ssl_handshake(int sock, int flags, int verify, int loglevel, char *host,
return 0;
}
if ((err = ERR_peek_error())) {
if (ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL &&
if ((td->socklist[i].flags & SOCK_WEBUI) &&
ERR_GET_LIB(ERR_peek_error()) == ERR_LIB_SSL &&
ERR_GET_REASON(err) == SSL_R_HTTP_REQUEST) {
/* We dont have access to real port, host or dcc information here */
putlog(LOG_MISC, "*", "TLS: error: HTTP request received on an SSL port");
int j;
char *response;
char *body = "Error: HTTP request received on an SSL port, please try HTTPS";
j = snprintf(NULL, 0,
"HTTP/1.1 200 \r\n" /* textual phrase is OPTIONAL */
"HTTP/1.1 400 \r\n" /* textual phrase is OPTIONAL */
"Content-Length: %zu\r\n"
"Content-Type: text/plain; charset=utf-8\r\n"
"Server: %s\r\n"
Expand All @@ -1114,7 +1112,7 @@ int ssl_handshake(int sock, int flags, int verify, int loglevel, char *host,
body);
response = nmalloc(j + 1);
sprintf(response,
"HTTP/1.1 200 \r\n" /* textual phrase is OPTIONAL */
"HTTP/1.1 400 \r\n" /* textual phrase is OPTIONAL */
"Content-Length: %zu\r\n"
"Content-Type: text/plain; charset=utf-8\r\n"
"Server: %s\r\n"
Expand All @@ -1123,7 +1121,6 @@ int ssl_handshake(int sock, int flags, int verify, int loglevel, char *host,
body);
if (write(sock, response, j) < 0) /* tputs() cannot be used here */
putlog(LOG_MISC, "*", "TLS: error: write(sock %i): %s", sock, strerror(errno));
// TODO: after reading of remaining bytes / ssl shutdown ?
nfree(response);
} else {
putlog(data->loglevel, "*",
Expand Down
Loading