Coverage Report

Created: 2026-01-17 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gnupg/common/userids.c
Line
Count
Source
1
/* userids.c - Utility functions for user ids.
2
 * Copyright (C) 2001, 2003, 2004, 2006,
3
 *               2009 Free Software Foundation, Inc.
4
 * Copyright (C) 2015  g10 Code GmbH
5
 *
6
 * This file is part of GnuPG.
7
 *
8
 * This file is free software; you can redistribute it and/or modify
9
 * it under the terms of either
10
 *
11
 *   - the GNU Lesser General Public License as published by the Free
12
 *     Software Foundation; either version 3 of the License, or (at
13
 *     your option) any later version.
14
 *
15
 * or
16
 *
17
 *   - the GNU General Public License as published by the Free
18
 *     Software Foundation; either version 2 of the License, or (at
19
 *     your option) any later version.
20
 *
21
 * or both in parallel, as here.
22
 *
23
 * This file is distributed in the hope that it will be useful,
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26
 * GNU General Public License for more details.
27
 *
28
 * You should have received a copy of the GNU General Public License
29
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
30
 */
31
32
#include <config.h>
33
#include <stdio.h>
34
#include <stdlib.h>
35
#include <string.h>
36
37
#include "util.h"
38
#include "userids.h"
39
40
41
/* Parse the user-id NAME and build a search description for it.
42
 * Returns 0 on success or an error code.  DESC may be NULL to merely
43
 * check the validity of a user-id.
44
 *
45
 * Some used rules:
46
 * - If the username starts with 8,9,16 or 17 hex-digits (the first one
47
 *   must be in the range 0..9), this is considered a keyid; depending
48
 *   on the length a short or complete one.
49
 * - If the username starts with 32,33,40 or 41 hex-digits (the first one
50
 *   must be in the range 0..9), this is considered a fingerprint.
51
 * - If the username starts with a left angle, we assume it is a complete
52
 *   email address and look only at this part.
53
 * - If the username starts with a colon we assume it is a unified
54
 *   key specfification.
55
 * - If the username starts with a '.', we assume it is the ending
56
 *   part of an email address
57
 * - If the username starts with an '@', we assume it is a part of an
58
 *   email address
59
 * - If the userid start with an '=' an exact compare is done.
60
 * - If the userid starts with a '*' a case insensitive substring search is
61
 *   done (This is the default).
62
 * - If the userid starts with a '+' we will compare individual words
63
 *   and a match requires that all the words are in the userid.
64
 *   Words are delimited by white space or "()<>[]{}.@-+_,;/&!"
65
 *   (note that you can't search for these characters). Compare
66
 *   is not case sensitive.
67
 * - If the userid starts with a '&' a 40 hex digits keygrip is expected.
68
 * - If the userid starts with a '^' followed by 40 hex digits it describes
69
 *   a Unique-Blob-ID (UBID) which is the hash of keyblob or certificate as
70
 *   stored in the database.  This is used in the IPC of the keyboxd.
71
 */
72
73
gpg_error_t
74
classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack)
75
0
{
76
0
  const char *s;
77
0
  char *s2 = NULL;
78
0
  int rc = 0;
79
0
  int hexprefix = 0;
80
0
  int hexlength;
81
0
  int mode = 0;
82
0
  KEYDB_SEARCH_DESC dummy_desc;
83
84
0
  if (!desc)
85
0
    desc = &dummy_desc;
86
87
  /* Clear the structure so that the mode field is set to zero unless
88
     we set it to the correct value right at the end of this
89
     function. */
90
0
  memset (desc, 0, sizeof *desc);
91
92
  /* Skip leading and trailing spaces.  */
93
0
  for(s = name; *s && spacep (s); s++ )
94
0
    ;
95
0
  if (*s && spacep (s + strlen(s) - 1))
96
0
    {
97
0
      s2 = xtrystrdup (s);
98
0
      if (!s2)
99
0
        {
100
0
          rc = gpg_error_from_syserror ();
101
0
          goto out;
102
0
        }
103
0
      trim_trailing_spaces (s2);
104
0
      s = s2;
105
0
    }
106
107
0
  switch (*s)
108
0
    {
109
0
    case 0:  /* Empty string is an error.  */
110
0
      rc = gpg_error (GPG_ERR_INV_USER_ID);
111
0
      goto out;
112
113
0
    case '.': /* An email address, compare from end.  Note that this
114
                 has not yet been implemented in the search code.  */
115
0
      mode = KEYDB_SEARCH_MODE_MAILEND;
116
0
      s++;
117
0
      desc->u.name = s;
118
0
      desc->name_used = 1;
119
0
      break;
120
121
0
    case '<': /* An email address.  */
122
0
      mode = KEYDB_SEARCH_MODE_MAIL;
123
      /* FIXME: The keyring code in g10 assumes that the mail name is
124
         prefixed with an '<'.  However the keybox code used for sm/
125
         assumes it has been removed.  For now we use this simple hack
126
         to overcome the problem.  */
127
0
      if (!openpgp_hack)
128
0
        s++;
129
0
      desc->u.name = s;
130
0
      desc->name_used = 1;
131
0
      break;
132
133
0
    case '@':  /* Part of an email address.  */
134
0
      mode = KEYDB_SEARCH_MODE_MAILSUB;
135
0
      s++;
136
0
      desc->u.name = s;
137
0
      desc->name_used = 1;
138
0
      break;
139
140
0
    case '=':  /* Exact compare.  */
141
0
      mode = KEYDB_SEARCH_MODE_EXACT;
142
0
      s++;
143
0
      desc->u.name = s;
144
0
      desc->name_used = 1;
145
0
      break;
146
147
0
    case '*':  /* Case insensitive substring search.  */
148
0
      mode = KEYDB_SEARCH_MODE_SUBSTR;
149
0
      s++;
150
0
      desc->u.name = s;
151
0
      desc->name_used = 1;
152
0
      break;
153
154
0
    case '+':  /* Compare individual words.  Note that this has not
155
                  yet been implemented in the search code.  */
156
0
      mode = KEYDB_SEARCH_MODE_WORDS;
157
0
      s++;
158
0
      desc->u.name = s;
159
0
      desc->name_used = 1;
160
0
      break;
161
162
0
    case '/': /* Subject's DN.  */
163
0
      s++;
164
0
      if (!*s || spacep (s)) /* No DN or prefixed with a space.  */
165
0
        {
166
0
          rc = gpg_error (GPG_ERR_INV_USER_ID);
167
0
          goto out;
168
0
        }
169
0
      desc->u.name = s;
170
0
      desc->name_used = 1;
171
0
      mode = KEYDB_SEARCH_MODE_SUBJECT;
172
0
      break;
173
174
0
    case '#': /* S/N with optional issuer id or just issuer id.  */
175
0
      {
176
0
        const char *si;
177
178
0
        s++;
179
0
        if ( *s == '/')
180
0
          { /* "#/" indicates an issuer's DN.  */
181
0
            s++;
182
0
            if (!*s || spacep (s)) /* No DN or prefixed with a space.  */
183
0
              {
184
0
                rc = gpg_error (GPG_ERR_INV_USER_ID);
185
0
                goto out;
186
0
              }
187
0
            desc->u.name = s;
188
0
            desc->name_used = 1;
189
0
            mode = KEYDB_SEARCH_MODE_ISSUER;
190
0
          }
191
0
        else
192
0
          { /* Serialnumber + optional issuer ID.  */
193
0
            for (si=s; *si && *si != '/'; si++)
194
0
              {
195
                 /* Check for an invalid digit in the serial number. */
196
0
                if (!strchr("01234567890abcdefABCDEF", *si))
197
0
                  {
198
0
                    rc = gpg_error (GPG_ERR_INV_USER_ID);
199
0
                    goto out;
200
0
                  }
201
0
              }
202
0
            desc->sn = (const unsigned char*)s;
203
0
            desc->snlen = si - s;
204
0
            desc->snhex = 1;
205
0
            if (!*si)
206
0
              mode = KEYDB_SEARCH_MODE_SN;
207
0
            else
208
0
              {
209
0
                s = si+1;
210
0
                if (!*s || spacep (s))  /* No DN or prefixed with a space.  */
211
0
                  {
212
0
                    rc = gpg_error (GPG_ERR_INV_USER_ID);
213
0
                    goto out;
214
0
                  }
215
0
                desc->u.name = s;
216
0
                desc->name_used = 1;
217
0
                mode = KEYDB_SEARCH_MODE_ISSUER_SN;
218
0
              }
219
0
          }
220
0
      }
221
0
      break;
222
223
0
    case ':': /* Unified fingerprint. */
224
0
      {
225
0
        const char *se, *si;
226
0
        int i;
227
228
0
        se = strchr (++s,':');
229
0
        if (!se)
230
0
          {
231
0
            rc = gpg_error (GPG_ERR_INV_USER_ID);
232
0
            goto out;
233
0
          }
234
0
        for (i=0,si=s; si < se; si++, i++ )
235
0
          {
236
0
            if (!strchr("01234567890abcdefABCDEF", *si))
237
0
              {
238
0
                rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid digit.  */
239
0
                goto out;
240
0
              }
241
0
          }
242
0
        if (i != 32 && i != 40 && i != 64)
243
0
          {
244
0
            rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr.  */
245
0
            goto out;
246
0
          }
247
0
        for (i=0,si=s; si < se; i++, si +=2)
248
0
          desc->u.fpr[i] = hextobyte(si);
249
0
        desc->fprlen = i;
250
0
        for (; i < 32; i++)
251
0
          desc->u.fpr[i]= 0;
252
0
        mode = KEYDB_SEARCH_MODE_FPR;
253
0
      }
254
0
      break;
255
256
0
    case '&': /* Keygrip*/
257
0
      {
258
0
        if (hex2bin (s+1, desc->u.grip, 20) < 0)
259
0
          {
260
0
            rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
261
0
            goto out;
262
0
          }
263
0
        mode = KEYDB_SEARCH_MODE_KEYGRIP;
264
0
      }
265
0
      break;
266
267
0
    case '^': /* UBID */
268
0
      {
269
0
        if (hex2bin (s+1, desc->u.ubid, UBID_LEN) < 0)
270
0
          {
271
0
            rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
272
0
            goto out;
273
0
          }
274
0
        mode = KEYDB_SEARCH_MODE_UBID;
275
0
      }
276
0
      break;
277
278
0
    default:
279
0
      if (s[0] == '0' && s[1] == 'x')
280
0
        {
281
0
          hexprefix = 1;
282
0
          s += 2;
283
0
        }
284
285
0
      hexlength = strspn(s, "0123456789abcdefABCDEF");
286
0
      if (hexlength >= 8 && s[hexlength] =='!')
287
0
        {
288
0
          desc->exact = 1;
289
0
          hexlength++; /* Just for the following check.  */
290
0
        }
291
292
      /* Check if a hexadecimal number is terminated by EOS or blank.  */
293
0
      if (hexlength && s[hexlength] && !spacep (s+hexlength))
294
0
        {
295
0
          if (hexprefix) /* A "0x" prefix without a correct
296
                            termination is an error.  */
297
0
            {
298
0
              rc = gpg_error (GPG_ERR_INV_USER_ID);
299
0
              goto out;
300
0
            }
301
          /* The first characters looked like a hex number, but the
302
             entire string is not.  */
303
0
          hexlength = 0;
304
0
        }
305
306
0
      if (desc->exact)
307
0
        hexlength--; /* Remove the bang.  */
308
309
0
      if ((hexlength == 8
310
0
           && (s[hexlength] == 0
311
0
               || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
312
0
          || (!hexprefix && hexlength == 9 && *s == '0'))
313
0
        {
314
          /* Short keyid.  */
315
0
          if (hexlength == 9)
316
0
            s++;
317
0
          desc->u.kid[1] = strtoul( s, NULL, 16 );
318
0
          mode = KEYDB_SEARCH_MODE_SHORT_KID;
319
0
        }
320
0
      else if ((hexlength == 16
321
0
                && (s[hexlength] == 0
322
0
                    || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
323
0
               || (!hexprefix && hexlength == 17 && *s == '0'))
324
0
        {
325
          /* Long keyid.  */
326
0
          char buf[9];
327
0
          if (hexlength == 17)
328
0
            s++;
329
0
          mem2str (buf, s, 9);
330
0
          desc->u.kid[0] = strtoul (buf, NULL, 16);
331
0
          desc->u.kid[1] = strtoul (s+8, NULL, 16);
332
0
          mode = KEYDB_SEARCH_MODE_LONG_KID;
333
0
        }
334
0
      else if ((hexlength == 32
335
0
                && (s[hexlength] == 0
336
0
                    || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
337
0
               || (!hexprefix && hexlength == 33 && *s == '0'))
338
0
        {
339
          /* MD5 fingerprint.  */
340
0
          int i;
341
0
          if (hexlength == 33)
342
0
            s++;
343
0
          memset (desc->u.fpr+16, 0, 4);
344
0
          for (i=0; i < 16; i++, s+=2)
345
0
            {
346
0
              int c = hextobyte(s);
347
0
              if (c == -1)
348
0
                {
349
0
                  rc = gpg_error (GPG_ERR_INV_USER_ID);
350
0
                  goto out;
351
0
                }
352
0
              desc->u.fpr[i] = c;
353
0
            }
354
0
          desc->fprlen = 16;
355
0
          for (; i < 32; i++)
356
0
            desc->u.fpr[i]= 0;
357
0
          mode = KEYDB_SEARCH_MODE_FPR;
358
0
        }
359
0
      else if ((hexlength == 40
360
0
                && (s[hexlength] == 0
361
0
                    || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
362
0
               || (!hexprefix && hexlength == 41 && *s == '0'))
363
0
        {
364
          /* SHA1 fingerprint.  */
365
0
          int i;
366
0
          if (hexlength == 41)
367
0
            s++;
368
0
          for (i=0; i < 20; i++, s+=2)
369
0
            {
370
0
              int c = hextobyte(s);
371
0
              if (c == -1)
372
0
                {
373
0
                  rc = gpg_error (GPG_ERR_INV_USER_ID);
374
0
                  goto out;
375
0
                }
376
0
              desc->u.fpr[i] = c;
377
0
            }
378
0
          desc->fprlen = 20;
379
0
          for (; i < 32; i++)
380
0
            desc->u.fpr[i]= 0;
381
0
          mode = KEYDB_SEARCH_MODE_FPR;
382
0
        }
383
0
      else if ((hexlength == 64
384
0
                && (s[hexlength] == 0
385
0
                    || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
386
0
               || (!hexprefix && hexlength == 65 && *s == '0'))
387
0
        {
388
          /* SHA256 fingerprint.  */
389
0
          int i;
390
0
          if (hexlength == 65)
391
0
            s++;
392
0
          for (i=0; i < 32; i++, s+=2)
393
0
            {
394
0
              int c = hextobyte(s);
395
0
              if (c == -1)
396
0
                {
397
0
                  rc = gpg_error (GPG_ERR_INV_USER_ID);
398
0
                  goto out;
399
0
                }
400
0
              desc->u.fpr[i] = c;
401
0
            }
402
0
          desc->fprlen = 32;
403
0
          mode = KEYDB_SEARCH_MODE_FPR;
404
0
        }
405
0
      else if (!hexprefix)
406
0
        {
407
          /* The fingerprint of an X.509 listing is often delimited by
408
           * colons, so we try to single this case out.  Note that the
409
           * OpenPGP bang suffix is not supported here.  */
410
0
          desc->exact = 0;
411
0
          mode = 0;
412
0
          hexlength = strspn (s, ":0123456789abcdefABCDEF");
413
0
          if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
414
0
            {
415
0
              int i;
416
417
0
              for (i=0; i < 20; i++, s += 3)
418
0
                {
419
0
                  int c = hextobyte(s);
420
0
                  if (c == -1 || (i < 19 && s[2] != ':'))
421
0
                    break;
422
0
                  desc->u.fpr[i] = c;
423
0
                }
424
0
              if (i == 20)
425
0
                {
426
0
                  desc->fprlen = 20;
427
0
                  mode = KEYDB_SEARCH_MODE_FPR;
428
0
                }
429
0
              for (; i < 32; i++)
430
0
                desc->u.fpr[i]= 0;
431
0
            }
432
0
          if (!mode)
433
0
            {
434
              /* Still not found.  Now check for a space separated
435
               * OpenPGP v4 fingerprint like:
436
               *   8061 5870 F5BA D690 3336  86D0 F2AD 85AC 1E42 B367
437
               * or
438
               *   8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367
439
               * FIXME: Support OpenPGP v5 fingerprint
440
               */
441
0
              hexlength = strspn (s, " 0123456789abcdefABCDEF");
442
0
              if (s[hexlength] && s[hexlength] != ' ')
443
0
                hexlength = 0; /* Followed by non-space.  */
444
0
              while (hexlength && s[hexlength-1] == ' ')
445
0
                hexlength--;   /* Trim trailing spaces.  */
446
0
              if ((hexlength == 49 || hexlength == 50)
447
0
                  && (!s[hexlength] || s[hexlength] == ' '))
448
0
                {
449
0
                  int i, c;
450
451
0
                  for (i=0; i < 20; i++)
452
0
                    {
453
0
                      if (i && !(i % 2))
454
0
                        {
455
0
                          if (*s != ' ')
456
0
                            break;
457
0
                          s++;
458
                          /* Skip the double space in the middle but
459
                             don't require it to help copying
460
                             fingerprints from sources which fold
461
                             multiple space to one.  */
462
0
                          if (i == 10 && *s == ' ')
463
0
                            s++;
464
0
                        }
465
466
0
                      c = hextobyte(s);
467
0
                      if (c == -1)
468
0
                        break;
469
0
                      desc->u.fpr[i] = c;
470
0
                      s += 2;
471
0
                    }
472
0
                  if (i == 20)
473
0
                    {
474
0
                      desc->fprlen = 20;
475
0
                      mode = KEYDB_SEARCH_MODE_FPR;
476
0
                    }
477
0
                  for (; i < 32; i++)
478
0
                    desc->u.fpr[i]= 0;
479
0
                }
480
0
            }
481
0
          if (!mode) /* Default to substring search.  */
482
0
            {
483
0
              desc->u.name = s;
484
0
              desc->name_used = 1;
485
0
              mode = KEYDB_SEARCH_MODE_SUBSTR;
486
0
            }
487
0
        }
488
0
      else
489
0
  {
490
          /* Hex number with a prefix but with a wrong length.  */
491
0
          rc = gpg_error (GPG_ERR_INV_USER_ID);
492
0
          goto out;
493
0
        }
494
0
    }
495
496
0
  desc->mode = mode;
497
0
 out:
498
0
  xfree (s2);
499
0
  return rc;
500
0
}