* agent.h: Add a callback function to the pin_entry_info structure.
[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
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   int tries = 0;
177
178   assert (!opaque);
179
180   if (maxbuf < 2)
181     return GNUPG_Invalid_Value;
182
183   /* FIXME: keep PI and TRIES in OPAQUE.  Actually this is a whole
184      mess becuase we should call the card's verify function from the
185      pinentry check pin CB. */
186   pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
187   pi->max_length = maxbuf-1;
188   pi->min_digits = 0;  /* we want a real passphrase */
189   pi->max_digits = 8;
190   pi->max_tries = 3;
191
192   rc = agent_askpin (info, pi);
193   if (!rc)
194     {
195       strncpy (buf, pi->pin, maxbuf-1);
196       buf[maxbuf-1] = 0;
197     }
198   xfree (pi);
199   return rc;
200 }
201
202
203
204
205 int
206 divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
207                const unsigned char *shadow_info, unsigned char **r_sig)
208 {
209   int rc;
210   char *kid;
211   size_t siglen;
212   char *sigval;
213   unsigned char *data;
214   size_t ndata;
215
216   rc = ask_for_card (shadow_info, &kid);
217   if (rc)
218     return rc;
219
220   rc = encode_md_for_card (digest, digestlen, algo, 
221                            &data, &ndata);
222   if (rc)
223     return rc;
224
225   rc = agent_card_pksign (kid, getpin_cb, NULL,
226                           data, ndata, &sigval, &siglen);
227   if (!rc)
228     *r_sig = sigval;
229   xfree (data);
230   xfree (kid);
231   
232   return rc;
233 }
234
235
236 /* Decrypt the the value given asn an S-expression in CIPHER using the
237    key identified by SHADOW_INFO and return the plaintext in an
238    allocated buffer in R_BUF.  */
239 int  
240 divert_pkdecrypt (const unsigned char *cipher,
241                   const unsigned char *shadow_info,
242                   char **r_buf, size_t *r_len)
243 {
244   int rc;
245   char *kid;
246   const unsigned char *s;
247   size_t n;
248   const unsigned char *ciphertext;
249   size_t ciphertextlen;
250   char *plaintext;
251   size_t plaintextlen;
252
253   s = cipher;
254   if (*s != '(')
255     return GNUPG_Invalid_Sexp;
256   s++;
257   n = snext (&s);
258   if (!n)
259     return GNUPG_Invalid_Sexp; 
260   if (!smatch (&s, n, "enc-val"))
261     return GNUPG_Unknown_Sexp; 
262   if (*s != '(')
263     return GNUPG_Unknown_Sexp;
264   s++;
265   n = snext (&s);
266   if (!n)
267     return GNUPG_Invalid_Sexp; 
268   if (!smatch (&s, n, "rsa"))
269     return GNUPG_Unsupported_Algorithm; 
270   if (*s != '(')
271     return GNUPG_Unknown_Sexp;
272   s++;
273   n = snext (&s);
274   if (!n)
275     return GNUPG_Invalid_Sexp; 
276   if (!smatch (&s, n, "a"))
277     return GNUPG_Unknown_Sexp;
278   n = snext (&s);
279   if (!n)
280     return GNUPG_Unknown_Sexp; 
281   ciphertext = s;
282   ciphertextlen = n;
283
284   rc = ask_for_card (shadow_info, &kid);
285   if (rc)
286     return rc;
287
288   rc = agent_card_pkdecrypt (kid, getpin_cb, NULL,
289                              ciphertext, ciphertextlen,
290                              &plaintext, &plaintextlen);
291   if (!rc)
292     {
293       *r_buf = plaintext;
294       *r_len = plaintextlen;
295     }
296   xfree (kid);
297   return rc;
298 }