From: Egor Date: Wed, 11 May 2022 09:34:23 +0000 (+0300) Subject: add tls support X-Git-Url: https://git.patchysicp.xyz/?a=commitdiff_plain;h=abe5da48e644c3490747ef533adcf5980157178d;p=patchyircd.git add tls support --- diff --git a/.gitignore b/.gitignore index 5c04fc5..d6975e7 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ setpass ops.txt init.txt chanlog.txt +cert.pem +key.pem diff --git a/common.h b/common.h index a1b0fb5..31d4ea1 100644 --- a/common.h +++ b/common.h @@ -4,6 +4,11 @@ #include "config.h" #include "trie.h" #include +#ifdef HAS_TLS +#include +#else +struct tls; +#endif #define SETBIT(var, bit, val) ((void)((val) ? ((var)|=(bit)) : ((var)&=~(bit)))) #define UMODE_INVISIBLE 1 @@ -11,11 +16,13 @@ #define UMODE_LOPER 4 #define UMODE_REGISTERED 8 #define UMODE_WALLOPS 16 +#define UMODE_SECURE 32 #define REG_INIT 0 #define REG_CAPLS 1 #define REG_REGISTERED 2 struct client_data { SOCKET FD; + struct tls *cctx; char type; char *nick; char *key; // case-mapped nick @@ -25,9 +32,10 @@ struct client_data { char *away; char *nick_reg; char *pass_reg; + char *peername; + unsigned short peerport; char buf[2048]; size_t bufLen; - size_t bufCap; char umode; char regstate; struct trie_node *channels; @@ -70,4 +78,9 @@ extern struct sockaddr_in6 serverAddrV6; extern size_t serverAddrV4len; extern size_t serverAddrV6len; +#ifndef _MSC_VER +#pragma GCC poison recv_log +#pragma GCC poison send_log +#endif + #endif diff --git a/config.example.mk b/config.example.mk index 686ee88..9bb7a5e 100644 --- a/config.example.mk +++ b/config.example.mk @@ -1,6 +1,7 @@ HEADERS+=config.mk CC=clang -CFLAGS=-Wall -Wextra +CFLAGS=-Wall -Wextra -DHAS_TLS +LDLIBS=-ltls CC.w32=i686-w64-mingw32-gcc CFLAGS.w32=-Wall -Wextra -DI_KNOW_HOW_TO_USE_A_LINKER -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 LDLIBS.w32=-lws2_32 diff --git a/config.h b/config.h index 36d19ae..45ff1f9 100644 --- a/config.h +++ b/config.h @@ -1,4 +1,5 @@ #define LISTENING_PORT 6667 +#define LISTENING_PORT_TLS 6697 #define LISTENING_BACKLOG 10 #define CLIENTS 30 #define SUPPORT_IPV4 diff --git a/irc.c b/irc.c index 0f6658d..5664232 100644 --- a/irc.c +++ b/irc.c @@ -10,7 +10,7 @@ struct trie_node *channel_map = NULL; const char *hostname = "irc.patchysicp.xyz"; #define is_registered(cl) ((cl)->regstate == REG_REGISTERED) void sendbuf(struct client_data *client) { - send_log(client->FD, databuf.buf, databuf.len, 0); + sendClient(client, databuf.buf, databuf.len); } void chanlog_time(time_t timestamp) { extern FILE *fchanlog; @@ -408,6 +408,7 @@ int processPacket(struct client_data *client, char *source, char *command, int a if (cl->umode & UMODE_LOPER) append_str(&databuf, "O"); if (cl->umode & UMODE_REGISTERED) append_str(&databuf, "r"); if (cl->umode & UMODE_WALLOPS) append_str(&databuf, "w"); + if (cl->umode & UMODE_SECURE) append_str(&databuf, "Z"); append_str(&databuf, "\r\n"); sendbuf(client); } else { diff --git a/main.c b/main.c index 82e0c88..e6f8262 100644 --- a/main.c +++ b/main.c @@ -80,14 +80,108 @@ fd_set readfds; size_t serverAddrV6len; #endif FILE *fchanlog = NULL; -int main() { + +SOCKET create_listen_sock(int af, struct sockaddr *addr, size_t len) +{ + SOCKET sock; + // Create socket + sock = socket(af, SOCK_STREAM, 0); + if(sock == (SOCKET)INVALID_SOCKET) { + net_perror("ERROR SOCKET"); + exit(EXIT_FAILURE); + } + // Set socket options int opt = 1; - SOCKET serverSocketV4 = 0; - SOCKET serverSocketV6 = 0; - SOCKET highestFD; - SOCKET SD; - int i; - char client_addr[100]; + if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*)&opt, sizeof(opt)) == -1) { + net_perror("ERROR SOCKET"); + exit(EXIT_FAILURE); + } +#ifdef SUPPORT_IPV6 + // IPV6 Only + if(af == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&opt, sizeof(opt)) == -1) { + net_perror("ERROR SOCKET IPV6"); + exit(EXIT_FAILURE); + } +#endif + // Set nonblock + net_nonblock(sock); + // Bind to socket + if(bind(sock, addr, len) == -1) { + net_perror("ERROR BIND"); + exit(EXIT_FAILURE); + } + // Listen to socket + if(listen(sock, LISTENING_BACKLOG) == -1) { + net_perror("ERROR LISTEN"); + exit(EXIT_FAILURE); + } + return sock; +} + +void accept_from_sock(SOCKET serverSocket, struct tls *ctx) +{ + while (true) { + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + SOCKET newSocket = accept(serverSocket, (struct sockaddr *)&addr, &len); + if (newSocket == (SOCKET)INVALID_SOCKET) { + if (!net_wouldblock()) + net_perror("ERROR newSocket"); + break; + } + // Set nonblock + net_nonblock(newSocket); + + char client_addr[100]; + int port; + if (addr.ss_family == AF_INET) { + inet_ntop(AF_INET, &((struct sockaddr_in*)&addr)->sin_addr, client_addr, sizeof(client_addr)); + port = ntohs(((struct sockaddr_in*)&addr)->sin_port); + } else if (addr.ss_family == AF_INET6) { + inet_ntop(AF_INET6, &((struct sockaddr_in6*)&addr)->sin6_addr, client_addr, sizeof(client_addr)); + port = ntohs(((struct sockaddr_in6*)&addr)->sin6_port); + } else { + strcpy(client_addr, "?"); + port = 0; + } + printf("Connection accepted from Socket: %"PRISocket", IP: %s PORT: %d\n", newSocket, + client_addr, port); + fflush(stdout); + struct tls *cctx = NULL; + if (ctx) { +#ifdef HAS_TLS + if (tls_accept_socket(ctx, &cctx, newSocket) == -1) { + close(newSocket); + fprintf(stderr, "tls_accept_socket: %s\n", tls_error(ctx)); + continue; + } +#endif + } + // Add to list of sockets + for(int i=0;iFD = newSocket; + clients[i]->cctx = cctx; + clients[i]->type = addr.ss_family == AF_INET6 ? 6 : 4; + clients[i]->peername = mystrdup(client_addr); + if (!clients[i]->peername) abort(); + clients[i]->peerport = port; + if (cctx) + clients[i]->umode |= UMODE_SECURE; + extern const char *hostname; + copy_strf(&databuf, ":%s NOTICE * :Hello welcome to PatchyIRCD!\r\n", hostname); + sendbuf(clients[i]); + break; + } + } + } +} + +int main() { + SOCKET listeners[4]; + struct tls *listeners_tls[4]; + int listener_count = 0; puts("[PatchyIRCD]: Starting up..."); fflush(stdout); @@ -106,6 +200,8 @@ int main() { serverAddrV4.sin_port = htons(LISTENING_PORT); serverAddrV4.sin_family = AF_INET; serverAddrV4len = sizeof(serverAddrV4); + listeners_tls[listener_count] = NULL; + listeners[listener_count++] = create_listen_sock(AF_INET, (struct sockaddr*)&serverAddrV4, sizeof(struct sockaddr_in)); #endif #ifdef SUPPORT_IPV6 memset(&serverAddrV6, 0, sizeof(struct sockaddr_in6)); @@ -114,76 +210,41 @@ int main() { serverAddrV6.sin6_port = htons(LISTENING_PORT); serverAddrV6.sin6_family = AF_INET6; serverAddrV6len = sizeof(serverAddrV6); + listeners_tls[listener_count] = NULL; + listeners[listener_count++] = create_listen_sock(AF_INET6, (struct sockaddr*)&serverAddrV6, sizeof(struct sockaddr_in6)); #endif +#ifdef HAS_TLS + struct tls_config *tls_cfg; + struct tls *tls_ctx; + if (tls_init() == -1) + return fprintf(stderr, "tls_init error\n"), 1; + if (!(tls_cfg = tls_config_new())) + return fprintf(stderr, "tls_config_new error\n"), 1; + if (tls_config_set_cert_file(tls_cfg, "cert.pem") == -1) + return fprintf(stderr, "tls_config_set_cert_file: %s\n", tls_config_error(tls_cfg)), 1; + if (tls_config_set_key_file(tls_cfg, "key.pem") == -1) + return fprintf(stderr, "tls_config_set_key_file: %s\n", tls_config_error(tls_cfg)), 1; + if (!(tls_ctx = tls_server())) + return fprintf(stderr, "tls_server error\n"), 1; + if (tls_configure(tls_ctx, tls_cfg) == -1) + return fprintf(stderr, "tls_configure: %s\n", tls_error(tls_ctx)), 1; + #ifdef SUPPORT_IPV4 - // Create socket - serverSocketV4 = socket(AF_INET, SOCK_STREAM, 0); - if(serverSocketV4 == (SOCKET)INVALID_SOCKET) { - net_perror("ERROR SOCKET IPV4"); - exit(EXIT_FAILURE); - } - // Set socket options - if(setsockopt(serverSocketV4, SOL_SOCKET,\ - SO_REUSEADDR,\ - (void*)&opt, sizeof(opt)) == -1) { - net_perror("ERROR SOCKET IPV4"); - exit(EXIT_FAILURE); - } - // Set nonblock - net_nonblock(serverSocketV4); - // Bind to socket - if(bind(serverSocketV4, (struct sockaddr*)&serverAddrV4,\ - sizeof(struct sockaddr_in)) == -1) { - net_perror("ERROR BIND IPV4"); - exit(EXIT_FAILURE); - } - // Listen to socket - if(listen(serverSocketV4, LISTENING_BACKLOG) == -1) { - net_perror("ERROR LISTEN IPV4"); - exit(EXIT_FAILURE); - } + serverAddrV4.sin_port = htons(LISTENING_PORT_TLS); + listeners_tls[listener_count] = tls_ctx; + listeners[listener_count++] = create_listen_sock(AF_INET, (struct sockaddr*)&serverAddrV4, sizeof(struct sockaddr_in)); #endif #ifdef SUPPORT_IPV6 - // Create socket - serverSocketV6 = socket(AF_INET6, SOCK_STREAM, 0); - if(serverSocketV6 == (SOCKET)INVALID_SOCKET) { - net_perror("ERROR SOCKET IPV6"); - exit(EXIT_FAILURE); - } - // Set socket options - if(setsockopt(serverSocketV6, SOL_SOCKET,\ - SO_REUSEADDR,\ - (void*)&opt, sizeof(opt)) == -1) { - net_perror("ERROR SOCKET IPV6"); - exit(EXIT_FAILURE); - } - // IPV6 Only - if(setsockopt(serverSocketV6, IPPROTO_IPV6,\ - IPV6_V6ONLY,\ - (void*)&opt, sizeof(opt)) == -1) { - net_perror("ERROR SOCKET IPV6"); - exit(EXIT_FAILURE); - } - // Set nonblock - net_nonblock(serverSocketV6); - // Bind to socket - if(bind(serverSocketV6, (struct sockaddr*)&serverAddrV6,\ - sizeof(struct sockaddr_in6)) == -1) { - net_perror("ERROR BIND IPV6"); - exit(EXIT_FAILURE); - } - // Listen to socket - if(listen(serverSocketV6, LISTENING_BACKLOG) == -1) { - net_perror("ERROR LISTEN IPV6"); - exit(EXIT_FAILURE); - } + serverAddrV6.sin6_port = htons(LISTENING_PORT_TLS); + listeners_tls[listener_count] = tls_ctx; + listeners[listener_count++] = create_listen_sock(AF_INET6, (struct sockaddr*)&serverAddrV6, sizeof(struct sockaddr_in6)); +#endif #endif FILE *f = fopen("init.txt", "rb"); if (f) { struct client_data *fake = allocClient(); - fake->bufCap = sizeof(fake->buf)-1; - fake->umode |= UMODE_OPER; + fake->umode |= UMODE_OPER | UMODE_SECURE; strcpy(fake->buf, "NICK :root\r\n"); fake->bufLen = strlen(fake->buf); parsePacket(fake); @@ -238,30 +299,28 @@ int main() { while(is_running){ FD_ZERO(&readfds); // Add listening SOCKETS to FDSET -#ifdef SUPPORT_IPV4 - FD_SET(serverSocketV4, &readfds); -#endif -#ifdef SUPPORT_IPV6 - FD_SET(serverSocketV6, &readfds); -#endif + for (int i = 0; i < listener_count; i++) + FD_SET(listeners[i], &readfds); #ifdef _WIN32 - // we don't need to do highestFD for this on, cuz it's windows only if (self_sock != INVALID_SOCKET) FD_SET(self_sock, &readfds); #endif + for (int i = 0; i < CLIENTS; i++) + if (clients[i]) + FD_SET(clients[i]->FD, &readfds); // Set highestFD - if(serverSocketV4 < serverSocketV6) highestFD = serverSocketV6; - else highestFD = serverSocketV4; - for(i=0;iFD, &readfds); - if(clients[i]->FD>highestFD){ +#ifdef _WIN32 + int highestFD = -1; +#else + SOCKET highestFD = -1; + for (int i = 0; i < listener_count; i++) + if (highestFD < listeners[i]) + highestFD = listeners[i]; + for (int i = 0; i < CLIENTS; i++) + if (clients[i] && highestFD < clients[i]->FD) highestFD = clients[i]->FD; - } - } - } - +#endif // Wait until activity on sockets if(select(highestFD+1,&readfds,NULL,NULL,NULL) == -1) { #ifndef _WIN32 @@ -279,79 +338,24 @@ int main() { #endif // Accept new connections - // TODO: Optimize this with goto -#ifdef SUPPORT_IPV4 - if (FD_ISSET(serverSocketV4, &readfds)) { - SOCKET newSocket = accept(serverSocketV4, NULL, NULL); - if(newSocket == (SOCKET)INVALID_SOCKET){ - net_perror("ERROR newSocket IPV4"); - } - getpeername(newSocket,(struct sockaddr*)&serverAddrV4,(socklen_t*)&serverAddrV4len); - inet_ntop(AF_INET, &(serverAddrV4.sin_addr), client_addr, sizeof(client_addr)); - printf("Connection accepted from Socket: %"PRISocket", IP: %s PORT: %d\n", newSocket, client_addr, ntohs(serverAddrV4.sin_port)); - fflush(stdout); - extern const char *hostname; - copy_strf(&databuf, ":%s NOTICE * :Hello welcome to PatchyIRCD!\r\n", hostname); - send_log(newSocket, databuf.buf, databuf.len, 0); - // Add to list of sockets - for(i=0;iFD = newSocket; - clients[i]->type = 4; - clients[i]->bufCap = sizeof(clients[i]->buf); - // Set nonblock - net_nonblock(clients[i]->FD); - break; - } - } - } -#endif -#ifdef SUPPORT_IPV6 - if (FD_ISSET(serverSocketV6, &readfds)) { - SOCKET newSocket = accept(serverSocketV6, NULL, NULL); - if(newSocket == (SOCKET)INVALID_SOCKET){ - net_perror("ERROR newSocket IPV6"); - } - getpeername(newSocket,(struct sockaddr*)&serverAddrV6,(socklen_t*)&serverAddrV6len); - inet_ntop(AF_INET6, &(serverAddrV6.sin6_addr), client_addr, sizeof(client_addr)); - printf("Connection accepted from Socket: %"PRISocket", IP: %s PORT: %d\n", newSocket, client_addr, ntohs(serverAddrV6.sin6_port)); - fflush(stdout); - extern const char *hostname; - copy_strf(&databuf, ":%s NOTICE * :Hello welcome to PatchyIRCD!\r\n", hostname); - send_log(newSocket, databuf.buf, databuf.len, 0); - // Add to list of sockets - for(i=0;iFD = newSocket; - clients[i]->type = 6; - clients[i]->bufCap = sizeof(clients[i]->buf); - // Set nonblock - net_nonblock(clients[i]->FD); - break; - } - } - } -#endif + for (int i = 0; i < listener_count; i++) + if (FD_ISSET(listeners[i], &readfds)) + accept_from_sock(listeners[i], listeners_tls[i]); // Respond to already connected clients - for(i=0;iFD; - if(FD_ISSET(SD, &readfds)) { + for (int i = 0; i < CLIENTS; i++) + if (clients[i] && FD_ISSET(clients[i]->FD, &readfds)) recvProc(clients[i]); - } - } } puts("[PatchyIRCD]: Cleaning up..."); fflush(stdout); - for(i=0;itype == 4) { - getpeername(cl->FD,(struct sockaddr*)&serverAddrV4,(socklen_t*)&serverAddrV4len); - inet_ntop(AF_INET, &(serverAddrV4.sin_addr), data, sizeof(data)); - printf("Client disconnected from Socket: %"PRISocket", IP: %s PORT: %d\n", cl->FD, data, ntohs(serverAddrV4.sin_port)); - fflush(stdout); - } -#endif -#ifdef SUPPORT_IPV6 - if(cl->type == 6) { - getpeername(cl->FD,(struct sockaddr*)&serverAddrV6,(socklen_t*)&serverAddrV6len); - inet_ntop(AF_INET6, &(serverAddrV6.sin6_addr), data, sizeof(data)); - printf("Client disconnected from Socket: %"PRISocket", IP: %s PORT: %d\n", cl->FD, data, ntohs(serverAddrV6.sin6_port)); + if (cl->peername) { + printf("Client disconnected from Socket: %"PRISocket", IP: %s PORT: %d\n", cl->FD, cl->peername, cl->peerport); fflush(stdout); } -#endif // Start GitHub Copilot if(!byChoice && cl->nick && cl->username) { msg_quit(cl, "", "Has left the building (Forced)"); @@ -44,6 +28,20 @@ void killConnection(struct client_data *cl, bool byChoice) { sendbuf(cl); // End GitHub Copilot if (cl->FD != INVALID_SOCKET) { +#ifdef HAS_TLS + if (cl->cctx) { + // FIXME: this blocks + int rv; + do { + rv = tls_close(cl->cctx); + } while (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT); + if (rv == -1) { + fprintf(stderr, "tls_close (%"PRISocket"): %s\n", cl->FD, tls_error(cl->cctx)); + } + tls_free(cl->cctx); + cl->cctx = NULL; + } +#endif shutdown(cl->FD, SHUT_RDWR); closesocket(cl->FD); } @@ -66,6 +64,7 @@ void killConnection(struct client_data *cl, bool byChoice) { if (cl->pass_reg) memset(cl->pass_reg, 0, strlen(cl->pass_reg)); free(cl->pass_reg); + free(cl->peername); free(cl); } @@ -75,20 +74,43 @@ void recvProc(struct client_data *cl) { int packetReturn; while(1){ // I hate you - if(cl->bufLen == cl->bufCap) { killConnection(cl, false); return; } - bytesRead = recv(cl->FD,cl->buf + cl->bufLen,cl->bufCap - cl->bufLen, 0); - switch(bytesRead){ - // Disconnect - case 0: - killConnection(cl, false); - return; - // Error - case -1: - if(!net_wouldblock()){ + if(cl->bufLen == sizeof(cl->buf)) { killConnection(cl, false); return; } +#if HAS_TLS + if (cl->cctx) { + bytesRead = tls_read(cl->cctx, cl->buf + cl->bufLen, sizeof(cl->buf) - cl->bufLen); + switch (bytesRead) { + // Disconnect + case 0: killConnection(cl, false); - net_perror("RECV"); - } - return; + return; + // Error + case -1: + killConnection(cl, false); + fprintf(stderr, "tls_read: %s\n", tls_error(cl->cctx)); + return; + case TLS_WANT_POLLIN: + return; + case TLS_WANT_POLLOUT: // FIXME: this blocks + continue; + } + } + else +#endif + { + bytesRead = recv(cl->FD,cl->buf + cl->bufLen,sizeof(cl->buf) - cl->bufLen, 0); + switch(bytesRead){ + // Disconnect + case 0: + killConnection(cl, false); + return; + // Error + case -1: + if(!net_wouldblock()){ + killConnection(cl, false); + net_perror("RECV"); + } + return; + } } cl->bufLen += bytesRead; while(1){ @@ -104,3 +126,38 @@ void recvProc(struct client_data *cl) { } } } + +void sendClient(struct client_data *cl, const char *buf, size_t len) +{ + if (cl->FD == (SOCKET)INVALID_SOCKET) + return; + fprintf(stderr, "SERVER %"PRISocket": %.*s", cl->FD, (int)len, (char*)buf); + // TODO: error handling + // FIXME: blocks, probably the reason why server is slow +#ifdef HAS_TLS + if (cl->cctx) { + while (len) { + int rv = tls_write(cl->cctx, buf, len); + if (rv == -1) + break; + if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) + continue; + buf += rv; + len -= rv; + } + } else +#endif + { + while (len) { + int rv = send(cl->FD, buf, len, 0); + if (rv == -1) { + if (net_wouldblock()) + continue; + else + break; + } + buf += rv; + len -= rv; + } + } +} diff --git a/net.h b/net.h index 19c1ae1..0c3daf1 100644 --- a/net.h +++ b/net.h @@ -8,5 +8,6 @@ void sendbuf_visible(struct client_data *client); struct client_data *allocClient(); void killConnection(struct client_data *client, bool byChoice); void recvProc(struct client_data *cl); +void sendClient(struct client_data *cl, const char *buf, size_t len); #endif diff --git a/passwd.c b/passwd.c index 88d83d4..26e4b19 100644 --- a/passwd.c +++ b/passwd.c @@ -1,10 +1,17 @@ #ifdef _WIN32 int password_set(const char *key, const char *pw, const char *fn, const char *tmpfn) { + (void)key; + (void)pw; + (void)fn; + (void)tmpfn; return -1; } int password_check(const char *key, const char *pw, const char *fn) { + (void)key; + (void)pw; + (void)fn; return -1; } #else