Back to index

lightning-sunbird  0.9+nobinonly
cmsattr.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 /*
00038  * CMS attributes.
00039  *
00040  * $Id: cmsattr.c,v 1.9 2004/04/25 15:03:16 gerv%gerv.net Exp $
00041  */
00042 
00043 #include "cmslocal.h"
00044 
00045 #include "secasn1.h"
00046 #include "secitem.h"
00047 #include "secoid.h"
00048 #include "pk11func.h"
00049 #include "prtime.h"
00050 #include "secerr.h"
00051 
00052 /*
00053  * -------------------------------------------------------------------
00054  * XXX The following Attribute stuff really belongs elsewhere.
00055  * The Attribute type is *not* part of CMS but rather X.501.
00056  * But for now, since CMS is the only customer of attributes,
00057  * we define them here.  Once there is a use outside of CMS,
00058  * then change the attribute types and functions from internal
00059  * to external naming convention, and move them elsewhere!
00060  */
00061 
00062 
00063 /*
00064  * NSS_CMSAttribute_Create - create an attribute
00065  *
00066  * if value is NULL, the attribute won't have a value. It can be added later
00067  * with NSS_CMSAttribute_AddValue.
00068  */
00069 NSSCMSAttribute *
00070 NSS_CMSAttribute_Create(PRArenaPool *poolp, SECOidTag oidtag, SECItem *value, PRBool encoded)
00071 {
00072     NSSCMSAttribute *attr;
00073     SECItem *copiedvalue;
00074     void *mark;
00075 
00076     PORT_Assert (poolp != NULL);
00077 
00078     mark = PORT_ArenaMark (poolp);
00079 
00080     attr = (NSSCMSAttribute *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSAttribute));
00081     if (attr == NULL)
00082        goto loser;
00083 
00084     attr->typeTag = SECOID_FindOIDByTag(oidtag);
00085     if (attr->typeTag == NULL)
00086        goto loser;
00087 
00088     if (SECITEM_CopyItem(poolp, &(attr->type), &(attr->typeTag->oid)) != SECSuccess)
00089        goto loser;
00090 
00091     if (value != NULL) {
00092        if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
00093            goto loser;
00094 
00095        if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
00096            goto loser;
00097     }
00098 
00099     attr->encoded = encoded;
00100 
00101     PORT_ArenaUnmark (poolp, mark);
00102 
00103     return attr;
00104 
00105 loser:
00106     PORT_Assert (mark != NULL);
00107     PORT_ArenaRelease (poolp, mark);
00108     return NULL;
00109 }
00110 
00111 /*
00112  * NSS_CMSAttribute_AddValue - add another value to an attribute
00113  */
00114 SECStatus
00115 NSS_CMSAttribute_AddValue(PLArenaPool *poolp, NSSCMSAttribute *attr, SECItem *value)
00116 {
00117     SECItem *copiedvalue;
00118     void *mark;
00119 
00120     PORT_Assert (poolp != NULL);
00121 
00122     mark = PORT_ArenaMark(poolp);
00123 
00124     if (value == NULL) {
00125        PORT_SetError(SEC_ERROR_INVALID_ARGS);
00126        goto loser;
00127     }
00128 
00129     if ((copiedvalue = SECITEM_ArenaDupItem(poolp, value)) == NULL)
00130        goto loser;
00131 
00132     if (NSS_CMSArray_Add(poolp, (void ***)&(attr->values), (void *)copiedvalue) != SECSuccess)
00133        goto loser;
00134 
00135     PORT_ArenaUnmark(poolp, mark);
00136     return SECSuccess;
00137 
00138 loser:
00139     PORT_Assert (mark != NULL);
00140     PORT_ArenaRelease (poolp, mark);
00141     return SECFailure;
00142 }
00143 
00144 /*
00145  * NSS_CMSAttribute_GetType - return the OID tag
00146  */
00147 SECOidTag
00148 NSS_CMSAttribute_GetType(NSSCMSAttribute *attr)
00149 {
00150     SECOidData *typetag;
00151 
00152     typetag = SECOID_FindOID(&(attr->type));
00153     if (typetag == NULL)
00154        return SEC_OID_UNKNOWN;
00155 
00156     return typetag->offset;
00157 }
00158 
00159 /*
00160  * NSS_CMSAttribute_GetValue - return the first attribute value
00161  *
00162  * We do some sanity checking first:
00163  * - Multiple values are *not* expected.
00164  * - Empty values are *not* expected.
00165  */
00166 SECItem *
00167 NSS_CMSAttribute_GetValue(NSSCMSAttribute *attr)
00168 {
00169     SECItem *value;
00170 
00171     if (attr == NULL)
00172        return NULL;
00173 
00174     value = attr->values[0];
00175 
00176     if (value == NULL || value->data == NULL || value->len == 0)
00177        return NULL;
00178 
00179     if (attr->values[1] != NULL)
00180        return NULL;
00181 
00182     return value;
00183 }
00184 
00185 /*
00186  * NSS_CMSAttribute_CompareValue - compare the attribute's first value against data
00187  */
00188 PRBool
00189 NSS_CMSAttribute_CompareValue(NSSCMSAttribute *attr, SECItem *av)
00190 {
00191     SECItem *value;
00192     
00193     if (attr == NULL)
00194        return PR_FALSE;
00195 
00196     value = NSS_CMSAttribute_GetValue(attr);
00197 
00198     return (value != NULL && value->len == av->len &&
00199        PORT_Memcmp (value->data, av->data, value->len) == 0);
00200 }
00201 
00202 /*
00203  * templates and functions for separate ASN.1 encoding of attributes
00204  *
00205  * used in NSS_CMSAttributeArray_Reorder
00206  */
00207 
00208 /*
00209  * helper function for dynamic template determination of the attribute value
00210  */
00211 static const SEC_ASN1Template *
00212 cms_attr_choose_attr_value_template(void *src_or_dest, PRBool encoding)
00213 {
00214     const SEC_ASN1Template *theTemplate;
00215     NSSCMSAttribute *attribute;
00216     SECOidData *oiddata;
00217     PRBool encoded;
00218 
00219     PORT_Assert (src_or_dest != NULL);
00220     if (src_or_dest == NULL)
00221        return NULL;
00222 
00223     attribute = (NSSCMSAttribute *)src_or_dest;
00224 
00225     if (encoding && (!attribute->values || !attribute->values[0] ||
00226         attribute->encoded)) {
00227         /* we're encoding, and the attribute has no value or the attribute
00228          * value is already encoded. */
00229         return SEC_ASN1_GET(SEC_AnyTemplate);
00230     }
00231 
00232     /* get attribute's typeTag */
00233     oiddata = attribute->typeTag;
00234     if (oiddata == NULL) {
00235        oiddata = SECOID_FindOID(&attribute->type);
00236        attribute->typeTag = oiddata;
00237     }
00238 
00239     if (oiddata == NULL) {
00240        /* still no OID tag? OID is unknown then. en/decode value as ANY. */
00241        encoded = PR_TRUE;
00242        theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
00243     } else {
00244        switch (oiddata->offset) {
00245        case SEC_OID_PKCS9_SMIME_CAPABILITIES:
00246        case SEC_OID_SMIME_ENCRYPTION_KEY_PREFERENCE:
00247            /* these guys need to stay DER-encoded */
00248        default:
00249            /* same goes for OIDs that are not handled here */
00250            encoded = PR_TRUE;
00251            theTemplate = SEC_ASN1_GET(SEC_AnyTemplate);
00252            break;
00253            /* otherwise choose proper template */
00254        case SEC_OID_PKCS9_EMAIL_ADDRESS:
00255        case SEC_OID_RFC1274_MAIL:
00256        case SEC_OID_PKCS9_UNSTRUCTURED_NAME:
00257            encoded = PR_FALSE;
00258            theTemplate = SEC_ASN1_GET(SEC_IA5StringTemplate);
00259            break;
00260        case SEC_OID_PKCS9_CONTENT_TYPE:
00261            encoded = PR_FALSE;
00262            theTemplate = SEC_ASN1_GET(SEC_ObjectIDTemplate);
00263            break;
00264        case SEC_OID_PKCS9_MESSAGE_DIGEST:
00265            encoded = PR_FALSE;
00266            theTemplate = SEC_ASN1_GET(SEC_OctetStringTemplate);
00267            break;
00268        case SEC_OID_PKCS9_SIGNING_TIME:
00269            encoded = PR_FALSE;
00270            theTemplate = SEC_ASN1_GET(CERT_TimeChoiceTemplate);
00271            break;
00272          /* XXX Want other types here, too */
00273        }
00274     }
00275 
00276     if (encoding) {
00277        /*
00278         * If we are encoding and we think we have an already-encoded value,
00279         * then the code which initialized this attribute should have set
00280         * the "encoded" property to true (and we would have returned early,
00281         * up above).  No devastating error, but that code should be fixed.
00282         * (It could indicate that the resulting encoded bytes are wrong.)
00283         */
00284        PORT_Assert (!encoded);
00285     } else {
00286        /*
00287         * We are decoding; record whether the resulting value is
00288         * still encoded or not.
00289         */
00290        attribute->encoded = encoded;
00291     }
00292     return theTemplate;
00293 }
00294 
00295 static const SEC_ASN1TemplateChooserPtr cms_attr_chooser
00296        = cms_attr_choose_attr_value_template;
00297 
00298 const SEC_ASN1Template nss_cms_attribute_template[] = {
00299     { SEC_ASN1_SEQUENCE,
00300          0, NULL, sizeof(NSSCMSAttribute) },
00301     { SEC_ASN1_OBJECT_ID,
00302          offsetof(NSSCMSAttribute,type) },
00303     { SEC_ASN1_DYNAMIC | SEC_ASN1_SET_OF,
00304          offsetof(NSSCMSAttribute,values),
00305          &cms_attr_chooser },
00306     { 0 }
00307 };
00308 
00309 const SEC_ASN1Template nss_cms_set_of_attribute_template[] = {
00310     { SEC_ASN1_SET_OF, 0, nss_cms_attribute_template },
00311 };
00312 
00313 /* =============================================================================
00314  * Attribute Array methods
00315  */
00316 
00317 /*
00318  * NSS_CMSAttributeArray_Encode - encode an Attribute array as SET OF Attributes
00319  *
00320  * If you are wondering why this routine does not reorder the attributes
00321  * first, and might be tempted to make it do so, see the comment by the
00322  * call to ReorderAttributes in cmsencode.c.  (Or, see who else calls this
00323  * and think long and hard about the implications of making it always
00324  * do the reordering.)
00325  */
00326 SECItem *
00327 NSS_CMSAttributeArray_Encode(PRArenaPool *poolp, NSSCMSAttribute ***attrs, SECItem *dest)
00328 {
00329     return SEC_ASN1EncodeItem (poolp, dest, (void *)attrs, nss_cms_set_of_attribute_template);
00330 }
00331 
00332 /*
00333  * NSS_CMSAttributeArray_Reorder - sort attribute array by attribute's DER encoding
00334  *
00335  * make sure that the order of the attributes guarantees valid DER (which must be
00336  * in lexigraphically ascending order for a SET OF); if reordering is necessary it
00337  * will be done in place (in attrs).
00338  */
00339 SECStatus
00340 NSS_CMSAttributeArray_Reorder(NSSCMSAttribute **attrs)
00341 {
00342     return NSS_CMSArray_SortByDER((void **)attrs, nss_cms_attribute_template, NULL);
00343 }
00344 
00345 /*
00346  * NSS_CMSAttributeArray_FindAttrByOidTag - look through a set of attributes and
00347  * find one that matches the specified object ID.
00348  *
00349  * If "only" is true, then make sure that there is not more than one attribute
00350  * of the same type.  Otherwise, just return the first one found. (XXX Does
00351  * anybody really want that first-found behavior?  It was like that when I found it...)
00352  */
00353 NSSCMSAttribute *
00354 NSS_CMSAttributeArray_FindAttrByOidTag(NSSCMSAttribute **attrs, SECOidTag oidtag, PRBool only)
00355 {
00356     SECOidData *oid;
00357     NSSCMSAttribute *attr1, *attr2;
00358 
00359     if (attrs == NULL)
00360        return NULL;
00361 
00362     oid = SECOID_FindOIDByTag(oidtag);
00363     if (oid == NULL)
00364        return NULL;
00365 
00366     while ((attr1 = *attrs++) != NULL) {
00367        if (attr1->type.len == oid->oid.len && PORT_Memcmp (attr1->type.data,
00368                                                      oid->oid.data,
00369                                                      oid->oid.len) == 0)
00370            break;
00371     }
00372 
00373     if (attr1 == NULL)
00374        return NULL;
00375 
00376     if (!only)
00377        return attr1;
00378 
00379     while ((attr2 = *attrs++) != NULL) {
00380        if (attr2->type.len == oid->oid.len && PORT_Memcmp (attr2->type.data,
00381                                                      oid->oid.data,
00382                                                      oid->oid.len) == 0)
00383            break;
00384     }
00385 
00386     if (attr2 != NULL)
00387        return NULL;
00388 
00389     return attr1;
00390 }
00391 
00392 /*
00393  * NSS_CMSAttributeArray_AddAttr - add an attribute to an
00394  * array of attributes. 
00395  */
00396 SECStatus
00397 NSS_CMSAttributeArray_AddAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, NSSCMSAttribute *attr)
00398 {
00399     NSSCMSAttribute *oattr;
00400     void *mark;
00401     SECOidTag type;
00402 
00403     mark = PORT_ArenaMark(poolp);
00404 
00405     /* find oidtag of attr */
00406     type = NSS_CMSAttribute_GetType(attr);
00407 
00408     /* see if we have one already */
00409     oattr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
00410     PORT_Assert (oattr == NULL);
00411     if (oattr != NULL)
00412        goto loser;   /* XXX or would it be better to replace it? */
00413 
00414     /* no, shove it in */
00415     if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
00416        goto loser;
00417 
00418     PORT_ArenaUnmark(poolp, mark);
00419     return SECSuccess;
00420 
00421 loser:
00422     PORT_ArenaRelease(poolp, mark);
00423     return SECFailure;
00424 }
00425 
00426 /*
00427  * NSS_CMSAttributeArray_SetAttr - set an attribute's value in a set of attributes
00428  */
00429 SECStatus
00430 NSS_CMSAttributeArray_SetAttr(PLArenaPool *poolp, NSSCMSAttribute ***attrs, SECOidTag type, SECItem *value, PRBool encoded)
00431 {
00432     NSSCMSAttribute *attr;
00433     void *mark;
00434 
00435     mark = PORT_ArenaMark(poolp);
00436 
00437     /* see if we have one already */
00438     attr = NSS_CMSAttributeArray_FindAttrByOidTag(*attrs, type, PR_FALSE);
00439     if (attr == NULL) {
00440        /* not found? create one! */
00441        attr = NSS_CMSAttribute_Create(poolp, type, value, encoded);
00442        if (attr == NULL)
00443            goto loser;
00444        /* and add it to the list */
00445        if (NSS_CMSArray_Add(poolp, (void ***)attrs, (void *)attr) != SECSuccess)
00446            goto loser;
00447     } else {
00448        /* found, shove it in */
00449        /* XXX we need a decent memory model @#$#$!#!!! */
00450        attr->values[0] = value;
00451        attr->encoded = encoded;
00452     }
00453 
00454     PORT_ArenaUnmark (poolp, mark);
00455     return SECSuccess;
00456 
00457 loser:
00458     PORT_ArenaRelease (poolp, mark);
00459     return SECFailure;
00460 }
00461