Implement decryption for TCOS 3 cards.
[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 <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 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 coccured */
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 coccured */
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 coccured */
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       log_error ("error reading certificate: %s\n",
266                  gpg_strerror (rc));
267       return rc;
268     }
269
270   rc = assuan_send_data (assuan_context, derbuf, derbuflen);
271   xfree (derbuf);
272   if (!rc)
273     rc = assuan_send_data (assuan_context, NULL, 0);
274   if (!rc)
275     rc = assuan_write_line (assuan_context, "END");
276   if (rc)
277     {
278       log_error ("sending certificate failed: %s\n",
279                  gpg_strerror (rc));
280       return rc;
281     }
282   return 0;
283 }
284
285 /* Perform the learn operation.  If ASSUAN_CONTEXT is not NULL all new
286    certificates are send back via Assuan.  */
287 int
288 agent_handle_learn (ctrl_t ctrl, void *assuan_context)
289 {
290   int rc;
291   struct kpinfo_cb_parm_s parm;
292   struct certinfo_cb_parm_s cparm;
293   struct sinfo_cb_parm_s sparm;
294   char *serialno = NULL;
295   KEYPAIR_INFO item;
296   SINFO sitem;
297   unsigned char grip[20];
298   char *p;
299   int i;
300   static int certtype_list[] = { 
301     111, /* Root CA */
302     101, /* trusted */
303     102, /* useful */
304     100, /* regular */
305     /* We don't include 110 here because gpgsm can't handle that
306        special root CA format. */
307     -1 /* end of list */
308   };
309
310
311   memset (&parm, 0, sizeof parm);
312   memset (&cparm, 0, sizeof cparm);
313   memset (&sparm, 0, sizeof sparm);
314   parm.ctrl = ctrl;
315   cparm.ctrl = ctrl;
316
317   /* Check whether a card is present and get the serial number */
318   rc = agent_card_serialno (ctrl, &serialno);
319   if (rc)
320     goto leave;
321
322   /* Now gather all the available info. */
323   rc = agent_card_learn (ctrl, kpinfo_cb, &parm, certinfo_cb, &cparm,
324                          sinfo_cb, &sparm);
325   if (!rc && (parm.error || cparm.error || sparm.error))
326     rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error;
327   if (rc)
328     {
329       log_debug ("agent_card_learn failed: %s\n", gpg_strerror (rc));
330       goto leave;
331     }
332   
333   log_info ("card has S/N: %s\n", serialno);
334
335   /* Pass on all the collected status information. */
336   if (assuan_context)
337     {
338       for (sitem = sparm.info; sitem; sitem = sitem->next)
339         {
340           assuan_write_status (assuan_context, sitem->keyword, sitem->data);
341         }
342     }
343
344   /* Write out the certificates in a standard order. */
345   for (i=0; certtype_list[i] != -1; i++)
346     {
347       CERTINFO citem;
348       for (citem = cparm.info; citem; citem = citem->next)
349         {
350           if (certtype_list[i] != citem->type)
351             continue;
352
353           if (opt.verbose)
354             log_info ("          id: %s    (type=%d)\n",
355                       citem->id, citem->type);
356           
357           if (assuan_context)
358             {
359               rc = send_cert_back (ctrl, citem->id, assuan_context);
360               if (rc)
361                 goto leave;
362               citem->done = 1;
363             }
364         }
365     }
366   
367   for (item = parm.info; item; item = item->next)
368     {
369       unsigned char *pubkey, *shdkey;
370       size_t n;
371
372       if (opt.verbose)
373         log_info ("          id: %s    (grip=%s)\n", item->id, item->hexgrip);
374
375       if (item->no_cert)
376         continue; /* No public key yet available. */
377
378       if (assuan_context)
379         {
380           agent_write_status (ctrl, "KEYPAIRINFO",
381                               item->hexgrip, item->id, NULL);
382         }
383
384       for (p=item->hexgrip, i=0; i < 20; p += 2, i++)
385         grip[i] = xtoi_2 (p);
386       
387       if (!agent_key_available (grip))
388         continue; /* The key is already available. */
389       
390       /* Unknown key - store it. */
391       rc = agent_card_readkey (ctrl, item->id, &pubkey);
392       if (rc)
393         {
394           log_debug ("agent_card_readkey failed: %s\n", gpg_strerror (rc));
395           goto leave;
396         }
397
398       {
399         unsigned char *shadow_info = make_shadow_info (serialno, item->id);
400         if (!shadow_info)
401           {
402             rc = gpg_error (GPG_ERR_ENOMEM);
403             xfree (pubkey);
404             goto leave;
405           }
406         rc = agent_shadow_key (pubkey, shadow_info, &shdkey);
407         xfree (shadow_info);
408       }
409       xfree (pubkey);
410       if (rc)
411         {
412           log_error ("shadowing the key failed: %s\n", gpg_strerror (rc));
413           goto leave;
414         }
415       n = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
416       assert (n);
417
418       rc = agent_write_private_key (grip, shdkey, n, 0);
419       xfree (shdkey);
420       if (rc)
421         {
422           log_error ("error writing key: %s\n", gpg_strerror (rc));
423           goto leave;
424         }
425
426       if (opt.verbose)
427         log_info ("stored\n");
428       
429       if (assuan_context)
430         {
431           CERTINFO citem;
432           
433           /* only send the certificate if we have not done so before */
434           for (citem = cparm.info; citem; citem = citem->next)
435             {
436               if (!strcmp (citem->id, item->id))
437                 break;
438             }
439           if (!citem)
440             {
441               rc = send_cert_back (ctrl, item->id, assuan_context);
442               if (rc)
443                 goto leave;
444             }
445         }
446     }
447
448   
449  leave:
450   xfree (serialno);
451   release_keypair_info (parm.info);
452   release_certinfo (cparm.info);
453   release_sinfo (sparm.info);
454   return rc;
455 }
456
457