* divert-scd.c (getpin_cb): Pass a more descritive text to the
[gnupg.git] / agent / divert-scd.c
1 /* divert-scd.c - divert operations to the scdaemon 
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 "sexp-parse.h"
33 #include "i18n.h"
34
35
36 static int
37 ask_for_card (const unsigned char *shadow_info, char **r_kid)
38 {
39   int rc, i;
40   const unsigned char *s;
41   size_t n;
42   char *serialno;
43   int no_card = 0;
44   char *desc;
45   char *want_sn, *want_kid;
46   int want_sn_displen;
47
48   *r_kid = NULL;
49   s = shadow_info;
50   if (*s != '(')
51     return GNUPG_Invalid_Sexp;
52   s++;
53   n = snext (&s);
54   if (!n)
55     return GNUPG_Invalid_Sexp;
56   want_sn = xtrymalloc (n*2+1);
57   if (!want_sn)
58     return GNUPG_Out_Of_Core;
59   for (i=0; i < n; i++)
60     sprintf (want_sn+2*i, "%02X", s[i]);
61   s += n;
62   /* We assume that a 20 byte serial number is a standard one which
63      seems to have the property to have a zero in the last nibble.  We
64      don't display this '0' because it may confuse the user */
65   want_sn_displen = strlen (want_sn);
66   if (want_sn_displen == 20 && want_sn[19] == '0')
67     want_sn_displen--;
68
69   n = snext (&s);
70   if (!n)
71     return GNUPG_Invalid_Sexp;
72   want_kid = xtrymalloc (n+1);
73   if (!want_kid)
74     {
75       xfree (want_sn);
76       return GNUPG_Out_Of_Core;
77     }
78   memcpy (want_kid, s, n);
79   want_kid[n] = 0;
80
81   for (;;)
82     {
83       rc = agent_card_serialno (&serialno);
84       if (!rc)
85         {
86           log_debug ("detected card with S/N %s\n", serialno);
87           i = strcmp (serialno, want_sn);
88           xfree (serialno);
89           serialno = NULL;
90           if (!i)
91             {
92               xfree (want_sn);
93               *r_kid = want_kid;
94               return 0; /* yes, we have the correct card */
95             }
96         }
97       else if (rc == GNUPG_Card_Not_Present)
98         {
99           log_debug ("no card present\n");
100           rc = 0;
101           no_card = 1;
102         }
103       else
104         {
105           log_error ("error accesing card: %s\n", gnupg_strerror (rc));
106         }
107
108       if (!rc)
109         {
110           if (asprintf (&desc,
111                     "%s:%%0A%%0A"
112                     "  \"%.*s\"",
113                     no_card? "Please insert the card with serial number" 
114                     : "Please remove the current card and "
115                     "insert the one with serial number",
116                     want_sn_displen, want_sn) < 0)
117             {
118               rc = GNUPG_Out_Of_Core;
119             }
120           else
121             {
122               rc = agent_get_confirmation (desc, NULL, NULL);
123               free (desc);
124             }
125         }
126       if (rc)
127         {
128           xfree (want_sn);
129           xfree (want_kid);
130           return rc;
131         }
132     }
133 }
134
135
136 /* Put the DIGEST into an DER encoded comtainer and return it in R_VAL. */
137 static int
138 encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
139                     unsigned char **r_val, size_t *r_len)
140 {
141   byte *frame;
142   byte asn[100];
143   size_t asnlen;
144
145   asnlen = DIM(asn);
146   if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
147     {
148       log_error ("no object identifier for algo %d\n", algo);
149       return GNUPG_Internal_Error;
150     }
151
152   frame = xtrymalloc (asnlen + digestlen);
153   if (!frame)
154     return GNUPG_Out_Of_Core;
155   memcpy (frame, asn, asnlen);
156   memcpy (frame+asnlen, digest, digestlen);
157   if (DBG_CRYPTO)
158     log_printhex ("encoded hash:", frame, asnlen+digestlen);
159       
160   *r_val = frame;
161   *r_len = asnlen+digestlen;
162   return 0;
163 }
164
165
166 /* Callback used to ask for the PIN which should be set into BUF.  The
167    buf has been allocated by the caller and is of size MAXBUF which
168    includes the terminating null.  The function should return an UTF-8
169    string with the passphrase, the buffer may optionally be padded
170    with arbitrary characters */
171 static int 
172 getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
173 {
174   struct pin_entry_info_s *pi;
175   int rc;
176   char *desc;
177
178   assert (!opaque);
179
180   if (maxbuf < 2)
181     return GNUPG_Invalid_Value;
182
183
184   /* FIXME: keep PI and TRIES in OPAQUE.  Frankly this is a whole
185      mess because we should call the card's verify function from the
186      pinentry check pin CB. */
187   pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
188   pi->max_length = maxbuf-1;
189   pi->min_digits = 0;  /* we want a real passphrase */
190   pi->max_digits = 8;
191   pi->max_tries = 3;
192
193   if ( asprintf (&desc, _("Please enter the PIN%s%s%s to unlock the card"), 
194                  info? " (`":"",
195                  info? info:"",
196                  info? "')":"") < 0)
197     desc = NULL;
198   rc = agent_askpin (desc?desc:info, pi);
199   free (desc);
200   if (!rc)
201     {
202       strncpy (buf, pi->pin, maxbuf-1);
203       buf[maxbuf-1] = 0;
204     }
205   xfree (pi);
206   return rc;
207 }
208
209
210
211
212 int
213 divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
214                const unsigned char *shadow_info, unsigned char **r_sig)
215 {
216   int rc;
217   char *kid;
218   size_t siglen;
219   char *sigval;
220   unsigned char *data;
221   size_t ndata;
222
223   rc = ask_for_card (shadow_info, &kid);
224   if (rc)
225     return rc;
226
227   rc = encode_md_for_card (digest, digestlen, algo, 
228                            &data, &ndata);
229   if (rc)
230     return rc;
231
232   rc = agent_card_pksign (kid, getpin_cb, NULL,
233                           data, ndata, &sigval, &siglen);
234   if (!rc)
235     *r_sig = sigval;
236   xfree (data);
237   xfree (kid);
238   
239   return rc;
240 }
241
242
243 /* Decrypt the the value given asn an S-expression in CIPHER using the
244    key identified by SHADOW_INFO and return the plaintext in an
245    allocated buffer in R_BUF.  */
246 int  
247 divert_pkdecrypt (const unsigned char *cipher,
248                   const unsigned char *shadow_info,
249                   char **r_buf, size_t *r_len)
250 {
251   int rc;
252   char *kid;
253   const unsigned char *s;
254   size_t n;
255   const unsigned char *ciphertext;
256   size_t ciphertextlen;
257   char *plaintext;
258   size_t plaintextlen;
259
260   s = cipher;
261   if (*s != '(')
262     return GNUPG_Invalid_Sexp;
263   s++;
264   n = snext (&s);
265   if (!n)
266     return GNUPG_Invalid_Sexp; 
267   if (!smatch (&s, n, "enc-val"))
268     return GNUPG_Unknown_Sexp; 
269   if (*s != '(')
270     return GNUPG_Unknown_Sexp;
271   s++;
272   n = snext (&s);
273   if (!n)
274     return GNUPG_Invalid_Sexp; 
275   if (!smatch (&s, n, "rsa"))
276     return GNUPG_Unsupported_Algorithm; 
277   if (*s != '(')
278     return GNUPG_Unknown_Sexp;
279   s++;
280   n = snext (&s);
281   if (!n)
282     return GNUPG_Invalid_Sexp; 
283   if (!smatch (&s, n, "a"))
284     return GNUPG_Unknown_Sexp;
285   n = snext (&s);
286   if (!n)
287     return GNUPG_Unknown_Sexp; 
288   ciphertext = s;
289   ciphertextlen = n;
290
291   rc = ask_for_card (shadow_info, &kid);
292   if (rc)
293     return rc;
294
295   rc = agent_card_pkdecrypt (kid, getpin_cb, NULL,
296                              ciphertext, ciphertextlen,
297                              &plaintext, &plaintextlen);
298   if (!rc)
299     {
300       *r_buf = plaintext;
301       *r_len = plaintextlen;
302     }
303   xfree (kid);
304   return rc;
305 }