chiark / gitweb /
agent: Avoid tight timer tick when possible.
[gnupg2.git] / agent / learncard.c
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 <https://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 release_keypair_info (KEYPAIR_INFO info)
91 {
92   while (info)
93     {
94       KEYPAIR_INFO tmp = info->next;
95       xfree (info);
96       info = tmp;
97     }
98 }
99
100 /* Destructor for certificate information objects. */
101 static void
102 release_certinfo (CERTINFO info)
103 {
104   while (info)
105     {
106       CERTINFO tmp = info->next;
107       xfree (info);
108       info = tmp;
109     }
110 }
111
112 /* Destructor for status information objects. */
113 static void
114 release_sinfo (SINFO info)
115 {
116   while (info)
117     {
118       SINFO tmp = info->next;
119       xfree (info);
120       info = tmp;
121     }
122 }
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 kpinfo_cb (void *opaque, const char *line)
130 {
131   struct kpinfo_cb_parm_s *parm = opaque;
132   KEYPAIR_INFO item;
133   char *p;
134
135   if (parm->error)
136     return; /* no need to gather data after an error occurred */
137
138   if ((parm->error = agent_write_status (parm->ctrl, "PROGRESS",
139                                          "learncard", "k", "0", "0", NULL)))
140     return;
141
142   item = xtrycalloc (1, sizeof *item + strlen (line));
143   if (!item)
144     {
145       parm->error = out_of_core ();
146       return;
147     }
148   strcpy (item->hexgrip, line);
149   for (p = item->hexgrip; hexdigitp (p); p++)
150     ;
151   if (p == item->hexgrip && *p == 'X' && spacep (p+1))
152     {
153       item->no_cert = 1;
154       p++;
155     }
156   else if ((p - item->hexgrip) != 40 || !spacep (p))
157     { /* not a 20 byte hex keygrip or not followed by a space */
158       parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
159       xfree (item);
160       return;
161     }
162   *p++ = 0;
163   while (spacep (p))
164     p++;
165   item->id = p;
166   while (*p && !spacep (p))
167     p++;
168   if (p == item->id)
169     { /* invalid ID string */
170       parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
171       xfree (item);
172       return;
173     }
174   *p = 0; /* ignore trailing stuff */
175
176   /* store it */
177   item->next = parm->info;
178   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 certinfo_cb (void *opaque, const char *line)
186 {
187   struct certinfo_cb_parm_s *parm = opaque;
188   CERTINFO item;
189   int type;
190   char *p, *pend;
191
192   if (parm->error)
193     return; /* no need to gather data after an error occurred */
194
195   if ((parm->error = agent_write_status (parm->ctrl, "PROGRESS",
196                                          "learncard", "c", "0", "0", NULL)))
197     return;
198
199   type = strtol (line, &p, 10);
200   while (spacep (p))
201     p++;
202   for (pend = p; *pend && !spacep (pend); pend++)
203     ;
204   if (p == pend || !*p)
205     {
206       parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
207       return;
208     }
209   *pend = 0; /* ignore trailing stuff */
210
211   item = xtrycalloc (1, sizeof *item + strlen (p));
212   if (!item)
213     {
214       parm->error = out_of_core ();
215       return;
216     }
217   item->type = type;
218   strcpy (item->id, p);
219   /* store it */
220   item->next = parm->info;
221   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 sinfo_cb (void *opaque, const char *keyword, size_t keywordlen,
229           const char *data)
230 {
231   struct sinfo_cb_parm_s *sparm = opaque;
232   SINFO item;
233
234   if (sparm->error)
235     return; /* no need to gather data after an error occurred */
236
237   item = xtrycalloc (1, sizeof *item + keywordlen + 1 + strlen (data));
238   if (!item)
239     {
240       sparm->error = out_of_core ();
241       return;
242     }
243   memcpy (item->keyword, keyword, keywordlen);
244   item->data = item->keyword + keywordlen;
245   *item->data = 0;
246   item->data++;
247   strcpy (item->data, data);
248   /* store it */
249   item->next = sparm->info;
250   sparm->info = item;
251 }
252
253
254
255 static int
256 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   rc = agent_card_readcert (ctrl, id, &derbuf, &derbuflen);
263   if (rc)
264     {
265       const char *action;
266
267       switch (gpg_err_code (rc))
268         {
269         case GPG_ERR_INV_ID:
270         case GPG_ERR_NOT_FOUND:
271           action = " - ignored";
272           break;
273         default:
274           action = "";
275           break;
276         }
277       if (opt.verbose || !*action)
278         log_info ("error reading certificate '%s': %s%s\n",
279                   id? id:"?", gpg_strerror (rc), action);
280
281       return *action? 0 : rc;
282     }
283
284   rc = assuan_send_data (assuan_context, derbuf, derbuflen);
285   xfree (derbuf);
286   if (!rc)
287     rc = assuan_send_data (assuan_context, NULL, 0);
288   if (!rc)
289     rc = assuan_write_line (assuan_context, "END");
290   if (rc)
291     {
292       log_error ("sending certificate failed: %s\n",
293                  gpg_strerror (rc));
294       return rc;
295     }
296   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 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   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   memset (&parm, 0, sizeof parm);
327   memset (&cparm, 0, sizeof cparm);
328   memset (&sparm, 0, sizeof sparm);
329   parm.ctrl = ctrl;
330   cparm.ctrl = ctrl;
331
332   /* Check whether a card is present and get the serial number */
333   rc = agent_card_serialno (ctrl, &serialno);
334   if (rc)
335     goto leave;
336
337   /* Now gather all the available info. */
338   rc = agent_card_learn (ctrl, kpinfo_cb, &parm, certinfo_cb, &cparm,
339                          sinfo_cb, &sparm);
340   if (!rc && (parm.error || cparm.error || sparm.error))
341     rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error;
342   if (rc)
343     {
344       log_debug ("agent_card_learn failed: %s\n", gpg_strerror (rc));
345       goto leave;
346     }
347
348   log_info ("card has S/N: %s\n", serialno);
349
350   /* Pass on all the collected status information. */
351   if (assuan_context)
352     {
353       for (sitem = sparm.info; sitem; sitem = sitem->next)
354         {
355           assuan_write_status (assuan_context, sitem->keyword, sitem->data);
356         }
357     }
358
359   /* Write out the certificates in a standard order. */
360   for (i=0; certtype_list[i] != -1; i++)
361     {
362       CERTINFO citem;
363       for (citem = cparm.info; citem; citem = citem->next)
364         {
365           if (certtype_list[i] != citem->type)
366             continue;
367
368           if (opt.verbose)
369             log_info ("          id: %s    (type=%d)\n",
370                       citem->id, citem->type);
371
372           if (assuan_context && send)
373             {
374               rc = send_cert_back (ctrl, citem->id, assuan_context);
375               if (rc)
376                 goto leave;
377               citem->done = 1;
378             }
379         }
380     }
381
382   for (item = parm.info; item; item = item->next)
383     {
384       unsigned char *pubkey;
385
386       if (opt.verbose)
387         log_info ("          id: %s    (grip=%s)\n", item->id, item->hexgrip);
388
389       if (item->no_cert)
390         continue; /* No public key yet available. */
391
392       if (assuan_context)
393         {
394           agent_write_status (ctrl, "KEYPAIRINFO",
395                               item->hexgrip, item->id, NULL);
396         }
397
398       for (p=item->hexgrip, i=0; i < 20; p += 2, i++)
399         grip[i] = xtoi_2 (p);
400
401       if (!force && !agent_key_available (grip))
402         continue; /* The key is already available. */
403
404       /* Unknown key - store it. */
405       rc = agent_card_readkey (ctrl, item->id, &pubkey);
406       if (rc)
407         {
408           log_debug ("agent_card_readkey failed: %s\n", gpg_strerror (rc));
409           goto leave;
410         }
411
412       rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, force);
413       xfree (pubkey);
414       if (rc)
415         goto leave;
416
417       if (opt.verbose)
418         log_info ("          id: %s - shadow key created\n", item->id);
419
420       if (assuan_context && send)
421         {
422           CERTINFO citem;
423
424           /* only send the certificate if we have not done so before */
425           for (citem = cparm.info; citem; citem = citem->next)
426             {
427               if (!strcmp (citem->id, item->id))
428                 break;
429             }
430           if (!citem)
431             {
432               rc = send_cert_back (ctrl, item->id, assuan_context);
433               if (rc)
434                 goto leave;
435             }
436         }
437     }
438
439
440  leave:
441   xfree (serialno);
442   release_keypair_info (parm.info);
443   release_certinfo (cparm.info);
444   release_sinfo (sparm.info);
445   return rc;
446 }