commit - 4ec19c03323c872a576d6891b3c5fa3fe70261c4
commit + 69ce65bacb0155be5fb9159a3dfc5c8e3390cc0d
blob - 39c5730bd4a76da18380af3f44562c806b1e005e
blob + 59fa617afaaa91f0f0b6b6338785e11c4c240bf8
--- doc/Protocol.txt
+++ doc/Protocol.txt
- "cloakhost": the cloaked hostname of a client
- "info": info text ("real name") of a client
- "user": the user name of a client (can't be empty)
+ - "certfp": the cert fingerprint of a client
III. Numerics used by IRC+ Protocol
blob - 22897ef51356aed7136a2a662398a9c44aa3254e
blob + 439298c6736cd6b8dce93c546cfa01ebe3923fb1
--- src/ngircd/conf-ssl.h
+++ src/ngircd/conf-ssl.h
void *cookie; /* pointer to server configuration structure
(for outgoing connections), or NULL. */
#endif
+ char *fingerprint;
};
#endif
blob - 45e6458a19d5805d48b13a11f27c639cd3ee3963
blob + 7141eaca6ebb4bf523459e7465075bb0a01ae207
--- src/ngircd/conn-ssl.c
+++ src/ngircd/conn-ssl.c
#define DH_BITS 2048
#define DH_BITS_MIN 1024
+#define MAX_HASH_SIZE 64 /* from gnutls-int.h */
+
static gnutls_certificate_credentials_t x509_cred;
static gnutls_dh_params_t dh_params;
static bool ConnSSL_LoadServerKey_gnutls PARAMS(( void ));
#endif
+#define CERTFP_LEN (20 * 2 + 1)
+
static bool ConnSSL_Init_SSL PARAMS(( CONNECTION *c ));
static int ConnectAccept PARAMS(( CONNECTION *c, bool connect ));
static int ConnSSL_HandleError PARAMS(( CONNECTION *c, const int code, const char *fname ));
memcpy(buf, (char *)(array_start(pass)), size);
return size;
}
+
+
+static int
+Verify_openssl(UNUSED int preverify_ok, UNUSED X509_STORE_CTX *x509_ctx)
+{
+ return 1;
+}
#endif
SSL_shutdown(ssl);
SSL_free(ssl);
c->ssl_state.ssl = NULL;
+ if (c->ssl_state.fingerprint) {
+ free(c->ssl_state.fingerprint);
+ c->ssl_state.fingerprint = NULL;
+ }
}
#endif
#ifdef HAVE_LIBGNUTLS
SSL_CTX_set_options(newctx, SSL_OP_SINGLE_DH_USE|SSL_OP_NO_SSLv2);
SSL_CTX_set_mode(newctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ SSL_CTX_set_verify(newctx, SSL_VERIFY_PEER|SSL_VERIFY_CLIENT_ONCE, Verify_openssl);
SSL_CTX_free(ssl_ctx);
ssl_ctx = newctx;
Log(LOG_INFO, "%s initialized.", SSLeay_version(SSLEAY_VERSION));
return false;
}
assert(c->ssl_state.ssl == NULL);
+ assert(c->ssl_state.fingerprint == NULL);
c->ssl_state.ssl = SSL_new(ssl_ctx);
if (!c->ssl_state.ssl) {
* http://www.mail-archive.com/help-gnutls@gnu.org/msg00286.html
*/
gnutls_transport_set_ptr(c->ssl_state.gnutls_session, (gnutls_transport_ptr_t) (long) c->sock);
+ gnutls_certificate_server_set_request(c->ssl_state.gnutls_session, GNUTLS_CERT_REQUEST);
ret = gnutls_credentials_set(c->ssl_state.gnutls_session, GNUTLS_CRD_CERTIFICATE, x509_cred);
if (ret < 0) {
Log(LOG_ERR, "gnutls_credentials_set: %s", gnutls_strerror(ret));
assert(Conn_OPTION_ISSET(c, CONN_SSL));
return ConnectAccept(c, true);
}
+
+static int
+ConnSSL_InitFingerprint( CONNECTION *c )
+{
+ const char hex[] = "0123456789abcdef";
+ int i;
+
+#ifdef HAVE_LIBSSL
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int digest_size;
+ X509 *cert;
+
+ cert = SSL_get_peer_certificate(c->ssl_state.ssl);
+ if (!cert)
+ return 0;
+
+ if (!X509_digest(cert, EVP_sha1(), digest, &digest_size)) {
+ X509_free(cert);
+ return 0;
+ }
+
+ X509_free(cert);
+#endif /* HAVE_LIBSSL */
+#ifdef HAVE_LIBGNUTLS
+ gnutls_x509_crt_t cert;
+ unsigned int cert_list_size;
+ const gnutls_datum_t *cert_list;
+ unsigned char digest[MAX_HASH_SIZE];
+ size_t digest_size;
+
+ if (gnutls_certificate_type_get(c->ssl_state.gnutls_session) != GNUTLS_CRT_X509)
+ return 0;
+
+ if (gnutls_x509_crt_init(&cert) != GNUTLS_E_SUCCESS)
+ return 0;
+ cert_list_size = 0;
+ cert_list = gnutls_certificate_get_peers(c->ssl_state.gnutls_session,
+ &cert_list_size);
+ if (!cert_list) {
+ gnutls_x509_crt_deinit(cert);
+ return 0;
+ }
+
+ if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) != GNUTLS_E_SUCCESS) {
+ gnutls_x509_crt_deinit(cert);
+ return 0;
+ }
+ digest_size = sizeof(digest);
+ if (gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, digest, &digest_size)) {
+ gnutls_x509_crt_deinit(cert);
+ return 0;
+ }
+
+ gnutls_x509_crt_deinit(cert);
+#endif /* HAVE_LIBGNUTLS */
+
+ assert(c->ssl_state.fingerprint == NULL);
+
+ c->ssl_state.fingerprint = malloc(CERTFP_LEN);
+ if (!c->ssl_state.fingerprint)
+ return 0;
+
+ for (i = 0; i < (int)digest_size; i++) {
+ c->ssl_state.fingerprint[i * 2] = hex[digest[i] / 16];
+ c->ssl_state.fingerprint[i * 2 + 1] = hex[digest[i] % 16];
+ }
+ c->ssl_state.fingerprint[i * 2] = '\0';
+
+ return 1;
+}
+
/* accept/connect wrapper. if connect is true, connect to peer, otherwise wait for incoming connection */
static int
ConnectAccept( CONNECTION *c, bool connect)
if (ret)
return ConnSSL_HandleError(c, ret, "gnutls_handshake");
#endif /* _GNUTLS */
+ (void)ConnSSL_InitFingerprint(c);
+
Conn_OPTION_DEL(c, (CONN_SSL_WANT_WRITE|CONN_SSL_WANT_READ|CONN_SSL_CONNECT));
ConnSSL_LogCertInfo(c);
#endif
}
+char *
+ConnSSL_GetFingerprint(CONNECTION *c)
+{
+ return c->ssl_state.fingerprint;
+}
+
+bool
+ConnSSL_SetFingerprint(CONNECTION *c, const char *fingerprint)
+{
+ assert (c != NULL);
+ c->ssl_state.fingerprint = strdup(fingerprint);
+ return c->ssl_state.fingerprint != NULL;
+}
#else
bool
blob - aea0846f46cb2036b15cff074678e7df482a953e
blob + fc705f13b9e3b2c91a0a90c485decf04c06b5a5c
--- src/ngircd/conn-ssl.h
+++ src/ngircd/conn-ssl.h
GLOBAL ssize_t ConnSSL_Read PARAMS(( CONNECTION *c, void *buf, size_t count));
GLOBAL bool ConnSSL_GetCipherInfo PARAMS(( CONNECTION *c, char *buf, size_t len ));
+GLOBAL char *ConnSSL_GetFingerprint PARAMS(( CONNECTION *c ));
+GLOBAL bool ConnSSL_SetFingerprint PARAMS(( CONNECTION *c, const char *fingerprint ));
+
#endif /* SSL_SUPPORT */
#endif /* conn_ssl_h */
blob - 087f5fc86de5f12c7c80395062e92c8fd675bb9a
blob + 9c6baef2676dd35aab47eaca13b8bf3ef39946f5
--- src/ngircd/conn.c
+++ src/ngircd/conn.c
assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION)));
return Conn_OPTION_ISSET(&My_Connections[Idx], CONN_SSL);
}
+
+GLOBAL char *
+Conn_GetFingerprint(CONN_ID Idx)
+{
+ if (Idx < 0)
+ return NULL;
+ assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION)));
+ return ConnSSL_GetFingerprint(&My_Connections[Idx]);
+}
+
+
+GLOBAL bool
+Conn_SetFingerprint(CONN_ID Idx, const char *fingerprint)
+{
+ if (Idx < 0)
+ return false;
+ assert(Idx < (int) array_length(&My_ConnArray, sizeof(CONNECTION)));
+ return ConnSSL_SetFingerprint(&My_Connections[Idx], fingerprint);
+}
+#else
+GLOBAL bool
+Conn_UsesSSL(UNUSED CONN_ID Idx)
+{
+ return false;
+}
+
+
+GLOBAL char *
+Conn_GetFingerprint(UNUSED CONN_ID Idx)
+{
+ return NULL;
+}
+
+
+GLOBAL bool
+Conn_SetFingerprint(UNUSED CONN_ID Idx, UNUSED const char *fingerprint)
+{
+ return true;
+}
#endif
blob - 9236c58ba7767a7be858a6a331abeedc78e02221
blob + a6cf53a47be4f3695e66f9fd74c76fcf56480a31
--- src/ngircd/conn.h
+++ src/ngircd/conn.h
GLOBAL CLIENT* Conn_GetClient PARAMS((CONN_ID i));
GLOBAL PROC_STAT* Conn_GetProcStat PARAMS((CONN_ID i));
+GLOBAL char *Conn_GetFingerprint PARAMS((CONN_ID Idx));
+GLOBAL bool Conn_SetFingerprint PARAMS((CONN_ID Idx, const char *fingerprint));
+GLOBAL bool Conn_UsesSSL PARAMS((CONN_ID Idx));
+
#ifdef SSL_SUPPORT
GLOBAL bool Conn_GetCipherInfo PARAMS((CONN_ID Idx, char *buf, size_t len));
-GLOBAL bool Conn_UsesSSL PARAMS((CONN_ID Idx));
-#else
-static inline bool
-Conn_UsesSSL(UNUSED CONN_ID Idx)
-{ return false; }
#endif
GLOBAL const char *Conn_GetIPAInfo PARAMS((CONN_ID Idx));
blob - 046648fdfa02cab53369ccbf81f33de40801de0f
blob + 22284cb328c87b0e76a15cbd00c15cd8889bbec2
--- src/ngircd/irc-info.c
+++ src/ngircd/irc-info.c
return DISCONNECTED;
/* Connected using SSL? */
- if (Conn_UsesSSL(Client_Conn(c)) &&
- !IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from),
- Client_ID(c)))
- return DISCONNECTED;
+ if (Conn_UsesSSL(Client_Conn(c))) {
+ if (!IRC_WriteStrClient(from, RPL_WHOISSSL_MSG, Client_ID(from),
+ Client_ID(c)))
+ return DISCONNECTED;
+
+ /* Certificate fingerprint? */
+ if (Conn_GetFingerprint(Client_Conn(c)) &&
+ from == c &&
+ !IRC_WriteStrClient(from, RPL_WHOISCERTFP_MSG,
+ Client_ID(from), Client_ID(c),
+ Conn_GetFingerprint(Client_Conn(c))))
+ return DISCONNECTED;
+ }
/* Registered nickname? */
if (Client_HasMode(c, 'R') &&
entry->id, entry->server, t_str);
}
+#ifdef SSL_SUPPORT
static bool
-Show_MOTD_Start(CLIENT *Client)
+Show_MOTD_SSLInfo(CLIENT *Client)
{
- return IRC_WriteStrClient(Client, RPL_MOTDSTART_MSG,
- Client_ID( Client ), Client_ID( Client_ThisServer( )));
-}
+ char buf[COMMAND_LEN];
+ char c_str[128];
-static bool
-Show_MOTD_Sendline(CLIENT *Client, const char *msg)
-{
- return IRC_WriteStrClient(Client, RPL_MOTD_MSG, Client_ID( Client ), msg);
-}
+ if (Conn_GetCipherInfo(Client_Conn(Client), c_str, sizeof(c_str))) {
+ snprintf(buf, sizeof(buf), "Connected using Cipher %s", c_str);
+ if (!IRC_WriteStrClient(Client, RPL_MOTD_MSG,
+ Client_ID(Client), buf))
+ return false;
+ }
-static bool
-Show_MOTD_End(CLIENT *Client)
-{
- if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client)))
- return DISCONNECTED;
-
- if (*Conf_CloakHost)
- return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
- Client_ID(Client),
- Client_Hostname(Client));
-
- return CONNECTED;
-}
+ if (Conn_GetFingerprint(Client_Conn(Client))) {
+ snprintf(buf, sizeof(buf),
+ "Your client certificate fingerprint is: %s",
+ Conn_GetFingerprint(Client_Conn(Client)));
+ if (!IRC_WriteStrClient(Client, RPL_MOTD_MSG,
+ Client_ID(Client), buf))
+ return false;
+ }
-#ifdef SSL_SUPPORT
-static bool Show_MOTD_SSLInfo(CLIENT *Client)
-{
- bool ret = true;
- char buf[COMMAND_LEN] = "Connected using Cipher ";
-
- if (!Conn_GetCipherInfo(Client_Conn(Client), buf + 23, sizeof buf - 23))
- return true;
-
- if (!Show_MOTD_Sendline(Client, buf))
- ret = false;
-
- return ret;
+ return true;
}
#else
-static inline bool
+static bool
Show_MOTD_SSLInfo(UNUSED CLIENT *c)
-{ return true; }
+{
+ return true;
+}
#endif
/* Global functions */
if (len_tot == 0 && !Conn_UsesSSL(Client_Conn(Client)))
return IRC_WriteStrClient(Client, ERR_NOMOTD_MSG, Client_ID(Client));
- if (!Show_MOTD_Start(Client))
+ if (!IRC_WriteStrClient(Client, RPL_MOTDSTART_MSG, Client_ID(Client),
+ Client_ID(Client_ThisServer())))
return DISCONNECTED;
line = array_start(&Conf_Motd);
assert(len_tot >= len_str);
len_tot -= len_str;
- if (!Show_MOTD_Sendline(Client, line))
+ if (!IRC_WriteStrClient(Client, RPL_MOTD_MSG, Client_ID(Client), line))
return DISCONNECTED;
line += len_str;
}
if (!Show_MOTD_SSLInfo(Client))
return DISCONNECTED;
- return Show_MOTD_End(Client);
+
+ if (!IRC_WriteStrClient(Client, RPL_ENDOFMOTD_MSG, Client_ID(Client)))
+ return DISCONNECTED;
+
+ if (*Conf_CloakHost)
+ return IRC_WriteStrClient(Client, RPL_HOSTHIDDEN_MSG,
+ Client_ID(Client),
+ Client_Hostname(Client));
+
+ return CONNECTED;
} /* IRC_Show_MOTD */
/**
blob - 308a7157ac4bad8c969391fe30a32b5be7a04cb6
blob + d64ffb2178c8fff634e377a1c82964135cde9956
--- src/ngircd/irc-metadata.c
+++ src/ngircd/irc-metadata.c
Client_SetInfo(target, Req->argv[2]);
else if (*Req->argv[2] && strcasecmp(Req->argv[1], "user") == 0)
Client_SetUser(target, Req->argv[2], true);
+ else if (*Req->argv[2] && strcasecmp(Req->argv[1], "certfp") == 0)
+ Conn_SetFingerprint(Client_Conn(target), Req->argv[2]);
else
Log(LOG_WARNING,
"Ignored metadata update from \"%s\" for client \"%s\": \"%s=%s\" - unknown key!",
blob - 371abc262d7dc6f708a7cee156884d6d2186efa5
blob + 3a91c1838998ec1a85dba3f10978ca2a82a21563
--- src/ngircd/messages.h
+++ src/ngircd/messages.h
#define RPL_NETUSERS_MSG "266 %s %lu %lu :Current global users: %lu, Max: %lu"
#define RPL_STATSCONN_MSG "250 %s :Highest connection count: %lu (%lu connections received)"
#define RPL_WHOISSSL_MSG "275 %s %s :is connected via SSL (secure link)"
+#define RPL_WHOISCERTFP_MSG "276 %s %s :has client certificate fingerprint %s"
#define RPL_AWAY_MSG "301 %s %s :%s"
#define RPL_USERHOST_MSG "302 %s :"
blob - 9b8240bdca0227f7e9862d4fb6d5e0646d36ce7a
blob + f7f3ac919b8c64e1061896fffacef6056dac3167
--- src/ngircd/numeric.c
+++ src/ngircd/numeric.c
Client_HostnameCloaked(User)))
return DISCONNECTED;
}
+ }
+
+ if (Conn_GetFingerprint(conn)) {
+ if (!IRC_WriteStrClient(Client,
+ "METADATA %s certfp :%s",
+ Client_ID(User),
+ Conn_GetFingerprint(conn)))
+ return DISCONNECTED;
}
return CONNECTED;