![Marcus Brinkmann](/assets/img/avatar_default.png)
2003-06-06 Marcus Brinkmann <marcus@g10code.de> * gpgme.texi: Change error codes to GPG_ERR_* variants. (Error Handling): Rewritten. gpgme/ 2003-06-05 Marcus Brinkmann <marcus@g10code.de> Everywhere: Use libgpg-error error codes. * Makefile.am (EXTRA_DIST): Remove mkerrors. (BUILT_SOURCES): Remove errors.c. (MOSTLYCLEANFILES): Likewise. (libgpgme_la_SOURCES): Likewise. Add error.c. (errors.c): Remove target. * mkerrors: File removed. * error.c: New file. * gpgme.h (gpgme_error_t): Change to type gpg_error_t. (gpgme_err_code_t, gpgme_err_source_t): New types. (gpgme_err_code, gpgme_err_source, gpgme_error, gpgme_err_make): New static inline functions. (gpgme_strsource, gpgme_err_code_from_errno, gpgme_err_code_to_errno, gpgme_err_make_from_errno, gpgme_error_from_errno): New prototypes. tests/ 2003-06-06 Marcus Brinkmann <marcus@g10code.de> Everywhere: Use libgpg-error error codes. * gpg/Makefile.am (noinst_HEADERS): New variable. * gpg/t-support.h: New file. * gpgsm/Makefile.am (noinst_HEADERS): New variable. * gpgsm/t-support.h: New file.
707 lines
14 KiB
C
707 lines
14 KiB
C
/* 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 <stdlib.h>
|
||
#include <string.h>
|
||
#include <assert.h>
|
||
#include <errno.h>
|
||
|
||
#include "util.h"
|
||
#include "ops.h"
|
||
#include "sema.h"
|
||
|
||
|
||
/* Protects all reference counters in keys. All other accesses to a
|
||
key are read only. */
|
||
DEFINE_STATIC_LOCK (key_ref_lock);
|
||
|
||
|
||
/* Create a new key. */
|
||
gpgme_error_t
|
||
_gpgme_key_new (gpgme_key_t *r_key)
|
||
{
|
||
gpgme_key_t key;
|
||
|
||
key = calloc (1, sizeof *key);
|
||
if (!key)
|
||
return gpg_error_from_errno (errno);
|
||
key->_refs = 1;
|
||
|
||
*r_key = key;
|
||
return 0;
|
||
}
|
||
|
||
|
||
gpgme_error_t
|
||
_gpgme_key_add_subkey (gpgme_key_t key, gpgme_subkey_t *r_subkey)
|
||
{
|
||
gpgme_subkey_t subkey;
|
||
|
||
subkey = calloc (1, sizeof *subkey);
|
||
if (!subkey)
|
||
return gpg_error_from_errno (errno);
|
||
subkey->keyid = subkey->_keyid;
|
||
subkey->_keyid[16] = '\0';
|
||
|
||
if (!key->subkeys)
|
||
key->subkeys = subkey;
|
||
if (key->_last_subkey)
|
||
key->_last_subkey->next = subkey;
|
||
key->_last_subkey = subkey;
|
||
|
||
*r_subkey = subkey;
|
||
return 0;
|
||
}
|
||
|
||
|
||
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 (char *src, char **name, char **email,
|
||
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 (char *src, char **name, char **email,
|
||
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;
|
||
}
|
||
|
||
|
||
/* Take a name from the --with-colon listing, remove certain escape
|
||
sequences sequences and put it into the list of UIDs. */
|
||
gpgme_error_t
|
||
_gpgme_key_append_name (gpgme_key_t key, char *src)
|
||
{
|
||
gpgme_user_id_t 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 gpg_error_from_errno (errno);
|
||
memset (uid, 0, sizeof *uid);
|
||
|
||
uid->uid = ((char *) uid) + sizeof (*uid);
|
||
dst = uid->uid;
|
||
_gpgme_decode_c_string (src, &dst, src_len + 1);
|
||
|
||
dst += src_len + 1;
|
||
if (key->protocol == GPGME_PROTOCOL_CMS)
|
||
parse_x509_user_id (uid->uid, &uid->name, &uid->email,
|
||
&uid->comment, dst);
|
||
else
|
||
parse_user_id (uid->uid, &uid->name, &uid->email,
|
||
&uid->comment, dst);
|
||
|
||
if (!key->uids)
|
||
key->uids = uid;
|
||
if (key->_last_uid)
|
||
key->_last_uid->next = uid;
|
||
key->_last_uid = uid;
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
gpgme_key_sig_t
|
||
_gpgme_key_add_sig (gpgme_key_t key, char *src)
|
||
{
|
||
int src_len = src ? strlen (src) : 0;
|
||
gpgme_user_id_t uid;
|
||
gpgme_key_sig_t sig;
|
||
|
||
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. */
|
||
sig = calloc (1, sizeof (*sig) + 2 * src_len + 3);
|
||
if (!sig)
|
||
return NULL;
|
||
sig->keyid = sig->_keyid;
|
||
sig->_keyid[16] = '\0';
|
||
sig->uid = ((char *) sig) + sizeof (*sig);
|
||
|
||
if (src)
|
||
{
|
||
char *dst = sig->uid;
|
||
_gpgme_decode_c_string (src, &dst, src_len + 1);
|
||
dst += src_len + 1;
|
||
if (key->protocol == GPGME_PROTOCOL_CMS)
|
||
parse_x509_user_id (sig->uid, &sig->name, &sig->email,
|
||
&sig->comment, dst);
|
||
else
|
||
parse_user_id (sig->uid, &sig->name, &sig->email,
|
||
&sig->comment, dst);
|
||
}
|
||
|
||
if (!uid->signatures)
|
||
uid->signatures = sig;
|
||
if (uid->_last_keysig)
|
||
uid->_last_keysig->next = sig;
|
||
uid->_last_keysig = sig;
|
||
|
||
return sig;
|
||
}
|
||
|
||
|
||
/* Acquire a reference to KEY. */
|
||
void
|
||
gpgme_key_ref (gpgme_key_t key)
|
||
{
|
||
LOCK (key_ref_lock);
|
||
key->_refs++;
|
||
UNLOCK (key_ref_lock);
|
||
}
|
||
|
||
|
||
/* gpgme_key_unref releases 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_unref (gpgme_key_t key)
|
||
{
|
||
gpgme_user_id_t uid;
|
||
gpgme_subkey_t subkey;
|
||
|
||
LOCK (key_ref_lock);
|
||
assert (key->_refs > 0);
|
||
if (--key->_refs)
|
||
{
|
||
UNLOCK (key_ref_lock);
|
||
return;
|
||
}
|
||
UNLOCK (key_ref_lock);
|
||
|
||
subkey = key->subkeys;
|
||
while (subkey)
|
||
{
|
||
gpgme_subkey_t next = subkey->next;
|
||
if (subkey->fpr)
|
||
free (subkey->fpr);
|
||
free (subkey);
|
||
subkey = next;
|
||
}
|
||
|
||
uid = key->uids;
|
||
while (uid)
|
||
{
|
||
gpgme_user_id_t next_uid = uid->next;
|
||
gpgme_key_sig_t keysig = uid->signatures;
|
||
|
||
while (keysig)
|
||
{
|
||
gpgme_key_sig_t next = keysig->next;
|
||
free (keysig);
|
||
keysig = next;
|
||
}
|
||
free (uid);
|
||
uid = next_uid;
|
||
}
|
||
|
||
if (key->issuer_serial)
|
||
free (key->issuer_serial);
|
||
if (key->issuer_name)
|
||
free (key->issuer_name);
|
||
|
||
if (key->chain_id)
|
||
free (key->chain_id);
|
||
|
||
free (key);
|
||
}
|
||
|
||
|
||
/* Compatibility interfaces. */
|
||
|
||
void
|
||
gpgme_key_release (gpgme_key_t key)
|
||
{
|
||
gpgme_key_unref (key);
|
||
}
|
||
|
||
|
||
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 const char *
|
||
capabilities_to_string (gpgme_subkey_t subkey)
|
||
{
|
||
static const char *const strings[8] =
|
||
{
|
||
"",
|
||
"c",
|
||
"s",
|
||
"sc",
|
||
"e",
|
||
"ec",
|
||
"es",
|
||
"esc"
|
||
};
|
||
return strings[(!!subkey->can_encrypt << 2)
|
||
| (!!subkey->can_sign << 1)
|
||
| (!!subkey->can_certify)];
|
||
}
|
||
|
||
|
||
/* Return the value of the attribute WHAT of ITEM, which has to be
|
||
representable by a string. */
|
||
const char *
|
||
gpgme_key_get_string_attr (gpgme_key_t key, _gpgme_attr_t what,
|
||
const void *reserved, int idx)
|
||
{
|
||
gpgme_subkey_t subkey;
|
||
gpgme_user_id_t uid;
|
||
int i;
|
||
|
||
if (!key || reserved || idx < 0)
|
||
return NULL;
|
||
|
||
/* Select IDXth subkey. */
|
||
subkey = key->subkeys;
|
||
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->fpr : NULL;
|
||
|
||
case GPGME_ATTR_ALGO:
|
||
return subkey ? gpgme_pubkey_algo_name (subkey->pubkey_algo) : NULL;
|
||
|
||
case GPGME_ATTR_TYPE:
|
||
return key->protocol == GPGME_PROTOCOL_CMS ? "X.509" : "PGP";
|
||
|
||
case GPGME_ATTR_OTRUST:
|
||
return otrust_to_string (key->owner_trust);
|
||
|
||
case GPGME_ATTR_USERID:
|
||
return uid ? uid->uid : NULL;
|
||
|
||
case GPGME_ATTR_NAME:
|
||
return uid ? uid->name : NULL;
|
||
|
||
case GPGME_ATTR_EMAIL:
|
||
return uid ? uid->email : NULL;
|
||
|
||
case GPGME_ATTR_COMMENT:
|
||
return uid ? uid->comment : 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;
|
||
}
|
||
}
|
||
|
||
|
||
unsigned long
|
||
gpgme_key_get_ulong_attr (gpgme_key_t key, _gpgme_attr_t what,
|
||
const void *reserved, int idx)
|
||
{
|
||
gpgme_subkey_t subkey;
|
||
gpgme_user_id_t uid;
|
||
int i;
|
||
|
||
if (!key || reserved || idx < 0)
|
||
return 0;
|
||
|
||
/* Select IDXth subkey. */
|
||
subkey = key->subkeys;
|
||
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->pubkey_algo : 0;
|
||
|
||
case GPGME_ATTR_LEN:
|
||
return subkey ? (unsigned long) subkey->length : 0;
|
||
|
||
case GPGME_ATTR_TYPE:
|
||
return key->protocol == GPGME_PROTOCOL_CMS ? 1 : 0;
|
||
|
||
case GPGME_ATTR_CREATED:
|
||
return (subkey && subkey->timestamp >= 0)
|
||
? (unsigned long) subkey->timestamp : 0;
|
||
|
||
case GPGME_ATTR_EXPIRE:
|
||
return (subkey && subkey->expires >= 0)
|
||
? (unsigned long) subkey->expires : 0;
|
||
|
||
case GPGME_ATTR_VALIDITY:
|
||
return uid ? uid->validity : 0;
|
||
|
||
case GPGME_ATTR_OTRUST:
|
||
return key->owner_trust;
|
||
|
||
case GPGME_ATTR_IS_SECRET:
|
||
return !!key->secret;
|
||
|
||
case GPGME_ATTR_KEY_REVOKED:
|
||
return subkey ? subkey->revoked : 0;
|
||
|
||
case GPGME_ATTR_KEY_INVALID:
|
||
return subkey ? subkey->invalid : 0;
|
||
|
||
case GPGME_ATTR_KEY_EXPIRED:
|
||
return subkey ? subkey->expired : 0;
|
||
|
||
case GPGME_ATTR_KEY_DISABLED:
|
||
return subkey ? subkey->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->can_encrypt;
|
||
|
||
case GPGME_ATTR_CAN_SIGN:
|
||
return key->can_sign;
|
||
|
||
case GPGME_ATTR_CAN_CERTIFY:
|
||
return key->can_certify;
|
||
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
|
||
static gpgme_key_sig_t
|
||
get_keysig (gpgme_key_t key, int uid_idx, int idx)
|
||
{
|
||
gpgme_user_id_t uid;
|
||
gpgme_key_sig_t sig;
|
||
|
||
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;
|
||
|
||
sig = uid->signatures;
|
||
while (sig && idx > 0)
|
||
{
|
||
sig = sig->next;
|
||
idx--;
|
||
}
|
||
return sig;
|
||
}
|
||
|
||
|
||
const char *
|
||
gpgme_key_sig_get_string_attr (gpgme_key_t key, int uid_idx,
|
||
_gpgme_attr_t what,
|
||
const void *reserved, int idx)
|
||
{
|
||
gpgme_key_sig_t certsig = get_keysig (key, uid_idx, idx);
|
||
|
||
if (!certsig || reserved)
|
||
return NULL;
|
||
|
||
switch (what)
|
||
{
|
||
case GPGME_ATTR_KEYID:
|
||
return certsig->keyid;
|
||
|
||
case GPGME_ATTR_ALGO:
|
||
return gpgme_pubkey_algo_name (certsig->pubkey_algo);
|
||
|
||
case GPGME_ATTR_USERID:
|
||
return certsig->uid;
|
||
|
||
case GPGME_ATTR_NAME:
|
||
return certsig->name;
|
||
|
||
case GPGME_ATTR_EMAIL:
|
||
return certsig->email;
|
||
|
||
case GPGME_ATTR_COMMENT:
|
||
return certsig->comment;
|
||
|
||
default:
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
|
||
unsigned long
|
||
gpgme_key_sig_get_ulong_attr (gpgme_key_t key, int uid_idx, _gpgme_attr_t what,
|
||
const void *reserved, int idx)
|
||
{
|
||
gpgme_key_sig_t certsig = get_keysig (key, uid_idx, idx);
|
||
|
||
if (!certsig || reserved)
|
||
return 0;
|
||
|
||
switch (what)
|
||
{
|
||
case GPGME_ATTR_ALGO:
|
||
return (unsigned long) certsig->pubkey_algo;
|
||
|
||
case GPGME_ATTR_CREATED:
|
||
return certsig->timestamp < 0 ? 0L : (unsigned long) certsig->timestamp;
|
||
|
||
case GPGME_ATTR_EXPIRE:
|
||
return certsig->expires < 0 ? 0L : (unsigned long) certsig->expires;
|
||
|
||
case GPGME_ATTR_KEY_REVOKED:
|
||
return certsig->revoked;
|
||
|
||
case GPGME_ATTR_KEY_INVALID:
|
||
return certsig->invalid;
|
||
|
||
case GPGME_ATTR_KEY_EXPIRED:
|
||
return certsig->expired;
|
||
|
||
case GPGME_ATTR_SIG_CLASS:
|
||
return certsig->class;
|
||
|
||
case GPGME_ATTR_SIG_STATUS:
|
||
return certsig->status;
|
||
|
||
default:
|
||
return 0;
|
||
}
|
||
}
|