Skip to content

Commit 0ee7198

Browse files
committed
Merge branch 'ob/imap-send-ssl-verify' into maint
* ob/imap-send-ssl-verify: imap-send: support subjectAltName as well imap-send: the subject of SSL certificate must match the host imap-send: move #ifdef around
2 parents 7ed1690 + e174744 commit 0ee7198

File tree

1 file changed

+65
-2
lines changed

1 file changed

+65
-2
lines changed

imap-send.c

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ typedef void *SSL;
3131
#else
3232
#include <openssl/evp.h>
3333
#include <openssl/hmac.h>
34+
#include <openssl/x509v3.h>
3435
#endif
3536

3637
struct store_conf {
@@ -266,19 +267,72 @@ static void socket_perror(const char *func, struct imap_socket *sock, int ret)
266267
}
267268
}
268269

270+
#ifdef NO_OPENSSL
269271
static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
270272
{
271-
#ifdef NO_OPENSSL
272273
fprintf(stderr, "SSL requested but SSL support not compiled in\n");
273274
return -1;
275+
}
276+
274277
#else
278+
279+
static int host_matches(const char *host, const char *pattern)
280+
{
281+
if (pattern[0] == '*' && pattern[1] == '.') {
282+
pattern += 2;
283+
if (!(host = strchr(host, '.')))
284+
return 0;
285+
host++;
286+
}
287+
288+
return *host && *pattern && !strcasecmp(host, pattern);
289+
}
290+
291+
static int verify_hostname(X509 *cert, const char *hostname)
292+
{
293+
int len;
294+
X509_NAME *subj;
295+
char cname[1000];
296+
int i, found;
297+
STACK_OF(GENERAL_NAME) *subj_alt_names;
298+
299+
/* try the DNS subjectAltNames */
300+
found = 0;
301+
if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {
302+
int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);
303+
for (i = 0; !found && i < num_subj_alt_names; i++) {
304+
GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
305+
if (subj_alt_name->type == GEN_DNS &&
306+
strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&
307+
host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))
308+
found = 1;
309+
}
310+
sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
311+
}
312+
if (found)
313+
return 0;
314+
315+
/* try the common name */
316+
if (!(subj = X509_get_subject_name(cert)))
317+
return error("cannot get certificate subject");
318+
if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
319+
return error("cannot get certificate common name");
320+
if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
321+
return 0;
322+
return error("certificate owner '%s' does not match hostname '%s'",
323+
cname, hostname);
324+
}
325+
326+
static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
327+
{
275328
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
276329
const SSL_METHOD *meth;
277330
#else
278331
SSL_METHOD *meth;
279332
#endif
280333
SSL_CTX *ctx;
281334
int ret;
335+
X509 *cert;
282336

283337
SSL_library_init();
284338
SSL_load_error_strings();
@@ -322,9 +376,18 @@ static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int ve
322376
return -1;
323377
}
324378

379+
if (verify) {
380+
/* make sure the hostname matches that of the certificate */
381+
cert = SSL_get_peer_certificate(sock->ssl);
382+
if (!cert)
383+
return error("unable to get peer certificate.");
384+
if (verify_hostname(cert, server.host) < 0)
385+
return -1;
386+
}
387+
325388
return 0;
326-
#endif
327389
}
390+
#endif
328391

329392
static int socket_read(struct imap_socket *sock, char *buf, int len)
330393
{

0 commit comments

Comments
 (0)