/* certinfo.c -- Functions to parse and process the X509 TLS id string * * GPLv2 only - Copyright (C) 2008 - 2012 * David Sommerseth * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /** * @file certinfo.c * @author David Sommerseth * @date 2008-08-06 * * @brief Functions for parsing X.509 subject information * */ #include #include #include #include #include #include /** * Simple strcmp() wrapper, which makes it NULL safe. * * @param s input value * @param v compare value * * @return Returns 1 if s and v are equal, otherwise 0. */ #define comp_attrib(s, v) ( (v == NULL || strlen_nullsafe(v) < 1) ? 0 : (strcmp(v, s) == 0) ) /** * Parses a X.509 subject string into a certinfo structure * * @param input String containing a X.509 subject line * * @return Pointer to a certinfo structure containing the information */ certinfo *parse_tlsid(const char *input, const char *digest) { char tmp[130], *mainp, *origptr, *sub, *tok, *tok2; certinfo *ret = NULL; if( (input == NULL) || strlen(input) < 5) return NULL; ret = (certinfo *) malloc_nullsafe(NULL, sizeof(certinfo)+2); memset(&tmp, 0, 130); mainp = strdup(input); origptr = mainp; tok = strsep(&mainp, "/\0"); while( tok != NULL ) { if( (tok != NULL) && (strlen(tok) > 0 ) ) { sub = strdup(tok); tok2 = strsep(&sub, "=\0"); if( comp_attrib("O\0", tok2) ) { ret->org = strdup(strsep(&sub, "=\0")); } else if( comp_attrib("CN\0", tok2) ) { ret->common_name = strdup(strsep(&sub, "=\0")); } else if( comp_attrib("emailAddress\0", tok2) ) { ret->email = strdup(strsep(&sub, "=\0")); } if( tok2 != NULL ) { free(tok2); tok2 = NULL; } } tok = strsep(&mainp, "/\0"); } free(origptr); mainp = NULL; origptr = NULL; /* Copy over the digest info */ ret->digest = digest; /* Make sure we at least have empty NULL terminated strings */ if( ret->org == NULL ) { ret->org = strdup("\0"); } if( ret->common_name == NULL ) { ret->common_name = strdup("\0"); } if( ret->email == NULL ) { ret->email = strdup("\0"); } return ret; } /** * Parses an X.509 certificate data structure into a certinfo struct * This implementation depends on OpenSSL */ certinfo *parse_x509_cert (X509 *cert) { int i, n, objnid; const int hashlen = SHA_DIGEST_LENGTH*3+2; ASN1_OBJECT *asn1 = NULL; ASN1_STRING *asn1val = NULL; X509_NAME *x509_name = NULL; X509_NAME_ENTRY *ent = NULL; const char *objbuf = NULL; unsigned char *buf = NULL; char *ptr = NULL; certinfo *ret = NULL; ret = (certinfo *) malloc_nullsafe(NULL, sizeof(certinfo)+2); x509_name = X509_get_subject_name(cert); n = X509_NAME_entry_count(x509_name); for (i = 0; i < n; i++) { ent = X509_NAME_get_entry(x509_name, i); if( !ent ) { continue; } asn1 = X509_NAME_ENTRY_get_object(ent); if( !asn1 ) { continue; } asn1val = X509_NAME_ENTRY_get_data(ent); if( !asn1val ) { continue; } objnid = OBJ_obj2nid(asn1); if( objnid == NID_undef ) { continue; } objbuf = OBJ_nid2sn(objnid); if( !objbuf ) { continue; } if( ASN1_STRING_to_UTF8(&buf, asn1val) <= 0 ) { continue; } // At this point we now have the 'field name' from the certificate // available in objbuf and the 'field value' in buf. if( (ret->org == NULL) && (strcmp(objbuf, "O") == 0) ) { ret->org = strdup_nullsafe((char *) buf); } else if( (ret->common_name == NULL) && (strcmp(objbuf, "CN") == 0) ) { ret->common_name = strdup_nullsafe((char *) buf); } else if( (ret->email == NULL) && (strcmp(objbuf, "emailAddress") == 0) ) { ret->email = strdup_nullsafe((char *) buf); } OPENSSL_free(buf); buf = NULL; if( ret->org && ret->common_name && ret->email ) { // If we got all we need, skip the rest break; } } // Extract the SHA1 certificate fingerprint/digest ptr = ret->digest = malloc_nullsafe(NULL, hashlen); buf = cert->sha1_hash; for( i = 0; i < SHA_DIGEST_LENGTH; ++i ) { snprintf(ptr, 4, "%02x:", buf[i]); ptr += 3; } *(ptr-1) = 0; return ret; } /** * Frees up the memory used by a certinfo structure * * @param p Pointer to a certinfo structure to be freed */ void free_certinfo(certinfo *p) { if( p == NULL ) return; if( p->digest != NULL ) free(p->digest); if( p->org != NULL ) free(p->org); if( p->common_name != NULL ) free(p->common_name); if( p->email != NULL ) free(p->email); free(p); }