Post-connection assertions Step 2: Peer Authentication

111 presenting certificates of some smaller chain length, it is a good idea to set the value to exclude certificates composed of longer chains from being verified successfully. Setting the depth to zero allows chains of unlimited length to be used. There is a known security vulnerability in SSL_CTX_set_verify_depth in versions of OpenSSL prior to 0.9.6. The problem stemmed from the fact that the internal verification routine did not properly check extensions on peer certificate chains; it approved certificate chains that contained non-CA certificates as long as they led to a trusted root CA. Thus, using any verification depth greater than one left the application susceptible to attack from anyone signed by the trusted root CA. Since this problem has been fixed in newer versions of OpenSSL by checking the X509v3 fields regarding CA authorization, this vulnerability should be of only academic interest.

5.1.3.4 Incorporating certificate revocation lists

A large problem with SSL security is the availability and usage of certificate revocation lists. Since certificates can be revoked by the issuing CA, we must somehow account for this in our SSL implementation. To do this, an application must load CRL files in order for the internal verification process to ensure each certificate it verifies is not revoked. Unfortunately, OpenSSLs CRL functionality is incomplete in Version 0.9.6. The features necessary to utilize CRL information will be complete in new versions starting with 0.9.7. Because this functionality is not available at the time of writing, CRL usage will not be incorporated in this example. However, we can tell you what you will need to do once newer releases are made. Remember, it is paramount to include CRL checking when verifying certificates. If any new version of OpenSSL is used when building applications, this step is required for security. The SSL interface itself does not support CRL incorporation directly; instead, we must use the underlying X509 interface. The function SSL_CTX_get_cert_store retrieves the internal X509_STORE object from the SSL_CTX object. Operations on this store object allow us to perform a variety of tweaks on the verification process. In fact, the functions SSL_CTX_load_verify_locations and SSL_CTX_set_default_paths call functions against this same X509_STORE object to perform their respective operations. X509_STORE SSL_CTX_get_cert_storeSSL_CTX ctx; All of the details for interacting with certificate stores to set further verification parameters or incorporate CRL data are discussed in Chapter 10 with the verification of certificate chains. We strongly recommend that developers implementing applications consult the verification process in Chapter 10 that uses X509_STORE objects to learn the proper method of SSL certificate verification against CRLs. The process involves adding the CRL files to the X509_STORE via a file lookup method and then setting the stores flags to check certificates against the CRLs.

5.1.3.5 Post-connection assertions

Essentially, SSL_CTX_set_verify and SSL_CTX_set_verify_depth are all we need to use in order for OpenSSL to verify the peer certificate chain upon connection. There is more, however. After connecting the SSL object, we need to assert that some assumed properties about the connection are indeed true. OpenSSL provides several functions that allow us to create a post- 112 connection verification routine to make sure that we havent been fooled by a malicious peer. This post-connection verification routine is very important because it allows for much finer grained control over the certificate that is presented by the peer, beyond the certificate verification that is required by the SSL protocol proper. The function SSL_get_peer_certificate will return a pointer to an X509 object that contains the peers certificate. While the handshake is complete and, presumably, the verification completed correctly, we must still use this function. Consider the case in which the peer presents no certificate when one is requested but not required. The certificate verification routines—both the built-in and the filter—will not return errors since there was nothing wrong with the NULL certificate. Thus, to prevent this condition, we must call this function and check that the return value is not NULL . If this function returns a non- NULL value, the reference count of the return object is increased. In order to prevent memory leaks, we must call X509_free to decrement the count after were done using the object. Our application will be vulnerable if we do not check the peer certificate beyond verification of the chain. For example, lets say that were making a web browsing application. To keep it simple, well allow just one trusted CA. When we do this, any SSL peer with a certificate signed by the same CA will be verified correctly. This isnt secure. Nothing prevents an attacker from getting his own certificate signed by the CA and then hijacking all your sessions. We thwart this kind of masquerade by tying the certificate to some piece of information unique to the machine. For purposes of SSL, this piece of information is the entitys fully qualified domain name FQDN, also called the DNS name. The common practice with X.509v1 certificates was to put the FQDN in the certificates commonName field of the subjectName field. This practice is no longer recommended for new applications since X.509v3 allows certificate extensions to hold the FQDN as well as other identifying information, such as IP address. The proper place for the FQDN is in the dNSName field of the subjectAltName extension. We use the function post_connection_check to perform these checks for us. We recommend always checking for the dNSName field first, and if it isnt present, we can check the commonName field. Checking the commonName field is strictly for backward compatibility, so if this isnt a concern, it can safely be omitted. Our example function will check for the extension first and then fall back to the commonName . One feature our example does omit is the optional wildcard expansion. RFC 2818 specifies a paradigm for allowing FQDNs in certificates to contain wildcards. Implementing this functionality is simply a text-processing issue and is thus omitted for clarity. SSL_get_verify_result is another API function that we will employ in our post-connection check. This function returns the error code last generated by the verification routines. If no error has occurred, X509_V_OK is returned. We should call this function and make sure the returned value equals X509_V_OK . When browsing the example application, it is obvious that robust error handling has been left out for clarity. For example, the programs simply exit when an error occurs. In most cases, we will want to do something better to handle errors in some application-specific way. Checking the verify result is always a good idea. It makes an assertion that no matter what error handling occurred up to this point, if the result isnt OK now, we should disconnect. Example 5-8 shows a function that performs the checks that weve just described. In the example, well check to be sure that the certificate contains the FQDN of the peer to which we expect to be connecting. For the client, well want to make sure that the server presents a certificate that contains the FQDN of the servers address. Likewise, for the server, well want to make sure that the client presents a certificate that contains the FQDN of the clients address. In this case, our checking of the client certificate will be very strict because well be expecting the client to be 113 using a specific FQDN, and well allow only that one. For the purposes of our example client and server, this function should appear in common.c and be prototyped in common.h. Example 5-8. A function to do post-connection assertions implemented in common.c and prototyped in common.h long post_connection_checkSSL ssl, char host { X509 cert; X509_NAME subj; char data[256]; int extcount; int ok = 0; Checking the return from SSL_get_peer_certificate here is not strictly necessary. With our example programs, it is not possible for it to return NULL. However, it is good form to check the return since it can return NULL if the examples are modified to enable anonymous ciphers or for the server to not require a client certificate. if cert = SSL_get_peer_certificatessl || host goto err_occured; if extcount = X509_get_ext_countcert 0 { int i; for i = 0; i extcount; i++ { char extstr; X509_EXTENSION ext; ext = X509_get_extcert, i; extstr = OBJ_nid2snOBJ_obj2nidX509_EXTENSION_get_objectext; if strcmpextstr, subjectAltName { int j; unsigned char data; STACK_OFCONF_VALUE val; CONF_VALUE nval; X509V3_EXT_METHOD meth; if meth = X509V3_EXT_getext break; data = ext-value-data; val = meth-i2vmeth, meth-d2iNULL, data, ext-value- length, NULL; for j = 0; j sk_CONF_VALUE_numval; j++ { nval = sk_CONF_VALUE_valueval, j; if strcmpnval-name, DNS strcmpnval- value, host { ok = 1; break; 114 } } } if ok break; } } if ok subj = X509_get_subject_namecert X509_NAME_get_text_by_NIDsubj, NID_commonName, data, 256 { data[255] = 0; if strcasecmpdata, host = 0 goto err_occured; } X509_freecert; return SSL_get_verify_resultssl; err_occured: if cert X509_freecert; return X509_V_ERR_APPLICATION_VERIFICATION; } At a high level, the function post_connection_check is implemented as a wrapper around SSL_get_verify_result , which performs our extra peer certificate checks. It uses the reserved error code X509_V_ERR_APPLICATION_VERIFICATION to indicate errors where there is no peer certificate present or the certificate presented does not match the expected FQDN. This function will return an error in the following circumstances: • If no peer certificate is found • If it is called with a NULL second argument, i.e., if no FQDN is specified to compare against • If the dNSName fields found if any do not match the host argument and the commonName also doesnt match the host argument if found • Any time the SSL_get_verify_result routine returns an error As long as none of the above errors occurs, the value X509_V_OK will be returned. Our example programs are further extended below to employ this function. Unfortunately, the code to check the dNSName is not very clear. We use the X509 functions to access the extensions. We then iterate through the extensions and use the extension-specific parsing routines to find all extensions that are subjectAltName fields. Since a subjectAltName field may itself contain several fields, we must then iterate through those fields to find any dNSName fields they are tagged with the short name DNS . Since it is rather confusing, we will step through this function to explain its behavior. Having a stronger understanding of some of the more advanced programming techniques presented in Chapter 10 will help demystify the implementation of this function. At the onset, we simply get the peer certificate from the SSL object. If the function X509_get_ext_count returns a positive number, we know there are some X.509v3 extensions present in the peers certificate. Given this, we iterate over the extensions to look for a subjectAltName . The function X509_get_ext will retrieve an extension for us based on the counter. We also use the variable extstr to hold the extracted short name of the extension. Unfortunately, we must use three functions to perform this task: the innermost extracts the 115 ASN1_OBJECT , the next fetches the NID , and the outermost function gets the short name as a const char from the NID . Next, we check if the extension is what were looking for by comparing the short name against the string constant subjectAltName . Once were sure its the right extension, we need to extract the X509V3_EXT_METHOD object from the extension. This object is a container of extension-specific functions for manipulating the data within the extension. We access the data we wish to manipulate directly through the value member of the X509_EXTENSION structure. The d2i and i2v functions serve the purpose of converting the raw data in the subjectAltName to a stack of CONF_VALUE objects. This is necessary to make it simple to iterate over the several kinds of fields in the subjectAltName so that we may find the dNSName fields. We check each member of this CONF_VALUE stack to see if we have a match for the host string in a dNSName field. Keep in mind that the dNSName field is named DNS in the extension itself, since its referenced by its short name. As soon as we find a match, we stop the iterations over all the extensions. We only pursue checking the commonName of the certificate if no match is found in a dNSName field. If we fail to find an FQDN that matches the host argument in either the dNSName field or the commonName , we return the code for an application-specific error. In most real-world cases, matching one specific FQDN is not desirable. Most often, a server would have a list known as a whitelist that contains all of the acceptable FQDNs for connecting clients. If the clients certificate contains an FQDN that appears on this list, the certificate is accepted; otherwise, it is rejected.

5.1.3.6 Further extension of the examples