/* name.c - Object to access GeneralNames etc. * Copyright (C) 2002, 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 #include "util.h" #include "asn1-func.h" #include "convert.h" #include "ber-help.h" struct ksba_name_s { int ref_count; int n_names; /* number of names */ char **names; /* array with the parsed names */ }; /* Also this is a public function it is not yet usable becuase we don't have a way to set real information. */ gpg_error_t ksba_name_new (ksba_name_t *r_name) { *r_name = xtrycalloc (1, sizeof **r_name); if (!*r_name) return gpg_error_from_errno (errno); (*r_name)->ref_count++; return 0; } void ksba_name_ref (ksba_name_t name) { if (!name) fprintf (stderr, "BUG: ksba_name_ref for NULL\n"); else ++name->ref_count; } void ksba_name_release (ksba_name_t name) { int i; if (!name) return; if (name->ref_count < 1) { fprintf (stderr, "BUG: trying to release an already released name\n"); return; } if (--name->ref_count) return; for (i=0; i < name->n_names; i++) xfree (name->names[i]); xfree (name->names); name->n_names = 0; xfree (name); } /* This is an internal function to create an ksba_name_t object from an DER encoded image which must point to an GeneralNames object */ gpg_error_t _ksba_name_new_from_der (ksba_name_t *r_name, const unsigned char *image, size_t imagelen) { gpg_error_t err; ksba_name_t name; struct tag_info ti; const unsigned char *der; size_t derlen; int n; char *p; if (!r_name || !image) return gpg_error (GPG_ERR_INV_VALUE); *r_name = NULL; /* Count and check for encoding errors - we won't do this again during the second pass */ der = image; derlen = imagelen; n = 0; while (derlen) { err = _ksba_ber_parse_tl (&der, &derlen, &ti); if (err) return err; if (ti.class != CLASS_CONTEXT) return gpg_error (GPG_ERR_INV_CERT_OBJ); /* we expected a tag */ if (ti.ndef) return gpg_error (GPG_ERR_NOT_DER_ENCODED); if (derlen < ti.length) return gpg_error (GPG_ERR_BAD_BER); switch (ti.tag) { case 1: /* rfc822Name - this is an imlicit IA5_STRING */ case 4: /* Name */ case 6: /* URI */ n++; break; default: break; } /* advance pointer */ der += ti.length; derlen -= ti.length; } /* allocate array and set all slots to NULL for easier error recovery */ err = ksba_name_new (&name); if (err) return err; if (!n) return 0; /* empty GeneralNames */ name->names = xtrycalloc (n, sizeof *name->names); if (!name->names) { ksba_name_release (name); return gpg_error (GPG_ERR_ENOMEM); } name->n_names = n; /* start the second pass */ der = image; derlen = imagelen; n = 0; while (derlen) { char numbuf[21]; err = _ksba_ber_parse_tl (&der, &derlen, &ti); assert (!err); switch (ti.tag) { case 1: /* rfc822Name - this is an imlicit IA5_STRING */ p = name->names[n] = xtrymalloc (ti.length+3); if (!p) { ksba_name_release (name); return gpg_error (GPG_ERR_ENOMEM); } *p++ = '<'; memcpy (p, der, ti.length); p += ti.length; *p++ = '>'; *p = 0; n++; break; case 4: /* Name */ err = _ksba_derdn_to_str (der, ti.length, &p); if (err) return err; /* FIXME: we need to release some of the memory */ name->names[n++] = p; break; case 6: /* URI */ sprintf (numbuf, "%u:", (unsigned int)ti.length); p = name->names[n] = xtrymalloc (1+5+strlen (numbuf) + ti.length +1+1); if (!p) { ksba_name_release (name); return gpg_error (GPG_ERR_ENOMEM); } p = stpcpy (p, "(3:uri"); p = stpcpy (p, numbuf); memcpy (p, der, ti.length); p += ti.length; *p++ = ')'; *p = 0; /* extra safeguard null */ n++; break; default: break; } /* advance pointer */ der += ti.length; derlen -= ti.length; } *r_name = name; return 0; } /* By iterating IDX up starting with 0, this function returns the all General Names stored in NAME. The format of the returned name is either a RFC-2253 formated one which can be detected by checking whether the first character is letter or a digit. RFC 2822 conform email addresses are returned enclosed in angle brackets, the opening angle bracket should be used to detect this. Other formats are returned as an S-Expression in canonical format, so a opening parenthesis may be used to detect this encoding, in this case the name may include binary null characters, so strlen might return a length shorter than actually used, the real length is implictly given by the structure of the S-Exp, an extra null is appended for safety reasons. One common format return is probably an Universal Resource Identifier which has the S-expression: "(uri )". The return string has the same lifetime as NAME. */ const char * ksba_name_enum (ksba_name_t name, int idx) { if (!name || idx < 0) return NULL; if (idx >= name->n_names) return NULL; /* end of list */ return name->names[idx]; } /* Convenience function to return names representing an URI. Caller must free the return value. Note that this function should not be used for enumeration */ char * ksba_name_get_uri (ksba_name_t name, int idx) { const char *s = ksba_name_enum (name, idx); int n; char *buf; if (!s || strncmp (s, "(3:uri", 6)) return NULL; /* we do only return URIs */ s += 6; for (n=0; *s && *s != ':' && digitp (s); s++) n = n*10 + atoi_1 (s); if (!n || *s != ':') return NULL; /* oops */ s++; buf = xtrymalloc (n+1); if (buf) { memcpy (buf, s, n); buf[n] = 0; } return buf; }