gpg: Fix memory leak in parse_auto_key_locate.
[gnupg.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   struct kpinfo_cb_parm_s parm;
306   struct certinfo_cb_parm_s cparm;
307   struct sinfo_cb_parm_s sparm;
308   const char *serialno = NULL;
309   KEYPAIR_INFO item;
310   SINFO sitem;
311   unsigned char grip[20];
312   char *p;
313   int i;
314   static int certtype_list[] = {
315     111, /* Root CA */
316     101, /* trusted */
317     102, /* useful */
318     100, /* regular */
319     /* We don't include 110 here because gpgsm can't handle that
320        special root CA format. */
321     -1 /* end of list */
322   };
323
324
325   memset (&parm, 0, sizeof parm);
326   memset (&cparm, 0, sizeof cparm);
327   memset (&sparm, 0, sizeof sparm);
328   parm.ctrl = ctrl;
329   cparm.ctrl = ctrl;
330
331   /* Now gather all the available info. */
332   rc = agent_card_learn (ctrl, kpinfo_cb, &parm, certinfo_cb, &cparm,
333                          sinfo_cb, &sparm);
334   if (!rc && (parm.error || cparm.error || sparm.error))
335     rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error;
336   if (rc)
337     {
338       log_debug ("agent_card_learn failed: %s\n", gpg_strerror (rc));
339       goto leave;
340     }
341
342   /* Pass on all the collected status information. */
343   if (assuan_context)
344     {
345       for (sitem = sparm.info; sitem; sitem = sitem->next)
346         {
347           if (!strcmp (sitem->keyword, "SERIALNO"))
348             serialno = sitem->data;
349           assuan_write_status (assuan_context, sitem->keyword, sitem->data);
350         }
351     }
352
353   if (!serialno)
354     {
355       rc = GPG_ERR_NOT_FOUND;
356       goto leave;
357     }
358
359   log_info ("card has S/N: %s\n", serialno);
360
361   /* Write out the certificates in a standard order. */
362   for (i=0; certtype_list[i] != -1; i++)
363     {
364       CERTINFO citem;
365       for (citem = cparm.info; citem; citem = citem->next)
366         {
367           if (certtype_list[i] != citem->type)
368             continue;
369
370           if (opt.verbose)
371             log_info ("          id: %s    (type=%d)\n",
372                       citem->id, citem->type);
373
374           if (assuan_context && send)
375             {
376               rc = send_cert_back (ctrl, citem->id, assuan_context);
377               if (rc)
378                 goto leave;
379               citem->done = 1;
380             }
381         }
382     }
383
384   for (item = parm.info; item; item = item->next)
385     {
386       unsigned char *pubkey;
387
388       if (opt.verbose)
389         log_info ("          id: %s    (grip=%s)\n", item->id, item->hexgrip);
390
391       if (item->no_cert)
392         continue; /* No public key yet available. */
393
394       if (assuan_context)
395         {
396           agent_write_status (ctrl, "KEYPAIRINFO",
397                               item->hexgrip, item->id, NULL);
398         }
399
400       for (p=item->hexgrip, i=0; i < 20; p += 2, i++)
401         grip[i] = xtoi_2 (p);
402
403       if (!force && !agent_key_available (grip))
404         continue; /* The key is already available. */
405
406       /* Unknown key - store it. */
407       rc = agent_card_readkey (ctrl, item->id, &pubkey);
408       if (rc)
409         {
410           log_debug ("agent_card_readkey failed: %s\n", gpg_strerror (rc));
411           goto leave;
412         }
413
414       rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, force);
415       xfree (pubkey);
416       if (rc)
417         goto leave;
418
419       if (opt.verbose)
420         log_info ("          id: %s - shadow key created\n", item->id);
421
422       if (assuan_context && send)
423         {
424           CERTINFO citem;
425
426           /* only send the certificate if we have not done so before */
427           for (citem = cparm.info; citem; citem = citem->next)
428             {
429               if (!strcmp (citem->id, item->id))
430                 break;
431             }
432           if (!citem)
433             {
434               rc = send_cert_back (ctrl, item->id, assuan_context);
435               if (rc)
436                 goto leave;
437             }
438         }
439     }
440
441
442  leave:
443   release_keypair_info (parm.info);
444   release_certinfo (cparm.info);
445   release_sinfo (sparm.info);
446   return rc;
447 }