s/qmail 4.2.29a
Next generation secure email transport
Loading...
Searching...
No Matches
tls_remote.c
Go to the documentation of this file.
1#include <unistd.h>
2#include "ucspissl.h"
3#include "fmt.h"
4#include "stralloc.h"
5#include "str.h"
6#include "byte.h"
7#include "case.h"
8#include "dns.h"
9#include "constmap.h"
10#include "tls_remote.h"
11#include "tls_errors.h"
12
24/* Caution: OpenSSL's X509_pubkey_digest() does not work as expected.
25 I've included now: X509_pkey_digest() and X509_cert_digest() (as makro) */
26
27#define X509_cert_digest X509_digest
28
29int tls_certkey(SSL_CTX *ctx,const char *cert,const char *key,char *ppwd)
30{
31 if (!cert) return 0;
32
33 if (SSL_CTX_use_certificate_chain_file(ctx,cert) != 1)
34 return -1;
35
36 if (!key) key = cert;
37
38 if (ppwd) SSL_CTX_set_default_passwd_cb_userdata(ctx,ppwd);
39
40 if (SSL_CTX_use_PrivateKey_file(ctx,key,SSL_FILETYPE_PEM) != 1)
41 return -2;
42
43 if (SSL_CTX_check_private_key(ctx) != 1)
44 return -3;
45
46 return 0;
47}
48
49int tls_conn(SSL *ssl,int smtpfd)
50{
51 SSL_set_options(ssl,SSL_OP_NO_SSLv2);
52 SSL_set_options(ssl,SSL_OP_NO_SSLv3);
53 return SSL_set_fd(ssl,smtpfd);
54}
55
56int tls_checkpeer(SSL *ssl,X509 *cert,const stralloc host,const int flag,const int verify)
57{
58 STACK_OF(GENERAL_NAME) *extensions;
59 const GENERAL_NAME *ext;
60 char buf[SSL_NAME_LEN];
61 char *dnsname = 0;
62 int dname = 0;
63 int num;
64 int len;
65 int fflag;
66 int i;
67 int rc = 0;
68
69 fflag = flag;
70 if (flag > 20) fflag = flag - 20;
71 if (flag > 10) fflag = flag - 10;
72
73 /* X.509 CA DN/SAN name validation against DNS */
74
75 if (host.len && fflag > 4) {
76 extensions = (GENERAL_NAME *)X509_get_ext_d2i(cert,NID_subject_alt_name,0,0);
77 num = sk_GENERAL_NAME_num(extensions); /* num = 0, if no SAN extensions */
78
79 for (i = 0; i < num; ++i) {
80 ext = sk_GENERAL_NAME_value(extensions,i);
81 if (ext->type == GEN_DNS) {
82 #if (OPENSSL_VERSION_NUMBER < 0x10100000L) // 0xmnnffppsL
83 if (ASN1_STRING_type(ext->d.ia5) != V_ASN1_IA5STRING) continue;
84 dnsname = (char *)ASN1_STRING_data(ext->d.ia5);
85 #else
86 if (OBJ_sn2nid((const char*)ext->d.ia5) != V_ASN1_IA5STRING) continue;
87 dnsname = (char *)ASN1_STRING_get0_data(ext->d.ia5);
88 #endif
89 len = ASN1_STRING_length(ext->d.ia5);
90 dname = 1;
91 }
92 }
93
94 if (!dname) {
95 X509_NAME_get_text_by_NID(X509_get_subject_name(cert),NID_commonName,buf,sizeof(buf));
96 buf[SSL_NAME_LEN - 1] = 0;
97 dnsname = buf;
98 len = SSL_NAME_LEN - 1;
99 }
100
101 switch (fflag) {
102 case 5: if (dnsname[0] == '*' && dnsname[1] == '.')
103 if (case_diffrs(dnsname + 1,host.s)) return -3;
104 if (case_diffrs(dnsname,host.s)) return -3;
105 rc = 3; break;
106 case 6: if (case_diffs(dnsname,host.s)) return -3;
107 rc = 2; break;
108 }
109 }
110
111 /* X.509 CA Verification: root CA must be available */
112
113 if (fflag > 3 && verify > -2) {
114 if (SSL_get_verify_result(ssl) != X509_V_OK) return -2;
115 else rc = 1;
116 }
117
118 return rc;
119}
120
121int tls_checkcrl(SSL *ssl) // not implemented yet
122{
123
124 return 0;
125}
126
127int dig_ascii(char *digascii,const char *digest,const int len)
128{
129 static const char hextab[] = "0123456789abcdef";
130 int j;
131
132 for (j = 0; j < len; j++) {
133 digascii[2 * j] = hextab[(unsigned char)digest[j] >> 4];
134 digascii[2 * j + 1] = hextab[(unsigned char)digest[j] & 0x0f];
135 }
136 digascii[2 * len] = '\0';
137
138 return (2 * j); // 2*len
139}
140
141/* X509_pkey_digest() takes the same args as X509_digest();
142 however returning the correct hash of pubkey in md.
143 Subjects keys are restricted to 2048 byte in size.
144 Return codes: 1: sucess, 0: failed. */
145
146int X509_pkey_digest(const X509 *cert,const EVP_MD *type,unsigned char *md,unsigned int *dlen)
147{
148 unsigned int len = 0;
149 unsigned int size = 2048;
150 unsigned char *buf;
151 unsigned char *buf2;
152 unsigned char buffer[size]; // avoid malloc
153
154/* Following Viktor's suggestion */
155
156 if (!X509_get0_pubkey_bitstr(cert)) return 0; // no Subject public key
157
158 len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert),0);
159 if (len > size) return 0;
160 buf2 = buf = buffer;
161 i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert),(unsigned char **)&buf2);
162 if (buf2 - buf != len) return 0;
163
164 if (!EVP_Digest(buf,len,md,dlen,type,0)) return 0; // OpenSSL voodoo
165 return 1;
166}
167
168/* Return codes: -4: no X.509 cert (fatal), -3: matching error (deferred),
169 -2: unsupported type, -1: weird TLSA record
170 0: No X.509 cert; seen: usage++; */
171
172int tlsa_check(const STACK_OF(X509) *certs,const stralloc host,const unsigned long p)
173{
174 const EVP_MD *methodsha256 = EVP_sha256();
175 const EVP_MD *methodsha512 = EVP_sha512();
176 stralloc out = {0};
177 stralloc sa = {0};
178 stralloc cn = {0};
179 unsigned char digest[EVP_MAX_MD_SIZE];
180 unsigned int dlen = 0;
181 unsigned int n = 0;
182 int i = 0;
183 int r;
184 char port[FMT_ULONG];
185 uint16 type;
186 uint16 selector;
187 uint16 usage;
188
189// construct TLSA FQDN -- simple procedure; returning Usage
190
191 if (host.len < 2) return 0;
192 if (!stralloc_copyb(&sa,"_",1)) temp_nomem();
193 port[fmt_ulong(port,p)] = 0;
194 if (!stralloc_cats(&sa,port)) temp_nomem();
195 if (!stralloc_cats(&sa,"._tcp.")) temp_nomem();
196 if (!stralloc_cats(&sa,host.s)) temp_nomem();
197
198 if (dns_cname(&cn,&sa) > 0) // query name could be a cname
199 { if (dns_tlsa(&out,&cn) <= 0) return 0; }
200 else
201 { if (dns_tlsa(&out,&sa) <= 0) return 0; }
202 if (out.len < 5) return -1;
203
204 /* https://www.openssl.org/docs/man3.0/man3/X509_digest.html (1.1.1):
205 "The len parameter, if not NULL, points to a place where the digest size will be stored."
206 [sigh]
207 */
208
209 do {
210 usage = (unsigned char) out.s[i]; // Usage: PKIX-TA [0], PKIX-EE [1], DANE-TA [2], DANE-EE [3]
211 selector = (unsigned char) out.s[i + 1]; // Selector: 0 = Cert, 1 = SPKI
212 type = (unsigned char) out.s[i + 2]; // Type: 0/1/2 = [Cert|SPKI]/SHA256/SHA512
213
214 unsigned len = sk_X509_num(certs);
215 for (n = 0; n < len; n++) {
216 X509 *cert = sk_X509_value(certs,n);
217 if (type == 1) {
218 if (selector == 0) r = X509_cert_digest(cert,methodsha256,digest,&dlen);
219 if (selector == 1) r = X509_pkey_digest(cert,methodsha256,digest,&dlen);
220 } else if (type == 2) {
221 if (selector == 0) r = X509_cert_digest(cert,methodsha512,digest,&dlen);
222 if (selector == 1) r = X509_pkey_digest(cert,methodsha512,digest,&dlen);
223 } else
224 return -2;
225
226 if (!byte_diff(digest,dlen,out.s + i + 3)) return ++usage;
227 }
228
229 i += (dlen + 3);
230 } while (i < out.len - 4);
231
232 return -3;
233}
234
235int tls_fingerprint(X509 *cert,const char *fingerprint,int dlen)
236{
237 const EVP_MD *methodsha1 = EVP_sha1();
238 const EVP_MD *methodsha224 = EVP_sha224();
239 const EVP_MD *methodsha256 = EVP_sha256();
240 const EVP_MD *methodsha512 = EVP_sha512();
241 unsigned char digest[EVP_MAX_MD_SIZE];
242 unsigned char digascii[257];
243 unsigned int len;
244
245 switch (dlen) { /* fetch digest from cert; len = bitlength/8 */
246 case 40: if (!X509_digest(cert,methodsha1,digest,&len)) return -2;
247 case 56: if (!X509_digest(cert,methodsha224,digest,&len)) return -2;
248 case 64: if (!X509_digest(cert,methodsha256,digest,&len)) return -2;
249 case 128: if (!X509_digest(cert,methodsha512,digest,&len)) return -2;
250 default: return -3;
251 }
252
253 len = dig_ascii(digascii,digest,len);
254 if (!str_diffn(digascii,fingerprint,len)) return 1;
255
256 return 0;
257}
258
259int tls_exit(SSL *ssl)
260{
261 if (SSL_shutdown(ssl) == 0)
262 SSL_shutdown(ssl);
263
264 return 0;
265}
266
280int tls_destination(const stralloc hostname)
281{
282 int i;
283 stralloc tlshost = {0};
284 stralloc tlsdest = {0};
285
286 if (!stralloc_copy(&tlshost,&hostname)) temp_nomem();
287 if (!stralloc_0(&tlshost)) temp_nomem();
288
289// Host rules
290
291 if (!stralloc_copys(&tlsdest,"!")) temp_nomem();
292 if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem();
293 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return -1;
294
295 if (!stralloc_copys(&tlsdest,"?")) temp_nomem();
296 if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem();
297 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 9;
298
299 if (!stralloc_copys(&tlsdest,"/")) temp_nomem();
300 if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem();
301 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 8;
302
303 if (!stralloc_copys(&tlsdest,"%")) temp_nomem(); // CERT + hash
304 if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem();
305 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 7;
306
307 if (!stralloc_copys(&tlsdest,"=")) temp_nomem(); // CERT + FQDN
308 if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem();
309 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 6;
310
311 if (!stralloc_copys(&tlsdest,"~")) temp_nomem(); // CERT + Wild
312 if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem();
313 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 5;
314
315// Domain rules
316
317 for (i = 0; i < tlshost.len; ++i) // TLS fallthru
318 if ((i == 0) || (tlshost.s[i] == '.')) {
319 if (!stralloc_copys(&tlsdest,"?")) temp_nomem();
320 if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem();
321 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 9;
322 }
323
324 for (i = 0; i < tlshost.len; ++i) // no TLSA
325 if ((i == 0) || (tlshost.s[i] == '.')) {
326 if (!stralloc_copys(&tlsdest,"/")) temp_nomem();
327 if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem();
328 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 8;
329 }
330
331 for (i = 0; i < tlshost.len; ++i) // CERT + Wild
332 if ((i == 0) || (tlshost.s[i] == '.')) {
333 if (!stralloc_copys(&tlsdest,"~")) temp_nomem();
334 if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem();
335 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 5;
336 }
337
338 for (i = 0; i < tlshost.len; ++i) // CERT - generic
339 if ((i == 0) || (tlshost.s[i] == '.')) {
340 if (!stralloc_copys(&tlsdest,"")) temp_nomem();
341 if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem();
342 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 4;
343 }
344
345 for (i = 0; i < tlshost.len; ++i) // ADH per host/domain
346 if ((i == 0) || (tlshost.s[i] == '.')) {
347 if (!stralloc_copys(&tlsdest,"-")) temp_nomem();
348 if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem();
349 if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 2;
350 }
351
352// General rules (mandatory TLS)
353
354 tlsdestinfo = 0;
355 if (constmap(&maptlsdestinations,"/*",2)) return 8; // no TLSA
356 if (constmap(&maptlsdestinations,"=*",2)) return 6; // CERT + FQDN
357 if (constmap(&maptlsdestinations,"~*",2)) return 5; // CERT + Wild
358 if (constmap(&maptlsdestinations,"+*",2)) return 4; // CERT
359 if (constmap(&maptlsdestinations,"-*",2)) return 2; // ADH
360
361// Fall thru rules (optional TLS)
362
363 if (constmap(&maptlsdestinations,"?",1)) return 9; // fallback to no TLS
364 if (constmap(&maptlsdestinations,"*",1)) return 3; // CERT
365 if (constmap(&maptlsdestinations,"-",1)) return 1; // ADH
366
367 return 0;
368}
369
370int tls_domaincerts(const stralloc domainname)
371{
372 int i;
373 tlsdomaininfo = 0; // extern
374
375/* Our Certs - per domain */
376
377 if (domainname.len)
378 for (i = 0; i < domainname.len; ++i)
379 if ((i == 0) || (domainname.s[i] == '.'))
380 if ((tlsdomaininfo = constmap(&mapdomaincerts,domainname.s + i,domainname.len - i))) return 2;
381
382/* Standard Cert (if any) */
383
384 if ((tlsdomaininfo = constmap(&mapdomaincerts,"*",1))) return 1;
385
386 return 0;
387}
char num[FMT_ULONG]
Definition: chkspawn.c:8
int stralloc_copys(stralloc *, char const *)
int dns_tlsa(stralloc *out, const stralloc *fqdn)
Definition: dns_tlsa.c:41
stralloc out
Definition: dnscname.c:12
stralloc sa
Definition: dnscname.c:11
stralloc key
Definition: fastforward.c:116
uint32 dlen
Definition: fastforward.c:117
char buf[100+FMT_ULONG]
Definition: hier.c:10
void p(char *, char *, int, int, int)
Definition: install.c:39
char host[256]
Definition: hostname.c:5
void usage()
Definition: newinclude.c:24
stralloc selector
Definition: qmail-dksign.c:237
char * ext
Definition: qmail-local.c:60
stralloc hostname
Definition: qmail-local.c:75
unsigned int port
char buf2[100]
Definition: qmail-qmtpd.c:77
unsigned long size
Definition: qmail-qread.c:55
int smtpfd
Definition: qmail-remote.c:281
SSL_CTX * ctx
Definition: qmail-remote.c:106
SSL * ssl
Definition: qmail-remote.c:105
int j
Definition: qmail-send.c:920
stralloc tlsdest
Definition: qmail-smtpam.c:282
stralloc dnsname
Definition: spf.c:27
void temp_nomem(void)
Definition: qmail-dksign.c:81
int tls_exit(SSL *ssl)
Definition: tls_remote.c:259
int tls_domaincerts(const stralloc domainname)
Definition: tls_remote.c:370
int tls_fingerprint(X509 *cert, const char *fingerprint, int dlen)
Definition: tls_remote.c:235
int tls_checkcrl(SSL *ssl)
Definition: tls_remote.c:121
int X509_pkey_digest(const X509 *cert, const EVP_MD *type, unsigned char *md, unsigned int *dlen)
Definition: tls_remote.c:146
int tls_conn(SSL *ssl, int smtpfd)
Definition: tls_remote.c:49
int tls_destination(const stralloc hostname)
tls_destination
Definition: tls_remote.c:280
int tlsa_check(const STACK_OF(X509) *certs, const stralloc host, const unsigned long p)
Definition: tls_remote.c:172
int dig_ascii(char *digascii, const char *digest, const int len)
Definition: tls_remote.c:127
int tls_certkey(SSL_CTX *ctx, const char *cert, const char *key, char *ppwd)
Definition: tls_remote.c:29
int tls_checkpeer(SSL *ssl, X509 *cert, const stralloc host, const int flag, const int verify)
Definition: tls_remote.c:56
#define X509_cert_digest
Definition: tls_remote.c:27
char * tlsdomaininfo
Definition: qmail-remote.c:411
struct constmap maptlsdestinations
Definition: qmail-remote.c:416
struct constmap mapdomaincerts
Definition: qmail-remote.c:414
char * tlsdestinfo
Definition: qmail-remote.c:410
#define SSL_NAME_LEN
Definition: ucspitls.h:9