Source code

Revision control

Copy as Markdown

Other Tools

/* alg1485.c - implementation of RFCs 1485, 1779 and 2253.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "prprf.h"
#include "cert.h"
#include "certi.h"
#include "xconst.h"
#include "genname.h"
#include "secitem.h"
#include "secerr.h"
typedef struct NameToKindStr {
const char* name;
unsigned int maxLen; /* max bytes in UTF8 encoded string value */
SECOidTag kind;
int valueType;
} NameToKind;
/* local type for directory string--could be printable_string or utf8 */
#define SEC_ASN1_DS SEC_ASN1_HIGH_TAG_NUMBER
/* clang-format off */
/* Add new entries to this table, and maybe to function ParseRFC1485AVA */
static const NameToKind name2kinds[] = {
/* IANA registered type names
*/
/* RFC 3280, 4630 MUST SUPPORT */
{ "CN", 640, SEC_OID_AVA_COMMON_NAME, SEC_ASN1_DS},
{ "ST", 128, SEC_OID_AVA_STATE_OR_PROVINCE,
SEC_ASN1_DS},
{ "O", 128, SEC_OID_AVA_ORGANIZATION_NAME,
SEC_ASN1_DS},
{ "OU", 128, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME,
SEC_ASN1_DS},
{ "dnQualifier", 32767, SEC_OID_AVA_DN_QUALIFIER, SEC_ASN1_PRINTABLE_STRING},
{ "C", 2, SEC_OID_AVA_COUNTRY_NAME, SEC_ASN1_PRINTABLE_STRING},
{ "serialNumber", 64, SEC_OID_AVA_SERIAL_NUMBER,SEC_ASN1_PRINTABLE_STRING},
/* RFC 3280, 4630 SHOULD SUPPORT */
{ "L", 128, SEC_OID_AVA_LOCALITY, SEC_ASN1_DS},
{ "title", 64, SEC_OID_AVA_TITLE, SEC_ASN1_DS},
{ "SN", 64, SEC_OID_AVA_SURNAME, SEC_ASN1_DS},
{ "givenName", 64, SEC_OID_AVA_GIVEN_NAME, SEC_ASN1_DS},
{ "initials", 64, SEC_OID_AVA_INITIALS, SEC_ASN1_DS},
{ "generationQualifier",
64, SEC_OID_AVA_GENERATION_QUALIFIER,
SEC_ASN1_DS},
/* RFC 3280, 4630 MAY SUPPORT */
{ "DC", 128, SEC_OID_AVA_DC, SEC_ASN1_IA5_STRING},
{ "MAIL", 256, SEC_OID_RFC1274_MAIL, SEC_ASN1_IA5_STRING},
{ "UID", 256, SEC_OID_RFC1274_UID, SEC_ASN1_DS},
/* ------------------ "strict" boundary ---------------------------------
* In strict mode, cert_NameToAscii does not encode any of the attributes
* below this line. The first SECOidTag below this line must be used to
* conditionally define the "endKind" in function AppendAVA() below.
* Most new attribute names should be added below this line.
* Maybe this line should be up higher? Say, after the 3280 MUSTs and
* before the 3280 SHOULDs?
*/
/* values from draft-ietf-ldapbis-user-schema-05 (not in RFC 3280) */
{ "postalAddress", 128, SEC_OID_AVA_POSTAL_ADDRESS, SEC_ASN1_DS},
{ "postalCode", 40, SEC_OID_AVA_POSTAL_CODE, SEC_ASN1_DS},
{ "postOfficeBox", 40, SEC_OID_AVA_POST_OFFICE_BOX,SEC_ASN1_DS},
{ "houseIdentifier",64, SEC_OID_AVA_HOUSE_IDENTIFIER,SEC_ASN1_DS},
/* end of IANA registered type names */
/* legacy keywords */
{ "E", 128, SEC_OID_PKCS9_EMAIL_ADDRESS,SEC_ASN1_IA5_STRING},
{ "STREET", 128, SEC_OID_AVA_STREET_ADDRESS, SEC_ASN1_DS},
{ "pseudonym", 64, SEC_OID_AVA_PSEUDONYM, SEC_ASN1_DS},
/* values defined by the CAB Forum for EV */
{ "incorporationLocality", 128, SEC_OID_EV_INCORPORATION_LOCALITY,
SEC_ASN1_DS},
{ "incorporationState", 128, SEC_OID_EV_INCORPORATION_STATE,
SEC_ASN1_DS},
{ "incorporationCountry", 2, SEC_OID_EV_INCORPORATION_COUNTRY,
SEC_ASN1_PRINTABLE_STRING},
{ "businessCategory", 64, SEC_OID_BUSINESS_CATEGORY, SEC_ASN1_DS},
/* values defined in X.520 */
{ "name", 64, SEC_OID_AVA_NAME, SEC_ASN1_DS},
{ 0, 256, SEC_OID_UNKNOWN, 0},
};
/* Table facilitates conversion of ASCII hex to binary. */
static const PRInt16 x2b[256] = {
/* #0x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #1x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #2x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #3x */ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
/* #4x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #5x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #6x */ -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #7x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #8x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #9x */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #ax */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #bx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #cx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #dx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #ex */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
/* #fx */ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
#define IS_HEX(c) (x2b[(PRUint8)(c)] >= 0)
#define C_DOUBLE_QUOTE '\042'
#define C_BACKSLASH '\134'
#define C_EQUAL '='
#define OPTIONAL_SPACE(c) \
(((c) == ' ') || ((c) == '\r') || ((c) == '\n'))
#define SPECIAL_CHAR(c) \
(((c) == ',') || ((c) == '=') || ((c) == C_DOUBLE_QUOTE) || \
((c) == '\r') || ((c) == '\n') || ((c) == '+') || \
((c) == '<') || ((c) == '>') || ((c) == '#') || \
((c) == ';') || ((c) == C_BACKSLASH))
#define IS_PRINTABLE(c) \
((((c) >= 'a') && ((c) <= 'z')) || \
(((c) >= 'A') && ((c) <= 'Z')) || \
(((c) >= '0') && ((c) <= '9')) || \
((c) == ' ') || \
((c) == '\'') || \
((c) == '\050') || /* ( */ \
((c) == '\051') || /* ) */ \
(((c) >= '+') && ((c) <= '/')) || /* + , - . / */ \
((c) == ':') || \
((c) == '=') || \
((c) == '?'))
/* clang-format on */
/* RFC 2253 says we must escape ",+\"\\<>;=" EXCEPT inside a quoted string.
* Inside a quoted string, we only need to escape " and \
* We choose to quote strings containing any of those special characters,
* so we only need to escape " and \
*/
#define NEEDS_ESCAPE(c) (c == C_DOUBLE_QUOTE || c == C_BACKSLASH)
#define NEEDS_HEX_ESCAPE(c) ((PRUint8)c < 0x20 || c == 0x7f)
int
cert_AVAOidTagToMaxLen(SECOidTag tag)
{
const NameToKind* n2k = name2kinds;
while (n2k->kind != tag && n2k->kind != SEC_OID_UNKNOWN) {
++n2k;
}
return (n2k->kind != SEC_OID_UNKNOWN) ? n2k->maxLen : -1;
}
static PRBool
IsPrintable(unsigned char* data, unsigned len)
{
unsigned char ch, *end;
end = data + len;
while (data < end) {
ch = *data++;
if (!IS_PRINTABLE(ch)) {
return PR_FALSE;
}
}
return PR_TRUE;
}
static void
skipSpace(const char** pbp, const char* endptr)
{
const char* bp = *pbp;
while (bp < endptr && OPTIONAL_SPACE(*bp)) {
bp++;
}
*pbp = bp;
}
static SECStatus
scanTag(const char** pbp, const char* endptr, char* tagBuf, int tagBufSize)
{
const char* bp;
char* tagBufp;
int taglen;
PORT_Assert(tagBufSize > 0);
/* skip optional leading space */
skipSpace(pbp, endptr);
if (*pbp == endptr) {
/* nothing left */
return SECFailure;
}
/* fill tagBuf */
taglen = 0;
bp = *pbp;
tagBufp = tagBuf;
while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) {
if (++taglen >= tagBufSize) {
*pbp = bp;
return SECFailure;
}
*tagBufp++ = *bp++;
}
/* null-terminate tagBuf -- guaranteed at least one space left */
*tagBufp++ = 0;
*pbp = bp;
/* skip trailing spaces till we hit something - should be an equal sign */
skipSpace(pbp, endptr);
if (*pbp == endptr) {
/* nothing left */
return SECFailure;
}
if (**pbp != C_EQUAL) {
/* should be an equal sign */
return SECFailure;
}
/* skip over the equal sign */
(*pbp)++;
return SECSuccess;
}
/* Returns the number of bytes in the value. 0 means failure. */
static int
scanVal(const char** pbp, const char* endptr, char* valBuf, int valBufSize)
{
const char* bp;
char* valBufp;
int vallen = 0;
PRBool isQuoted;
PORT_Assert(valBufSize > 0);
/* skip optional leading space */
skipSpace(pbp, endptr);
if (*pbp == endptr) {
/* nothing left */
return 0;
}
bp = *pbp;
/* quoted? */
if (*bp == C_DOUBLE_QUOTE) {
isQuoted = PR_TRUE;
/* skip over it */
bp++;
} else {
isQuoted = PR_FALSE;
}
valBufp = valBuf;
while (bp < endptr) {
char c = *bp;
if (c == C_BACKSLASH) {
/* escape character */
bp++;
if (bp >= endptr) {
/* escape charater must appear with paired char */
*pbp = bp;
return 0;
}
c = *bp;
if (IS_HEX(c) && (endptr - bp) >= 2 && IS_HEX(bp[1])) {
bp++;
c = (char)((x2b[(PRUint8)c] << 4) | x2b[(PRUint8)*bp]);
}
} else if (c == '#' && bp == *pbp) {
/* ignore leading #, quotation not required for it. */
} else if (!isQuoted && SPECIAL_CHAR(c)) {
/* unescaped special and not within quoted value */
break;
} else if (c == C_DOUBLE_QUOTE) {
/* reached unescaped double quote */
break;
}
/* append character */
vallen++;
if (vallen >= valBufSize) {
*pbp = bp;
return 0;
}
*valBufp++ = c;
bp++;
}
/* strip trailing spaces from unquoted values */
if (!isQuoted) {
while (valBufp > valBuf) {
char c = valBufp[-1];
if (!OPTIONAL_SPACE(c))
break;
--valBufp;
}
vallen = valBufp - valBuf;
}
if (isQuoted) {
/* insist that we stopped on a double quote */
if (*bp != C_DOUBLE_QUOTE) {
*pbp = bp;
return 0;
}
/* skip over the quote and skip optional space */
bp++;
skipSpace(&bp, endptr);
}
*pbp = bp;
/* null-terminate valBuf -- guaranteed at least one space left */
*valBufp = 0;
return vallen;
}
/* Caller must set error code upon failure */
static SECStatus
hexToBin(PLArenaPool* pool, SECItem* destItem, const char* src, int len)
{
PRUint8* dest;
destItem->data = NULL;
if (len <= 0 || (len & 1)) {
goto loser;
}
len >>= 1;
if (!SECITEM_AllocItem(pool, destItem, len)) {
goto loser;
}
dest = destItem->data;
for (; len > 0; len--, src += 2) {
PRUint16 bin = ((PRUint16)x2b[(PRUint8)src[0]] << 4);
bin |= (PRUint16)x2b[(PRUint8)src[1]];
if (bin >> 15) { /* is negative */
goto loser;
}
*dest++ = (PRUint8)bin;
}
return SECSuccess;
loser:
if (!pool)
SECITEM_FreeItem(destItem, PR_FALSE);
return SECFailure;
}
/* Parses one AVA, starting at *pbp. Stops at endptr.
* Advances *pbp past parsed AVA and trailing separator (if present).
* On any error, returns NULL and *pbp is undefined.
* On success, returns CERTAVA allocated from arena, and (*pbp)[-1] was
* the last character parsed. *pbp is either equal to endptr or
* points to first character after separator.
*/
static CERTAVA*
ParseRFC1485AVA(PLArenaPool* arena, const char** pbp, const char* endptr)
{
CERTAVA* a;
const NameToKind* n2k;
const char* bp;
int vt = -1;
int valLen;
PRBool isDottedOid = PR_FALSE;
SECOidTag kind = SEC_OID_UNKNOWN;
SECStatus rv = SECFailure;
SECItem derOid = { 0, NULL, 0 };
SECItem derVal = { 0, NULL, 0 };
char sep = 0;
char tagBuf[32];
char valBuf[1024];
PORT_Assert(arena);
if (SECSuccess != scanTag(pbp, endptr, tagBuf, sizeof tagBuf) ||
!(valLen = scanVal(pbp, endptr, valBuf, sizeof valBuf))) {
goto loser;
}
bp = *pbp;
if (bp < endptr) {
sep = *bp++; /* skip over separator */
}
*pbp = bp;
/* if we haven't finished, insist that we've stopped on a separator */
if (sep && sep != ',' && sep != ';' && sep != '+') {
goto loser;
}
/* is this a dotted decimal OID attribute type ? */
if (!PL_strncasecmp("oid.", tagBuf, 4) || isdigit(tagBuf[0])) {
rv = SEC_StringToOID(arena, &derOid, tagBuf, strlen(tagBuf));
isDottedOid = (PRBool)(rv == SECSuccess);
} else {
for (n2k = name2kinds; n2k->name; n2k++) {
SECOidData* oidrec;
if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) {
kind = n2k->kind;
vt = n2k->valueType;
oidrec = SECOID_FindOIDByTag(kind);
if (oidrec == NULL)
goto loser;
derOid = oidrec->oid;
break;
}
}
}
if (kind == SEC_OID_UNKNOWN && rv != SECSuccess)
goto loser;
/* Is this a hex encoding of a DER attribute value ? */
if ('#' == valBuf[0]) {
/* convert attribute value from hex to binary */
rv = hexToBin(arena, &derVal, valBuf + 1, valLen - 1);
if (rv)
goto loser;
a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal);
} else {
if (kind == SEC_OID_AVA_COUNTRY_NAME && valLen != 2)
goto loser;
if (vt == SEC_ASN1_PRINTABLE_STRING &&
!IsPrintable((unsigned char*)valBuf, valLen))
goto loser;
if (vt == SEC_ASN1_DS) {
/* RFC 4630: choose PrintableString or UTF8String */
if (IsPrintable((unsigned char*)valBuf, valLen))
vt = SEC_ASN1_PRINTABLE_STRING;
else
vt = SEC_ASN1_UTF8_STRING;
}
derVal.data = (unsigned char*)valBuf;
derVal.len = valLen;
if (kind == SEC_OID_UNKNOWN && isDottedOid) {
a = CERT_CreateAVAFromRaw(arena, &derOid, &derVal);
} else {
a = CERT_CreateAVAFromSECItem(arena, kind, vt, &derVal);
}
}
return a;
loser:
/* matched no kind -- invalid tag */
PORT_SetError(SEC_ERROR_INVALID_AVA);
return 0;
}
static CERTName*
ParseRFC1485Name(const char* buf, int len)
{
SECStatus rv;
CERTName* name;
const char *bp, *e;
CERTAVA* ava;
CERTRDN* rdn = NULL;
name = CERT_CreateName(NULL);
if (name == NULL) {
return NULL;
}
e = buf + len;
bp = buf;
while (bp < e) {
ava = ParseRFC1485AVA(name->arena, &bp, e);
if (ava == 0)
goto loser;
if (!rdn) {
rdn = CERT_CreateRDN(name->arena, ava, (CERTAVA*)0);
if (rdn == 0)
goto loser;
rv = CERT_AddRDN(name, rdn);
} else {
rv = CERT_AddAVA(name->arena, rdn, ava);
}
if (rv)
goto loser;
if (bp[-1] != '+')
rdn = NULL; /* done with this RDN */
skipSpace(&bp, e);
}
if (name->rdns[0] == 0) {
/* empty name -- illegal */
goto loser;
}
/* Reverse order of RDNS to comply with RFC */
{
CERTRDN** firstRdn;
CERTRDN** lastRdn;
CERTRDN* tmp;
/* get first one */
firstRdn = name->rdns;
/* find last one */
lastRdn = name->rdns;
while (*lastRdn)
lastRdn++;
lastRdn--;
/* reverse list */
for (; firstRdn < lastRdn; firstRdn++, lastRdn--) {
tmp = *firstRdn;
*firstRdn = *lastRdn;
*lastRdn = tmp;
}
}
/* return result */
return name;
loser:
CERT_DestroyName(name);
return NULL;
}
CERTName*
CERT_AsciiToName(const char* string)
{
CERTName* name;
name = ParseRFC1485Name(string, PORT_Strlen(string));
return name;
}
/************************************************************************/
typedef struct stringBufStr {
char* buffer;
unsigned offset;
unsigned size;
} stringBuf;
#define DEFAULT_BUFFER_SIZE 200
static SECStatus
AppendStr(stringBuf* bufp, char* str)
{
char* buf;
unsigned bufLen, bufSize, len;
int size = 0;
/* Figure out how much to grow buf by (add in the '\0') */
buf = bufp->buffer;
bufLen = bufp->offset;
len = PORT_Strlen(str);
bufSize = bufLen + len;
if (!buf) {
bufSize++;
size = PR_MAX(DEFAULT_BUFFER_SIZE, bufSize * 2);
buf = (char*)PORT_Alloc(size);
bufp->size = size;
} else if (bufp->size < bufSize) {
size = bufSize * 2;
buf = (char*)PORT_Realloc(buf, size);
bufp->size = size;
}
if (!buf) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return SECFailure;
}
bufp->buffer = buf;
bufp->offset = bufSize;
/* Concatenate str onto buf */
buf = buf + bufLen;
if (bufLen)
buf--; /* stomp on old '\0' */
PORT_Memcpy(buf, str, len + 1); /* put in new null */
return SECSuccess;
}
typedef enum {
minimalEscape = 0, /* only hex escapes, and " and \ */
minimalEscapeAndQuote, /* as above, plus quoting */
fullEscape /* no quoting, full escaping */
} EQMode;
/* Some characters must be escaped as a hex string, e.g. c -> \nn .
* Others must be escaped by preceding with a '\', e.g. c -> \c , but
* there are certain "special characters" that may be handled by either
* escaping them, or by enclosing the entire attribute value in quotes.
* A NULL value for pEQMode implies selecting minimalEscape mode.
* Some callers will do quoting when needed, others will not.
* If a caller selects minimalEscapeAndQuote, and the string does not
* need quoting, then this function changes it to minimalEscape.
*/
static int
cert_RFC1485_GetRequiredLen(const char* src, int srclen, EQMode* pEQMode)
{
int i, reqLen = 0;
EQMode mode = pEQMode ? *pEQMode : minimalEscape;
PRBool needsQuoting = PR_FALSE;
char lastC = 0;
/* need to make an initial pass to determine if quoting is needed */
for (i = 0; i < srclen; i++) {
char c = src[i];
reqLen++;
if (NEEDS_HEX_ESCAPE(c)) { /* c -> \xx */
reqLen += 2;
} else if (NEEDS_ESCAPE(c)) { /* c -> \c */
reqLen++;
} else if (SPECIAL_CHAR(c)) {
if (mode == minimalEscapeAndQuote) /* quoting is allowed */
needsQuoting = PR_TRUE; /* entirety will need quoting */
else if (mode == fullEscape)
reqLen++; /* MAY escape this character */
} else if (OPTIONAL_SPACE(c) && OPTIONAL_SPACE(lastC)) {
if (mode == minimalEscapeAndQuote) /* quoting is allowed */
needsQuoting = PR_TRUE; /* entirety will need quoting */
}
lastC = c;
}
/* if it begins or ends in optional space it needs quoting */
if (!needsQuoting && srclen > 0 && mode == minimalEscapeAndQuote &&
(OPTIONAL_SPACE(src[srclen - 1]) || OPTIONAL_SPACE(src[0]))) {
needsQuoting = PR_TRUE;
}
if (needsQuoting)
reqLen += 2;
if (pEQMode && mode == minimalEscapeAndQuote && !needsQuoting)
*pEQMode = minimalEscape;
return reqLen;
}
static const char hexChars[16] = { "0123456789abcdef" };
static SECStatus
escapeAndQuote(char* dst, int dstlen, char* src, int srclen, EQMode* pEQMode)
{
int i, reqLen = 0;
EQMode mode = pEQMode ? *pEQMode : minimalEscape;
/* space for terminal null */
reqLen = cert_RFC1485_GetRequiredLen(src, srclen, &mode) + 1;
if (reqLen > dstlen) {
PORT_SetError(SEC_ERROR_OUTPUT_LEN);
return SECFailure;
}
if (mode == minimalEscapeAndQuote)
*dst++ = C_DOUBLE_QUOTE;
for (i = 0; i < srclen; i++) {
char c = src[i];
if (NEEDS_HEX_ESCAPE(c)) {
*dst++ = C_BACKSLASH;
*dst++ = hexChars[(c >> 4) & 0x0f];
*dst++ = hexChars[c & 0x0f];
} else {
if (NEEDS_ESCAPE(c) || (SPECIAL_CHAR(c) && mode == fullEscape)) {
*dst++ = C_BACKSLASH;
}
*dst++ = c;
}
}
if (mode == minimalEscapeAndQuote)
*dst++ = C_DOUBLE_QUOTE;
*dst++ = 0;
if (pEQMode)
*pEQMode = mode;
return SECSuccess;
}
SECStatus
CERT_RFC1485_EscapeAndQuote(char* dst, int dstlen, char* src, int srclen)
{
EQMode mode = minimalEscapeAndQuote;
return escapeAndQuote(dst, dstlen, src, srclen, &mode);
}
/* convert an OID to dotted-decimal representation */
/* Returns a string that must be freed with PR_smprintf_free(), */
char*
CERT_GetOidString(const SECItem* oid)
{
PRUint8* stop; /* points to first byte after OID string */
PRUint8* first; /* byte of an OID component integer */
PRUint8* last; /* byte of an OID component integer */
char* rvString = NULL;
char* prefix = NULL;
#define MAX_OID_LEN 1024 /* bytes */
if (oid->len > MAX_OID_LEN) {
PORT_SetError(SEC_ERROR_INPUT_LEN);
return NULL;
}
/* If the OID has length 1, we bail. */
if (oid->len < 2) {
return NULL;
}
/* first will point to the next sequence of bytes to decode */
first = (PRUint8*)oid->data;
/* stop points to one past the legitimate data */
stop = &first[oid->len];
/*
* Check for our pseudo-encoded single-digit OIDs
*/
if ((*first == 0x80) && (2 == oid->len)) {
/* Funky encoding. The second byte is the number */
rvString = PR_smprintf("%lu", (PRUint32)first[1]);
if (!rvString) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
}
return rvString;
}
for (; first < stop; first = last + 1) {
unsigned int bytesBeforeLast;
for (last = first; last < stop; last++) {
if (0 == (*last & 0x80)) {
break;
}
}
/* There's no first bit set, so this isn't valid. Bail.*/
if (last == stop) {
goto unsupported;
}
bytesBeforeLast = (unsigned int)(last - first);
if (bytesBeforeLast <= 3U) { /* 0-28 bit number */
PRUint32 n = 0;
PRUint32 c;
#define CGET(i, m) \
c = last[-i] & m; \
n |= c << (7 * i)
#define CASE(i, m) \
case i: \
CGET(i, m); \
if (!n) \
goto unsupported /* fall-through */
switch (bytesBeforeLast) {
CASE(3, 0x7f);
CASE(2, 0x7f);
CASE(1, 0x7f);
case 0:
n |= last[0] & 0x7f;
break;
}
if (last[0] & 0x80) {
goto unsupported;
}
if (!rvString) {
/* This is the first number.. decompose it */
PRUint32 one = PR_MIN(n / 40, 2); /* never > 2 */
PRUint32 two = n - (one * 40);
rvString = PR_smprintf("OID.%lu.%lu", one, two);
} else {
prefix = rvString;
rvString = PR_smprintf("%s.%lu", prefix, n);
}
} else if (bytesBeforeLast <= 9U) { /* 29-64 bit number */
PRUint64 n = 0;
PRUint64 c;
switch (bytesBeforeLast) {
CASE(9, 0x01);
CASE(8, 0x7f);
CASE(7, 0x7f);
CASE(6, 0x7f);
CASE(5, 0x7f);
CASE(4, 0x7f);
CGET(3, 0x7f);
CGET(2, 0x7f);
CGET(1, 0x7f);
CGET(0, 0x7f);
break;
}
if (last[0] & 0x80)
goto unsupported;
if (!rvString) {
/* This is the first number.. decompose it */
PRUint64 one = PR_MIN(n / 40, 2); /* never > 2 */
PRUint64 two = n - (one * 40);
rvString = PR_smprintf("OID.%llu.%llu", one, two);
} else {
prefix = rvString;
rvString = PR_smprintf("%s.%llu", prefix, n);
}
} else {
/* More than a 64-bit number, or not minimal encoding. */
unsupported:
if (!rvString)
rvString = PR_smprintf("OID.UNSUPPORTED");
else {
prefix = rvString;
rvString = PR_smprintf("%s.UNSUPPORTED", prefix);
}
}
if (prefix) {
PR_smprintf_free(prefix);
prefix = NULL;
}
if (!rvString) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
break;
}
}
return rvString;
}
/* convert DER-encoded hex to a string */
static SECItem*
get_hex_string(SECItem* data)
{
SECItem* rv;
unsigned int i, j;
static const char hex[] = { "0123456789ABCDEF" };
/* '#' + 2 chars per octet + terminator */
rv = SECITEM_AllocItem(NULL, NULL, data->len * 2 + 2);
if (!rv) {
return NULL;
}
rv->data[0] = '#';
rv->len = 1 + 2 * data->len;
for (i = 0; i < data->len; i++) {
j = data->data[i];
rv->data[2 * i + 1] = hex[j >> 4];
rv->data[2 * i + 2] = hex[j & 15];
}
rv->data[rv->len] = 0;
return rv;
}
/* For compliance with RFC 2253, RFC 3280 and RFC 4630, we choose to
* use the NAME=STRING form, rather than the OID.N.N=#hexXXXX form,
* when both of these conditions are met:
* 1) The attribute name OID (kind) has a known name string that is
* defined in one of those RFCs, or in RFCs that they cite, AND
* 2) The attribute's value encoding is RFC compliant for the kind
* (e.g., the value's encoding tag is correct for the kind, and
* the value's length is in the range allowed for the kind, and
* the value's contents are appropriate for the encoding tag).
* Otherwise, we use the OID.N.N=#hexXXXX form.
*
* If the caller prefers maximum human readability to RFC compliance,
* then
* - We print the kind in NAME= string form if we know the name
* string for the attribute type OID, regardless of whether the
* value is correctly encoded or not. else we use the OID.N.N= form.
* - We use the non-hex STRING form for the attribute value if the
* value can be represented in such a form. Otherwise, we use
* the hex string form.
* This implies that, for maximum human readability, in addition to
* the two forms allowed by the RFC, we allow two other forms of output:
* - the OID.N.N=STRING form, and
* - the NAME=#hexXXXX form
* When the caller prefers maximum human readability, we do not allow
* the value of any attribute to exceed the length allowed by the RFC.
* If the attribute value exceeds the allowed length, we truncate it to
* the allowed length and append "...".
* Also in this case, we arbitrarily impose a limit on the length of the
* entire AVA encoding, regardless of the form, of 384 bytes per AVA.
* This limit includes the trailing NULL character. If the encoded
* AVA length exceeds that limit, this function reports failure to encode
* the AVA.
*
* An ASCII representation of an AVA is said to be "invertible" if
* conversion back to DER reproduces the original DER encoding exactly.
* The RFC 2253 rules do not ensure that all ASCII AVAs derived according
* to its rules are invertible. That is because the RFCs allow some
* attribute values to be encoded in any of a number of encodings,
* and the encoding type information is lost in the non-hex STRING form.
* This is particularly true of attributes of type DirectoryString.
* The encoding type information is always preserved in the hex string
* form, because the hex includes the entire DER encoding of the value.
*
* So, when the caller perfers maximum invertibility, we apply the
* RFC compliance rules stated above, and add a third required
* condition on the use of the NAME=STRING form.
* 3) The attribute's kind is not is allowed to be encoded in any of
* several different encodings, such as DirectoryStrings.
*
* The chief difference between CERT_N2A_STRICT and CERT_N2A_INVERTIBLE
* is that the latter forces DirectoryStrings to be hex encoded.
*
* As a simplification, we assume the value is correctly encoded for
* its encoding type. That is, we do not test that all the characters
* in a string encoded type are allowed by that type. We assume it.
*/
static SECStatus
AppendAVA(stringBuf* bufp, CERTAVA* ava, CertStrictnessLevel strict)
{
#define TMPBUF_LEN 2048
const NameToKind* pn2k = name2kinds;
SECItem* avaValue = NULL;
char* unknownTag = NULL;
char* encodedAVA = NULL;
PRBool useHex = PR_FALSE; /* use =#hexXXXX form */
PRBool truncateName = PR_FALSE;
PRBool truncateValue = PR_FALSE;
SECOidTag endKind;
SECStatus rv;
unsigned int len;
unsigned int nameLen, valueLen;
unsigned int maxName, maxValue;
EQMode mode = minimalEscapeAndQuote;
NameToKind n2k = { NULL, 32767, SEC_OID_UNKNOWN, SEC_ASN1_DS };
char tmpBuf[TMPBUF_LEN];
#define tagName n2k.name /* non-NULL means use NAME= form */
#define maxBytes n2k.maxLen
#define tag n2k.kind
#define vt n2k.valueType
/* READABLE mode recognizes more names from the name2kinds table
* than do STRICT or INVERTIBLE modes. This assignment chooses the
* point in the table where the attribute type name scanning stops.
*/
endKind = (strict == CERT_N2A_READABLE) ? SEC_OID_UNKNOWN
: SEC_OID_AVA_POSTAL_ADDRESS;
tag = CERT_GetAVATag(ava);
while (pn2k->kind != tag && pn2k->kind != endKind) {
++pn2k;
}
if (pn2k->kind != endKind) {
n2k = *pn2k;
} else if (strict != CERT_N2A_READABLE) {
useHex = PR_TRUE;
}
/* For invertable form, force Directory Strings to use hex form. */
if (strict == CERT_N2A_INVERTIBLE && vt == SEC_ASN1_DS) {
tagName = NULL; /* must use OID.N form */
useHex = PR_TRUE; /* must use hex string */
}
if (!useHex) {
avaValue = CERT_DecodeAVAValue(&ava->value);
if (!avaValue) {
useHex = PR_TRUE;
if (strict != CERT_N2A_READABLE) {
tagName = NULL; /* must use OID.N form */
}
}
}
if (!tagName) {
/* handle unknown attribute types per RFC 2253 */
tagName = unknownTag = CERT_GetOidString(&ava->type);
if (!tagName) {
if (avaValue)
SECITEM_FreeItem(avaValue, PR_TRUE);
return SECFailure;
}
}
if (useHex) {
avaValue = get_hex_string(&ava->value);
if (!avaValue) {
if (unknownTag)
PR_smprintf_free(unknownTag);
return SECFailure;
}
}
nameLen = strlen(tagName);
valueLen =
(useHex ? avaValue->len : cert_RFC1485_GetRequiredLen((char*)avaValue->data, avaValue->len, &mode));
len = nameLen + valueLen + 2; /* Add 2 for '=' and trailing NUL */
maxName = nameLen;
maxValue = valueLen;
if (len <= sizeof(tmpBuf)) {
encodedAVA = tmpBuf;
} else if (strict != CERT_N2A_READABLE) {
encodedAVA = PORT_Alloc(len);
if (!encodedAVA) {
SECITEM_FreeItem(avaValue, PR_TRUE);
if (unknownTag)
PR_smprintf_free(unknownTag);
return SECFailure;
}
} else {
/* Must make output fit in tmpbuf */
unsigned int fair = (sizeof tmpBuf) / 2 - 1; /* for = and \0 */
if (nameLen < fair) {
/* just truncate the value */
maxValue = (sizeof tmpBuf) - (nameLen + 6); /* for "=...\0",
and possibly '"' */
} else if (valueLen < fair) {
/* just truncate the name */
maxName = (sizeof tmpBuf) - (valueLen + 5); /* for "=...\0" */
} else {
/* truncate both */
maxName = maxValue = fair - 3; /* for "..." */
}
if (nameLen > maxName) {
PORT_Assert(unknownTag && unknownTag == tagName);
truncateName = PR_TRUE;
nameLen = maxName;
}
encodedAVA = tmpBuf;
}
memcpy(encodedAVA, tagName, nameLen);
if (truncateName) {
/* If tag name is too long, we know it is an OID form that was
* allocated from the heap, so we can modify it in place
*/
encodedAVA[nameLen - 1] = '.';
encodedAVA[nameLen - 2] = '.';
encodedAVA[nameLen - 3] = '.';
}
encodedAVA[nameLen++] = '=';
if (unknownTag)
PR_smprintf_free(unknownTag);
if (strict == CERT_N2A_READABLE && maxValue > maxBytes)
maxValue = maxBytes;
if (valueLen > maxValue) {
valueLen = maxValue;
truncateValue = PR_TRUE;
}
/* escape and quote as necessary - don't quote hex strings */
if (useHex) {
char* end = encodedAVA + nameLen + valueLen;
memcpy(encodedAVA + nameLen, (char*)avaValue->data, valueLen);
end[0] = '\0';
if (truncateValue) {
end[-1] = '.';
end[-2] = '.';
end[-3] = '.';
}
rv = SECSuccess;
} else if (!truncateValue) {
rv = escapeAndQuote(encodedAVA + nameLen, len - nameLen,
(char*)avaValue->data, avaValue->len, &mode);
} else {
/* must truncate the escaped and quoted value */
char bigTmpBuf[TMPBUF_LEN * 3 + 3];
PORT_Assert(valueLen < sizeof tmpBuf);
rv = escapeAndQuote(bigTmpBuf, sizeof bigTmpBuf, (char*)avaValue->data,
PR_MIN(avaValue->len, valueLen), &mode);
bigTmpBuf[valueLen--] = '\0'; /* hard stop here */
/* See if we're in the middle of a multi-byte UTF8 character */
while (((bigTmpBuf[valueLen] & 0xc0) == 0x80) && valueLen > 0) {
bigTmpBuf[valueLen--] = '\0';
}
/* add ellipsis to signify truncation. */
bigTmpBuf[++valueLen] = '.';
bigTmpBuf[++valueLen] = '.';
bigTmpBuf[++valueLen] = '.';
if (bigTmpBuf[0] == '"')
bigTmpBuf[++valueLen] = '"';
bigTmpBuf[++valueLen] = '\0';
PORT_Assert(nameLen + valueLen <= (sizeof tmpBuf) - 1);
memcpy(encodedAVA + nameLen, bigTmpBuf, valueLen + 1);
}
SECITEM_FreeItem(avaValue, PR_TRUE);
if (rv == SECSuccess)
rv = AppendStr(bufp, encodedAVA);
if (encodedAVA != tmpBuf)
PORT_Free(encodedAVA);
return rv;
}
#undef tagName
#undef maxBytes
#undef tag
#undef vt
char*
CERT_NameToAsciiInvertible(CERTName* name, CertStrictnessLevel strict)
{
CERTRDN** rdns;
CERTRDN** lastRdn;
CERTRDN** rdn;
PRBool first = PR_TRUE;
stringBuf strBuf = { NULL, 0, 0 };
rdns = name->rdns;
if (rdns == NULL) {
return NULL;
}
/* find last RDN */
lastRdn = rdns;
while (*lastRdn)
lastRdn++;
lastRdn--;
/*
* Loop over name contents in _reverse_ RDN order appending to string
*/
for (rdn = lastRdn; rdn >= rdns; rdn--) {
CERTAVA** avas = (*rdn)->avas;
CERTAVA* ava;
PRBool newRDN = PR_TRUE;
/*
* XXX Do we need to traverse the AVAs in reverse order, too?
*/
while (avas && (ava = *avas++) != NULL) {
SECStatus rv;
/* Put in comma or plus separator */
if (!first) {
/* Use of spaces is deprecated in RFC 2253. */
rv = AppendStr(&strBuf, newRDN ? "," : "+");
if (rv)
goto loser;
} else {
first = PR_FALSE;
}
/* Add in tag type plus value into strBuf */
rv = AppendAVA(&strBuf, ava, strict);
if (rv)
goto loser;
newRDN = PR_FALSE;
}
}
return strBuf.buffer;
loser:
if (strBuf.buffer) {
PORT_Free(strBuf.buffer);
}
return NULL;
}
char*
CERT_NameToAscii(CERTName* name)
{
return CERT_NameToAsciiInvertible(name, CERT_N2A_READABLE);
}
/*
* Return the string representation of a DER encoded distinguished name
* "dername" - The DER encoded name to convert
*/
char*
CERT_DerNameToAscii(SECItem* dername)
{
int rv;
PLArenaPool* arena = NULL;
CERTName name;
char* retstr = NULL;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
goto loser;
}
rv = SEC_QuickDERDecodeItem(arena, &name, CERT_NameTemplate, dername);
if (rv != SECSuccess) {
goto loser;
}
retstr = CERT_NameToAscii(&name);
loser:
if (arena != NULL) {
PORT_FreeArena(arena, PR_FALSE);
}
return (retstr);
}
static char*
avaToString(PLArenaPool* arena, CERTAVA* ava)
{
char* buf = NULL;
SECItem* avaValue;
int valueLen;
avaValue = CERT_DecodeAVAValue(&ava->value);
if (!avaValue) {
return buf;
}
valueLen =
cert_RFC1485_GetRequiredLen((char*)avaValue->data, avaValue->len, NULL) + 1;
if (arena) {
buf = (char*)PORT_ArenaZAlloc(arena, valueLen);
} else {
buf = (char*)PORT_ZAlloc(valueLen);
}
if (buf) {
SECStatus rv =
escapeAndQuote(buf, valueLen, (char*)avaValue->data, avaValue->len, NULL);
if (rv != SECSuccess) {
if (!arena)
PORT_Free(buf);
buf = NULL;
}
}
SECITEM_FreeItem(avaValue, PR_TRUE);
return buf;
}
/* RDNs are sorted from most general to most specific.
* This code returns the FIRST one found, the most general one found.
*/
static char*
CERT_GetNameElement(PLArenaPool* arena, const CERTName* name, int wantedTag)
{
CERTRDN** rdns = name->rdns;
CERTRDN* rdn;
CERTAVA* ava = NULL;
while (rdns && (rdn = *rdns++) != 0) {
CERTAVA** avas = rdn->avas;
while (avas && (ava = *avas++) != 0) {
int tag = CERT_GetAVATag(ava);
if (tag == wantedTag) {
avas = NULL;
rdns = NULL; /* break out of all loops */
}
}
}
return ava ? avaToString(arena, ava) : NULL;
}
/* RDNs are sorted from most general to most specific.
* This code returns the LAST one found, the most specific one found.
* This is particularly appropriate for Common Name. See RFC 2818.
*/
static char*
CERT_GetLastNameElement(PLArenaPool* arena, const CERTName* name, int wantedTag)
{
CERTRDN** rdns = name->rdns;
CERTRDN* rdn;
CERTAVA* lastAva = NULL;
while (rdns && (rdn = *rdns++) != 0) {
CERTAVA** avas = rdn->avas;
CERTAVA* ava;
while (avas && (ava = *avas++) != 0) {
int tag = CERT_GetAVATag(ava);
if (tag == wantedTag) {
lastAva = ava;
}
}
}
return lastAva ? avaToString(arena, lastAva) : NULL;
}
char*
CERT_GetCertificateEmailAddress(CERTCertificate* cert)
{
char* rawEmailAddr = NULL;
SECItem subAltName;
SECStatus rv;
CERTGeneralName* nameList = NULL;
CERTGeneralName* current;
PLArenaPool* arena = NULL;
int i;
subAltName.data = NULL;
rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject),
SEC_OID_PKCS9_EMAIL_ADDRESS);
if (rawEmailAddr == NULL) {
rawEmailAddr =
CERT_GetNameElement(cert->arena, &(cert->subject), SEC_OID_RFC1274_MAIL);
}
if (rawEmailAddr == NULL) {
rv =
CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &subAltName);
if (rv != SECSuccess) {
goto finish;
}
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
goto finish;
}
nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName);
if (!nameList) {
goto finish;
}
if (nameList != NULL) {
do {
if (current->type == certDirectoryName) {
rawEmailAddr =
CERT_GetNameElement(cert->arena, &(current->name.directoryName),
SEC_OID_PKCS9_EMAIL_ADDRESS);
if (rawEmailAddr ==
NULL) {
rawEmailAddr =
CERT_GetNameElement(cert->arena, &(current->name.directoryName),
SEC_OID_RFC1274_MAIL);
}
} else if (current->type == certRFC822Name) {
rawEmailAddr =
(char*)PORT_ArenaZAlloc(cert->arena, current->name.other.len + 1);
if (!rawEmailAddr) {
goto finish;
}
PORT_Memcpy(rawEmailAddr, current->name.other.data,
current->name.other.len);
rawEmailAddr[current->name.other.len] =
'\0';
}
if (rawEmailAddr) {
break;
}
current = CERT_GetNextGeneralName(current);
} while (current != nameList);
}
}
if (rawEmailAddr) {
for (i = 0; i <= (int)PORT_Strlen(rawEmailAddr); i++) {
rawEmailAddr[i] = tolower(rawEmailAddr[i]);
}
}
finish:
/* Don't free nameList, it's part of the arena. */
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
if (subAltName.data) {
SECITEM_FreeItem(&subAltName, PR_FALSE);
}
return (rawEmailAddr);
}
static char*
appendStringToBuf(char* dest, char* src, PRUint32* pRemaining)
{
PRUint32 len;
if (dest && src && src[0] && *pRemaining > (len = PL_strlen(src))) {
PRUint32 i;
for (i = 0; i < len; ++i)
dest[i] = tolower(src[i]);
dest[len] = 0;
dest += len + 1;
*pRemaining -= len + 1;
}
return dest;
}
#undef NEEDS_HEX_ESCAPE
#define NEEDS_HEX_ESCAPE(c) (c < 0x20)
static char*
appendItemToBuf(char* dest, SECItem* src, PRUint32* pRemaining)
{
if (dest && src && src->data && src->len && src->data[0]) {
PRUint32 len = src->len;
PRUint32 i;
PRUint32 reqLen = len + 1;
/* are there any embedded control characters ? */
for (i = 0; i < len; i++) {
if (NEEDS_HEX_ESCAPE(src->data[i]))
reqLen += 2;
}
if (*pRemaining > reqLen) {
for (i = 0; i < len; ++i) {
PRUint8 c = src->data[i];
if (NEEDS_HEX_ESCAPE(c)) {
*dest++ =
C_BACKSLASH;
*dest++ =
hexChars[(c >> 4) & 0x0f];
*dest++ =
hexChars[c & 0x0f];
} else {
*dest++ =
tolower(c);
}
}
*dest++ = '\0';
*pRemaining -= reqLen;
}
}
return dest;
}
/* Returns a pointer to an environment-like string, a series of
** null-terminated strings, terminated by a zero-length string.
** This function is intended to be internal to NSS.
*/
char*
cert_GetCertificateEmailAddresses(CERTCertificate* cert)
{
char* rawEmailAddr = NULL;
char* addrBuf = NULL;
char* pBuf = NULL;
PORTCheapArenaPool tmpArena;
PRUint32 maxLen = 0;
PRInt32 finalLen = 0;
SECStatus rv;
SECItem subAltName;
PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
subAltName.data = NULL;
maxLen = cert->derCert.len;
PORT_Assert(maxLen);
if (!maxLen)
maxLen = 2000; /* a guess, should never happen */
pBuf = addrBuf = (char*)PORT_ArenaZAlloc(&tmpArena.arena, maxLen + 1);
if (!addrBuf)
goto loser;
rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject,
SEC_OID_PKCS9_EMAIL_ADDRESS);
pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
rawEmailAddr = CERT_GetNameElement(&tmpArena.arena, &cert->subject,
SEC_OID_RFC1274_MAIL);
pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, &subAltName);
if (rv == SECSuccess && subAltName.data) {
CERTGeneralName* nameList = NULL;
if (!!(nameList = CERT_DecodeAltNameExtension(&tmpArena.arena, &subAltName))) {
CERTGeneralName* current = nameList;
do {
if (current->type == certDirectoryName) {
rawEmailAddr =
CERT_GetNameElement(&tmpArena.arena,
&current->name.directoryName,
SEC_OID_PKCS9_EMAIL_ADDRESS);
pBuf =
appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
rawEmailAddr =
CERT_GetNameElement(&tmpArena.arena,
&current->name.directoryName,
SEC_OID_RFC1274_MAIL);
pBuf =
appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
} else if (current->type == certRFC822Name) {
pBuf =
appendItemToBuf(pBuf, &current->name.other, &maxLen);
}
current = CERT_GetNextGeneralName(current);
} while (current != nameList);
}
SECITEM_FreeItem(&subAltName, PR_FALSE);
/* Don't free nameList, it's part of the tmpArena. */
}
/* now copy superstring to cert's arena */
finalLen = (pBuf - addrBuf) + 1;
pBuf = NULL;
if (finalLen > 1) {
pBuf = PORT_ArenaAlloc(cert->arena, finalLen);
if (pBuf) {
PORT_Memcpy(pBuf, addrBuf, finalLen);
}
}
loser:
PORT_DestroyCheapArena(&tmpArena);
return pBuf;
}
/* returns pointer to storage in cert's arena. Storage remains valid
** as long as cert's reference count doesn't go to zero.
** Caller should strdup or otherwise copy.
*/
const char* /* const so caller won't muck with it. */
CERT_GetFirstEmailAddress(CERTCertificate* cert)
{
if (cert && cert->emailAddr && cert->emailAddr[0])
return (const char*)cert->emailAddr;
return NULL;
}
/* returns pointer to storage in cert's arena. Storage remains valid
** as long as cert's reference count doesn't go to zero.
** Caller should strdup or otherwise copy.
*/
const char* /* const so caller won't muck with it. */
CERT_GetNextEmailAddress(CERTCertificate* cert, const char* prev)
{
if (cert && prev && prev[0]) {
PRUint32 len = PL_strlen(prev);
prev += len + 1;
if (prev && prev[0])
return prev;
}
return NULL;
}
/* This is seriously bogus, now that certs store their email addresses in
** subject Alternative Name extensions.
** Returns a string allocated by PORT_StrDup, which the caller must free.
*/
char*
CERT_GetCertEmailAddress(const CERTName* name)
{
char* rawEmailAddr;
char* emailAddr;
rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_PKCS9_EMAIL_ADDRESS);
if (rawEmailAddr == NULL) {
rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_MAIL);
}
emailAddr = CERT_FixupEmailAddr(rawEmailAddr);
if (rawEmailAddr) {
PORT_Free(rawEmailAddr);
}
return (emailAddr);
}
/* The return value must be freed with PORT_Free. */
char*
CERT_GetCommonName(const CERTName* name)
{
return (CERT_GetLastNameElement(NULL, name, SEC_OID_AVA_COMMON_NAME));
}
char*
CERT_GetCountryName(const CERTName* name)
{
return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_COUNTRY_NAME));
}
char*
CERT_GetLocalityName(const CERTName* name)
{
return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_LOCALITY));
}
char*
CERT_GetStateName(const CERTName* name)
{
return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_STATE_OR_PROVINCE));
}
char*
CERT_GetOrgName(const CERTName* name)
{
return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATION_NAME));
}
char*
CERT_GetDomainComponentName(const CERTName* name)
{
return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_DC));
}
char*
CERT_GetOrgUnitName(const CERTName* name)
{
return (
CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME));
}
char*
CERT_GetDnQualifier(const CERTName* name)
{
return (CERT_GetNameElement(NULL, name, SEC_OID_AVA_DN_QUALIFIER));
}
char*
CERT_GetCertUid(const CERTName* name)
{
return (CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_UID));
}