summaryrefslogtreecommitdiffstats
path: root/openssl.c
blob: 47acd833b6acdca3c12833f638f798cefb928101 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "openssl.h"
#include "helper.h"

static BOOL initDone = FALSE;
static const EVP_MD *sha1 = NULL;

void ssl_printErrors(char *bailMsg)
{
	unsigned long err;
	while ((err = ERR_get_error())) {
		char *msg = ERR_error_string(err, NULL);
		printf("OpenSSL: %s\n", msg);
	}
	if (bailMsg != NULL) bail(bailMsg);
}

BOOL ssl_init()
{
	if (initDone) return TRUE;
	SSL_load_error_strings();
	SSL_library_init();
	OpenSSL_add_all_algorithms();
	sha1 = EVP_get_digestbyname("sha1");
	if (sha1 == NULL) ssl_printErrors("Could not load SHA-1 digest\n");
	return TRUE;
}

SSL_CTX* ssl_newServerCtx(char *certfile, char *keyfile)
{
	const SSL_METHOD *m = SSLv23_server_method();
	if (m == NULL) ssl_printErrors("newServerCtx: method is NULL");
	SSL_CTX *ctx = SSL_CTX_new(m);
	if (ctx == NULL) ssl_printErrors("newServerCtx: ctx is NULL");
	SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
	SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
	SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM);
	SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM);
	if (!SSL_CTX_check_private_key(ctx)) ssl_printErrors("Could not load cert/private key");
	SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
	return ctx;
}

SSL_CTX* ssl_newClientCtx()
{
	const SSL_METHOD *m = SSLv23_client_method();
	if (m == NULL) ssl_printErrors("newClientCtx: method is NULL");
	SSL_CTX *ctx = SSL_CTX_new(m);
	if (ctx == NULL) ssl_printErrors("newClientCtx: ctx is NULL");
	SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
	SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
	return ctx;
}

SSL *ssl_new(int clientFd, SSL_CTX *ctx)
{
	SSL *ssl = SSL_new(ctx);
	if (ssl == NULL) {
		ssl_printErrors(NULL);
		return NULL;
	}
	if (!SSL_set_fd(ssl, clientFd)) {
		ssl_printErrors(NULL);
		SSL_free(ssl);
		return NULL;
	}
	return ssl;
}

BOOL ssl_acceptClient(epoll_client_t *client)
{
	if (client->sslAccepted) return TRUE;
	int ret = SSL_accept(client->ssl);
	if (ret == 1) {
		client->sslAccepted = TRUE;
		return TRUE;
	}
	if (ret < 0) {
		int err = SSL_get_error(client->ssl, ret);
		if (SSL_BLOCKED(err)) return TRUE;
	}
	return FALSE;
}

BOOL ssl_connectServer(epoll_server_t *server)
{
	if (server->sslConnected) return TRUE;
	int ret = SSL_connect(server->ssl);
	if (ret == 1) {
		if (!ssl_checkCertificateHash(server)) {
			printf("Warning: Fingerprint of %s doesn't match value given in config, refusing to talk to server!\n", server->serverData->addr);
			return FALSE;
		}
		server->sslConnected = TRUE;
		return TRUE;
	}
	if (ret <= 0) {
		int err = SSL_get_error(server->ssl, ret);
		if (SSL_BLOCKED(err)) return TRUE;
		if (err == SSL_ERROR_SSL) {
			ssl_printErrors(NULL);
		} else {
			printf("SSL Unknown error %d\n", err);
		}
	}
	return FALSE;
}

BOOL ssl_checkCertificateHash(epoll_server_t *server)
{
	if (server->ssl == NULL) {
		printf("Bug: Asked to check certificate of non-SSL connection\n");
		return FALSE;
	}
	for (int i = 0; i < FINGERPRINTLEN; ++i) {
		if (server->serverData->fingerprint[i] != 0) {
			unsigned char md[EVP_MAX_MD_SIZE];
			unsigned int n = 20;
			X509 *cert = SSL_get_peer_certificate(server->ssl);
			if (cert == NULL) {
				printf("Warning: Server %s has no certificate!\n", server->serverData->addr);
				return FALSE;
			}
			X509_free(cert);
			X509_digest(cert, sha1, md, &n);
			return n == 20 && memcmp(md, server->serverData->fingerprint, n) == 0;
		}
	}
	return TRUE;
}