/* Implementation specific date parser. Does not actually verify the date */ int32 getValidity(psPool_t *pool, unsigned char **pp, int32 len, char **notBefore, char **notAfter) { unsigned char *p = *pp, *end; int32 seqLen, timeLen; end = p + len; if (len < 1 || *(p++) != (ASN_SEQUENCE | ASN_CONSTRUCTED) || asnParseLength(&p, len - 1, &seqLen) < 0 || (end - p) < seqLen) { return -1; } /* Have notBefore and notAfter times in UTCTime or GeneralizedTime formats */ if ((end - p) < 1 || ((*p != ASN_UTCTIME) && (*p != ASN_GENERALIZEDTIME))) { return -1; } p++; /* Allocate them as null terminated strings */ if (asnParseLength(&p, seqLen, &timeLen) < 0 || (end - p) < timeLen) { return -1; } *notBefore = psMalloc(pool, timeLen + 1); if (*notBefore == NULL) { return -8; /* SSL_MEM_ERROR */ } memcpy(*notBefore, p, timeLen); (*notBefore)[timeLen] = '\0'; p = p + timeLen; if ((end - p) < 1 || ((*p != ASN_UTCTIME) && (*p != ASN_GENERALIZEDTIME))) { return -1; } p++; if (asnParseLength(&p, seqLen - timeLen, &timeLen) < 0 || (end - p) < timeLen) { return -1; } *notAfter = psMalloc(pool, timeLen + 1); if (*notAfter == NULL) { return -8; /* SSL_MEM_ERROR */ } memcpy(*notAfter, p, timeLen); (*notAfter)[timeLen] = '\0'; p = p + timeLen; *pp = p; return 0; }
/* Get the BIT STRING key and plug into RSA structure. Not included in asn1.c because it deals directly with the sslRsaKey_t struct. */ int32 getPubKey(psPool_t *pool, unsigned char **pp, int32 len, sslRsaKey_t *pubKey) { unsigned char *p = *pp; int32 pubKeyLen, ignore_bits, seqLen; if (len < 1 || (*(p++) != ASN_BIT_STRING) || asnParseLength(&p, len - 1, &pubKeyLen) < 0 || (len - 1) < pubKeyLen) { return -1; } ignore_bits = *p++; /* We assume this is always zero */ sslAssert(ignore_bits == 0); if (getSequence(&p, pubKeyLen, &seqLen) < 0 || getBig(pool, &p, seqLen, &pubKey->N) < 0 || getBig(pool, &p, seqLen, &pubKey->e) < 0) { return -1; } pubKey->size = mp_unsigned_bin_size(&pubKey->N); *pp = p; return 0; }
/* Could be optional. If the tag doesn't contain the value from the left of the IMPLICIT keyword we don't have a match and we don't incr the pointer. */ int32 getImplicitBitString(psPool_t *pool, unsigned char **pp, int32 len, int32 impVal, unsigned char **bitString, int32 *bitLen) { unsigned char *p = *pp; int32 ignore_bits; if (len < 1) { return -1; } /* We don't treat this case as an error, because of the optional nature. */ if (*p != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | impVal)) { return 0; } p++; if (asnParseLength(&p, len, bitLen) < 0) { return -1; } ignore_bits = *p++; sslAssert(ignore_bits == 0); *bitString = psMalloc(pool, *bitLen); if (*bitString == NULL) { return -8; /* SSL_MEM_ERROR */ } memcpy(*bitString, p, *bitLen); *pp = p + *bitLen; return 0; }
/* Currently just returning the raw BIT STRING and size in bytes */ int32 getSignature(psPool_t *pool, unsigned char **pp, int32 len, unsigned char **sig, int32 *sigLen) { unsigned char *p = *pp, *end; int32 ignore_bits, llen; end = p + len; if (len < 1 || (*(p++) != ASN_BIT_STRING) || asnParseLength(&p, len - 1, &llen) < 0 || (end - p) < llen) { return -1; } ignore_bits = *p++; /* We assume this is always 0. */ sslAssert(ignore_bits == 0); /* Length included the ignore_bits byte */ *sigLen = llen - 1; *sig = psMalloc(pool, *sigLen); if (*sig == NULL) { return -8; /* SSL_MEM_ERROR */ } memcpy(*sig, p, *sigLen); *pp = p + *sigLen; return 0; }
/* Explicit value encoding has an additional tag layer */ int32 getExplicitVersion(unsigned char **pp, int32 len, int32 expVal, int32 *val) { unsigned char *p = *pp; int32 exLen; if (len < 1) { return -1; } /* This is an optional value, so don't error if not present. The default value is version 1 */ if (*p != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | expVal)) { *val = 0; return 0; } p++; if (asnParseLength(&p, len - 1, &exLen) < 0 || (len - 1) < exLen) { return -1; } if (getInteger(&p, exLen, val) < 0) { return -1; } *pp = p; return 0; }
/* Although a certificate serial number is encoded as an integer type, that doesn't prevent it from being abused as containing a variable length binary value. Get it here. */ int32 getSerialNum(psPool_t *pool, unsigned char **pp, int32 len, unsigned char **sn, int32 *snLen) { unsigned char *p = *pp; int32 vlen; if ((*p != (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 2)) && (*p != ASN_INTEGER)) { matrixStrDebugMsg("ASN getSerialNumber failed\n", NULL); return -1; } p++; if (len < 1 || asnParseLength(&p, len - 1, &vlen) < 0) { matrixStrDebugMsg("ASN getSerialNumber failed\n", NULL); return -1; } *snLen = vlen; *sn = psMalloc(pool, vlen); if (*sn == NULL) { return -8; /* SSL_MEM_ERROR */ } memcpy(*sn, p, vlen); p += vlen; *pp = p; return 0; }
/* Implementation specific OID parser for suppported RSA algorithms */ int32 getAlgorithmIdentifier(unsigned char **pp, int32 len, int32 *oi, int32 isPubKey) { unsigned char *p = *pp, *end; int32 arcLen, llen; end = p + len; if (len < 1 || getSequence(&p, len, &llen) < 0) { return -1; } if (end - p < 1) { return -1; } if (*(p++) != ASN_OID || asnParseLength(&p, (int32)(end - p), &arcLen) < 0 || llen < arcLen) { return -1; } /* List of expected (currently supported) OIDs algorithm summed length hex sha1 88 05 2b0e03021a md2 646 08 2a864886f70d0202 md5 649 08 2a864886f70d0205 rsaEncryption 645 09 2a864886f70d010101 md2WithRSAEncryption: 646 09 2a864886f70d010102 md5WithRSAEncryption 648 09 2a864886f70d010104 sha-1WithRSAEncryption 649 09 2a864886f70d010105 Yes, the summing isn't ideal (as can be seen with the duplicate 649), but the specific implementation makes this ok. */ if (end - p < 2) { return -1; } if (isPubKey && (*p != 0x2a) && (*(p + 1) != 0x86)) { /* Expecting DSA here if not RSA, but OID doesn't always match */ matrixStrDebugMsg("Unsupported algorithm identifier\n", NULL); return -1; } *oi = 0; while (arcLen-- > 0) { *oi += (int32)*p++; } /* Each of these cases might have a trailing NULL parameter. Skip it */ if (*p != ASN_NULL) { *pp = p; return 0; } if (end - p < 2) { return -1; } *pp = p + 2; return 0; }
/* Extract a set length from the DER stream */ int32 getSet(unsigned char **pp, int32 len, int32 *setlen) { unsigned char *p = *pp; if (len < 1 || *(p++) != (ASN_SET | ASN_CONSTRUCTED) || asnParseLength(&p, len - 1, setlen) < 0 || len < *setlen) { return -1; } *pp = p; return 0; }
/* Do the signature validation for a subject certificate against a known CA certificate */ int32 psAsnConfirmSignature(unsigned char *sigHash, unsigned char *sigOut, int32 sigLen) { unsigned char *end, *p = sigOut; unsigned char hash[SSL_SHA1_HASH_SIZE]; int32 len, oi; end = p + sigLen; /* DigestInfo ::= SEQUENCE { digestAlgorithm DigestAlgorithmIdentifier, digest Digest } DigestAlgorithmIdentifier ::= AlgorithmIdentifier Digest ::= OCTET STRING */ if (getSequence(&p, (int32)(end - p), &len) < 0) { return -1; } /* Could be MD5 or SHA1 */ if (getAlgorithmIdentifier(&p, (int32)(end - p), &oi, 0) < 0) { return -1; } if ((*p++ != ASN_OCTET_STRING) || asnParseLength(&p, (int32)(end - p), &len) < 0 || (end - p) < len) { return -1; } memcpy(hash, p, len); if (oi == OID_MD5 || oi == OID_MD2) { if (len != SSL_MD5_HASH_SIZE) { return -1; } } else if (oi == OID_SHA1) { if (len != SSL_SHA1_HASH_SIZE) { return -1; } } else { return -1; } /* hash should match sigHash */ if (memcmp(hash, sigHash, len) != 0) { return -1; } return 0; }
/* Get an integer */ int32 getInteger(unsigned char **pp, int32 len, int32 *val) { unsigned char *p = *pp, *end; uint32 ui; int32 vlen; end = p + len; if (len < 1 || *(p++) != ASN_INTEGER || asnParseLength(&p, len - 1, &vlen) < 0) { matrixStrDebugMsg("ASN getInteger failed\n", NULL); return -1; } /* This check prevents us from having a big positive integer where the high bit is set because it will be encoded as 5 bytes (with leading blank byte). If that is required, a getUnsigned routine should be used */ if (vlen > sizeof(int32) || end - p < vlen) { matrixStrDebugMsg("ASN getInteger failed\n", NULL); return -1; } ui = 0; /* If high bit is set, it's a negative integer, so perform the two's compliment Otherwise do a standard big endian read (most likely case for RSA) */ if (*p & 0x80) { while (vlen-- > 0) { ui = (ui << 8) | (*p ^ 0xFF); p++; } vlen = (int32)ui; vlen++; vlen = -vlen; *val = vlen; } else { while (vlen-- > 0) { ui = (ui << 8) | *p; p++; } *val = (int32)ui; } *pp = p; return 0; }
/* Callback to extract a big int32 (stream of bytes) from the DER stream */ int32 getBig(psPool_t *pool, unsigned char **pp, int32 len, mp_int *big) { unsigned char *p = *pp; int32 vlen; if (len < 1 || *(p++) != ASN_INTEGER || asnParseLength(&p, len - 1, &vlen) < 0) { matrixStrDebugMsg("ASN getBig failed\n", NULL); return -1; } mp_init(pool, big); if (mp_read_unsigned_bin(big, p, vlen) != 0) { mp_clear(big); matrixStrDebugMsg("ASN getBig failed\n", NULL); return -1; } p += vlen; *pp = p; return 0; }
/* X509v3 extensions */ static int32 getExplicitExtensions(psPool_t *pool, unsigned char **pp, int32 inlen, int32 expVal, v3extensions_t *extensions) { unsigned char *p = *pp, *end; unsigned char *extEnd, *extStart; int32 len, noid, tmpLen, critical, fullExtLen; unsigned char oid[SSL_MD5_HASH_SIZE]; sslMd5Context_t md5ctx; end = p + inlen; if (inlen < 1) { return -1; } /* Not treating this as an error because it is optional. */ if (*p != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | expVal)) { return 0; } p++; if (asnParseLength(&p, (int32)(end - p), &len) < 0 || (end - p) < len) { return -1; } /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension Extension ::= SEQUENCE { extnID OBJECT IDENTIFIER, extnValue OCTET STRING } */ if (getSequence(&p, (int32)(end - p), &len) < 0) { return -1; } extEnd = p + len; while ((p != extEnd) && *p == (ASN_SEQUENCE | ASN_CONSTRUCTED)) { if (getSequence(&p, (int32)(extEnd - p), &fullExtLen) < 0) { return -1; } extStart = p; /* Conforming CAs MUST support key identifiers, basic constraints, key usage, and certificate policies extensions id-ce-authorityKeyIdentifier OBJECT IDENTIFIER ::= { id-ce 35 } id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } 133 id-ce-keyUsage OBJECT IDENTIFIER ::= { id-ce 15 } id-ce-certificatePolicies OBJECT IDENTIFIER ::= { id-ce 32 } id-ce-subjectAltName OBJECT IDENTIFIER ::= { id-ce 17 } 131 */ if (extEnd - p < 1 || *p++ != ASN_OID) { return -1; } if (asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || (extEnd - p) < len) { return -1; } /* Send the OID through a digest to get the unique id */ matrixMd5Init(&md5ctx); while (len-- > 0) { matrixMd5Update(&md5ctx, p, sizeof(char)); p++; } matrixMd5Final(&md5ctx, oid); noid = lookupExt(oid); /* Possible boolean value here for 'critical' id. It's a failure if a critical extension is found that is not supported */ critical = 0; if (*p == ASN_BOOLEAN) { p++; if (*p++ != 1) { matrixStrDebugMsg("Error parsing cert extension\n", NULL); return -1; } if (*p++ > 0) { critical = 1; } } if (extEnd - p < 1 || (*p++ != ASN_OCTET_STRING) || asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || extEnd - p < len) { matrixStrDebugMsg("Expecting OCTET STRING in ext parse\n", NULL); return -1; } switch (noid) { /* BasicConstraints ::= SEQUENCE { cA BOOLEAN DEFAULT FALSE, pathLenConstraint INTEGER (0..MAX) OPTIONAL } */ case EXT_BASIC_CONSTRAINTS: if (getSequence(&p, (int32)(extEnd - p), &len) < 0) { return -1; } /* "This goes against PKIX guidelines but some CAs do it and some software requires this to avoid interpreting an end user certificate as a CA." - OpenSSL certificate configuration doc basicConstraints=CA:FALSE */ if (len == 0) { break; } if (extEnd - p < 3) { return -1; } if (*p++ != ASN_BOOLEAN) { return -1; } if (*p++ != 1) { return -1; } extensions->bc.ca = *p++; /* Now need to check if there is a path constraint. Only makes sense if cA is true. If it's missing, there is no limit to the cert path */ if (*p == ASN_INTEGER) { if (getInteger(&p, (int32)(extEnd - p), &(extensions->bc.pathLenConstraint)) < 0) { return -1; } } else { extensions->bc.pathLenConstraint = -1; } break; case EXT_ALT_SUBJECT_NAME: if (getSequence(&p, (int32)(extEnd - p), &len) < 0) { return -1; } /* Looking only for DNS, URI, and email here to support FQDN for Web clients FUTURE: Support all subject alt name members GeneralName ::= CHOICE { otherName [0] OtherName, rfc822Name [1] IA5String, dNSName [2] IA5String, x400Address [3] ORAddress, directoryName [4] Name, ediPartyName [5] EDIPartyName, uniformResourceIdentifier [6] IA5String, iPAddress [7] OCTET STRING, registeredID [8] OBJECT IDENTIFIER } */ while (len > 0) { if (*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 2)) { p++; tmpLen = *p++; if (extEnd - p < tmpLen) { return -1; } extensions->san.dns = psMalloc(pool, tmpLen + 1); if (extensions->san.dns == NULL) { return -8; /* SSL_MEM_ERROR */ } memset(extensions->san.dns, 0x0, tmpLen + 1); memcpy(extensions->san.dns, p, tmpLen); } else if (*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 6)) { p++; tmpLen = *p++; if (extEnd - p < tmpLen) { return -1; } extensions->san.uri = psMalloc(pool, tmpLen + 1); if (extensions->san.uri == NULL) { return -8; /* SSL_MEM_ERROR */ } memset(extensions->san.uri, 0x0, tmpLen + 1); memcpy(extensions->san.uri, p, tmpLen); } else if (*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 1)) { p++; tmpLen = *p++; if (extEnd - p < tmpLen) { return -1; } extensions->san.email = psMalloc(pool, tmpLen + 1); if (extensions->san.email == NULL) { return -8; /* SSL_MEM_ERROR */ } memset(extensions->san.email, 0x0, tmpLen + 1); memcpy(extensions->san.email, p, tmpLen); } else { matrixStrDebugMsg("Unsupported subjectAltName type.n", NULL); p++; tmpLen = *p++; if (extEnd - p < tmpLen) { return -1; } } p = p + tmpLen; len -= tmpLen + 2; /* the magic 2 is the type and length */ } break; #ifdef USE_FULL_CERT_PARSE case EXT_AUTH_KEY_ID: /* AuthorityKeyIdentifier ::= SEQUENCE { keyIdentifier [0] KeyIdentifier OPTIONAL, authorityCertIssuer [1] GeneralNames OPTIONAL, authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL } KeyIdentifier ::= OCTET STRING */ if (getSequence(&p, (int32)(extEnd - p), &len) < 0) { return -1; } /* Have seen a cert that has a zero length ext here. Let it pass. */ if (len == 0) { break; } /* All memebers are optional */ if (*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 0)) { p++; if (asnParseLength(&p, (int32)(extEnd - p), &extensions->ak.keyLen) < 0 || extEnd - p < extensions->ak.keyLen) { return -1; } extensions->ak.keyId = psMalloc(pool, extensions->ak.keyLen); if (extensions->ak.keyId == NULL) { return -8; /* SSL_MEM_ERROR */ } memcpy(extensions->ak.keyId, p, extensions->ak.keyLen); p = p + extensions->ak.keyLen; } if (*p == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 1)) { p++; if (asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || len < 1 || extEnd - p < len) { return -1; } if ((*p ^ ASN_CONTEXT_SPECIFIC ^ ASN_CONSTRUCTED) != 4) { /* FUTURE: support other name types We are just dealing with DN formats here */ matrixIntDebugMsg("Error auth key-id name type: %d\n", *p ^ ASN_CONTEXT_SPECIFIC ^ ASN_CONSTRUCTED); return -1; } p++; if (asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || extEnd - p < len) { return -1; } if (getDNAttributes(pool, &p, (int32)(extEnd - p), &(extensions->ak.attribs)) < 0) { return -1; } } if ((*p == (ASN_CONTEXT_SPECIFIC | ASN_PRIMITIVE | 2)) || (*p == ASN_INTEGER)){ /* Treat as a serial number (not a native INTEGER) */ if (getSerialNum(pool, &p, (int32)(extEnd - p), &(extensions->ak.serialNum), &len) < 0) { return -1; } extensions->ak.serialNumLen = len; } break; case EXT_KEY_USAGE: /* KeyUsage ::= BIT STRING { digitalSignature (0), nonRepudiation (1), keyEncipherment (2), dataEncipherment (3), keyAgreement (4), keyCertSign (5), cRLSign (6), encipherOnly (7), decipherOnly (8) } */ if (*p++ != ASN_BIT_STRING) { return -1; } if (asnParseLength(&p, (int32)(extEnd - p), &len) < 0 || extEnd - p < len) { return -1; } if (len != 2) { return -1; } /* Assure all unused bits are 0 and store away */ extensions->keyUsage = (*(p + 1)) & ~((1 << *p) -1); p = p + len; break; case EXT_SUBJ_KEY_ID: /* The value of the subject key identifier MUST be the value placed in the key identifier field of the Auth Key Identifier extension of certificates issued by the subject of this certificate. */ if (*p++ != ASN_OCTET_STRING || asnParseLength(&p, (int32)(extEnd - p), &(extensions->sk.len)) < 0 || extEnd - p < extensions->sk.len) { return -1; } extensions->sk.id = psMalloc(pool, extensions->sk.len); if (extensions->sk.id == NULL) { return -8; /* SSL_MEM_ERROR */ } memcpy(extensions->sk.id, p, extensions->sk.len); p = p + extensions->sk.len; break; #endif /* USE_FULL_CERT_PARSE */ /* Unsupported or skipping because USE_FULL_CERT_PARSE is undefined */ default: if (critical) { /* SPEC DIFFERENCE: Ignoring an unrecognized critical extension. The specification dictates an error should occur, but real-world experience has shown this is not a realistic or desirable action. Also, no other SSL implementations have been found to error in this case. It is NOT a security risk in an RSA authenticaion sense. */ matrixStrDebugMsg("Unknown critical ext encountered\n", NULL); } p++; /* Skip over based on the length reported from the ASN_SEQUENCE surrounding the entire extension. It is not a guarantee that the value of the extension itself will contain it's own length. */ p = p + (fullExtLen - (p - extStart)); break; } } *pp = p; return 0; }
/* Implementations of this specification MUST be prepared to receive the following standard attribute types in issuer names: country, organization, organizational-unit, distinguished name qualifier, state or province name, and common name */ int32 getDNAttributes(psPool_t *pool, unsigned char **pp, int32 len, DNattributes_t *attribs) { sslSha1Context_t hash; unsigned char *p = *pp; unsigned char *dnEnd, *dnStart; int32 llen, setlen, arcLen, id, stringType; char *stringOut; dnStart = p; if (getSequence(&p, len, &llen) < 0) { return -1; } dnEnd = p + llen; matrixSha1Init(&hash); while (p < dnEnd) { if (getSet(&p, (int32)(dnEnd - p), &setlen) < 0) { return -1; } if (getSequence(&p, (int32)(dnEnd - p), &llen) < 0) { return -1; } if (dnEnd <= p || (*(p++) != ASN_OID) || asnParseLength(&p, (int32)(dnEnd - p), &arcLen) < 0 || (dnEnd - p) < arcLen) { return -1; } /* id-at OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 4} id-at-commonName OBJECT IDENTIFIER ::= {id-at 3} id-at-countryName OBJECT IDENTIFIER ::= {id-at 6} id-at-localityName OBJECT IDENTIFIER ::= {id-at 7} id-at-stateOrProvinceName OBJECT IDENTIFIER ::= {id-at 8} id-at-organizationName OBJECT IDENTIFIER ::= {id-at 10} id-at-organizationalUnitName OBJECT IDENTIFIER ::= {id-at 11} */ *pp = p; /* FUTURE: Currently skipping OIDs not of type {joint-iso-ccitt(2) ds(5) 4} However, we could be dealing with an OID we MUST support per RFC. domainComponent is one such example. */ if (dnEnd - p < 2) { return -1; } if ((*p++ != 85) || (*p++ != 4) ) { p = *pp; /* Move past the OID and string type, get data size, and skip it. NOTE: Have had problems parsing older certs in this area. */ if (dnEnd - p < arcLen + 1) { return -1; } p += arcLen + 1; if (asnParseLength(&p, (int32)(dnEnd - p), &llen) < 0 || dnEnd - p < llen) { return -1; } p = p + llen; continue; } /* Next are the id of the attribute type and the ASN string type */ if (arcLen != 3 || dnEnd - p < 2) { return -1; } id = (int32)*p++; /* Done with OID parsing */ stringType = (int32)*p++; asnParseLength(&p, (int32)(dnEnd - p), &llen); if (dnEnd - p < llen) { return -1; } switch (stringType) { case ASN_PRINTABLESTRING: case ASN_UTF8STRING: case ASN_IA5STRING: case ASN_T61STRING: case ASN_BMPSTRING: stringOut = psMalloc(pool, llen + 1); if (stringOut == NULL) { return -8; /* SSL_MEM_ERROR */ } memcpy(stringOut, p, llen); stringOut[llen] = '\0'; p = p + llen; break; default: matrixStrDebugMsg("Parsing untested DN attrib type\n", NULL); return -1; } switch (id) { case ATTRIB_COUNTRY_NAME: if (attribs->country) { psFree(attribs->country); } attribs->country = stringOut; break; case ATTRIB_STATE_PROVINCE: if (attribs->state) { psFree(attribs->state); } attribs->state = stringOut; break; case ATTRIB_LOCALITY: if (attribs->locality) { psFree(attribs->locality); } attribs->locality = stringOut; break; case ATTRIB_ORGANIZATION: if (attribs->organization) { psFree(attribs->organization); } attribs->organization = stringOut; break; case ATTRIB_ORG_UNIT: if (attribs->orgUnit) { psFree(attribs->orgUnit); } attribs->orgUnit = stringOut; break; case ATTRIB_COMMON_NAME: if (attribs->commonName) { psFree(attribs->commonName); } attribs->commonName = stringOut; break; /* Not a MUST support */ default: psFree(stringOut); stringOut = NULL; break; } /* Hash up the DN. Nice for validation later */ if (stringOut != NULL) { matrixSha1Update(&hash, (unsigned char*)stringOut, llen); } } matrixSha1Final(&hash, (unsigned char*)attribs->hash); *pp = p; return 0; }