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 : *
5 : * This file is part of GnuPG.
6 : *
7 : * This file is free software; you can redistribute it and/or modify
8 : * it under the terms of either
9 : *
10 : * - the GNU Lesser General Public License as published by the Free
11 : * Software Foundation; either version 3 of the License, or (at
12 : * your option) any later version.
13 : *
14 : * or
15 : *
16 : * - the GNU General Public License as published by the Free
17 : * Software Foundation; either version 2 of the License, or (at
18 : * your option) any later version.
19 : *
20 : * or both in parallel, as here.
21 : *
22 : * This file is distributed in the hope that it will be useful,
23 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 : * GNU General Public License for more details.
26 : *
27 : * You should have received a copy of the GNU General Public License
28 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
29 : */
30 :
31 : #include <config.h>
32 : #include <stdio.h>
33 : #include <stdlib.h>
34 : #include <string.h>
35 :
36 : #include "util.h"
37 : #include "userids.h"
38 :
39 :
40 : /* Parse the user-id NAME and build a search description for it.
41 : * Returns 0 on success or an error code. DESC may be NULL to merely
42 : * check the validity of a user-id.
43 : *
44 : * Some used rules:
45 : * - If the username starts with 8,9,16 or 17 hex-digits (the first one
46 : * must be in the range 0..9), this is considered a keyid; depending
47 : * on the length a short or complete one.
48 : * - If the username starts with 32,33,40 or 41 hex-digits (the first one
49 : * must be in the range 0..9), this is considered a fingerprint.
50 : * - If the username starts with a left angle, we assume it is a complete
51 : * email address and look only at this part.
52 : * - If the username starts with a colon we assume it is a unified
53 : * key specfification.
54 : * - If the username starts with a '.', we assume it is the ending
55 : * part of an email address
56 : * - If the username starts with an '@', we assume it is a part of an
57 : * email address
58 : * - If the userid start with an '=' an exact compare is done.
59 : * - If the userid starts with a '*' a case insensitive substring search is
60 : * done (This is the default).
61 : * - If the userid starts with a '+' we will compare individual words
62 : * and a match requires that all the words are in the userid.
63 : * Words are delimited by white space or "()<>[]{}.@-+_,;/&!"
64 : * (note that you can't search for these characters). Compare
65 : * is not case sensitive.
66 : * - If the userid starts with a '&' a 40 hex digits keygrip is expected.
67 : */
68 :
69 : gpg_error_t
70 419 : classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack)
71 : {
72 : const char *s;
73 419 : int hexprefix = 0;
74 : int hexlength;
75 419 : int mode = 0;
76 : KEYDB_SEARCH_DESC dummy_desc;
77 :
78 419 : if (!desc)
79 0 : desc = &dummy_desc;
80 :
81 : /* Clear the structure so that the mode field is set to zero unless
82 : we set it to the correct value right at the end of this
83 : function. */
84 419 : memset (desc, 0, sizeof *desc);
85 :
86 : /* Skip leading spaces. */
87 419 : for(s = name; *s && spacep (s); s++ )
88 : ;
89 :
90 419 : switch (*s)
91 : {
92 : case 0: /* Empty string is an error. */
93 0 : return gpg_error (GPG_ERR_INV_USER_ID);
94 :
95 : case '.': /* An email address, compare from end. Note that this
96 : has not yet been implemented in the search code. */
97 0 : mode = KEYDB_SEARCH_MODE_MAILEND;
98 0 : s++;
99 0 : desc->u.name = s;
100 0 : break;
101 :
102 : case '<': /* An email address. */
103 0 : mode = KEYDB_SEARCH_MODE_MAIL;
104 : /* FIXME: The keyring code in g10 assumes that the mail name is
105 : prefixed with an '<'. However the keybox code used for sm/
106 : assumes it has been removed. For now we use this simple hack
107 : to overcome the problem. */
108 0 : if (!openpgp_hack)
109 0 : s++;
110 0 : desc->u.name = s;
111 0 : break;
112 :
113 : case '@': /* Part of an email address. */
114 0 : mode = KEYDB_SEARCH_MODE_MAILSUB;
115 0 : s++;
116 0 : desc->u.name = s;
117 0 : break;
118 :
119 : case '=': /* Exact compare. */
120 0 : mode = KEYDB_SEARCH_MODE_EXACT;
121 0 : s++;
122 0 : desc->u.name = s;
123 0 : break;
124 :
125 : case '*': /* Case insensitive substring search. */
126 0 : mode = KEYDB_SEARCH_MODE_SUBSTR;
127 0 : s++;
128 0 : desc->u.name = s;
129 0 : break;
130 :
131 : case '+': /* Compare individual words. Note that this has not
132 : yet been implemented in the search code. */
133 0 : mode = KEYDB_SEARCH_MODE_WORDS;
134 0 : s++;
135 0 : desc->u.name = s;
136 0 : break;
137 :
138 : case '/': /* Subject's DN. */
139 0 : s++;
140 0 : if (!*s || spacep (s)) /* No DN or prefixed with a space. */
141 0 : return gpg_error (GPG_ERR_INV_USER_ID);
142 0 : desc->u.name = s;
143 0 : mode = KEYDB_SEARCH_MODE_SUBJECT;
144 0 : break;
145 :
146 : case '#': /* S/N with optional issuer id or just issuer id. */
147 : {
148 : const char *si;
149 :
150 0 : s++;
151 0 : if ( *s == '/')
152 : { /* "#/" indicates an issuer's DN. */
153 0 : s++;
154 0 : if (!*s || spacep (s)) /* No DN or prefixed with a space. */
155 0 : return gpg_error (GPG_ERR_INV_USER_ID);
156 0 : desc->u.name = s;
157 0 : mode = KEYDB_SEARCH_MODE_ISSUER;
158 : }
159 : else
160 : { /* Serialnumber + optional issuer ID. */
161 0 : for (si=s; *si && *si != '/'; si++)
162 : {
163 : /* Check for an invalid digit in the serial number. */
164 0 : if (!strchr("01234567890abcdefABCDEF", *si))
165 0 : return gpg_error (GPG_ERR_INV_USER_ID);
166 : }
167 0 : desc->sn = (const unsigned char*)s;
168 0 : desc->snlen = -1;
169 0 : if (!*si)
170 0 : mode = KEYDB_SEARCH_MODE_SN;
171 : else
172 : {
173 0 : s = si+1;
174 0 : if (!*s || spacep (s)) /* No DN or prefixed with a space. */
175 0 : return gpg_error (GPG_ERR_INV_USER_ID);
176 0 : desc->u.name = s;
177 0 : mode = KEYDB_SEARCH_MODE_ISSUER_SN;
178 : }
179 : }
180 : }
181 0 : break;
182 :
183 : case ':': /* Unified fingerprint. */
184 : {
185 : const char *se, *si;
186 : int i;
187 :
188 0 : se = strchr (++s,':');
189 0 : if (!se)
190 0 : return gpg_error (GPG_ERR_INV_USER_ID);
191 0 : for (i=0,si=s; si < se; si++, i++ )
192 : {
193 0 : if (!strchr("01234567890abcdefABCDEF", *si))
194 0 : return gpg_error (GPG_ERR_INV_USER_ID); /* Invalid digit. */
195 : }
196 0 : if (i != 32 && i != 40)
197 0 : return gpg_error (GPG_ERR_INV_USER_ID); /* Invalid length of fpr. */
198 0 : for (i=0,si=s; si < se; i++, si +=2)
199 0 : desc->u.fpr[i] = hextobyte(si);
200 0 : for (; i < 20; i++)
201 0 : desc->u.fpr[i]= 0;
202 0 : s = se + 1;
203 0 : mode = KEYDB_SEARCH_MODE_FPR;
204 : }
205 0 : break;
206 :
207 : case '&': /* Keygrip*/
208 : {
209 0 : if (hex2bin (s+1, desc->u.grip, 20) < 0)
210 0 : return gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */
211 0 : mode = KEYDB_SEARCH_MODE_KEYGRIP;
212 : }
213 0 : break;
214 :
215 : default:
216 419 : if (s[0] == '0' && s[1] == 'x')
217 : {
218 95 : hexprefix = 1;
219 95 : s += 2;
220 : }
221 :
222 419 : hexlength = strspn(s, "0123456789abcdefABCDEF");
223 419 : if (hexlength >= 8 && s[hexlength] =='!')
224 : {
225 0 : desc->exact = 1;
226 0 : hexlength++; /* Just for the following check. */
227 : }
228 :
229 : /* Check if a hexadecimal number is terminated by EOS or blank. */
230 419 : if (hexlength && s[hexlength] && !spacep (s+hexlength))
231 : {
232 0 : if (hexprefix) /* A "0x" prefix without a correct
233 : termination is an error. */
234 0 : return gpg_error (GPG_ERR_INV_USER_ID);
235 : /* The first characters looked like a hex number, but the
236 : entire string is not. */
237 0 : hexlength = 0;
238 : }
239 :
240 419 : if (desc->exact)
241 0 : hexlength--; /* Remove the bang. */
242 :
243 419 : if (hexlength == 8
244 183 : || (!hexprefix && hexlength == 9 && *s == '0'))
245 : {
246 : /* Short keyid. */
247 236 : if (hexlength == 9)
248 0 : s++;
249 236 : desc->u.kid[1] = strtoul( s, NULL, 16 );
250 236 : mode = KEYDB_SEARCH_MODE_SHORT_KID;
251 : }
252 183 : else if (hexlength == 16
253 151 : || (!hexprefix && hexlength == 17 && *s == '0'))
254 32 : {
255 : /* Long keyid. */
256 : char buf[9];
257 32 : if (hexlength == 17)
258 0 : s++;
259 32 : mem2str (buf, s, 9);
260 32 : desc->u.kid[0] = strtoul (buf, NULL, 16);
261 32 : desc->u.kid[1] = strtoul (s+8, NULL, 16);
262 32 : mode = KEYDB_SEARCH_MODE_LONG_KID;
263 : }
264 151 : else if (hexlength == 32
265 151 : || (!hexprefix && hexlength == 33 && *s == '0'))
266 0 : {
267 : /* MD5 fingerprint. */
268 : int i;
269 0 : if (hexlength == 33)
270 0 : s++;
271 0 : memset (desc->u.fpr+16, 0, 4);
272 0 : for (i=0; i < 16; i++, s+=2)
273 : {
274 0 : int c = hextobyte(s);
275 0 : if (c == -1)
276 0 : return gpg_error (GPG_ERR_INV_USER_ID);
277 0 : desc->u.fpr[i] = c;
278 : }
279 0 : mode = KEYDB_SEARCH_MODE_FPR16;
280 : }
281 151 : else if (hexlength == 40
282 148 : || (!hexprefix && hexlength == 41 && *s == '0'))
283 3 : {
284 : /* SHA1/RMD160 fingerprint. */
285 : int i;
286 3 : if (hexlength == 41)
287 0 : s++;
288 63 : for (i=0; i < 20; i++, s+=2)
289 : {
290 60 : int c = hextobyte(s);
291 60 : if (c == -1)
292 0 : return gpg_error (GPG_ERR_INV_USER_ID);
293 60 : desc->u.fpr[i] = c;
294 : }
295 3 : mode = KEYDB_SEARCH_MODE_FPR20;
296 : }
297 148 : else if (!hexprefix)
298 : {
299 : /* The fingerprint in an X.509 listing is often delimited by
300 : colons, so we try to single this case out. */
301 148 : mode = 0;
302 148 : hexlength = strspn (s, ":0123456789abcdefABCDEF");
303 148 : if (hexlength == 59 && (!s[hexlength] || spacep (s+hexlength)))
304 : {
305 : int i;
306 :
307 0 : for (i=0; i < 20; i++, s += 3)
308 : {
309 0 : int c = hextobyte(s);
310 0 : if (c == -1 || (i < 19 && s[2] != ':'))
311 : break;
312 0 : desc->u.fpr[i] = c;
313 : }
314 0 : if (i == 20)
315 0 : mode = KEYDB_SEARCH_MODE_FPR20;
316 : }
317 148 : if (!mode)
318 : {
319 : /* Still not found. Now check for a space separated
320 : OpenPGP v4 fingerprint like:
321 : 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367
322 : or
323 : 8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367
324 : */
325 148 : hexlength = strspn (s, " 0123456789abcdefABCDEF");
326 148 : if (s[hexlength] && s[hexlength] != ' ')
327 146 : hexlength = 0; /* Followed by non-space. */
328 296 : while (hexlength && s[hexlength-1] == ' ')
329 0 : hexlength--; /* Trim trailing spaces. */
330 148 : if ((hexlength == 49 || hexlength == 50)
331 2 : && (!s[hexlength] || s[hexlength] == ' '))
332 : {
333 : int i, c;
334 :
335 42 : for (i=0; i < 20; i++)
336 : {
337 40 : if (i && !(i % 2))
338 : {
339 18 : if (*s != ' ')
340 0 : break;
341 18 : s++;
342 : /* Skip the double space in the middle but
343 : don't require it to help copying
344 : fingerprints from sources which fold
345 : multiple space to one. */
346 18 : if (i == 10 && *s == ' ')
347 2 : s++;
348 : }
349 :
350 40 : c = hextobyte(s);
351 40 : if (c == -1)
352 0 : break;
353 40 : desc->u.fpr[i] = c;
354 40 : s += 2;
355 : }
356 2 : if (i == 20)
357 2 : mode = KEYDB_SEARCH_MODE_FPR20;
358 : }
359 : }
360 148 : if (!mode) /* Default to substring search. */
361 : {
362 146 : desc->exact = 0;
363 146 : desc->u.name = s;
364 146 : mode = KEYDB_SEARCH_MODE_SUBSTR;
365 : }
366 : }
367 : else
368 : {
369 : /* Hex number with a prefix but with a wrong length. */
370 0 : return gpg_error (GPG_ERR_INV_USER_ID);
371 : }
372 : }
373 :
374 419 : desc->mode = mode;
375 419 : return 0;
376 : }
|