Back to index

lightning-sunbird  0.9+nobinonly
alg1485.c
Go to the documentation of this file.
00001 /* alg1485.c - implementation of RFCs 1485, 1779 and 2253.
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Netscape security libraries.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either the GNU General Public License Version 2 or later (the "GPL"), or
00027  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "prprf.h"
00040 #include "cert.h"
00041 #include "xconst.h"
00042 #include "genname.h"
00043 #include "secitem.h"
00044 #include "secerr.h"
00045 
00046 /* for better RFC 2253 compliance. */
00047 #define NSS_STRICT_RFC_2253_VALUES_ONLY 1
00048 
00049 struct NameToKind {
00050     const char * name;
00051     unsigned int maxLen; /* max bytes in UTF8 encoded string value */
00052     SECOidTag    kind;
00053 };
00054 
00055 /* Add new entries to this table, and maybe to function CERT_ParseRFC1485AVA */
00056 static const struct NameToKind name2kinds[] = {
00057 /* keywords given in RFC 2253 */
00058     { "CN",                      64, SEC_OID_AVA_COMMON_NAME              },
00059     { "L",                      128, SEC_OID_AVA_LOCALITY                 },
00060     { "ST",                     128, SEC_OID_AVA_STATE_OR_PROVINCE        },
00061     { "O",                       64, SEC_OID_AVA_ORGANIZATION_NAME        },
00062     { "OU",                      64, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME },
00063     { "C",                        2, SEC_OID_AVA_COUNTRY_NAME             },
00064     { "STREET",                 128, SEC_OID_AVA_STREET_ADDRESS           },
00065     { "DC",                     128, SEC_OID_AVA_DC                       },
00066     { "UID",                    256, SEC_OID_RFC1274_UID                  },
00067 
00068 #ifndef NSS_STRICT_RFC_2253_KEYWORDS_ONLY
00069 /* NSS legacy keywords */
00070     { "dnQualifier",          32767, SEC_OID_AVA_DN_QUALIFIER             },
00071     { "E",                      128, SEC_OID_PKCS9_EMAIL_ADDRESS          },
00072     { "MAIL",                   256, SEC_OID_RFC1274_MAIL                 },
00073 
00074 #ifndef NSS_LEGACY_KEYWORDS_ONLY
00075 /* values from draft-ietf-ldapbis-user-schema-05 */
00076     { "SN",                      64, SEC_OID_AVA_SURNAME                  },
00077     { "serialNumber",            64, SEC_OID_AVA_SERIAL_NUMBER            },
00078     { "title",                   64, SEC_OID_AVA_TITLE                    },
00079     { "postalAddress",          128, SEC_OID_AVA_POSTAL_ADDRESS           },
00080     { "postalCode",              40, SEC_OID_AVA_POSTAL_CODE              },
00081     { "postOfficeBox",           40, SEC_OID_AVA_POST_OFFICE_BOX          },
00082     { "givenName",               64, SEC_OID_AVA_GIVEN_NAME               },
00083     { "initials",                64, SEC_OID_AVA_INITIALS                 },
00084     { "generationQualifier",     64, SEC_OID_AVA_GENERATION_QUALIFIER     },
00085     { "houseIdentifier",         64, SEC_OID_AVA_HOUSE_IDENTIFIER         },
00086 #if 0 /* removed.  Not yet in any IETF draft or RFC. */
00087     { "pseudonym",               64, SEC_OID_AVA_PSEUDONYM                },
00088 #endif
00089 #endif
00090 #endif
00091     { 0,                        256, SEC_OID_UNKNOWN                      }
00092 };
00093 
00094 #define C_DOUBLE_QUOTE '\042'
00095 
00096 #define C_BACKSLASH '\134'
00097 
00098 #define C_EQUAL '='
00099 
00100 #define OPTIONAL_SPACE(c) \
00101     (((c) == ' ') || ((c) == '\r') || ((c) == '\n'))
00102 
00103 #define SPECIAL_CHAR(c)                                        \
00104     (((c) == ',') || ((c) == '=') || ((c) == C_DOUBLE_QUOTE) ||       \
00105      ((c) == '\r') || ((c) == '\n') || ((c) == '+') ||         \
00106      ((c) == '<') || ((c) == '>') || ((c) == '#') ||           \
00107      ((c) == ';') || ((c) == C_BACKSLASH))
00108 
00109 
00110 #define IS_PRINTABLE(c)                                        \
00111     ((((c) >= 'a') && ((c) <= 'z')) ||                         \
00112      (((c) >= 'A') && ((c) <= 'Z')) ||                         \
00113      (((c) >= '0') && ((c) <= '9')) ||                         \
00114      ((c) == ' ') ||                                    \
00115      ((c) == '\'') ||                                          \
00116      ((c) == '\050') ||                          /* ( */              \
00117      ((c) == '\051') ||                          /* ) */              \
00118      (((c) >= '+') && ((c) <= '/')) ||           /* + , - . / */      \
00119      ((c) == ':') ||                                    \
00120      ((c) == '=') ||                                    \
00121      ((c) == '?'))
00122 
00123 int
00124 cert_AVAOidTagToMaxLen(SECOidTag tag)
00125 {
00126     const struct NameToKind *n2k = name2kinds;
00127 
00128     while (n2k->kind != tag && n2k->kind != SEC_OID_UNKNOWN) {
00129        ++n2k;
00130     }
00131     return (n2k->kind != SEC_OID_UNKNOWN) ? n2k->maxLen : -1;
00132 }
00133 
00134 static PRBool
00135 IsPrintable(unsigned char *data, unsigned len)
00136 {
00137     unsigned char ch, *end;
00138 
00139     end = data + len;
00140     while (data < end) {
00141        ch = *data++;
00142        if (!IS_PRINTABLE(ch)) {
00143            return PR_FALSE;
00144        }
00145     }
00146     return PR_TRUE;
00147 }
00148 
00149 static PRBool
00150 Is7Bit(unsigned char *data, unsigned len)
00151 {
00152     unsigned char ch, *end;
00153 
00154     end = data + len;
00155     while (data < end) {
00156         ch = *data++;
00157         if ((ch & 0x80)) {
00158             return PR_FALSE;
00159         }
00160     }
00161     return PR_TRUE;
00162 }
00163 
00164 static void
00165 skipSpace(char **pbp, char *endptr)
00166 {
00167     char *bp = *pbp;
00168     while (bp < endptr && OPTIONAL_SPACE(*bp)) {
00169        bp++;
00170     }
00171     *pbp = bp;
00172 }
00173 
00174 static SECStatus
00175 scanTag(char **pbp, char *endptr, char *tagBuf, int tagBufSize)
00176 {
00177     char *bp, *tagBufp;
00178     int taglen;
00179 
00180     PORT_Assert(tagBufSize > 0);
00181     
00182     /* skip optional leading space */
00183     skipSpace(pbp, endptr);
00184     if (*pbp == endptr) {
00185        /* nothing left */
00186        return SECFailure;
00187     }
00188     
00189     /* fill tagBuf */
00190     taglen = 0;
00191     bp = *pbp;
00192     tagBufp = tagBuf;
00193     while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) {
00194        if (++taglen >= tagBufSize) {
00195            *pbp = bp;
00196            return SECFailure;
00197        }
00198        *tagBufp++ = *bp++;
00199     }
00200     /* null-terminate tagBuf -- guaranteed at least one space left */
00201     *tagBufp++ = 0;
00202     *pbp = bp;
00203     
00204     /* skip trailing spaces till we hit something - should be an equal sign */
00205     skipSpace(pbp, endptr);
00206     if (*pbp == endptr) {
00207        /* nothing left */
00208        return SECFailure;
00209     }
00210     if (**pbp != C_EQUAL) {
00211        /* should be an equal sign */
00212        return SECFailure;
00213     }
00214     /* skip over the equal sign */
00215     (*pbp)++;
00216     
00217     return SECSuccess;
00218 }
00219 
00220 static SECStatus
00221 scanVal(char **pbp, char *endptr, char *valBuf, int valBufSize)  
00222 {
00223     char *bp, *valBufp;
00224     int vallen;
00225     PRBool isQuoted;
00226     
00227     PORT_Assert(valBufSize > 0);
00228     
00229     /* skip optional leading space */
00230     skipSpace(pbp, endptr);
00231     if(*pbp == endptr) {
00232        /* nothing left */
00233        return SECFailure;
00234     }
00235     
00236     bp = *pbp;
00237     
00238     /* quoted? */
00239     if (*bp == C_DOUBLE_QUOTE) {
00240        isQuoted = PR_TRUE;
00241        /* skip over it */
00242        bp++;
00243     } else {
00244        isQuoted = PR_FALSE;
00245     }
00246     
00247     valBufp = valBuf;
00248     vallen = 0;
00249     while (bp < endptr) {
00250        char c = *bp;
00251        if (c == C_BACKSLASH) {
00252            /* escape character */
00253            bp++;
00254            if (bp >= endptr) {
00255               /* escape charater must appear with paired char */
00256               *pbp = bp;
00257               return SECFailure;
00258            }
00259        } else if (!isQuoted && SPECIAL_CHAR(c)) {
00260            /* unescaped special and not within quoted value */
00261            break;
00262        } else if (c == C_DOUBLE_QUOTE) {
00263            /* reached unescaped double quote */
00264            break;
00265        }
00266        /* append character */
00267         vallen++;
00268        if (vallen >= valBufSize) {
00269            *pbp = bp;
00270            return SECFailure;
00271        }
00272        *valBufp++ = *bp++;
00273     }
00274     
00275     /* stip trailing spaces from unquoted values */
00276     if (!isQuoted) {
00277        if (valBufp > valBuf) {
00278            valBufp--;
00279            while ((valBufp > valBuf) && OPTIONAL_SPACE(*valBufp)) {
00280               valBufp--;
00281            }
00282            valBufp++;
00283        }
00284     }
00285     
00286     if (isQuoted) {
00287        /* insist that we stopped on a double quote */
00288        if (*bp != C_DOUBLE_QUOTE) {
00289            *pbp = bp;
00290            return SECFailure;
00291        }
00292        /* skip over the quote and skip optional space */
00293        bp++;
00294        skipSpace(&bp, endptr);
00295     }
00296     
00297     *pbp = bp;
00298     
00299     if (valBufp == valBuf) {
00300        /* empty value -- not allowed */
00301        return SECFailure;
00302     }
00303     
00304     /* null-terminate valBuf -- guaranteed at least one space left */
00305     *valBufp++ = 0;
00306     
00307     return SECSuccess;
00308 }
00309 
00310 CERTAVA *
00311 CERT_ParseRFC1485AVA(PRArenaPool *arena, char **pbp, char *endptr,
00312                   PRBool singleAVA) 
00313 {
00314     CERTAVA *a;
00315     const struct NameToKind *n2k;
00316     int vt;
00317     int valLen;
00318     char *bp;
00319 
00320     char tagBuf[32];
00321     char valBuf[384];
00322 
00323     if (scanTag(pbp, endptr, tagBuf, sizeof(tagBuf)) == SECFailure ||
00324        scanVal(pbp, endptr, valBuf, sizeof(valBuf)) == SECFailure) {
00325        PORT_SetError(SEC_ERROR_INVALID_AVA);
00326        return 0;
00327     }
00328 
00329     /* insist that if we haven't finished we've stopped on a separator */
00330     bp = *pbp;
00331     if (bp < endptr) {
00332        if (singleAVA || (*bp != ',' && *bp != ';')) {
00333            PORT_SetError(SEC_ERROR_INVALID_AVA);
00334            *pbp = bp;
00335            return 0;
00336        }
00337        /* ok, skip over separator */
00338        bp++;
00339     }
00340     *pbp = bp;
00341 
00342     for (n2k = name2kinds; n2k->name; n2k++) {
00343        if (PORT_Strcasecmp(n2k->name, tagBuf) == 0) {
00344            valLen = PORT_Strlen(valBuf);
00345            if (n2k->kind == SEC_OID_AVA_COUNTRY_NAME) {
00346               vt = SEC_ASN1_PRINTABLE_STRING;
00347               if (valLen != 2) {
00348                   PORT_SetError(SEC_ERROR_INVALID_AVA);
00349                   return 0;
00350               }
00351               if (!IsPrintable((unsigned char*) valBuf, 2)) {
00352                   PORT_SetError(SEC_ERROR_INVALID_AVA);
00353                   return 0;
00354               }
00355            } else if ((n2k->kind == SEC_OID_PKCS9_EMAIL_ADDRESS) ||
00356                      (n2k->kind == SEC_OID_RFC1274_MAIL)) {
00357               vt = SEC_ASN1_IA5_STRING;
00358            } else {
00359               /* Hack -- for rationale see X.520 DirectoryString defn */
00360               if (IsPrintable((unsigned char*)valBuf, valLen)) {
00361                   vt = SEC_ASN1_PRINTABLE_STRING;
00362                 } else if (Is7Bit((unsigned char *)valBuf, valLen)) {
00363                     vt = SEC_ASN1_T61_STRING;
00364               } else {
00365                   /* according to RFC3280, UTF8String is preferred encoding */
00366                   vt = SEC_ASN1_UTF8_STRING;
00367               }
00368            }
00369            a = CERT_CreateAVA(arena, n2k->kind, vt, (char *) valBuf);
00370            return a;
00371        }
00372     }
00373     /* matched no kind -- invalid tag */
00374     PORT_SetError(SEC_ERROR_INVALID_AVA);
00375     return 0;
00376 }
00377 
00378 static CERTName *
00379 ParseRFC1485Name(char *buf, int len)
00380 {
00381     SECStatus rv;
00382     CERTName *name;
00383     char *bp, *e;
00384     CERTAVA *ava;
00385     CERTRDN *rdn;
00386 
00387     name = CERT_CreateName(NULL);
00388     if (name == NULL) {
00389        return NULL;
00390     }
00391     
00392     e = buf + len;
00393     bp = buf;
00394     while (bp < e) {
00395        ava = CERT_ParseRFC1485AVA(name->arena, &bp, e, PR_FALSE);
00396        if (ava == 0) goto loser;
00397        rdn = CERT_CreateRDN(name->arena, ava, 0);
00398        if (rdn == 0) goto loser;
00399        rv = CERT_AddRDN(name, rdn);
00400        if (rv) goto loser;
00401        skipSpace(&bp, e);
00402     }
00403 
00404     if (name->rdns[0] == 0) {
00405        /* empty name -- illegal */
00406        goto loser;
00407     }
00408 
00409     /* Reverse order of RDNS to comply with RFC */
00410     {
00411        CERTRDN **firstRdn;
00412        CERTRDN **lastRdn;
00413        CERTRDN *tmp;
00414        
00415        /* get first one */
00416        firstRdn = name->rdns;
00417        
00418        /* find last one */
00419        lastRdn = name->rdns;
00420        while (*lastRdn) lastRdn++;
00421        lastRdn--;
00422        
00423        /* reverse list */
00424        for ( ; firstRdn < lastRdn; firstRdn++, lastRdn--) {
00425            tmp = *firstRdn;
00426            *firstRdn = *lastRdn;
00427            *lastRdn = tmp;
00428        }
00429     }
00430     
00431     /* return result */
00432     return name;
00433     
00434   loser:
00435     CERT_DestroyName(name);
00436     return NULL;
00437 }
00438 
00439 CERTName *
00440 CERT_AsciiToName(char *string)
00441 {
00442     CERTName *name;
00443     name = ParseRFC1485Name(string, PORT_Strlen(string));
00444     return name;
00445 }
00446 
00447 /************************************************************************/
00448 
00449 typedef struct stringBufStr {
00450     char *buffer;
00451     unsigned offset;
00452     unsigned size;
00453 } stringBuf;
00454 
00455 #define DEFAULT_BUFFER_SIZE 200
00456 
00457 static SECStatus
00458 AppendStr(stringBuf *bufp, char *str)
00459 {
00460     char *buf;
00461     unsigned bufLen, bufSize, len;
00462     int size = 0;
00463 
00464     /* Figure out how much to grow buf by (add in the '\0') */
00465     buf = bufp->buffer;
00466     bufLen = bufp->offset;
00467     len = PORT_Strlen(str);
00468     bufSize = bufLen + len;
00469     if (!buf) {
00470        bufSize++;
00471        size = PR_MAX(DEFAULT_BUFFER_SIZE,bufSize*2);
00472        buf = (char *) PORT_Alloc(size);
00473        bufp->size = size;
00474     } else if (bufp->size < bufSize) {
00475        size = bufSize*2;
00476        buf =(char *) PORT_Realloc(buf,size);
00477        bufp->size = size;
00478     }
00479     if (!buf) {
00480        PORT_SetError(SEC_ERROR_NO_MEMORY);
00481        return SECFailure;
00482     }
00483     bufp->buffer = buf;
00484     bufp->offset = bufSize;
00485 
00486     /* Concatenate str onto buf */
00487     buf = buf + bufLen;
00488     if (bufLen) buf--;                    /* stomp on old '\0' */
00489     PORT_Memcpy(buf, str, len+1);         /* put in new null */
00490     return SECSuccess;
00491 }
00492 
00493 SECStatus
00494 CERT_RFC1485_EscapeAndQuote(char *dst, int dstlen, char *src, int srclen)
00495 {
00496     int i, reqLen=0;
00497     char *d = dst;
00498     PRBool needsQuoting = PR_FALSE;
00499     char lastC = 0;
00500     
00501     /* need to make an initial pass to determine if quoting is needed */
00502     for (i = 0; i < srclen; i++) {
00503        char c = src[i];
00504        reqLen++;
00505        if (!needsQuoting && (SPECIAL_CHAR(c) ||
00506            (OPTIONAL_SPACE(c) && OPTIONAL_SPACE(lastC)))) {
00507            /* entirety will need quoting */
00508            needsQuoting = PR_TRUE;
00509        }
00510        if (c == C_DOUBLE_QUOTE || c == C_BACKSLASH) {
00511            /* this char will need escaping */
00512            reqLen++;
00513        }
00514        lastC = c;
00515     }
00516     /* if it begins or ends in optional space it needs quoting */
00517     if (!needsQuoting && srclen > 0 && 
00518        (OPTIONAL_SPACE(src[srclen-1]) || OPTIONAL_SPACE(src[0]))) {
00519        needsQuoting = PR_TRUE;
00520     }
00521     
00522     if (needsQuoting) reqLen += 2;
00523 
00524     /* space for terminal null */
00525     reqLen++;
00526     
00527     if (reqLen > dstlen) {
00528        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
00529        return SECFailure;
00530     }
00531     
00532     d = dst;
00533     if (needsQuoting) *d++ = C_DOUBLE_QUOTE;
00534     for (i = 0; i < srclen; i++) {
00535        char c = src[i];
00536        if (c == C_DOUBLE_QUOTE || c == C_BACKSLASH) {
00537            /* escape it */
00538            *d++ = C_BACKSLASH;
00539        }
00540        *d++ = c;
00541     }
00542     if (needsQuoting) *d++ = C_DOUBLE_QUOTE;
00543     *d++ = 0;
00544     return SECSuccess;
00545 }
00546 
00547 /* convert an OID to dotted-decimal representation */
00548 /* Returns a string that must be freed with PR_smprintf_free(), */
00549 char *
00550 CERT_GetOidString(const SECItem *oid)
00551 {
00552     PRUint8 *end;
00553     PRUint8 *d;
00554     PRUint8 *e;
00555     char *a         = NULL;
00556     char *b;
00557 
00558 #define MAX_OID_LEN 1024 /* bytes */
00559 
00560     if (oid->len > MAX_OID_LEN) {
00561        PORT_SetError(SEC_ERROR_INPUT_LEN);
00562        return NULL;
00563     }
00564 
00565     /* d will point to the next sequence of bytes to decode */
00566     d = (PRUint8 *)oid->data;
00567     /* end points to one past the legitimate data */
00568     end = &d[ oid->len ];
00569 
00570     /*
00571      * Check for our pseudo-encoded single-digit OIDs
00572      */
00573     if( (*d == 0x80) && (2 == oid->len) ) {
00574        /* Funky encoding.  The second byte is the number */
00575        a = PR_smprintf("%lu", (PRUint32)d[1]);
00576        if( (char *)NULL == a ) {
00577            PORT_SetError(SEC_ERROR_NO_MEMORY);
00578            return (char *)NULL;
00579        }
00580        return a;
00581     }
00582 
00583     for( ; d < end; d = &e[1] ) {
00584     
00585        for( e = d; e < end; e++ ) {
00586            if( 0 == (*e & 0x80) ) {
00587               break;
00588            }
00589        }
00590     
00591        if( ((e-d) > 4) || (((e-d) == 4) && (*d & 0x70)) ) {
00592            /* More than a 32-bit number */
00593        } else {
00594            PRUint32 n = 0;
00595       
00596            switch( e-d ) {
00597            case 4:
00598               n |= ((PRUint32)(e[-4] & 0x0f)) << 28;
00599            case 3:
00600               n |= ((PRUint32)(e[-3] & 0x7f)) << 21;
00601            case 2:
00602               n |= ((PRUint32)(e[-2] & 0x7f)) << 14;
00603            case 1:
00604               n |= ((PRUint32)(e[-1] & 0x7f)) <<  7;
00605            case 0:
00606               n |= ((PRUint32)(e[-0] & 0x7f))      ;
00607            }
00608       
00609            if( (char *)NULL == a ) {
00610               /* This is the first number.. decompose it */
00611               PRUint32 one = PR_MIN(n/40, 2); /* never > 2 */
00612               PRUint32 two = n - one * 40;
00613         
00614               a = PR_smprintf("OID.%lu.%lu", one, two);
00615               if( (char *)NULL == a ) {
00616                   PORT_SetError(SEC_ERROR_NO_MEMORY);
00617                   return (char *)NULL;
00618               }
00619            } else {
00620               b = PR_smprintf("%s.%lu", a, n);
00621               if( (char *)NULL == b ) {
00622                   PR_smprintf_free(a);
00623                   PORT_SetError(SEC_ERROR_NO_MEMORY);
00624                   return (char *)NULL;
00625               }
00626         
00627               PR_smprintf_free(a);
00628               a = b;
00629            }
00630        }
00631     }
00632 
00633     return a;
00634 }
00635 
00636 /* convert DER-encoded hex to a string */
00637 static SECItem *
00638 get_hex_string(SECItem *data)
00639 {
00640     SECItem *rv;
00641     unsigned int i, j;
00642     static const char hex[] = { "0123456789ABCDEF" };
00643 
00644     /* '#' + 2 chars per octet + terminator */
00645     rv = SECITEM_AllocItem(NULL, NULL, data->len*2 + 2);
00646     if (!rv) {
00647        return NULL;
00648     }
00649     rv->data[0] = '#';
00650     rv->len = 1 + 2 * data->len;
00651     for (i=0; i<data->len; i++) {
00652        j = data->data[i];
00653        rv->data[2*i+1] = hex[j >> 4];
00654        rv->data[2*i+2] = hex[j & 15];
00655     }
00656     rv->data[rv->len] = 0;
00657     return rv;
00658 }
00659 
00660 static SECStatus
00661 AppendAVA(stringBuf *bufp, CERTAVA *ava)
00662 {
00663     const struct NameToKind *n2k = name2kinds;
00664     const char *tagName;
00665     unsigned len, maxLen;
00666     int tag;
00667     SECStatus rv;
00668     SECItem *avaValue = NULL;
00669     char *unknownTag = NULL;
00670     PRBool hexValue = PR_FALSE;
00671     char tmpBuf[384];
00672 
00673     tag = CERT_GetAVATag(ava);
00674     while (n2k->kind != tag && n2k->kind != SEC_OID_UNKNOWN) {
00675         ++n2k;
00676     }
00677     if (n2k->kind != SEC_OID_UNKNOWN) {
00678         tagName = n2k->name;
00679     } else {
00680        /* handle unknown attribute types per RFC 2253 */
00681        tagName = unknownTag = CERT_GetOidString(&ava->type);
00682        if (!tagName)
00683            return SECFailure;
00684     }
00685     maxLen = n2k->maxLen;
00686 
00687 #ifdef NSS_STRICT_RFC_2253_VALUES_ONLY
00688     if (!unknownTag)
00689 #endif
00690     avaValue = CERT_DecodeAVAValue(&ava->value);
00691     if(!avaValue) {
00692        /* the attribute value is not recognized, get the hex value */
00693        avaValue = get_hex_string(&ava->value);
00694        if(!avaValue) {
00695            if (unknownTag) PR_smprintf_free(unknownTag);
00696            return SECFailure;
00697        }
00698        hexValue = PR_TRUE;
00699     }
00700 
00701     /* Check value length */
00702     if (avaValue->len > maxLen + 3) {  /* must be room for "..." */
00703        /* avaValue is a UTF8 string, freshly allocated and returned to us 
00704        ** by CERT_DecodeAVAValue or get_hex_string just above, so we can
00705        ** modify it here.  See if we're in the middle of a multi-byte
00706        ** UTF8 character.
00707        */
00708        while (((avaValue->data[maxLen] & 0xc0) == 0x80) && maxLen > 0) {
00709           maxLen--;
00710        }
00711        /* add elipsis to signify truncation. */
00712        avaValue->data[maxLen++] = '.'; 
00713        avaValue->data[maxLen++] = '.';
00714        avaValue->data[maxLen++] = '.';
00715        avaValue->data[maxLen]   = 0;
00716        avaValue->len = maxLen;
00717     }
00718 
00719     len = PORT_Strlen(tagName);
00720     if (len+1 > sizeof(tmpBuf)) {
00721        if (unknownTag) PR_smprintf_free(unknownTag);
00722        SECITEM_FreeItem(avaValue, PR_TRUE);
00723        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
00724        return SECFailure;
00725     }
00726     PORT_Memcpy(tmpBuf, tagName, len);
00727     if (unknownTag) PR_smprintf_free(unknownTag);
00728     tmpBuf[len++] = '=';
00729     
00730     /* escape and quote as necessary - don't quote hex strings */
00731     if (hexValue) {
00732         /* appent avaValue to tmpBuf */
00733        if (avaValue->len + len + 1 > sizeof tmpBuf) {
00734            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
00735            rv = SECFailure;
00736        } else {
00737            PORT_Strncpy(tmpBuf+len, (char *)avaValue->data, avaValue->len + 1);
00738            rv = SECSuccess;
00739        }
00740     } else 
00741        rv = CERT_RFC1485_EscapeAndQuote(tmpBuf+len, sizeof(tmpBuf)-len, 
00742                                  (char *)avaValue->data, avaValue->len);
00743     SECITEM_FreeItem(avaValue, PR_TRUE);
00744     if (rv) return SECFailure;
00745     
00746     rv = AppendStr(bufp, tmpBuf);
00747     return rv;
00748 }
00749 
00750 char *
00751 CERT_NameToAscii(CERTName *name)
00752 {
00753     CERTRDN** rdns;
00754     CERTRDN** lastRdn;
00755     CERTRDN** rdn;
00756     PRBool first = PR_TRUE;
00757     stringBuf strBuf = { NULL, 0, 0 };
00758     
00759     rdns = name->rdns;
00760     if (rdns == NULL) {
00761        return NULL;
00762     }
00763     
00764     /* find last RDN */
00765     lastRdn = rdns;
00766     while (*lastRdn) lastRdn++;
00767     lastRdn--;
00768     
00769     /*
00770      * Loop over name contents in _reverse_ RDN order appending to string
00771      */
00772     for (rdn = lastRdn; rdn >= rdns; rdn--) {
00773        CERTAVA** avas = (*rdn)->avas;
00774        CERTAVA* ava;
00775        PRBool newRDN = PR_TRUE;
00776 
00777        /* 
00778         * XXX Do we need to traverse the AVAs in reverse order, too?
00779         */
00780        while (avas && (ava = *avas++) != NULL) {
00781            SECStatus rv;
00782            /* Put in comma or plus separator */
00783            if (!first) {
00784               /* Use of spaces is deprecated in RFC 2253. */
00785               rv = AppendStr(&strBuf, newRDN ? "," : "+");
00786               if (rv) goto loser;
00787            } else {
00788               first = PR_FALSE;
00789            }
00790            
00791            /* Add in tag type plus value into buf */
00792            rv = AppendAVA(&strBuf, ava);
00793            if (rv) goto loser;
00794            newRDN = PR_FALSE;
00795        }
00796     }
00797     return strBuf.buffer;
00798 loser:
00799     if (strBuf.buffer) {
00800        PORT_Free(strBuf.buffer);
00801     }
00802     return NULL;
00803 }
00804 
00805 /*
00806  * Return the string representation of a DER encoded distinguished name
00807  * "dername" - The DER encoded name to convert
00808  */
00809 char *
00810 CERT_DerNameToAscii(SECItem *dername)
00811 {
00812     int rv;
00813     PRArenaPool *arena = NULL;
00814     CERTName name;
00815     char *retstr = NULL;
00816     
00817     arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00818     
00819     if ( arena == NULL) {
00820        goto loser;
00821     }
00822     
00823     rv = SEC_QuickDERDecodeItem(arena, &name, CERT_NameTemplate, dername);
00824     
00825     if ( rv != SECSuccess ) {
00826        goto loser;
00827     }
00828 
00829     retstr = CERT_NameToAscii(&name);
00830 
00831 loser:
00832     if ( arena != NULL ) {
00833        PORT_FreeArena(arena, PR_FALSE);
00834     }
00835     
00836     return(retstr);
00837 }
00838 
00839 /* RDNs are sorted from most general to most specific.
00840  * This code returns the FIRST one found, the most general one found.
00841  */
00842 static char *
00843 CERT_GetNameElement(PRArenaPool *arena, CERTName *name, int wantedTag)
00844 {
00845     CERTRDN** rdns;
00846     CERTRDN *rdn;
00847     char *buf = 0;
00848     
00849     rdns = name->rdns;
00850     while (rdns && (rdn = *rdns++) != 0) {
00851        CERTAVA** avas = rdn->avas;
00852        CERTAVA*  ava;
00853        while (avas && (ava = *avas++) != 0) {
00854            int tag = CERT_GetAVATag(ava);
00855            if ( tag == wantedTag ) {
00856               SECItem *decodeItem = CERT_DecodeAVAValue(&ava->value);
00857               if(!decodeItem) {
00858                   return NULL;
00859               }
00860               if (arena) {
00861                   buf = (char *)PORT_ArenaZAlloc(arena,decodeItem->len + 1);
00862               } else {
00863                   buf = (char *)PORT_ZAlloc(decodeItem->len + 1);
00864               }
00865               if ( buf ) {
00866                   PORT_Memcpy(buf, decodeItem->data, decodeItem->len);
00867                   buf[decodeItem->len] = 0;
00868               }
00869               SECITEM_FreeItem(decodeItem, PR_TRUE);
00870               goto done;
00871            }
00872        }
00873     }
00874     
00875   done:
00876     return buf;
00877 }
00878 
00879 /* RDNs are sorted from most general to most specific.
00880  * This code returns the LAST one found, the most specific one found.
00881  * This is particularly appropriate for Common Name.  See RFC 2818.
00882  */
00883 static char *
00884 CERT_GetLastNameElement(PRArenaPool *arena, CERTName *name, int wantedTag)
00885 {
00886     CERTRDN** rdns;
00887     CERTRDN *rdn;
00888     CERTAVA * lastAva = NULL;
00889     char *buf = 0;
00890     
00891     rdns = name->rdns;
00892     while (rdns && (rdn = *rdns++) != 0) {
00893        CERTAVA** avas = rdn->avas;
00894        CERTAVA*  ava;
00895        while (avas && (ava = *avas++) != 0) {
00896            int tag = CERT_GetAVATag(ava);
00897            if ( tag == wantedTag ) {
00898               lastAva = ava;
00899            }
00900        }
00901     }
00902 
00903     if (lastAva) {
00904        SECItem *decodeItem = CERT_DecodeAVAValue(&lastAva->value);
00905        if(!decodeItem) {
00906            return NULL;
00907        }
00908        if (arena) {
00909            buf = (char *)PORT_ArenaZAlloc(arena,decodeItem->len + 1);
00910        } else {
00911            buf = (char *)PORT_ZAlloc(decodeItem->len + 1);
00912        }
00913        if ( buf ) {
00914            PORT_Memcpy(buf, decodeItem->data, decodeItem->len);
00915            buf[decodeItem->len] = 0;
00916        }
00917        SECITEM_FreeItem(decodeItem, PR_TRUE);
00918     }    
00919     return buf;
00920 }
00921 
00922 char *
00923 CERT_GetCertificateEmailAddress(CERTCertificate *cert)
00924 {
00925     char *rawEmailAddr = NULL;
00926     SECItem subAltName;
00927     SECStatus rv;
00928     CERTGeneralName *nameList = NULL;
00929     CERTGeneralName *current;
00930     PRArenaPool *arena = NULL;
00931     int i;
00932     
00933     subAltName.data = NULL;
00934 
00935     rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject),
00936                                            SEC_OID_PKCS9_EMAIL_ADDRESS);
00937     if ( rawEmailAddr == NULL ) {
00938        rawEmailAddr = CERT_GetNameElement(cert->arena, &(cert->subject), 
00939                                                  SEC_OID_RFC1274_MAIL);
00940     }
00941     if ( rawEmailAddr == NULL) {
00942 
00943        rv = CERT_FindCertExtension(cert,  SEC_OID_X509_SUBJECT_ALT_NAME, 
00944                                                         &subAltName);
00945        if (rv != SECSuccess) {
00946            goto finish;
00947        }
00948        arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
00949        if (!arena) {
00950            goto finish;
00951        }
00952        nameList = current = CERT_DecodeAltNameExtension(arena, &subAltName);
00953        if (!nameList ) {
00954            goto finish;
00955        }
00956        if (nameList != NULL) {
00957            do {
00958               if (current->type == certDirectoryName) {
00959                   rawEmailAddr = CERT_GetNameElement(cert->arena,
00960                      &(current->name.directoryName), 
00961                                           SEC_OID_PKCS9_EMAIL_ADDRESS);
00962                   if ( rawEmailAddr == NULL ) {
00963                      rawEmailAddr = CERT_GetNameElement(cert->arena,
00964                        &(current->name.directoryName), SEC_OID_RFC1274_MAIL);
00965                   }
00966               } else if (current->type == certRFC822Name) {
00967                   rawEmailAddr = (char*)PORT_ArenaZAlloc(cert->arena,
00968                                           current->name.other.len + 1);
00969                   if (!rawEmailAddr) {
00970                      goto finish;
00971                   }
00972                   PORT_Memcpy(rawEmailAddr, current->name.other.data, 
00973                             current->name.other.len);
00974                   rawEmailAddr[current->name.other.len] = '\0';
00975               }
00976               if (rawEmailAddr) {
00977                   break;
00978               }
00979               current = CERT_GetNextGeneralName(current);
00980            } while (current != nameList);
00981        }
00982     }
00983     if (rawEmailAddr) {
00984        for (i = 0; i <= (int) PORT_Strlen(rawEmailAddr); i++) {
00985            rawEmailAddr[i] = tolower(rawEmailAddr[i]);
00986        }
00987     } 
00988 
00989 finish:
00990 
00991     /* Don't free nameList, it's part of the arena. */
00992 
00993     if (arena) {
00994        PORT_FreeArena(arena, PR_FALSE);
00995     }
00996 
00997     if ( subAltName.data ) {
00998        SECITEM_FreeItem(&subAltName, PR_FALSE);
00999     }
01000 
01001     return(rawEmailAddr);
01002 }
01003 
01004 static char *
01005 appendStringToBuf(char *dest, char *src, PRUint32 *pRemaining)
01006 {
01007     PRUint32 len;
01008     if (dest && src && src[0] && *pRemaining > (len = PL_strlen(src))) {
01009        PRUint32 i;
01010        for (i = 0; i < len; ++i)
01011            dest[i] = tolower(src[i]);
01012        dest[len] = 0;
01013        dest        += len + 1;
01014        *pRemaining -= len + 1;
01015     }
01016     return dest;
01017 }
01018 
01019 static char *
01020 appendItemToBuf(char *dest, SECItem *src, PRUint32 *pRemaining)
01021 {
01022     if (dest && src && src->data && src->len && src->data[0] && 
01023         *pRemaining > src->len + 1 ) {
01024        PRUint32 len = src->len;
01025        PRUint32 i;
01026        for (i = 0; i < len && src->data[i] ; ++i)
01027            dest[i] = tolower(src->data[i]);
01028        dest[len] = 0;
01029        dest        += len + 1;
01030        *pRemaining -= len + 1;
01031     }
01032     return dest;
01033 }
01034 
01035 /* Returns a pointer to an environment-like string, a series of 
01036 ** null-terminated strings, terminated by a zero-length string.
01037 ** This function is intended to be internal to NSS.
01038 */
01039 char *
01040 cert_GetCertificateEmailAddresses(CERTCertificate *cert)
01041 {
01042     char *           rawEmailAddr = NULL;
01043     char *           addrBuf      = NULL;
01044     char *           pBuf         = NULL;
01045     PRArenaPool *    tmpArena     = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
01046     PRUint32         maxLen       = 0;
01047     PRInt32          finalLen     = 0;
01048     SECStatus        rv;
01049     SECItem          subAltName;
01050     
01051     if (!tmpArena) 
01052        return addrBuf;
01053 
01054     subAltName.data = NULL;
01055     maxLen = cert->derCert.len;
01056     PORT_Assert(maxLen);
01057     if (!maxLen) 
01058        maxLen = 2000;  /* a guess, should never happen */
01059 
01060     pBuf = addrBuf = (char *)PORT_ArenaZAlloc(tmpArena, maxLen + 1);
01061     if (!addrBuf) 
01062        goto loser;
01063 
01064     rawEmailAddr = CERT_GetNameElement(tmpArena, &cert->subject,
01065                                    SEC_OID_PKCS9_EMAIL_ADDRESS);
01066     pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
01067 
01068     rawEmailAddr = CERT_GetNameElement(tmpArena, &cert->subject, 
01069                                    SEC_OID_RFC1274_MAIL);
01070     pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
01071 
01072     rv = CERT_FindCertExtension(cert,  SEC_OID_X509_SUBJECT_ALT_NAME, 
01073                             &subAltName);
01074     if (rv == SECSuccess && subAltName.data) {
01075        CERTGeneralName *nameList     = NULL;
01076 
01077        if (!!(nameList = CERT_DecodeAltNameExtension(tmpArena, &subAltName))) {
01078            CERTGeneralName *current = nameList;
01079            do {
01080               if (current->type == certDirectoryName) {
01081                   rawEmailAddr = CERT_GetNameElement(tmpArena,
01082                                             &current->name.directoryName, 
01083                                           SEC_OID_PKCS9_EMAIL_ADDRESS);
01084                   pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
01085 
01086                   rawEmailAddr = CERT_GetNameElement(tmpArena,
01087                                          &current->name.directoryName, 
01088                                          SEC_OID_RFC1274_MAIL);
01089                   pBuf = appendStringToBuf(pBuf, rawEmailAddr, &maxLen);
01090               } else if (current->type == certRFC822Name) {
01091                   pBuf = appendItemToBuf(pBuf, &current->name.other, &maxLen);
01092               }
01093               current = CERT_GetNextGeneralName(current);
01094            } while (current != nameList);
01095        }
01096        SECITEM_FreeItem(&subAltName, PR_FALSE);
01097        /* Don't free nameList, it's part of the tmpArena. */
01098     }
01099     /* now copy superstring to cert's arena */
01100     finalLen = (pBuf - addrBuf) + 1;
01101     pBuf = NULL;
01102     if (finalLen > 1) {
01103        pBuf = PORT_ArenaAlloc(cert->arena, finalLen);
01104        if (pBuf) {
01105            PORT_Memcpy(pBuf, addrBuf, finalLen);
01106        }
01107     }
01108 loser:
01109     if (tmpArena)
01110        PORT_FreeArena(tmpArena, PR_FALSE);
01111 
01112     return pBuf;
01113 }
01114 
01115 /* returns pointer to storage in cert's arena.  Storage remains valid
01116 ** as long as cert's reference count doesn't go to zero.
01117 ** Caller should strdup or otherwise copy.
01118 */
01119 const char *  /* const so caller won't muck with it. */
01120 CERT_GetFirstEmailAddress(CERTCertificate * cert)
01121 {
01122     if (cert && cert->emailAddr && cert->emailAddr[0])
01123        return (const char *)cert->emailAddr;
01124     return NULL;
01125 }
01126 
01127 /* returns pointer to storage in cert's arena.  Storage remains valid
01128 ** as long as cert's reference count doesn't go to zero.
01129 ** Caller should strdup or otherwise copy.
01130 */
01131 const char *  /* const so caller won't muck with it. */
01132 CERT_GetNextEmailAddress(CERTCertificate * cert, const char * prev)
01133 {
01134     if (cert && prev && prev[0]) {
01135        PRUint32 len = PL_strlen(prev);
01136        prev += len + 1;
01137        if (prev && prev[0])
01138            return prev;
01139     }
01140     return NULL;
01141 }
01142 
01143 /* This is seriously bogus, now that certs store their email addresses in
01144 ** subject Alternative Name extensions. 
01145 ** Returns a string allocated by PORT_StrDup, which the caller must free.
01146 */
01147 char *
01148 CERT_GetCertEmailAddress(CERTName *name)
01149 {
01150     char *rawEmailAddr;
01151     char *emailAddr;
01152 
01153     
01154     rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_PKCS9_EMAIL_ADDRESS);
01155     if ( rawEmailAddr == NULL ) {
01156        rawEmailAddr = CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_MAIL);
01157     }
01158     emailAddr = CERT_FixupEmailAddr(rawEmailAddr);
01159     if ( rawEmailAddr ) {
01160        PORT_Free(rawEmailAddr);
01161     }
01162     return(emailAddr);
01163 }
01164 
01165 /* The return value must be freed with PORT_Free. */
01166 char *
01167 CERT_GetCommonName(CERTName *name)
01168 {
01169     return(CERT_GetLastNameElement(NULL, name, SEC_OID_AVA_COMMON_NAME));
01170 }
01171 
01172 char *
01173 CERT_GetCountryName(CERTName *name)
01174 {
01175     return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_COUNTRY_NAME));
01176 }
01177 
01178 char *
01179 CERT_GetLocalityName(CERTName *name)
01180 {
01181     return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_LOCALITY));
01182 }
01183 
01184 char *
01185 CERT_GetStateName(CERTName *name)
01186 {
01187     return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_STATE_OR_PROVINCE));
01188 }
01189 
01190 char *
01191 CERT_GetOrgName(CERTName *name)
01192 {
01193     return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATION_NAME));
01194 }
01195 
01196 char *
01197 CERT_GetDomainComponentName(CERTName *name)
01198 {
01199     return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_DC));
01200 }
01201 
01202 char *
01203 CERT_GetOrgUnitName(CERTName *name)
01204 {
01205     return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME));
01206 }
01207 
01208 char *
01209 CERT_GetDnQualifier(CERTName *name)
01210 {
01211     return(CERT_GetNameElement(NULL, name, SEC_OID_AVA_DN_QUALIFIER));
01212 }
01213 
01214 char *
01215 CERT_GetCertUid(CERTName *name)
01216 {
01217     return(CERT_GetNameElement(NULL, name, SEC_OID_RFC1274_UID));
01218 }
01219