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