Line data Source code
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 0 : release_keypair_info (KEYPAIR_INFO info)
91 : {
92 0 : while (info)
93 : {
94 0 : KEYPAIR_INFO tmp = info->next;
95 0 : xfree (info);
96 0 : info = tmp;
97 : }
98 0 : }
99 :
100 : /* Destructor for certificate information objects. */
101 : static void
102 0 : release_certinfo (CERTINFO info)
103 : {
104 0 : while (info)
105 : {
106 0 : CERTINFO tmp = info->next;
107 0 : xfree (info);
108 0 : info = tmp;
109 : }
110 0 : }
111 :
112 : /* Destructor for status information objects. */
113 : static void
114 0 : release_sinfo (SINFO info)
115 : {
116 0 : while (info)
117 : {
118 0 : SINFO tmp = info->next;
119 0 : xfree (info);
120 0 : info = tmp;
121 : }
122 0 : }
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 0 : kpinfo_cb (void *opaque, const char *line)
130 : {
131 0 : struct kpinfo_cb_parm_s *parm = opaque;
132 : KEYPAIR_INFO item;
133 : char *p;
134 :
135 0 : if (parm->error)
136 0 : return; /* no need to gather data after an error occurred */
137 :
138 0 : if ((parm->error = agent_write_status (parm->ctrl, "PROGRESS",
139 : "learncard", "k", "0", "0", NULL)))
140 0 : return;
141 :
142 0 : item = xtrycalloc (1, sizeof *item + strlen (line));
143 0 : if (!item)
144 : {
145 0 : parm->error = out_of_core ();
146 0 : return;
147 : }
148 0 : strcpy (item->hexgrip, line);
149 0 : for (p = item->hexgrip; hexdigitp (p); p++)
150 : ;
151 0 : if (p == item->hexgrip && *p == 'X' && spacep (p+1))
152 : {
153 0 : item->no_cert = 1;
154 0 : p++;
155 : }
156 0 : else if ((p - item->hexgrip) != 40 || !spacep (p))
157 : { /* not a 20 byte hex keygrip or not followed by a space */
158 0 : parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
159 0 : xfree (item);
160 0 : return;
161 : }
162 0 : *p++ = 0;
163 0 : while (spacep (p))
164 0 : p++;
165 0 : item->id = p;
166 0 : while (*p && !spacep (p))
167 0 : p++;
168 0 : if (p == item->id)
169 : { /* invalid ID string */
170 0 : parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
171 0 : xfree (item);
172 0 : return;
173 : }
174 0 : *p = 0; /* ignore trailing stuff */
175 :
176 : /* store it */
177 0 : item->next = parm->info;
178 0 : 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 0 : certinfo_cb (void *opaque, const char *line)
186 : {
187 0 : struct certinfo_cb_parm_s *parm = opaque;
188 : CERTINFO item;
189 : int type;
190 : char *p, *pend;
191 :
192 0 : if (parm->error)
193 0 : return; /* no need to gather data after an error occurred */
194 :
195 0 : if ((parm->error = agent_write_status (parm->ctrl, "PROGRESS",
196 : "learncard", "c", "0", "0", NULL)))
197 0 : return;
198 :
199 0 : type = strtol (line, &p, 10);
200 0 : while (spacep (p))
201 0 : p++;
202 0 : for (pend = p; *pend && !spacep (pend); pend++)
203 : ;
204 0 : if (p == pend || !*p)
205 : {
206 0 : parm->error = gpg_error (GPG_ERR_INV_RESPONSE);
207 0 : return;
208 : }
209 0 : *pend = 0; /* ignore trailing stuff */
210 :
211 0 : item = xtrycalloc (1, sizeof *item + strlen (p));
212 0 : if (!item)
213 : {
214 0 : parm->error = out_of_core ();
215 0 : return;
216 : }
217 0 : item->type = type;
218 0 : strcpy (item->id, p);
219 : /* store it */
220 0 : item->next = parm->info;
221 0 : 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 0 : sinfo_cb (void *opaque, const char *keyword, size_t keywordlen,
229 : const char *data)
230 : {
231 0 : struct sinfo_cb_parm_s *sparm = opaque;
232 : SINFO item;
233 :
234 0 : if (sparm->error)
235 0 : return; /* no need to gather data after an error occurred */
236 :
237 0 : item = xtrycalloc (1, sizeof *item + keywordlen + 1 + strlen (data));
238 0 : if (!item)
239 : {
240 0 : sparm->error = out_of_core ();
241 0 : return;
242 : }
243 0 : memcpy (item->keyword, keyword, keywordlen);
244 0 : item->data = item->keyword + keywordlen;
245 0 : *item->data = 0;
246 0 : item->data++;
247 0 : strcpy (item->data, data);
248 : /* store it */
249 0 : item->next = sparm->info;
250 0 : sparm->info = item;
251 : }
252 :
253 :
254 :
255 : static int
256 0 : 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 0 : rc = agent_card_readcert (ctrl, id, &derbuf, &derbuflen);
263 0 : if (rc)
264 : {
265 : const char *action;
266 :
267 0 : switch (gpg_err_code (rc))
268 : {
269 : case GPG_ERR_INV_ID:
270 : case GPG_ERR_NOT_FOUND:
271 0 : action = " - ignored";
272 0 : break;
273 : default:
274 0 : action = "";
275 0 : break;
276 : }
277 0 : if (opt.verbose || !*action)
278 0 : log_info ("error reading certificate '%s': %s%s\n",
279 : id? id:"?", gpg_strerror (rc), action);
280 :
281 0 : return *action? 0 : rc;
282 : }
283 :
284 0 : rc = assuan_send_data (assuan_context, derbuf, derbuflen);
285 0 : xfree (derbuf);
286 0 : if (!rc)
287 0 : rc = assuan_send_data (assuan_context, NULL, 0);
288 0 : if (!rc)
289 0 : rc = assuan_write_line (assuan_context, "END");
290 0 : if (rc)
291 : {
292 0 : log_error ("sending certificate failed: %s\n",
293 : gpg_strerror (rc));
294 0 : return rc;
295 : }
296 0 : 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 0 : agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force)
303 : {
304 : int rc;
305 :
306 : struct kpinfo_cb_parm_s parm;
307 : struct certinfo_cb_parm_s cparm;
308 : struct sinfo_cb_parm_s sparm;
309 0 : char *serialno = NULL;
310 : KEYPAIR_INFO item;
311 : SINFO sitem;
312 : unsigned char grip[20];
313 : char *p;
314 : int i;
315 : static int certtype_list[] = {
316 : 111, /* Root CA */
317 : 101, /* trusted */
318 : 102, /* useful */
319 : 100, /* regular */
320 : /* We don't include 110 here because gpgsm can't handle that
321 : special root CA format. */
322 : -1 /* end of list */
323 : };
324 :
325 :
326 0 : memset (&parm, 0, sizeof parm);
327 0 : memset (&cparm, 0, sizeof cparm);
328 0 : memset (&sparm, 0, sizeof sparm);
329 0 : parm.ctrl = ctrl;
330 0 : cparm.ctrl = ctrl;
331 :
332 : /* Check whether a card is present and get the serial number */
333 0 : rc = agent_card_serialno (ctrl, &serialno);
334 0 : if (rc)
335 0 : goto leave;
336 :
337 : /* Now gather all the available info. */
338 0 : rc = agent_card_learn (ctrl, kpinfo_cb, &parm, certinfo_cb, &cparm,
339 : sinfo_cb, &sparm);
340 0 : if (!rc && (parm.error || cparm.error || sparm.error))
341 0 : rc = parm.error? parm.error : cparm.error? cparm.error : sparm.error;
342 0 : if (rc)
343 : {
344 0 : log_debug ("agent_card_learn failed: %s\n", gpg_strerror (rc));
345 0 : goto leave;
346 : }
347 :
348 0 : log_info ("card has S/N: %s\n", serialno);
349 :
350 : /* Pass on all the collected status information. */
351 0 : if (assuan_context)
352 : {
353 0 : for (sitem = sparm.info; sitem; sitem = sitem->next)
354 : {
355 0 : assuan_write_status (assuan_context, sitem->keyword, sitem->data);
356 : }
357 : }
358 :
359 : /* Write out the certificates in a standard order. */
360 0 : for (i=0; certtype_list[i] != -1; i++)
361 : {
362 : CERTINFO citem;
363 0 : for (citem = cparm.info; citem; citem = citem->next)
364 : {
365 0 : if (certtype_list[i] != citem->type)
366 0 : continue;
367 :
368 0 : if (opt.verbose)
369 0 : log_info (" id: %s (type=%d)\n",
370 0 : citem->id, citem->type);
371 :
372 0 : if (assuan_context && send)
373 : {
374 0 : rc = send_cert_back (ctrl, citem->id, assuan_context);
375 0 : if (rc)
376 0 : goto leave;
377 0 : citem->done = 1;
378 : }
379 : }
380 : }
381 :
382 0 : for (item = parm.info; item; item = item->next)
383 : {
384 : unsigned char *pubkey;
385 :
386 0 : if (opt.verbose)
387 0 : log_info (" id: %s (grip=%s)\n", item->id, item->hexgrip);
388 :
389 0 : if (item->no_cert)
390 0 : continue; /* No public key yet available. */
391 :
392 0 : if (assuan_context)
393 : {
394 0 : agent_write_status (ctrl, "KEYPAIRINFO",
395 0 : item->hexgrip, item->id, NULL);
396 : }
397 :
398 0 : for (p=item->hexgrip, i=0; i < 20; p += 2, i++)
399 0 : grip[i] = xtoi_2 (p);
400 :
401 0 : if (!force && !agent_key_available (grip))
402 0 : continue; /* The key is already available. */
403 :
404 : /* Unknown key - store it. */
405 0 : rc = agent_card_readkey (ctrl, item->id, &pubkey);
406 0 : if (rc)
407 : {
408 0 : log_debug ("agent_card_readkey failed: %s\n", gpg_strerror (rc));
409 0 : goto leave;
410 : }
411 :
412 0 : rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, force);
413 0 : xfree (pubkey);
414 0 : if (rc)
415 0 : goto leave;
416 :
417 0 : if (opt.verbose)
418 0 : log_info (" id: %s - shadow key created\n", item->id);
419 :
420 0 : if (assuan_context && send)
421 : {
422 : CERTINFO citem;
423 :
424 : /* only send the certificate if we have not done so before */
425 0 : for (citem = cparm.info; citem; citem = citem->next)
426 : {
427 0 : if (!strcmp (citem->id, item->id))
428 0 : break;
429 : }
430 0 : if (!citem)
431 : {
432 0 : rc = send_cert_back (ctrl, item->id, assuan_context);
433 0 : if (rc)
434 0 : goto leave;
435 : }
436 : }
437 : }
438 :
439 :
440 : leave:
441 0 : xfree (serialno);
442 0 : release_keypair_info (parm.info);
443 0 : release_certinfo (cparm.info);
444 0 : release_sinfo (sparm.info);
445 0 : return rc;
446 : }
|