Exemple #1
0
/* Return a certificate object for the given fingerprint.  STRING is
   expected to be a SHA-1 fingerprint in standard hex notation with or
   without colons.  If no matching certificate is available in the
   cache NULL is returned.  The caller must release a returned
   certificate.  Note that although we are using reference counting
   the caller should not just compare the pointers to check for
   identical certificates. */
ksba_cert_t
get_cert_byhexfpr (const char *string)
{
  unsigned char fpr[20];
  const char *s;
  int i;

  if (strchr (string, ':'))
    {
      for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1);)
        {
          if (s[2] && s[2] != ':')
            break; /* Invalid string. */
          fpr[i++] = xtoi_2 (s);
          s += 2;
          if (i!= 20 && *s == ':')
            s++;
        }
    }
  else
    {
      for (s=string,i=0; i < 20 && hexdigitp (s) && hexdigitp(s+1); s+=2 )
        fpr[i++] = xtoi_2 (s);
    }
  if (i!=20 || *s)
    {
      log_error (_("invalid SHA1 fingerprint string '%s'\n"), string);
      return NULL;
    }

  return get_cert_byfpr (fpr);
}
Exemple #2
0
/* Get the manufacturer of the token.  */
void
slot_token_version (slot_iterator_t id, CK_BYTE *hw_major, CK_BYTE *hw_minor,
		    CK_BYTE *fw_major, CK_BYTE *fw_minor)
{
  struct slot *slot = scute_table_data (slots, id);

  /* slots_update() makes sure serialno is valid.  */
  *hw_major = xtoi_2 (slot->info.serialno + 12);
  *hw_minor = xtoi_2 (slot->info.serialno + 14);
  *fw_major = 0;
  *fw_minor = 0;
}
Exemple #3
0
/* Do the percent and plus/space unescaping from STRING to BUFFER and
   return the length of the valid buffer.  Plus unescaping is only
   done if WITHPLUS is true.  An escaped Nul character will be
   replaced by NULREPL.  */
static size_t
do_unescape (unsigned char *buffer, const unsigned char *string,
             int withplus, int nulrepl)
{
  unsigned char *p = buffer;

  while (*string)
    {
      if (*string == '%' && string[1] && string[2])
        {
          string++;
          *p = xtoi_2 (string);
          if (!*p)
            *p = nulrepl;
          string++;
        }
      else if (*string == '+' && withplus)
        *p = ' ';
      else
        *p = *string;
      p++;
      string++;
    }

  return (p - buffer);
}
Exemple #4
0
static size_t
do_unescape_inplace (char *string, int withplus, int nulrepl)
{
  unsigned char *p, *p0;

  p = p0 = string;
  while (*string)
    {
      if (*string == '%' && string[1] && string[2])
        {
          string++;
          *p = xtoi_2 (string);
          if (!*p)
            *p = nulrepl;
          string++;
        }
      else if (*string == '+' && withplus)
        *p = ' ';
      else
        *p = *string;
      p++;
      string++;
    }

  return (p - p0);
}
Exemple #5
0
/* Return a new malloced string by unescaping the string S.  Escaping
   is percent escaping and '+'/space mapping.  A binary Nul will
   silently be replaced by a 0xFF.  Function returns NULL to indicate
   an out of memory status.  PArsing stops at the end of the string or
   a white space character. */
static char *
unescape_passphrase_string (const unsigned char *s)
{
  char *buffer, *d;

  buffer = d = xtrymalloc_secure (strlen ((const char*)s)+1);
  if (!buffer)
    return NULL;
  while (*s && !spacep (s))
    {
      if (*s == '%' && s[1] && s[2])
        {
          s++;
          *d = xtoi_2 (s);
          if (!*d)
            *d = '\xff';
          d++;
          s += 2;
        }
      else if (*s == '+')
        {
          *d++ = ' ';
          s++;
        }
      else
        *d++ = *s++;
    }
  *d = 0;
  return buffer;
}
Exemple #6
0
/* Convert STRING consisting of hex characters into its binary representation
   and store that at BUFFER.  BUFFER needs to be of LENGTH bytes.  The
   function check that the STRING will convert exactly to LENGTH
   bytes. Colons between the hex digits are allowed, if one colon
   has been given a colon is expected very 2 characters. The string
   is delimited by either end of string or a white space character.
   The function returns -1 on error or the length of the parsed
   string.  */
int
hexcolon2bin (const char *string, void *buffer, size_t length)
{
  int i;
  const char *s = string;
  int need_colon = 0;

  for (i=0; i < length; )
    {
      if (i==1 && *s == ':')  /* Skip colons between hex digits.  */
        {
          need_colon = 1;
          s++;
        }
      else if (need_colon && *s == ':')
        s++;
      else if (need_colon)
        return -1;           /* Colon expected. */
      if (!hexdigitp (s) || !hexdigitp (s+1))
        return -1;           /* Invalid hex digits. */
      ((unsigned char*)buffer)[i++] = xtoi_2 (s);
      s += 2;
    }
  if (*s == ':')
    return -1;             /* Trailing colons are not allowed.  */
  if (*s && (!isascii (*s) || !isspace (*s)) )
    return -1;             /* Not followed by Nul or white space.  */
  if (i != length)
    return -1;             /* Not of expected length.  */
  if (*s)
    s++; /* Skip the delimiter. */
  return s - string;
}
/* Convert STRING consisting of hex characters into its binary
 representation and store that at BUFFER.  BUFFER needs to be of
 LENGTH bytes.  The function returns -1 on
 error or the length of the parsed string.  */
int convert_hexstring_to_char (const char *hex_string, size_t length, void *char_string)
{
    int i;
    const char *s = hex_string;
    char_string = malloc(length);
    char *p;
    p = malloc(length);
    for (i=0; i < length; )
    {
        if (!hexdigitp (s) || !hexdigitp (s+1)) return -1;           /* Invalid hex digits. */
        ((unsigned char*)char_string)[i++] = xtoi_2 (s);
        p[i++] = xtoi_2 (s);
        s += 2;
    }
    if (*s && (!isascii (*s) || !isspace (*s)) ) return -1;             /* Not followed by Nul or white space.  */
    if (i != length) return -1;             /* Not of expected length.  */
    if (*s) s++; /* Skip the delimiter. */
    return (int)(s - hex_string);
}
Exemple #8
0
/* This function also does deescaping for data lines.  */
gpg_error_t
assuan_client_read_response (assuan_context_t ctx,
			     char **line_r, int *linelen_r)
{
  gpg_error_t rc;
  char *line = NULL;
  int linelen = 0;

  *line_r = NULL;
  *linelen_r = 0;

  do 
    {
      do
	{
	  rc = _assuan_read_line (ctx);
	}
      while (_assuan_error_is_eagain (ctx, rc));
      if (rc)
        return rc;
      line = ctx->inbound.line;
      linelen = ctx->inbound.linelen;
    }    
  while (!linelen);

  /* For data lines, we deescape immediately.  The user will never
     have to worry about it.  */
  if (linelen >= 1 && line[0] == 'D' && line[1] == ' ')
    {
      char *s, *d;
      for (s=d=line; linelen; linelen--)
	{
	  if (*s == '%' && linelen > 2)
	    { /* handle escaping */
	      s++;
	      *d++ = xtoi_2 (s);
	      s += 2;
	      linelen -= 2;
	    }
	  else
	    *d++ = *s++;
	}
      *d = 0; /* add a hidden string terminator */

      linelen = d - line;
      ctx->inbound.linelen = linelen;
    }

  *line_r = line;
  *linelen_r = linelen;

  return 0;
}
Exemple #9
0
/* Get the manufacturer of the token.  */
char *
slot_token_manufacturer (slot_iterator_t id)
{
  struct slot *slot = scute_table_data (slots, id);
  unsigned int uval;

  /* slots_update() makes sure this is valid.  */
  uval = xtoi_2 (slot->info.serialno + 16) * 256
    + xtoi_2 (slot->info.serialno + 18);

  /* Note:  Make sure that there is no colon or linefeed in the string. */
  switch (uval)
    {
    case 0x0001:
      return "PPC Card Systems";

    case 0x0002:
      return "Prism";

    case 0x0003:
      return "OpenFortress";

    case 0x0004:
      return "Wewid AB";

    case 0x0005:
      return "ZeitControl";

    case 0x002A:
      return "Magrathea";

    case 0x0000:
    case 0xffff:
      return "test card";

    default: return (uval & 0xff00) == 0xff00? "unmanaged S/N range":"unknown";
    }

  /* Not reached.  */
}
Exemple #10
0
/* Take a 20 byte hexencoded string and put it into the the provided
   20 byte buffer FPR in binary format. */
static int
unhexify_fpr (const char *hexstr, unsigned char *fpr)
{
    const char *s;
    int n;

    for (s=hexstr, n=0; hexdigitp (s); s++, n++)
        ;
    if (*s || (n != 40))
        return 0; /* no fingerprint (invalid or wrong length). */
    for (s=hexstr, n=0; *s; s += 2, n++)
        fpr[n] = xtoi_2 (s);
    return 1; /* okay */
}
Exemple #11
0
/* Convert the hex encoded STRING back into binary and store the
   result into the provided buffer RESULT.  The actual size of that
   buffer will be returned.  The caller should provide RESULT of at
   least strlen(STRING)/2 bytes.  There is no error detection, the
   parsing stops at the first non hex character.  With RESULT given as
   NULL, the fucntion does only return the size of the buffer which
   would be needed.  */
size_t
unhexify (unsigned char *result, const char *string)
{
  const char *s;
  size_t n;

  for (s=string,n=0; hexdigitp (s) && hexdigitp(s+1); s += 2)
    {
      if (result)
        result[n] = xtoi_2 (s);
      n++;
    }
  return n;
}
Exemple #12
0
/* Note, that it is sufficient to allocate the target string D as
   long as the source string S, i.e.: strlen(s)+1; */
static void
strcpy_escaped (char *d, const char *s)
{
  while (*s)
    {
      if (*s == '%' && s[1] && s[2])
        { 
          s++;
          *d++ = xtoi_2 ( s);
          s += 2;
        }
      else
        *d++ = *s++;
    }
  *d = 0; 
}
Exemple #13
0
/* Convert STRING consisting of hex characters into its binary
   representation and return it as an allocated buffer. The valid
   length of the buffer is returned at R_LENGTH.  The string is
   delimited by end of string.  The function returns NULL on
   error.  */
static void *
hex2buffer (const char *string, size_t *r_length)
{
  const char *s;
  unsigned char *buffer;
  size_t length;

  buffer = xmalloc (strlen(string)/2+1);
  length = 0;
  for (s=string; *s; s +=2 )
    {
      if (!hexdigitp (s) || !hexdigitp (s+1))
        return NULL;           /* Invalid hex digits. */
      ((unsigned char*)buffer)[length++] = xtoi_2 (s);
    }
  *r_length = length;
  return buffer;
}
Exemple #14
0
/* Convert STRING consisting of hex characters into its binary
   representation and return it as an allocated buffer. The valid
   length of the buffer is returned at R_LENGTH.  The string is
   delimited by end of string.  The function returns NULL on
   error.  */
static void *
data_from_hex (const char *string, size_t *r_length)
{
  const char *s;
  unsigned char *buffer;
  size_t length;

  buffer = gcry_xmalloc (strlen(string)/2+1);
  length = 0;
  for (s=string; *s; s +=2 )
    {
      if (!hexdigitp (s) || !hexdigitp (s+1))
        die ("error parsing hex string `%s'\n", string);
      ((unsigned char*)buffer)[length++] = xtoi_2 (s);
    }
  *r_length = length;
  return buffer;
}
Exemple #15
0
/* Do an in-place percent unescaping of STRING. Returns STRING. Noet
   that this function does not do a '+'-to-space unescaping.*/
char *
unpercent_string (char *string)
{
  char *s = string;
  char *d = string;

  while (*s)
    {
      if (*s == '%' && s[1] && s[2])
        {
          s++;
          *d++ = xtoi_2 ( s);
          s += 2;
        }
      else
        *d++ = *s++;
    }
  *d = 0;
  return string;
}
Exemple #16
0
/* Convert HEXSTRING consisting of hex characters into string and
   store that at BUFFER.  HEXSTRING is either delimited by end of
   string or a white space character.  The function makes sure that
   the resulting string in BUFFER is terminated by a Nul byte.  Note
   that the retruned string may include embedded Nul bytes; the extra
   Nul byte at the end is used to make sure tha the result can always
   be used as a C-string.

   BUFSIZE is the available length of BUFFER; if the converted result
   plus a possible required extra Nul character does not fit into this
   buffer, the function returns NULL and won't change the existing
   content of BUFFER.  In-place conversion is possible as long as
   BUFFER points to HEXSTRING.

   If BUFFER is NULL and BUFSIZE is 0 the function scans HEXSTRING but
   does not store anything.  This may be used to find the end of
   HEXSTRING.

   On success the function returns a pointer to the next character
   after HEXSTRING (which is either end-of-string or a the next white
   space).  If BUFLEN is not NULL the number of valid vytes in BUFFER
   is stored there (an extra Nul byte is not counted); this will even
   be done if BUFFER has been passed as NULL. */
const char *
hex2str (const char *hexstring, char *buffer, size_t bufsize, size_t *buflen)
{
  const char *s = hexstring;
  int idx, count;
  int need_nul = 0;

  if (buflen)
    *buflen = 0;

  for (s=hexstring, count=0; hexdigitp (s) && hexdigitp (s+1); s += 2, count++)
    ;
  if (*s && (!isascii (*s) || !isspace (*s)) )
    {
      gpg_err_set_errno (EINVAL);
      return NULL;   /* Not followed by Nul or white space.  */
    }
  /* We need to append a nul character.  However we don't want that if
     the hexstring already ends with "00".  */
  need_nul = ((s == hexstring) || !(s[-2] == '0' && s[-1] == '0'));
  if (need_nul)
    count++;

  if (buffer)
    {
      if (count > bufsize)
        {
          gpg_err_set_errno (EINVAL);
          return NULL; /* Too long.  */
        }

      for (s=hexstring, idx=0; hexdigitp (s) && hexdigitp (s+1); s += 2)
        ((unsigned char*)buffer)[idx++] = xtoi_2 (s);
      if (need_nul)
        buffer[idx] = 0;
    }

  if (buflen)
    *buflen = count - need_nul;
  return s;
}
Exemple #17
0
/* Convert STRING consisting of hex characters into its binary
   representation and store that at BUFFER.  BUFFER needs to be of
   LENGTH bytes.  The function checks that the STRING will convert
   exactly to LENGTH bytes. The string is delimited by either end of
   string or a white space character.  The function returns -1 on
   error or the length of the parsed string.  */
int
hex2bin (const char *string, void *buffer, size_t length)
{
  int i;
  const char *s = string;

  for (i=0; i < length; )
    {
      if (!hexdigitp (s) || !hexdigitp (s+1))
        return -1;           /* Invalid hex digits. */
      ((unsigned char*)buffer)[i++] = xtoi_2 (s);
      s += 2;
    }
  if (*s && (!isascii (*s) || !isspace (*s)) )
    return -1;             /* Not followed by Nul or white space.  */
  if (i != length)
    return -1;             /* Not of expected length.  */
  if (*s)
    s++; /* Skip the delimiter. */
  return s - string;
}
Exemple #18
0
/****************
 * Remove all %xx escapes; this is done inplace.
 * Returns: New length of the string.
 */
static int
remove_percent_escapes (unsigned char *string)
{
  int n = 0;
  unsigned char *p, *s;

  for (p = s = string; *s; s++)
    {
      if (*s == '%')
        {
          if (s[1] && s[2] && hexdigitp (s+1) && hexdigitp (s+2))
            {
              s++;
              *p = xtoi_2 (s);
              s++;
              p++;
              n++;
            }
          else
            {
              *p++ = *s++;
              if (*s)
                *p++ = *s++;
              if (*s)
                *p++ = *s++;
              if (*s)
                *p = 0;
              return -1;   /* Bad URI. */
            }
        }
      else
        {
          *p++ = *s;
          n++;
        }
    }
  *p = 0;  /* Always keep a string terminator. */
  return n;
}
Exemple #19
0
/* Create a simple S-expression from the hex string at LINE.  Returns
   a newly allocated buffer with that canonical encoded S-expression
   or NULL in case of an error.  On return the number of characters
   scanned in LINE will be stored at NSCANNED.  This fucntions stops
   converting at the first character not representing a hexdigit. Odd
   numbers of hex digits are allowed; a leading zero is then
   assumed. If no characters have been found, NULL is returned.*/
unsigned char *
make_simple_sexp_from_hexstr (const char *line, size_t *nscanned)
{
    size_t n, len;
    const char *s;
    unsigned char *buf;
    unsigned char *p;
    char numbuf[50], *numbufp;
    size_t numbuflen;

    for (n=0, s=line; hexdigitp (s); s++, n++)
        ;
    if (nscanned)
        *nscanned = n;
    if (!n)
        return NULL;
    len = ((n+1) & ~0x01)/2;
    numbufp = smklen (numbuf, sizeof numbuf, len, &numbuflen);
    buf = xtrymalloc (1 + numbuflen + len + 1 + 1);
    if (!buf)
        return NULL;
    buf[0] = '(';
    p = (unsigned char *)stpcpy ((char *)buf+1, numbufp);
    s = line;
    if ((n&1))
    {
        *p++ = xtoi_1 (s);
        s++;
        n--;
    }
    for (; n > 1; n -=2, s += 2)
        *p++ = xtoi_2 (s);
    *p++ = ')';
    *p = 0; /* (Not really neaded.) */

    return buf;
}
Exemple #20
0
/* helper for the rfc2253 string parser */
static const unsigned char *
parse_dn_part (struct dn_array_s *array, const unsigned char *string)
{
  static struct {
    const char *label;
    const char *oid;
  } label_map[] = {
    /* Warning: When adding new labels, make sure that the buffer
       below we be allocated large enough. */
    {"EMail",        "1.2.840.113549.1.9.1" },
    {"T",            "2.5.4.12" },
    {"GN",           "2.5.4.42" },
    {"SN",           "2.5.4.4" },
    {"NameDistinguisher", "0.2.262.1.10.7.20"},
    {"ADDR",         "2.5.4.16" },
    {"BC",           "2.5.4.15" },
    {"D",            "2.5.4.13" },
    {"PostalCode",   "2.5.4.17" },
    {"Pseudo",       "2.5.4.65" },
    {"SerialNumber", "2.5.4.5" },
    {NULL, NULL}
  };
  const unsigned char *s, *s1;
  size_t n;
  char *p;
  int i;

  /* Parse attributeType */
  for (s = string+1; *s && *s != '='; s++)
    ;
  if (!*s)
    return NULL; /* error */
  n = s - string;
  if (!n)
    return NULL; /* empty key */

  /* We need to allocate a few bytes more due to the possible mapping
     from the shorter OID to the longer label. */
  array->key = p = xtrymalloc (n+10);
  if (!array->key)
    return NULL;
  memcpy (p, string, n);
  p[n] = 0;
  trim_trailing_spaces (p);

  if (digitp (p))
    {
      for (i=0; label_map[i].label; i++ )
        if ( !strcmp (p, label_map[i].oid) )
          {
            strcpy (p, label_map[i].label);
            break;
          }
    }
  string = s + 1;

  if (*string == '#')
    { /* hexstring */
      string++;
      for (s=string; hexdigitp (s); s++)
        s++;
      n = s - string;
      if (!n || (n & 1))
        return NULL; /* Empty or odd number of digits. */
      n /= 2;
      array->value = p = xtrymalloc (n+1);
      if (!p)
        return NULL;
      for (s1=string; n; s1 += 2, n--, p++)
        {
          *(unsigned char *)p = xtoi_2 (s1);
          if (!*p)
            *p = 0x01; /* Better print a wrong value than truncating
                          the string. */
        }
      *p = 0;
   }
  else
    { /* regular v3 quoted string */
      for (n=0, s=string; *s; s++)
        {
          if (*s == '\\')
            { /* pair */
              s++;
              if (*s == ',' || *s == '=' || *s == '+'
                  || *s == '<' || *s == '>' || *s == '#' || *s == ';'
                  || *s == '\\' || *s == '\"' || *s == ' ')
                n++;
              else if (hexdigitp (s) && hexdigitp (s+1))
                {
                  s++;
                  n++;
                }
              else
                return NULL; /* invalid escape sequence */
            }
          else if (*s == '\"')
            return NULL; /* invalid encoding */
          else if (*s == ',' || *s == '=' || *s == '+'
                   || *s == '<' || *s == '>' || *s == ';' )
            break;
          else
            n++;
        }

      array->value = p = xtrymalloc (n+1);
      if (!p)
        return NULL;
      for (s=string; n; s++, n--)
        {
          if (*s == '\\')
            {
              s++;
              if (hexdigitp (s))
                {
                  *(unsigned char *)p++ = xtoi_2 (s);
                  s++;
                }
              else
                *p++ = *s;
            }
          else
            *p++ = *s;
        }
      *p = 0;
    }
  return s;
}
Exemple #21
0
/* Ask the gpg-agent for a passphrase and present the user with a
   DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text.
   If a CACHEID is not NULL it is used to locate the passphrase in in
   the cache and store it under this ID.  If OPT_CHECK is true
   gpg-agent is asked to apply some checks on the passphrase security.
   If ERRORCODE is not NULL it should point a variable receiving an
   errorcode; this error code might be 0 if the user canceled the
   operation.  The function returns NULL to indicate an error.  */
char *
simple_pwquery (const char *cacheid, 
                const char *tryagain,
                const char *prompt,
                const char *description,
                int opt_check,
                int *errorcode)
{
  int fd = -1;
  int nread;
  char *result = NULL;
  char *pw = NULL;
  char *p;
  int rc, i; 

  rc = agent_open (&fd);
  if (rc)
    goto leave;

  if (!cacheid)
    cacheid = "X";
  if (!tryagain)
    tryagain = "X";
  if (!prompt)
    prompt = "X";
  if (!description)
    description = "X";

  {
    char *line;
    /* We allocate 3 times the needed space so that there is enough
       space for escaping. */
    line = spwq_malloc (15 + 10
                        + 3*strlen (cacheid) + 1
                        + 3*strlen (tryagain) + 1
                        + 3*strlen (prompt) + 1
                        + 3*strlen (description) + 1
                        + 2);
    if (!line)
      {
        rc = SPWQ_OUT_OF_CORE;
        goto leave;
      }
    strcpy (line, "GET_PASSPHRASE ");
    p = line+15;
    if (opt_check)
      p = stpcpy (p, "--check ");
    p = copy_and_escape (p, cacheid);
    *p++ = ' ';
    p = copy_and_escape (p, tryagain);
    *p++ = ' ';
    p = copy_and_escape (p, prompt);
    *p++ = ' ';
    p = copy_and_escape (p, description);
    *p++ = '\n';
    rc = writen (fd, line, p - line);
    spwq_free (line);
    if (rc)
      goto leave;
  }

  /* get response */
  pw = spwq_secure_malloc (500);
  nread = readline (fd, pw, 499);
  if (nread < 0)
    {
      rc = -nread;
      goto leave;
    }
  if (nread < 3)
    {
      rc = SPWQ_PROTOCOL_ERROR;
      goto leave;
    }
      
  if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') 
    { /* we got a passphrase - convert it back from hex */
      size_t pwlen = 0;
      
      for (i=3; i < nread && hexdigitp (pw+i); i+=2)
        pw[pwlen++] = xtoi_2 (pw+i);
      pw[pwlen] = 0; /* make a C String */
      result = pw;
      pw = NULL;
    }
  else if ((nread > 7 && !memcmp (pw, "ERR 111", 7)
            && (pw[7] == ' ' || pw[7] == '\n') )
           || ((nread > 4 && !memcmp (pw, "ERR ", 4)
                && (strtoul (pw+4, NULL, 0) & 0xffff) == 99)) ) 
    {
      /* 111 is the old Assuan code for canceled which might still
         be in use by old installations. 99 is GPG_ERR_CANCELED as
         used by modern gpg-agents; 0xffff is used to mask out the
         error source.  */
#ifdef SPWQ_USE_LOGGING
      log_info (_("canceled by user\n") );
#endif
      *errorcode = 0; /* Special error code to indicate Cancel. */
    }
  else if (nread > 4 && !memcmp (pw, "ERR ", 4))
    {
      switch ( (strtoul (pw+4, NULL, 0) & 0xffff) )
        {
        case 85: rc = SPWQ_NO_PIN_ENTRY;  break;
        default: rc = SPWQ_GENERAL_ERROR; break;
        }
    }
  else 
    {
#ifdef SPWQ_USE_LOGGING
      log_error (_("problem with the agent\n"));
#endif
      rc = SPWQ_ERR_RESPONSE;
    }
        
 leave:
  if (errorcode)
    *errorcode = rc;
  if (fd != -1)
    close (fd);
  if (pw)
    spwq_secure_free (pw);
  return result;
}
Exemple #22
0
int
_assuan_inquire_ext_cb (assuan_context_t ctx)
{
  int rc;
  unsigned char *line;
  int linelen;
  struct membuf *mb;
  unsigned char *p;

  line = (unsigned char *) ctx->inbound.line;
  linelen = ctx->inbound.linelen;
  mb = ctx->inquire_membuf;

  if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
    {
      rc = _assuan_error (ASSUAN_Canceled);
      goto leave;
    }
  if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
      && (!line[3] || line[3] == ' '))
    {
      rc = 0;
      goto leave;
    }

  if (line[0] != 'D' || line[1] != ' ' || mb == NULL)
    {
      rc = _assuan_error (ASSUAN_Unexpected_Command);
      goto leave;
    }
  
  if (linelen < 3)
    return 0;
  line += 2;
  linelen -= 2;
  
  p = line;
  while (linelen)
    {
      for (;linelen && *p != '%'; linelen--, p++)
	;
      put_membuf (mb, line, p-line);
      if (linelen > 2)
	{ /* handle escaping */
	  unsigned char tmp[1];
	  p++;
	  *tmp = xtoi_2 (p);
	  p += 2;
	  linelen -= 3;
	  put_membuf (mb, tmp, 1);
	}
      line = p;
    }
  if (mb->too_large)
    {
      rc = _assuan_error (ASSUAN_Too_Much_Data);
      goto leave;
    }

  return 0;

 leave:
  {
    size_t buf_len = 0;
    unsigned char *buf = NULL;

    if (mb)
      {
	buf = get_membuf (mb, &buf_len);
	if (!buf)
	  rc = _assuan_error (ASSUAN_Out_Of_Core);
	free_membuf (mb);
	free (mb);
	ctx->inquire_membuf = NULL;
      }
    ctx->in_inquire = 0;
    rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len);
  }
  return rc;
}
Exemple #23
0
/**
 * assuan_inquire:
 * @ctx: An assuan context
 * @keyword: The keyword used for the inquire
 * @r_buffer: Returns an allocated buffer
 * @r_length: Returns the length of this buffer
 * @maxlen: If not 0, the size limit of the inquired data.
 * 
 * A Server may use this to Send an inquire.  r_buffer, r_length and
 * maxlen may all be NULL/0 to indicate that no real data is expected.
 * 
 * Return value: 0 on success or an ASSUAN error code
 **/
assuan_error_t
assuan_inquire (assuan_context_t ctx, const char *keyword,
                unsigned char **r_buffer, size_t *r_length, size_t maxlen)
{
  assuan_error_t rc;
  struct membuf mb;
  char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */
  unsigned char *line, *p;
  int linelen;
  int nodataexpected;

  if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf)))
    return _assuan_error (ASSUAN_Invalid_Value);
  nodataexpected = !r_buffer && !r_length && !maxlen;
  if (!nodataexpected && (!r_buffer || !r_length))
    return _assuan_error (ASSUAN_Invalid_Value);
  if (!ctx->is_server)
    return _assuan_error (ASSUAN_Not_A_Server);
  if (ctx->in_inquire)
    return _assuan_error (ASSUAN_Nested_Commands);
  
  ctx->in_inquire = 1;
  if (nodataexpected)
    memset (&mb, 0, sizeof mb); /* avoid compiler warnings */
  else
    init_membuf (&mb, maxlen? maxlen:1024, maxlen);

  strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword);
  rc = assuan_write_line (ctx, cmdbuf);
  if (rc)
    goto leave;

  for (;;)
    {
      do 
        {
	  do
	    rc = _assuan_read_line (ctx);
	  while (_assuan_error_is_eagain (rc));
          if (rc)
            goto leave;
          line = (unsigned char *) ctx->inbound.line;
          linelen = ctx->inbound.linelen;
        }    
      while (*line == '#' || !linelen);
      if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
          && (!line[3] || line[3] == ' '))
        break; /* END command received*/
      if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
        {
          rc = _assuan_error (ASSUAN_Canceled);
          goto leave;
        }
      if (line[0] != 'D' || line[1] != ' ' || nodataexpected)
        {
          rc = _assuan_error (ASSUAN_Unexpected_Command);
          goto leave;
        }
      if (linelen < 3)
        continue;
      line += 2;
      linelen -= 2;

      p = line;
      while (linelen)
        {
          for (;linelen && *p != '%'; linelen--, p++)
            ;
          put_membuf (&mb, line, p-line);
          if (linelen > 2)
            { /* handle escaping */
              unsigned char tmp[1];
              p++;
              *tmp = xtoi_2 (p);
              p += 2;
              linelen -= 3;
              put_membuf (&mb, tmp, 1);
            }
          line = p;
        }
      if (mb.too_large)
        {
          rc = _assuan_error (ASSUAN_Too_Much_Data);
          goto leave;
        }
    }

  if (!nodataexpected)
    {
      *r_buffer = get_membuf (&mb, r_length);
      if (!*r_buffer)
        rc = _assuan_error (ASSUAN_Out_Of_Core);
    }

 leave:
  if (!nodataexpected)
    free_membuf (&mb);
  ctx->in_inquire = 0;
  return rc;
}
gpg_error_t
_assuan_inquire_ext_cb (assuan_context_t ctx)
{
  gpg_error_t rc;
  unsigned char *line;
  int linelen;
  struct membuf *mb;
  unsigned char *p;

  line = (unsigned char *) ctx->inbound.line;
  linelen = ctx->inbound.linelen;
  mb = ctx->inquire_membuf;

  if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
    {
      rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED);
      goto leave;
    }
  if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
      && (!line[3] || line[3] == ' '))
    {
      rc = 0;
      goto leave;
    }

  if (line[0] != 'D' || line[1] != ' ' || mb == NULL)
    {
      rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD);
      goto leave;
    }
  
  if (linelen < 3)
    return 0;
  line += 2;
  linelen -= 2;
  
  p = line;
  while (linelen)
    {
      for (;linelen && *p != '%'; linelen--, p++)
	;
      put_membuf (ctx, mb, line, p-line);
      if (linelen > 2)
	{ /* handle escaping */
	  unsigned char tmp[1];
	  p++;
	  *tmp = xtoi_2 (p);
	  p += 2;
	  linelen -= 3;
	  put_membuf (ctx, mb, tmp, 1);
	}
      line = p;
    }
  if (mb->too_large)
    {
      rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA);
      goto leave;
    }

  return 0;

 leave:
  {
    size_t buf_len = 0;
    unsigned char *buf = NULL;
    
    if (mb)
      {
	buf = get_membuf (ctx, mb, &buf_len);
	if (!buf)
	  rc = _assuan_error (ctx, gpg_err_code_from_syserror ());
	free_membuf (ctx, mb);
	free (mb);
	ctx->inquire_membuf = NULL;
      }
    ctx->in_inquire = 0;
    rc = (ctx->inquire_cb) (ctx->inquire_cb_data, rc, buf, buf_len);
  }
  return rc;
}
/**
 * assuan_inquire:
 * @ctx: An assuan context
 * @keyword: The keyword used for the inquire
 * @r_buffer: Returns an allocated buffer
 * @r_length: Returns the length of this buffer
 * @maxlen: If not 0, the size limit of the inquired data.
 * 
 * A Server may use this to Send an inquire.  r_buffer, r_length and
 * maxlen may all be NULL/0 to indicate that no real data is expected.
 * 
 * Return value: 0 on success or an ASSUAN error code
 **/
gpg_error_t
assuan_inquire (assuan_context_t ctx, const char *keyword,
                unsigned char **r_buffer, size_t *r_length, size_t maxlen)
{
  gpg_error_t rc;
  struct membuf mb;
  char cmdbuf[LINELENGTH-10]; /* (10 = strlen ("INQUIRE ")+CR,LF) */
  unsigned char *line, *p;
  int linelen;
  int nodataexpected;

  if (!ctx || !keyword || (10 + strlen (keyword) >= sizeof (cmdbuf)))
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
  nodataexpected = !r_buffer && !r_length && !maxlen;
  if (!nodataexpected && (!r_buffer || !r_length))
    return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE);
  if (!ctx->is_server)
    return _assuan_error (ctx, GPG_ERR_ASS_NOT_A_SERVER);
  if (ctx->in_inquire)
    return _assuan_error (ctx, GPG_ERR_ASS_NESTED_COMMANDS);
  
  ctx->in_inquire = 1;
  if (nodataexpected)
    memset (&mb, 0, sizeof mb); /* avoid compiler warnings */
  else
    init_membuf (ctx, &mb, maxlen? maxlen:1024, maxlen);

  strcpy (stpcpy (cmdbuf, "INQUIRE "), keyword);
  rc = assuan_write_line (ctx, cmdbuf);
  if (rc)
    goto leave;

  for (;;)
    {
      do 
        {
	  do
	    rc = _assuan_read_line (ctx);
	  while (_assuan_error_is_eagain (ctx, rc));
          if (rc)
            goto leave;
          line = (unsigned char *) ctx->inbound.line;
          linelen = ctx->inbound.linelen;
        }    
      while (*line == '#' || !linelen);
      if (line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
          && (!line[3] || line[3] == ' '))
        break; /* END command received*/
      if (line[0] == 'C' && line[1] == 'A' && line[2] == 'N')
        {
          rc = _assuan_error (ctx, GPG_ERR_ASS_CANCELED);
          goto leave;
        }
      if (line[0] != 'D' || line[1] != ' ' || nodataexpected)
        {
          rc = _assuan_error (ctx, GPG_ERR_ASS_UNEXPECTED_CMD);
          goto leave;
        }
      if (linelen < 3)
        continue;
      line += 2;
      linelen -= 2;

      p = line;
      while (linelen)
        {
          for (;linelen && *p != '%'; linelen--, p++)
            ;
          put_membuf (ctx, &mb, line, p-line);
          if (linelen > 2)
            { /* handle escaping */
              unsigned char tmp[1];
              p++;
              *tmp = xtoi_2 (p);
              p += 2;
              linelen -= 3;
              put_membuf (ctx, &mb, tmp, 1);
            }
          line = p;
        }
      if (mb.too_large)
        {
          rc = _assuan_error (ctx, GPG_ERR_ASS_TOO_MUCH_DATA);
          goto leave;
        }
    }

  if (!nodataexpected)
    {
      *r_buffer = get_membuf (ctx, &mb, r_length);
      if (!*r_buffer)
	rc = _assuan_error (ctx, gpg_err_code_from_syserror ());
    }

 leave:
  if (!nodataexpected)
    free_membuf (ctx, &mb);
  ctx->in_inquire = 0;
  return rc;
}
Exemple #26
0
/* Read the first PEM certificate from the file FNAME.  If fname is
   NULL the next certificate is read from stdin.  The certificate is
   returned in an alloced buffer whose address will be returned in
   RBUF and its length in RBUFLEN.  */
static gpg_error_t
read_pem_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
{
  FILE *fp;
  int c;
  int pos;
  int value;
  unsigned char *buf;
  size_t bufsize, buflen;
  enum {
    s_init, s_idle, s_lfseen, s_begin,
    s_b64_0, s_b64_1, s_b64_2, s_b64_3,
    s_waitend
  } state = s_init;

  fp = fname? fopen (fname, "r") : stdin;
  if (!fp)
    return gpg_error_from_errno (errno);

  pos = 0;
  value = 0;
  bufsize = 8192;
  buf = xmalloc (bufsize);
  buflen = 0;
  while ((c=getc (fp)) != EOF)
    {
      int escaped_c = 0;

      if (opt.escaped_pem)
        {
          if (c == '%')
            {
              char tmp[2];
              if ((c = getc(fp)) == EOF)
                break;
              tmp[0] = c;
              if ((c = getc(fp)) == EOF)
                break;
              tmp[1] = c;
              if (!hexdigitp (tmp) || !hexdigitp (tmp+1))
                {
                  log_error ("invalid percent escape sequence\n");
                  state = s_idle; /* Force an error. */
                  /* Skip to end of line.  */
                  while ( (c=getc (fp)) != EOF && c != '\n')
                    ;
                  goto ready;
                }
              c = xtoi_2 (tmp);
              escaped_c = 1;
            }
          else if (c == '\n')
            goto ready; /* Ready.  */
        }
      switch (state)
        {
        case s_idle:
          if (c == '\n')
            {
              state = s_lfseen;
              pos = 0;
            }
          break;
        case s_init:
          state = s_lfseen;
        case s_lfseen:
          if (c != "-----BEGIN "[pos])
            state = s_idle;
          else if (pos == 10)
            state = s_begin;
          else
            pos++;
          break;
        case s_begin:
          if (c == '\n')
            state = s_b64_0;
          break;
        case s_b64_0:
        case s_b64_1:
        case s_b64_2:
        case s_b64_3:
          {
            if (buflen >= bufsize)
              {
                bufsize += 8192;
                buf = xrealloc (buf, bufsize);
              }

            if (c == '-')
              state = s_waitend;
            else if ((c = asctobin[c & 0xff]) == 255 )
              ; /* Just skip invalid base64 characters. */
            else if (state == s_b64_0)
              {
                value = c << 2;
                state = s_b64_1;
              }
            else if (state == s_b64_1)
              {
                value |= (c>>4)&3;
                buf[buflen++] = value;
                value = (c<<4)&0xf0;
                state = s_b64_2;
              }
            else if (state == s_b64_2)
              {
                value |= (c>>2)&15;
                buf[buflen++] = value;
                value = (c<<6)&0xc0;
                state = s_b64_3;
              }
            else
              {
Exemple #27
0
/* Unquote STRING of LENGTH and store it into BUF.  The surrounding
   quotes are must already be removed from STRING.  We assume that the
   quoted string is syntacillay correct.  */
static size_t
unquote_string (const char *string, size_t length, unsigned char *buf)
{
  int esc = 0;
  const unsigned char *s = (const unsigned char*)string;
  unsigned char *d = buf;
  size_t n = length;

  for (; n; n--, s++)
    {
      if (esc)
        {
          switch (*s)
            {
            case 'b':  *d++ = '\b'; break;
            case 't':  *d++ = '\t'; break;
            case 'v':  *d++ = '\v'; break;
            case 'n':  *d++ = '\n'; break;
            case 'f':  *d++ = '\f'; break;
            case 'r':  *d++ = '\r'; break;
            case '"':  *d++ = '\"'; break;
            case '\'': *d++ = '\''; break;
            case '\\': *d++ = '\\'; break;

            case '\r':  /* ignore CR[,LF] */
              if (n>1 && s[1] == '\n')
                {
                  s++; n--;
                }
              break;

            case '\n':  /* ignore LF[,CR] */
              if (n>1 && s[1] == '\r')
                {
                  s++; n--;
                }
              break;

            case 'x': /* hex value */
              if (n>2 && hexdigitp (s+1) && hexdigitp (s+2))
                {
                  s++; n--;
                  *d++ = xtoi_2 (s);
                  s++; n--;
                }
              break;

            default:
              if (n>2 && octdigitp (s) && octdigitp (s+1) && octdigitp (s+2))
                {
                  *d++ = (atoi_1 (s)*64) + (atoi_1 (s+1)*8) + atoi_1 (s+2);
                  s += 2;
                  n -= 2;
                }
              break;
	    }
          esc = 0;
        }
      else if( *s == '\\' )
        esc = 1;
      else
        *d++ = *s;
    }

  return d - buf;
}
Exemple #28
0
/* Parse a DN and return an array-ized one.  This is not a validating
   parser and it does not support any old-stylish syntax; gpgme is
   expected to return only rfc2253 compatible strings. */
static const unsigned char *
parse_dn_part (DnPair *array, const unsigned char *string)
{
  const unsigned char *s, *s1;
  size_t n;
  char *p;

  /* parse attributeType */
  for (s = string+1; *s && *s != '='; s++)
    ;
  if (!*s)
    return NULL; /* error */
  n = s - string;
  if (!n)
    return NULL; /* empty key */
  p = (char*)malloc (n+1);
  
  
  memcpy (p, string, n);
  p[n] = 0;
  trim_trailing_spaces ((char*)p);
  // map OIDs to their names:
  for ( unsigned int i = 0 ; i < numOidMaps ; ++i )
    if ( !strcasecmp ((char*)p, oidmap[i].oid) ) {
      free( p );
      p = strdup( oidmap[i].name );
      break;
    }
  array->key = p;
  string = s + 1;

  if (*string == '#')
    { /* hexstring */
      string++;
      for (s=string; hexdigitp (s); s++)
        s++;
      n = s - string;
      if (!n || (n & 1))
        return NULL; /* empty or odd number of digits */
      n /= 2;
      array->value = p = (char*)malloc (n+1);
      
      
      for (s1=string; n; s1 += 2, n--)
        *p++ = xtoi_2 (s1);
      *p = 0;
   }
  else
    { /* regular v3 quoted string */
      for (n=0, s=string; *s; s++)
        {
          if (*s == '\\')
            { /* pair */
              s++;
              if (*s == ',' || *s == '=' || *s == '+'
                  || *s == '<' || *s == '>' || *s == '#' || *s == ';' 
                  || *s == '\\' || *s == '\"' || *s == ' ')
                n++;
              else if (hexdigitp (s) && hexdigitp (s+1))
                {
                  s++;
                  n++;
                }
              else
                return NULL; /* invalid escape sequence */
            }
          else if (*s == '\"')
            return NULL; /* invalid encoding */
          else if (*s == ',' || *s == '=' || *s == '+'
                   || *s == '<' || *s == '>' || *s == '#' || *s == ';' )
            break; 
          else
            n++;
        }

      array->value = p = (char*)malloc (n+1);
      
      
      for (s=string; n; s++, n--)
        {
          if (*s == '\\')
            { 
              s++;
              if (hexdigitp (s))
                {
                  *p++ = xtoi_2 (s);
                  s++;
                }
              else
                *p++ = *s;
            }
          else
            *p++ = *s;
        }
      *p = 0;
    }
  return s;
}
Exemple #29
0
static gpg_error_t
learn_status_cb (void *opaque, const char *line)
{
    struct agent_card_info_s *parm = opaque;
    const char *keyword = line;
    int keywordlen;
    int i;

    for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
        ;
    while (spacep (line))
        line++;

    if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
    {
        xfree (parm->serialno);
        parm->serialno = store_serialno (line);
        parm->is_v2 = (strlen (parm->serialno) >= 16
                       && xtoi_2 (parm->serialno+12) >= 2 );
    }
    else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen))
    {
        xfree (parm->apptype);
        parm->apptype = unescape_status_string (line);
    }
    else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
    {
        xfree (parm->disp_name);
        parm->disp_name = unescape_status_string (line);
    }
    else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen))
    {
        xfree (parm->disp_lang);
        parm->disp_lang = unescape_status_string (line);
    }
    else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen))
    {
        parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0;
    }
    else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen))
    {
        xfree (parm->pubkey_url);
        parm->pubkey_url = unescape_status_string (line);
    }
    else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen))
    {
        xfree (parm->login_data);
        parm->login_data = unescape_status_string (line);
    }
    else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen))
    {
        parm->sig_counter = strtoul (line, NULL, 0);
    }
    else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen))
    {
        char *p, *buf;

        buf = p = unescape_status_string (line);
        if (buf)
        {
            while (spacep (p))
                p++;
            parm->chv1_cached = atoi (p);
            while (*p && !spacep (p))
                p++;
            while (spacep (p))
                p++;
            for (i=0; *p && i < 3; i++)
            {
                parm->chvmaxlen[i] = atoi (p);
                while (*p && !spacep (p))
                    p++;
                while (spacep (p))
                    p++;
            }
            for (i=0; *p && i < 3; i++)
            {
                parm->chvretry[i] = atoi (p);
                while (*p && !spacep (p))
                    p++;
                while (spacep (p))
                    p++;
            }
            xfree (buf);
        }
    }
    else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen))
    {
        char *p, *p2, *buf;
        int abool;

        buf = p = unescape_status_string (line);
        if (buf)
        {
            for (p = strtok (buf, " "); p; p = strtok (NULL, " "))
            {
                p2 = strchr (p, '=');
                if (p2)
                {
                    *p2++ = 0;
                    abool = (*p2 == '1');
                    if (!strcmp (p, "ki"))
                        parm->extcap.ki = abool;
                    else if (!strcmp (p, "aac"))
                        parm->extcap.aac = abool;
                }
            }
            xfree (buf);
        }
    }
    else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
    {
        int no = atoi (line);
        while (*line && !spacep (line))
            line++;
        while (spacep (line))
            line++;
        if (no == 1)
            parm->fpr1valid = unhexify_fpr (line, parm->fpr1);
        else if (no == 2)
            parm->fpr2valid = unhexify_fpr (line, parm->fpr2);
        else if (no == 3)
            parm->fpr3valid = unhexify_fpr (line, parm->fpr3);
    }
    else if (keywordlen == 8 && !memcmp (keyword, "KEY-TIME", keywordlen))
    {
        int no = atoi (line);
        while (* line && !spacep (line))
            line++;
        while (spacep (line))
            line++;
        if (no == 1)
            parm->fpr1time = strtoul (line, NULL, 10);
        else if (no == 2)
            parm->fpr2time = strtoul (line, NULL, 10);
        else if (no == 3)
            parm->fpr3time = strtoul (line, NULL, 10);
    }
    else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen))
    {
        int no = atoi (line);
        while (*line && !spacep (line))
            line++;
        while (spacep (line))
            line++;
        if (no == 1)
            parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1);
        else if (no == 2)
            parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2);
        else if (no == 3)
            parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3);
    }
    else if (keywordlen == 8 && !memcmp (keyword, "KEY-ATTR", keywordlen))
    {
        int keyno, algo, nbits;

        sscanf (line, "%d %d %d", &keyno, &algo, &nbits);
        keyno--;
        if (keyno >= 0 && keyno < DIM (parm->key_attr))
        {
            parm->key_attr[keyno].algo = algo;
            parm->key_attr[keyno].nbits = nbits;
        }
    }

    return 0;
}
Exemple #30
0
/* Decode the C formatted string SRC and store the result in the
   buffer *DESTP which is LEN bytes long.  If LEN is zero, then a
   large enough buffer is allocated with malloc and *DESTP is set to
   the result.  Currently, LEN is only used to specify if allocation
   is desired or not, the caller is expected to make sure that *DESTP
   is large enough if LEN is not zero.  */
static gpg_error_t
decode_c_string (const char *src, char **destp, size_t len)
{
  char *dest;

  /* Set up the destination buffer.  */
  if (len)
    {
      if (len < strlen (src) + 1)
	return gpg_error (GPG_ERR_INTERNAL);

      dest = *destp;
    }
  else
    {
      /* The converted string will never be larger than the original
	 string.  */
      dest = malloc (strlen (src) + 1);
      if (!dest)
	return gpg_error_from_syserror ();

      *destp = dest;
    }

  /* Convert the string.  */
  while (*src)
    {
      if (*src != '\\')
	{
	  *(dest++) = *(src++);
	  continue;
	}

      switch (src[1])
	{
#define DECODE_ONE(match,result)	\
	case match:			\
	  src += 2;			\
	  *(dest++) = result;		\
	  break;

	  DECODE_ONE ('\'', '\'');
	  DECODE_ONE ('\"', '\"');
	  DECODE_ONE ('\?', '\?');
	  DECODE_ONE ('\\', '\\');
	  DECODE_ONE ('a', '\a');
	  DECODE_ONE ('b', '\b');
	  DECODE_ONE ('f', '\f');
	  DECODE_ONE ('n', '\n');
	  DECODE_ONE ('r', '\r');
	  DECODE_ONE ('t', '\t');
	  DECODE_ONE ('v', '\v');

	case 'x':
	  {
	    int val = xtoi_2 (&src[2]);

	    if (val == -1)
	      {
		/* Should not happen.  */
		*(dest++) = *(src++);
		*(dest++) = *(src++);
		if (*src)
		  *(dest++) = *(src++);
		if (*src)
		  *(dest++) = *(src++);
	      }
	    else
	      {
		if (!val)
		  {
		    /* A binary zero is not representable in a C
		       string.  */
		    *(dest++) = '\\';
		    *(dest++) = '0'; 
		  }
		else 
		  *((unsigned char *) dest++) = val;
		src += 4;
	      }
	  }
	  break;

	default:
	  {
	    /* Should not happen.  */
	    *(dest++) = *(src++);
	    *(dest++) = *(src++);
	  }
        } 
    }
  *(dest++) = 0;

  return 0;
}