1
0

0002-Support-for-server-certificate-validation-on-server-.patch 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. From 315125a8bcf6d80c969a3310c43b486b0e422e5b Mon Sep 17 00:00:00 2001
  2. From: Christoph Biedl <ngircd.anoy@manchmal.in-ulm.de>
  3. Date: Sun, 2 Nov 2014 14:48:34 +0100
  4. Subject: [PATCH 02/20] Support for server certificate validation on server
  5. links [S2S-TLS]
  6. This patch provides code to validate the server certificate in
  7. server links, defeating nasty man-in-the-middle attacks on server
  8. links.
  9. Features:
  10. - Check whether the certificate is signed by a trusted certificate
  11. authority (CA).
  12. - Check the host name, including wildcard certificates and Subject
  13. Alternative Names.
  14. - Optionally check against a certificate revocation list (CRL).
  15. - Implementation for both OpenSSL and GnuTLS linkage.
  16. Left for another day:
  17. - Parameterize the TLS parameter of an outbound connection. Currently,
  18. it's hardcoded to disable all versions before TLSv1.1.
  19. - Using certificate as CA-certificate. They work for GnuTLS only but
  20. perhaps this should rather raise an error there, too.
  21. - Optional OCSP checking.
  22. - Checking client certificates. Code is there but this first needs some
  23. consideration about the use cases. This could replace all other
  24. authentication methods, for both client-server and server-server
  25. connections.
  26. This patch is based on a patch by Florian Westphal from 2009, which
  27. implemented this for OpenSSL only:
  28. From: Florian Westphal <fw@strlen.de>
  29. Date: Mon, 18 May 2009 00:29:02 +0200
  30. Subject: SSL/TLS: Add initial certificate support to OpenSSL backend
  31. Commit message modified by Alex Barton.
  32. Closes #120, "Server links using TLS/SSL need certificate validation".
  33. Supersedes PR #8, "Options for verifying and requiring SSL client
  34. certificates", which had (incomplete?) code for OpenSSL, no GnuTLS.
  35. (cherry picked from commit 817937b218c4b57515f54216ebc936cd69df0aae)
  36. ---
  37. doc/sample-ngircd.conf.tmpl | 11 ++
  38. man/ngircd.conf.5.tmpl | 10 ++
  39. src/ngircd/conf.c | 27 ++-
  40. src/ngircd/conf.h | 3 +
  41. src/ngircd/conn-ssl.c | 323 ++++++++++++++++++++++++++++++++++--
  42. src/ngircd/conn.c | 21 +++
  43. src/ngircd/conn.h | 3 +-
  44. 7 files changed, 385 insertions(+), 13 deletions(-)
  45. --- a/doc/sample-ngircd.conf.tmpl
  46. +++ b/doc/sample-ngircd.conf.tmpl
  47. @@ -266,6 +266,13 @@
  48. # is only available when ngIRCd is compiled with support for SSL!
  49. # So don't forget to remove the ";" above if this is the case ...
  50. + # SSL Trusted CA Certificates File (for verifying peer certificates)
  51. + ;CAFile = /etc/ssl/CA/cacert.pem
  52. +
  53. + # Certificate Revocation File (for marking otherwise valid
  54. + # certficates as invalid)
  55. + ;CRLFile = /etc/ssl/CA/crl.pem
  56. +
  57. # SSL Server Key Certificate
  58. ;CertFile = :ETCDIR:/ssl/server-cert.pem
  59. @@ -357,6 +364,10 @@
  60. # Connect to the remote server using TLS/SSL (Default: false)
  61. ;SSLConnect = yes
  62. + # Verify the TLS certificate presented by the remote server
  63. + # (Default: yes)
  64. + ;SSLVerify = yes
  65. +
  66. # Define a (case insensitive) list of masks matching nicknames that
  67. # should be treated as IRC services when introduced via this remote
  68. # server, separated by commas (",").
  69. --- a/man/ngircd.conf.5.tmpl
  70. +++ b/man/ngircd.conf.5.tmpl
  71. @@ -385,6 +385,13 @@
  72. section. Please note that this whole section is only recognized by ngIRCd
  73. when it is compiled with support for SSL using OpenSSL or GnuTLS!
  74. .TP
  75. +\fBCAFile (string)\fR
  76. +Filename pointing to the Trusted CA Certificates. This is required for
  77. +verifying peer certificates.
  78. +.TP
  79. +\fBCRLFile (string)\fR
  80. +Filename of Certificate Revocation List.
  81. +.TP
  82. \fBCertFile\fR (string)
  83. SSL Certificate file of the private server key.
  84. .TP
  85. @@ -479,6 +486,9 @@
  86. \fBSSLConnect\fR (boolean)
  87. Connect to the remote server using TLS/SSL. Default: false.
  88. .TP
  89. +\fBSSLVerify\fR (boolean)
  90. +Verify the TLS certificate presented by the remote server. Default: yes.
  91. +.TP
  92. \fBServiceMask\fR (string)
  93. Define a (case insensitive) list of masks matching nicknames that should be
  94. treated as IRC services when introduced via this remote server, separated
  95. --- a/src/ngircd/conf.c
  96. +++ b/src/ngircd/conf.c
  97. @@ -112,6 +112,12 @@
  98. free(Conf_SSLOptions.CertFile);
  99. Conf_SSLOptions.CertFile = NULL;
  100. + free(Conf_SSLOptions.CAFile);
  101. + Conf_SSLOptions.CAFile = NULL;
  102. +
  103. + free(Conf_SSLOptions.CRLFile);
  104. + Conf_SSLOptions.CRLFile = NULL;
  105. +
  106. free(Conf_SSLOptions.DHFile);
  107. Conf_SSLOptions.DHFile = NULL;
  108. array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
  109. @@ -464,7 +470,10 @@
  110. printf( " Host = %s\n", Conf_Server[i].host );
  111. printf( " Port = %u\n", (unsigned int)Conf_Server[i].port );
  112. #ifdef SSL_SUPPORT
  113. - printf( " SSLConnect = %s\n", Conf_Server[i].SSLConnect?"yes":"no");
  114. + printf(" SSLConnect = %s\n",
  115. + yesno_to_str(Conf_Server[i].SSLConnect));
  116. + printf(" SSLVerify = %s\n",
  117. + yesno_to_str(Conf_Server[i].SSLVerify));
  118. #endif
  119. printf( " MyPassword = %s\n", Conf_Server[i].pwd_in );
  120. printf( " PeerPassword = %s\n", Conf_Server[i].pwd_out );
  121. @@ -1774,6 +1783,16 @@
  122. Conf_SSLOptions.CipherList = strdup_warn(Arg);
  123. return;
  124. }
  125. + if (strcasecmp(Var, "CAFile") == 0) {
  126. + assert(Conf_SSLOptions.CAFile == NULL);
  127. + Conf_SSLOptions.CAFile = strdup_warn(Arg);
  128. + return;
  129. + }
  130. + if (strcasecmp(Var, "CRLFile") == 0) {
  131. + assert(Conf_SSLOptions.CRLFile == NULL);
  132. + Conf_SSLOptions.CRLFile = strdup_warn(Arg);
  133. + return;
  134. + }
  135. Config_Error_Section(File, Line, Var, "SSL");
  136. }
  137. @@ -1904,7 +1923,11 @@
  138. if( strcasecmp( Var, "SSLConnect" ) == 0 ) {
  139. New_Server.SSLConnect = Check_ArgIsTrue(Arg);
  140. return;
  141. - }
  142. + }
  143. + if (strcasecmp(Var, "SSLVerify") == 0) {
  144. + New_Server.SSLVerify = Check_ArgIsTrue(Arg);
  145. + return;
  146. + }
  147. #endif
  148. if( strcasecmp( Var, "Group" ) == 0 ) {
  149. /* Server group */
  150. --- a/src/ngircd/conf.h
  151. +++ b/src/ngircd/conf.h
  152. @@ -61,6 +61,7 @@
  153. ng_ipaddr_t dst_addr[2]; /**< List of addresses to connect to */
  154. #ifdef SSL_SUPPORT
  155. bool SSLConnect; /**< Establish connection using SSL? */
  156. + bool SSLVerify; /**< Verify server certificate using CA? */
  157. #endif
  158. char svs_mask[CLIENT_ID_LEN]; /**< Mask of nicknames that should be
  159. treated and counted as services */
  160. @@ -76,6 +77,8 @@
  161. array ListenPorts; /**< Array of listening SSL ports */
  162. array KeyFilePassword; /**< Key file password */
  163. char *CipherList; /**< Set SSL cipher list to use */
  164. + char *CAFile; /**< Trusted CA certificates file */
  165. + char *CRLFile; /**< Certificate revocation file */
  166. };
  167. #endif
  168. --- a/src/ngircd/conn-ssl.c
  169. +++ b/src/ngircd/conn-ssl.c
  170. @@ -43,13 +43,17 @@
  171. #include <openssl/err.h>
  172. #include <openssl/rand.h>
  173. #include <openssl/dh.h>
  174. +#include <openssl/x509v3.h>
  175. static SSL_CTX * ssl_ctx;
  176. static DH *dh_params;
  177. static bool ConnSSL_LoadServerKey_openssl PARAMS(( SSL_CTX *c ));
  178. +static bool ConnSSL_SetVerifyProperties_openssl PARAMS((SSL_CTX * c));
  179. #endif
  180. +#define MAX_CERT_CHAIN_LENGTH 10 /* XXX: do not hardcode */
  181. +
  182. #ifdef HAVE_LIBGNUTLS
  183. #include <sys/types.h>
  184. #include <sys/stat.h>
  185. @@ -74,6 +78,7 @@
  186. static gnutls_dh_params_t dh_params;
  187. static gnutls_priority_t priorities_cache = NULL;
  188. static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void ));
  189. +static bool ConnSSL_SetVerifyProperties_gnutls PARAMS((void));
  190. #endif
  191. #define SHA256_STRING_LEN (32 * 2 + 1)
  192. @@ -131,10 +136,38 @@
  193. /**
  194. * Log OpenSSL error message.
  195. *
  196. + * @param level The log level
  197. * @param msg The error message.
  198. * @param info Additional information text or NULL.
  199. */
  200. static void
  201. +LogOpenSSL_CertInfo(int level, X509 * cert, const char *msg)
  202. +{
  203. + BIO *mem;
  204. + char *memptr;
  205. + long len;
  206. +
  207. + assert(cert);
  208. + assert(msg);
  209. +
  210. + if (!cert)
  211. + return;
  212. + mem = BIO_new(BIO_s_mem());
  213. + if (!mem)
  214. + return;
  215. + X509_NAME_print_ex(mem, X509_get_subject_name(cert), 4,
  216. + XN_FLAG_ONELINE);
  217. + X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 4, XN_FLAG_ONELINE);
  218. + if (BIO_write(mem, "", 1) == 1) {
  219. + len = BIO_get_mem_data(mem, &memptr);
  220. + if (memptr && len > 0)
  221. + Log(level, "%s: \"%s\"", msg, memptr);
  222. + }
  223. + (void)BIO_set_close(mem, BIO_CLOSE);
  224. + BIO_free(mem);
  225. +}
  226. +
  227. +static void
  228. LogOpenSSLError(const char *error, const char *info)
  229. {
  230. unsigned long err = ERR_get_error();
  231. @@ -176,9 +209,16 @@
  232. static int
  233. -Verify_openssl(UNUSED int preverify_ok, UNUSED X509_STORE_CTX *x509_ctx)
  234. +Verify_openssl(int preverify_ok, X509_STORE_CTX * ctx)
  235. {
  236. - return 1;
  237. + int err;
  238. +
  239. + if (!preverify_ok) {
  240. + err = X509_STORE_CTX_get_error(ctx);
  241. + Log(LOG_ERR, "Certificate validation failed: %s",
  242. + X509_verify_cert_error_string(err));
  243. + }
  244. + return preverify_ok;
  245. }
  246. #endif
  247. @@ -354,7 +394,12 @@
  248. }
  249. SSL_CTX_set_session_id_context(newctx, (unsigned char *)"ngircd", 6);
  250. - SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2);
  251. + if (!ConnSSL_SetVerifyProperties_openssl(newctx))
  252. + goto out;
  253. + SSL_CTX_set_options(newctx,
  254. + SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 |
  255. + SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 |
  256. + SSL_OP_NO_COMPRESSION);
  257. SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
  258. SSL_CTX_set_verify(newctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE,
  259. Verify_openssl);
  260. @@ -394,6 +439,9 @@
  261. goto out;
  262. }
  263. + if (!ConnSSL_SetVerifyProperties_gnutls())
  264. + goto out;
  265. +
  266. Log(LOG_INFO, "GnuTLS %s initialized.", gnutls_check_version(NULL));
  267. initialized = true;
  268. return true;
  269. @@ -406,6 +454,37 @@
  270. #ifdef HAVE_LIBGNUTLS
  271. static bool
  272. +ConnSSL_SetVerifyProperties_gnutls(void)
  273. +{
  274. + int err;
  275. +
  276. + if (!Conf_SSLOptions.CAFile)
  277. + return true;
  278. +
  279. + err = gnutls_certificate_set_x509_trust_file(x509_cred,
  280. + Conf_SSLOptions.CAFile,
  281. + GNUTLS_X509_FMT_PEM);
  282. + if (err < 0) {
  283. + Log(LOG_ERR, "Failed to load x509 trust file %s: %s",
  284. + Conf_SSLOptions.CAFile, gnutls_strerror(err));
  285. + return false;
  286. + }
  287. + if (Conf_SSLOptions.CRLFile) {
  288. + err =
  289. + gnutls_certificate_set_x509_crl_file(x509_cred,
  290. + Conf_SSLOptions.CRLFile,
  291. + GNUTLS_X509_FMT_PEM);
  292. + if (err < 0) {
  293. + Log(LOG_ERR, "Failed to load x509 crl file %s: %s",
  294. + Conf_SSLOptions.CRLFile, gnutls_strerror(err));
  295. + return false;
  296. + }
  297. + }
  298. + return true;
  299. +}
  300. +
  301. +
  302. +static bool
  303. ConnSSL_LoadServerKey_gnutls(void)
  304. {
  305. int err;
  306. @@ -531,6 +610,56 @@
  307. }
  308. +static bool
  309. +ConnSSL_SetVerifyProperties_openssl(SSL_CTX * ctx)
  310. +{
  311. + X509_STORE *store = NULL;
  312. + X509_LOOKUP *lookup;
  313. + int verify_flags = SSL_VERIFY_PEER;
  314. + bool ret = false;
  315. +
  316. + if (!Conf_SSLOptions.CAFile)
  317. + return true;
  318. +
  319. + if (SSL_CTX_load_verify_locations(ctx, Conf_SSLOptions.CAFile, NULL) !=
  320. + 1) {
  321. + LogOpenSSLError("SSL_CTX_load_verify_locations", NULL);
  322. + goto out;
  323. + }
  324. +
  325. + if (Conf_SSLOptions.CRLFile) {
  326. + X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new();
  327. + X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
  328. + SSL_CTX_set1_param(ctx, param);
  329. +
  330. + store = SSL_CTX_get_cert_store(ctx);
  331. + assert(store);
  332. + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
  333. + if (!lookup) {
  334. + LogOpenSSLError("X509_STORE_add_lookup",
  335. + Conf_SSLOptions.CRLFile);
  336. + goto out;
  337. + }
  338. +
  339. + if (X509_load_crl_file
  340. + (lookup, Conf_SSLOptions.CRLFile, X509_FILETYPE_PEM) != 1) {
  341. + LogOpenSSLError("X509_load_crl_file",
  342. + Conf_SSLOptions.CRLFile);
  343. + goto out;
  344. + }
  345. + }
  346. +
  347. + SSL_CTX_set_verify(ctx, verify_flags, Verify_openssl);
  348. + SSL_CTX_set_verify_depth(ctx, MAX_CERT_CHAIN_LENGTH);
  349. + ret = true;
  350. +out:
  351. + if (Conf_SSLOptions.CRLFile)
  352. + free(Conf_SSLOptions.CRLFile);
  353. + Conf_SSLOptions.CRLFile = NULL;
  354. + return ret;
  355. +}
  356. +
  357. +
  358. #endif
  359. static bool
  360. ConnSSL_Init_SSL(CONNECTION *c)
  361. @@ -602,7 +731,7 @@
  362. bool
  363. -ConnSSL_PrepareConnect(CONNECTION *c, UNUSED CONF_SERVER *s)
  364. +ConnSSL_PrepareConnect(CONNECTION * c, CONF_SERVER * s)
  365. {
  366. bool ret;
  367. #ifdef HAVE_LIBGNUTLS
  368. @@ -613,7 +742,7 @@
  369. Log(LOG_ERR, "Failed to initialize new SSL session: %s",
  370. gnutls_strerror(err));
  371. return false;
  372. - }
  373. + }
  374. #endif
  375. ret = ConnSSL_Init_SSL(c);
  376. if (!ret)
  377. @@ -621,7 +750,23 @@
  378. Conn_OPTION_ADD(c, CONN_SSL_CONNECT);
  379. #ifdef HAVE_LIBSSL
  380. assert(c->ssl_state.ssl);
  381. - SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_NONE, NULL);
  382. + if (s->SSLVerify) {
  383. + X509_VERIFY_PARAM *param = NULL;
  384. + param = SSL_get0_param(c->ssl_state.ssl);
  385. + X509_VERIFY_PARAM_set_hostflags(param,
  386. + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
  387. +Log(LOG_ERR, "DEBUG: Setting up hostname verification for '%s'", s->host);
  388. + int err = X509_VERIFY_PARAM_set1_host(param, s->host, 0);
  389. + if (err != 1) {
  390. + Log(LOG_ERR,
  391. + "Cannot set up hostname verification for '%s': %u",
  392. + s->host, err);
  393. + return false;
  394. + }
  395. + SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_PEER,
  396. + Verify_openssl);
  397. + } else
  398. + SSL_set_verify(c->ssl_state.ssl, SSL_VERIFY_NONE, NULL);
  399. #endif
  400. return true;
  401. }
  402. @@ -720,18 +865,102 @@
  403. }
  404. +#ifdef HAVE_LIBGNUTLS
  405. +static void *
  406. +LogMalloc(size_t s)
  407. +{
  408. + void *mem = malloc(s);
  409. + if (!mem)
  410. + Log(LOG_ERR, "Out of memory: Could not allocate %lu byte",
  411. + (unsigned long)s);
  412. + return mem;
  413. +}
  414. +
  415. +
  416. static void
  417. -ConnSSL_LogCertInfo( CONNECTION *c )
  418. +LogGnuTLS_CertInfo(int level, gnutls_x509_crt_t cert, const char *msg)
  419. {
  420. + char *dn, *issuer_dn;
  421. + size_t size = 0;
  422. + int err = gnutls_x509_crt_get_dn(cert, NULL, &size);
  423. + if (size == 0) {
  424. + Log(LOG_ERR, "gnutls_x509_crt_get_dn: size == 0");
  425. + return;
  426. + }
  427. + if (err && err != GNUTLS_E_SHORT_MEMORY_BUFFER)
  428. + goto err_crt_get;
  429. + dn = LogMalloc(size);
  430. + if (!dn)
  431. + return;
  432. + err = gnutls_x509_crt_get_dn(cert, dn, &size);
  433. + if (err)
  434. + goto err_crt_get;
  435. + gnutls_x509_crt_get_issuer_dn(cert, NULL, &size);
  436. + assert(size);
  437. + issuer_dn = LogMalloc(size);
  438. + if (!issuer_dn) {
  439. + Log(level, "%s: Distinguished Name: %s", msg, dn);
  440. + free(dn);
  441. + return;
  442. + }
  443. + gnutls_x509_crt_get_issuer_dn(cert, issuer_dn, &size);
  444. + Log(level, "%s: Distinguished Name: \"%s\", Issuer \"%s\"", msg, dn,
  445. + issuer_dn);
  446. + free(dn);
  447. + free(issuer_dn);
  448. + return;
  449. +
  450. + err_crt_get:
  451. + Log(LOG_ERR, "gnutls_x509_crt_get_dn: %s", gnutls_strerror(err));
  452. + return;
  453. +}
  454. +#endif
  455. +
  456. +
  457. +static void
  458. +ConnSSL_LogCertInfo( CONNECTION * c, bool connect)
  459. +{
  460. + bool cert_seen = false, cert_ok = false;
  461. + char msg[128];
  462. #ifdef HAVE_LIBSSL
  463. + const char *comp_alg = "no compression";
  464. + const void *comp;
  465. + X509 *peer_cert = NULL;
  466. SSL *ssl = c->ssl_state.ssl;
  467. assert(ssl);
  468. - Log(LOG_INFO, "Connection %d: initialized %s using cipher %s.",
  469. - c->sock, SSL_get_version(ssl), SSL_get_cipher(ssl));
  470. + comp = SSL_get_current_compression(ssl);
  471. + if (comp)
  472. + comp_alg = SSL_COMP_get_name(comp);
  473. + Log(LOG_INFO, "Connection %d: initialized %s using cipher %s, %s.",
  474. + c->sock, SSL_get_version(ssl), SSL_get_cipher(ssl), comp_alg);
  475. + peer_cert = SSL_get_peer_certificate(ssl);
  476. + if (peer_cert && connect) {
  477. + cert_seen = true;
  478. + /* Client: Check server certificate */
  479. + int err = SSL_get_verify_result(ssl);
  480. + if (err == X509_V_OK) {
  481. + const char *peername = SSL_get0_peername(ssl);
  482. + if (peername != NULL)
  483. + cert_ok = true;
  484. +
  485. + Log(LOG_ERR, "X509_V_OK, peername = '%s'", peername);
  486. +
  487. + } else
  488. + Log(LOG_ERR, "Certificate validation failed: %s",
  489. + X509_verify_cert_error_string(err));
  490. + snprintf(msg, sizeof(msg), "%svalid peer certificate",
  491. + cert_ok ? "" : "in");
  492. + LogOpenSSL_CertInfo(cert_ok ? LOG_DEBUG : LOG_ERR, peer_cert,
  493. + msg);
  494. +
  495. + X509_free(peer_cert);
  496. + }
  497. #endif
  498. #ifdef HAVE_LIBGNUTLS
  499. + unsigned int status;
  500. + gnutls_credentials_type_t cred;
  501. gnutls_session_t sess = c->ssl_state.gnutls_session;
  502. gnutls_cipher_algorithm_t cipher = gnutls_cipher_get(sess);
  503. @@ -740,7 +969,81 @@
  504. gnutls_protocol_get_name(gnutls_protocol_get_version(sess)),
  505. gnutls_cipher_get_name(cipher),
  506. gnutls_mac_get_name(gnutls_mac_get(sess)));
  507. + cred = gnutls_auth_get_type(c->ssl_state.gnutls_session);
  508. + if (cred == GNUTLS_CRD_CERTIFICATE && connect) {
  509. + cert_seen = true;
  510. + int verify =
  511. + gnutls_certificate_verify_peers2(c->
  512. + ssl_state.gnutls_session,
  513. + &status);
  514. +Log(LOG_ERR, "DEBUG: verify = %d", verify);
  515. + if (verify < 0) {
  516. + Log(LOG_ERR,
  517. + "gnutls_certificate_verify_peers2 failed: %s",
  518. + gnutls_strerror(verify));
  519. + goto done_cn_validation;
  520. + } else if (status) {
  521. + gnutls_datum_t out;
  522. +
  523. + if (gnutls_certificate_verification_status_print
  524. + (status, gnutls_certificate_type_get(sess), &out,
  525. + 0) == GNUTLS_E_SUCCESS) {
  526. + Log(LOG_ERR,
  527. + "Certificate validation failed: %s",
  528. + out.data);
  529. + gnutls_free(out.data);
  530. + }
  531. + }
  532. +Log(LOG_ERR, "DEBUG: status = %d", status);
  533. +
  534. + gnutls_x509_crt_t cert;
  535. + unsigned cert_list_size;
  536. + const gnutls_datum_t *cert_list =
  537. + gnutls_certificate_get_peers(sess, &cert_list_size);
  538. + if (!cert_list || cert_list_size == 0) {
  539. + Log(LOG_ERR, "No certificates found");
  540. + goto done_cn_validation;
  541. + }
  542. + int err = gnutls_x509_crt_init(&cert);
  543. + if (err < 0) {
  544. + Log(LOG_ERR,
  545. + "Failed to initialize x509 certificate: %s",
  546. + gnutls_strerror(err));
  547. + goto done_cn_validation;
  548. + }
  549. + err = gnutls_x509_crt_import(cert, cert_list,
  550. + GNUTLS_X509_FMT_DER);
  551. + if (err < 0) {
  552. + Log(LOG_ERR, "Failed to parse the certificate: %s",
  553. + gnutls_strerror(err));
  554. + goto done_cn_validation;
  555. + }
  556. + err = gnutls_x509_crt_check_hostname(cert, c->host);
  557. + if (err == 0)
  558. + Log(LOG_ERR,
  559. + "Failed to verify the hostname, expected \"%s\"",
  560. + c->host);
  561. + else
  562. + cert_ok = verify == 0 && status == 0;
  563. +
  564. + snprintf(msg, sizeof(msg), "%svalid peer certificate",
  565. + cert_ok ? "" : "in");
  566. + LogGnuTLS_CertInfo(cert_ok ? LOG_DEBUG : LOG_ERR, cert, msg);
  567. +
  568. + gnutls_x509_crt_deinit(cert);
  569. +done_cn_validation:
  570. + ;
  571. + }
  572. #endif
  573. + /*
  574. + * can be used later to check if connection was authenticated, e.g.
  575. + * if inbound connection tries to register itself as server.
  576. + * Could also restrict /OPER to authenticated connections, etc.
  577. + */
  578. + if (cert_ok)
  579. + Conn_OPTION_ADD(c, CONN_SSL_PEERCERT_OK);
  580. + if (!cert_seen)
  581. + Log(LOG_INFO, "Peer did not present a certificate");
  582. }
  583. @@ -879,7 +1182,7 @@
  584. (void)ConnSSL_InitCertFp(c);
  585. Conn_OPTION_DEL(c, (CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_CONNECT));
  586. - ConnSSL_LogCertInfo(c);
  587. + ConnSSL_LogCertInfo(c, connect);
  588. Conn_StartLogin(CONNECTION2ID(c));
  589. return 1;
  590. --- a/src/ngircd/conn.c
  591. +++ b/src/ngircd/conn.c
  592. @@ -2546,6 +2546,7 @@
  593. cb_connserver_login_ssl(int sock, short unused)
  594. {
  595. CONN_ID idx = Socket2Index(sock);
  596. + int serveridx;
  597. (void) unused;
  598. @@ -2564,10 +2565,30 @@
  599. return;
  600. }
  601. + serveridx = Conf_GetServer(idx);
  602. + assert(serveridx >= 0);
  603. + if (serveridx < 0)
  604. + goto err;
  605. +
  606. Log( LOG_INFO, "SSL connection %d with \"%s:%d\" established.", idx,
  607. My_Connections[idx].host, Conf_Server[Conf_GetServer( idx )].port );
  608. + if (!Conn_OPTION_ISSET(&My_Connections[idx], CONN_SSL_PEERCERT_OK)) {
  609. + if (Conf_Server[serveridx].SSLVerify) {
  610. + Log(LOG_ERR,
  611. + "SSLVerify enabled for %d, but peer certificate check failed",
  612. + idx);
  613. + goto err;
  614. + }
  615. + Log(LOG_WARNING,
  616. + "Peer certificate check failed for %d, but SSLVerify is disabled, continuing",
  617. + idx);
  618. + }
  619. server_login(idx);
  620. + return;
  621. + err:
  622. + Log(LOG_ERR, "SSL connection on socket %d failed!", sock);
  623. + Conn_Close(idx, "Can't connect!", NULL, false);
  624. }
  625. --- a/src/ngircd/conn.h
  626. +++ b/src/ngircd/conn.h
  627. @@ -40,7 +40,8 @@
  628. #define CONN_SSL 32 /* this connection is SSL encrypted */
  629. #define CONN_SSL_WANT_WRITE 64 /* SSL/TLS library needs to write protocol data */
  630. #define CONN_SSL_WANT_READ 128 /* SSL/TLS library needs to read protocol data */
  631. -#define CONN_SSL_FLAGS_ALL (CONN_SSL_CONNECT|CONN_SSL|CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ)
  632. +#define CONN_SSL_PEERCERT_OK 256 /* peer presented a valid certificate (used to check inbound server auth */
  633. +#define CONN_SSL_FLAGS_ALL (CONN_SSL_CONNECT|CONN_SSL|CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_PEERCERT_OK)
  634. #endif
  635. typedef int CONN_ID;