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