Line data Source code
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 : */
69 :
70 : gpg_error_t
71 622 : classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack)
72 : {
73 : const char *s;
74 622 : char *s2 = NULL;
75 622 : int rc = 0;
76 622 : int hexprefix = 0;
77 : int hexlength;
78 622 : int mode = 0;
79 : KEYDB_SEARCH_DESC dummy_desc;
80 :
81 622 : if (!desc)
82 0 : desc = &dummy_desc;
83 :
84 : /* Clear the structure so that the mode field is set to zero unless
85 : we set it to the correct value right at the end of this
86 : function. */
87 622 : memset (desc, 0, sizeof *desc);
88 :
89 : /* Skip leading and trailing spaces. */
90 622 : for(s = name; *s && spacep (s); s++ )
91 : ;
92 622 : if (*s && spacep (s + strlen(s) - 1))
93 : {
94 0 : s2 = xtrystrdup (s);
95 0 : if (!s2)
96 : {
97 0 : rc = gpg_error_from_syserror ();
98 0 : goto out;
99 : }
100 0 : trim_trailing_spaces (s2);
101 0 : s = s2;
102 : }
103 :
104 622 : switch (*s)
105 : {
106 : case 0: /* Empty string is an error. */
107 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
108 0 : goto out;
109 :
110 : case '.': /* An email address, compare from end. Note that this
111 : has not yet been implemented in the search code. */
112 0 : mode = KEYDB_SEARCH_MODE_MAILEND;
113 0 : s++;
114 0 : desc->u.name = s;
115 0 : break;
116 :
117 : case '<': /* An email address. */
118 0 : mode = KEYDB_SEARCH_MODE_MAIL;
119 : /* FIXME: The keyring code in g10 assumes that the mail name is
120 : prefixed with an '<'. However the keybox code used for sm/
121 : assumes it has been removed. For now we use this simple hack
122 : to overcome the problem. */
123 0 : if (!openpgp_hack)
124 0 : s++;
125 0 : desc->u.name = s;
126 0 : break;
127 :
128 : case '@': /* Part of an email address. */
129 0 : mode = KEYDB_SEARCH_MODE_MAILSUB;
130 0 : s++;
131 0 : desc->u.name = s;
132 0 : break;
133 :
134 : case '=': /* Exact compare. */
135 8 : mode = KEYDB_SEARCH_MODE_EXACT;
136 8 : s++;
137 8 : desc->u.name = s;
138 8 : break;
139 :
140 : case '*': /* Case insensitive substring search. */
141 0 : mode = KEYDB_SEARCH_MODE_SUBSTR;
142 0 : s++;
143 0 : desc->u.name = s;
144 0 : break;
145 :
146 : case '+': /* Compare individual words. Note that this has not
147 : yet been implemented in the search code. */
148 0 : mode = KEYDB_SEARCH_MODE_WORDS;
149 0 : s++;
150 0 : desc->u.name = s;
151 0 : break;
152 :
153 : case '/': /* Subject's DN. */
154 0 : s++;
155 0 : if (!*s || spacep (s)) /* No DN or prefixed with a space. */
156 : {
157 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
158 0 : goto out;
159 : }
160 0 : desc->u.name = s;
161 0 : mode = KEYDB_SEARCH_MODE_SUBJECT;
162 0 : break;
163 :
164 : case '#': /* S/N with optional issuer id or just issuer id. */
165 : {
166 : const char *si;
167 :
168 0 : s++;
169 0 : if ( *s == '/')
170 : { /* "#/" indicates an issuer's DN. */
171 0 : s++;
172 0 : if (!*s || spacep (s)) /* No DN or prefixed with a space. */
173 : {
174 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
175 0 : goto out;
176 : }
177 0 : desc->u.name = s;
178 0 : mode = KEYDB_SEARCH_MODE_ISSUER;
179 : }
180 : else
181 : { /* Serialnumber + optional issuer ID. */
182 0 : for (si=s; *si && *si != '/'; si++)
183 : {
184 : /* Check for an invalid digit in the serial number. */
185 0 : if (!strchr("01234567890abcdefABCDEF", *si))
186 : {
187 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
188 0 : goto out;
189 : }
190 : }
191 0 : desc->sn = (const unsigned char*)s;
192 0 : desc->snlen = -1;
193 0 : if (!*si)
194 0 : mode = KEYDB_SEARCH_MODE_SN;
195 : else
196 : {
197 0 : s = si+1;
198 0 : if (!*s || spacep (s)) /* No DN or prefixed with a space. */
199 : {
200 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
201 0 : goto out;
202 : }
203 0 : desc->u.name = s;
204 0 : mode = KEYDB_SEARCH_MODE_ISSUER_SN;
205 : }
206 : }
207 : }
208 0 : break;
209 :
210 : case ':': /* Unified fingerprint. */
211 : {
212 : const char *se, *si;
213 : int i;
214 :
215 0 : se = strchr (++s,':');
216 0 : if (!se)
217 : {
218 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
219 0 : goto out;
220 : }
221 0 : for (i=0,si=s; si < se; si++, i++ )
222 : {
223 0 : if (!strchr("01234567890abcdefABCDEF", *si))
224 : {
225 0 : rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid digit. */
226 0 : goto out;
227 : }
228 : }
229 0 : if (i != 32 && i != 40)
230 : {
231 0 : rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr. */
232 0 : goto out;
233 : }
234 0 : for (i=0,si=s; si < se; i++, si +=2)
235 0 : desc->u.fpr[i] = hextobyte(si);
236 0 : for (; i < 20; i++)
237 0 : desc->u.fpr[i]= 0;
238 0 : mode = KEYDB_SEARCH_MODE_FPR;
239 : }
240 0 : break;
241 :
242 : case '&': /* Keygrip*/
243 : {
244 0 : if (hex2bin (s+1, desc->u.grip, 20) < 0)
245 : {
246 0 : rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
247 0 : goto out;
248 : }
249 0 : mode = KEYDB_SEARCH_MODE_KEYGRIP;
250 : }
251 0 : break;
252 :
253 : default:
254 614 : if (s[0] == '0' && s[1] == 'x')
255 : {
256 95 : hexprefix = 1;
257 95 : s += 2;
258 : }
259 :
260 614 : hexlength = strspn(s, "0123456789abcdefABCDEF");
261 614 : if (hexlength >= 8 && s[hexlength] =='!')
262 : {
263 3 : desc->exact = 1;
264 3 : hexlength++; /* Just for the following check. */
265 : }
266 :
267 : /* Check if a hexadecimal number is terminated by EOS or blank. */
268 614 : if (hexlength && s[hexlength] && !spacep (s+hexlength))
269 : {
270 32 : if (hexprefix) /* A "0x" prefix without a correct
271 : termination is an error. */
272 : {
273 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
274 0 : goto out;
275 : }
276 : /* The first characters looked like a hex number, but the
277 : entire string is not. */
278 32 : hexlength = 0;
279 : }
280 :
281 614 : if (desc->exact)
282 3 : hexlength--; /* Remove the bang. */
283 :
284 614 : if ((hexlength == 8
285 243 : && (s[hexlength] == 0
286 3 : || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
287 371 : || (!hexprefix && hexlength == 9 && *s == '0'))
288 : {
289 : /* Short keyid. */
290 243 : if (hexlength == 9)
291 0 : s++;
292 243 : desc->u.kid[1] = strtoul( s, NULL, 16 );
293 243 : mode = KEYDB_SEARCH_MODE_SHORT_KID;
294 : }
295 371 : else if ((hexlength == 16
296 0 : && (s[hexlength] == 0
297 0 : || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
298 371 : || (!hexprefix && hexlength == 17 && *s == '0'))
299 0 : {
300 : /* Long keyid. */
301 : char buf[9];
302 0 : if (hexlength == 17)
303 0 : s++;
304 0 : mem2str (buf, s, 9);
305 0 : desc->u.kid[0] = strtoul (buf, NULL, 16);
306 0 : desc->u.kid[1] = strtoul (s+8, NULL, 16);
307 0 : mode = KEYDB_SEARCH_MODE_LONG_KID;
308 : }
309 371 : else if ((hexlength == 32
310 0 : && (s[hexlength] == 0
311 0 : || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
312 371 : || (!hexprefix && hexlength == 33 && *s == '0'))
313 0 : {
314 : /* MD5 fingerprint. */
315 : int i;
316 0 : if (hexlength == 33)
317 0 : s++;
318 0 : memset (desc->u.fpr+16, 0, 4);
319 0 : for (i=0; i < 16; i++, s+=2)
320 : {
321 0 : int c = hextobyte(s);
322 0 : if (c == -1)
323 : {
324 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
325 0 : goto out;
326 : }
327 0 : desc->u.fpr[i] = c;
328 : }
329 0 : mode = KEYDB_SEARCH_MODE_FPR16;
330 : }
331 371 : else if ((hexlength == 40
332 191 : && (s[hexlength] == 0
333 0 : || (s[hexlength] == '!' && s[hexlength + 1] == 0)))
334 180 : || (!hexprefix && hexlength == 41 && *s == '0'))
335 191 : {
336 : /* SHA1/RMD160 fingerprint. */
337 : int i;
338 191 : if (hexlength == 41)
339 0 : s++;
340 4011 : for (i=0; i < 20; i++, s+=2)
341 : {
342 3820 : int c = hextobyte(s);
343 3820 : if (c == -1)
344 : {
345 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
346 0 : goto out;
347 : }
348 3820 : desc->u.fpr[i] = c;
349 : }
350 191 : mode = KEYDB_SEARCH_MODE_FPR20;
351 : }
352 180 : else if (!hexprefix)
353 : {
354 : /* The fingerprint in an X.509 listing is often delimited by
355 : colons, so we try to single this case out. */
356 180 : mode = 0;
357 180 : hexlength = strspn (s, ":0123456789abcdefABCDEF");
358 180 : if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
359 : {
360 : int i;
361 :
362 0 : for (i=0; i < 20; i++, s += 3)
363 : {
364 0 : int c = hextobyte(s);
365 0 : if (c == -1 || (i < 19 && s[2] != ':'))
366 : break;
367 0 : desc->u.fpr[i] = c;
368 : }
369 0 : if (i == 20)
370 0 : mode = KEYDB_SEARCH_MODE_FPR20;
371 : }
372 180 : if (!mode)
373 : {
374 : /* Still not found. Now check for a space separated
375 : OpenPGP v4 fingerprint like:
376 : 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367
377 : or
378 : 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367
379 : */
380 180 : hexlength = strspn (s, " 0123456789abcdefABCDEF");
381 180 : if (s[hexlength] && s[hexlength] != ' ')
382 177 : hexlength = 0; /* Followed by non-space. */
383 360 : while (hexlength && s[hexlength-1] == ' ')
384 0 : hexlength--; /* Trim trailing spaces. */
385 180 : if ((hexlength == 49 || hexlength == 50)
386 3 : && (!s[hexlength] || s[hexlength] == ' '))
387 : {
388 : int i, c;
389 :
390 63 : for (i=0; i < 20; i++)
391 : {
392 60 : if (i && !(i % 2))
393 : {
394 27 : if (*s != ' ')
395 0 : break;
396 27 : s++;
397 : /* Skip the double space in the middle but
398 : don't require it to help copying
399 : fingerprints from sources which fold
400 : multiple space to one. */
401 27 : if (i == 10 && *s == ' ')
402 3 : s++;
403 : }
404 :
405 60 : c = hextobyte(s);
406 60 : if (c == -1)
407 0 : break;
408 60 : desc->u.fpr[i] = c;
409 60 : s += 2;
410 : }
411 3 : if (i == 20)
412 3 : mode = KEYDB_SEARCH_MODE_FPR20;
413 : }
414 : }
415 180 : if (!mode) /* Default to substring search. */
416 : {
417 177 : desc->exact = 0;
418 177 : desc->u.name = s;
419 177 : mode = KEYDB_SEARCH_MODE_SUBSTR;
420 : }
421 : }
422 : else
423 : {
424 : /* Hex number with a prefix but with a wrong length. */
425 0 : rc = gpg_error (GPG_ERR_INV_USER_ID);
426 0 : goto out;
427 : }
428 : }
429 :
430 622 : desc->mode = mode;
431 : out:
432 622 : xfree (s2);
433 622 : return rc;
434 : }
|