diff --git a/include/h.h b/include/h.h index 97fe4b942..d2b39f22d 100644 --- a/include/h.h +++ b/include/h.h @@ -888,6 +888,7 @@ extern void send_cap_notify(int add, char *token); extern void sendbufto_one(Client *to, char *msg, unsigned int quick); extern MODVAR int current_serial; extern char *spki_fingerprint(Client *acptr); +extern char *spki_fingerprint_ex(X509 *x509_cert); extern int is_module_loaded(char *name); extern void close_std_descriptors(void); extern void banned_client(Client *acptr, char *bantype, char *reason, int global, int noexit); @@ -962,3 +963,4 @@ extern void unreal_del_quotes(char *i); extern char *unreal_add_quotes(char *str); extern int unreal_add_quotes_r(char *i, char *o, size_t len); extern void user_account_login(MessageTag *recv_mtags, Client *client); +extern void link_generator(void); diff --git a/include/struct.h b/include/struct.h index 420ba71a4..10195a4d0 100644 --- a/include/struct.h +++ b/include/struct.h @@ -741,6 +741,7 @@ struct LoopStruct { unsigned tainted : 1; Client *rehash_save_cptr, *rehash_save_client; int rehash_save_sig; + void (*boot_function)(); }; /** Matching types for Match.type */ diff --git a/src/conf.c b/src/conf.c index b5e60e5f3..7b1e4d1de 100644 --- a/src/conf.c +++ b/src/conf.c @@ -10461,3 +10461,79 @@ int reloadable_perm_module_unloaded(void) return ret; } + +char *link_generator_spkifp(TLSOptions *tlsoptions) +{ + SSL_CTX *ctx; + SSL *ssl; + X509 *cert; + + ctx = init_ctx(tlsoptions, 1); + if (!ctx) + exit(1); + ssl = SSL_new(ctx); + if (!ssl) + exit(1); + cert = SSL_get_certificate(ssl); + return spki_fingerprint_ex(cert); +} + +void link_generator(void) +{ + ConfigItem_listen *lstn; + TLSOptions *tlsopt = iConf.tls_options; /* never null */ + int port = 0; + char *ip = NULL; + char *spkifp; + + for (lstn = conf_listen; lstn; lstn = lstn->next) + { + if ((lstn->options & LISTENER_SERVERSONLY) && + (lstn->options & LISTENER_TLS)) + { + if (lstn->tls_options) + tlsopt = lstn->tls_options; + port = lstn->port; + if (strcmp(lstn->ip, "*")) + ip = lstn->ip; + /* else NULL */ + break; + } + } + + if (!port) + { + printf("You don't have any listen { } blocks that are serversonly.\n"); + printf("It is recommended to have at least one. Add this to your configuration file:\n"); + printf("listen { ip *; port 6900; options { tls; serversonly; }; };\n"); + exit(1); + } + + spkifp = link_generator_spkifp(tlsopt); + if (!spkifp) + { + printf("Could not calculate spkifp. Maybe you have uncommon SSL/TLS options set? Odd...\n"); + exit(1); + } + + printf("\n"); + printf("Add the following link block to the unrealircd.conf on the OTHER side of the link\n"); + printf("(so NOT in the unrealircd.conf on THIS machine). Here it is, just copy-paste:\n"); + printf("################################################################################\n"); + printf("link %s {\n" + " incoming {\n" + " mask *;\n" + " };\n" + " outgoing {\n" + " hostname %s;\n" + " port %d;\n" + " }\n" + " password \"%s\" { spkifp; }\n" + "};\n", + conf_me->name, + ip ? ip : conf_me->name, + port, + spkifp); + printf("################################################################################\n"); + exit(0); +} diff --git a/src/ircd.c b/src/ircd.c index 9ad0e0d48..60fef0625 100644 --- a/src/ircd.c +++ b/src/ircd.c @@ -1160,6 +1160,9 @@ int InitUnrealIRCd(int argc, char *argv[]) case '8': utf8_test(); exit(0); + case 'L': + loop.boot_function = link_generator; + break; default: #ifndef _WIN32 return bad_command(myargv[0]); @@ -1292,6 +1295,8 @@ int InitUnrealIRCd(int argc, char *argv[]) fflush(stderr); exit(0); } + if (loop.boot_function) + loop.boot_function(); #ifndef _WIN32 fprintf(stderr, "Dynamic configuration initialized.. booting IRCd.\n"); #endif diff --git a/src/tls.c b/src/tls.c index 37c1b7547..7da64dfc2 100644 --- a/src/tls.c +++ b/src/tls.c @@ -1244,6 +1244,8 @@ int certificate_quality_check(SSL_CTX *ctx, char **errstr) return 1; } +char *spki_fingerprint_ex(X509 *x509_cert); + /** Return the SPKI Fingerprint for a client. * * This is basically the same output as @@ -1254,45 +1256,50 @@ int certificate_quality_check(SSL_CTX *ctx, char **errstr) char *spki_fingerprint(Client *cptr) { X509 *x509_cert = NULL; + char *ret; + + if (!MyConnect(cptr) || !cptr->local->ssl) + return NULL; + + x509_cert = SSL_get_peer_certificate(cptr->local->ssl); + if (!x509_cert) + return NULL; + ret = spki_fingerprint_ex(x509_cert); + X509_free(x509_cert); + return ret; +} + +char *spki_fingerprint_ex(X509 *x509_cert) +{ unsigned char *der_cert = NULL, *p; int der_cert_len, n; static char retbuf[256]; SHA256_CTX ckctx; unsigned char checksum[SHA256_DIGEST_LENGTH]; - if (!MyConnect(cptr) || !cptr->local->ssl) - return NULL; + memset(retbuf, 0, sizeof(retbuf)); - x509_cert = SSL_get_peer_certificate(cptr->local->ssl); - - if (x509_cert) + der_cert_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509_cert), NULL); + if ((der_cert_len > 0) && (der_cert_len < 16384)) { - memset(retbuf, 0, sizeof(retbuf)); + der_cert = p = safe_alloc(der_cert_len); + n = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509_cert), &p); - der_cert_len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509_cert), NULL); - if ((der_cert_len > 0) && (der_cert_len < 16384)) + if ((n > 0) && ((p - der_cert) == der_cert_len)) { - der_cert = p = safe_alloc(der_cert_len); - n = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x509_cert), &p); + /* The DER encoded SPKI is stored in 'der_cert' with length 'der_cert_len'. + * Now we need to create an SHA256 hash out of it. + */ + SHA256_Init(&ckctx); + SHA256_Update(&ckctx, der_cert, der_cert_len); + SHA256_Final(checksum, &ckctx); - if ((n > 0) && ((p - der_cert) == der_cert_len)) - { - /* The DER encoded SPKI is stored in 'der_cert' with length 'der_cert_len'. - * Now we need to create an SHA256 hash out of it. - */ - SHA256_Init(&ckctx); - SHA256_Update(&ckctx, der_cert, der_cert_len); - SHA256_Final(checksum, &ckctx); - - /* And convert the binary to a base64 string... */ - n = b64_encode(checksum, SHA256_DIGEST_LENGTH, retbuf, sizeof(retbuf)); - safe_free(der_cert); - X509_free(x509_cert); - return retbuf; /* SUCCESS */ - } + /* And convert the binary to a base64 string... */ + n = b64_encode(checksum, SHA256_DIGEST_LENGTH, retbuf, sizeof(retbuf)); safe_free(der_cert); + return retbuf; /* SUCCESS */ } - X509_free(x509_cert); + safe_free(der_cert); } return NULL; } diff --git a/unrealircd.in b/unrealircd.in index 9fc0c5ba2..31430b86e 100644 --- a/unrealircd.in +++ b/unrealircd.in @@ -289,6 +289,8 @@ elif [ "$1" = "hot-patch" -o "$1" = "cold-patch" ] ; then else echo "Patch applied successfully. You must now restart your IRC server." fi +elif [ "$1" = "genlinkblock" ] ; then + @BINDIR@/unrealircd -L else if [ "$1" = "" ]; then echo "This script expects a parameter. Use:"