/* 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; }