Obtaining SSL certificate info from SSL object - BUG?

John Nagle nagle at animats.com
Tue Oct 24 19:50:47 CEST 2006


Donn Cave wrote:
> In article <453D95EA.1020602 at animats.com>,
>  John Nagle <nagle at animats.com> wrote:
> 
> 
>>     The Python SSL object offers two methods from obtaining
>>the info from an SSL certificate, "server()" and "issuer()".
>>The actual values in the certificate are a series of name/value
>>pairs in ASN.1 binary format.  But what "server()" and "issuer()"
>>return are strings, with the pairs separated by "/".  The
>>documentation at "http://docs.python.org/lib/ssl-objects.html"
>>says "Returns a string containing the ASN.1 distinguished name identifying 
>>the 
>>server's certificate.
> 
> ...
> 
>>"/O=VeriSign Trust Network/OU=VeriSign, Inc./OU=VeriSign International
>>Server CA - Class 3/OU=www.verisign.com/CPS Incorp.by Ref. LIABILITY 
>>LTD.(c)97 
>>VeriSign".
>>
>>Note that
>>
>>    "OU=Terms of use at www.verisign.com/rpa (c)00"
>>
>>with a "/" in the middle of the value field.
> 
> ...
> 
>>Is there a workaround for this?  Without rebuilding Python
>>and becoming incompatible?
> 
> 
> As a practical matter, I think it's fairly safe to assume
> there will be no values that include / in a context like
> really looks like that X.500 style distinguished name.

    Actually, we've just discovered an exploit.  By
ordering a low-level certificate with a "/" in the right
place, you can create the illusion (at least for flawed
implementations like this one) that the certificate
belongs to someone else.  Just order a certificate from
GoDaddy, enter something like this in the "Name" field

	"Myphonyname/C=US/ST=California/L=San Jose/O=eBay Inc./OU=Site
  	Operations/CN=signin.ebay.com"

and Python code will be spoofed into thinking you're eBay.

    Fortunately, browsers don't use Python code.

    The actual bug is in

	python/trunk/Modules/_ssl.c

at

	if ((self->server_cert = SSL_get_peer_certificate(self->ssl))) {
		X509_NAME_oneline(X509_get_subject_name(self->server_cert),
				  self->server, X509_NAME_MAXLEN);
		X509_NAME_oneline(X509_get_issuer_name(self->server_cert),
				  self->issuer, X509_NAME_MAXLEN);

The "X509_name_oneline" function takes an X509_NAME structure, which is
the certificate system's representation of a list, and flattens it
into a printable string.  This is a debug function, not one for use in
production code.  The SSL documentation for "X509_name_oneline" says:	

	"The functions X509_NAME_oneline() and X509_NAME_print() are legacy
	functions which produce a non standard output form, they don't handle
	multi character fields and have various quirks and inconsistencies.
	Their use is strongly discouraged in new applications."

What OpenSSL callers are supposed to do is call X509_NAME_entry_count()
to get the number of entries in an X509_NAME structure, then get each
entry with X509_NAME_get_entry().  A few more calls will obtain
the name/value pair from the entry, as UTF8 strings, which should
be converted to Python UNICODE strings.

X509_NAME_oneline() doesn't handle Unicode; it converts non-ASCII
values to "\xnn" format. Again, it's for debug output only.

So what's needed are two new functions for Python's SSL sockets to
replace "issuer" and "server".  The new functions should return
lists of Unicode strings representing the key/value pairs.
(A list is needed, not a dictionary; two strings with the same key
are both possible and common.)

The reason this now matters is that new "high assurance" certs,
the ones that tell you how much a site can be trusted, are now being
deployed, and to use them effectively, you need that info.  Support for
them is in Internet Explorer 7, so they're going to be widespread soon.
Python needs to catch up.

I'll submit a bug report.

				John Nagle
				Animats



More information about the Python-list mailing list