mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
915297705a
* common/util.h (UBID_LEN): New. Use it at all places. * kbx/keybox-blob.c (create_blob_finish): Do not write the UBID item. * kbx/keybox-dump.c (print_ubib): Remove. (_keybox_dump_blob): Do not print the now removed ubid flag. * kbx/keybox-search-desc.h (struct keydb_search_desc): Use constants for the size of the ubid and grip. * kbx/keybox-search.c (blob_cmp_ubid): New. (has_ubid): Make it a simple wrapper around blob_cmp_ubid. (keybox_get_data): Add arg 'r_ubid'. * kbx/frontend.h (enum kbxd_store_modes): New. * kbx/kbxserver.c (cmd_store): Add new option --insert. * kbx/backend-cache.c (be_cache_initialize): New. (be_cache_add_resource): Call it here. * kbx/backend-kbx.c (be_kbx_seek): Remove args 'fpr' and 'fprlen'. (be_kbx_search): Get the UBID from keybox_get_data. * kbx/backend-support.c (be_fingerprint_from_blob): Replace by ... (be_ubid_from_blob): new. Change all callers. * kbx/frontend.c (kbxd_add_resource): Temporary disable the cache but use the new cache init function. (kbxd_store): Replace arg 'only_update' by 'mode'. Seek using the ubid. Take care of the mode. -- It turned out that using the hash of the entire blob was not helpful. Thus we redefine the Unique-Blob-ID (UBID) as the primary fingerprint of the blob. In case this is a v5 OpenPGP key a left truncated version of the SHA-256 hash is used; in all other cases the full SHA-1 hash. Using a SHA-256 hash does not make sense because v4 keys are and will for some time be the majority of keys and thus padding them with zeroes won't make any difference. Even if fingerprint collisions can eventually be created we will assume that the keys are bogus and that it does not make sense to store its twin also in our key storage. We can also easily extend the update code to detect a collision and reject the update. Signed-off-by: Werner Koch <wk@gnupg.org>
490 lines
15 KiB
C
490 lines
15 KiB
C
/* userids.c - Utility functions for user ids.
|
|
* Copyright (C) 2001, 2003, 2004, 2006,
|
|
* 2009 Free Software Foundation, Inc.
|
|
* Copyright (C) 2015 g10 Code GmbH
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* This file 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.
|
|
*
|
|
* This file 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 this program; if not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "util.h"
|
|
#include "userids.h"
|
|
|
|
|
|
/* Parse the user-id NAME and build a search description for it.
|
|
* Returns 0 on success or an error code. DESC may be NULL to merely
|
|
* check the validity of a user-id.
|
|
*
|
|
* Some used rules:
|
|
* - If the username starts with 8,9,16 or 17 hex-digits (the first one
|
|
* must be in the range 0..9), this is considered a keyid; depending
|
|
* on the length a short or complete one.
|
|
* - If the username starts with 32,33,40 or 41 hex-digits (the first one
|
|
* must be in the range 0..9), this is considered a fingerprint.
|
|
* - If the username starts with a left angle, we assume it is a complete
|
|
* email address and look only at this part.
|
|
* - If the username starts with a colon we assume it is a unified
|
|
* key specfification.
|
|
* - If the username starts with a '.', we assume it is the ending
|
|
* part of an email address
|
|
* - If the username starts with an '@', we assume it is a part of an
|
|
* email address
|
|
* - If the userid start with an '=' an exact compare is done.
|
|
* - If the userid starts with a '*' a case insensitive substring search is
|
|
* done (This is the default).
|
|
* - If the userid starts with a '+' we will compare individual words
|
|
* and a match requires that all the words are in the userid.
|
|
* Words are delimited by white space or "()<>[]{}.@-+_,;/&!"
|
|
* (note that you can't search for these characters). Compare
|
|
* is not case sensitive.
|
|
* - If the userid starts with a '&' a 40 hex digits keygrip is expected.
|
|
* - If the userid starts with a '^' followed by 40 hex digits it describes
|
|
* a Unique-Blob-ID (UBID) which is the hash of keyblob or certificate as
|
|
* stored in the database. This is used in the IPC of the keyboxd.
|
|
*/
|
|
|
|
gpg_error_t
|
|
classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack)
|
|
{
|
|
const char *s;
|
|
char *s2 = NULL;
|
|
int rc = 0;
|
|
int hexprefix = 0;
|
|
int hexlength;
|
|
int mode = 0;
|
|
KEYDB_SEARCH_DESC dummy_desc;
|
|
|
|
if (!desc)
|
|
desc = &dummy_desc;
|
|
|
|
/* Clear the structure so that the mode field is set to zero unless
|
|
we set it to the correct value right at the end of this
|
|
function. */
|
|
memset (desc, 0, sizeof *desc);
|
|
|
|
/* Skip leading and trailing spaces. */
|
|
for(s = name; *s && spacep (s); s++ )
|
|
;
|
|
if (*s && spacep (s + strlen(s) - 1))
|
|
{
|
|
s2 = xtrystrdup (s);
|
|
if (!s2)
|
|
{
|
|
rc = gpg_error_from_syserror ();
|
|
goto out;
|
|
}
|
|
trim_trailing_spaces (s2);
|
|
s = s2;
|
|
}
|
|
|
|
switch (*s)
|
|
{
|
|
case 0: /* Empty string is an error. */
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
|
|
case '.': /* An email address, compare from end. Note that this
|
|
has not yet been implemented in the search code. */
|
|
mode = KEYDB_SEARCH_MODE_MAILEND;
|
|
s++;
|
|
desc->u.name = s;
|
|
break;
|
|
|
|
case '<': /* An email address. */
|
|
mode = KEYDB_SEARCH_MODE_MAIL;
|
|
/* FIXME: The keyring code in g10 assumes that the mail name is
|
|
prefixed with an '<'. However the keybox code used for sm/
|
|
assumes it has been removed. For now we use this simple hack
|
|
to overcome the problem. */
|
|
if (!openpgp_hack)
|
|
s++;
|
|
desc->u.name = s;
|
|
break;
|
|
|
|
case '@': /* Part of an email address. */
|
|
mode = KEYDB_SEARCH_MODE_MAILSUB;
|
|
s++;
|
|
desc->u.name = s;
|
|
break;
|
|
|
|
case '=': /* Exact compare. */
|
|
mode = KEYDB_SEARCH_MODE_EXACT;
|
|
s++;
|
|
desc->u.name = s;
|
|
break;
|
|
|
|
case '*': /* Case insensitive substring search. */
|
|
mode = KEYDB_SEARCH_MODE_SUBSTR;
|
|
s++;
|
|
desc->u.name = s;
|
|
break;
|
|
|
|
case '+': /* Compare individual words. Note that this has not
|
|
yet been implemented in the search code. */
|
|
mode = KEYDB_SEARCH_MODE_WORDS;
|
|
s++;
|
|
desc->u.name = s;
|
|
break;
|
|
|
|
case '/': /* Subject's DN. */
|
|
s++;
|
|
if (!*s || spacep (s)) /* No DN or prefixed with a space. */
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
desc->u.name = s;
|
|
mode = KEYDB_SEARCH_MODE_SUBJECT;
|
|
break;
|
|
|
|
case '#': /* S/N with optional issuer id or just issuer id. */
|
|
{
|
|
const char *si;
|
|
|
|
s++;
|
|
if ( *s == '/')
|
|
{ /* "#/" indicates an issuer's DN. */
|
|
s++;
|
|
if (!*s || spacep (s)) /* No DN or prefixed with a space. */
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
desc->u.name = s;
|
|
mode = KEYDB_SEARCH_MODE_ISSUER;
|
|
}
|
|
else
|
|
{ /* Serialnumber + optional issuer ID. */
|
|
for (si=s; *si && *si != '/'; si++)
|
|
{
|
|
/* Check for an invalid digit in the serial number. */
|
|
if (!strchr("01234567890abcdefABCDEF", *si))
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
}
|
|
desc->sn = (const unsigned char*)s;
|
|
desc->snlen = -1;
|
|
if (!*si)
|
|
mode = KEYDB_SEARCH_MODE_SN;
|
|
else
|
|
{
|
|
s = si+1;
|
|
if (!*s || spacep (s)) /* No DN or prefixed with a space. */
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
desc->u.name = s;
|
|
mode = KEYDB_SEARCH_MODE_ISSUER_SN;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ':': /* Unified fingerprint. */
|
|
{
|
|
const char *se, *si;
|
|
int i;
|
|
|
|
se = strchr (++s,':');
|
|
if (!se)
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
for (i=0,si=s; si < se; si++, i++ )
|
|
{
|
|
if (!strchr("01234567890abcdefABCDEF", *si))
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid digit. */
|
|
goto out;
|
|
}
|
|
}
|
|
if (i != 32 && i != 40 && i != 64)
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr. */
|
|
goto out;
|
|
}
|
|
for (i=0,si=s; si < se; i++, si +=2)
|
|
desc->u.fpr[i] = hextobyte(si);
|
|
desc->fprlen = i;
|
|
for (; i < 32; i++)
|
|
desc->u.fpr[i]= 0;
|
|
mode = KEYDB_SEARCH_MODE_FPR;
|
|
}
|
|
break;
|
|
|
|
case '&': /* Keygrip*/
|
|
{
|
|
if (hex2bin (s+1, desc->u.grip, 20) < 0)
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
|
|
goto out;
|
|
}
|
|
mode = KEYDB_SEARCH_MODE_KEYGRIP;
|
|
}
|
|
break;
|
|
|
|
case '^': /* UBID */
|
|
{
|
|
if (hex2bin (s+1, desc->u.ubid, UBID_LEN) < 0)
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
|
|
goto out;
|
|
}
|
|
mode = KEYDB_SEARCH_MODE_UBID;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (s[0] == '0' && s[1] == 'x')
|
|
{
|
|
hexprefix = 1;
|
|
s += 2;
|
|
}
|
|
|
|
hexlength = strspn(s, "0123456789abcdefABCDEF");
|
|
if (hexlength >= 8 && s[hexlength] =='!')
|
|
{
|
|
desc->exact = 1;
|
|
hexlength++; /* Just for the following check. */
|
|
}
|
|
|
|
/* Check if a hexadecimal number is terminated by EOS or blank. */
|
|
if (hexlength && s[hexlength] && !spacep (s+hexlength))
|
|
{
|
|
if (hexprefix) /* A "0x" prefix without a correct
|
|
termination is an error. */
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
/* The first characters looked like a hex number, but the
|
|
entire string is not. */
|
|
hexlength = 0;
|
|
}
|
|
|
|
if (desc->exact)
|
|
hexlength--; /* Remove the bang. */
|
|
|
|
if ((hexlength == 8
|
|
&& (s[hexlength] == 0
|
|
|| (s[hexlength] == '!' && s[hexlength + 1] == 0)))
|
|
|| (!hexprefix && hexlength == 9 && *s == '0'))
|
|
{
|
|
/* Short keyid. */
|
|
if (hexlength == 9)
|
|
s++;
|
|
desc->u.kid[1] = strtoul( s, NULL, 16 );
|
|
mode = KEYDB_SEARCH_MODE_SHORT_KID;
|
|
}
|
|
else if ((hexlength == 16
|
|
&& (s[hexlength] == 0
|
|
|| (s[hexlength] == '!' && s[hexlength + 1] == 0)))
|
|
|| (!hexprefix && hexlength == 17 && *s == '0'))
|
|
{
|
|
/* Long keyid. */
|
|
char buf[9];
|
|
if (hexlength == 17)
|
|
s++;
|
|
mem2str (buf, s, 9);
|
|
desc->u.kid[0] = strtoul (buf, NULL, 16);
|
|
desc->u.kid[1] = strtoul (s+8, NULL, 16);
|
|
mode = KEYDB_SEARCH_MODE_LONG_KID;
|
|
}
|
|
else if ((hexlength == 32
|
|
&& (s[hexlength] == 0
|
|
|| (s[hexlength] == '!' && s[hexlength + 1] == 0)))
|
|
|| (!hexprefix && hexlength == 33 && *s == '0'))
|
|
{
|
|
/* MD5 fingerprint. */
|
|
int i;
|
|
if (hexlength == 33)
|
|
s++;
|
|
memset (desc->u.fpr+16, 0, 4);
|
|
for (i=0; i < 16; i++, s+=2)
|
|
{
|
|
int c = hextobyte(s);
|
|
if (c == -1)
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
desc->u.fpr[i] = c;
|
|
}
|
|
desc->fprlen = 16;
|
|
for (; i < 32; i++)
|
|
desc->u.fpr[i]= 0;
|
|
mode = KEYDB_SEARCH_MODE_FPR;
|
|
}
|
|
else if ((hexlength == 40
|
|
&& (s[hexlength] == 0
|
|
|| (s[hexlength] == '!' && s[hexlength + 1] == 0)))
|
|
|| (!hexprefix && hexlength == 41 && *s == '0'))
|
|
{
|
|
/* SHA1 fingerprint. */
|
|
int i;
|
|
if (hexlength == 41)
|
|
s++;
|
|
for (i=0; i < 20; i++, s+=2)
|
|
{
|
|
int c = hextobyte(s);
|
|
if (c == -1)
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
desc->u.fpr[i] = c;
|
|
}
|
|
desc->fprlen = 20;
|
|
for (; i < 32; i++)
|
|
desc->u.fpr[i]= 0;
|
|
mode = KEYDB_SEARCH_MODE_FPR;
|
|
}
|
|
else if ((hexlength == 64
|
|
&& (s[hexlength] == 0
|
|
|| (s[hexlength] == '!' && s[hexlength + 1] == 0)))
|
|
|| (!hexprefix && hexlength == 65 && *s == '0'))
|
|
{
|
|
/* SHA256 fingerprint. */
|
|
int i;
|
|
if (hexlength == 65)
|
|
s++;
|
|
for (i=0; i < 32; i++, s+=2)
|
|
{
|
|
int c = hextobyte(s);
|
|
if (c == -1)
|
|
{
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
desc->u.fpr[i] = c;
|
|
}
|
|
desc->fprlen = 32;
|
|
mode = KEYDB_SEARCH_MODE_FPR;
|
|
}
|
|
else if (!hexprefix)
|
|
{
|
|
/* The fingerprint of an X.509 listing is often delimited by
|
|
* colons, so we try to single this case out. Note that the
|
|
* OpenPGP bang suffix is not supported here. */
|
|
desc->exact = 0;
|
|
mode = 0;
|
|
hexlength = strspn (s, ":0123456789abcdefABCDEF");
|
|
if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i < 20; i++, s += 3)
|
|
{
|
|
int c = hextobyte(s);
|
|
if (c == -1 || (i < 19 && s[2] != ':'))
|
|
break;
|
|
desc->u.fpr[i] = c;
|
|
}
|
|
if (i == 20)
|
|
{
|
|
desc->fprlen = 20;
|
|
mode = KEYDB_SEARCH_MODE_FPR;
|
|
}
|
|
for (; i < 32; i++)
|
|
desc->u.fpr[i]= 0;
|
|
}
|
|
if (!mode)
|
|
{
|
|
/* Still not found. Now check for a space separated
|
|
* OpenPGP v4 fingerprint like:
|
|
* 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367
|
|
* or
|
|
* 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367
|
|
* FIXME: Support OpenPGP v5 fingerprint
|
|
*/
|
|
hexlength = strspn (s, " 0123456789abcdefABCDEF");
|
|
if (s[hexlength] && s[hexlength] != ' ')
|
|
hexlength = 0; /* Followed by non-space. */
|
|
while (hexlength && s[hexlength-1] == ' ')
|
|
hexlength--; /* Trim trailing spaces. */
|
|
if ((hexlength == 49 || hexlength == 50)
|
|
&& (!s[hexlength] || s[hexlength] == ' '))
|
|
{
|
|
int i, c;
|
|
|
|
for (i=0; i < 20; i++)
|
|
{
|
|
if (i && !(i % 2))
|
|
{
|
|
if (*s != ' ')
|
|
break;
|
|
s++;
|
|
/* Skip the double space in the middle but
|
|
don't require it to help copying
|
|
fingerprints from sources which fold
|
|
multiple space to one. */
|
|
if (i == 10 && *s == ' ')
|
|
s++;
|
|
}
|
|
|
|
c = hextobyte(s);
|
|
if (c == -1)
|
|
break;
|
|
desc->u.fpr[i] = c;
|
|
s += 2;
|
|
}
|
|
if (i == 20)
|
|
{
|
|
desc->fprlen = 20;
|
|
mode = KEYDB_SEARCH_MODE_FPR;
|
|
}
|
|
for (; i < 32; i++)
|
|
desc->u.fpr[i]= 0;
|
|
}
|
|
}
|
|
if (!mode) /* Default to substring search. */
|
|
{
|
|
desc->u.name = s;
|
|
mode = KEYDB_SEARCH_MODE_SUBSTR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Hex number with a prefix but with a wrong length. */
|
|
rc = gpg_error (GPG_ERR_INV_USER_ID);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
desc->mode = mode;
|
|
out:
|
|
xfree (s2);
|
|
return rc;
|
|
}
|