69f184474d79d5ea4d368428e4bc91d47f97b69d
[gnupg.git] / agent / divert-scd.c
1 /* divert-scd.c - divert operations to the scdaemon 
2  *      Copyright (C) 2002, 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 #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 (CTRL ctrl, 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 gpg_error (GPG_ERR_INV_SEXP);
52   s++;
53   n = snext (&s);
54   if (!n)
55     return gpg_error (GPG_ERR_INV_SEXP);
56   want_sn = xtrymalloc (n*2+1);
57   if (!want_sn)
58     return 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 gpg_error (GPG_ERR_INV_SEXP);
72   want_kid = xtrymalloc (n+1);
73   if (!want_kid)
74     {
75       gpg_error_t tmperr = out_of_core ();
76       xfree (want_sn);
77       return tmperr;
78     }
79   memcpy (want_kid, s, n);
80   want_kid[n] = 0;
81
82   for (;;)
83     {
84       rc = agent_card_serialno (&serialno);
85       if (!rc)
86         {
87           log_debug ("detected card with S/N %s\n", serialno);
88           i = strcmp (serialno, want_sn);
89           xfree (serialno);
90           serialno = NULL;
91           if (!i)
92             {
93               xfree (want_sn);
94               *r_kid = want_kid;
95               return 0; /* yes, we have the correct card */
96             }
97         }
98       else if (gpg_err_code (rc) == GPG_ERR_CARD_NOT_PRESENT)
99         {
100           log_debug ("no card present\n");
101           rc = 0;
102           no_card = 1;
103         }
104       else
105         {
106           log_error ("error accesing card: %s\n", gpg_strerror (rc));
107         }
108
109       if (!rc)
110         {
111           if (asprintf (&desc,
112                     "%s:%%0A%%0A"
113                     "  \"%.*s\"",
114                     no_card? "Please insert the card with serial number" 
115                     : "Please remove the current card and "
116                     "insert the one with serial number",
117                     want_sn_displen, want_sn) < 0)
118             {
119               rc = out_of_core ();
120             }
121           else
122             {
123               rc = agent_get_confirmation (ctrl, desc, NULL, NULL);
124               free (desc);
125             }
126         }
127       if (rc)
128         {
129           xfree (want_sn);
130           xfree (want_kid);
131           return rc;
132         }
133     }
134 }
135
136
137 /* Put the DIGEST into an DER encoded comtainer and return it in R_VAL. */
138 static int
139 encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo,
140                     unsigned char **r_val, size_t *r_len)
141 {
142   byte *frame;
143   byte asn[100];
144   size_t asnlen;
145
146   asnlen = DIM(asn);
147   if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen))
148     {
149       log_error ("no object identifier for algo %d\n", algo);
150       return gpg_error (GPG_ERR_INTERNAL);
151     }
152
153   frame = xtrymalloc (asnlen + digestlen);
154   if (!frame)
155     return out_of_core ();
156   memcpy (frame, asn, asnlen);
157   memcpy (frame+asnlen, digest, digestlen);
158   if (DBG_CRYPTO)
159     log_printhex ("encoded hash:", frame, asnlen+digestlen);
160       
161   *r_val = frame;
162   *r_len = asnlen+digestlen;
163   return 0;
164 }
165
166
167 /* Callback used to ask for the PIN which should be set into BUF.  The
168    buf has been allocated by the caller and is of size MAXBUF which
169    includes the terminating null.  The function should return an UTF-8
170    string with the passphrase, the buffer may optionally be padded
171    with arbitrary characters */
172 static int 
173 getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf)
174 {
175   struct pin_entry_info_s *pi;
176   int rc;
177   char *desc;
178   CTRL ctrl = opaque;
179
180   if (maxbuf < 2)
181     return gpg_error (GPG_ERR_INV_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 (ctrl, 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 (CTRL ctrl, 
214                const unsigned char *digest, size_t digestlen, int algo,
215                const unsigned char *shadow_info, unsigned char **r_sig)
216 {
217   int rc;
218   char *kid;
219   size_t siglen;
220   char *sigval;
221   unsigned char *data;
222   size_t ndata;
223
224   rc = ask_for_card (ctrl, shadow_info, &kid);
225   if (rc)
226     return rc;
227
228   rc = encode_md_for_card (digest, digestlen, algo, 
229                            &data, &ndata);
230   if (rc)
231     return rc;
232
233   rc = agent_card_pksign (kid, getpin_cb, ctrl,
234                           data, ndata, &sigval, &siglen);
235   if (!rc)
236     *r_sig = sigval;
237   xfree (data);
238   xfree (kid);
239   
240   return rc;
241 }
242
243
244 /* Decrypt the the value given asn an S-expression in CIPHER using the
245    key identified by SHADOW_INFO and return the plaintext in an
246    allocated buffer in R_BUF.  */
247 int  
248 divert_pkdecrypt (CTRL ctrl,
249                   const unsigned char *cipher,
250                   const unsigned char *shadow_info,
251                   char **r_buf, size_t *r_len)
252 {
253   int rc;
254   char *kid;
255   const unsigned char *s;
256   size_t n;
257   const unsigned char *ciphertext;
258   size_t ciphertextlen;
259   char *plaintext;
260   size_t plaintextlen;
261
262   s = cipher;
263   if (*s != '(')
264     return gpg_error (GPG_ERR_INV_SEXP);
265   s++;
266   n = snext (&s);
267   if (!n)
268     return gpg_error (GPG_ERR_INV_SEXP); 
269   if (!smatch (&s, n, "enc-val"))
270     return gpg_error (GPG_ERR_UNKNOWN_SEXP); 
271   if (*s != '(')
272     return gpg_error (GPG_ERR_UNKNOWN_SEXP);
273   s++;
274   n = snext (&s);
275   if (!n)
276     return gpg_error (GPG_ERR_INV_SEXP); 
277   if (!smatch (&s, n, "rsa"))
278     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); 
279   if (*s != '(')
280     return gpg_error (GPG_ERR_UNKNOWN_SEXP);
281   s++;
282   n = snext (&s);
283   if (!n)
284     return gpg_error (GPG_ERR_INV_SEXP); 
285   if (!smatch (&s, n, "a"))
286     return gpg_error (GPG_ERR_UNKNOWN_SEXP);
287   n = snext (&s);
288   if (!n)
289     return gpg_error (GPG_ERR_UNKNOWN_SEXP); 
290   ciphertext = s;
291   ciphertextlen = n;
292
293   rc = ask_for_card (ctrl, shadow_info, &kid);
294   if (rc)
295     return rc;
296
297   rc = agent_card_pkdecrypt (kid, getpin_cb, ctrl,
298                              ciphertext, ciphertextlen,
299                              &plaintext, &plaintextlen);
300   if (!rc)
301     {
302       *r_buf = plaintext;
303       *r_len = plaintextlen;
304     }
305   xfree (kid);
306   return rc;
307 }
308
309
310 int  
311 divert_generic_cmd (CTRL ctrl, const char *cmdline, void *assuan_context)
312 {
313   return agent_card_scd (cmdline, getpin_cb, ctrl, assuan_context);
314 }
315
316
317
318
319