Milán Major

netscape-revival

1 branch
Code

security/lib/cert/gename.c

#include "cert.h"
#include "gename.h"

extern int SEC_ERROR_EXTENSION_VALUE_INVALID;

#define ERROR_BREAK(error) PORT_SetError(error); rv = SECFailure; break;

/* We only want to deal with the URI field for now.  Skip other fields
   during the decoding process for now.
 */
static const SEC_ASN1Template CERT_URITemplate[] = {
    { SEC_ASN1_CONTEXT_SPECIFIC | 6 ,
          offsetof(CERTGeneralName, name.uri), SEC_IA5StringTemplate,
          sizeof (CERTGeneralName)}
};

static const SEC_ASN1Template CERT_DirectoryNameTemplate[] = {
    { SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | 4,
          offsetof(CERTGeneralName, derDirectoryName), SEC_AnyTemplate,
          sizeof (CERTGeneralName)}
};

static const SEC_ASN1Template CERT_OtherNamesTemplate[] = {
    { SEC_ASN1_ANY | SEC_ASN1_OPTIONAL,
          offsetof(CERTGeneralName, name.other), SEC_AnyTemplate,
          sizeof (CERTGeneralName)}
};

const SEC_ASN1Template CERT_GeneralNameTemplate[] = {
    { SEC_ASN1_SEQUENCE_OF, 0, SEC_AnyTemplate }
};

void cert_DestroyGenName (CERTGeneralName **genNames)
{
    CERTGeneralName **genName;

    if (!genNames || !*genNames)
	return;
    
    genName = genNames;
    while (genName) {
	if ((*genName)->type == certDirectoryName) {
	    CERT_DestroyName(&((*genName)->name.directoryName));
	}
	else {    
	    PORT_Free ((*genName)->name.other.data);
	    PORT_Free (*genName);
	}
	++genName;
    }
    PORT_Free (*genNames);
}

SECStatus
cert_EncodeGeneralName (PRArenaPool *arena, CERTGeneralName **genNames,
			SECItem ***encodedGenName)
{
    CERTGeneralName **genName;
    SECItem **encodedName;
    SECStatus rv = SECSuccess;
    int entriesCount = 0;
    void *mark;

    PORT_Assert (arena);
    
    /* there is nothing to do */
    if (!genNames || !*genNames)
	return (SECSuccess);
    
    genName = genNames;

    /* Count all entries so we don't have to reallocate buffer.
       At the same time validate data.
     */
    while (*genName) {
	switch ((*genName)->type) {
	    case certURI :
		if ((*genName)->name.uri.data == NULL)  {
		    ERROR_BREAK (SEC_ERROR_EXTENSION_VALUE_INVALID);
		}
		break;

	    case certDNSName:
	    case certEDIPartyName:
	    case certIPAddress:
	    case certOtherName:
	    case certRegisterID:
	    case certRFC822Name:
	    case certX400Address:
		if ((*genName)->name.other.data == NULL ||
		   ((*genName)->name.other.data[0] & 0x1f) != (*genName)->type - 1) {
		    ERROR_BREAK (SEC_ERROR_EXTENSION_VALUE_INVALID);
		}
		else {
		    SEC_ASN1Template template;
		    SECItem decodedData;

		    template.kind = SEC_ASN1_ANY;
		    template.offset = 0;
		    template.sub = NULL;
		    template.size = sizeof (SECItem);
		    decodedData.data = NULL;
		    
		    /* The only thing we can check here is the outter tag is
		       encoded properly.
		     */
		    rv = SEC_ASN1DecodeItem
			 (NULL, &decodedData, &template, &((*genName)->name.other));
		    PORT_Free (decodedData.data);
		}
		break;

	    case certDirectoryName:		
		if (!((*genName)->name.directoryName.rdns) ||
		    !*((*genName)->name.directoryName.rdns)) {
		    ERROR_BREAK (SEC_ERROR_EXTENSION_VALUE_INVALID);			
		}
		break;
		
	    default:
		ERROR_BREAK (SEC_ERROR_EXTENSION_VALUE_INVALID);
		break;
	}
	if (rv != SECSuccess)
	    break;
	++entriesCount;
        ++genName;
    }
    if (*genName || rv != SECSuccess)
	return (SECFailure);
    
    if (entriesCount == 0)
	return (SECSuccess);

    mark = PORT_ArenaMark (arena); 
    /* allocate enough space to hold the encoded entries */
    encodedName = (SECItem **)PORT_ArenaZAlloc
		  (arena, sizeof (SECItem *) * (entriesCount + 1));
    if (encodedName == NULL)  {
	PORT_ArenaRelease (arena, mark);
	return (SECFailure);
    }

    entriesCount = 0;
    genName = genNames;
    *encodedGenName = encodedName;
    while (*genName) {
	switch ((*genName)->type) {
	    case certURI :
		encodedName[entriesCount] = SEC_ASN1EncodeItem
					    (arena, (SECItem *)NULL,
					     *genName, CERT_URITemplate);
		break;
		
	    case certDNSName:
	    case certEDIPartyName:
	    case certIPAddress:
	    case certOtherName:
	    case certRegisterID:
	    case certRFC822Name:
	    case certX400Address:
		/* for these types, we expect the value is already encoded */
		encodedName[entriesCount] = SEC_ASN1EncodeItem
					    (arena, (SECItem *)NULL,
					     *genName, CERT_OtherNamesTemplate);
		break;
	    case certDirectoryName:
		SEC_ASN1EncodeItem (arena, &((*genName)->derDirectoryName),
				    &((*genName)->name.directoryName), CERT_NameTemplate);
		if ((*genName)->derDirectoryName.data == NULL) {
		    ERROR_BREAK (SEC_ERROR_EXTENSION_VALUE_INVALID);
		}
			
			
		encodedName[entriesCount] = SEC_ASN1EncodeItem
					    (arena, (SECItem *)NULL,
					     *genName, CERT_DirectoryNameTemplate);
		break;
	}
	if (!encodedName[entriesCount++]) {
	    rv = SECFailure;
	    break;
	}
	++genName;
    }
    if (*genName) {
	PORT_ArenaRelease (arena, mark);
	return (SECFailure);
    }
    return (SECSuccess);
}    

SECStatus
cert_DecodeGeneralName (PRArenaPool *arena, CERTGeneralName ***genNames,
			SECItem **encodedGenName)
{
    CERTGeneralName **genName = NULL;
    SECItem **encodedName;
    SECStatus rv = SECSuccess;
    void *mark;
    int entriesCount = 0;

    PORT_Assert (arena);
    
    /* there is nothing to decode */
    if (!encodedGenName || !*encodedGenName)
	return (SECSuccess);
    
    *genNames = NULL;
    encodedName = encodedGenName;

    for (entriesCount = 0;*encodedName; ++entriesCount, ++encodedName) {} 

    if (entriesCount == 0)
	return (SECSuccess);

    mark = PORT_ArenaMark (arena);
    genName = (CERTGeneralName **)PORT_ArenaZAlloc
	      (arena, sizeof (CERTGeneralName *) * (entriesCount + 1));
    if (!genName) {
	PORT_ArenaRelease (arena, mark);
	return (SECFailure);
    }
    
    encodedName = encodedGenName;
    *genNames = genName;
    while (*encodedName) {
	*genName = (CERTGeneralName *)PORT_ArenaZAlloc
		   (arena, sizeof (CERTGeneralName));
	if (!*genName)
	    break;

	(*genName)->type = ((*encodedName)->data[0] & 0x1f) + 1;
	switch ((*genName)->type) {
	    case certURI:
		rv = SEC_ASN1DecodeItem
		     (arena,*genName, CERT_URITemplate, *encodedName);
		break;
		
	    case certDNSName:
	    case certEDIPartyName:
	    case certIPAddress:
	    case certOtherName:
	    case certRegisterID:
	    case certRFC822Name:
	    case certX400Address:
	    /* Since we don't support these for now, just save them in the encoded format */
		(*genName)->name.other.data = PORT_ArenaZAlloc
					  (arena, (*encodedName)->len);
		if (!(*genName)->name.other.data)
		    rv = SECFailure;
		else
		    PORT_Memcpy ((*genName)->name.other.data, (*encodedName)->data,
			     (*genName)->name.other.len = (*encodedName)->len);
		break;

	    case certDirectoryName:
		rv = SEC_ASN1DecodeItem
		     (arena, *genName, CERT_DirectoryNameTemplate, *encodedName);
		if (rv != SECSuccess)
		    break;
		rv = SEC_ASN1DecodeItem
		     (arena, &((*genName)->name.directoryName), CERT_NameTemplate,
		      &((*genName)->derDirectoryName));
		break;
	}
	if (rv != SECSuccess)
	    break;
	++genName;
	++encodedName;
    }

    if (*encodedName) {
	PORT_ArenaRelease (arena,mark);
	*genNames = NULL;
	return (SECFailure);
    }
    return (SECSuccess);
}

/* For Directory name type, if derFormat is TRUE, then value must be of type CERTName *;
   otherwise, it must be of type SECItem *
 */
void *
CERT_GetGeneralNameByType (CERTGeneralName **genNames,
			   CERTGeneralNameType type, PRBool derFormat)
{
    CERTGeneralName **genName;
    
    if (!genNames)
	return (NULL);
    genName = genNames;

    while (*genName) {
	if ((*genName)->type == type) {
	    switch (type) {
		case certDNSName:
		case certEDIPartyName:
		case certIPAddress:
		case certOtherName:
		case certRegisterID:
		case certRFC822Name:
		case certX400Address:
		    return (&(*genName)->name.other);
		case certURI:
		    return (&(*genName)->name.uri);
		    break;
		case certDirectoryName:
		    if (derFormat)
			return (&(*genName)->derDirectoryName);
		    else
			return (&(*genName)->name.directoryName);
		    break;
	    }
	}
	++genName;
    }
    return (NULL);
}