* call-dirmngr.c (start_dirmngr): Use PATHSEP_C instead of ':'.
[gnupg.git] / sm / call-dirmngr.c
1 /* call-dirmngr.c - communication with the dromngr 
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 <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h> 
27 #include <time.h>
28 #include <assert.h>
29 #include <ctype.h>
30
31 #include "gpgsm.h"
32 #include <gcrypt.h>
33 #include <assuan.h>
34
35 #include "i18n.h"
36 #include "keydb.h"
37
38 /* The name of the socket for a system daemon.  */
39 #define DEFAULT_SOCKET_NAME "/var/run/dirmngr/socket"
40
41 struct membuf {
42   size_t len;
43   size_t size;
44   char *buf;
45   int out_of_core;
46 };
47
48
49
50 static ASSUAN_CONTEXT dirmngr_ctx = NULL;
51 static int force_pipe_server = 0;
52
53 struct inq_certificate_parm_s {
54   ASSUAN_CONTEXT ctx;
55   ksba_cert_t cert;
56   ksba_cert_t issuer_cert;
57 };
58
59 struct isvalid_status_parm_s {
60   CTRL ctrl;
61   int seen;
62   unsigned char fpr[20];
63 };
64
65
66 struct lookup_parm_s {
67   CTRL ctrl;
68   ASSUAN_CONTEXT ctx;
69   void (*cb)(void *, ksba_cert_t);
70   void *cb_value;
71   struct membuf data;
72   int error;
73 };
74
75 struct run_command_parm_s {
76   ASSUAN_CONTEXT ctx;
77 };
78
79
80 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
81    create a buffer, put_membuf to append bytes and get_membuf to
82    release and return the buffer.  Allocation errors are detected but
83    only returned at the final get_membuf(), this helps not to clutter
84    the code with out of core checks.  */
85
86 static void
87 init_membuf (struct membuf *mb, int initiallen)
88 {
89   mb->len = 0;
90   mb->size = initiallen;
91   mb->out_of_core = 0;
92   mb->buf = xtrymalloc (initiallen);
93   if (!mb->buf)
94       mb->out_of_core = 1;
95 }
96
97 static void
98 put_membuf (struct membuf *mb, const void *buf, size_t len)
99 {
100   if (mb->out_of_core)
101     return;
102
103   if (mb->len + len >= mb->size)
104     {
105       char *p;
106       
107       mb->size += len + 1024;
108       p = xtryrealloc (mb->buf, mb->size);
109       if (!p)
110         {
111           mb->out_of_core = 1;
112           return;
113         }
114       mb->buf = p;
115     }
116   memcpy (mb->buf + mb->len, buf, len);
117   mb->len += len;
118 }
119
120 static void *
121 get_membuf (struct membuf *mb, size_t *len)
122 {
123   char *p;
124
125   if (mb->out_of_core)
126     {
127       xfree (mb->buf);
128       mb->buf = NULL;
129       return NULL;
130     }
131
132   p = mb->buf;
133   *len = mb->len;
134   mb->buf = NULL;
135   mb->out_of_core = 1; /* don't allow a reuse */
136   return p;
137 }
138
139
140
141
142 \f
143 /* Try to connect to the agent via socket or fork it off and work by
144    pipes.  Handle the server's initial greeting */
145 static int
146 start_dirmngr (void)
147 {
148   int rc;
149   char *infostr, *p;
150   ASSUAN_CONTEXT ctx;
151   int try_default = 0;
152
153   if (dirmngr_ctx)
154     return 0; /* fixme: We need a context for each thread or serialize
155                  the access to the dirmngr */
156   /* Note: if you change this to multiple connections, you also need
157      to take care of the implicit option sending caching. */
158
159   infostr = force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
160   if (opt.prefer_system_dirmngr && !force_pipe_server
161       &&(!infostr || !*infostr))
162     {
163       infostr = DEFAULT_SOCKET_NAME;
164       try_default = 1;
165     }
166   if (!infostr || !*infostr)
167     {
168       const char *pgmname;
169       const char *argv[3];
170       int no_close_list[3];
171       int i;
172
173       if (opt.verbose)
174         log_info (_("no running dirmngr - starting one\n"));
175       
176       if (fflush (NULL))
177         {
178           gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
179           log_error ("error flushing pending output: %s\n", strerror (errno));
180           return tmperr;
181         }
182
183       if (!opt.dirmngr_program || !*opt.dirmngr_program)
184         opt.dirmngr_program = GNUPG_DEFAULT_DIRMNGR;
185       if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
186         pgmname = opt.dirmngr_program;
187       else
188         pgmname++;
189
190       argv[0] = pgmname;
191       argv[1] = "--server";
192       argv[2] = NULL;
193
194       i=0;
195       if (log_get_fd () != -1)
196         no_close_list[i++] = log_get_fd ();
197       no_close_list[i++] = fileno (stderr);
198       no_close_list[i] = -1;
199
200       /* connect to the agent and perform initial handshaking */
201       rc = assuan_pipe_connect (&ctx, opt.dirmngr_program, (char**)argv,
202                                 no_close_list);
203     }
204   else
205     {
206       int prot;
207       int pid;
208
209       infostr = xstrdup (infostr);
210       if (!try_default && *infostr)
211         {
212           if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
213             {
214               log_error (_("malformed DIRMNGR_INFO environment variable\n"));
215               xfree (infostr);
216               force_pipe_server = 1;
217               return start_dirmngr ();
218             }
219           *p++ = 0;
220           pid = atoi (p);
221           while (*p && *p != PATHSEP_C)
222             p++;
223           prot = *p? atoi (p+1) : 0;
224           if (prot != 1)
225             {
226               log_error (_("dirmngr protocol version %d is not supported\n"),
227                          prot);
228               xfree (infostr);
229               force_pipe_server = 1;
230               return start_dirmngr ();
231             }
232         }
233       else
234         pid = -1;
235
236       rc = assuan_socket_connect (&ctx, infostr, pid);
237       xfree (infostr);
238       if (rc == ASSUAN_Connect_Failed)
239         {
240           log_error (_("can't connect to the dirmngr - trying fall back\n"));
241           force_pipe_server = 1;
242           return start_dirmngr ();
243         }
244     }
245
246   if (rc)
247     {
248       log_error ("can't connect to the dirmngr: %s\n", assuan_strerror (rc));
249       return gpg_error (GPG_ERR_NO_DIRMNGR);
250     }
251   dirmngr_ctx = ctx;
252
253   if (DBG_ASSUAN)
254     log_debug ("connection to dirmngr established\n");
255   return 0;
256 }
257
258
259 \f
260 /* Handle a SENDCERT inquiry. */
261 static AssuanError
262 inq_certificate (void *opaque, const char *line)
263 {
264   struct inq_certificate_parm_s *parm = opaque;
265   AssuanError rc;
266   const unsigned char *der;
267   size_t derlen;
268   int issuer_mode = 0;
269
270   if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]))
271     {
272       line += 8;
273     }
274   else if (!strncmp (line, "SENDISSUERCERT", 14)
275            && (line[14] == ' ' || !line[14]))
276     {
277       line += 14;
278       issuer_mode = 1;
279     }
280   else
281     {
282       log_error ("unsupported inquiry `%s'\n", line);
283       return ASSUAN_Inquire_Unknown;
284     }
285
286   if (!*line)
287     { /* Send the current certificate. */
288       der = ksba_cert_get_image (issuer_mode? parm->issuer_cert : parm->cert,
289                                  &derlen);
290       if (!der)
291         rc = ASSUAN_Inquire_Error;
292       else
293         rc = assuan_send_data (parm->ctx, der, derlen);
294     }
295   else if (issuer_mode)
296     {
297       log_error ("sending specific issuer certificate back "
298                  "is not yet implemented\n");
299       rc = ASSUAN_Inquire_Error;
300     }
301   else 
302     { /* Send the given certificate. */
303       int err;
304       ksba_cert_t cert;
305
306
307       err = gpgsm_find_cert (line, &cert);
308       if (err)
309         {
310           log_error ("certificate not found: %s\n", gpg_strerror (err));
311           rc = ASSUAN_Inquire_Error;
312         }
313       else
314         {
315           der = ksba_cert_get_image (cert, &derlen);
316           if (!der)
317             rc = ASSUAN_Inquire_Error;
318           else
319             rc = assuan_send_data (parm->ctx, der, derlen);
320           ksba_cert_release (cert);
321         }
322     }
323
324   return rc; 
325 }
326
327
328 /* Take a 20 byte hexencoded string and put it into the the provided
329    20 byte buffer FPR in binary format. */
330 static int
331 unhexify_fpr (const char *hexstr, unsigned char *fpr)
332 {
333   const char *s;
334   int n;
335
336   for (s=hexstr, n=0; hexdigitp (s); s++, n++)
337     ;
338   if (*s || (n != 40))
339     return 0; /* no fingerprint (invalid or wrong length). */
340   n /= 2;
341   for (s=hexstr, n=0; *s; s += 2, n++)
342     fpr[n] = xtoi_2 (s);
343   return 1; /* okay */
344 }
345
346
347 static assuan_error_t
348 isvalid_status_cb (void *opaque, const char *line)
349 {
350   struct isvalid_status_parm_s *parm = opaque;
351
352   if (!strncmp (line, "PROGRESS", 8) && (line[8]==' ' || !line[8]))
353     {
354       if (parm->ctrl)
355         {
356           for (line += 8; *line == ' '; line++)
357             ;
358           if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line))
359             return ASSUAN_Canceled;
360         }
361     }
362   else if (!strncmp (line, "ONLY_VALID_IF_CERT_VALID", 24)
363       && (line[24]==' ' || !line[24]))
364     {
365       parm->seen++;
366       if (!line[24] || !unhexify_fpr (line+25, parm->fpr))
367         parm->seen++; /* Bumb it to indicate an error. */
368     }
369   return 0;
370 }
371
372
373
374 \f
375 /* Call the directory manager to check whether the certificate is valid
376    Returns 0 for valid or usually one of the errors:
377
378   GPG_ERR_CERTIFICATE_REVOKED
379   GPG_ERR_NO_CRL_KNOWN
380   GPG_ERR_CRL_TOO_OLD
381
382   With USE_OCSP set to true, the dirmngr is asked to do an OCSP
383   request first.
384  */
385 int
386 gpgsm_dirmngr_isvalid (ctrl_t ctrl,
387                        ksba_cert_t cert, ksba_cert_t issuer_cert, int use_ocsp)
388 {
389   static int did_options;
390   int rc;
391   char *certid;
392   char line[ASSUAN_LINELENGTH];
393   struct inq_certificate_parm_s parm;
394   struct isvalid_status_parm_s stparm;
395
396
397   rc = start_dirmngr ();
398   if (rc)
399     return rc;
400
401   if (use_ocsp)
402     {
403       certid = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
404     }
405   else
406     {
407       certid = gpgsm_get_certid (cert);
408       if (!certid)
409         {
410           log_error ("error getting the certificate ID\n");
411           return gpg_error (GPG_ERR_GENERAL);
412         }
413     }
414
415   if (opt.verbose > 1)
416     {
417       char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
418       log_info ("asking dirmngr about %s%s\n", fpr,
419                 use_ocsp? " (using OCSP)":"");
420       xfree (fpr);
421     }
422
423   parm.ctx = dirmngr_ctx;
424   parm.cert = cert;
425   parm.issuer_cert = issuer_cert;
426
427   stparm.ctrl = ctrl;
428   stparm.seen = 0;
429   memset (stparm.fpr, 0, 20);
430
431   /* FIXME: If --disable-crl-checks has been set, we should pass an
432      option to dirmngr, so that no fallback CRL check is done after an
433      ocsp check. */
434
435   /* It is sufficient to send the options only once because we have
436      one connection per process only. */
437   if (!did_options)
438     {
439       if (opt.force_crl_refresh)
440         assuan_transact (dirmngr_ctx, "OPTION force-crl-refresh=1",
441                          NULL, NULL, NULL, NULL, NULL, NULL);
442       did_options = 1;
443     }
444   snprintf (line, DIM(line)-1, "ISVALID %s", certid);
445   line[DIM(line)-1] = 0;
446   xfree (certid);
447
448   rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
449                         inq_certificate, &parm,
450                         isvalid_status_cb, &stparm);
451   if (opt.verbose > 1)
452     log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
453   rc = map_assuan_err (rc);
454
455   if (!rc && stparm.seen)
456     {
457       /* Need to also check the certificate validity. */
458       if (stparm.seen != 1)
459         {
460           log_error ("communication problem with dirmngr detected\n");
461           rc = gpg_error (GPG_ERR_INV_CRL);
462         }
463       else
464         {
465           KEYDB_HANDLE kh;
466           ksba_cert_t rspcert = NULL;
467
468           /* Fixme: First try to get the certificate from the
469              dirmngr's cache - it should be there. */
470           kh = keydb_new (0);
471           if (!kh)
472             rc = gpg_error (GPG_ERR_ENOMEM);
473           if (!rc)
474             rc = keydb_search_fpr (kh, stparm.fpr);
475           if (!rc)
476             rc = keydb_get_cert (kh, &rspcert);
477           if (rc)
478             {
479               log_error ("unable to find the certificate used "
480                          "by the dirmngr: %s\n", gpg_strerror (rc));
481               rc = gpg_error (GPG_ERR_INV_CRL);
482             }
483           keydb_release (kh);
484
485           if (!rc)
486             {
487               rc = gpgsm_cert_use_ocsp_p (rspcert);
488               if (rc)
489                 rc = gpg_error (GPG_ERR_INV_CRL);
490               else
491                 {
492                   /* Note, the flag = 1: This avoids checking this
493                      certificate over and over again. */
494                   rc = gpgsm_validate_chain (ctrl, rspcert, NULL, 0, NULL, 1);
495                   if (rc)
496                     {
497                       log_error ("invalid certificate used for CRL/OCSP: %s\n",
498                                  gpg_strerror (rc));
499                       rc = gpg_error (GPG_ERR_INV_CRL);
500                     }
501                 }
502             }
503           ksba_cert_release (rspcert);
504         }
505     }
506   return rc;
507 }
508
509
510 \f
511 /* Lookup helpers*/
512 static AssuanError
513 lookup_cb (void *opaque, const void *buffer, size_t length)
514 {
515   struct lookup_parm_s *parm = opaque;
516   size_t len;
517   char *buf;
518   ksba_cert_t cert;
519   int rc;
520
521   if (parm->error)
522     return 0;
523
524   if (buffer)
525     {
526       put_membuf (&parm->data, buffer, length);
527       return 0;
528     }
529   /* END encountered - process what we have */
530   buf = get_membuf (&parm->data, &len);
531   if (!buf)
532     {
533       parm->error = gpg_error (GPG_ERR_ENOMEM);
534       return 0;
535     }
536
537   rc = ksba_cert_new (&cert);
538   if (rc)
539     {
540       parm->error = rc;
541       return 0;
542     }
543   rc = ksba_cert_init_from_mem (cert, buf, len);
544   if (rc)
545     {
546       log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc));
547     }
548   else
549     {
550       parm->cb (parm->cb_value, cert);
551     }
552
553   ksba_cert_release (cert);
554   init_membuf (&parm->data, 4096);
555   return 0;
556 }
557
558 /* Return a properly escaped pattern from NAMES.  The only error
559    return is NULL to indicate a malloc failure. */
560 static char *
561 pattern_from_strlist (STRLIST names)
562 {
563   STRLIST sl;
564   int n;
565   const char *s;
566   char *pattern, *p;
567
568   for (n=0, sl=names; sl; sl = sl->next)
569     {
570       for (s=sl->d; *s; s++, n++)
571         {
572           if (*s == '%' || *s == ' ' || *s == '+')
573             n += 2;
574         }
575       n++;
576     }
577
578   p = pattern = xtrymalloc (n+1);
579   if (!pattern)
580     return NULL;
581
582   for (n=0, sl=names; sl; sl = sl->next)
583     {
584       for (s=sl->d; *s; s++)
585         {
586           switch (*s)
587             {
588             case '%':
589               *p++ = '%';
590               *p++ = '2';
591               *p++ = '5';
592               break;
593             case ' ':
594               *p++ = '%';
595               *p++ = '2';
596               *p++ = '0';
597               break;
598             case '+':
599               *p++ = '%';
600               *p++ = '2';
601               *p++ = 'B';
602               break;
603             default:
604               *p++ = *s;
605               break;
606             }
607         }
608       *p++ = ' ';
609     }
610   if (p == pattern)
611     *pattern = 0; /* is empty */
612   else
613     p[-1] = '\0'; /* remove trailing blank */
614   
615   return pattern;
616 }
617
618 static AssuanError
619 lookup_status_cb (void *opaque, const char *line)
620 {
621   struct lookup_parm_s *parm = opaque;
622
623   if (!strncmp (line, "PROGRESS", 8) && (line[8]==' ' || !line[8]))
624     {
625       if (parm->ctrl)
626         {
627           for (line += 8; *line == ' '; line++)
628             ;
629           if (gpgsm_status (parm->ctrl, STATUS_PROGRESS, line))
630             return ASSUAN_Canceled;
631         }
632     }
633   else if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9]))
634     {
635       if (parm->ctrl)
636         {
637           for (line +=9; *line == ' '; line++)
638             ;
639           gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line);
640         }
641     }
642   return 0;
643 }
644
645
646 /* Run the Directroy Managers lookup command using the pattern
647    compiled from the strings given in NAMES.  The caller must provide
648    the callback CB which will be passed cert by cert.  Note that CTRL
649    is optional. */
650 int 
651 gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names,
652                       void (*cb)(void*, ksba_cert_t), void *cb_value)
653
654   int rc;
655   char *pattern;
656   char line[ASSUAN_LINELENGTH];
657   struct lookup_parm_s parm;
658   size_t len;
659
660   rc = start_dirmngr ();
661   if (rc)
662     return rc;
663
664   pattern = pattern_from_strlist (names);
665   if (!pattern)
666     return OUT_OF_CORE (errno);
667   snprintf (line, DIM(line)-1, "LOOKUP %s", pattern);
668   line[DIM(line)-1] = 0;
669   xfree (pattern);
670
671   parm.ctrl = ctrl;
672   parm.ctx = dirmngr_ctx;
673   parm.cb = cb;
674   parm.cb_value = cb_value;
675   parm.error = 0;
676   init_membuf (&parm.data, 4096);
677
678   rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm,
679                         NULL, NULL, lookup_status_cb, &parm);
680   xfree (get_membuf (&parm.data, &len));
681   if (rc)
682     return map_assuan_err (rc);
683   return parm.error;
684 }
685
686
687 \f
688 /* Run Command helpers*/
689
690 /* Fairly simple callback to write all output of dirmngr to stdout. */
691 static AssuanError
692 run_command_cb (void *opaque, const void *buffer, size_t length)
693 {
694   if (buffer)
695     {
696       if ( fwrite (buffer, length, 1, stdout) != 1 )
697         log_error ("error writing to stdout: %s\n", strerror (errno));
698     }
699   return 0;
700 }
701
702 /* Handle inquiries from the dirmngr COMMAND. */
703 static AssuanError
704 run_command_inq_cb (void *opaque, const char *line)
705 {
706   struct run_command_parm_s *parm = opaque;
707   AssuanError rc = 0;
708
709   if ( !strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]) )
710     { /* send the given certificate */
711       int err;
712       ksba_cert_t cert;
713       const unsigned char *der;
714       size_t derlen;
715
716       line += 8;
717       if (!*line)
718         return ASSUAN_Inquire_Error;
719
720       err = gpgsm_find_cert (line, &cert);
721       if (err)
722         {
723           log_error ("certificate not found: %s\n", gpg_strerror (err));
724           rc = ASSUAN_Inquire_Error;
725         }
726       else
727         {
728           der = ksba_cert_get_image (cert, &derlen);
729           if (!der)
730             rc = ASSUAN_Inquire_Error;
731           else
732             rc = assuan_send_data (parm->ctx, der, derlen);
733           ksba_cert_release (cert);
734         }
735     }
736   else if ( !strncmp (line, "PRINTINFO", 9) && (line[9] == ' ' || !line[9]) )
737     { /* Simply show the message given in the argument. */
738       line += 9;
739       log_info ("dirmngr: %s\n", line);
740     }
741   else
742     {
743       log_error ("unsupported inquiry `%s'\n", line);
744       rc = ASSUAN_Inquire_Unknown;
745     }
746
747   return rc; 
748 }
749
750 static AssuanError
751 run_command_status_cb (void *opaque, const char *line)
752 {
753   ctrl_t ctrl = opaque;
754
755   if (opt.verbose)
756     {
757       log_info ("dirmngr status: %s\n", line);
758     }
759   if (!strncmp (line, "PROGRESS", 8) && (line[8]==' ' || !line[8]))
760     {
761       if (ctrl)
762         {
763           for (line += 8; *line == ' '; line++)
764             ;
765           if (gpgsm_status (ctrl, STATUS_PROGRESS, line))
766             return ASSUAN_Canceled;
767         }
768     }
769   return 0;
770 }
771
772
773
774 /* Pass COMMAND to dirmngr and print all output generated by Dirmngr
775    to stdout.  A couple of inquiries are defined (see above).  ARGC
776    arguments in ARGV are given to the Dirmngr.  Spaces, plus and
777    percent characters within the argument strings are percent escaped
778    so that blanks can act as delimiters. */
779 int
780 gpgsm_dirmngr_run_command (CTRL ctrl, const char *command,
781                            int argc, char **argv)
782
783   int rc;
784   int i;
785   const char *s;
786   char *line, *p;
787   size_t len;
788   struct run_command_parm_s parm;
789
790   rc = start_dirmngr ();
791   if (rc)
792     return rc;
793
794   parm.ctx = dirmngr_ctx;
795
796   len = strlen (command) + 1;
797   for (i=0; i < argc; i++)
798     len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */
799   line = xtrymalloc (len);
800   if (!line)
801     return OUT_OF_CORE (errno);
802
803   p = stpcpy (line, command);
804   for (i=0; i < argc; i++)
805     {
806       *p++ = ' ';
807       for (s=argv[i]; *s; s++)
808         {
809           if (!isascii (*s))
810             *p++ = *s;
811           else if (*s == ' ')
812             *p++ = '+';
813           else if (!isprint (*s) || *s == '+')
814             {
815               sprintf (p, "%%%02X", *s);
816               p += 3;
817             }
818           else
819             *p++ = *s;
820         }
821     }
822   *p = 0;
823
824   rc = assuan_transact (dirmngr_ctx, line,
825                         run_command_cb, NULL,
826                         run_command_inq_cb, &parm,
827                         run_command_status_cb, ctrl);
828   xfree (line);
829   log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
830   return map_assuan_err (rc);
831 }