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
classify_user_id(const char * name,KEYDB_SEARCH_DESC * desc,int openpgp_hack)74 classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack)
75 {
76 const char *s;
77 char *s2 = NULL;
78 int rc = 0;
79 int hexprefix = 0;
80 int hexlength;
81 int mode = 0;
82 KEYDB_SEARCH_DESC dummy_desc;
83
84 if (!desc)
85 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 memset (desc, 0, sizeof *desc);
91
92 /* Skip leading and trailing spaces. */
93 for(s = name; *s && spacep (s); s++ )
94 ;
95 if (*s && spacep (s + strlen(s) - 1))
96 {
97 s2 = xtrystrdup (s);
98 if (!s2)
99 {
100 rc = gpg_error_from_syserror ();
101 goto out;
102 }
103 trim_trailing_spaces (s2);
104 s = s2;
105 }
106
107 switch (*s)
108 {
109 case 0: /* Empty string is an error. */
110 rc = gpg_error (GPG_ERR_INV_USER_ID);
111 goto out;
112
113 case '.': /* An email address, compare from end. Note that this
114 has not yet been implemented in the search code. */
115 mode = KEYDB_SEARCH_MODE_MAILEND;
116 s++;
117 desc->u.name = s;
118 desc->name_used = 1;
119 break;
120
121 case '<': /* An email address. */
122 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 if (!openpgp_hack)
128 s++;
129 desc->u.name = s;
130 desc->name_used = 1;
131 break;
132
133 case '@': /* Part of an email address. */
134 mode = KEYDB_SEARCH_MODE_MAILSUB;
135 s++;
136 desc->u.name = s;
137 desc->name_used = 1;
138 break;
139
140 case '=': /* Exact compare. */
141 mode = KEYDB_SEARCH_MODE_EXACT;
142 s++;
143 desc->u.name = s;
144 desc->name_used = 1;
145 break;
146
147 case '*': /* Case insensitive substring search. */
148 mode = KEYDB_SEARCH_MODE_SUBSTR;
149 s++;
150 desc->u.name = s;
151 desc->name_used = 1;
152 break;
153
154 case '+': /* Compare individual words. Note that this has not
155 yet been implemented in the search code. */
156 mode = KEYDB_SEARCH_MODE_WORDS;
157 s++;
158 desc->u.name = s;
159 desc->name_used = 1;
160 break;
161
162 case '/': /* Subject's DN. */
163 s++;
164 if (!*s || spacep (s)) /* No DN or prefixed with a space. */
165 {
166 rc = gpg_error (GPG_ERR_INV_USER_ID);
167 goto out;
168 }
169 desc->u.name = s;
170 desc->name_used = 1;
171 mode = KEYDB_SEARCH_MODE_SUBJECT;
172 break;
173
174 case '#': /* S/N with optional issuer id or just issuer id. */
175 {
176 const char *si;
177
178 s++;
179 if ( *s == '/')
180 { /* "#/" indicates an issuer's DN. */
181 s++;
182 if (!*s || spacep (s)) /* No DN or prefixed with a space. */
183 {
184 rc = gpg_error (GPG_ERR_INV_USER_ID);
185 goto out;
186 }
187 desc->u.name = s;
188 desc->name_used = 1;
189 mode = KEYDB_SEARCH_MODE_ISSUER;
190 }
191 else
192 { /* Serialnumber + optional issuer ID. */
193 for (si=s; *si && *si != '/'; si++)
194 {
195 /* Check for an invalid digit in the serial number. */
196 if (!strchr("01234567890abcdefABCDEF", *si))
197 {
198 rc = gpg_error (GPG_ERR_INV_USER_ID);
199 goto out;
200 }
201 }
202 desc->sn = (const unsigned char*)s;
203 desc->snlen = si - s;
204 desc->snhex = 1;
205 if (!*si)
206 mode = KEYDB_SEARCH_MODE_SN;
207 else
208 {
209 s = si+1;
210 if (!*s || spacep (s)) /* No DN or prefixed with a space. */
211 {
212 rc = gpg_error (GPG_ERR_INV_USER_ID);
213 goto out;
214 }
215 desc->u.name = s;
216 desc->name_used = 1;
217 mode = KEYDB_SEARCH_MODE_ISSUER_SN;
218 }
219 }
220 }
221 break;
222
223 case ':': /* Unified fingerprint. */
224 {
225 const char *se, *si;
226 int i;
227
228 se = strchr (++s,':');
229 if (!se)
230 {
231 rc = gpg_error (GPG_ERR_INV_USER_ID);
232 goto out;
233 }
234 for (i=0,si=s; si < se; si++, i++ )
235 {
236 if (!strchr("01234567890abcdefABCDEF", *si))
237 {
238 rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid digit. */
239 goto out;
240 }
241 }
242 if (i != 32 && i != 40 && i != 64)
243 {
244 rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr. */
245 goto out;
246 }
247 for (i=0,si=s; si < se; i++, si +=2)
248 desc->u.fpr[i] = hextobyte(si);
249 desc->fprlen = i;
250 for (; i < 32; i++)
251 desc->u.fpr[i]= 0;
252 mode = KEYDB_SEARCH_MODE_FPR;
253 }
254 break;
255
256 case '&': /* Keygrip*/
257 {
258 if (hex2bin (s+1, desc->u.grip, 20) < 0)
259 {
260 rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
261 goto out;
262 }
263 mode = KEYDB_SEARCH_MODE_KEYGRIP;
264 }
265 break;
266
267 case '^': /* UBID */
268 {
269 if (hex2bin (s+1, desc->u.ubid, UBID_LEN) < 0)
270 {
271 rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
272 goto out;
273 }
274 mode = KEYDB_SEARCH_MODE_UBID;
275 }
276 break;
277
278 default:
279 if (s[0] == '0' && s[1] == 'x')
280 {
281 hexprefix = 1;
282 s += 2;
283 }
284
285 hexlength = strspn(s, "0123456789abcdefABCDEF");
286 if (hexlength >= 8 && s[hexlength] =='!')
287 {
288 desc->exact = 1;
289 hexlength++; /* Just for the following check. */
290 }
291
292 /* Check if a hexadecimal number is terminated by EOS or blank. */
293 if (hexlength && s[hexlength] && !spacep (s+hexlength))
294 {
295 if (hexprefix) /* A "0x" prefix without a correct
296 termination is an error. */
297 {
298 rc = gpg_error (GPG_ERR_INV_USER_ID);
299 goto out;
300 }
301 /* The first characters looked like a hex number, but the
302 entire string is not. */
303 hexlength = 0;
304 }
305
306 if (desc->exact)
307 hexlength--; /* Remove the bang. */
308
309 if ((hexlength == 8
310 && (s[hexlength] == 0
311 || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
312 || (!hexprefix && hexlength == 9 && *s == '0'))
313 {
314 /* Short keyid. */
315 if (hexlength == 9)
316 s++;
317 desc->u.kid[1] = strtoul( s, NULL, 16 );
318 mode = KEYDB_SEARCH_MODE_SHORT_KID;
319 }
320 else if ((hexlength == 16
321 && (s[hexlength] == 0
322 || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
323 || (!hexprefix && hexlength == 17 && *s == '0'))
324 {
325 /* Long keyid. */
326 char buf[9];
327 if (hexlength == 17)
328 s++;
329 mem2str (buf, s, 9);
330 desc->u.kid[0] = strtoul (buf, NULL, 16);
331 desc->u.kid[1] = strtoul (s+8, NULL, 16);
332 mode = KEYDB_SEARCH_MODE_LONG_KID;
333 }
334 else if ((hexlength == 32
335 && (s[hexlength] == 0
336 || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
337 || (!hexprefix && hexlength == 33 && *s == '0'))
338 {
339 /* MD5 fingerprint. */
340 int i;
341 if (hexlength == 33)
342 s++;
343 memset (desc->u.fpr+16, 0, 4);
344 for (i=0; i < 16; i++, s+=2)
345 {
346 int c = hextobyte(s);
347 if (c == -1)
348 {
349 rc = gpg_error (GPG_ERR_INV_USER_ID);
350 goto out;
351 }
352 desc->u.fpr[i] = c;
353 }
354 desc->fprlen = 16;
355 for (; i < 32; i++)
356 desc->u.fpr[i]= 0;
357 mode = KEYDB_SEARCH_MODE_FPR;
358 }
359 else if ((hexlength == 40
360 && (s[hexlength] == 0
361 || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
362 || (!hexprefix && hexlength == 41 && *s == '0'))
363 {
364 /* SHA1 fingerprint. */
365 int i;
366 if (hexlength == 41)
367 s++;
368 for (i=0; i < 20; i++, s+=2)
369 {
370 int c = hextobyte(s);
371 if (c == -1)
372 {
373 rc = gpg_error (GPG_ERR_INV_USER_ID);
374 goto out;
375 }
376 desc->u.fpr[i] = c;
377 }
378 desc->fprlen = 20;
379 for (; i < 32; i++)
380 desc->u.fpr[i]= 0;
381 mode = KEYDB_SEARCH_MODE_FPR;
382 }
383 else if ((hexlength == 64
384 && (s[hexlength] == 0
385 || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
386 || (!hexprefix && hexlength == 65 && *s == '0'))
387 {
388 /* SHA256 fingerprint. */
389 int i;
390 if (hexlength == 65)
391 s++;
392 for (i=0; i < 32; i++, s+=2)
393 {
394 int c = hextobyte(s);
395 if (c == -1)
396 {
397 rc = gpg_error (GPG_ERR_INV_USER_ID);
398 goto out;
399 }
400 desc->u.fpr[i] = c;
401 }
402 desc->fprlen = 32;
403 mode = KEYDB_SEARCH_MODE_FPR;
404 }
405 else if (!hexprefix)
406 {
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 desc->exact = 0;
411 mode = 0;
412 hexlength = strspn (s, ":0123456789abcdefABCDEF");
413 if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
414 {
415 int i;
416
417 for (i=0; i < 20; i++, s += 3)
418 {
419 int c = hextobyte(s);
420 if (c == -1 || (i < 19 && s[2] != ':'))
421 break;
422 desc->u.fpr[i] = c;
423 }
424 if (i == 20)
425 {
426 desc->fprlen = 20;
427 mode = KEYDB_SEARCH_MODE_FPR;
428 }
429 for (; i < 32; i++)
430 desc->u.fpr[i]= 0;
431 }
432 if (!mode)
433 {
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 hexlength = strspn (s, " 0123456789abcdefABCDEF");
442 if (s[hexlength] && s[hexlength] != ' ')
443 hexlength = 0; /* Followed by non-space. */
444 while (hexlength && s[hexlength-1] == ' ')
445 hexlength--; /* Trim trailing spaces. */
446 if ((hexlength == 49 || hexlength == 50)
447 && (!s[hexlength] || s[hexlength] == ' '))
448 {
449 int i, c;
450
451 for (i=0; i < 20; i++)
452 {
453 if (i && !(i % 2))
454 {
455 if (*s != ' ')
456 break;
457 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 if (i == 10 && *s == ' ')
463 s++;
464 }
465
466 c = hextobyte(s);
467 if (c == -1)
468 break;
469 desc->u.fpr[i] = c;
470 s += 2;
471 }
472 if (i == 20)
473 {
474 desc->fprlen = 20;
475 mode = KEYDB_SEARCH_MODE_FPR;
476 }
477 for (; i < 32; i++)
478 desc->u.fpr[i]= 0;
479 }
480 }
481 if (!mode) /* Default to substring search. */
482 {
483 desc->u.name = s;
484 desc->name_used = 1;
485 mode = KEYDB_SEARCH_MODE_SUBSTR;
486 }
487 }
488 else
489 {
490 /* Hex number with a prefix but with a wrong length. */
491 rc = gpg_error (GPG_ERR_INV_USER_ID);
492 goto out;
493 }
494 }
495
496 desc->mode = mode;
497 out:
498 xfree (s2);
499 return rc;
500 }
501