Line data Source code
1 : /* learncard.c - Handle the LEARN command
2 : * Copyright (C) 2002, 2003, 2004, 2009 Free Software Foundation, Inc.
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * GnuPG is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * GnuPG is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include <config.h>
21 : #include <errno.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 : #include <ctype.h>
26 : #include <assert.h>
27 : #include <unistd.h>
28 : #include <sys/stat.h>
29 :
30 : #include "agent.h"
31 : #include <assuan.h>
32 :
33 : /* Structures used by the callback mechanism to convey information
34 : pertaining to key pairs. */
35 : struct keypair_info_s
36 : {
37 : struct keypair_info_s *next;
38 : int no_cert;
39 : char *id; /* points into grip */
40 : char hexgrip[1]; /* The keygrip (i.e. a hash over the public key
41 : parameters) formatted as a hex string.
42 : Allocated somewhat large to also act as
43 : memeory for the above ID field. */
44 : };
45 : typedef struct keypair_info_s *KEYPAIR_INFO;
46 :
47 : struct kpinfo_cb_parm_s
48 : {
49 : ctrl_t ctrl;
50 : int error;
51 : KEYPAIR_INFO info;
52 : };
53 :
54 :
55 : /* Structures used by the callback mechanism to convey information
56 : pertaining to certificates. */
57 : struct certinfo_s {
58 : struct certinfo_s *next;
59 : int type;
60 : int done;
61 : char id[1];
62 : };
63 : typedef struct certinfo_s *CERTINFO;
64 :
65 : struct certinfo_cb_parm_s
66 : {
67 : ctrl_t ctrl;
68 : int error;
69 : CERTINFO info;
70 : };
71 :
72 :
73 : /* Structures used by the callback mechanism to convey assuan status
74 : lines. */
75 : struct sinfo_s {
76 : struct sinfo_s *next;
77 : char *data; /* Points into keyword. */
78 : char keyword[1];
79 : };
80 : typedef struct sinfo_s *SINFO;
81 :
82 : struct sinfo_cb_parm_s {
83 : int error;
84 : SINFO info;
85 : };
86 :
87 :
88 : /* Destructor for key information objects. */
89 : static void
90 0 : release_keypair_info (KEYPAIR_INFO info)
91 : {
92 0 : while (info)
93 : {
94 0 : KEYPAIR_INFO tmp = info->next;
95 0 : xfree (info);
96 0 : info = tmp;
97 : }
98 0 : }
99 :
100 : /* Destructor for certificate information objects. */
101 : static void
102 0 : release_certinfo (CERTINFO info)
103 : {
104 0 : while (info)
105 : {
106 0 : CERTINFO tmp = info->next;
107 0 : xfree (info);
108 0 : info = tmp;
109 : }
110 0 : }
111 :
112 : /* Destructor for status information objects. */
113 : static void
114 0 : release_sinfo (SINFO info)
115 : {
116 0 : while (info)
117 : {
118 0 : SINFO tmp = info->next;
119 0 : xfree (info);
120 0 : info = tmp;
121 : }
122 0 : }
123 :
124 :
125 :
126 : /* This callback is used by agent_card_learn and passed the content of
127 : all KEYPAIRINFO lines. It merely stores this data away */
128 : static void
129 0 : kpinfo_cb (void *opaque, const char *line)
130 : {
131 0 : struct kpinfo_cb_parm_s *parm = opaque;
132 : KEYPAIR_INFO item;
133 : char *p;
134 :
135 0 : if (parm->error)
136 0 : return; /* no need to gather data after an error coccured */
137 :
138 0 : if ((parm->error = agent_write_status (parm->ctrl, "PROGRESS",
139 : "learncard", "k", "0", "0", NULL)))
140 0 : return;
141 :
142 0 : item = xtrycalloc (1, sizeof *item + strlen (line));
143 0 : if (!item)
144 : {
145 0 : parm->error = out_of_core ();
146 0 : return;
147 : }
148 0 : strcpy (item->hexgrip, line);
149 0 : for (p = item->hexgrip; hexdigitp (p); p++)
150 : ;
151 0 : if (p == item->hexgrip && *p == 'X' && spacep (p+1))
152 : {
153 0 : item->no_cert = 1;
154 0 : p++;
155 : }
156 0 : else if ((p - item->hexgrip) != 40 || !spacep (p))
157 : { /* not a 20 byte hex keygrip or not followed by a space */
158 0 : parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
159 0 : xfree (item);
160 0 : return;
161 : }
162 0 : *p++ = 0;
163 0 : while (spacep (p))
164 0 : p++;
165 0 : item->id = p;
166 0 : while (*p && !spacep (p))
167 0 : p++;
168 0 : if (p == item->id)
169 : { /* invalid ID string */
170 0 : parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
171 0 : xfree (item);
172 0 : return;
173 : }
174 0 : *p = 0; /* ignore trailing stuff */
175 :
176 : /* store it */
177 0 : item->next = parm->info;
178 0 : parm->info = item;
179 : }
180 :
181 :
182 : /* This callback is used by agent_card_learn and passed the content of
183 : all CERTINFO lines. It merely stores this data away */
184 : static void
185 0 : certinfo_cb (void *opaque, const char *line)
186 : {
187 0 : struct certinfo_cb_parm_s *parm = opaque;
188 : CERTINFO item;
189 : int type;
190 : char *p, *pend;
191 :
192 0 : if (parm->error)
193 0 : return; /* no need to gather data after an error coccured */
194 :
195 0 : if ((parm->error = agent_write_status (parm->ctrl, "PROGRESS",
196 : "learncard", "c", "0", "0", NULL)))
197 0 : return;
198 :
199 0 : type = strtol (line, &p, 10);
200 0 : while (spacep (p))
201 0 : p++;
202 0 : for (pend = p; *pend && !spacep (pend); pend++)
203 : ;
204 0 : if (p == pend || !*p)
205 : {
206 0 : parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
207 0 : return;
208 : }
209 0 : *pend = 0; /* ignore trailing stuff */
210 :
211 0 : item = xtrycalloc (1, sizeof *item + strlen (p));
212 0 : if (!item)
213 : {
214 0 : parm->error = out_of_core ();
215 0 : return;
216 : }
217 0 : item->type = type;
218 0 : strcpy (item->id, p);
219 : /* store it */
220 0 : item->next = parm->info;
221 0 : parm->info = item;
222 : }
223 :
224 :
225 : /* This callback is used by agent_card_learn and passed the content of
226 : all SINFO lines. It merely stores this data away */
227 : static void
228 0 : sinfo_cb (void *opaque, const char *keyword, size_t keywordlen,
229 : const char *data)
230 : {
231 0 : struct sinfo_cb_parm_s *sparm = opaque;
232 : SINFO item;
233 :
234 0 : if (sparm->error)
235 0 : return; /* no need to gather data after an error coccured */
236 :
237 0 : item = xtrycalloc (1, sizeof *item + keywordlen + 1 + strlen (data));
238 0 : if (!item)
239 : {
240 0 : sparm->error = out_of_core ();
241 0 : return;
242 : }
243 0 : memcpy (item->keyword, keyword, keywordlen);
244 0 : item->data = item->keyword + keywordlen;
245 0 : *item->data = 0;
246 0 : item->data++;
247 0 : strcpy (item->data, data);
248 : /* store it */
249 0 : item->next = sparm->info;
250 0 : sparm->info = item;
251 : }
252 :
253 :
254 :
255 : static int
256 0 : send_cert_back (ctrl_t ctrl, const char *id, void *assuan_context)
257 : {
258 : int rc;
259 : char *derbuf;
260 : size_t derbuflen;
261 :
262 0 : rc = agent_card_readcert (ctrl, id, &derbuf, &derbuflen);
263 0 : if (rc)
264 : {
265 : const char *action;
266 :
267 0 : switch (gpg_err_code (rc))
268 : {
269 : case GPG_ERR_INV_ID:
270 : case GPG_ERR_NOT_FOUND:
271 0 : action = " - ignored";
272 0 : break;
273 : default:
274 0 : action = "";
275 0 : break;
276 : }
277 0 : if (opt.verbose || !*action)
278 0 : log_info ("error reading certificate '%s': %s%s\n",
279 : id? id:"?", gpg_strerror (rc), action);
280 :
281 0 : return *action? 0 : rc;
282 : }
283 :
284 0 : rc = assuan_send_data (assuan_context, derbuf, derbuflen);
285 0 : xfree (derbuf);
286 0 : if (!rc)
287 0 : rc = assuan_send_data (assuan_context, NULL, 0);
288 0 : if (!rc)
289 0 : rc = assuan_write_line (assuan_context, "END");
290 0 : if (rc)
291 : {
292 0 : log_error ("sending certificate failed: %s\n",
293 : gpg_strerror (rc));
294 0 : return rc;
295 : }
296 0 : return 0;
297 : }
298 :
299 : /* Perform the learn operation. If ASSUAN_CONTEXT is not NULL and
300 : SEND is true all new certificates are send back via Assuan. */
301 : int
302 0 : agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
303 : {
304 : int rc;
305 :
306 : struct kpinfo_cb_parm_s parm;
307 : struct certinfo_cb_parm_s cparm;
308 : struct sinfo_cb_parm_s sparm;
309 0 : char *serialno = NULL;
310 : KEYPAIR_INFO item;
311 : SINFO sitem;
312 : unsigned char grip[20];
313 : char *p;
314 : int i;
315 : static int certtype_list[] = {
316 : 111, /* Root CA */
317 : 101, /* trusted */
318 : 102, /* useful */
319 : 100, /* regular */
320 : /* We don't include 110 here because gpgsm can't handle that
321 : special root CA format. */
322 : -1 /* end of list */
323 : };
324 :
325 :
326 0 : memset (&parm, 0, sizeof parm);
327 0 : memset (&cparm, 0, sizeof cparm);
328 0 : memset (&sparm, 0, sizeof sparm);
329 0 : parm.ctrl = ctrl;
330 0 : cparm.ctrl = ctrl;
331 :
332 : /* Check whether a card is present and get the serial number */
333 0 : rc = agent_card_serialno (ctrl, &serialno);
334 0 : if (rc)
335 0 : goto leave;
336 :
337 : /* Now gather all the available info. */
338 0 : rc = agent_card_learn (ctrl, kpinfo_cb, &parm, certinfo_cb, &cparm,
339 : sinfo_cb, &sparm);
340 0 : if (!rc && (parm.error || cparm.error || sparm.error))
341 0 : rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error;
342 0 : if (rc)
343 : {
344 0 : log_debug ("agent_card_learn failed: %s\n", gpg_strerror (rc));
345 0 : goto leave;
346 : }
347 :
348 0 : log_info ("card has S/N: %s\n", serialno);
349 :
350 : /* Pass on all the collected status information. */
351 0 : if (assuan_context)
352 : {
353 0 : for (sitem = sparm.info; sitem; sitem = sitem->next)
354 : {
355 0 : assuan_write_status (assuan_context, sitem->keyword, sitem->data);
356 : }
357 : }
358 :
359 : /* Write out the certificates in a standard order. */
360 0 : for (i=0; certtype_list[i] != -1; i++)
361 : {
362 : CERTINFO citem;
363 0 : for (citem = cparm.info; citem; citem = citem->next)
364 : {
365 0 : if (certtype_list[i] != citem->type)
366 0 : continue;
367 :
368 0 : if (opt.verbose)
369 0 : log_info (" id: %s (type=%d)\n",
370 0 : citem->id, citem->type);
371 :
372 0 : if (assuan_context && send)
373 : {
374 0 : rc = send_cert_back (ctrl, citem->id, assuan_context);
375 0 : if (rc)
376 0 : goto leave;
377 0 : citem->done = 1;
378 : }
379 : }
380 : }
381 :
382 0 : for (item = parm.info; item; item = item->next)
383 : {
384 : unsigned char *pubkey, *shdkey;
385 : size_t n;
386 :
387 0 : if (opt.verbose)
388 0 : log_info (" id: %s (grip=%s)\n", item->id, item->hexgrip);
389 :
390 0 : if (item->no_cert)
391 0 : continue; /* No public key yet available. */
392 :
393 0 : if (assuan_context)
394 : {
395 0 : agent_write_status (ctrl, "KEYPAIRINFO",
396 0 : item->hexgrip, item->id, NULL);
397 : }
398 :
399 0 : for (p=item->hexgrip, i=0; i < 20; p += 2, i++)
400 0 : grip[i] = xtoi_2 (p);
401 :
402 0 : if (!force && !agent_key_available (grip))
403 0 : continue; /* The key is already available. */
404 :
405 : /* Unknown key - store it. */
406 0 : rc = agent_card_readkey (ctrl, item->id, &pubkey);
407 0 : if (rc)
408 : {
409 0 : log_debug ("agent_card_readkey failed: %s\n", gpg_strerror (rc));
410 0 : goto leave;
411 : }
412 :
413 : {
414 0 : unsigned char *shadow_info = make_shadow_info (serialno, item->id);
415 0 : if (!shadow_info)
416 : {
417 0 : rc = gpg_error (GPG_ERR_ENOMEM);
418 0 : xfree (pubkey);
419 0 : goto leave;
420 : }
421 0 : rc = agent_shadow_key (pubkey, shadow_info, &shdkey);
422 0 : xfree (shadow_info);
423 : }
424 0 : xfree (pubkey);
425 0 : if (rc)
426 : {
427 0 : log_error ("shadowing the key failed: %s\n", gpg_strerror (rc));
428 0 : goto leave;
429 : }
430 0 : n = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
431 0 : assert (n);
432 :
433 0 : rc = agent_write_private_key (grip, shdkey, n, force);
434 0 : xfree (shdkey);
435 0 : if (rc)
436 : {
437 0 : log_error ("error writing key: %s\n", gpg_strerror (rc));
438 0 : goto leave;
439 : }
440 :
441 0 : if (opt.verbose)
442 0 : log_info (" id: %s - shadow key created\n", item->id);
443 :
444 0 : if (assuan_context && send)
445 : {
446 : CERTINFO citem;
447 :
448 : /* only send the certificate if we have not done so before */
449 0 : for (citem = cparm.info; citem; citem = citem->next)
450 : {
451 0 : if (!strcmp (citem->id, item->id))
452 0 : break;
453 : }
454 0 : if (!citem)
455 : {
456 0 : rc = send_cert_back (ctrl, item->id, assuan_context);
457 0 : if (rc)
458 0 : goto leave;
459 : }
460 : }
461 : }
462 :
463 :
464 : leave:
465 0 : xfree (serialno);
466 0 : release_keypair_info (parm.info);
467 0 : release_certinfo (cparm.info);
468 0 : release_sinfo (sparm.info);
469 0 : return rc;
470 : }
|