/[winpt]/trunk/Src/wptCardEdit.cpp
ViewVC logotype

Contents of /trunk/Src/wptCardEdit.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 306 - (show annotations)
Fri Mar 23 14:07:24 2007 UTC (17 years, 11 months ago) by twoaday
File size: 15508 byte(s)


1 /* wptCardEdit.cpp - OpenPGP card editing interface
2 * Copyright (C) 2005, 2006, 2007 Timo Schulz
3 *
4 * This file is part of WinPT.
5 *
6 * WinPT 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 * WinPT 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 #ifdef HAVE_CONFIG_H
17 #include <config.h>
18 #endif
19
20 #include <windows.h>
21 #include <time.h>
22
23 #include "wptGPG.h"
24 #include "wptCard.h"
25 #include "wptCardEdit.h"
26 #include "wptErrors.h"
27 #include "wptTypes.h"
28 #include "wptW32API.h"
29
30
31 /* Create a new Smartcard context. */
32 static gpgme_error_t
33 gpg_card_new (gpg_card_t *ret_card)
34 {
35 gpg_card_t c;
36
37 if (!ret_card)
38 return gpg_error (GPG_ERR_INV_ARG);
39 *ret_card = NULL;
40 c = (gpg_card_t)calloc (1, sizeof *c);
41 if (!c)
42 return gpg_error (GPG_ERR_ENOMEM);
43 *ret_card = c;
44 return 0;
45 }
46
47
48 /* Release an existing Smartcard context. @card. */
49 void
50 gpg_card_release (gpg_card_t card)
51 {
52 int i;
53
54 if (!card)
55 return;
56 safe_free (card->aid);
57 safe_free (card->version);
58 safe_free (card->lang);
59 safe_free (card->login);
60 for (i=0; i < N_CARD_FPR; i++) {
61 safe_free (card->fpr[i]);
62 safe_free (card->ca_fpr[i]);
63 safe_free (card->fpr_created_str[i]);
64 }
65 safe_free (card->surname);
66 safe_free (card->givenname);
67 safe_free (card->serial);
68 safe_free (card->vendor);
69 safe_free (card->url);
70 safe_free (card->card_type);
71 safe_free (card);
72 }
73
74
75 /* Parse the timestamp @long_ts and return a string
76 representation of it (format: depens on locale setting). */
77 static char*
78 get_str_timestamp (long long_ts)
79 {
80 char timebuf[64];
81 const char *dat;
82
83 if (long_ts < 1)
84 return strdup ("");
85 dat = get_locale_date (long_ts, timebuf, DIM (timebuf)-1);
86 if (!dat)
87 return strdup ("");
88 return strdup (dat);
89 }
90
91
92 /* Parse the card version AABB => 0100 */
93 static void
94 parse_version (gpg_card_t card, const char *ver)
95 {
96 char buf[6];
97
98 if (strlen (ver) != 4)
99 return;
100 buf[0] = *ver++;
101 buf[1] = *ver++;
102 buf[2] = 0;
103 buf[3] = *ver++;
104 buf[4] = *ver++;
105 buf[5] = 0;
106 card->ver[0] = atoi (buf);
107 card->ver[1] = atoi (buf+3);
108 }
109
110
111 /* Colon handler for parsing the GPG card colon output. */
112 static gpgme_error_t
113 statuscard_colon_handler (gpg_card_t card, char * line)
114 {
115 enum rectype_t {
116 CARD_None = 0,
117 CARD_AID,
118 CARD_Version,
119 CARD_Vendor,
120 CARD_Serial,
121 CARD_Name,
122 CARD_Lang,
123 CARD_Sex,
124 CARD_Url,
125 CARD_Login,
126 CARD_MaxPinLen,
127 CARD_SigCount,
128 CARD_CAFpr,
129 CARD_Fpr,
130 CARD_FprTime
131 };
132 enum rectype_t rectype = CARD_None;
133 char *p, *pend;
134 int field = 0;
135
136 if (!line)
137 return gpg_error (GPG_ERR_EOF);
138
139 for (p = line; p; p = pend) {
140 field++;
141 pend = strchr (p, ':');
142 if (pend)
143 *pend++ = 0;
144
145 if (field == 1) {
146 if (!strcmp (p, "AID"))
147 rectype = CARD_AID;
148 else if( !strcmp( p, "version" ) )
149 rectype = CARD_Version;
150 else if( !strcmp( p, "vendor" ) )
151 rectype = CARD_Vendor;
152 else if( !strcmp( p, "serial" ) )
153 rectype = CARD_Serial;
154 else if( !strcmp( p, "name" ) )
155 rectype = CARD_Name;
156 else if( !strcmp( p, "lang" ) )
157 rectype = CARD_Lang;
158 else if( !strcmp( p, "sex" ) )
159 rectype = CARD_Sex;
160 else if( !strcmp( p, "url" ) )
161 rectype = CARD_Url;
162 else if( !strcmp( p, "login" ) )
163 rectype = CARD_Login;
164 else if( !strcmp( p, "maxpinlen" ) )
165 rectype = CARD_MaxPinLen;
166 else if( !strcmp( p, "sigcount" ) )
167 rectype = CARD_SigCount;
168 else if (!strcmp (p, "cafpr"))
169 rectype = CARD_CAFpr;
170 else if (!strcmp (p, "fpr"))
171 rectype = CARD_Fpr;
172 else if (!strcmp (p, "fprtime"))
173 rectype = CARD_FprTime;
174 else
175 rectype = CARD_None;
176 }
177 switch (rectype) {
178 case CARD_AID:
179 if (field == 2) {
180 card->aid = strdup (p);
181 if (!card->aid)
182 return gpg_error (GPG_ERR_ENOMEM);
183 }
184 else if (field == 3) {
185 card->card_type = strdup (p);
186 if (!card->card_type)
187 return gpg_error (GPG_ERR_ENOMEM);
188 }
189 break;
190
191 case CARD_Version:
192 if (field == 2) {
193 card->version = strdup (p);
194 if (!card->version)
195 return gpg_error (GPG_ERR_ENOMEM);
196 parse_version (card, p);
197 }
198 break;
199
200 case CARD_Vendor:
201 if (field == 3) {
202 card->vendor = strdup (p);
203 if (!card->vendor)
204 return gpg_error (GPG_ERR_ENOMEM);
205 }
206 break;
207
208 case CARD_Serial:
209 if (field == 2) {
210 card->serial = strdup (p);
211 if (!card->serial)
212 return gpg_error (GPG_ERR_ENOMEM);
213 }
214 break;
215 case CARD_Name:
216 if (field == 2) {
217 card->givenname = strdup (p);
218 if (!card->givenname)
219 return gpg_error (GPG_ERR_ENOMEM);
220 }
221 else if (field == 3) {
222 card->surname = strdup (p);
223 if (!card->surname)
224 return gpg_error (GPG_ERR_ENOMEM);
225 }
226 break;
227 case CARD_Lang:
228 if( field == 2 ) {
229 card->lang = strdup (p);
230 if (!card->lang)
231 return gpg_error (GPG_ERR_ENOMEM);
232 }
233 break;
234 case CARD_Sex:
235 if( field == 2 )
236 card->sex = *p;
237 break;
238
239 case CARD_Url:
240 if( field == 2 ) {
241 if (card->url)
242 free (card->url);
243 card->url = (char*)calloc (1, strlen (p) + 1);
244 if( !card->url ) {
245 return gpg_error (GPG_ERR_ENOMEM);
246 }
247 gpg_decode_c_string (p, &card->url, strlen (p) + 1);
248 if (!card->url)
249 return gpg_error (GPG_ERR_ENOMEM);
250 }
251 break;
252
253 case CARD_Login:
254 if (field == 2) {
255 card->login = strdup (p);
256 if (!card->login)
257 return gpg_error (GPG_ERR_ENOMEM);
258 }
259 break;
260
261 case CARD_MaxPinLen:
262 break;
263
264 case CARD_SigCount:
265 if (field == 2)
266 card->sig_count = atol (p);
267 break;
268
269 case CARD_CAFpr:
270 if (!p)
271 break;
272 if (field > 4 || field == 1)
273 break;
274 card->ca_fpr[field-2] = strdup (p);
275 if (!card->ca_fpr[field-2])
276 return gpg_error (GPG_ERR_ENOMEM);
277 break;
278
279 case CARD_Fpr:
280 if (field > 4 || field == 1) /* max N_CARD_FPR=3 fprs */
281 break;
282 card->fpr[field-2] = strdup (p);
283 if (!card->fpr[field-2])
284 return gpg_error (GPG_ERR_ENOMEM);
285 break;
286
287 case CARD_FprTime:
288 if (field > 4 || field == 1)
289 break;
290 card->fpr_created[field-2] = strtoul (p, NULL, 10);
291 if (card->fpr_created[field-2] > 0)
292 card->fpr_created_str[field-2] = get_str_timestamp (strtoul (p, NULL, 10));
293 else
294 card->fpr_created_str[field-2] = NULL;
295 break;
296
297 default:
298 break;
299
300 }
301 }
302 return 0;
303 }
304
305
306 /* Dummy handler to get the colon data and then quit. */
307 static gpgme_error_t
308 list_handler (void *opaque, gpgme_status_code_t code, const char *key, int fd)
309 {
310 static int wait_card=0;
311 GpgCardEdit *ce = (GpgCardEdit *)opaque;
312 const char *s = "";
313 DWORD n;
314
315 if (!ce)
316 return gpg_error (GPG_ERR_INV_VALUE);
317
318 if (code == GPGME_STATUS_CARDCTRL) {
319 if (!strcmp (key, "5"))
320 ce->setResult (GPG_CARDRES_NOCARD);
321 else if (!strcmp (key, "1"))
322 wait_card = 1;
323 }
324 if (wait_card && !strcmp (key, "cardctrl.insert_card.okay")) {
325 /* :TODO: show card ID */
326 n = MessageBox (NULL, _("Please insert the card and click OK or Cancel to abort."),
327 _("GPG Card Status"), MB_ICONQUESTION|MB_OKCANCEL);
328 if (n == IDCANCEL) {
329 s = "c\n";
330 ce->setResult (GPG_CARDRES_CANCEL);
331 if (!WriteFile ((HANDLE)fd, s, strlen (s), &n, NULL))
332 log_debug ("list_handler: WriteFile() failed ec=%d\r\n",
333 (int)GetLastError ());
334 wait_card = 0;
335 }
336 }
337 if (!strcmp (key, "cardedit.prompt")) {
338 s = "quit\n";
339 if (!WriteFile ((HANDLE)fd, s, strlen (s), &n, NULL))
340 log_debug ("list_handler: WriteFile() failed ec=%d\r\n",
341 (int)GetLastError ());
342 wait_card = 0;
343 }
344 return 0;
345 }
346
347
348 /* Construct an empty object. */
349 GpgCardEdit::GpgCardEdit (void)
350 {
351 pin = NULL;
352 pin_new = NULL;
353 admin_pin = NULL;
354
355 keygen.comment = NULL;
356 keygen.email = NULL;
357 keygen.name = NULL;
358 keygen.expdate = NULL;
359 keygen.key_fpr = NULL;
360
361 type = 0;
362 cnt = 0;
363 cancel = 0;
364 result = 0;
365
366 gpgme_new (&ctx); /* XXX: check return code */
367 }
368
369 /* Release the object. */
370 GpgCardEdit::~GpgCardEdit (void)
371 {
372 if (keygen.name)
373 free (keygen.name);
374 if (keygen.comment)
375 free (keygen.comment);
376 if (keygen.email)
377 free (keygen.email);
378 if (keygen.expdate)
379 free (keygen.expdate);
380 if (keygen.key_fpr)
381 free (keygen.key_fpr);
382 gpgme_release (ctx);
383 }
384
385
386 /* Set the user PIN for the object to @pin. */
387 void
388 GpgCardEdit::setPIN (const char *_pin)
389 {
390 this->pin = _pin;
391 }
392
393
394 /* Set the admin PIN for the object to @admin_pin. */
395 void
396 GpgCardEdit::setAdminPIN (const char *_admin_pin)
397 {
398 this->admin_pin = _admin_pin;
399 }
400
401
402 /* Set the new user PIN for the object to @new_pin. */
403 void
404 GpgCardEdit::setNewPIN (const char *_new_pin)
405 {
406 this->pin_new = _new_pin;
407 }
408
409
410 /* Set the passphrase needed when generating a key to @pass. */
411 void
412 GpgCardEdit::setKeygenPassphrase (const char *_pass)
413 {
414 this->keygen.pass = _pass;
415 }
416
417
418 /* Set the callback to @cb and the hook value to @cb_value. */
419 void
420 GpgCardEdit::setCallback (const char* (*_cb)(int code, void *opaque),
421 void *_cb_value)
422 {
423 this->cb_value = _cb_value;
424 this->card_cb = _cb;
425 }
426
427
428 /* Read the information from the inserted card and return
429 it in form of a card context @r_card.
430 Return value: 0 on success. */
431 gpgme_error_t
432 GpgCardEdit::getCardStatus (gpg_card_t *r_card)
433 {
434 gpgme_data_t out = NULL;
435 gpgme_error_t err;
436 gpg_card_t card = NULL;
437 char buf[200];
438
439 if (!r_card)
440 return gpg_error (GPG_ERR_INV_ARG);
441 *r_card = NULL;
442
443 err = gpg_card_new (&card);
444 if (err)
445 return err;
446
447 err = gpgme_data_new (&out);
448 if (err) {
449 gpg_card_release (card);
450 return err;
451 }
452
453 err = gpgme_op_card_edit (ctx, NULL, list_handler, this, out);
454 gpgme_data_rewind (out);
455 if (err) {
456 gpgme_data_release (out);
457 gpg_card_release (card);
458 return err;
459 }
460 if (getResult () & GPG_CARDRES_NOCARD ||
461 getResult () & GPG_CARDRES_CANCEL) {
462 gpg_card_release (card);
463 gpgme_data_release (out);
464 return gpg_error (GPG_ERR_CARD_NOT_PRESENT);
465 }
466
467 *r_card = card;
468 while (gpg_data_readline (out, buf, DIM (buf)-2) > 0) {
469 err = statuscard_colon_handler (card, buf);
470 if (err)
471 break;
472 }
473 gpgme_data_release (out);
474
475 return err;
476 }
477
478
479 /* Generate a key on a smart card with the following params:
480 @flags: user specific params.
481 @name: name of the key holder.
482 @email: email of the key holder.
483 @comment: optional comment.
484 @valid: how long is the key valid in days.
485 Return value: 0 on success. */
486 gpgme_error_t
487 GpgCardEdit::genKey (int flags,
488 const char *name, const char *email, const char *comment,
489 long valid, char **r_key_fpr)
490 {
491 gpgme_error_t err;
492
493 if (!name || !email)
494 return gpg_error (GPG_ERR_INV_ARG);
495 if (!this->keygen.pass)
496 return gpg_error (GPG_ERR_INV_OBJ);
497
498 type = GPG_EDITCARD_GENKEY;
499 this->keygen.flags = flags;
500
501 if (this->keygen.name)
502 free (this->keygen.name);
503 this->keygen.name = strdup (name);
504 if (this->keygen.email)
505 free (this->keygen.email);
506 this->keygen.email = strdup (email);
507 if (this->keygen.comment)
508 free (this->keygen.comment);
509 this->keygen.comment = comment? strdup (comment) : strdup ("");
510 if (this->keygen.expdate)
511 free (this->keygen.expdate);
512 this->keygen.expdate = (char*)calloc (1, 16);
513 if (this->keygen.expdate)
514 sprintf (this->keygen.expdate, "%lu", valid);
515
516 if (!this->keygen.name || !this->keygen.email)
517 return gpg_error (GPG_ERR_ENOMEM);
518
519 err = gpg_card_edit (ctx, this);
520 if (!err)
521 err = getResult () & GPG_CARDRES_CANCEL? gpg_error (GPG_ERR_CANCELED) : 0;
522 if (!err && r_key_fpr)
523 *r_key_fpr = strdup (keygen.key_fpr? keygen.key_fpr : "");
524 return err;
525 }
526
527
528 /* Change the pin from @pin to @pin_new.
529 Return value: 0 on success. */
530 gpgme_error_t
531 GpgCardEdit::changePIN (int _type)
532 {
533 gpgme_error_t err;
534
535 if (!this->pin_new)
536 return gpg_error (GPG_ERR_INV_ARG);
537
538 if (!_type) {
539 if (this->pin && this->pin_new)
540 this->type = GPG_EDITCARD_CHUPIN;
541 else if (this->pin && this->admin_pin)
542 this->type = GPG_EDITCARD_CHAPIN;
543 else
544 this->type = GPG_EDITCARD_UNBPIN;
545 }
546 else
547 this->type = _type;
548 /* check if the user provided the needed PIN. */
549 if ((this->type == GPG_EDITCARD_CHUPIN && !this->pin) ||
550 (this->type == GPG_EDITCARD_CHAPIN && !this->admin_pin))
551 return gpg_error (GPG_ERR_INV_ARG);
552
553 err = gpg_card_edit (ctx, this);
554 return err;
555 }
556
557
558 /* Update the given and the surname on the card. */
559 gpgme_error_t
560 GpgCardEdit::updateName (const char *given, const char *sur)
561 {
562 gpgme_error_t err;
563
564 if (!this->admin_pin)
565 return gpg_error (GPG_ERR_INV_OBJ);
566
567 this->type = GPG_EDITCARD_NAME;
568 this->edit.surname = sur;
569 this->edit.givenname = given;
570
571 err = gpg_card_edit (ctx, this);
572 return err;
573 }
574
575 /* Update the given URL on the card. */
576 gpgme_error_t
577 GpgCardEdit::updateURL (const char *_url)
578 {
579 gpgme_error_t err;
580
581 if (!this->admin_pin)
582 return gpg_error (GPG_ERR_INV_OBJ);
583
584 type = GPG_EDITCARD_KEYURL;
585 this->edit.keyurl = _url;
586
587 err = gpg_card_edit (ctx, this);
588 return err;
589 }
590
591
592 gpgme_error_t
593 GpgCardEdit::updateLogin (const char *login)
594 {
595 gpgme_error_t err;
596
597 if (!this->admin_pin)
598 return gpg_error (GPG_ERR_INV_OBJ);
599
600 this->type = GPG_EDITCARD_LOGIN;
601 this->edit.login = login;
602
603 err = gpg_card_edit (ctx, this);
604 return err;
605 }
606
607
608 gpgme_error_t
609 GpgCardEdit::updateSex (char sex)
610 {
611 gpgme_error_t err;
612
613 if (!this->admin_pin)
614 return gpg_error (GPG_ERR_INV_OBJ);
615
616 type = GPG_EDITCARD_SEX;
617 this->edit.sex = sex;
618
619 err = gpg_card_edit (ctx, this);
620 return err;
621 }
622
623 gpgme_error_t
624 GpgCardEdit::updateLanguage (const char *lang)
625 {
626 gpgme_error_t err;
627
628 if (!this->admin_pin)
629 return gpg_error (GPG_ERR_INV_OBJ);
630
631 type = GPG_EDITCARD_LANG;
632 this->edit.lang = lang;
633
634 err = gpg_card_edit (ctx, this);
635 return err;
636 }
637
638
639 /* Fetch the key specified by the url stored on the card.
640 Return value: 0 on success. */
641 gpgme_error_t
642 GpgCardEdit::fetchKey (void)
643 {
644 gpgme_error_t err = 0;
645
646 if (!this->pin)
647 return gpg_error (GPG_ERR_INV_OBJ);
648
649 return err;
650 }
651
652
653 /* Dispatcher for the various commands. @cmd is the card command ID
654 and @arg1 and @arg2 the actual values for the command.
655 Return value: 0 on success. */
656 gpgme_error_t
657 GpgCardEdit:: doCmd (int cmd, const char *arg1, const char *arg2)
658 {
659 switch (cmd) {
660 case GPG_EDITCARD_NAME:
661 return updateName (arg1, arg2); /* given, surname */
662
663 case GPG_EDITCARD_LANG:
664 return updateLanguage (arg1); /* lang */
665
666 case GPG_EDITCARD_SEX:
667 return updateSex (*arg1); /* sex */
668
669 case GPG_EDITCARD_KEYURL:
670 return updateURL (arg1); /* url */
671
672 case GPG_EDITCARD_LOGIN:
673 return updateLogin (arg1); /* login */
674
675 default:
676 return gpg_error (GPG_ERR_INV_VALUE);
677 }
678
679 return 0;
680 }
681
682
683 /* Set the result for the executed operation.
684 The values will be ORed. */
685 void
686 GpgCardEdit::setResult (int res)
687 {
688 result |= res;
689 }
690
691
692 /* Return the result of the executed operation. */
693 int
694 GpgCardEdit::getResult (void)
695 {
696 return result;
697 }
698
699
700 /* Reset the object state. */
701 void
702 GpgCardEdit::reset (void)
703 {
704 cnt = 0;
705 }
706
707
708 /* Return card command type */
709 int
710 GpgCardEdit::getType (void)
711 {
712 return type;
713 }

Properties

Name Value
svn:eol-style native

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26