/* oid.c - Object identifier helper functions
* Copyright (C) 2001, 2009, 2012 g10 Code GmbH
*
* This file is part of KSBA.
*
* KSBA is free software; you can redistribute it and/or modify
* it under the terms of either
*
* - the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at
* your option) any later version.
*
* or
*
* - the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* or both in parallel, as here.
*
* KSBA is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
* License for more details.
*
* You should have received a copies of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, see .
*/
#include
#include
#include
#include
#include
#include "util.h"
#include "asn1-func.h"
#include "convert.h"
/**
* ksba_oid_to_str:
* @buffer: A BER encoded OID
* @length: The length of this OID
*
* Take a buffer with an object identifier in BER encoding and return
* a string representing this OID. We do not use the ASN.1 syntax
* here but delimit the arcs with dots, so it is easier to parse in
* most cases. This dotted-decimal notation is also known as LDAPOID
* string and described in RFC-2251.
*
* The function returns an empty string for an empty buffer and does
* no interpretation of the OID. The caller must free the returned
* string using ksba_free() or the function he has registered as a
* replacement.
*
*
* Return value: A allocated string or NULL in case of memory problem.
**/
char *
ksba_oid_to_str (const char *buffer, size_t length)
{
const unsigned char *buf = buffer;
char *string, *p;
int n = 0;
unsigned long val, valmask;
valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1));
/* To calculate the length of the string we can safely assume an
upper limit of 3 decimal characters per byte. Two extra bytes
account for the special first octect */
string = p = xtrymalloc (length*(1+3)+2+1);
if (!string)
return NULL;
if (!length)
{
*p = 0;
return string;
}
if (buf[0] < 40)
p += sprintf (p, "0.%d", buf[n]);
else if (buf[0] < 80)
p += sprintf (p, "1.%d", buf[n]-40);
else {
val = buf[n] & 0x7f;
while ( (buf[n]&0x80) && ++n < length )
{
if ( (val & valmask) )
goto badoid; /* Overflow. */
val <<= 7;
val |= buf[n] & 0x7f;
}
if (val < 80)
goto badoid;
val -= 80;
sprintf (p, "2.%lu", val);
p += strlen (p);
}
for (n++; n < length; n++)
{
val = buf[n] & 0x7f;
while ( (buf[n]&0x80) && ++n < length )
{
if ( (val & valmask) )
goto badoid; /* Overflow. */
val <<= 7;
val |= buf[n] & 0x7f;
}
sprintf (p, ".%lu", val);
p += strlen (p);
}
*p = 0;
return string;
badoid:
/* Return a special OID (gnu.gnupg.badoid) to indicate the error
case. The OID is broken and thus we return one which can't do
any harm. Formally this does not need to be a bad OID but an OID
with an arc that can't be represented in a 32 bit word is more
than likely corrupt. */
xfree (string);
return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973");
}
/* Take the OID at NODE and return it in string format */
char *
_ksba_oid_node_to_str (const unsigned char *image, AsnNode node)
{
if (!node || node->type != TYPE_OBJECT_ID || node->off == -1)
return NULL;
return ksba_oid_to_str (image+node->off + node->nhdr, node->len);
}
static size_t
make_flagged_int (unsigned long value, char *buf, size_t buflen)
{
int more = 0;
int shift;
/* fixme: figure out the number of bits in an ulong and start with
that value as shift (after making it a multiple of 7) a more
straigtforward implementation is to do it in reverse order using
a temporary buffer - saves a lot of compares */
for (more=0, shift=28; shift > 0; shift -= 7)
{
if (more || value >= (1<> shift);
value -= (value >> shift) << shift;
more = 1;
}
}
buf[buflen++] = value;
return buflen;
}
/**
* ksba_oid_from_str:
* @string: A string with the OID in dotted decimal form
* @rbuf: Returns the DER encoded OID
* @rlength: and its length
*
* Convertes the OID given in dotted decimal form to an DER encoding
* and returns it in allocated buffer rbuf and its length in rlength.
* rbuf is set to NULL in case of an error is returned.
* Scanning stops at the first white space.
* The caller must free the returned buffer using ksba_free() or the
* function he has registered as a replacement.
*
* Return value: 0 on success or an error value
**/
gpg_error_t
ksba_oid_from_str (const char *string, unsigned char **rbuf, size_t *rlength)
{
unsigned char *buf;
size_t buflen;
unsigned long val1, val;
const char *endp;
int arcno;
if (!string || !rbuf || !rlength)
return gpg_error (GPG_ERR_INV_VALUE);
*rbuf = NULL;
*rlength = 0;
/* we allow the OID to be prefixed with either "oid." or "OID." */
if ( !strncmp (string, "oid.", 4) || !strncmp (string, "OID.", 4))
string += 4;
if (!*string)
return gpg_error (GPG_ERR_INV_VALUE);
/* we can safely assume that the encoded OID is shorter than the string */
buf = xtrymalloc ( strlen(string) + 2);
if (!buf)
return gpg_error (GPG_ERR_ENOMEM);
buflen = 0;
val1 = 0; /* avoid compiler warnings */
arcno = 0;
do {
arcno++;
val = strtoul (string, (char**)&endp, 10);
if (!digitp (string) || !(*endp == '.' || !*endp))
{
xfree (buf);
return gpg_error (GPG_ERR_INV_OID_STRING);
}
if (*endp == '.')
string = endp+1;
if (arcno == 1)
{
if (val > 2)
break; /* not allowed, error catched below */
val1 = val;
}
else if (arcno == 2)
{ /* need to combine the first to arcs in one octet */
if (val1 < 2)
{
if (val > 39)
{
xfree (buf);
return gpg_error (GPG_ERR_INV_OID_STRING);
}
buf[buflen++] = val1*40 + val;
}
else
{
val += 80;
buflen = make_flagged_int (val, buf, buflen);
}
}
else
{
buflen = make_flagged_int (val, buf, buflen);
}
} while (*endp == '.');
if (arcno == 1)
{ /* it is not possible to encode only the first arc */
xfree (buf);
return gpg_error (GPG_ERR_INV_OID_STRING);
}
*rbuf = buf;
*rlength = buflen;
return 0;
}
/* Convert the string in BUFFER which is of length BUFLEN to its DER
encoding and returns it in a new allocated buffer RBUF and its
length in RLENGTH. RBUF is set to NULL if an error is returned.
The caller must free the returned buffer using ksba_free() or the
function he has registered as a replacement. */
gpg_error_t
_ksba_oid_from_buf (const void *buffer, size_t buflen,
unsigned char **rbuf, size_t *rlength)
{
gpg_error_t err;
char *string;
string = xtrymalloc (buflen+1);
if (!string)
{
*rbuf = NULL;
*rlength = 0;
return gpg_error_from_syserror ();
}
memcpy (string, buffer, buflen);
string[buflen] = 0;
err = ksba_oid_from_str (string, rbuf, rlength);
xfree (string);
return err;
}