#include <strings.h>
#include <stdlib.h>
#include <kmfapi.h>
#include <kmfapiP.h>
#include <ber_der.h>
#include <rdn_parser.h>
#include <stdio.h>
#include <values.h>
#include <libcustr.h>
static const struct NameToKind name2kinds[] = {
{ "CN", OID_AVA_COMMON_NAME, (KMF_OID *)&KMFOID_CommonName},
{ "SN", OID_AVA_SURNAME, (KMF_OID *)&KMFOID_Surname},
{ "GN", OID_AVA_GIVEN_NAME, (KMF_OID *)&KMFOID_GivenName},
{ "emailAddress", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
{ "E", OID_PKCS9_EMAIL_ADDRESS, (KMF_OID *)&KMFOID_EmailAddress},
{ "MAIL", OID_RFC1274_MAIL, (KMF_OID *)&KMFOID_RFC822mailbox},
{ "STREET", OID_AVA_STREET_ADDRESS, (KMF_OID *)&KMFOID_StreetAddress},
{ "UID", OID_RFC1274_UID, (KMF_OID *)&KMFOID_userid},
{ "OU", OID_AVA_ORGANIZATIONAL_UNIT_NAME,
(KMF_OID *)&KMFOID_OrganizationalUnitName},
{ "O", OID_AVA_ORGANIZATION_NAME, (KMF_OID *)&KMFOID_OrganizationName},
{ "L", OID_AVA_LOCALITY, (KMF_OID *)&KMFOID_LocalityName},
{ "ST", OID_AVA_STATE_OR_PROVINCE,
(KMF_OID *)&KMFOID_StateProvinceName},
{ "C", OID_AVA_COUNTRY_NAME, (KMF_OID *)&KMFOID_CountryName},
{ "DC", OID_AVA_DC, (KMF_OID *)&KMFOID_domainComponent},
{ 0, OID_UNKNOWN, NULL}
};
static KMF_BOOL
IsPrintable(unsigned char *data, unsigned len)
{
unsigned char ch, *end;
end = data + len;
while (data < end) {
ch = *data++;
if (!IS_PRINTABLE(ch)) {
return (B_FALSE);
}
}
return (B_TRUE);
}
static KMF_BOOL
Is7Bit(unsigned char *data, unsigned len)
{
unsigned char ch, *end;
end = data + len;
while (data < end) {
ch = *data++;
if ((ch & 0x80)) {
return (B_FALSE);
}
}
return (B_TRUE);
}
static void
skipSpace(char **pbp, char *endptr)
{
char *bp = *pbp;
while (bp < endptr && OPTIONAL_SPACE(*bp)) {
bp++;
}
*pbp = bp;
}
static KMF_RETURN
scanTag(char **pbp, char *endptr, char *tagBuf, int tagBufSize)
{
char *bp, *tagBufp;
int taglen;
if (tagBufSize <= 0)
return (KMF_ERR_INTERNAL);
skipSpace(pbp, endptr);
if (*pbp == endptr) {
return (KMF_ERR_RDN_PARSER);
}
taglen = 0;
bp = *pbp;
tagBufp = tagBuf;
while (bp < endptr && !OPTIONAL_SPACE(*bp) && (*bp != C_EQUAL)) {
if (++taglen >= tagBufSize) {
*pbp = bp;
return (KMF_ERR_RDN_PARSER);
}
*tagBufp++ = *bp++;
}
*tagBufp++ = 0;
*pbp = bp;
skipSpace(pbp, endptr);
if (*pbp == endptr) {
return (KMF_ERR_RDN_PARSER);
}
if (**pbp != C_EQUAL) {
return (KMF_ERR_RDN_PARSER);
}
(*pbp)++;
return (KMF_OK);
}
static KMF_RETURN
scanVal(char **pbp, char *endptr, char *valBuf, int valBufSize)
{
char *bp, *valBufp;
int vallen;
boolean_t isQuoted;
if (valBufSize <= 0)
return (KMF_ERR_INTERNAL);
skipSpace(pbp, endptr);
if (*pbp == endptr) {
return (KMF_ERR_RDN_PARSER);
}
bp = *pbp;
if (*bp == C_DOUBLE_QUOTE) {
isQuoted = B_TRUE;
bp++;
} else {
isQuoted = B_FALSE;
}
valBufp = valBuf;
vallen = 0;
while (bp < endptr) {
char c = *bp;
if (c == C_BACKSLASH) {
bp++;
if (bp >= endptr) {
*pbp = bp;
return (KMF_ERR_RDN_PARSER);
}
} else if (!isQuoted && SPECIAL_CHAR(c)) {
break;
} else if (c == C_DOUBLE_QUOTE) {
break;
}
vallen++;
if (vallen >= valBufSize) {
*pbp = bp;
return (KMF_ERR_RDN_PARSER);
}
*valBufp++ = *bp++;
}
if (!isQuoted) {
if (valBufp > valBuf) {
valBufp--;
while ((valBufp > valBuf) && OPTIONAL_SPACE(*valBufp)) {
valBufp--;
}
valBufp++;
}
}
if (isQuoted) {
if (*bp != C_DOUBLE_QUOTE) {
*pbp = bp;
return (KMF_ERR_RDN_PARSER);
}
bp++;
skipSpace(&bp, endptr);
}
*pbp = bp;
if (valBufp == valBuf) {
return (KMF_ERR_RDN_PARSER);
}
*valBufp++ = 0;
return (KMF_OK);
}
static KMF_RETURN
CreateRDN(KMF_X509_TYPE_VALUE_PAIR *ava, KMF_X509_RDN *newrdn)
{
(void) memset(newrdn, 0, sizeof (KMF_X509_RDN));
newrdn->numberOfPairs = 1;
newrdn->AttributeTypeAndValue = ava;
return (KMF_OK);
}
static KMF_RETURN
copy_oid(KMF_OID *dst, KMF_OID *src)
{
KMF_RETURN ret = KMF_OK;
if (dst == NULL || src == NULL)
return (KMF_ERR_BAD_PARAMETER);
dst->Data = malloc(src->Length);
if (dst->Data == NULL)
return (KMF_ERR_MEMORY);
dst->Length = src->Length;
(void) memcpy(dst->Data, src->Data, src->Length);
return (ret);
}
static KMF_RETURN
CreateAVA(KMF_OID *oid, int valueType, char *value,
KMF_X509_TYPE_VALUE_PAIR **newava)
{
int rv = KMF_OK;
KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
*newava = NULL;
ava = (KMF_X509_TYPE_VALUE_PAIR*) malloc(
sizeof (KMF_X509_TYPE_VALUE_PAIR));
if (ava == NULL) {
return (KMF_ERR_MEMORY);
} else {
(void) memset(ava, 0, sizeof (KMF_X509_TYPE_VALUE_PAIR));
ava->valueType = valueType;
ava->value.Data = malloc(strlen(value));
if (ava->value.Data == NULL) {
free(ava);
return (KMF_ERR_MEMORY);
}
(void) memcpy(ava->value.Data, value, strlen(value));
ava->value.Length = strlen(value);
rv = copy_oid(&ava->type, oid);
if (rv != KMF_OK) {
free(ava->value.Data);
free(ava);
return (rv);
}
}
*newava = ava;
return (rv);
}
static KMF_RETURN
ParseRdnAttribute(char **pbp, char *endptr, boolean_t singleAVA,
KMF_X509_TYPE_VALUE_PAIR **a)
{
KMF_RETURN rv;
const struct NameToKind *n2k;
int vt;
int valLen;
char *bp;
char tagBuf[32];
char valBuf[384];
rv = scanTag(pbp, endptr, tagBuf, sizeof (tagBuf));
if (rv != KMF_OK)
return (rv);
rv = scanVal(pbp, endptr, valBuf, sizeof (valBuf));
if (rv != KMF_OK)
return (rv);
bp = *pbp;
if (bp < endptr) {
if (singleAVA || (*bp != ',' && *bp != ';')) {
*pbp = bp;
return (KMF_ERR_RDN_ATTR);
}
bp++;
}
*pbp = bp;
for (n2k = name2kinds; n2k->name; n2k++) {
if (strcasecmp(n2k->name, tagBuf) == 0) {
valLen = strlen(valBuf);
if (n2k->kind == OID_AVA_COUNTRY_NAME) {
vt = BER_PRINTABLE_STRING;
if (valLen != 2) {
return (KMF_ERR_RDN_ATTR);
}
if (!IsPrintable((unsigned char *) valBuf, 2)) {
return (KMF_ERR_RDN_ATTR);
}
} else if ((n2k->kind == OID_PKCS9_EMAIL_ADDRESS) ||
(n2k->kind == OID_RFC1274_MAIL)) {
vt = BER_IA5STRING;
} else {
if (IsPrintable((unsigned char *)valBuf,
valLen)) {
vt = BER_PRINTABLE_STRING;
} else if (Is7Bit((unsigned char *)valBuf,
valLen)) {
vt = BER_T61STRING;
}
}
rv = CreateAVA(n2k->OID, vt, (char *)valBuf, a);
return (rv);
}
}
return (KMF_ERR_RDN_ATTR);
}
static int
rdnavcompare(const void *a, const void *b)
{
KMF_X509_RDN *r1, *r2;
KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
int i, p1, p2;
const struct NameToKind *n2k;
KMF_OID *oidrec;
r1 = (KMF_X509_RDN *)a;
r2 = (KMF_X509_RDN *)b;
av1 = r1->AttributeTypeAndValue;
av2 = r2->AttributeTypeAndValue;
p1 = p2 = MAXINT;
for (n2k = name2kinds, i = 0;
n2k->name && (p1 == MAXINT || p2 == MAXINT);
n2k++, i++) {
oidrec = n2k->OID;
if (oidrec != NULL) {
if (IsEqualOid(&av1->type, oidrec))
p1 = i;
if (IsEqualOid(&av2->type, oidrec))
p2 = i;
}
}
if (p1 > p2)
return (-1);
else if (p1 < p2)
return (1);
else
return (1);
}
static KMF_RETURN
ParseDistinguishedName(char *buf, int len, KMF_X509_NAME *name)
{
KMF_RETURN rv = KMF_OK;
char *bp, *e;
KMF_X509_TYPE_VALUE_PAIR *ava = NULL;
KMF_X509_RDN rdn;
(void) memset(name, 0, sizeof (KMF_X509_NAME));
e = buf + len;
bp = buf;
while (bp < e) {
rv = ParseRdnAttribute(&bp, e, B_FALSE, &ava);
if (rv != KMF_OK) goto loser;
rv = CreateRDN(ava, &rdn);
if (rv != KMF_OK) goto loser;
if (AddRDN(name, &rdn) != KMF_OK) goto loser;
skipSpace(&bp, e);
}
qsort((void *)name->RelativeDistinguishedName,
name->numberOfRDNs, sizeof (KMF_X509_RDN), rdnavcompare);
return (rv);
loser:
kmf_free_dn(name);
return (rv);
}
static KMF_BOOL
IsEqualData(KMF_DATA *d1, KMF_DATA *d2)
{
return ((d1->Length == d2->Length) &&
!memcmp(d1->Data, d2->Data, d1->Length));
}
int
kmf_compare_rdns(KMF_X509_NAME *name1, KMF_X509_NAME *name2)
{
int i, j;
boolean_t avfound;
KMF_X509_RDN *r1, *r2;
KMF_X509_TYPE_VALUE_PAIR *av1, *av2;
if (name1 == NULL || name2 == NULL)
return (1);
if (name1->numberOfRDNs != name2->numberOfRDNs)
return (1);
for (i = 0; i < name1->numberOfRDNs; i++) {
r1 = (KMF_X509_RDN *)&name1->RelativeDistinguishedName[i];
av1 = (KMF_X509_TYPE_VALUE_PAIR *)r1->AttributeTypeAndValue;
avfound = FALSE;
for (j = 0; j < name2->numberOfRDNs && !avfound; j++) {
r2 = (KMF_X509_RDN *)
&name2->RelativeDistinguishedName[j];
av2 = (KMF_X509_TYPE_VALUE_PAIR *)
r2->AttributeTypeAndValue;
avfound = (IsEqualOid(&av1->type, &av2->type) &&
IsEqualData(&av1->value, &av2->value));
}
if (!avfound)
return (1);
}
return (0);
}
KMF_RETURN
kmf_dn_parser(char *string, KMF_X509_NAME *name)
{
KMF_RETURN err;
if (string == NULL || name == NULL)
return (KMF_ERR_BAD_PARAMETER);
err = ParseDistinguishedName(string, (int)strlen(string), name);
return (err);
}
static const char hexdigits[] = "0123456789abcdef";
static KMF_RETURN
binvalue_to_string(KMF_DATA *data, custr_t *str)
{
size_t i;
uchar_t c;
if (custr_appendc(str, '#') != 0)
return (KMF_ERR_MEMORY);
for (i = 0; i < data->Length; i++) {
c = data->Data[i];
if (custr_appendc(str, hexdigits[(c >> 4) & 0xf]) != 0 ||
custr_appendc(str, hexdigits[(c & 0xf)]) != 0) {
return (KMF_ERR_MEMORY);
}
}
return (KMF_OK);
}
static KMF_RETURN
value_to_string(KMF_DATA *data, custr_t *str)
{
size_t i;
uchar_t c;
for (i = 0; i < data->Length; i++) {
c = data->Data[i];
if (c < ' ' || c >= 0x7f) {
if (custr_append_printf(str, "\\%02hhX", c) != 0)
return (KMF_ERR_MEMORY);
continue;
}
switch (c) {
case '#':
if (i != 0)
break;
case ' ':
if (i != 0 && i + 1 != data->Length)
break;
case '"':
case '+':
case ',':
case ';':
case '<':
case '>':
case '\\':
if (custr_appendc(str, '\\') != 0)
return (KMF_ERR_MEMORY);
}
if (custr_appendc(str, c) != 0)
return (KMF_ERR_MEMORY);
}
return (KMF_OK);
}
static KMF_RETURN
ava_to_string(KMF_X509_TYPE_VALUE_PAIR *tvp, custr_t *str)
{
KMF_OID *kind_oid;
KMF_OID *rdn_oid = &tvp->type;
const char *attr = NULL;
size_t i;
KMF_RETURN ret = KMF_OK;
boolean_t found = B_FALSE;
for (i = 0; name2kinds[i].name != NULL; i++) {
kind_oid = name2kinds[i].OID;
if (!IsEqualOid(kind_oid, rdn_oid))
continue;
attr = name2kinds[i].name;
found = B_TRUE;
break;
}
if (!found && (attr = kmf_oid_to_string(rdn_oid)) == NULL) {
ret = KMF_ERR_MEMORY;
goto done;
}
if (custr_append(str, attr) != 0) {
ret = KMF_ERR_MEMORY;
goto done;
}
if (custr_appendc(str, '=') != 0) {
ret = KMF_ERR_MEMORY;
goto done;
}
switch (tvp->valueType) {
case BER_UTF8_STRING:
case BER_PRINTABLE_STRING:
case BER_T61STRING:
case BER_IA5STRING:
if (found) {
ret = value_to_string(&tvp->value, str);
break;
}
default:
ret = binvalue_to_string(&tvp->value, str);
break;
}
done:
if (!found)
free((void *)attr);
return (ret);
}
static KMF_RETURN
rdn_to_string(KMF_X509_RDN *rdn, custr_t *str)
{
KMF_RETURN ret;
size_t i;
for (i = 0; i < rdn->numberOfPairs; i++) {
if (i > 0 && custr_appendc(str, '+') != 0)
return (KMF_ERR_MEMORY);
ret = ava_to_string(&rdn->AttributeTypeAndValue[i], str);
if (ret != KMF_OK)
return (ret);
}
return (KMF_OK);
}
KMF_RETURN
kmf_dn_to_string(KMF_X509_NAME *name, char **string)
{
custr_t *str = NULL;
KMF_RETURN err = KMF_OK;
size_t i;
if (name == NULL || string == NULL)
return (KMF_ERR_BAD_PARAMETER);
*string = NULL;
if (custr_alloc(&str) != 0)
return (KMF_ERR_MEMORY);
for (i = 0; i < name->numberOfRDNs; i++) {
KMF_X509_RDN *rdn = &name->RelativeDistinguishedName[i];
if (i > 0 && custr_append(str, ", ") != 0) {
err = KMF_ERR_MEMORY;
goto done;
}
if ((err = rdn_to_string(rdn, str)) != KMF_OK)
goto done;
}
if ((*string = strdup(custr_cstr(str))) == NULL)
err = KMF_ERR_MEMORY;
done:
custr_free(str);
return (err);
}