sm/
[gnupg.git] / agent / learncard.c
1 /* learncard.c - Handle the LEARN command
2  *      Copyright (C) 2002 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 2 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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30
31 #include "agent.h"
32 #include "../assuan/assuan.h"
33
34 struct keypair_info_s {
35   struct keypair_info_s *next;
36   int no_cert;
37   char *id;  /* points into grip */
38   char hexgrip[1];
39 };
40 typedef struct keypair_info_s *KEYPAIR_INFO;
41
42 struct kpinfo_cb_parm_s {
43   int error;
44   KEYPAIR_INFO info;
45 };
46
47
48 static void
49 release_keypair_info (KEYPAIR_INFO info)
50 {
51   while (info)
52     {
53       KEYPAIR_INFO tmp = info->next;
54       xfree (info);
55       info = tmp;
56     }
57 }
58
59
60
61 /* This callback is used by agent_card_leanr and passed the content of
62    all KEYPAIRINFO lines.  It merely store this data away */
63 static void
64 kpinfo_cb (void *opaque, const char *line)
65 {
66   struct kpinfo_cb_parm_s *parm = opaque;
67   KEYPAIR_INFO item;
68   char *p;
69
70   if (parm->error)
71     return; /* no need to gather data after an error coccured */
72   item = xtrycalloc (1, sizeof *item + strlen (line));
73   if (!item)
74     {
75       parm->error = GNUPG_Out_Of_Core;
76       return;
77     }
78   strcpy (item->hexgrip, line);
79   for (p = item->hexgrip; hexdigitp (p); p++)
80     ;
81   if (p == item->hexgrip && *p == 'X' && spacep (p+1))
82     {
83       item->no_cert = 1;
84       p++;
85     }
86   else if ((p - item->hexgrip) != 40 || !spacep (p))
87     { /* not a 20 byte hex keygrip or now followed by a space */
88       parm->error = GNUPG_Invalid_Response;
89       xfree (item);
90       return;
91     }
92   *p++ = 0;
93   while (spacep (p))
94     p++;
95   item->id = p;
96   for (; hexdigitp (p) || *p == '.'; p++)
97     ;
98   if (!(spacep (p) || !*p))
99     { /* invalid ID string */
100       parm->error = GNUPG_Invalid_Response;
101       xfree (item);
102       return;
103     }
104   *p = 0; /* ignore trailing stuff */
105   
106   /* store it */
107   item->next = parm->info;
108   parm->info = item;
109 }
110
111
112 /* Create an S-expression with the shadow info.  */
113 static unsigned char *
114 make_shadow_info (const char *serialno, const char *idstring)
115 {
116   const char *s;
117   unsigned char *info, *p;
118   char numbuf[21];
119   int n;
120
121   for (s=serialno, n=0; *s && s[1]; s += 2)
122     n++;
123
124   info = p = xtrymalloc (1 + 21 + n
125                            + 21 + strlen (idstring) + 1 + 1);
126   *p++ = '(';
127   sprintf (numbuf, "%d:", n);
128   p = stpcpy (p, numbuf);
129   for (s=serialno; *s && s[1]; s += 2)
130     *p++ = xtoi_2 (s);
131   sprintf (numbuf, "%d:", strlen (idstring));
132   p = stpcpy (p, numbuf);
133   p = stpcpy (p, idstring);
134   *p++ = ')';
135   *p = 0;
136   return info;
137 }
138
139
140 /* Perform the learn operation.  If ASSUAN_CONTEXT is not NULL all new
141    certificates are send via Assuan */
142 int
143 agent_handle_learn (void *assuan_context)
144 {
145   int rc;
146   struct kpinfo_cb_parm_s parm;
147   char *serialno = NULL;
148   KEYPAIR_INFO item;
149   unsigned char grip[20];
150   char *p;
151   int i;
152
153   memset (&parm, 0, sizeof parm);
154
155   /* Check whether a card is present and get the serial number */
156   rc = agent_card_serialno (&serialno);
157   if (rc)
158     goto leave;
159
160   /* now gather all the availabe info */
161   rc = agent_card_learn (kpinfo_cb, &parm);
162   if (!rc && parm.error)
163     rc = parm.error;
164   if (rc)
165     {
166       log_debug ("agent_card_learn failed: %s\n", gnupg_strerror (rc));
167       goto leave;
168     }
169
170   log_info ("card has S/N: %s\n", serialno);
171   for (item = parm.info; item; item = item->next)
172     {
173       unsigned char *pubkey, *shdkey;
174       size_t n;
175
176       if (opt.verbose)
177         log_info ("          id: %s    (grip=%s)\n", item->id, item->hexgrip);
178
179       if (item->no_cert)
180         continue; /* no public key yet available */
181
182       for (p=item->hexgrip, i=0; i < 20; p += 2, i++)
183         grip[i] = xtoi_2 (p);
184       
185       if (!agent_key_available (grip))
186         continue;
187       
188       /* unknown - store it */
189       rc = agent_card_readkey (item->id, &pubkey);
190       if (rc)
191         {
192           log_debug ("agent_card_readkey failed: %s\n", gnupg_strerror (rc));
193           goto leave;
194         }
195
196       {
197         unsigned char *shadow_info = make_shadow_info (serialno, item->id);
198         if (!shadow_info)
199           {
200             rc = GNUPG_Out_Of_Core;
201             xfree (pubkey);
202             goto leave;
203           }
204         rc = agent_shadow_key (pubkey, shadow_info, &shdkey);
205         xfree (shadow_info);
206       }
207       xfree (pubkey);
208       if (rc)
209         {
210           log_error ("shadowing the key failed: %s\n", gnupg_strerror (rc));
211           goto leave;
212         }
213       n = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
214       assert (n);
215
216       rc = agent_write_private_key (grip, shdkey, n, 0);
217       xfree (shdkey);
218       if (rc)
219         {
220           log_error ("error writing key: %s\n", gnupg_strerror (rc));
221           goto leave;
222         }
223
224       if (opt.verbose)
225         log_info ("stored\n");
226       
227       if (assuan_context)
228         {
229           char *derbuf;
230           size_t derbuflen;
231
232           rc = agent_card_readcert (item->id, &derbuf, &derbuflen);
233           if (rc)
234             {
235               log_error ("error reading certificate: %s\n",
236                          gnupg_strerror (rc));
237               goto leave;
238             }
239
240           rc = assuan_send_data (assuan_context, derbuf, derbuflen);
241           xfree (derbuf);
242           if (!rc)
243             rc = assuan_send_data (assuan_context, NULL, 0);
244           if (!rc)
245             rc = assuan_write_line (assuan_context, "END");
246           if (rc)
247             {
248               log_error ("sending certificate failed: %s\n",
249                          assuan_strerror (rc));
250               rc = map_assuan_err (rc);
251               goto leave;
252             }
253         }
254     }
255
256   
257  leave:
258   xfree (serialno);
259   release_keypair_info (parm.info);
260   return rc;
261 }
262
263