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
release_keypair_info(KEYPAIR_INFO info)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
release_certinfo(CERTINFO info)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
release_sinfo(SINFO info)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
kpinfo_cb(void * opaque,const char * line)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
certinfo_cb(void * opaque,const char * line)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
sinfo_cb(void * opaque,const char * keyword,size_t keywordlen,const char * data)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
send_cert_back(ctrl_t ctrl,const char * id,void * assuan_context)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
agent_handle_learn(ctrl_t ctrl,int send,void * assuan_context,int force)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 rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, force);
412 xfree (pubkey);
413 if (rc)
414 goto leave;
415
416 if (opt.verbose)
417 log_info (" id: %s - shadow key created\n", item->id);
418
419 if (assuan_context && send)
420 {
421 CERTINFO citem;
422
423 /* only send the certificate if we have not done so before */
424 for (citem = cparm.info; citem; citem = citem->next)
425 {
426 if (!strcmp (citem->id, item->id))
427 break;
428 }
429 if (!citem)
430 {
431 rc = send_cert_back (ctrl, item->id, assuan_context);
432 if (rc)
433 goto leave;
434 }
435 }
436 }
437
438
439 leave:
440 release_keypair_info (parm.info);
441 release_certinfo (cparm.info);
442 release_sinfo (sparm.info);
443 return rc;
444 }
445