Updated changelog.
[exim.git] / src / src / tlscert-gnu.c
CommitLineData
9d1c15ef
JH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
5/* Copyright (c) Jeremy Harris 2014 */
6
7/* This file provides TLS/SSL support for Exim using the GnuTLS library,
8one of the available supported implementations. This file is #included into
9tls.c when USE_GNUTLS has been set.
10*/
11
12#include <gnutls/gnutls.h>
13/* needed for cert checks in verification and DN extraction: */
14#include <gnutls/x509.h>
15/* needed to disable PKCS11 autoload unless requested */
16#if GNUTLS_VERSION_NUMBER >= 0x020c00
17# include <gnutls/pkcs11.h>
18#endif
19
20
21/*****************************************************
22* Export/import a certificate, binary/printable
23*****************************************************/
24int
25tls_export_cert(uschar * buf, size_t buflen, void * cert)
26{
27size_t sz = buflen;
28void * reset_point = store_get(0);
29int fail = 0;
30uschar * cp;
31
32if (gnutls_x509_crt_export((gnutls_x509_crt_t)cert,
33 GNUTLS_X509_FMT_PEM, buf, &sz))
34 return 1;
35if ((cp = string_printing(buf)) != buf)
36 {
37 Ustrncpy(buf, cp, buflen);
38 if (buf[buflen-1])
39 fail = 1;
40 }
41store_reset(reset_point);
42return fail;
43}
44
45int
46tls_import_cert(const uschar * buf, void ** cert)
47{
48void * reset_point = store_get(0);
49gnutls_datum_t datum;
50gnutls_x509_crt_t crt;
51int fail = 0;
52
53gnutls_global_init();
54gnutls_x509_crt_init(&crt);
55
56datum.data = string_unprinting(US buf);
57datum.size = Ustrlen(datum.data);
58if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM))
59 fail = 1;
60else
61 *cert = (void *)crt;
62
63store_reset(reset_point);
64return fail;
65}
66
67void
68tls_free_cert(void * cert)
69{
70gnutls_x509_crt_deinit((gnutls_x509_crt_t) cert);
71gnutls_global_deinit();
72}
73
74/*****************************************************
75* Certificate field extraction routines
76*****************************************************/
8a6eec04
JH
77static uschar *
78g_err(const char * tag, const char * from, int gnutls_err)
79{
80expand_string_message = string_sprintf(stderr,
81 "%s: %s fail: %s\n", from, tag, gnutls_strerror(gnutls_err));
82return NULL;
83}
84
85
9d1c15ef
JH
86static uschar *
87time_copy(time_t t)
88{
89uschar * cp = store_get(32);
90struct tm * tp = gmtime(&t);
91size_t len = strftime(CS cp, 32, "%b %e %T %Y %Z", tp);
92return len > 0 ? cp : NULL;
93}
94
95/**/
96
97uschar *
9e4dddbd 98tls_cert_issuer(void * cert, uschar * mod)
9d1c15ef
JH
99{
100uschar txt[256];
101size_t sz = sizeof(txt);
102return ( gnutls_x509_crt_get_issuer_dn(cert, CS txt, &sz) == 0 )
103 ? string_copy(txt) : NULL;
104}
105
106uschar *
9e4dddbd 107tls_cert_not_after(void * cert, uschar * mod)
9d1c15ef
JH
108{
109return time_copy(
110 gnutls_x509_crt_get_expiration_time((gnutls_x509_crt_t)cert));
111}
112
113uschar *
9e4dddbd 114tls_cert_not_before(void * cert, uschar * mod)
9d1c15ef
JH
115{
116return time_copy(
117 gnutls_x509_crt_get_activation_time((gnutls_x509_crt_t)cert));
118}
119
120uschar *
9e4dddbd 121tls_cert_serial_number(void * cert, uschar * mod)
9d1c15ef
JH
122{
123uschar bin[50], txt[150];
124size_t sz = sizeof(bin);
125uschar * sp;
126uschar * dp;
127
128if (gnutls_x509_crt_get_serial((gnutls_x509_crt_t)cert,
129 bin, &sz) || sz > sizeof(bin))
130 return NULL;
131for(dp = txt, sp = bin; sz; dp += 2, sp++, sz--)
132 sprintf(dp, "%.2x", *sp);
133for(sp = txt; sp[0]=='0' && sp[1]; ) sp++; /* leading zeroes */
134return string_copy(sp);
135}
136
137uschar *
9e4dddbd 138tls_cert_signature(void * cert, uschar * mod)
9d1c15ef
JH
139{
140uschar * cp1;
141uschar * cp2;
142uschar * cp3;
143size_t len = 0;
144int ret;
145
146if ((ret = gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, cp1, &len)) !=
147 GNUTLS_E_SHORT_MEMORY_BUFFER)
148 {
149 fprintf(stderr, "%s: gs0 fail: %s\n", __FUNCTION__, gnutls_strerror(ret));
150 return NULL;
151 }
152
153cp1 = store_get(len*4+1);
154
155if (gnutls_x509_crt_get_signature((gnutls_x509_crt_t)cert, cp1, &len) != 0)
156 {
157 fprintf(stderr, "%s: gs1 fail\n", __FUNCTION__);
158 return NULL;
159 }
160
161for(cp3 = cp2 = cp1+len; cp1 < cp2; cp3 += 3, cp1++)
162 sprintf(cp3, "%.2x ", *cp1);
163cp3[-1]= '\0';
164
165return cp2;
166}
167
168uschar *
9e4dddbd 169tls_cert_signature_algorithm(void * cert, uschar * mod)
9d1c15ef
JH
170{
171gnutls_sign_algorithm_t algo =
172 gnutls_x509_crt_get_signature_algorithm((gnutls_x509_crt_t)cert);
173return algo < 0 ? NULL : string_copy(gnutls_sign_get_name(algo));
174}
175
176uschar *
9e4dddbd 177tls_cert_subject(void * cert, uschar * mod)
9d1c15ef 178{
8a6eec04
JH
179uschar * cp = NULL;
180int ret;
181size_t siz = 0;
182
183ret = gnutls_x509_crt_get_dn(cert, cp, &siz);
184if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
185 {
186 fprintf(stderr, "%s: gs0 fail: %s\n", __FUNCTION__, gnutls_strerror(ret));
187 return NULL;
188 }
189
190cp = store_get(siz);
191
192ret = gnutls_x509_crt_get_dn(cert, cp, &siz);
193if (ret < 0)
194 {
195 fprintf(stderr, "%s: gs1 fail: %s\n", __FUNCTION__, gnutls_strerror(ret));
196 return NULL;
197 }
198
199return cp;
9d1c15ef
JH
200}
201
202uschar *
9e4dddbd 203tls_cert_version(void * cert, uschar * mod)
9d1c15ef
JH
204{
205return string_sprintf("%d", gnutls_x509_crt_get_version(cert));
206}
207
208uschar *
209tls_cert_ext_by_oid(void * cert, uschar * oid, int idx)
210{
211uschar * cp1 = NULL;
212uschar * cp2;
213uschar * cp3;
214size_t siz = 0;
215unsigned int crit;
216int ret;
217
218ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert,
219 oid, idx, cp1, &siz, &crit);
220if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
8a6eec04 221 return g_err("ge0", __FUNCTION__, ret);
9d1c15ef
JH
222
223cp1 = store_get(siz*4 + 1);
224
225ret = gnutls_x509_crt_get_extension_by_oid ((gnutls_x509_crt_t)cert,
226 oid, idx, cp1, &siz, &crit);
227if (ret < 0)
8a6eec04 228 return g_err("ge1", __FUNCTION__, ret);
9d1c15ef
JH
229
230/* binary data, DER encoded */
231
232/* just dump for now */
233for(cp3 = cp2 = cp1+siz; cp1 < cp2; cp3 += 3, cp1++)
234 sprintf(cp3, "%.2x ", *cp1);
235cp3[-1]= '\0';
236
237return cp2;
238}
239
240uschar *
9e4dddbd 241tls_cert_subject_altname(void * cert, uschar * mod)
9d1c15ef 242{
9e4dddbd
JH
243uschar * list = NULL;
244int index;
245size_t siz;
9d1c15ef 246int ret;
9e4dddbd
JH
247uschar sep = '\n';
248uschar * tag = US"";
249uschar * ele;
250int match = -1;
9d1c15ef 251
9e4dddbd 252while (mod)
9d1c15ef 253 {
9e4dddbd
JH
254 if (*mod == '>' && *++mod) sep = *mod++;
255 else if (Ustrcmp(mod, "dns")==0) { match = GNUTLS_SAN_DNSNAME; mod += 3; }
256 else if (Ustrcmp(mod, "uri")==0) { match = GNUTLS_SAN_URI; mod += 3; }
257 else if (Ustrcmp(mod, "mail")==0) { match = GNUTLS_SAN_RFC822NAME; mod += 4; }
258 else continue;
259
260 if (*mod++ != ',')
9d1c15ef 261 break;
9d1c15ef
JH
262 }
263
9e4dddbd 264for(index = 0;; index++)
9d1c15ef 265 {
9e4dddbd
JH
266 siz = 0;
267 switch(ret = gnutls_x509_crt_get_subject_alt_name(
268 (gnutls_x509_crt_t)cert, index, NULL, &siz, NULL))
269 {
270 case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
271 return list; /* no more elements; normal exit */
272
273 case GNUTLS_E_SHORT_MEMORY_BUFFER:
274 break;
275
276 default:
8a6eec04 277 return g_err("gs0", __FUNCTION__, ret);
9e4dddbd
JH
278 }
279
280 ele = store_get(siz+1);
281 if ((ret = gnutls_x509_crt_get_subject_alt_name(
282 (gnutls_x509_crt_t)cert, index, ele, &siz, NULL)) < 0)
8a6eec04 283 return g_err("gs1", __FUNCTION__, ret);
9e4dddbd
JH
284 ele[siz] = '\0';
285
286 if (match != -1 && match != ret)
287 continue;
288 switch (ret)
289 {
290 case GNUTLS_SAN_DNSNAME: tag = US"DNS"; break;
291 case GNUTLS_SAN_URI: tag = US"URI"; break;
292 case GNUTLS_SAN_RFC822NAME: tag = US"MAIL"; break;
293 default: continue; /* ignore unrecognised types */
294 }
295 list = string_append_listele(list, sep,
296 match == -1 ? string_sprintf("%s=%s", tag, ele) : ele);
9d1c15ef 297 }
9e4dddbd 298/*NOTREACHED*/
9d1c15ef
JH
299}
300
301uschar *
9e4dddbd 302tls_cert_ocsp_uri(void * cert, uschar * mod)
9d1c15ef
JH
303{
304#if GNUTLS_VERSION_NUMBER >= 0x030000
305gnutls_datum_t uri;
9e4dddbd
JH
306int ret;
307uschar sep = '\n';
308int index;
309uschar * list = NULL;
310
311if (mod)
312 if (*mod == '>' && *++mod) sep = *mod++;
9d1c15ef 313
9e4dddbd
JH
314for(index = 0;; index++)
315 {
316 ret = gnutls_x509_crt_get_authority_info_access((gnutls_x509_crt_t)cert,
317 index, GNUTLS_IA_OCSP_URI, &uri, NULL);
9d1c15ef 318
9e4dddbd
JH
319 if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
320 return list;
321 if (ret < 0)
8a6eec04 322 return g_err("gai", __FUNCTION__, ret);
9d1c15ef 323
9e4dddbd
JH
324 list = string_append_listele(list, sep,
325 string_copyn(uri.data, uri.size));
326 }
327/*NOTREACHED*/
9d1c15ef
JH
328
329#else
330
331expand_string_message =
332 string_sprintf("%s: OCSP support with GnuTLS requires version 3.0.0\n",
333 __FUNCTION__);
334return NULL;
335
336#endif
337}
338
339uschar *
9e4dddbd 340tls_cert_crl_uri(void * cert, uschar * mod)
9d1c15ef
JH
341{
342int ret;
9e4dddbd
JH
343size_t siz;
344uschar sep = '\n';
345int index;
346uschar * list = NULL;
347uschar * ele;
348
349if (mod)
350 if (*mod == '>' && *++mod) sep = *mod++;
9d1c15ef 351
9e4dddbd 352for(index = 0;; index++)
9d1c15ef 353 {
9e4dddbd
JH
354 siz = 0;
355 switch(ret = gnutls_x509_crt_get_crl_dist_points(
356 (gnutls_x509_crt_t)cert, index, NULL, &siz, NULL, NULL))
357 {
358 case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
359 return list;
360 case GNUTLS_E_SHORT_MEMORY_BUFFER:
361 break;
362 default:
8a6eec04 363 return g_err("gc0", __FUNCTION__, ret);
9e4dddbd
JH
364 }
365
366 ele = store_get(siz+1);
367 if ((ret = gnutls_x509_crt_get_crl_dist_points(
8a6eec04
JH
368 (gnutls_x509_crt_t)cert, index, ele, &siz, NULL, NULL)) < 0)
369 return g_err("gc1", __FUNCTION__, ret);
370
9e4dddbd
JH
371 ele[siz] = '\0';
372 list = string_append_listele(list, sep, ele);
9d1c15ef 373 }
9e4dddbd 374/*NOTREACHED*/
9d1c15ef
JH
375}
376
377
6a8a60e0
JH
378/*****************************************************
379* Certificate operator routines
380*****************************************************/
381static uschar *
382fingerprint(gnutls_x509_crt_t cert, gnutls_digest_algorithm_t algo)
383{
384int ret;
385size_t siz = 0;
386uschar * cp;
387uschar * cp2;
388uschar * cp3;
389
390if ((ret = gnutls_x509_crt_get_fingerprint(cert, algo, NULL, &siz))
391 != GNUTLS_E_SHORT_MEMORY_BUFFER)
8a6eec04
JH
392 return g_err("gf0", __FUNCTION__, ret);
393
6a8a60e0
JH
394cp = store_get(siz*3+1);
395if ((ret = gnutls_x509_crt_get_fingerprint(cert, algo, cp, &siz)) < 0)
8a6eec04
JH
396 return g_err("gf1", __FUNCTION__, ret);
397
6a8a60e0
JH
398for (cp3 = cp2 = cp+siz; cp < cp2; cp++, cp3+=2)
399 sprintf(cp3, "%02X",*cp);
400return cp2;
401}
402
403
404uschar *
405tls_cert_fprt_md5(void * cert)
406{
407return fingerprint((gnutls_x509_crt_t)cert, GNUTLS_DIG_MD5);
408}
409
410uschar *
411tls_cert_fprt_sha1(void * cert)
412{
413return fingerprint((gnutls_x509_crt_t)cert, GNUTLS_DIG_SHA1);
414}
415
9ef9101c
JH
416uschar *
417tls_cert_fprt_sha256(void * cert)
418{
419return fingerprint((gnutls_x509_crt_t)cert, GNUTLS_DIG_SHA256);
420}
421
6a8a60e0 422
9d1c15ef
JH
423/* vi: aw ai sw=2
424*/
425/* End of tlscert-gnu.c */