gpgme/gpgme/key.c
Marcus Brinkmann 8c359e5416 2003-04-25 Marcus Brinkmann <marcus@g10code.de>
* key.c (gpgme_key_get_string_attr): Fix validity attribute.
2003-04-25 10:59:52 +00:00

970 lines
21 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* key.c - Key objects.
Copyright (C) 2000 Werner Koch (dd9jn)
Copyright (C) 2001, 2002, 2003 g10 Code GmbH
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of 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.
GPGME 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 copy of the GNU General Public License
along with GPGME; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ctype.h>
#include "util.h"
#include "ops.h"
#include "key.h"
#include "sema.h"
/* Protects all reference counters in keys. All other accesses to a
key are either read only or happen before the key is entered into
the cache. */
DEFINE_STATIC_LOCK (key_ref_lock);
static const char *
pkalgo_to_string (int algo)
{
switch (algo)
{
case 1:
case 2:
case 3:
return "RSA";
case 16:
case 20:
return "ElG";
case 17:
return "DSA";
default:
return "Unknown";
}
}
static const char *
otrust_to_string (int otrust)
{
switch (otrust)
{
case GPGME_VALIDITY_NEVER:
return "n";
case GPGME_VALIDITY_MARGINAL:
return "m";
case GPGME_VALIDITY_FULL:
return "f";
case GPGME_VALIDITY_ULTIMATE:
return "u";
default:
return "?";
}
}
static const char *
validity_to_string (int validity)
{
switch (validity)
{
case GPGME_VALIDITY_UNDEFINED:
return "q";
case GPGME_VALIDITY_NEVER:
return "n";
case GPGME_VALIDITY_MARGINAL:
return "m";
case GPGME_VALIDITY_FULL:
return "f";
case GPGME_VALIDITY_ULTIMATE:
return "u";
case GPGME_VALIDITY_UNKNOWN:
default:
return "?";
}
}
static GpgmeError
key_new (GpgmeKey *r_key, int secret)
{
GpgmeKey key;
*r_key = NULL;
key = calloc (1, sizeof *key);
if (!key)
return GPGME_Out_Of_Core;
key->ref_count = 1;
*r_key = key;
if (secret)
key->secret = 1;
return 0;
}
GpgmeError
_gpgme_key_new (GpgmeKey *r_key)
{
return key_new (r_key, 0);
}
GpgmeError
_gpgme_key_new_secret (GpgmeKey *r_key)
{
return key_new (r_key, 1);
}
/* Acquire a reference to KEY. */
void
gpgme_key_ref (GpgmeKey key)
{
if (!key)
return;
LOCK (key_ref_lock);
key->ref_count++;
UNLOCK (key_ref_lock);
}
static struct subkey_s *
add_subkey (GpgmeKey key, int secret)
{
struct subkey_s *k, *kk;
k = calloc (1, sizeof *k);
if (!k)
return NULL;
if (!(kk = key->keys.next))
key->keys.next = k;
else
{
while (kk->next)
kk = kk->next;
kk->next = k;
}
if (secret)
k->secret = 1;
return k;
}
struct subkey_s *
_gpgme_key_add_subkey (GpgmeKey key)
{
return add_subkey (key, 0);
}
struct subkey_s *
_gpgme_key_add_secret_subkey (GpgmeKey key)
{
return add_subkey (key, 1);
}
static char *
set_user_id_part (char *tail, const char *buf, size_t len)
{
while (len && (buf[len - 1] == ' ' || buf[len - 1] == '\t'))
len--;
for (; len; len--)
*tail++ = *buf++;
*tail++ = 0;
return tail;
}
static void
parse_user_id (const char *src, const char **name, const char **email,
const char **comment, char *tail)
{
const char *start = NULL;
int in_name = 0;
int in_email = 0;
int in_comment = 0;
while (*src)
{
if (in_email)
{
if (*src == '<')
/* Not legal but anyway. */
in_email++;
else if (*src == '>')
{
if (!--in_email && !*email)
{
*email = tail;
tail = set_user_id_part (tail, start, src - start);
}
}
}
else if (in_comment)
{
if (*src == '(')
in_comment++;
else if (*src == ')')
{
if (!--in_comment && !*comment)
{
*comment = tail;
tail = set_user_id_part (tail, start, src - start);
}
}
}
else if (*src == '<')
{
if (in_name)
{
if (!*name)
{
*name = tail;
tail = set_user_id_part (tail, start, src - start);
}
in_name = 0;
}
in_email = 1;
start = src + 1;
}
else if (*src == '(')
{
if (in_name)
{
if (!*name)
{
*name = tail;
tail = set_user_id_part (tail, start, src - start);
}
in_name = 0;
}
in_comment = 1;
start = src + 1;
}
else if (!in_name && *src != ' ' && *src != '\t')
{
in_name = 1;
start = src;
}
src++;
}
if (in_name)
{
if (!*name)
{
*name = tail;
tail = set_user_id_part (tail, start, src - start);
}
}
/* Let unused parts point to an EOS. */
tail--;
if (!*name)
*name = tail;
if (!*email)
*email = tail;
if (!*comment)
*comment = tail;
}
static void
parse_x509_user_id (const char *src, const char **name, const char **email,
const char **comment, char *tail)
{
if (*src == '<' && src[strlen (src) - 1] == '>')
*email = src;
/* Let unused parts point to an EOS. */
tail--;
if (!*name)
*name = tail;
if (!*email)
*email = tail;
if (!*comment)
*comment = tail;
}
struct certsig_s *
_gpgme_key_add_certsig (GpgmeKey key, char *src)
{
int src_len = src ? strlen (src) : 0;
struct user_id_s *uid;
struct certsig_s *certsig;
assert (key); /* XXX */
uid = key->last_uid;
assert (uid); /* XXX */
/* We can malloc a buffer of the same length, because the converted
string will never be larger. Actually we allocate it twice the
size, so that we are able to store the parsed stuff there too. */
certsig = calloc (1, sizeof (*certsig) + 2 * src_len + 3);
if (!certsig)
return NULL;
if (src)
{
char *dst = certsig->name;
_gpgme_decode_c_string (src, &dst, src_len + 1);
dst += src_len + 1;
if (key->x509)
parse_x509_user_id (src, &certsig->name_part, &certsig->email_part,
&certsig->comment_part, dst);
else
parse_user_id (src, &certsig->name_part, &certsig->email_part,
&certsig->comment_part, dst);
}
if (!uid->certsigs)
uid->certsigs = certsig;
if (uid->last_certsig)
uid->last_certsig->next = certsig;
uid->last_certsig = certsig;
return certsig;
}
/**
* gpgme_key_release:
* @key: Key Object or NULL
*
* Release the key object. Note, that this function may not do an
* actual release if there are other shallow copies of the objects.
* You have to call this function for every newly created key object
* as well as for every gpgme_key_ref() done on the key object.
**/
void
gpgme_key_release (GpgmeKey key)
{
struct certsig_s *c, *c2;
struct user_id_s *u, *u2;
struct subkey_s *k, *k2;
if (!key)
return;
LOCK (key_ref_lock);
assert (key->ref_count);
if (--key->ref_count)
{
UNLOCK (key_ref_lock);
return;
}
UNLOCK (key_ref_lock);
free (key->keys.fingerprint);
for (k = key->keys.next; k; k = k2)
{
k2 = k->next;
free (k->fingerprint);
free (k);
}
for (u = key->uids; u; u = u2)
{
u2 = u->next;
for (c = u->certsigs; c; c = c2)
{
c2 = c->next;
free (c);
}
free (u);
}
free (key->issuer_serial);
free (key->issuer_name);
free (key->chain_id);
free (key);
}
/**
* gpgme_key_unref:
* @key: Key Object
*
* This is an alias for gpgme_key_release().
**/
void
gpgme_key_unref (GpgmeKey key)
{
gpgme_key_release (key);
}
/* Take a name from the --with-colon listing, remove certain escape
sequences sequences and put it into the list of UIDs. */
GpgmeError
_gpgme_key_append_name (GpgmeKey key, const char *src)
{
struct user_id_s *uid;
char *dst;
int src_len = strlen (src);
assert (key);
/* We can malloc a buffer of the same length, because the converted
string will never be larger. Actually we allocate it twice the
size, so that we are able to store the parsed stuff there too. */
uid = malloc (sizeof (*uid) + 2 * src_len + 3);
if (!uid)
return GPGME_Out_Of_Core;
memset (uid, 0, sizeof *uid);
dst = uid->name;
_gpgme_decode_c_string (src, &dst, src_len + 1);
dst += src_len + 1;
if (key->x509)
parse_x509_user_id (src, &uid->name_part, &uid->email_part,
&uid->comment_part, dst);
else
parse_user_id (src, &uid->name_part, &uid->email_part,
&uid->comment_part, dst);
if (!key->uids)
key->uids = uid;
if (key->last_uid)
key->last_uid->next = uid;
key->last_uid = uid;
return 0;
}
static void
add_otag (GpgmeData d, const char *tag)
{
_gpgme_data_append_string (d, " <");
_gpgme_data_append_string (d, tag);
_gpgme_data_append_string (d, ">");
}
static void
add_ctag (GpgmeData d, const char *tag)
{
_gpgme_data_append_string (d, "</");
_gpgme_data_append_string (d, tag);
_gpgme_data_append_string (d, ">\n");
}
static void
add_tag_and_string (GpgmeData d, const char *tag, const char *string)
{
add_otag (d, tag);
_gpgme_data_append_string_for_xml (d, string);
add_ctag (d, tag);
}
static void
add_tag_and_uint (GpgmeData d, const char *tag, unsigned int val)
{
char buf[30];
sprintf (buf, "%u", val);
add_tag_and_string (d, tag, buf);
}
static void
add_tag_and_time (GpgmeData d, const char *tag, time_t val)
{
char buf[30];
if (!val || val == (time_t) - 1)
return;
sprintf (buf, "%lu", (unsigned long) val);
add_tag_and_string (d, tag, buf);
}
static void
one_certsig_as_xml (GpgmeData data, struct certsig_s *certsig)
{
_gpgme_data_append_string (data, " <signature>\n");
if (certsig->flags.invalid)
_gpgme_data_append_string (data, " <invalid/>\n");
if (certsig->flags.revoked)
_gpgme_data_append_string (data, " <revoked/>\n");
if (certsig->flags.expired)
_gpgme_data_append_string (data, " <expired/>\n");
add_tag_and_string (data, "keyid", certsig->keyid);
add_tag_and_uint (data, "algo", certsig->algo);
add_tag_and_time (data, "created", certsig->timestamp);
add_tag_and_time (data, "expire", certsig->expires_at);
if (*certsig->name)
add_tag_and_string (data, "raw", certsig->name);
if (*certsig->name_part)
add_tag_and_string (data, "name", certsig->name_part);
if (*certsig->email_part)
add_tag_and_string (data, "email", certsig->email_part);
if (*certsig->comment_part)
add_tag_and_string (data, "comment", certsig->comment_part);
_gpgme_data_append_string (data, " </signature>\n");
}
static void
one_uid_as_xml (GpgmeData data, struct user_id_s *uid)
{
struct certsig_s *certsig;
_gpgme_data_append_string (data, " <userid>\n");
if (uid->invalid)
_gpgme_data_append_string (data, " <invalid/>\n");
if (uid->revoked)
_gpgme_data_append_string (data, " <revoked/>\n");
add_tag_and_string (data, "raw", uid->name);
if (*uid->name_part)
add_tag_and_string (data, "name", uid->name_part);
if (*uid->email_part)
add_tag_and_string (data, "email", uid->email_part);
if (*uid->comment_part)
add_tag_and_string (data, "comment", uid->comment_part);
/* Now the signatures. */
for (certsig = uid->certsigs; certsig; certsig = certsig->next)
one_certsig_as_xml (data, certsig);
_gpgme_data_append_string (data, " </userid>\n");
}
/**
* gpgme_key_get_as_xml:
* @key: Key object
*
* Return the key object as an XML string. The classer has to free
* that string.
*
* Return value: An XML string or NULL in case of a memory problem or
* a NULL passed as @key
**/
char *
gpgme_key_get_as_xml (GpgmeKey key)
{
GpgmeData d;
struct user_id_s *u;
struct subkey_s *k;
if (!key)
return NULL;
if (gpgme_data_new (&d))
return NULL;
_gpgme_data_append_string (d, "<GnupgKeyblock>\n"
" <mainkey>\n");
if (key->keys.secret)
_gpgme_data_append_string (d, " <secret/>\n");
if (key->keys.flags.invalid)
_gpgme_data_append_string (d, " <invalid/>\n");
if (key->keys.flags.revoked)
_gpgme_data_append_string (d, " <revoked/>\n");
if (key->keys.flags.expired)
_gpgme_data_append_string (d, " <expired/>\n");
if (key->keys.flags.disabled)
_gpgme_data_append_string (d, " <disabled/>\n");
add_tag_and_string (d, "keyid", key->keys.keyid);
if (key->keys.fingerprint)
add_tag_and_string (d, "fpr", key->keys.fingerprint);
add_tag_and_uint (d, "algo", key->keys.key_algo);
add_tag_and_uint (d, "len", key->keys.key_len);
add_tag_and_time (d, "created", key->keys.timestamp);
add_tag_and_time (d, "expire", key->keys.expires_at);
add_tag_and_string (d, "otrust", otrust_to_string (key->otrust));
if (key->issuer_serial)
add_tag_and_string (d, "serial", key->issuer_serial);
if (key->issuer_name)
add_tag_and_string (d, "issuer", key->issuer_name);
if (key->chain_id)
add_tag_and_string (d, "chainid", key->chain_id);
_gpgme_data_append_string (d, " </mainkey>\n");
/* Now the user IDs. */
for (u = key->uids; u; u = u->next)
one_uid_as_xml (d,u);
/* And now the subkeys. */
for (k = key->keys.next; k; k = k->next)
{
_gpgme_data_append_string (d, " <subkey>\n");
if (k->secret)
_gpgme_data_append_string (d, " <secret/>\n");
if (k->flags.invalid)
_gpgme_data_append_string (d, " <invalid/>\n");
if (k->flags.revoked)
_gpgme_data_append_string (d, " <revoked/>\n");
if (k->flags.expired)
_gpgme_data_append_string (d, " <expired/>\n");
if (k->flags.disabled)
_gpgme_data_append_string (d, " <disabled/>\n");
add_tag_and_string (d, "keyid", k->keyid);
if (k->fingerprint)
add_tag_and_string (d, "fpr", k->fingerprint);
add_tag_and_uint (d, "algo", k->key_algo);
add_tag_and_uint (d, "len", k->key_len);
add_tag_and_time (d, "created", k->timestamp);
add_tag_and_time (d, "expire", k->expires_at);
_gpgme_data_append_string (d, " </subkey>\n");
}
_gpgme_data_append_string (d, "</GnupgKeyblock>\n");
return _gpgme_data_release_and_return_string (d);
}
static const char *
capabilities_to_string (struct subkey_s *k)
{
static const char *const strings[8] =
{
"",
"c",
"s",
"sc",
"e",
"ec",
"es",
"esc"
};
return strings[(!!k->flags.can_encrypt << 2)
| (!!k->flags.can_sign << 1)
| (!!k->flags.can_certify)];
}
/**
* gpgme_key_get_string_attr:
* @key: Key Object
* @what: Attribute specifier
* @reserved: Must be 0
* @idx: Index counter
*
* Return a attribute as specified by @what and @idx. Note that not
* all attributes can be returned as a string, in which case NULL is
* returned. @idx is used to iterate through attributes which do have
* more than one instance (e.g. user IDs or sub keys).
*
* Return value: NULL or an const string which is only valid as long
* as the key object itself is valid.
**/
const char *
gpgme_key_get_string_attr (GpgmeKey key, GpgmeAttr what,
const void *reserved, int idx)
{
struct subkey_s *subkey;
struct user_id_s *uid;
int i;
if (!key || reserved || idx < 0)
return NULL;
/* Select IDXth subkey. */
subkey = &key->keys;
for (i = 0; i < idx; i++)
{
subkey = subkey->next;
if (!subkey)
break;
}
/* Select the IDXth user ID. */
uid = key->uids;
for (i = 0; i < idx; i++)
{
uid = uid->next;
if (!uid)
break;
}
switch (what)
{
case GPGME_ATTR_KEYID:
return subkey ? subkey->keyid : NULL;
case GPGME_ATTR_FPR:
return subkey ? subkey->fingerprint : NULL;
case GPGME_ATTR_ALGO:
return subkey ? pkalgo_to_string (subkey->key_algo) : NULL;
case GPGME_ATTR_TYPE:
return key->x509 ? "X.509" : "PGP";
case GPGME_ATTR_OTRUST:
return otrust_to_string (key->otrust);
case GPGME_ATTR_USERID:
return uid ? uid->name : NULL;
case GPGME_ATTR_NAME:
return uid ? uid->name_part : NULL;
case GPGME_ATTR_EMAIL:
return uid ? uid->email_part : NULL;
case GPGME_ATTR_COMMENT:
return uid ? uid->comment_part : NULL;
case GPGME_ATTR_VALIDITY:
return uid ? validity_to_string (uid->validity) : NULL;
case GPGME_ATTR_KEY_CAPS:
return subkey ? capabilities_to_string (subkey) : NULL;
case GPGME_ATTR_SERIAL:
return key->issuer_serial;
case GPGME_ATTR_ISSUER:
return idx ? NULL : key->issuer_name;
case GPGME_ATTR_CHAINID:
return key->chain_id;
default:
return NULL;
}
}
/**
* gpgme_key_get_ulong_attr:
* @key:
* @what:
* @reserved:
* @idx:
*
* Return a attribute as specified by @what and @idx. Note that not
* all attributes can be returned as an integer, in which case 0 is
* returned. @idx is used to iterate through attributes which do have
* more than one instance (e.g. user IDs or sub keys).
*
* See gpgme.h for a list of attributes.
*
* Return value: 0 or the requested value.
**/
unsigned long
gpgme_key_get_ulong_attr (GpgmeKey key, GpgmeAttr what,
const void *reserved, int idx)
{
struct subkey_s *subkey;
struct user_id_s *uid;
int i;
if (!key || reserved || idx < 0)
return 0;
/* Select IDXth subkey. */
subkey = &key->keys;
for (i = 0; i < idx; i++)
{
subkey = subkey->next;
if (!subkey)
break;
}
/* Select the IDXth user ID. */
uid = key->uids;
for (i = 0; i < idx; i++)
{
uid = uid->next;
if (!uid)
break;
}
switch (what)
{
case GPGME_ATTR_ALGO:
return subkey ? (unsigned long) subkey->key_algo : 0;
case GPGME_ATTR_LEN:
return subkey ? (unsigned long) subkey->key_len : 0;
case GPGME_ATTR_TYPE:
return key->x509 ? 1 : 0;
case GPGME_ATTR_CREATED:
return (subkey && subkey->timestamp >= 0)
? (unsigned long) subkey->timestamp : 0;
case GPGME_ATTR_EXPIRE:
return (subkey && subkey->expires_at >= 0)
? (unsigned long) subkey->expires_at : 0;
case GPGME_ATTR_VALIDITY:
return uid ? uid->validity : 0;
case GPGME_ATTR_OTRUST:
return key->otrust;
case GPGME_ATTR_IS_SECRET:
return !!key->secret;
case GPGME_ATTR_KEY_REVOKED:
return subkey ? subkey->flags.revoked : 0;
case GPGME_ATTR_KEY_INVALID:
return subkey ? subkey->flags.invalid : 0;
case GPGME_ATTR_KEY_EXPIRED:
return subkey ? subkey->flags.expired : 0;
case GPGME_ATTR_KEY_DISABLED:
return subkey ? subkey->flags.disabled : 0;
case GPGME_ATTR_UID_REVOKED:
return uid ? uid->revoked : 0;
case GPGME_ATTR_UID_INVALID:
return uid ? uid->invalid : 0;
case GPGME_ATTR_CAN_ENCRYPT:
return key->gloflags.can_encrypt;
case GPGME_ATTR_CAN_SIGN:
return key->gloflags.can_sign;
case GPGME_ATTR_CAN_CERTIFY:
return key->gloflags.can_certify;
default:
return 0;
}
}
static struct certsig_s *
get_certsig (GpgmeKey key, int uid_idx, int idx)
{
struct user_id_s *uid;
struct certsig_s *certsig;
if (!key || uid_idx < 0 || idx < 0)
return NULL;
uid = key->uids;
while (uid && uid_idx > 0)
{
uid = uid->next;
uid_idx--;
}
if (!uid)
return NULL;
certsig = uid->certsigs;
while (certsig && idx > 0)
{
certsig = certsig->next;
idx--;
}
return certsig;
}
const char *
gpgme_key_sig_get_string_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
const void *reserved, int idx)
{
struct certsig_s *certsig = get_certsig (key, uid_idx, idx);
if (!certsig || reserved)
return NULL;
switch (what)
{
case GPGME_ATTR_KEYID:
return certsig->keyid;
case GPGME_ATTR_ALGO:
return pkalgo_to_string (certsig->algo);
case GPGME_ATTR_USERID:
return certsig->name;
case GPGME_ATTR_NAME:
return certsig->name_part;
case GPGME_ATTR_EMAIL:
return certsig->email_part;
case GPGME_ATTR_COMMENT:
return certsig->comment_part;
default:
return NULL;
}
}
unsigned long
gpgme_key_sig_get_ulong_attr (GpgmeKey key, int uid_idx, GpgmeAttr what,
const void *reserved, int idx)
{
struct certsig_s *certsig = get_certsig (key, uid_idx, idx);
if (!certsig || reserved)
return 0;
switch (what)
{
case GPGME_ATTR_ALGO:
return (unsigned long) certsig->algo;
case GPGME_ATTR_CREATED:
return certsig->timestamp < 0 ? 0L : (unsigned long) certsig->timestamp;
case GPGME_ATTR_EXPIRE:
return certsig->expires_at < 0 ? 0L : (unsigned long) certsig->expires_at;
case GPGME_ATTR_KEY_REVOKED:
return certsig->flags.revoked;
case GPGME_ATTR_KEY_INVALID:
return certsig->flags.invalid;
case GPGME_ATTR_KEY_EXPIRED:
return certsig->flags.expired;
case GPGME_ATTR_SIG_CLASS:
return certsig->sig_class;
case GPGME_ATTR_SIG_STATUS:
return certsig->sig_stat;
default:
return 0;
}
}