merged YS' elite cert management patch

This commit is contained in:
nohar 2005-08-25 08:17:10 +00:00
parent e32c1c1f1d
commit e245735f71
8 changed files with 309 additions and 58 deletions

View File

@ -76,7 +76,8 @@ CONFIG_HEADER = ./src/config.h
CONFIG_CLEAN_FILES =
DIST_COMMON = README AUTHORS COPYING ChangeLog INSTALL Makefile.am \
Makefile.in NEWS TODO aclocal.m4 config.guess config.sub configure \
configure.in install-sh missing mkinstalldirs
configure.in install-sh missing mkinstalldirs src/config.h.in \
src/stamp-h.in
DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
@ -100,6 +101,34 @@ config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES)
cd $(srcdir) && $(AUTOCONF)
src/config.h: src/stamp-h
@if test ! -f $@; then \
rm -f src/stamp-h; \
$(MAKE) src/stamp-h; \
else :; fi
src/stamp-h: $(srcdir)/src/config.h.in $(top_builddir)/config.status
cd $(top_builddir) \
&& CONFIG_FILES= CONFIG_HEADERS=src/config.h \
$(SHELL) ./config.status
@echo timestamp > src/stamp-h 2> /dev/null
$(srcdir)/src/config.h.in: $(srcdir)/src/stamp-h.in
@if test ! -f $@; then \
rm -f $(srcdir)/src/stamp-h.in; \
$(MAKE) $(srcdir)/src/stamp-h.in; \
else :; fi
$(srcdir)/src/stamp-h.in: $(top_srcdir)/configure.in $(ACLOCAL_M4)
cd $(top_srcdir) && $(AUTOHEADER)
@echo timestamp > $(srcdir)/src/stamp-h.in 2> /dev/null
mostlyclean-hdr:
clean-hdr:
distclean-hdr:
-rm -f src/config.h
maintainer-clean-hdr:
# This directory's subdirectories are mostly independent; you can cd
# into them and run `make' without going through this Makefile.
# To change the values of `make' variables: instead of editing Makefiles,
@ -288,32 +317,32 @@ distclean-generic:
-rm -f config.cache config.log stamp-h stamp-h[0-9]*
maintainer-clean-generic:
mostlyclean-am: mostlyclean-tags mostlyclean-generic
mostlyclean-am: mostlyclean-hdr mostlyclean-tags mostlyclean-generic
mostlyclean: mostlyclean-recursive
clean-am: clean-tags clean-generic mostlyclean-am
clean-am: clean-hdr clean-tags clean-generic mostlyclean-am
clean: clean-recursive
distclean-am: distclean-tags distclean-generic clean-am
distclean-am: distclean-hdr distclean-tags distclean-generic clean-am
distclean: distclean-recursive
-rm -f config.status
maintainer-clean-am: maintainer-clean-tags maintainer-clean-generic \
distclean-am
maintainer-clean-am: maintainer-clean-hdr maintainer-clean-tags \
maintainer-clean-generic distclean-am
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
maintainer-clean: maintainer-clean-recursive
-rm -f config.status
.PHONY: install-data-recursive uninstall-data-recursive \
install-exec-recursive uninstall-exec-recursive installdirs-recursive \
uninstalldirs-recursive all-recursive check-recursive \
installcheck-recursive info-recursive dvi-recursive \
mostlyclean-recursive distclean-recursive clean-recursive \
.PHONY: mostlyclean-hdr distclean-hdr clean-hdr maintainer-clean-hdr \
install-data-recursive uninstall-data-recursive install-exec-recursive \
uninstall-exec-recursive installdirs-recursive uninstalldirs-recursive \
all-recursive check-recursive installcheck-recursive info-recursive \
dvi-recursive mostlyclean-recursive distclean-recursive clean-recursive \
maintainer-clean-recursive tags tags-recursive mostlyclean-tags \
distclean-tags clean-tags maintainer-clean-tags distdir info-am info \
dvi-am dvi check check-am installcheck-am installcheck install-exec-am \

View File

@ -77,17 +77,24 @@ user {
password = "3880f2b39b3b9cb507b052b695d2680859bfc327";
# SSL certificates checking mode for user:
# "none" to accept anything;
# "basic" to accept if the certificate is contained in the store below;
# "ca" to do a complete certificate chain checking with the objects
# - "none" to accept anything;
# - "basic" to accept if the certificate is contained in the store;
# In "basic" mode, encountered untrusted certificates can be added to
# the store interactively by connecting a client and "trusting" them.
# - "ca" to do a complete certificate chain checking with the objects
# in the store below (you have to put in it every cert, CRL, up to the
# root CA).
# root CA). You have to build your store manually, so you may prefer
# using "basic" unless you're a crypto zealot...
ssl_check_mode = "none";
# Location of the user's store for SSL certificate check
# Standard openssl store, you must put PEM objects with .pem extension
# and run `c_rehash .' in it
ssl_check_store = "/home/nohar/.bip/certstore";
# In "basic" mode, that must point to a single file with all trusted
# certs concatenated together (the interactive "trust" appends to this
# file).
# In "ca" mode, it's a directory of a standard openssl store; you must
# put PEM objects (certificates, CRLs...) with .pem extension and run
# `c_rehash .' in it
ssl_check_store = "/home/nohar/.bip/trustedcerts.txt";
# These will be the default for each connections
default_nick = "nohar";

163
src/bip.c
View File

@ -58,6 +58,9 @@ int conf_blreset_on_talk = 0;
list_t *parse_conf(FILE *file);
static void conf_die(char *fmt, ...);
#ifdef HAVE_LIBSSL
static int adm_trust(struct link_client *ic, struct line *line);
#endif
static void hash_binary(char *hex, unsigned char **password, unsigned int *seed)
{
@ -791,6 +794,11 @@ void ircize(list_t *ll)
MAYFREE(link->s_password);
MAYFREE(link->connect_nick);
MAYFREE(link->vhost);
#ifdef HAVE_LIBSSL
MAYFREE(link->ssl_check_store);
sk_X509_free(link->untrusted_certs);
#endif
for (i = 0; i < link->serverc; i++)
server_free(link->serverv[i]);
@ -843,6 +851,7 @@ void ircize(list_t *ll)
link->s_ssl = c->network->ssl;
link->ssl_check_mode = u->ssl_check_mode;
link->ssl_check_store = strmaydup(u->ssl_check_store);
link->untrusted_certs = sk_X509_new_null();
#endif
if (!link->user)
@ -985,6 +994,150 @@ void write_user_list(connection_t *c, char *dest)
"end of bip user list");
}
#ifdef HAVE_LIBSSL
int link_add_untrusted(struct link_server *ls, X509 *cert)
{
int i;
/* Check whether the cert is already in the stack */
for (i = 0; i < sk_X509_num(LINK(ls)->untrusted_certs); i++) {
if (!X509_cmp(cert,
sk_X509_value(LINK(ls)->untrusted_certs, i)))
return 1;
}
return sk_X509_push(LINK(ls)->untrusted_certs, cert);
}
int ssl_check_trust(struct link_client *ic)
{
X509 *trustcert = NULL;
char subject[270];
char issuer[270];
unsigned char fp[EVP_MAX_MD_SIZE];
char fpstr[EVP_MAX_MD_SIZE * 3 + 20];
unsigned int fplen;
int i;
if(!LINK(ic)->untrusted_certs ||
sk_X509_num(LINK(ic)->untrusted_certs) <= 0)
return 0;
trustcert = sk_X509_value(LINK(ic)->untrusted_certs, 0);
strcpy(subject, "Subject: ");
strcpy(issuer, "Issuer: ");
strcpy(fpstr, "MD5 fingerprint: ");
X509_NAME_oneline(X509_get_subject_name(trustcert), subject + 9, 256);
X509_NAME_oneline(X509_get_issuer_name(trustcert), issuer + 9, 256);
X509_digest(trustcert, EVP_md5(), fp, &fplen);
for(i = 0; i < (int)fplen; i++)
sprintf(fpstr + 17 + (i * 3), "%02X%c",
fp[i], (i == (int)fplen - 1) ? '\0' : ':');
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"This server SSL certificate was not "
"accepted because it is not in your store "
"of trusted certificates:");
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", subject);
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", issuer);
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm", fpstr);
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"WARNING: if you've already trusted a "
"certificate for this server before, that "
"probably means it has changed.");
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"If so, YOU MAY BE SUBJECT OF A "
"MAN-IN-THE-MIDDLE ATTACK! PLEASE DON'T TRUST "
"THIS CERTIFICATE IF YOU'RE NOT SURE THIS IS "
"NOT THE CASE.");
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"Type /QUOTE BIP TRUST OK to trust this "
"certificate, /QUOTE BIP TRUST NO to discard it.");
return 1;
}
#if 0
static int ssl_trust_next_cert(struct link_client *ic)
{
(void)ic;
}
static int ssl_discard_next_cert(struct link_client *ic)
{
(void)ic;
}
#endif /* 0 */
#endif
#ifdef HAVE_LIBSSL
static int adm_trust(struct link_client *ic, struct line *line)
{
if (ic->allow_trust != 1) {
mylog(LOG_ERROR, "User attempted TRUST command without "
"being allowed to!");
unbind_from_link(ic);
return OK_CLOSE;
}
if(!LINK(ic)->untrusted_certs ||
sk_X509_num(LINK(ic)->untrusted_certs) <= 0) {
/* shouldn't have been asked to /QUOTE TRUST but well... */
WRITE_LINE2(CONN(ic), P_SERV, "NOTICE", "TrustEm",
"No untrusted certificates.");
return ERR_PROTOCOL;
}
if (line->elemc != 3)
return ERR_PROTOCOL;
if (!strcasecmp(line->elemv[2], "OK")) {
/* OK, attempt to trust the cert! */
BIO *bio = BIO_new_file(LINK(ic)->ssl_check_store, "a+");
X509 *trustcert = sk_X509_shift(LINK(ic)->untrusted_certs);
if(!bio || !trustcert ||
PEM_write_bio_X509(bio, trustcert) <= 0)
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":==== Error while trusting test!\r\n");
else
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":==== Certificate now trusted.\r\n");
BIO_free_all(bio);
X509_free(trustcert);
} else if (!strcasecmp(line->elemv[2], "NO")) {
/* NO, discard the cert! */
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":==== Certificate discarded.\r\n");
X509_free(sk_X509_shift(LINK(ic)->untrusted_certs));
} else
return ERR_PROTOCOL;
if (!ssl_check_trust(ic)) {
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":No more certificates waiting awaiting "
"user trust, thanks!\r\n");
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
":If the certificate is trusted, bip should "
"be able to connect to the server on the "
"next retry. Please wait a while and try "
"connecting your client again.\r\n");
LINK(ic)->recon_timer = 1; /* Speed up reconnection... */
unbind_from_link(ic);
return OK_CLOSE;
}
return OK_FORGET;
}
#endif
extern struct link_client *reloading_client;
void adm_blreset(struct link_client *ic)
{
@ -997,7 +1150,8 @@ void adm_blreset(struct link_client *ic)
}
}
void adm_bip(struct link_client *ic, struct line *line)
int adm_bip(struct link_client *ic, struct line *line);
int adm_bip(struct link_client *ic, struct line *line)
{
char *nick;
if (LINK(ic)->l_server)
@ -1005,7 +1159,7 @@ void adm_bip(struct link_client *ic, struct line *line)
else
nick = LINK(ic)->prev_nick;
if (line->elemc < 2)
return;
return OK_FORGET;
if (strcasecmp(line->elemv[1], "RELOAD") == 0) {
reloading_client = ic;
@ -1023,7 +1177,12 @@ void adm_bip(struct link_client *ic, struct line *line)
} else if (strcasecmp(line->elemv[1], "HELP") == 0) {
WRITE_LINE2(CONN(ic), P_IRCMASK, "PRIVMSG", nick,
"/BIP (RELOAD|LIST|JUMP|BLRESET|HELP)");
#ifdef HAVE_LIBSSL
} else if (strcasecmp(line->elemv[1], "TRUST") == 0) {
return adm_trust(ic, line);
#endif
}
return OK_FORGET;
}
void free_conf(list_t *l)

View File

@ -62,4 +62,8 @@ struct c_channel
char *key;
};
int adm_bip(struct link_client *ic, struct line *line);
int ssl_check_trust(struct link_client *ic);
void adm_blreset(struct link_client *ic);
#endif

View File

@ -25,6 +25,8 @@ static BIO *errbio = NULL;
extern char *conf_ssl_certfile;
static int SSLize(connection_t *cn, int *nc);
static SSL_CTX *SSL_init_context(void);
/* SSH like trust management */
int link_add_untrusted(void *ls, X509 *cert);
#endif
static int connection_timedout(connection_t *cn);
@ -341,6 +343,7 @@ list_t *read_lines(connection_t *cn, int *error)
case CONN_ERROR:
case CONN_DISCONN:
case CONN_EXCEPT:
case CONN_UNTRUSTED:
*error = 1;
ret = NULL;
break;
@ -488,6 +491,7 @@ int cn_is_new(connection_t *cn)
case CONN_EXCEPT:
case CONN_NEED_SSLIZE:
case CONN_OK:
case CONN_UNTRUSTED:
return 0;
case CONN_NEW:
case CONN_INPROGRESS:
@ -505,6 +509,7 @@ int cn_is_in_error(connection_t *cn)
case CONN_ERROR:
case CONN_DISCONN:
case CONN_EXCEPT:
case CONN_UNTRUSTED:
return 1;
case CONN_NEW:
case CONN_INPROGRESS:
@ -1184,6 +1189,8 @@ static int bip_ssl_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
"in store, rejecting it!");
err = X509_V_ERR_CERT_REJECTED;
X509_STORE_CTX_set_error(ctx, err);
link_add_untrusted(c->user_data, X509_dup(err_cert));
}
}
@ -1226,30 +1233,40 @@ static int SSLize(connection_t *cn, int *nc)
SSL_CIPHER *cipher;
char buf[128];
int len;
int err;
cipher = SSL_get_current_cipher(cn->ssl_h);
SSL_CIPHER_description(cipher, buf, 128);
len = strlen(buf);
if (len > 0)
buf[len-1] = '\0';
mylog(LOG_DEBUG, "Negociated cyphers: %s",buf);
if (cn->ssl_check_mode > 0 &&
(err = SSL_get_verify_result(cn->ssl_h))
!= X509_V_OK) {
mylog(LOG_ERROR, "Certificate check failed: %s (%d)!",
X509_verify_cert_error_string(err),
err);
cn->connected = CONN_ERROR;
return 1;
}
buf[len - 1] = '\0';
mylog(LOG_DEBUG, "Negociated cyphers: %s", buf);
cn->connected = CONN_OK;
*nc = 1;
return 0;
}
switch (cn->ssl_check_mode) {
case SSL_CHECK_NONE:
break;
case SSL_CHECK_BASIC:
if((err = SSL_get_verify_result(cn->ssl_h)) != X509_V_OK) {
mylog(LOG_ERROR, "Certificate check failed: %s (%d)!",
X509_verify_cert_error_string(err), err);
cn->connected = CONN_UNTRUSTED;
return 1;
}
break;
case SSL_CHECK_CA:
if((err = SSL_get_verify_result(cn->ssl_h)) != X509_V_OK) {
mylog(LOG_ERROR, "Certificate check failed: %s (%d)!",
X509_verify_cert_error_string(err), err);
cn->connected = CONN_UNTRUSTED;
return 1;
}
break;
}
if (err2 == SSL_ERROR_SYSCALL) {
/* socked died */
cn->connected = CONN_ERROR;
@ -1280,11 +1297,23 @@ static connection_t *_connection_new_SSL(char *dsthostname, char *dstport,
conn->cert = NULL;
conn->ssl_check_mode = check_mode;
conn->ssl_check_store = check_store;
if (conn->ssl_check_mode != SSL_CHECK_NONE &&
!SSL_CTX_load_verify_locations(conn->ssl_ctx_h, NULL,
switch (conn->ssl_check_mode) {
case SSL_CHECK_BASIC:
if (!SSL_CTX_load_verify_locations(conn->ssl_ctx_h, check_store,
NULL)) {
mylog(LOG_ERROR, "Can't assign check store to "
"SSL connection! Proceeding without!");
}
break;
case SSL_CHECK_CA:
if (!SSL_CTX_load_verify_locations(conn->ssl_ctx_h, NULL,
check_store)) {
mylog(LOG_ERROR, "Can't assign check store to SSL connection!");
return conn;
mylog(LOG_ERROR, "Can't assign check store to "
"SSL connection!");
return conn;
}
break;
}
switch (conn->ssl_check_mode) {
@ -1331,6 +1360,11 @@ connection_t *connection_new(char *dsthostname, int dstport, char *srchostname,
int timeout)
{
char dstportbuf[20], srcportbuf[20], *tmp;
#ifndef HAVE_LIBSSL
(void)ssl;
(void)ssl_check_mode;
(void)ssl_check_store;
#endif
/* TODO: allow litteral service name in the function interface */
if (snprintf(dstportbuf, 20, "%d", dstport) >= 20)
dstportbuf[19] = '\0';

View File

@ -50,6 +50,7 @@
#define CONN_EXCEPT 6
#define CONN_NEW 7
#define CONN_NEED_SSLIZE 8
#define CONN_UNTRUSTED 9
#define WRITE_OK 0
#define WRITE_ERROR -1

View File

@ -17,6 +17,7 @@
#include <stdio.h>
#include "util.h"
#include "irc.h"
#include "bip.h"
#include "log.h"
#include "connection.h"
#include "md5.h"
@ -63,13 +64,6 @@ static void irc_cli_make_join(struct link_client *ic);
#define CONNECT_TIMEOUT 60
#define ERR_PROTOCOL (-1)
#define ERR_AUTH (-2)
#define OK_COPY (1)
#define OK_FORGET (2)
#define OK_CLOSE (3)
#define OK_COPY_CLI (4)
struct channel *channel_new(const char *name)
{
struct channel *chan;
@ -298,7 +292,6 @@ static void irc_server_join(struct link_server *s)
static void irc_server_connected(struct link_server *server)
{
int i;
LINK(server)->s_state = IRCS_CONNECTED;
irc_server_join(server);
log_connected(LINK(server)->log);
@ -310,11 +303,6 @@ static void irc_server_connected(struct link_server *server)
write_line(CONN(server), str);
free(str);
}
#if 0
for (i = 0; i < LINK(server)->l_clientc; i++)
irc_cli_make_join(LINK(server)->l_clientv[i]);
#endif
}
/*
@ -527,7 +515,7 @@ static void bind_to_link(struct link *l, struct link_client *ic)
l->l_clientv[i] = ic;
}
static void unbind_from_link(struct link_client *ic)
void unbind_from_link(struct link_client *ic)
{
struct link *l = LINK(ic);
int i;
@ -550,11 +538,9 @@ static void unbind_from_link(struct link_client *ic)
fatal("realloc");
}
void adm_bip(struct link_client *ic, struct line *line);
int irc_cli_bip(struct link_client *ic, struct line *line)
{
adm_bip(ic, line);
return OK_FORGET;
return adm_bip(ic, line);
}
#define PASS_SEP ':'
@ -682,6 +668,17 @@ static int irc_cli_startup(struct link_client *ic, struct line *line,
return ERR_AUTH;
}
if (LINK(ic)->s_state != IRCS_CONNECTED) {
#ifdef HAVE_LIBSSL
/* Check if we have an untrusted certificate from the server */
if (ssl_check_trust(ic)) {
ic->allow_trust = 1;
free(init_nick);
return OK_FORGET;
}
#endif
}
if (LINK(ic)->s_state == IRCS_NONE) {
/* drop it if corresponding server hasn't connected at all. */
write_line_fast(CONN(ic), ":irc.bip.net NOTICE pouet "
@ -693,7 +690,7 @@ static int irc_cli_startup(struct link_client *ic, struct line *line,
}
#if 0
TODO code this
TODO code this -- OR NOT!
if (!connection_check_source(ic, ic->client->src_ipl))
return ERR_AUTH;
#endif
@ -798,7 +795,6 @@ static int irc_cli_quit(struct link_client *ic, struct line *line)
return OK_CLOSE;
}
void adm_blreset(struct link_client *ic);
static int irc_cli_privmsg(struct link_client *ic, struct line *line)
{
log_cli_privmsg(LINK(ic)->log, LINK(ic)->l_server->nick,
@ -1847,8 +1843,13 @@ connection_t *irc_server_connect(struct link *link)
conn = connection_new(link->serverv[link->cur_server]->host,
link->serverv[link->cur_server]->port,
link->vhost, link->bind_port,
#ifdef HAVE_LIBSSL
link->s_ssl, link->ssl_check_mode,
link->ssl_check_store, CONNECT_TIMEOUT);
link->ssl_check_store,
#else
0, 0, NULL,
#endif
CONNECT_TIMEOUT);
if (!conn)
fatal("connection_new");

View File

@ -16,6 +16,14 @@
#include "connection.h"
#include "line.h"
#define ERR_PROTOCOL (-1)
#define ERR_AUTH (-2)
#define OK_COPY (1)
#define OK_FORGET (2)
#define OK_CLOSE (3)
#define OK_COPY_CLI (4)
#define P_SERV "bip.bip.bip"
#define S_PING "BIPPING"
#define P_IRCMASK "-bip!bip@bip.bip.bip"
@ -103,8 +111,11 @@ struct link {
int bind_port;
int s_ssl;
#ifdef HAVE_LIBSSL
int ssl_check_mode;
char *ssl_check_store;
STACK_OF(X509) *untrusted_certs;
#endif
};
struct link_connection {
@ -134,6 +145,10 @@ struct link_client {
char *init_pass;
int state;
int logging_timer;
#ifdef HAVE_LIBSSL
int allow_trust;
#endif
};
#define link_client_new() calloc(sizeof(struct link_client), 1)
@ -180,4 +195,5 @@ int ischannel(char p);
void irc_client_close(struct link_client *);
void irc_client_free(struct link_client *);
struct link *irc_link_new();
void unbind_from_link(struct link_client *ic);
#endif