ssl.c 9.96 KB
Newer Older
deuce's avatar
deuce committed
1
2
#include <stdbool.h>
#include <stdio.h>
3
4

#include <threadwrap.h>
5
#include "xpprintf.h"
6

deuce's avatar
deuce committed
7
#include "ssl.h"
8
//#include "js_socket.h"	// TODO... move this stuff in here?
deuce's avatar
deuce committed
9

10
void DLLCALL free_crypt_attrstr(char *attr)
11
12
13
14
{
	free(attr);
}

15
char* DLLCALL get_crypt_attribute(CRYPT_HANDLE sess, C_IN CRYPT_ATTRIBUTE_TYPE attr)
16
17
18
19
20
21
22
{
	int		len = 0;
	char	*estr = NULL;

	if (cryptStatusOK(cryptGetAttributeString(sess, attr, NULL, &len))) {
		estr = malloc(len + 1);
		if (estr) {
deuce's avatar
deuce committed
23
24
25
26
			if (cryptStatusError(cryptGetAttributeString(sess, attr, estr, &len))) {
				free(estr);
				return NULL;
			}
27
28
29
30
31
32
33
			estr[len] = 0;
			return estr;
		}
	}
	return NULL;
}

34
char* DLLCALL get_crypt_error(CRYPT_HANDLE sess)
35
36
37
38
{
	return get_crypt_attribute(sess, CRYPT_ATTRIBUTE_ERRORMESSAGE);
}

deuce's avatar
deuce committed
39
40
41
42
static int DLLCALL crypt_ll(int error)
{
	switch(error) {
		case CRYPT_ERROR_INCOMPLETE:
43
		case CRYPT_ERROR_NOSECURE:
deuce's avatar
deuce committed
44
			return LOG_WARNING;
45
46
		case CRYPT_ERROR_INTERNAL:
			return LOG_NOTICE;
deuce's avatar
deuce committed
47
48
49
		case CRYPT_ERROR_COMPLETE:
		case CRYPT_ERROR_READ:
		case CRYPT_ERROR_WRITE:
50
		case CRYPT_ENVELOPE_RESOURCE:
deuce's avatar
deuce committed
51
			return LOG_DEBUG;
52
		case CRYPT_ERROR_NOTAVAIL:
deuce's avatar
deuce committed
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
		case CRYPT_ERROR_TIMEOUT:
			return LOG_INFO;
	}
	return LOG_ERR;
}

static const char *crypt_lstr(int level)
{
	switch(level) {
		case LOG_EMERG:
			return "!ERROR";
		case LOG_ALERT:
			return "!ERROR";
		case LOG_CRIT:
			return "!ERROR";
		case LOG_ERR:
			return "ERROR";
		case LOG_WARNING:
			return "WARNING";
		case LOG_NOTICE:
			return "note";
		case LOG_INFO:
			return "info";
		case LOG_DEBUG:
			return "dbg";
	}
	return "!!!!!!!!";
}

bool DLLCALL get_crypt_error_string(int status, CRYPT_HANDLE sess, char **estr, const char *action, int *lvl)
deuce's avatar
deuce committed
83
{
84
	char	*emsg = NULL;
deuce's avatar
deuce committed
85
86
	bool	allocated = false;
	int	level;
deuce's avatar
deuce committed
87

88
89
90
	if (cryptStatusOK(status)) {
		if (estr)
			*estr = NULL;
deuce's avatar
deuce committed
91
		return true;
92
	}
deuce's avatar
deuce committed
93

deuce's avatar
deuce committed
94
95
96
97
	level = crypt_ll(status);
	if (lvl)
		*lvl = level;

98
99
100
	if (estr) {
		if (sess != CRYPT_UNUSED)
			emsg = get_crypt_error(sess);
deuce's avatar
deuce committed
101
102
		if (emsg != NULL)
			allocated = true;
103
104
105
		if (emsg == NULL) {
			switch(status) {
				case CRYPT_ERROR_PARAM1:
deuce's avatar
deuce committed
106
					emsg = "Bad argument, parameter 1";
107
108
					break;
				case CRYPT_ERROR_PARAM2:
deuce's avatar
deuce committed
109
					emsg = "Bad argument, parameter 2";
110
111
					break;
				case CRYPT_ERROR_PARAM3:
deuce's avatar
deuce committed
112
					emsg = "Bad argument, parameter 3";
113
114
					break;
				case CRYPT_ERROR_PARAM4:
deuce's avatar
deuce committed
115
					emsg = "Bad argument, parameter 4";
116
117
					break;
				case CRYPT_ERROR_PARAM5:
deuce's avatar
deuce committed
118
					emsg = "Bad argument, parameter 5";
119
120
					break;
				case CRYPT_ERROR_PARAM6:
deuce's avatar
deuce committed
121
					emsg = "Bad argument, parameter 6";
122
123
					break;
				case CRYPT_ERROR_PARAM7:
deuce's avatar
deuce committed
124
					emsg = "Bad argument, parameter 7";
125
					break;
126

127
				/* Errors due to insufficient resources */
128

129
				case CRYPT_ERROR_MEMORY:
deuce's avatar
deuce committed
130
					emsg = "Out of memory";
131
132
					break;
				case CRYPT_ERROR_NOTINITED:
deuce's avatar
deuce committed
133
					emsg = "Data has not been initialised";
134
135
					break;
				case CRYPT_ERROR_INITED:
deuce's avatar
deuce committed
136
					emsg = "Data has already been init'd";
137
138
					break;
				case CRYPT_ERROR_NOSECURE:
deuce's avatar
deuce committed
139
					emsg = "Opn.not avail.at requested sec.level";
140
141
					break;
				case CRYPT_ERROR_RANDOM:
deuce's avatar
deuce committed
142
					emsg = "No reliable random data available";
143
144
					break;
				case CRYPT_ERROR_FAILED:
deuce's avatar
deuce committed
145
					emsg = "Operation failed";
146
147
					break;
				case CRYPT_ERROR_INTERNAL:
deuce's avatar
deuce committed
148
					emsg = "Internal consistency check failed";
149
					break;
150

151
				/* Security violations */
152

153
				case CRYPT_ERROR_NOTAVAIL:
deuce's avatar
deuce committed
154
					emsg = "This type of opn.not available";
155
156
					break;
				case CRYPT_ERROR_PERMISSION:
deuce's avatar
deuce committed
157
					emsg = "No permiss.to perform this operation";
158
159
					break;
				case CRYPT_ERROR_WRONGKEY:
deuce's avatar
deuce committed
160
					emsg = "Incorrect key used to decrypt data";
161
162
					break;
				case CRYPT_ERROR_INCOMPLETE:
deuce's avatar
deuce committed
163
					emsg = "Operation incomplete/still in progress";
164
165
					break;
				case CRYPT_ERROR_COMPLETE:
deuce's avatar
deuce committed
166
					emsg = "Operation complete/can't continue";
167
168
					break;
				case CRYPT_ERROR_TIMEOUT:
deuce's avatar
deuce committed
169
					emsg = "Operation timed out before completion";
170
171
					break;
				case CRYPT_ERROR_INVALID:
deuce's avatar
deuce committed
172
					emsg = "Invalid/inconsistent information";
173
174
					break;
				case CRYPT_ERROR_SIGNALLED:
deuce's avatar
deuce committed
175
					emsg = "Resource destroyed by extnl.event";
176
					break;
177

178
				/* High-level function errors */
179

180
				case CRYPT_ERROR_OVERFLOW:
deuce's avatar
deuce committed
181
					emsg = "Resources/space exhausted";
182
183
					break;
				case CRYPT_ERROR_UNDERFLOW:
deuce's avatar
deuce committed
184
					emsg = "Not enough data available";
185
186
					break;
				case CRYPT_ERROR_BADDATA:
deuce's avatar
deuce committed
187
					emsg = "Bad/unrecognised data format";
188
189
					break;
				case CRYPT_ERROR_SIGNATURE:
deuce's avatar
deuce committed
190
					emsg = "Signature/integrity check failed";
191
					break;
192

193
				/* Data access function errors */
194

195
				case CRYPT_ERROR_OPEN:
deuce's avatar
deuce committed
196
					emsg = "Cannot open object";
197
198
					break;
				case CRYPT_ERROR_READ:
deuce's avatar
deuce committed
199
					emsg = "Cannot read item from object";
200
201
					break;
				case CRYPT_ERROR_WRITE:
deuce's avatar
deuce committed
202
					emsg = "Cannot write item to object";
203
204
					break;
				case CRYPT_ERROR_NOTFOUND:
deuce's avatar
deuce committed
205
					emsg = "Requested item not found in object";
206
207
					break;
				case CRYPT_ERROR_DUPLICATE:
deuce's avatar
deuce committed
208
					emsg = "Item already present in object";
209
					break;
210

211
212
213
				/* Data enveloping errors */

				case CRYPT_ENVELOPE_RESOURCE:
deuce's avatar
deuce committed
214
					emsg = "Need resource to proceed";
215
216
					break;
			}
217
		}
218
		if (emsg) {
deuce's avatar
deuce committed
219
220
221
			asprintf(estr, "%s '%s' (%d) %s", crypt_lstr(level), emsg, status, action);
			if (allocated)
				free_crypt_attrstr(emsg);
222
223
		}
		else
deuce's avatar
deuce committed
224
			asprintf(estr, "%s (%d) %s", crypt_lstr(level), status, action);
225
	}
deuce's avatar
deuce committed
226
227
228
	return false;
}

229
230
static pthread_once_t crypt_init_once = PTHREAD_ONCE_INIT;
static pthread_mutex_t ssl_cert_mutex;
231
static bool cryptlib_initialized;
232
233
234
235
236
237
238
239
240
241
242
243
244

static void do_cryptEnd(void)
{
	cryptEnd();
}

static void internal_do_cryptInit(void)
{
	int ret;

	if((ret=cryptInit())==CRYPT_OK) {
		cryptAddRandom(NULL,CRYPT_RANDOM_SLOWPOLL);
		atexit(do_cryptEnd);
245
		cryptlib_initialized = true;
246
247
248
249
250
251
252
253
254
255
	}
	else {
		lprintf(LOG_ERR,"cryptInit() returned %d", ret);
	}
	pthread_mutex_init(&ssl_cert_mutex, NULL);
	return;
}

int DLLCALL do_cryptInit(void)
{
256
257
258
	if (pthread_once(&crypt_init_once, internal_do_cryptInit) != 0)
		return 0;
	return cryptlib_initialized;
259
260
}

261
262
263
264
265
bool DLLCALL is_crypt_initialized(void)
{
	return cryptlib_initialized;
}

deuce's avatar
deuce committed
266
#define DO(action, handle, x)	get_crypt_error_string(x, handle, estr, action, level)
deuce's avatar
deuce committed
267

deuce's avatar
deuce committed
268
CRYPT_CONTEXT DLLCALL get_ssl_cert(scfg_t *cfg, char **estr, int *level)
deuce's avatar
deuce committed
269
270
{
	CRYPT_KEYSET		ssl_keyset;
deuce's avatar
deuce committed
271
	CRYPT_CONTEXT		ssl_context = -1;	// MSVC requires this to be initialized
deuce's avatar
deuce committed
272
273
274
	CRYPT_CERTIFICATE	ssl_cert;
	int					i;
	char				sysop_email[sizeof(cfg->sys_inetaddr)+6];
deuce's avatar
deuce committed
275
	char				str[MAX_PATH+1];
deuce's avatar
deuce committed
276

277
	if (estr)
278
		*estr = NULL;
deuce's avatar
deuce committed
279
280
	if(!do_cryptInit())
		return -1;
281
	pthread_mutex_lock(&ssl_cert_mutex);
282
283
284
285
	if (cfg->tls_certificate != -1 || !cfg->prepped) {
		pthread_mutex_unlock(&ssl_cert_mutex);
		return cfg->tls_certificate;
	}
deuce's avatar
deuce committed
286
287
	/* Get the certificate... first try loading it from a file... */
	SAFEPRINTF2(str,"%s%s",cfg->ctrl_dir,"ssl.cert");
288
	if(cryptStatusOK(cryptKeysetOpen(&ssl_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_READONLY))) {
289
		if(!DO("getting private key", ssl_keyset, cryptGetPrivateKey(ssl_keyset, &ssl_context, CRYPT_KEYID_NAME, "ssl_cert", cfg->sys_pass))) {
290
			pthread_mutex_unlock(&ssl_cert_mutex);
deuce's avatar
deuce committed
291
			return -1;
292
		}
deuce's avatar
deuce committed
293
294
295
	}
	else {
		/* Couldn't do that... create a new context and use the cert from there... */
296
		if(!DO("creating SSL context", CRYPT_UNUSED, cryptStatusOK(i=cryptCreateContext(&ssl_context, CRYPT_UNUSED, CRYPT_ALGO_RSA)))) {
297
			pthread_mutex_unlock(&ssl_cert_mutex);
deuce's avatar
deuce committed
298
299
			return -1;
		}
300
		if(!DO("setting label", ssl_context, cryptSetAttributeString(ssl_context, CRYPT_CTXINFO_LABEL, "ssl_cert", 8)))
deuce's avatar
deuce committed
301
			goto failure_return_1;
302
		if(!DO("generating key", ssl_context, cryptGenerateKey(ssl_context)))
deuce's avatar
deuce committed
303
			goto failure_return_1;
304
		if(!DO("opening keyset", CRYPT_UNUSED, cryptKeysetOpen(&ssl_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_CREATE)))
deuce's avatar
deuce committed
305
			goto failure_return_1;
306
		if(!DO("adding private key", ssl_keyset, cryptAddPrivateKey(ssl_keyset, ssl_context, cfg->sys_pass)))
deuce's avatar
deuce committed
307
			goto failure_return_2;
308
		if(!DO("creating certificate", CRYPT_UNUSED, cryptCreateCert(&ssl_cert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE)))
deuce's avatar
deuce committed
309
			goto failure_return_2;
310
		if(!DO("setting public key", ssl_cert, cryptSetAttribute(ssl_cert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, ssl_context)))
deuce's avatar
deuce committed
311
			goto failure_return_3;
312
		if(!DO("signing certificate", ssl_cert, cryptSetAttribute(ssl_cert, CRYPT_CERTINFO_SELFSIGNED, 1)))
313
			goto failure_return_3;
314
		if(!DO("verifying certificate", ssl_cert, cryptSetAttribute(ssl_cert, CRYPT_OPTION_CERT_VALIDITY, 3650)))
deuce's avatar
deuce committed
315
			goto failure_return_3;
316
		if(!DO("setting country name", ssl_cert, cryptSetAttributeString(ssl_cert, CRYPT_CERTINFO_COUNTRYNAME, "ZZ", 2)))
317
			goto failure_return_3;
318
		if(!DO("setting orginization name", ssl_cert, cryptSetAttributeString(ssl_cert, CRYPT_CERTINFO_ORGANIZATIONNAME, cfg->sys_name, strlen(cfg->sys_name))))
deuce's avatar
deuce committed
319
			goto failure_return_3;
320
		if(!DO("setting DNS name", ssl_cert, cryptSetAttributeString(ssl_cert, CRYPT_CERTINFO_DNSNAME, cfg->sys_inetaddr, strlen(cfg->sys_inetaddr))))
321
			goto failure_return_3;
322
		if(!DO("setting Common Name", ssl_cert, cryptSetAttributeString(ssl_cert, CRYPT_CERTINFO_COMMONNAME, cfg->sys_inetaddr, strlen(cfg->sys_inetaddr))))
deuce's avatar
deuce committed
323
			goto failure_return_3;
324
		sprintf(sysop_email, "sysop@%s", cfg->sys_inetaddr);
325
		if(!DO("setting email", ssl_cert, cryptSetAttributeString(ssl_cert, CRYPT_CERTINFO_RFC822NAME, sysop_email, strlen(sysop_email))))
deuce's avatar
deuce committed
326
			goto failure_return_3;
327
		if(!DO("signing certificate", ssl_cert, cryptSignCert(ssl_cert, ssl_context)))
deuce's avatar
deuce committed
328
			goto failure_return_3;
329
		if(!DO("adding public key", ssl_keyset, cryptAddPublicKey(ssl_keyset, ssl_cert)))
deuce's avatar
deuce committed
330
331
			goto failure_return_3;
		cryptDestroyCert(ssl_cert);
deuce's avatar
deuce committed
332
333
334
		cryptKeysetClose(ssl_keyset);
		cryptDestroyContext(ssl_context);
		// Finally, load it from the file.
335
		if(cryptStatusOK(cryptKeysetOpen(&ssl_keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, str, CRYPT_KEYOPT_READONLY))) {
336
			if(!DO("getting private key", ssl_keyset, cryptGetPrivateKey(ssl_keyset, &ssl_context, CRYPT_KEYID_NAME, "ssl_cert", cfg->sys_pass))) {
337
				ssl_context = -1;
deuce's avatar
deuce committed
338
339
			}
		}
deuce's avatar
deuce committed
340
341
342
	}

	cryptKeysetClose(ssl_keyset);
343
	pthread_mutex_unlock(&ssl_cert_mutex);
344
	cfg->tls_certificate = ssl_context;
deuce's avatar
deuce committed
345
346
347
348
349
350
351
352
	return ssl_context;

failure_return_3:
	cryptDestroyCert(ssl_cert);
failure_return_2:
	cryptKeysetClose(ssl_keyset);
failure_return_1:
	cryptDestroyContext(ssl_context);
353
	pthread_mutex_unlock(&ssl_cert_mutex);
deuce's avatar
deuce committed
354
355
	return -1;
}