Marked all unused args on non-W32 platforms.
[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   (void)ctrl;
673
674   *result = NULL;
675
676   rc = read_key_file (grip, &s_skey);
677   if (rc)
678     return rc;
679
680   list = gcry_sexp_find_token (s_skey, "shadowed-private-key", 0 );
681   if (!list)
682     list = gcry_sexp_find_token (s_skey, "protected-private-key", 0 );
683   if (!list)
684     list = gcry_sexp_find_token (s_skey, "private-key", 0 );
685   if (!list)
686     {
687       log_error ("invalid private key format\n");
688       gcry_sexp_release (s_skey);
689       return gpg_error (GPG_ERR_BAD_SECKEY);
690     }
691
692   l2 = gcry_sexp_cadr (list);
693   gcry_sexp_release (list);
694   list = l2;
695   name = gcry_sexp_nth_data (list, 0, &n);
696   if (n==3 && !memcmp (name, "rsa", 3))
697     {
698       algoname = "rsa";
699       elems = "ne";
700     }
701   else if (n==3 && !memcmp (name, "dsa", 3))
702     {
703       algoname = "dsa";
704       elems = "pqgy";
705     }
706   else if (n==3 && !memcmp (name, "elg", 3))
707     {
708       algoname = "elg";
709       elems = "pgy";
710     }
711   else
712     {
713       log_error ("unknown private key algorithm\n");
714       gcry_sexp_release (list);
715       gcry_sexp_release (s_skey);
716       return gpg_error (GPG_ERR_BAD_SECKEY);
717     }
718
719   /* Allocate an array for the parameters and copy them out of the
720      secret key.   FIXME: We should have a generic copy function. */
721   array = xtrycalloc (strlen(elems) + 1, sizeof *array);
722   if (!array)
723     {
724       rc = gpg_error_from_syserror ();
725       gcry_sexp_release (list);
726       gcry_sexp_release (s_skey);
727       return rc;
728     }
729
730   for (idx=0, s=elems; *s; s++, idx++ ) 
731     {
732       l2 = gcry_sexp_find_token (list, s, 1);
733       if (!l2)
734         {
735           /* Required parameter not found.  */
736           for (i=0; i<idx; i++)
737             gcry_mpi_release (array[i]);
738           xfree (array);
739           gcry_sexp_release (list);
740           gcry_sexp_release (s_skey);
741           return gpg_error (GPG_ERR_BAD_SECKEY);
742         }
743       array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
744       gcry_sexp_release (l2);
745       if (!array[idx])
746         {
747           /* Required parameter is invalid. */
748           for (i=0; i<idx; i++)
749             gcry_mpi_release (array[i]);
750           xfree (array);
751           gcry_sexp_release (list);
752           gcry_sexp_release (s_skey);
753           return gpg_error (GPG_ERR_BAD_SECKEY);
754         }
755     }
756   gcry_sexp_release (list);
757   list = NULL;
758
759   uri = NULL;
760   uri_length = 0;
761   uri_sexp = gcry_sexp_find_token (s_skey, "uri", 0);
762   if (uri_sexp)
763     uri = gcry_sexp_nth_data (uri_sexp, 1, &uri_length);
764
765   comment = NULL;
766   comment_length = 0;
767   comment_sexp = gcry_sexp_find_token (s_skey, "comment", 0);
768   if (comment_sexp)
769     comment = gcry_sexp_nth_data (comment_sexp, 1, &comment_length);
770
771   gcry_sexp_release (s_skey);
772   s_skey = NULL;
773
774
775   /* FIXME: The following thing is pretty ugly code; we should
776      investigate how to make it cleaner. Probably code to handle
777      canonical S-expressions in a memory buffer is better suioted for
778      such a task.  After all that is what we do in protect.c.  Neeed
779      to find common patterns and write a straightformward API to use
780      them.  */
781   assert (sizeof (size_t) <= sizeof (void*));
782
783   format = xtrymalloc (15+7*strlen (elems)+10+15+1+1);
784   if (!format)
785     {
786       rc = gpg_error_from_syserror ();
787       for (i=0; array[i]; i++)
788         gcry_mpi_release (array[i]);
789       xfree (array);
790       gcry_sexp_release (uri_sexp);
791       gcry_sexp_release (comment_sexp);
792       return rc;
793     }
794
795   argidx = 0;
796   p = stpcpy (stpcpy (format, "(public-key("), algoname);
797   for (idx=0, s=elems; *s; s++, idx++ ) 
798     {
799       *p++ = '(';
800       *p++ = *s;
801       p = stpcpy (p, " %m)");
802       assert (argidx < DIM (args));
803       args[argidx++] = &array[idx];
804     }
805   *p++ = ')';
806   if (uri)
807     {
808       p = stpcpy (p, "(uri %b)");
809       assert (argidx+1 < DIM (args));
810       args[argidx++] = (void *)uri_length;
811       args[argidx++] = (void *)uri;
812     }
813   if (comment)
814     {
815       p = stpcpy (p, "(comment %b)");
816       assert (argidx+1 < DIM (args));
817       args[argidx++] = (void *)comment_length;
818       args[argidx++] = (void*)comment;
819     }
820   *p++ = ')';
821   *p = 0;
822   assert (argidx < DIM (args));
823   args[argidx] = NULL;
824     
825   rc = gcry_sexp_build_array (&list, NULL, format, args);
826   xfree (format);
827   for (i=0; array[i]; i++)
828     gcry_mpi_release (array[i]);
829   xfree (array);
830   gcry_sexp_release (uri_sexp);
831   gcry_sexp_release (comment_sexp);
832
833   if (!rc)
834     *result = list;
835   return rc;
836 }
837
838
839
840 /* Return the secret key as an S-Exp after locating it using the grip.
841    Returns NULL if key is not available. 0 = key is available */
842 int
843 agent_key_available (const unsigned char *grip)
844 {
845   int i;
846   char *fname;
847   char hexgrip[40+4+1];
848   
849   for (i=0; i < 20; i++)
850     sprintf (hexgrip+2*i, "%02X", grip[i]);
851   strcpy (hexgrip+40, ".key");
852
853   fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
854   i = !access (fname, R_OK)? 0 : -1;
855   xfree (fname);
856   return i;
857 }
858
859
860