mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-02 20:18:55 +01:00
37df3d5f59
Having the LGPL on the common GnuPG code helps to share code between GnuPG and related projects (like GPGME and Libassuan). This is good for interoperability and to reduces bugs. * common/asshelp.c, common/asshelp.h, common/asshelp2.c, common/b64dec.c * common/b64enc.c, common/convert.c, common/dns-cert.c * common/dns-cert.h common/exechelp-posix.c, common/exechelp-w32.c * common/exechelp-w32ce.c, common/exechelp.h, common/get-passphrase.c * common/get-passphrase.h, common/gettime.c, common/gpgrlhelp.c * common/helpfile.c, common/homedir.c, common/http.c, common/http.h * common/i18n.c, common/init.c, common/init.h, common/iobuf.c * common/iobuf.h, common/localename.c, common/membuf.c, common/membuf.h * common/miscellaneous.c, common/openpgp-oid.c, common/openpgpdefs.h * common/percent.c, common/pka.c, common/pka.h, common/session-env.c * common/session-env.h, common/sexp-parse.h, common/sexputil.c * common/signal.c, common/srv.c, common/srv.h, common/ssh-utils.c * common/ssh-utils.h, common/sysutils.c, common/sysutils.h * common/tlv.c, common/tlv.h, common/ttyio.c, common/ttyio.h * common/userids.c, common/userids.h, common/xasprintf.c: Change license to LGPLv3+/GPLv2+/
377 lines
12 KiB
C
377 lines
12 KiB
C
/* userids.c - Utility functions for user ids.
|
|
* Copyright (C) 2001, 2003, 2004, 2006,
|
|
* 2009 Free Software Foundation, Inc.
|
|
*
|
|
* 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 <http://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 succdess 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.
|
|
*/
|
|
|
|
gpg_error_t
|
|
classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack)
|
|
{
|
|
const char *s;
|
|
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 spaces. */
|
|
for(s = name; *s && spacep (s); s++ )
|
|
;
|
|
|
|
switch (*s)
|
|
{
|
|
case 0: /* Empty string is an error. */
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
|
|
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. */
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
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. */
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
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))
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
}
|
|
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. */
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
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)
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
for (i=0,si=s; si < se; si++, i++ )
|
|
{
|
|
if (!strchr("01234567890abcdefABCDEF", *si))
|
|
return gpg_error (GPG_ERR_INV_USER_ID); /* Invalid digit. */
|
|
}
|
|
if (i != 32 && i != 40)
|
|
return gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr. */
|
|
for (i=0,si=s; si < se; i++, si +=2)
|
|
desc->u.fpr[i] = hextobyte(si);
|
|
for (; i < 20; i++)
|
|
desc->u.fpr[i]= 0;
|
|
s = se + 1;
|
|
mode = KEYDB_SEARCH_MODE_FPR;
|
|
}
|
|
break;
|
|
|
|
case '&': /* Keygrip*/
|
|
{
|
|
if (hex2bin (s+1, desc->u.grip, 20) < 0)
|
|
return gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
|
|
mode = KEYDB_SEARCH_MODE_KEYGRIP;
|
|
}
|
|
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. */
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
/* 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
|
|
|| (!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
|
|
|| (!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
|
|
|| (!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)
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
desc->u.fpr[i] = c;
|
|
}
|
|
mode = KEYDB_SEARCH_MODE_FPR16;
|
|
}
|
|
else if (hexlength == 40
|
|
|| (!hexprefix && hexlength == 41 && *s == '0'))
|
|
{
|
|
/* SHA1/RMD160 fingerprint. */
|
|
int i;
|
|
if (hexlength == 41)
|
|
s++;
|
|
for (i=0; i < 20; i++, s+=2)
|
|
{
|
|
int c = hextobyte(s);
|
|
if (c == -1)
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
desc->u.fpr[i] = c;
|
|
}
|
|
mode = KEYDB_SEARCH_MODE_FPR20;
|
|
}
|
|
else if (!hexprefix)
|
|
{
|
|
/* The fingerprint in an X.509 listing is often delimited by
|
|
colons, so we try to single this case out. */
|
|
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)
|
|
mode = KEYDB_SEARCH_MODE_FPR20;
|
|
}
|
|
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
|
|
*/
|
|
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)
|
|
mode = KEYDB_SEARCH_MODE_FPR20;
|
|
}
|
|
}
|
|
if (!mode) /* Default to substring search. */
|
|
{
|
|
desc->exact = 0;
|
|
desc->u.name = s;
|
|
mode = KEYDB_SEARCH_MODE_SUBSTR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Hex number with a prefix but with a wrong length. */
|
|
return gpg_error (GPG_ERR_INV_USER_ID);
|
|
}
|
|
}
|
|
|
|
desc->mode = mode;
|
|
return 0;
|
|
}
|