Add --reload command to gpgconf.
[gnupg.git] / agent / findkey.c
blobddcd26267b43246208fe7ccd91017a2cd24496fc
1 /* findkey.c - Locate the secret key
2 * Copyright (C) 2001, 2002, 2003, 2004, 2005,
3 * 2007 Free Software Foundation, Inc.
5 * This file is part of GnuPG.
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.
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.
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/>.
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) */
34 #include "agent.h"
35 #include "i18n.h"
37 #ifndef O_BINARY
38 #define O_BINARY 0
39 #endif
41 /* Helper to pass data to the check callback of the unprotect function. */
42 struct try_unprotect_arg_s
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. */
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)
59 int i;
60 char *fname;
61 FILE *fp;
62 char hexgrip[40+4+1];
63 int fd;
65 for (i=0; i < 20; i++)
66 sprintf (hexgrip+2*i, "%02X", grip[i]);
67 strcpy (hexgrip+40, ".key");
69 fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
71 if (!force && !access (fname, F_OK))
73 log_error ("secret key file `%s' already exists\n", fname);
74 xfree (fname);
75 return gpg_error (GPG_ERR_GENERAL);
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
90 if (fd < 0)
91 fp = NULL;
92 else
94 fp = fdopen (fd, "wb");
95 if (!fp)
97 int save_e = errno;
98 close (fd);
99 errno = save_e;
103 if (!fp)
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;
111 if (fwrite (buffer, length, 1, fp) != 1)
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;
120 if ( fclose (fp) )
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;
128 bump_key_eventcounter ();
129 xfree (fname);
130 return 0;
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)
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;
145 assert (!arg->unprotected_key);
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. */
155 if (!*protected_at)
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 ();
163 else
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 )
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 ();
182 if (desc)
184 /* Change required. */
185 if (opt.enforce_passphrase_constraints)
187 err = agent_get_confirmation (arg->ctrl, desc,
188 _("Change passphrase"), NULL);
189 if (!err)
190 arg->change_required = 1;
192 else
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;
202 xfree (desc);
205 return 0;
209 /* Modify a Key description, replacing certain special format
210 characters. List of currently supported replacements:
212 %% - Replaced by a single %
213 %c - Replaced by the content of COMMENT.
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.
218 static gpg_error_t
219 modify_description (const char *in, const char *comment, char **result)
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;
228 comment_length = strlen (comment);
229 in_len = strlen (in);
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++)
237 special = 0;
238 for (i = 0; i < in_len; i++)
240 if (special)
242 special = 0;
243 switch (in[i])
245 case '%':
246 if (out)
247 *out++ = '%';
248 else
249 out_len++;
250 break;
252 case 'c': /* Comment. */
253 if (out)
255 memcpy (out, comment, comment_length);
256 out += comment_length;
258 else
259 out_len += comment_length;
260 break;
262 default: /* Invalid special sequences are kept as they are. */
263 if (out)
265 *out++ = '%';
266 *out++ = in[i];
268 else
269 out_len+=2;
270 break;
273 else if (in[i] == '%')
274 special = 1;
275 else
277 if (out)
278 *out++ = in[i];
279 else
280 out_len++;
284 if (!pass)
286 *result = out = xtrymalloc (out_len + 1);
287 if (!out)
288 return gpg_error_from_syserror ();
292 *out = 0;
293 assert (*result + out_len == out);
294 return 0;
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)
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];
315 for (i=0; i < 20; i++)
316 sprintf (hexgrip+2*i, "%02X", grip[i]);
317 hexgrip[40] = 0;
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)
323 void *cache_marker;
324 const char *pw;
326 retry:
327 pw = agent_get_cache (hexgrip, cache_mode, &cache_marker);
328 if (pw)
330 rc = agent_unprotect (*keybuf, pw, NULL, &result, &resultlen);
331 agent_unlock_cache_entry (&cache_marker);
332 if (!rc)
334 xfree (*keybuf);
335 *keybuf = result;
336 return 0;
338 rc = 0;
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))
353 /* Active - wait */
354 if (!pinentry_active_p (ctrl, 60))
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;
361 /* Timeout - better call pinentry now the plain way. */
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;
379 rc = agent_askpin (ctrl, desc_text, NULL, NULL, pi);
380 if (!rc)
382 assert (arg.unprotected_key);
383 if (arg.change_required)
385 size_t canlen, erroff;
386 gcry_sexp_t s_skey;
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)
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;
401 rc = agent_protect_and_store (ctrl, s_skey);
402 gcry_sexp_release (s_skey);
403 if (rc)
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;
413 agent_put_cache (hexgrip, cache_mode, pi->pin, 0);
414 xfree (*keybuf);
415 *keybuf = arg.unprotected_key;
417 xfree (pi);
418 return rc;
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)
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];
437 *result = NULL;
439 for (i=0; i < 20; i++)
440 sprintf (hexgrip+2*i, "%02X", grip[i]);
441 strcpy (hexgrip+40, ".key");
443 fname = make_filename (opt.homedir, GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL);
444 fp = fopen (fname, "rb");
445 if (!fp)
447 rc = gpg_error_from_syserror ();
448 log_error ("can't open `%s': %s\n", fname, strerror (errno));
449 xfree (fname);
450 return rc;
453 if (fstat (fileno(fp), &st))
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;
462 buflen = st.st_size;
463 buf = xtrymalloc (buflen+1);
464 if (!buf || fread (buf, buflen, 1, fp) != 1)
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;
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)
481 log_error ("failed to build S-Exp (off=%u): %s\n",
482 (unsigned int)erroff, gpg_strerror (rc));
483 return rc;
485 *result = s_skey;
486 return 0;
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)
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;
507 *result = NULL;
508 if (shadow_info)
509 *shadow_info = NULL;
511 rc = read_key_file (grip, &s_skey);
512 if (rc)
513 return rc;
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)
523 rc = gpg_error_from_syserror ();
524 gcry_sexp_release (s_skey);
525 return rc;
527 len = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, buf, len);
528 assert (len);
531 switch (agent_private_key_type (buf))
533 case PRIVATE_KEY_CLEAR:
534 break; /* no unprotection needed */
535 case PRIVATE_KEY_PROTECTED:
537 gcry_sexp_t comment_sexp;
538 size_t comment_length;
539 char *desc_text_final;
540 const char *comment = NULL;
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)
550 comment = "";
551 comment_length = 0;
554 desc_text_final = NULL;
555 if (desc_text)
557 if (comment[comment_length])
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
567 memcpy (tmp, comment, comment_length);
568 tmp[comment_length] = 0;
569 rc = modify_description (desc_text, tmp, &desc_text_final);
570 xfree (tmp);
573 else
574 rc = modify_description (desc_text, comment, &desc_text_final);
577 if (!rc)
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));
585 gcry_sexp_release (comment_sexp);
586 xfree (desc_text_final);
588 break;
589 case PRIVATE_KEY_SHADOWED:
590 if (shadow_info)
592 const unsigned char *s;
593 size_t n;
595 rc = agent_get_shadow_info (buf, &s);
596 if (!rc)
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
605 memcpy (*shadow_info, s, n);
606 rc = 0;
607 got_shadow_info = 1;
610 if (rc)
611 log_error ("get_shadow_info failed: %s\n", gpg_strerror (rc));
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;
621 gcry_sexp_release (s_skey);
622 s_skey = NULL;
623 if (rc || got_shadow_info)
625 xfree (buf);
626 return rc;
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)
635 log_error ("failed to build S-Exp (off=%u): %s\n",
636 (unsigned int)erroff, gpg_strerror (rc));
637 return rc;
640 *result = s_skey;
641 return 0;
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)
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;
672 (void)ctrl;
674 *result = NULL;
676 rc = read_key_file (grip, &s_skey);
677 if (rc)
678 return rc;
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)
687 log_error ("invalid private key format\n");
688 gcry_sexp_release (s_skey);
689 return gpg_error (GPG_ERR_BAD_SECKEY);
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))
698 algoname = "rsa";
699 elems = "ne";
701 else if (n==3 && !memcmp (name, "dsa", 3))
703 algoname = "dsa";
704 elems = "pqgy";
706 else if (n==3 && !memcmp (name, "elg", 3))
708 algoname = "elg";
709 elems = "pgy";
711 else
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);
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)
724 rc = gpg_error_from_syserror ();
725 gcry_sexp_release (list);
726 gcry_sexp_release (s_skey);
727 return rc;
730 for (idx=0, s=elems; *s; s++, idx++ )
732 l2 = gcry_sexp_find_token (list, s, 1);
733 if (!l2)
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);
743 array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
744 gcry_sexp_release (l2);
745 if (!array[idx])
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);
756 gcry_sexp_release (list);
757 list = NULL;
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);
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);
771 gcry_sexp_release (s_skey);
772 s_skey = NULL;
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*));
783 format = xtrymalloc (15+7*strlen (elems)+10+15+1+1);
784 if (!format)
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;
795 argidx = 0;
796 p = stpcpy (stpcpy (format, "(public-key("), algoname);
797 for (idx=0, s=elems; *s; s++, idx++ )
799 *p++ = '(';
800 *p++ = *s;
801 p = stpcpy (p, " %m)");
802 assert (argidx < DIM (args));
803 args[argidx++] = &array[idx];
805 *p++ = ')';
806 if (uri)
808 p = stpcpy (p, "(uri %b)");
809 assert (argidx+1 < DIM (args));
810 args[argidx++] = (void *)uri_length;
811 args[argidx++] = (void *)uri;
813 if (comment)
815 p = stpcpy (p, "(comment %b)");
816 assert (argidx+1 < DIM (args));
817 args[argidx++] = (void *)comment_length;
818 args[argidx++] = (void*)comment;
820 *p++ = ')';
821 *p = 0;
822 assert (argidx < DIM (args));
823 args[argidx] = NULL;
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);
833 if (!rc)
834 *result = list;
835 return rc;
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 */
843 agent_key_available (const unsigned char *grip)
845 int i;
846 char *fname;
847 char hexgrip[40+4+1];
849 for (i=0; i < 20; i++)
850 sprintf (hexgrip+2*i, "%02X", grip[i]);
851 strcpy (hexgrip+40, ".key");
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;