2N/A/*
2N/A * The contents of this file are subject to the Mozilla Public
2N/A * License Version 1.1 (the "License"); you may not use this file
2N/A * except in compliance with the License. You may obtain a copy of
2N/A * the License at http://www.mozilla.org/MPL/
2N/A *
2N/A * Software distributed under the License is distributed on an "AS
2N/A * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
2N/A * implied. See the License for the specific language governing
2N/A * rights and limitations under the License.
2N/A *
2N/A * The Original Code is the Netscape security libraries.
2N/A *
2N/A * The Initial Developer of the Original Code is Netscape
2N/A * Communications Corporation. Portions created by Netscape are
2N/A * Copyright (C) 1994-2000 Netscape Communications Corporation. All
2N/A * Rights Reserved.
2N/A *
2N/A * Contributor(s):
2N/A *
2N/A * Alternatively, the contents of this file may be used under the
2N/A * terms of the GNU General Public License Version 2 or later (the
2N/A * "GPL"), in which case the provisions of the GPL are applicable
2N/A * instead of those above. If you wish to allow use of your
2N/A * version of this file only under the terms of the GPL and not to
2N/A * allow others to use your version of this file under the MPL,
2N/A * indicate your decision by deleting the provisions above and
2N/A * replace them with the notice and other provisions required by
2N/A * the GPL. If you do not delete the provisions above, a recipient
2N/A * may use your version of this file under either the MPL or the
2N/A * GPL.
2N/A *
2N/A * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A *
2N/A * File: rdn_parser.c
2N/A */
2N/A
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <kmfapi.h>
2N/A#include <kmfapiP.h>
2N/A#include <ber_der.h>
2N/A#include <rdn_parser.h>
2N/A#include <stdio.h>
2N/A#include <values.h>
2N/A
2N/A/*
2N/A * The order here is important. The OIDs are arranged in order of
2N/A * significance. The CN is the most specific value, the C (country)
2N/A * is less specific, etc. Add to this list with care.
2N/A */
2N/Astatic const struct NameToKind name2kinds[] = {
2N/A{ "CN", OID_AVA_COMMON_NAME, (KMF_OID *)&KMFOID_CommonName},
2N/A{ "SN", OID_AVA_SURNAME, (KMF_OID *)&KMFOID_Surname},
2N/A{ "GN", OID_AVA_GIVEN_NAME, (KMF_OID *)&KMFOID_GivenName},
2N/A{ "emailAddress", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
2N/A{ "E", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
2N/A{ "MAIL", OID_RFC1274_MAIL, (KMF_OID *)&KMFOID_RFC822mailbox},
2N/A{ "STREET", OID_AVA_STREET_ADDRESS, (KMF_OID *)&KMFOID_StreetAddress},
2N/A{ "UID", OID_RFC1274_UID, (KMF_OID *)&KMFOID_userid},
2N/A{ "OU", OID_AVA_ORGANIZATIONAL_UNIT_NAME,
2N/A (KMF_OID *)&KMFOID_OrganizationalUnitName},
2N/A{ "O", OID_AVA_ORGANIZATION_NAME, (KMF_OID *)&KMFOID_OrganizationName},
2N/A{ "L", OID_AVA_LOCALITY, (KMF_OID *)&KMFOID_LocalityName},
2N/A{ "ST", OID_AVA_STATE_OR_PROVINCE,
2N/A (KMF_OID *)&KMFOID_StateProvinceName},
2N/A{ "C", OID_AVA_COUNTRY_NAME, (KMF_OID *)&KMFOID_CountryName},
2N/A{ "DC", OID_AVA_DC, (KMF_OID *)&KMFOID_domainComponent},
2N/A{ 0, OID_UNKNOWN, NULL}
2N/A};
2N/A
2N/Astatic KMF_BOOL
2N/AIsPrintable(unsigned char *data, unsigned len)
2N/A{
2N/A unsigned char ch, *end;
2N/A
2N/A end = data + len;
2N/A while (data < end) {
2N/A ch = *data++;
2N/A if (!IS_PRINTABLE(ch)) {
2N/A return (B_FALSE);
2N/A }
2N/A }
2N/A return (B_TRUE);
2N/A}
2N/A
2N/Astatic KMF_BOOL
2N/AIs7Bit(unsigned char *data, unsigned len)
2N/A{
2N/A unsigned char ch, *end;
2N/A
2N/A end = data + len;
2N/A while (data < end) {
2N/A ch = *data++;
2N/A if ((ch & 0x80)) {
2N/A return (B_FALSE);
2N/A }
2N/A }
2N/A return (B_TRUE);
2N/A}
2N/A
2N/Astatic void
2N/AskipSpace(char **pbp, char *endptr)
2N/A{
2N/A char *bp = *pbp;
2N/A while (bp < endptr && OPTIONAL_SPACE(*bp)) {
2N/A bp++;
2N/A }
2N/A *pbp = bp;
2N/A}
2N/A
2N/Astatic KMF_RETURN
2N/AscanTag(char **pbp, char *endptr, char *tagBuf, int tagBufSize)
2N/A{
2N/A char *bp, *tagBufp;
2N/A int taglen;
2N/A
2N/A if (tagBufSize <= 0)
2N/A return (KMF_ERR_INTERNAL);
2N/A
2N/A /* skip optional leading space */
2N/A skipSpace(pbp, endptr);
2N/A if (*pbp == endptr) {
2N/A /* nothing left */
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A
2N/A /* fill tagBuf */
2N/A taglen = 0;
2N/A bp = *pbp;
2N/A tagBufp = tagBuf;
2N/A while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) {
2N/A if (++taglen >= tagBufSize) {
2N/A *pbp = bp;
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A *tagBufp++ = *bp++;
2N/A }
2N/A /* null-terminate tagBuf -- guaranteed at least one space left */
2N/A *tagBufp++ = 0;
2N/A *pbp = bp;
2N/A
2N/A /*
2N/A * skip trailing spaces till we hit something - should be
2N/A * an equal sign
2N/A */
2N/A skipSpace(pbp, endptr);
2N/A if (*pbp == endptr) {
2N/A /* nothing left */
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A if (**pbp != C_EQUAL) {
2N/A /* should be an equal sign */
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A /* skip over the equal sign */
2N/A (*pbp)++;
2N/A
2N/A return (KMF_OK);
2N/A}
2N/A
2N/Astatic KMF_RETURN
2N/AscanVal(char **pbp, char *endptr, char *valBuf, int valBufSize)
2N/A{
2N/A char *bp, *valBufp;
2N/A int vallen;
2N/A boolean_t isQuoted;
2N/A
2N/A if (valBufSize <= 0)
2N/A return (KMF_ERR_INTERNAL);
2N/A
2N/A /* skip optional leading space */
2N/A skipSpace(pbp, endptr);
2N/A if (*pbp == endptr) {
2N/A /* nothing left */
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A
2N/A bp = *pbp;
2N/A
2N/A /* quoted? */
2N/A if (*bp == C_DOUBLE_QUOTE) {
2N/A isQuoted = B_TRUE;
2N/A /* skip over it */
2N/A bp++;
2N/A } else {
2N/A isQuoted = B_FALSE;
2N/A }
2N/A
2N/A valBufp = valBuf;
2N/A vallen = 0;
2N/A while (bp < endptr) {
2N/A char c = *bp;
2N/A if (c == C_BACKSLASH) {
2N/A /* escape character */
2N/A bp++;
2N/A if (bp >= endptr) {
2N/A /*
2N/A * escape charater must appear with paired char
2N/A */
2N/A *pbp = bp;
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A } else if (!isQuoted && SPECIAL_CHAR(c)) {
2N/A /* unescaped special and not within quoted value */
2N/A break;
2N/A } else if (c == C_DOUBLE_QUOTE) {
2N/A /* reached unescaped double quote */
2N/A break;
2N/A }
2N/A /* append character */
2N/A vallen++;
2N/A if (vallen >= valBufSize) {
2N/A *pbp = bp;
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A *valBufp++ = *bp++;
2N/A }
2N/A
2N/A /* stip trailing spaces from unquoted values */
2N/A if (!isQuoted) {
2N/A if (valBufp > valBuf) {
2N/A valBufp--;
2N/A while ((valBufp > valBuf) && OPTIONAL_SPACE(*valBufp)) {
2N/A valBufp--;
2N/A }
2N/A valBufp++;
2N/A }
2N/A }
2N/A
2N/A if (isQuoted) {
2N/A /* insist that we stopped on a double quote */
2N/A if (*bp != C_DOUBLE_QUOTE) {
2N/A *pbp = bp;
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A /* skip over the quote and skip optional space */
2N/A bp++;
2N/A skipSpace(&bp, endptr);
2N/A }
2N/A
2N/A *pbp = bp;
2N/A
2N/A if (valBufp == valBuf) {
2N/A /* empty value -- not allowed */
2N/A return (KMF_ERR_RDN_PARSER);
2N/A }
2N/A
2N/A /* null-terminate valBuf -- guaranteed at least one space left */
2N/A *valBufp++ = 0;
2N/A
2N/A return (KMF_OK);
2N/A}
2N/A
2N/Astatic KMF_RETURN
2N/ACreateRDN(KMF_X509_TYPE_VALUE_PAIR *ava, KMF_X509_RDN *newrdn)
2N/A{
2N/A /* Each RDN has 1 AttrTypeAndValue */
2N/A (void) memset(newrdn, 0, sizeof (KMF_X509_RDN));
2N/A newrdn->numberOfPairs = 1;
2N/A newrdn->AttributeTypeAndValue = ava;
2N/A
2N/A return (KMF_OK);
2N/A}
2N/A
2N/Astatic KMF_RETURN
2N/Acopy_oid(KMF_OID *dst, KMF_OID *src)
2N/A{
2N/A KMF_RETURN ret = KMF_OK;
2N/A
2N/A if (dst == NULL || src == NULL)
2N/A return (KMF_ERR_BAD_PARAMETER);
2N/A
2N/A dst->Data = malloc(src->Length);
2N/A if (dst->Data == NULL)
2N/A return (KMF_ERR_MEMORY);
2N/A
2N/A dst->Length = src->Length;
2N/A (void) memcpy(dst->Data, src->Data, src->Length);
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/Astatic KMF_RETURN
2N/ACreateAVA(KMF_OID *oid, int valueType, char *value,
2N/A KMF_X509_TYPE_VALUE_PAIR **newava)
2N/A{
2N/A int rv = KMF_OK;
2N/A KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
2N/A
2N/A *newava = NULL;
2N/A ava = (KMF_X509_TYPE_VALUE_PAIR*) malloc(
2N/A sizeof (KMF_X509_TYPE_VALUE_PAIR));
2N/A if (ava == NULL) {
2N/A return (KMF_ERR_MEMORY);
2N/A } else {
2N/A (void) memset(ava, 0, sizeof (KMF_X509_TYPE_VALUE_PAIR));
2N/A ava->valueType = valueType;
2N/A ava->value.Data = malloc(strlen(value));
2N/A if (ava->value.Data == NULL) {
2N/A free(ava);
2N/A return (KMF_ERR_MEMORY);
2N/A }
2N/A (void) memcpy(ava->value.Data, value, strlen(value));
2N/A ava->value.Length = strlen(value);
2N/A
2N/A rv = copy_oid(&ava->type, oid);
2N/A if (rv != KMF_OK) {
2N/A /* Illegal AVA type */
2N/A free(ava->value.Data);
2N/A free(ava);
2N/A return (rv);
2N/A }
2N/A }
2N/A *newava = ava;
2N/A
2N/A return (rv);
2N/A}
2N/A
2N/Astatic KMF_RETURN
2N/AParseRdnAttribute(char **pbp, char *endptr, boolean_t singleAVA,
2N/A KMF_X509_TYPE_VALUE_PAIR **a)
2N/A{
2N/A KMF_RETURN rv;
2N/A const struct NameToKind *n2k;
2N/A int vt;
2N/A int valLen;
2N/A char *bp;
2N/A
2N/A char tagBuf[32];
2N/A char valBuf[384];
2N/A
2N/A rv = scanTag(pbp, endptr, tagBuf, sizeof (tagBuf));
2N/A if (rv != KMF_OK)
2N/A return (rv);
2N/A rv = scanVal(pbp, endptr, valBuf, sizeof (valBuf));
2N/A if (rv != KMF_OK)
2N/A return (rv);
2N/A
2N/A /* insist that if we haven't finished we've stopped on a separator */
2N/A bp = *pbp;
2N/A if (bp < endptr) {
2N/A if (singleAVA || (*bp != ',' && *bp != ';')) {
2N/A *pbp = bp;
2N/A return (KMF_ERR_RDN_ATTR);
2N/A }
2N/A /* ok, skip over separator */
2N/A bp++;
2N/A }
2N/A *pbp = bp;
2N/A
2N/A for (n2k = name2kinds; n2k->name; n2k++) {
2N/A if (strcasecmp(n2k->name, tagBuf) == 0) {
2N/A valLen = strlen(valBuf);
2N/A if (n2k->kind == OID_AVA_COUNTRY_NAME) {
2N/A vt = BER_PRINTABLE_STRING;
2N/A if (valLen != 2) {
2N/A return (KMF_ERR_RDN_ATTR);
2N/A }
2N/A if (!IsPrintable((unsigned char *) valBuf, 2)) {
2N/A return (KMF_ERR_RDN_ATTR);
2N/A }
2N/A } else if ((n2k->kind == OID_PKCS9_EMAIL_ADDRESS) ||
2N/A (n2k->kind == OID_RFC1274_MAIL)) {
2N/A vt = BER_IA5STRING;
2N/A } else {
2N/A /*
2N/A * Hack -- for rationale see X.520
2N/A * DirectoryString defn
2N/A */
2N/A if (IsPrintable((unsigned char *)valBuf,
2N/A valLen)) {
2N/A vt = BER_PRINTABLE_STRING;
2N/A } else if (Is7Bit((unsigned char *)valBuf,
2N/A valLen)) {
2N/A vt = BER_T61STRING;
2N/A }
2N/A }
2N/A rv = CreateAVA(n2k->OID, vt, (char *)valBuf, a);
2N/A return (rv);
2N/A }
2N/A }
2N/A /* matched no kind -- invalid tag */
2N/A return (KMF_ERR_RDN_ATTR);
2N/A}
2N/A
2N/Astatic int
2N/Ardnavcompare(const void *a, const void *b)
2N/A{
2N/A KMF_X509_RDN *r1, *r2;
2N/A KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
2N/A int i, p1, p2;
2N/A const struct NameToKind *n2k;
2N/A KMF_OID *oidrec;
2N/A
2N/A r1 = (KMF_X509_RDN *)a;
2N/A r2 = (KMF_X509_RDN *)b;
2N/A
2N/A av1 = r1->AttributeTypeAndValue;
2N/A av2 = r2->AttributeTypeAndValue;
2N/A
2N/A p1 = p2 = MAXINT;
2N/A /*
2N/A * The "Name2Kinds" list is ordered by significance.
2N/A * Compare the "ranking" of each of the OIDs to determine
2N/A * the result.
2N/A */
2N/A for (n2k = name2kinds, i = 0;
2N/A n2k->name && (p1 == MAXINT || p2 == MAXINT);
2N/A n2k++, i++) {
2N/A oidrec = n2k->OID;
2N/A if (oidrec != NULL) {
2N/A if (IsEqualOid(&av1->type, oidrec))
2N/A p1 = i;
2N/A if (IsEqualOid(&av2->type, oidrec))
2N/A p2 = i;
2N/A }
2N/A }
2N/A
2N/A if (p1 > p2)
2N/A return (-1);
2N/A else if (p1 < p2)
2N/A return (1);
2N/A else /* If equal, treat as if it is less than */
2N/A return (1);
2N/A}
2N/A
2N/Astatic KMF_RETURN
2N/AParseDistinguishedName(char *buf, int len, KMF_X509_NAME *name)
2N/A{
2N/A KMF_RETURN rv = KMF_OK;
2N/A char *bp, *e;
2N/A KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
2N/A KMF_X509_RDN rdn;
2N/A
2N/A (void) memset(name, 0, sizeof (KMF_X509_NAME));
2N/A e = buf + len;
2N/A bp = buf;
2N/A while (bp < e) {
2N/A rv = ParseRdnAttribute(&bp, e, B_FALSE, &ava);
2N/A if (rv != KMF_OK) goto loser;
2N/A rv = CreateRDN(ava, &rdn);
2N/A if (rv != KMF_OK) goto loser;
2N/A if (AddRDN(name, &rdn) != KMF_OK) goto loser;
2N/A skipSpace(&bp, e);
2N/A }
2N/A
2N/A /*
2N/A * Canonicalize the DN by sorting the elements
2N/A * in little-endian order, as per RFC 1485:
2N/A * "The name is presented/input in a little-endian
2N/A * order (most significant component last)."
2N/A */
2N/A qsort((void *)name->RelativeDistinguishedName,
2N/A name->numberOfRDNs, sizeof (KMF_X509_RDN), rdnavcompare);
2N/A
2N/A /* return result */
2N/A return (rv);
2N/A
2N/Aloser:
2N/A kmf_free_dn(name);
2N/A return (rv);
2N/A}
2N/A
2N/Astatic KMF_BOOL
2N/AIsEqualData(KMF_DATA *d1, KMF_DATA *d2)
2N/A{
2N/A return ((d1->Length == d2->Length) &&
2N/A !memcmp(d1->Data, d2->Data, d1->Length));
2N/A}
2N/A
2N/A/*
2N/A * Generic routine to compare 2 RDN structures.
2N/A *
2N/A * Because the ordering of the AV pairs may not be
2N/A * the same, we must compare each AV pair individually
2N/A *
2N/A * Return 0 if equal, 1 if not.
2N/A */
2N/Aint
2N/Akmf_compare_rdns(KMF_X509_NAME *name1, KMF_X509_NAME *name2)
2N/A{
2N/A int i, j;
2N/A boolean_t avfound;
2N/A KMF_X509_RDN *r1, *r2;
2N/A KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
2N/A
2N/A if (name1 == NULL || name2 == NULL)
2N/A return (1);
2N/A
2N/A if (name1->numberOfRDNs != name2->numberOfRDNs)
2N/A return (1);
2N/A
2N/A for (i = 0; i < name1->numberOfRDNs; i++) {
2N/A r1 = (KMF_X509_RDN *)&name1->RelativeDistinguishedName[i];
2N/A av1 = (KMF_X509_TYPE_VALUE_PAIR *)r1->AttributeTypeAndValue;
2N/A
2N/A avfound = FALSE;
2N/A for (j = 0; j < name2->numberOfRDNs && !avfound; j++) {
2N/A r2 = (KMF_X509_RDN *)
2N/A &name2->RelativeDistinguishedName[j];
2N/A av2 = (KMF_X509_TYPE_VALUE_PAIR *)
2N/A r2->AttributeTypeAndValue;
2N/A
2N/A avfound = (IsEqualOid(&av1->type, &av2->type) &&
2N/A IsEqualData(&av1->value, &av2->value));
2N/A }
2N/A /*
2N/A * If the current AV from name1 was not found in name2,
2N/A * we are done.
2N/A */
2N/A if (!avfound)
2N/A return (1);
2N/A }
2N/A
2N/A /* If we got this far, it must be a match */
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * kmf_dn_parser
2N/A *
2N/A * Public interface for parsing a Distinguished name in
2N/A * human-readable format into a binary KMF_X509_NAME.
2N/A */
2N/AKMF_RETURN
2N/Akmf_dn_parser(char *string, KMF_X509_NAME *name)
2N/A{
2N/A KMF_RETURN err;
2N/A
2N/A if (string == NULL || name == NULL)
2N/A return (KMF_ERR_BAD_PARAMETER);
2N/A
2N/A err = ParseDistinguishedName(string, (int)strlen(string), name);
2N/A return (err);
2N/A}