Support subjectAltName in SSL certificates

Signed-off-by: Thomas Jost <schnouki@schnouki.net>
Reviewed-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
Signed-off-by: Nicolas Sebrecht <nicolas.s-dev@laposte.net>
This commit is contained in:
Thomas Jost 2011-02-23 10:00:14 +01:00 committed by Nicolas Sebrecht
parent 37e5367966
commit 838a67bc40
2 changed files with 23 additions and 9 deletions

View File

@ -13,6 +13,8 @@ others.
New Features New Features
------------ ------------
* SSL: support subjectAltName.
Changes Changes
------- -------

View File

@ -17,10 +17,9 @@
import re, socket, time, subprocess import re, socket, time, subprocess
from offlineimap.ui import getglobalui from offlineimap.ui import getglobalui
from imaplib import *
# Import the symbols we need that aren't exported by default # Import the symbols we need that aren't exported by default
from imaplib import IMAP4_PORT, IMAP4_SSL_PORT, InternalDate, Mon2num from imaplib import IMAP4_PORT, IMAP4_SSL_PORT, InternalDate, Mon2num, IMAP4, IMAP4_SSL
try: try:
import ssl import ssl
@ -149,22 +148,35 @@ class WrappedIMAP4_SSL(IMAP4_SSL):
def _verifycert(self, cert, hostname): def _verifycert(self, cert, hostname):
'''Verify that cert (in socket.getpeercert() format) matches hostname. '''Verify that cert (in socket.getpeercert() format) matches hostname.
CRLs and subjectAltName are not handled. CRLs are not handled.
Returns error message if any problems are found and None on success. Returns error message if any problems are found and None on success.
''' '''
if not cert: if not cert:
return ('no certificate received') return ('no certificate received')
dnsname = hostname.lower() dnsname = hostname.lower()
certnames = []
# First read commonName
for s in cert.get('subject', []): for s in cert.get('subject', []):
key, value = s[0] key, value = s[0]
if key == 'commonName': if key == 'commonName':
certname = value.lower() certnames.append(value.lower())
if (certname == dnsname or if len(certnames) == 0:
'.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]): return ('no commonName found in certificate')
return None
return ('certificate is for %s') % certname # Then read subjectAltName
return ('no commonName found in certificate') for key, value in cert.get('subjectAltName', []):
if key == 'DNS':
certnames.append(value.lower())
# And finally try to match hostname with one of these names
for certname in certnames:
if (certname == dnsname or
'.' in dnsname and certname == '*.' + dnsname.split('.', 1)[1]):
return None
return ('no matching domain name found in certificate')
def _read_upto (self, n): def _read_upto (self, n):
"""Read up to n bytes, emptying existing _readbuffer first""" """Read up to n bytes, emptying existing _readbuffer first"""