2003-09-28 Timo Schulz <twoaday@freakmail.de>
[gnupg.git] / g10 / card-util.c
1 /* card-util.c - Utility functions for the OpenPGP card.
2  *      Copyright (C) 2003 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 #ifdef ENABLE_CARD_SUPPORT
23 /* 
24    Note, that most card related code has been taken from 1.9.x branch
25    and is maintained over there if at all possible.  Thus, if you make
26    changes here, please check that a similar change has been commited
27    to the 1.9.x branch.
28 */
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <assert.h>
34
35 #include "util.h"
36 #include "i18n.h"
37 #include "ttyio.h"
38 #include "status.h"
39 #include "options.h"
40 #include "main.h"
41
42 #include "cardglue.h"
43
44 #define CONTROL_D ('D' - 'A' + 1)
45
46
47 /* Change the PIN of a an OpenPGP card.  This is an interactive
48    function. */
49 void
50 change_pin (int chvno)
51 {
52   struct agent_card_info_s info;
53   int rc;
54   int reset_mode = 0;
55
56   rc = agent_learn (&info);
57   if (rc)
58     {
59       log_error (_("OpenPGP card not available: %s\n"),
60                   gpg_strerror (rc));
61       return;
62     }
63   
64   log_info (_("OpenPGP card no. %s detected\n"),
65               info.serialno? info.serialno : "[none]");
66
67   agent_release_card_info (&info);
68
69   if (opt.batch)
70     {
71       log_error (_("sorry, can't do this in batch mode\n"));
72       return;
73     }
74
75   for (;;)
76     {
77       char *answer;
78
79       tty_printf ("\n");
80       tty_printf ("1 - change signature PIN\n"
81                   "2 - change decryption and authentication PIN\n"
82                   "3 - change Admin's PIN\n"
83                   "R - toggle reset retry counter mode\n"
84                   "Q - quit\n");
85       tty_printf ("\n");
86       if (reset_mode)
87         {
88           tty_printf ("Reset Retry Counter mode active\n");
89           tty_printf ("\n");
90         }
91
92       answer = cpr_get("cardutil.change_pin.menu",_("Your selection? "));
93       cpr_kill_prompt();
94       if (strlen (answer) != 1)
95         continue;
96
97       rc = 0;
98       if (reset_mode && *answer == '3')
99         {
100           tty_printf ("Sorry, reset of the Admin PIN's retry counter "
101                       "is not possible.\n");
102         }
103       else if (*answer == '1'  || *answer == '2' || *answer == '3')
104         {
105           rc = agent_scd_change_pin (*answer - '0' + (reset_mode?100:0));
106           if (rc)
107             tty_printf ("Error changing/resetting the PIN: %s\n",
108                         gpg_strerror (rc));
109           else
110             tty_printf ("New PIN successfully set.\n");
111         }
112       else if (*answer == 'r' || *answer == 'R')
113         {
114           reset_mode = !reset_mode;
115         }
116       else if (*answer == 'q' || *answer == 'Q')
117         {
118           break;
119         }
120     }
121
122 }
123
124 static const char *
125 get_manufacturer (unsigned int no)
126 {
127   /* Note:  Make sure that there is no colon or linefeed in the string. */
128   switch (no)
129     {
130     case 0:
131     case 0xffff: return "test card";
132     case 0x0001: return "PPC Card Systems";
133     default: return "unknown";
134     }
135 }
136
137
138 static void
139 print_sha1_fpr (FILE *fp, const unsigned char *fpr)
140 {
141   int i;
142
143   if (fpr)
144     {
145       for (i=0; i < 20 ; i+=2, fpr += 2 )
146         {
147           if (i == 10 )
148             tty_fprintf (fp, " ");
149           tty_fprintf (fp, " %02X%02X", *fpr, fpr[1]);
150         }
151     }
152   else
153     tty_fprintf (fp, " [none]");
154   tty_fprintf (fp, "\n");
155 }
156
157
158 static void
159 print_sha1_fpr_colon (FILE *fp, const unsigned char *fpr)
160 {
161   int i;
162
163   if (fpr)
164     {
165       for (i=0; i < 20 ; i++, fpr++)
166         fprintf (fp, "%02X", *fpr);
167     }
168   putc (':', fp);
169 }
170
171
172 static void
173 print_name (FILE *fp, const char *text, const char *name)
174 {
175   tty_fprintf (fp, text);
176
177
178   /* FIXME: tty_printf_utf8_string2 eats everything after and
179      including an @ - e.g. when printing an url. */
180   if (name && *name)
181     {
182       if (fp)
183         print_utf8_string2 (fp, name, strlen (name), '\n');
184       else
185         tty_print_utf8_string2 (name, strlen (name), '\n');
186     }
187   else
188     tty_fprintf (fp, _("[not set]"));
189   tty_fprintf (fp, "\n");
190 }
191
192 static void
193 print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
194 {
195   if (opt.with_colons)
196     fprintf (fp, "%s:", tag);
197   else
198     tty_fprintf (fp, text);
199
200   if (name && *name)
201     {
202       char *p, *given, *buf = xstrdup (name);
203
204       given = strstr (buf, "<<");
205       for (p=buf; *p; p++)
206         if (*p == '<')
207           *p = ' ';
208       if (given && given[2])
209         {
210           *given = 0;
211           given += 2;
212           if (opt.with_colons)
213             print_string (fp, given, strlen (given), ':');
214           else if (fp)
215             print_utf8_string2 (fp, given, strlen (given), '\n');
216           else
217             tty_print_utf8_string2 (given, strlen (given), '\n');
218
219           if (opt.with_colons)
220             putc (':', fp);
221           else if (*buf)
222             tty_fprintf (fp, " ");
223         }
224
225       if (opt.with_colons)
226         print_string (fp, buf, strlen (buf), ':');
227       else if (fp)
228         print_utf8_string2 (fp, buf, strlen (buf), '\n');
229       else
230         tty_print_utf8_string2 (buf, strlen (buf), '\n');
231       xfree (buf);
232     }
233   else
234     {
235       if (opt.with_colons)
236         putc (':', fp);
237       else
238         tty_fprintf (fp, _("[not set]"));
239     }
240
241   if (opt.with_colons)
242     fputs (":\n", fp);
243   else
244     tty_fprintf (fp, "\n");
245 }
246
247
248 /* Print all available information about the current card. */
249 void
250 card_status (FILE *fp)
251 {
252   struct agent_card_info_s info;
253   PKT_public_key *pk = xcalloc (1, sizeof *pk);
254   int rc;
255   unsigned int uval;
256
257   rc = agent_learn (&info);
258   if (rc)
259     {
260       if (opt.with_colons)
261         fputs ("AID:::\n", fp);
262       log_error (_("OpenPGP card not available: %s\n"),
263                   gpg_strerror (rc));
264       xfree (pk);
265       return;
266     }
267
268   if (opt.with_colons)
269     fprintf (fp, "AID:%s:", info.serialno? info.serialno : "");
270   else
271     tty_fprintf (fp, "Application ID ...: %s\n",
272                  info.serialno? info.serialno : "[none]");
273   if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) 
274       || strlen (info.serialno) != 32 )
275     {
276       if (opt.with_colons)
277         fputs ("unknown:\n", fp);
278       log_info ("not an OpenPGP card\n");
279       agent_release_card_info (&info);
280       xfree (pk);
281       return;
282     }
283
284   if (opt.with_colons)
285     fputs ("openpgp-card:\n", fp);
286
287
288   if (opt.with_colons)
289     {
290       fprintf (fp, "version:%.4s:\n", info.serialno+12);
291       uval = xtoi_2(info.serialno+16)*256 + xtoi_2 (info.serialno+18);
292       fprintf (fp, "vendor:%04x:%s:\n", uval, get_manufacturer (uval));
293       fprintf (fp, "serial:%.8s:\n", info.serialno+20);
294       
295       print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
296
297       fputs ("lang:", fp);
298       if (info.disp_lang)
299         print_string (fp, info.disp_lang, strlen (info.disp_lang), ':');
300       fputs (":\n", fp);
301
302       fprintf (fp, "sex:%c:\n", (info.disp_sex == 1? 'm':
303                                  info.disp_sex == 2? 'f' : 'u'));
304
305       fputs ("url:", fp);
306       if (info.pubkey_url)
307         print_string (fp, info.pubkey_url, strlen (info.pubkey_url), ':');
308       fputs (":\n", fp);
309
310       fputs ("login:", fp);
311       if (info.login_data)
312         print_string (fp, info.login_data, strlen (info.login_data), ':');
313       fputs (":\n", fp);
314
315       fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
316       fprintf (fp, "maxpinlen:%d:%d:%d:\n",
317                    info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
318       fprintf (fp, "pinretry:%d:%d:%d:\n",
319                    info.chvretry[0], info.chvretry[1], info.chvretry[2]);
320       fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
321
322       fputs ("fpr:", fp);
323       print_sha1_fpr_colon (fp, info.fpr1valid? info.fpr1:NULL);
324       print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL);
325       print_sha1_fpr_colon (fp, info.fpr3valid? info.fpr3:NULL);
326       putc ('\n', fp);
327
328     }
329   else 
330     {
331       tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n",
332                    info.serialno[12] == '0'?"":info.serialno+12,
333                    info.serialno[13],
334                    info.serialno[14] == '0'?"":info.serialno+14,
335                    info.serialno[15]);
336       tty_fprintf (fp, "Manufacturer .....: %s\n", 
337                    get_manufacturer (xtoi_2(info.serialno+16)*256
338                                      + xtoi_2 (info.serialno+18)));
339       tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20);
340       
341       print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
342       print_name (fp, "Language prefs ...: ", info.disp_lang);
343       tty_fprintf (fp,    "Sex ..............: %s\n",
344                    info.disp_sex == 1? _("male"):
345                    info.disp_sex == 2? _("female") : _("unspecified"));
346       print_name (fp, "URL of public key : ", info.pubkey_url);
347       print_name (fp, "Login data .......: ", info.login_data);
348       tty_fprintf (fp,    "Signature PIN ....: %s\n",
349                    info.chv1_cached? _("cached"): _("not cached"));
350       tty_fprintf (fp,    "Max. PIN lengths .: %d %d %d\n",
351                    info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
352       tty_fprintf (fp,    "PIN retry counter : %d %d %d\n",
353                    info.chvretry[0], info.chvretry[1], info.chvretry[2]);
354       tty_fprintf (fp,    "Signature counter : %lu\n", info.sig_counter);
355       tty_fprintf (fp, "Signature key ....:");
356       print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL);
357       tty_fprintf (fp, "Encryption key....:");
358       print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
359       tty_fprintf (fp, "Authentication key:");
360       print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
361 /*       tty_fprintf (fp, "General key info..: ");  */
362 /*       if (info.fpr1valid && !get_pubkey_byfprint (pk, info.fpr1, 20)) */
363 /*         print_pubkey_info (fp, pk); */
364 /*       else */
365 /*         tty_fprintf (fp, "[none]\n"); */
366     }
367       
368   free_public_key (pk);
369   agent_release_card_info (&info);
370 }
371
372
373 static char *
374 get_one_name (const char *prompt1, const char *prompt2)
375 {
376   char *name;
377   int i;
378
379   for (;;)
380     {
381       name = cpr_get (prompt1, prompt2);
382       if (!name)
383         return NULL;
384       trim_spaces (name);
385       cpr_kill_prompt ();
386       for (i=0; name[i] && name[i] >= ' ' && name[i] <= 126; i++)
387         ;
388
389       /* The name must be in Latin-1 and not UTF-8 - lacking the code
390          to ensure this we restrict it to ASCII. */
391       if (name[i])
392         tty_printf (_("Error: Only plain ASCII is currently allowed.\n"));
393       else if (strchr (name, '<'))
394         tty_printf (_("Error: The \"<\" character may not be used.\n"));
395       else if (strstr (name, "  "))
396         tty_printf (_("Error: Double spaces are not allowed.\n"));    
397       else
398         return name;
399       xfree (name);
400     }
401 }
402
403
404
405 static int
406 change_name (void)
407 {
408   char *surname = NULL, *givenname = NULL;
409   char *isoname, *p;
410   int rc;
411
412   surname = get_one_name ("keygen.smartcard.surname",
413                                     _("Cardholder's surname: "));
414   givenname = get_one_name ("keygen.smartcard.givenname",
415                                        _("Cardholder's given name: "));
416   if (!surname || !givenname || (!*surname && !*givenname))
417     {
418       xfree (surname);
419       xfree (givenname);
420       return -1; /*canceled*/
421     }
422
423   isoname = xmalloc ( strlen (surname) + 2 + strlen (givenname) + 1);
424   strcpy (stpcpy (stpcpy (isoname, surname), "<<"), givenname);
425   xfree (surname);
426   xfree (givenname);
427   for (p=isoname; *p; p++)
428     if (*p == ' ')
429       *p = '<';
430
431   log_debug ("setting Name to `%s'\n", isoname);
432   rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) );
433   if (rc)
434     log_error ("error setting Name: %s\n", gpg_strerror (rc));
435
436   xfree (isoname);
437   return rc;
438 }
439
440
441 static int
442 change_url (void)
443 {
444   char *url;
445   int rc;
446
447   url = cpr_get ("cardedit.change_url", _("URL to retrieve public key: "));
448   if (!url)
449     return -1;
450   trim_spaces (url);
451   cpr_kill_prompt ();
452
453   rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) );
454   if (rc)
455     log_error ("error setting URL: %s\n", gpg_strerror (rc));
456   xfree (url);
457   return rc;
458 }
459
460 static int
461 change_login (void)
462 {
463   char *data;
464   int rc;
465
466   data = cpr_get ("cardedit.change_login",
467                   _("Login data (account name): "));
468   if (!data)
469     return -1;
470   trim_spaces (data);
471   cpr_kill_prompt ();
472
473   rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) );
474   if (rc)
475     log_error ("error setting login data: %s\n", gpg_strerror (rc));
476   xfree (data);
477   return rc;
478 }
479
480 static int
481 change_lang (void)
482 {
483   char *data, *p;
484   int rc;
485
486   data = cpr_get ("cardedit.change_lang",
487                   _("Language preferences: "));
488   if (!data)
489     return -1;
490   trim_spaces (data);
491   cpr_kill_prompt ();
492
493   if (strlen (data) > 8 || (strlen (data) & 1))
494     {
495       tty_printf (_("Error: invalid length of preference string.\n"));
496       xfree (data);
497       return -1;
498     }
499
500   for (p=data; *p && *p >= 'a' && *p <= 'z'; p++)
501     ;
502   if (*p)
503     {
504       tty_printf (_("Error: invalid characters in preference string.\n"));
505       xfree (data);
506       return -1;
507     }
508
509   rc = agent_scd_setattr ("DISP-LANG", data, strlen (data) );
510   if (rc)
511     log_error ("error setting lang: %s\n", gpg_strerror (rc));
512   xfree (data);
513   return rc;
514 }
515
516
517 static int
518 change_sex (void)
519 {
520   char *data;
521   const char *str;
522   int rc;
523
524   data = cpr_get ("cardedit.change_sex",
525                   _("Sex ((M)ale, (F)emale or space): "));
526   if (!data)
527     return -1;
528   trim_spaces (data);
529   cpr_kill_prompt ();
530
531   if (!*data)
532     str = "9";
533   else if ((*data == 'M' || *data == 'm') && !data[1])
534     str = "1";
535   else if ((*data == 'F' || *data == 'f') && !data[1])
536     str = "2";
537   else 
538     {
539       tty_printf (_("Error: invalid response.\n"));
540       xfree (data);
541       return -1;
542     }
543      
544   rc = agent_scd_setattr ("DISP-SEX", str, 1 );
545   if (rc)
546     log_error ("error setting sex: %s\n", gpg_strerror (rc));
547   xfree (data);
548   return rc;
549 }
550
551
552 /* Menu to edit all user changeable values on an OpenPGP card.  Only
553    Key creation is not handled here. */
554 void
555 card_edit (STRLIST commands)
556 {
557   enum cmdids {
558     cmdNOP = 0,
559     cmdQUIT, cmdHELP, cmdLIST, cmdDEBUG,
560     cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX,
561
562     cmdINVCMD
563   };
564
565   static struct {
566     const char *name;
567     enum cmdids id;
568     const char *desc;
569   } cmds[] = {
570     { N_("quit")  , cmdQUIT  , N_("quit this menu") },
571     { N_("q")     , cmdQUIT  , NULL   },
572     { N_("help")  , cmdHELP  , N_("show this help") },
573     {    "?"      , cmdHELP  , NULL   },
574     { N_("list")  , cmdLIST  , N_("list all available data") },
575     { N_("l")     , cmdLIST  , NULL   },
576     { N_("debug") , cmdDEBUG , NULL },
577     { N_("name")  , cmdNAME  , N_("change card holder's name") },
578     { N_("url")   , cmdURL   , N_("change URL to retrieve key") },
579     { N_("login") , cmdLOGIN , N_("change the login name") },
580     { N_("lang")  , cmdLANG  , N_("change the language preferences") },
581     { N_("sex")   , cmdSEX   , N_("change card holder's sex") },
582     { NULL, cmdINVCMD } 
583   };
584
585   enum cmdids cmd = cmdNOP;
586   int have_commands = !!commands;
587   int redisplay = 1;
588   char *answer = NULL;
589
590   if (opt.command_fd != -1)
591     ;
592   else if (opt.batch && !have_commands)
593     {
594       log_error(_("can't do that in batchmode\n"));
595       goto leave;
596     }
597
598   for (;;)
599     {
600       int arg_number;
601       const char *arg_string = "";
602       char *p;
603       int i;
604
605       tty_printf("\n");
606       if (redisplay )
607         {
608           if (opt.with_colons)
609             {
610               card_status (stdout);
611               fflush (stdout);
612             }
613           else
614             {
615               card_status (NULL);
616               tty_printf("\n");
617             }
618           redisplay = 0;
619         }
620
621       do
622         {
623           xfree (answer);
624           if (have_commands)
625             {
626               if (commands)
627                 {
628                   answer = xstrdup (commands->d);
629                   commands = commands->next;
630                 }
631               else if (opt.batch)
632                 {
633                   answer = xstrdup ("quit");
634                 }
635               else
636                 have_commands = 0;
637             }
638
639             if (!have_commands)
640               {
641                 answer = cpr_get_no_help("cardedit.prompt", _("Command> "));
642                 cpr_kill_prompt();
643             }
644             trim_spaces(answer);
645         }
646       while( *answer == '#' );
647
648       arg_number = 0; /* Yes, here is the init which egcc complains about */
649       if (!*answer)
650         cmd = cmdLIST; /* Default to the list command */
651       else if (*answer == CONTROL_D)
652         cmd = cmdQUIT;
653       else {
654         if ((p=strchr (answer,' ')))
655           {
656             *p++ = 0;
657             trim_spaces (answer);
658             trim_spaces (p);
659             arg_number = atoi(p);
660             arg_string = p;
661           }
662
663         for (i=0; cmds[i].name; i++ )
664           if (!ascii_strcasecmp (answer, cmds[i].name ))
665             break;
666
667         cmd = cmds[i].id;
668       }
669
670       switch (cmd)
671         {
672         case cmdHELP:
673           for (i=0; cmds[i].name; i++ )
674             if (cmds[i].desc)
675               tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
676           break;
677
678         case cmdLIST:
679           redisplay = 1;
680           break;
681
682         case cmdNAME:
683           change_name ();
684           break;
685
686         case cmdURL:
687           change_url ();
688           break;
689
690         case cmdLOGIN:
691           change_login ();
692           break;
693
694         case cmdLANG:
695           change_lang ();
696           break;
697
698         case cmdSEX:
699           change_sex ();
700           break;
701
702         case cmdQUIT:
703           goto leave;
704
705         case cmdNOP:
706           break;
707
708         case cmdINVCMD:
709         default:
710           tty_printf ("\n");
711           tty_printf (_("Invalid command  (try \"help\")\n"));
712           break;
713         } /* End command switch. */
714     } /* End of main menu loop. */
715
716  leave:
717   xfree (answer);
718 }
719
720 #endif /*ENABLE_CARD_SUPPORT*/