Print used library version with --version.
[gnupg.git] / agent / findkey.c
1 /* findkey.c - Locate the secret key
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005,
3  *               2007  Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
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 <fcntl.h>
28 #include <assert.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <assert.h>
32 #include <pth.h> /* (we use pth_sleep) */
33
34 #include "agent.h"
35 #include "i18n.h"
36
37 #ifndef O_BINARY
38 #define O_BINARY 0
39 #endif
40
41 /* Helper to pass data to the check callback of the unprotect function. */
42 struct try_unprotect_arg_s 
43 {
44   ctrl_t ctrl;
45   const unsigned char *protected_key;
46   unsigned char *unprotected_key;
47   int change_required; /* Set by the callback to indicate that the
48                           user should chnage the passphrase.  */
49 };
50
51
52 /* Write an S-expression formatted key to our key storage.  With FORCE
53    pased as true an existing key with the given GRIP will get
54    overwritten.  */
55 int
56 agent_write_private_key (const unsigned char *grip,
57                          const void *buffer, size_t length, int force)
58 {
59   int i;
60   char *fname;
61   FILE *fp;
62   char hexgrip[40+4+1];
63   int fd;
64   
65   for (i=0; i < 20; i++)
66     sprintf (hexgrip+2*i, "%02X", grip[i]);
67   strcpy (hexgrip+40, ".key");
68
69   fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
70
71   if (!force && !access (fname, F_OK))
72     {
73       log_error ("secret key file `%s' already exists\n", fname);
74       xfree (fname);
75       return gpg_error (GPG_ERR_GENERAL);
76     }
77
78   /* In FORCE mode we would like to create FNAME but only if it does
79      not already exist.  We cannot make this guarantee just using
80      POSIX (GNU provides the "x" opentype for fopen, however, this is
81      not portable).  Thus, we use the more flexible open function and
82      then use fdopen to obtain a stream. */
83   fd = open (fname, force? (O_CREAT | O_TRUNC | O_WRONLY | O_BINARY)
84                          : (O_CREAT | O_EXCL | O_WRONLY | O_BINARY),
85              S_IRUSR | S_IWUSR 
86 #ifndef HAVE_W32_SYSTEM
87                  | S_IRGRP 
88 #endif
89                  );
90   if (fd < 0)
91     fp = NULL;
92   else
93     {
94       fp = fdopen (fd, "wb");
95       if (!fp)
96         { 
97           int save_e = errno;
98           close (fd);
99           errno = save_e;
100         }
101     }
102
103   if (!fp) 
104     { 
105       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
106       log_error ("can't create `%s': %s\n", fname, strerror (errno));
107       xfree (fname);
108       return tmperr;
109     }
110
111   if (fwrite (buffer, length, 1, fp) != 1)
112     {
113       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
114       log_error ("error writing `%s': %s\n", fname, strerror (errno));
115       fclose (fp);
116       remove (fname);
117       xfree (fname);
118       return tmperr;
119     }
120   if ( fclose (fp) )
121     {
122       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
123       log_error ("error closing `%s': %s\n", fname, strerror (errno));
124       remove (fname);
125       xfree (fname);
126       return tmperr;
127     }
128   bump_key_eventcounter ();
129   xfree (fname);
130   return 0;
131 }
132
133
134 /* Callback function to try the unprotection from the passpharse query
135    code. */
136 static int
137 try_unprotect_cb (struct pin_entry_info_s *pi)
138 {
139   struct try_unprotect_arg_s *arg = pi->check_cb_arg;
140   size_t dummy;
141   gpg_error_t err;
142   gnupg_isotime_t now, protected_at, tmptime;
143   char *desc = NULL;
144
145   assert (!arg->unprotected_key);
146
147   arg->change_required = 0;
148   err = agent_unprotect (arg->protected_key, pi->pin, protected_at,
149                          &arg->unprotected_key, &dummy);
150   if (err)
151     return err;
152   if (!opt.max_passphrase_days || arg->ctrl->in_passwd)
153     return 0;  /* No regular passphrase change required.  */
154
155   if (!*protected_at)
156     {
157       /* No protection date known - must force passphrase change.  */
158       desc = xtrystrdup (_("Note: This passphrase has never been changed.%0A"
159                            "Please change it now."));
160       if (!desc)
161         return gpg_error_from_syserror ();
162     }
163   else
164     {
165       gnupg_get_isotime (now);
166       gnupg_copy_time (tmptime, protected_at);
167       err = add_days_to_isotime (tmptime, opt.max_passphrase_days);
168       if (err)
169         return err;
170       if (strcmp (now, tmptime) > 0 )
171         {
172           /* Passphrase "expired".  */
173           desc = xtryasprintf 
174             (_("This passphrase has not been changed%%0A"
175                "since %.4s-%.2s-%.2s.  Please change it now."),
176              protected_at, protected_at+4, protected_at+6);
177           if (!desc)
178             return gpg_error_from_syserror ();
179         }
180     }
181
182   if (desc)
183     {
184       /* Change required.  */
185       if (opt.enforce_passphrase_constraints)
186         {
187           err = agent_get_confirmation (arg->ctrl, desc,
188                                         _("Change passphrase"), NULL);
189           if (!err)
190             arg->change_required = 1;
191         }
192       else
193         {
194           err = agent_get_confirmation (arg->ctrl, desc,
195                                         _("Change passphrase"),
196                                         _("I'll change it later"));
197           if (!err)
198             arg->change_required = 1;
199           else if (gpg_err_code (err) == GPG_ERR_CANCELED)
200             err = 0;
201         }
202       xfree (desc);
203     }
204
205   return 0;
206 }
207
208
209 /* Modify a Key description, replacing certain special format
210    characters.  List of currently supported replacements:
211
212    %% - Replaced by a single %
213    %c - Replaced by the content of COMMENT.
214
215    The functions returns 0 on success or an error code.  On success a
216    newly allocated string is stored at the address of RESULT.
217  */
218 static gpg_error_t
219 modify_description (const char *in, const char *comment, char **result)
220 {
221   size_t comment_length;
222   size_t in_len;
223   size_t out_len;
224   char *out;
225   size_t i;
226   int special, pass;
227
228   comment_length = strlen (comment);
229   in_len  = strlen (in);
230
231   /* First pass calculates the length, second pass does the actual
232      copying.  */
233   out = NULL;
234   out_len = 0;
235   for (pass=0; pass < 2; pass++)
236     {
237       special = 0;
238       for (i = 0; i < in_len; i++)
239         {
240           if (special)
241             {
242               special = 0;
243               switch (in[i])
244                 {
245                 case '%':
246                   if (out)
247                     *out++ = '%';
248                   else
249                     out_len++;
250                   break;
251
252                 case 'c': /* Comment.  */
253                   if (out)
254                     {
255                       memcpy (out, comment, comment_length);
256                       out += comment_length;
257                     }
258                   else
259                     out_len += comment_length;
260                   break;
261
262                 default: /* Invalid special sequences are kept as they are. */
263                   if (out)
264                     {
265                       *out++ = '%';
266                       *out++ = in[i];
267                     }
268                   else
269                     out_len+=2;
270                   break;
271                 }
272             }
273           else if (in[i] == '%')
274             special = 1;
275           else
276             {
277               if (out)
278                 *out++ = in[i];
279               else
280                 out_len++;
281             }
282         }
283       
284       if (!pass)
285         {
286           *result = out = xtrymalloc (out_len + 1);
287           if (!out)
288             return gpg_error_from_syserror ();
289         }
290     }
291
292   *out = 0;
293   assert (*result + out_len == out);
294   return 0;
295 }
296
297   
298
299 /* Unprotect the canconical encoded S-expression key in KEYBUF.  GRIP
300    should be the hex encoded keygrip of that key to be used with the
301    caching mechanism. DESC_TEXT may be set to override the default
302    description used for the pinentry. */
303 static int
304 unprotect (ctrl_t ctrl, const char *desc_text,
305            unsigned char **keybuf, const unsigned char *grip, 
306            cache_mode_t cache_mode)
307 {
308   struct pin_entry_info_s *pi;
309   struct try_unprotect_arg_s arg;
310   int rc, i;
311   unsigned char *result;
312   size_t resultlen;
313   char hexgrip[40+1];
314   
315   for (i=0; i < 20; i++)
316     sprintf (hexgrip+2*i, "%02X", grip[i]);
317   hexgrip[40] = 0;
318
319   /* First try to get it from the cache - if there is none or we can't
320      unprotect it, we fall back to ask the user */
321   if (cache_mode != CACHE_MODE_IGNORE)
322     {
323       void *cache_marker;
324       const char *pw;
325       
326     retry:
327       pw = agent_get_cache (hexgrip, cache_mode, &cache_marker);
328       if (pw)
329         {
330           rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
331           agent_unlock_cache_entry (&cache_marker);
332           if (!rc)
333             {
334               xfree (*keybuf);
335               *keybuf = result;
336               return 0;
337             }
338           rc  = 0;
339         }
340
341       /* If the pinentry is currently in use, we wait up to 60 seconds
342          for it to close and check the cache again.  This solves a common
343          situation where several requests for unprotecting a key have
344          been made but the user is still entering the passphrase for
345          the first request.  Because all requests to agent_askpin are
346          serialized they would then pop up one after the other to
347          request the passphrase - despite that the user has already
348          entered it and is then available in the cache.  This
349          implementation is not race free but in the worst case the
350          user has to enter the passphrase only once more. */
351       if (pinentry_active_p (ctrl, 0))
352         {
353           /* Active - wait */
354           if (!pinentry_active_p (ctrl, 60))
355             {
356               /* We need to give the other thread a chance to actually put
357                  it into the cache. */
358               pth_sleep (1); 
359               goto retry;
360             }
361           /* Timeout - better call pinentry now the plain way. */
362         }
363     }
364
365   pi = gcry_calloc_secure (1, sizeof (*pi) + 100);
366   if (!pi)
367     return gpg_error_from_syserror ();
368   pi->max_length = 100;
369   pi->min_digits = 0;  /* we want a real passphrase */
370   pi->max_digits = 8;
371   pi->max_tries = 3;
372   pi->check_cb = try_unprotect_cb;
373   arg.ctrl = ctrl;
374   arg.protected_key = *keybuf;
375   arg.unprotected_key = NULL;
376   arg.change_required = 0;
377   pi->check_cb_arg = &arg;
378
379   rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi);
380   if (!rc)
381     {
382       assert (arg.unprotected_key);
383       if (arg.change_required)
384         {
385           size_t canlen, erroff;
386           gcry_sexp_t s_skey;
387           
388           assert (arg.unprotected_key);
389           canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL);
390           rc = gcry_sexp_sscan (&s_skey, &erroff,
391                                 (char*)arg.unprotected_key, canlen);
392           if (rc)
393             {
394               log_error ("failed to build S-Exp (off=%u): %s\n",
395                          (unsigned int)erroff, gpg_strerror (rc));
396               wipememory (arg.unprotected_key, canlen);
397               xfree (arg.unprotected_key);
398               xfree (pi);
399               return rc;
400             }
401           rc = agent_protect_and_store (ctrl, s_skey);
402           gcry_sexp_release (s_skey);
403           if (rc)
404             {
405               log_error ("changing the passphrase failed: %s\n", 
406                          gpg_strerror (rc));
407               wipememory (arg.unprotected_key, canlen);
408               xfree (arg.unprotected_key);
409               xfree (pi);
410               return rc;
411             }
412         }
413       agent_put_cache (hexgrip, cache_mode, pi->pin, 0);
414       xfree (*keybuf);
415       *keybuf = arg.unprotected_key;
416     }
417   xfree (pi);
418   return rc;
419 }
420
421
422 /* Read the key identified by GRIP from the private key directory and
423    return it as an gcrypt S-expression object in RESULT.  On failure
424    returns an error code and stores NULL at RESULT. */
425 static gpg_error_t
426 read_key_file (const unsigned char *grip, gcry_sexp_t *result)
427 {
428   int i, rc;
429   char *fname;
430   FILE *fp;
431   struct stat st;
432   unsigned char *buf;
433   size_t buflen, erroff;
434   gcry_sexp_t s_skey;
435   char hexgrip[40+4+1];
436   
437   *result = NULL;
438
439   for (i=0; i < 20; i++)
440     sprintf (hexgrip+2*i, "%02X", grip[i]);
441   strcpy (hexgrip+40, ".key");
442
443   fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
444   fp = fopen (fname, "rb");
445   if (!fp)
446     {
447       rc = gpg_error_from_syserror ();
448       log_error ("can't open `%s': %s\n", fname, strerror (errno));
449       xfree (fname);
450       return rc;
451     }
452   
453   if (fstat (fileno(fp), &st))
454     {
455       rc = gpg_error_from_syserror ();
456       log_error ("can't stat `%s': %s\n", fname, strerror (errno));
457       xfree (fname);
458       fclose (fp);
459       return rc;
460     }
461
462   buflen = st.st_size;
463   buf = xtrymalloc (buflen+1);
464   if (!buf || fread (buf, buflen, 1, fp) != 1)
465     {
466       rc = gpg_error_from_syserror ();
467       log_error ("error reading `%s': %s\n", fname, strerror (errno));
468       xfree (fname);
469       fclose (fp);
470       xfree (buf);
471       return rc;
472     }
473
474   /* Convert the file into a gcrypt S-expression object.  */
475   rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen);
476   xfree (fname);
477   fclose (fp);
478   xfree (buf);
479   if (rc)
480     {
481       log_error ("failed to build S-Exp (off=%u): %s\n",
482                  (unsigned int)erroff, gpg_strerror (rc));
483       return rc;
484     }
485   *result = s_skey;
486   return 0;
487 }
488
489
490 /* Return the secret key as an S-Exp in RESULT after locating it using
491    the grip.  Returns NULL in RESULT if the operation should be
492    diverted to a token; SHADOW_INFO will point then to an allocated
493    S-Expression with the shadow_info part from the file.  CACHE_MODE
494    defines now the cache shall be used.  DESC_TEXT may be set to
495    present a custom description for the pinentry. */
496 gpg_error_t
497 agent_key_from_file (ctrl_t ctrl, const char *desc_text,
498                      const unsigned char *grip, unsigned char **shadow_info,
499                      cache_mode_t cache_mode, gcry_sexp_t *result)
500 {
501   int rc;
502   unsigned char *buf;
503   size_t len, buflen, erroff;
504   gcry_sexp_t s_skey;
505   int got_shadow_info = 0;
506   
507   *result = NULL;
508   if (shadow_info)
509       *shadow_info = NULL;
510
511   rc = read_key_file (grip, &s_skey);
512   if (rc)
513     return rc;
514
515   /* For use with the protection functions we also need the key as an
516      canonical encoded S-expression in abuffer.  Create this buffer
517      now.  */
518   len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
519   assert (len);
520   buf = xtrymalloc (len);
521   if (!buf)
522     {
523       rc = gpg_error_from_syserror ();
524       gcry_sexp_release (s_skey);
525       return rc;
526     }
527   len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len);
528   assert (len);
529
530
531   switch (agent_private_key_type (buf))
532     {
533     case PRIVATE_KEY_CLEAR:
534       break; /* no unprotection needed */
535     case PRIVATE_KEY_PROTECTED:
536       {
537         gcry_sexp_t comment_sexp;
538         size_t comment_length;
539         char *desc_text_final;
540         const char *comment = NULL;
541
542         /* Note, that we will take the comment as a C string for
543            display purposes; i.e. all stuff beyond a Nul character is
544            ignored.  */
545         comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
546         if (comment_sexp)
547           comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length);
548         if (!comment)
549           {
550             comment = "";
551             comment_length = 0;
552           }
553
554         desc_text_final = NULL;
555         if (desc_text)
556           {
557             if (comment[comment_length])
558               {
559                 /* Not a C-string; create one.  We might here allocate
560                    more than actually displayed but well, that
561                    shouldn't be a problem.  */
562                 char *tmp = xtrymalloc (comment_length+1);
563                 if (!tmp)
564                   rc = gpg_error_from_syserror ();
565                 else
566                   {
567                     memcpy (tmp, comment, comment_length);
568                     tmp[comment_length] = 0;
569                     rc = modify_description (desc_text, tmp, &desc_text_final);
570                     xfree (tmp);
571                   }
572               }
573             else
574               rc = modify_description (desc_text, comment, &desc_text_final);
575           }
576
577         if (!rc)
578           {
579             rc = unprotect (ctrl, desc_text_final, &buf, grip, cache_mode);
580             if (rc)
581               log_error ("failed to unprotect the secret key: %s\n",
582                          gpg_strerror (rc));
583           }
584         
585         gcry_sexp_release (comment_sexp);
586         xfree (desc_text_final);
587       }
588       break;
589     case PRIVATE_KEY_SHADOWED:
590       if (shadow_info)
591         {
592           const unsigned char *s;
593           size_t n;
594
595           rc = agent_get_shadow_info (buf, &s);
596           if (!rc)
597             {
598               n = gcry_sexp_canon_len (s, 0, NULL,NULL);
599               assert (n);
600               *shadow_info = xtrymalloc (n);
601               if (!*shadow_info)
602                 rc = out_of_core ();
603               else
604                 {
605                   memcpy (*shadow_info, s, n);
606                   rc = 0;
607                   got_shadow_info = 1;
608                 }
609             }
610           if (rc)
611             log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc));
612         }
613       else
614         rc = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
615       break;
616     default:
617       log_error ("invalid private key format\n");
618       rc = gpg_error (GPG_ERR_BAD_SECKEY);
619       break;
620     }
621   gcry_sexp_release (s_skey);
622   s_skey = NULL;
623   if (rc || got_shadow_info)
624     {
625       xfree (buf);
626       return rc;
627     }
628
629   buflen = gcry_sexp_canon_len (buf, 0, NULL, NULL);
630   rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen);
631   wipememory (buf, buflen);
632   xfree (buf);
633   if (rc)
634     {
635       log_error ("failed to build S-Exp (off=%u): %s\n",
636                  (unsigned int)erroff, gpg_strerror (rc));
637       return rc;
638     }
639
640   *result = s_skey;
641   return 0;
642 }
643
644
645
646 /* Return the public key for the keygrip GRIP.  The result is stored
647    at RESULT.  This function extracts the public key from the private
648    key database.  On failure an error code is returned and NULL stored
649    at RESULT. */
650 gpg_error_t
651 agent_public_key_from_file (ctrl_t ctrl, 
652                             const unsigned char *grip,
653                             gcry_sexp_t *result)
654 {
655   int i, idx, rc;
656   gcry_sexp_t s_skey;
657   const char *algoname;
658   gcry_sexp_t uri_sexp, comment_sexp;
659   const char *uri, *comment;
660   size_t uri_length, comment_length;
661   char *format, *p;
662   void *args[4+2+2+1]; /* Size is max. # of elements + 2 for uri + 2
663                            for comment + end-of-list.  */
664   int argidx;
665   gcry_sexp_t list, l2;
666   const char *name;
667   const char *s;
668   size_t n;
669   const char *elems;
670   gcry_mpi_t *array;
671
672   *result = NULL;
673
674   rc = read_key_file (grip, &s_skey);
675   if (rc)
676     return rc;
677
678   list = gcry_sexp_find_token (s_skey, "shadowed-private-key", 0 );
679   if (!list)
680     list = gcry_sexp_find_token (s_skey, "protected-private-key", 0 );
681   if (!list)
682     list = gcry_sexp_find_token (s_skey, "private-key", 0 );
683   if (!list)
684     {
685       log_error ("invalid private key format\n");
686       gcry_sexp_release (s_skey);
687       return gpg_error (GPG_ERR_BAD_SECKEY);
688     }
689
690   l2 = gcry_sexp_cadr (list);
691   gcry_sexp_release (list);
692   list = l2;
693   name = gcry_sexp_nth_data (list, 0, &n);
694   if (n==3 && !memcmp (name, "rsa", 3))
695     {
696       algoname = "rsa";
697       elems = "ne";
698     }
699   else if (n==3 && !memcmp (name, "dsa", 3))
700     {
701       algoname = "dsa";
702       elems = "pqgy";
703     }
704   else if (n==3 && !memcmp (name, "elg", 3))
705     {
706       algoname = "elg";
707       elems = "pgy";
708     }
709   else
710     {
711       log_error ("unknown private key algorithm\n");
712       gcry_sexp_release (list);
713       gcry_sexp_release (s_skey);
714       return gpg_error (GPG_ERR_BAD_SECKEY);
715     }
716
717   /* Allocate an array for the parameters and copy them out of the
718      secret key.   FIXME: We should have a generic copy function. */
719   array = xtrycalloc (strlen(elems) + 1, sizeof *array);
720   if (!array)
721     {
722       rc = gpg_error_from_syserror ();
723       gcry_sexp_release (list);
724       gcry_sexp_release (s_skey);
725       return rc;
726     }
727
728   for (idx=0, s=elems; *s; s++, idx++ ) 
729     {
730       l2 = gcry_sexp_find_token (list, s, 1);
731       if (!l2)
732         {
733           /* Required parameter not found.  */
734           for (i=0; i<idx; i++)
735             gcry_mpi_release (array[i]);
736           xfree (array);
737           gcry_sexp_release (list);
738           gcry_sexp_release (s_skey);
739           return gpg_error (GPG_ERR_BAD_SECKEY);
740         }
741       array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
742       gcry_sexp_release (l2);
743       if (!array[idx])
744         {
745           /* Required parameter is invalid. */
746           for (i=0; i<idx; i++)
747             gcry_mpi_release (array[i]);
748           xfree (array);
749           gcry_sexp_release (list);
750           gcry_sexp_release (s_skey);
751           return gpg_error (GPG_ERR_BAD_SECKEY);
752         }
753     }
754   gcry_sexp_release (list);
755   list = NULL;
756
757   uri = NULL;
758   uri_length = 0;
759   uri_sexp = gcry_sexp_find_token (s_skey, "uri", 0);
760   if (uri_sexp)
761     uri = gcry_sexp_nth_data (uri_sexp, 1, &uri_length);
762
763   comment = NULL;
764   comment_length = 0;
765   comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
766   if (comment_sexp)
767     comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length);
768
769   gcry_sexp_release (s_skey);
770   s_skey = NULL;
771
772
773   /* FIXME: The following thing is pretty ugly code; we should
774      investigate how to make it cleaner. Probably code to handle
775      canonical S-expressions in a memory buffer is better suioted for
776      such a task.  After all that is what we do in protect.c.  Neeed
777      to find common patterns and write a straightformward API to use
778      them.  */
779   assert (sizeof (size_t) <= sizeof (void*));
780
781   format = xtrymalloc (15+7*strlen (elems)+10+15+1+1);
782   if (!format)
783     {
784       rc = gpg_error_from_syserror ();
785       for (i=0; array[i]; i++)
786         gcry_mpi_release (array[i]);
787       xfree (array);
788       gcry_sexp_release (uri_sexp);
789       gcry_sexp_release (comment_sexp);
790       return rc;
791     }
792
793   argidx = 0;
794   p = stpcpy (stpcpy (format, "(public-key("), algoname);
795   for (idx=0, s=elems; *s; s++, idx++ ) 
796     {
797       *p++ = '(';
798       *p++ = *s;
799       p = stpcpy (p, " %m)");
800       assert (argidx < DIM (args));
801       args[argidx++] = &array[idx];
802     }
803   *p++ = ')';
804   if (uri)
805     {
806       p = stpcpy (p, "(uri %b)");
807       assert (argidx+1 < DIM (args));
808       args[argidx++] = (void *)uri_length;
809       args[argidx++] = (void *)uri;
810     }
811   if (comment)
812     {
813       p = stpcpy (p, "(comment %b)");
814       assert (argidx+1 < DIM (args));
815       args[argidx++] = (void *)comment_length;
816       args[argidx++] = (void*)comment;
817     }
818   *p++ = ')';
819   *p = 0;
820   assert (argidx < DIM (args));
821   args[argidx] = NULL;
822     
823   rc = gcry_sexp_build_array (&list, NULL, format, args);
824   xfree (format);
825   for (i=0; array[i]; i++)
826     gcry_mpi_release (array[i]);
827   xfree (array);
828   gcry_sexp_release (uri_sexp);
829   gcry_sexp_release (comment_sexp);
830
831   if (!rc)
832     *result = list;
833   return rc;
834 }
835
836
837
838 /* Return the secret key as an S-Exp after locating it using the grip.
839    Returns NULL if key is not available. 0 = key is available */
840 int
841 agent_key_available (const unsigned char *grip)
842 {
843   int i;
844   char *fname;
845   char hexgrip[40+4+1];
846   
847   for (i=0; i < 20; i++)
848     sprintf (hexgrip+2*i, "%02X", grip[i]);
849   strcpy (hexgrip+40, ".key");
850
851   fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
852   i = !access (fname, R_OK)? 0 : -1;
853   xfree (fname);
854   return i;
855 }
856
857
858