Support DSA2.
[gnupg.git] / sm / call-agent.c
1 /* call-agent.c - divert operations to the agent
2  * Copyright (C) 2001, 2002, 2003, 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 <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 #ifdef HAVE_LOCALE_H
30 #include <locale.h>
31 #endif
32
33 #include "gpgsm.h"
34 #include <gcrypt.h>
35 #include <assuan.h>
36 #include "i18n.h"
37 #include "asshelp.h"
38 #include "keydb.h" /* fixme: Move this to import.c */
39 #include "membuf.h"
40
41
42 static assuan_context_t agent_ctx = NULL;
43
44
45 struct cipher_parm_s
46 {
47   assuan_context_t ctx;
48   const unsigned char *ciphertext;
49   size_t ciphertextlen;
50 };
51
52 struct genkey_parm_s
53 {
54   assuan_context_t ctx;
55   const unsigned char *sexp;
56   size_t sexplen;
57 };
58
59 struct learn_parm_s
60 {
61   int error;
62   assuan_context_t ctx;
63   membuf_t *data;
64 };
65
66
67 \f
68 /* Try to connect to the agent via socket or fork it off and work by
69    pipes.  Handle the server's initial greeting */
70 static int
71 start_agent (ctrl_t ctrl)
72 {
73   int rc;
74
75   if (agent_ctx)
76     rc = 0;      /* fixme: We need a context for each thread or
77                     serialize the access to the agent (which is
78                     suitable given that the agent is not MT. */
79   else
80     rc = start_new_gpg_agent (&agent_ctx,
81                               GPG_ERR_SOURCE_DEFAULT,
82                               opt.homedir,
83                               opt.agent_program,
84                               opt.display, opt.ttyname, opt.ttytype,
85                               opt.lc_ctype, opt.lc_messages,
86                               opt.xauthority, opt.pinentry_user_data,
87                               opt.verbose, DBG_ASSUAN,
88                               gpgsm_status2, ctrl);
89   if (!ctrl->agent_seen)
90     {
91       ctrl->agent_seen = 1;
92       audit_log_ok (ctrl->audit, AUDIT_AGENT_READY, rc);
93     }
94
95   return rc;
96 }
97
98
99
100 static int
101 membuf_data_cb (void *opaque, const void *buffer, size_t length)
102 {
103   membuf_t *data = opaque;
104
105   if (buffer)
106     put_membuf (data, buffer, length);
107   return 0;
108 }
109   
110
111
112 \f
113 /* Call the agent to do a sign operation using the key identified by
114    the hex string KEYGRIP. */
115 int
116 gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc,
117                     unsigned char *digest, size_t digestlen, int digestalgo,
118                     unsigned char **r_buf, size_t *r_buflen )
119 {
120   int rc, i;
121   char *p, line[ASSUAN_LINELENGTH];
122   membuf_t data;
123   size_t len;
124
125   *r_buf = NULL;
126   rc = start_agent (ctrl);
127   if (rc)
128     return rc;
129
130   if (digestlen*2 + 50 > DIM(line))
131     return gpg_error (GPG_ERR_GENERAL);
132
133   rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
134   if (rc)
135     return rc;
136
137   snprintf (line, DIM(line)-1, "SIGKEY %s", keygrip);
138   line[DIM(line)-1] = 0;
139   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
140   if (rc)
141     return rc;
142
143   if (desc)
144     {
145       snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
146       line[DIM(line)-1] = 0;
147       rc = assuan_transact (agent_ctx, line,
148                             NULL, NULL, NULL, NULL, NULL, NULL);
149       if (rc)
150         return rc;
151     }
152
153   sprintf (line, "SETHASH %d ", digestalgo);
154   p = line + strlen (line);
155   for (i=0; i < digestlen ; i++, p += 2 )
156     sprintf (p, "%02X", digest[i]);
157   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
158   if (rc)
159     return rc;
160
161   init_membuf (&data, 1024);
162   rc = assuan_transact (agent_ctx, "PKSIGN",
163                         membuf_data_cb, &data, NULL, NULL, NULL, NULL);
164   if (rc)
165     {
166       xfree (get_membuf (&data, &len));
167       return rc;
168     }
169   *r_buf = get_membuf (&data, r_buflen);
170
171   if (!gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL))
172     {
173       xfree (*r_buf); *r_buf = NULL;
174       return gpg_error (GPG_ERR_INV_VALUE);
175     }
176
177   return *r_buf? 0 : out_of_core ();
178 }
179
180
181 /* Call the scdaemon to do a sign operation using the key identified by
182    the hex string KEYID. */
183 int
184 gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
185                   unsigned char *digest, size_t digestlen, int digestalgo,
186                   unsigned char **r_buf, size_t *r_buflen )
187 {
188   int rc, i;
189   char *p, line[ASSUAN_LINELENGTH];
190   membuf_t data;
191   size_t len;
192   const char *hashopt;
193   unsigned char *sigbuf;
194   size_t sigbuflen;
195
196   *r_buf = NULL;
197
198   switch(digestalgo)
199     {
200     case GCRY_MD_SHA1:  hashopt = "--hash=sha1"; break;
201     case GCRY_MD_RMD160:hashopt = "--hash=rmd160"; break;
202     case GCRY_MD_MD5:   hashopt = "--hash=md5"; break;
203     case GCRY_MD_SHA256:hashopt = "--hash=sha256"; break;
204     default: 
205       return gpg_error (GPG_ERR_DIGEST_ALGO);
206     }
207
208   rc = start_agent (ctrl);
209   if (rc)
210     return rc;
211
212   if (digestlen*2 + 50 > DIM(line))
213     return gpg_error (GPG_ERR_GENERAL);
214
215   p = stpcpy (line, "SCD SETDATA " );
216   for (i=0; i < digestlen ; i++, p += 2 )
217     sprintf (p, "%02X", digest[i]);
218   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
219   if (rc)
220     return rc;
221
222   init_membuf (&data, 1024);
223
224   snprintf (line, DIM(line)-1, "SCD PKSIGN %s %s", hashopt, keyid);
225   line[DIM(line)-1] = 0;
226   rc = assuan_transact (agent_ctx, line,
227                         membuf_data_cb, &data, NULL, NULL, NULL, NULL);
228   if (rc)
229     {
230       xfree (get_membuf (&data, &len));
231       return rc;
232     }
233   sigbuf = get_membuf (&data, &sigbuflen);
234
235   /* Create an S-expression from it which is formatted like this:
236      "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" Fixme: If a card ever
237      creates non-RSA keys we need to change things. */
238   *r_buflen = 21 + 11 + sigbuflen + 4;
239   p = xtrymalloc (*r_buflen);
240   *r_buf = (unsigned char*)p;
241   if (!p)
242     {
243       xfree (sigbuf);
244       return 0;
245     }
246   p = stpcpy (p, "(7:sig-val(3:rsa(1:s" );
247   sprintf (p, "%u:", (unsigned int)sigbuflen);
248   p += strlen (p);
249   memcpy (p, sigbuf, sigbuflen);
250   p += sigbuflen;
251   strcpy (p, ")))");
252   xfree (sigbuf);
253
254   assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL));
255   return  0;
256 }
257
258
259
260 \f
261 /* Handle a CIPHERTEXT inquiry.  Note, we only send the data,
262    assuan_transact talkes care of flushing and writing the end */
263 static int
264 inq_ciphertext_cb (void *opaque, const char *keyword)
265 {
266   struct cipher_parm_s *parm = opaque; 
267   int rc;
268
269   assuan_begin_confidential (parm->ctx);
270   rc = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen);
271   assuan_end_confidential (parm->ctx);
272   return rc; 
273 }
274
275
276 /* Call the agent to do a decrypt operation using the key identified by
277    the hex string KEYGRIP. */
278 int
279 gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc,
280                        ksba_const_sexp_t ciphertext, 
281                        char **r_buf, size_t *r_buflen )
282 {
283   int rc;
284   char line[ASSUAN_LINELENGTH];
285   membuf_t data;
286   struct cipher_parm_s cipher_parm;
287   size_t n, len;
288   char *p, *buf, *endp;
289   size_t ciphertextlen;
290   
291   if (!keygrip || strlen(keygrip) != 40 || !ciphertext || !r_buf || !r_buflen)
292     return gpg_error (GPG_ERR_INV_VALUE);
293   *r_buf = NULL;
294
295   ciphertextlen = gcry_sexp_canon_len (ciphertext, 0, NULL, NULL);
296   if (!ciphertextlen)
297     return gpg_error (GPG_ERR_INV_VALUE);
298
299   rc = start_agent (ctrl);
300   if (rc)
301     return rc;
302
303   rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
304   if (rc)
305     return rc;
306
307   assert ( DIM(line) >= 50 );
308   snprintf (line, DIM(line)-1, "SETKEY %s", keygrip);
309   line[DIM(line)-1] = 0;
310   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
311   if (rc)
312     return rc;
313
314   if (desc)
315     {
316       snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
317       line[DIM(line)-1] = 0;
318       rc = assuan_transact (agent_ctx, line,
319                             NULL, NULL, NULL, NULL, NULL, NULL);
320       if (rc)
321         return rc;
322     }
323
324   init_membuf (&data, 1024);
325   cipher_parm.ctx = agent_ctx;
326   cipher_parm.ciphertext = ciphertext;
327   cipher_parm.ciphertextlen = ciphertextlen;
328   rc = assuan_transact (agent_ctx, "PKDECRYPT",
329                         membuf_data_cb, &data,
330                         inq_ciphertext_cb, &cipher_parm, NULL, NULL);
331   if (rc)
332     {
333       xfree (get_membuf (&data, &len));
334       return rc;
335     }
336
337   put_membuf (&data, "", 1); /* Make sure it is 0 terminated. */
338   buf = get_membuf (&data, &len);
339   if (!buf)
340     return gpg_error (GPG_ERR_ENOMEM);
341   assert (len); /* (we forced Nul termination.)  */
342
343   if (*buf == '(')
344     {
345       if (len < 13 || memcmp (buf, "(5:value", 8) ) /* "(5:valueN:D)\0" */
346         return gpg_error (GPG_ERR_INV_SEXP);
347       len -= 11;   /* Count only the data of the second part. */
348       p = buf + 8; /* Skip leading parenthesis and the value tag. */
349     }
350   else
351     {
352       /* For compatibility with older gpg-agents handle the old style
353          incomplete S-exps. */
354       len--;      /* Do not count the Nul. */
355       p = buf;
356     }
357
358   n = strtoul (p, &endp, 10);
359   if (!n || *endp != ':')
360     return gpg_error (GPG_ERR_INV_SEXP);
361   endp++;
362   if (endp-p+n > len)
363     return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */
364   
365   memmove (buf, endp, n);
366
367   *r_buflen = n;
368   *r_buf = buf;
369   return 0;
370 }
371
372
373
374
375 \f
376 /* Handle a KEYPARMS inquiry.  Note, we only send the data,
377    assuan_transact takes care of flushing and writing the end */
378 static int
379 inq_genkey_parms (void *opaque, const char *keyword)
380 {
381   struct genkey_parm_s *parm = opaque; 
382   int rc;
383
384   rc = assuan_send_data (parm->ctx, parm->sexp, parm->sexplen);
385   return rc; 
386 }
387
388
389 \f
390 /* Call the agent to generate a newkey */
391 int
392 gpgsm_agent_genkey (ctrl_t ctrl,
393                     ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey)
394 {
395   int rc;
396   struct genkey_parm_s gk_parm;
397   membuf_t data;
398   size_t len;
399   unsigned char *buf;
400
401   *r_pubkey = NULL;
402   rc = start_agent (ctrl);
403   if (rc)
404     return rc;
405
406   rc = assuan_transact (agent_ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
407   if (rc)
408     return rc;
409
410   init_membuf (&data, 1024);
411   gk_parm.ctx = agent_ctx;
412   gk_parm.sexp = keyparms;
413   gk_parm.sexplen = gcry_sexp_canon_len (keyparms, 0, NULL, NULL);
414   if (!gk_parm.sexplen)
415     return gpg_error (GPG_ERR_INV_VALUE);
416   rc = assuan_transact (agent_ctx, "GENKEY",
417                         membuf_data_cb, &data, 
418                         inq_genkey_parms, &gk_parm, NULL, NULL);
419   if (rc)
420     {
421       xfree (get_membuf (&data, &len));
422       return rc;
423     }
424   buf = get_membuf (&data, &len);
425   if (!buf)
426     return gpg_error (GPG_ERR_ENOMEM);
427   if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
428     {
429       xfree (buf);
430       return gpg_error (GPG_ERR_INV_SEXP);
431     }
432   *r_pubkey = buf;
433   return 0;
434 }
435
436 \f
437 /* Call the agent to read the public key part for a given keygrip.  If
438    FROMCARD is true, the key is directly read from the current
439    smartcard. In this case HEXKEYGRIP should be the keyID
440    (e.g. OPENPGP.3). */
441 int
442 gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip,
443                      ksba_sexp_t *r_pubkey)
444 {
445   int rc;
446   membuf_t data;
447   size_t len;
448   unsigned char *buf;
449   char line[ASSUAN_LINELENGTH];
450
451   *r_pubkey = NULL;
452   rc = start_agent (ctrl);
453   if (rc)
454     return rc;
455
456   rc = assuan_transact (agent_ctx, "RESET",NULL, NULL, NULL, NULL, NULL, NULL);
457   if (rc)
458     return rc;
459
460   snprintf (line, DIM(line)-1, "%sREADKEY %s",
461             fromcard? "SCD ":"", hexkeygrip);
462   line[DIM(line)-1] = 0;
463
464   init_membuf (&data, 1024);
465   rc = assuan_transact (agent_ctx, line,
466                         membuf_data_cb, &data, 
467                         NULL, NULL, NULL, NULL);
468   if (rc)
469     {
470       xfree (get_membuf (&data, &len));
471       return rc;
472     }
473   buf = get_membuf (&data, &len);
474   if (!buf)
475     return gpg_error (GPG_ERR_ENOMEM);
476   if (!gcry_sexp_canon_len (buf, len, NULL, NULL))
477     {
478       xfree (buf);
479       return gpg_error (GPG_ERR_INV_SEXP);
480     }
481   *r_pubkey = buf;
482   return 0;
483 }
484
485 \f
486
487 static int
488 istrusted_status_cb (void *opaque, const char *line)
489 {
490   struct rootca_flags_s *flags = opaque;
491
492   if (!strncmp (line, "TRUSTLISTFLAG", 13) && (line[13]==' ' || !line[13]))
493     {
494       for (line += 13; *line == ' '; line++)
495         ;
496       if (!strncmp (line, "relax", 5) && (line[5] == ' ' || !line[5]))
497         flags->relax = 1;
498       else if (!strncmp (line, "cm", 2) && (line[2] == ' ' || !line[2]))
499         flags->chain_model = 1;
500     }
501   return 0;
502 }
503
504
505
506 /* Ask the agent whether the certificate is in the list of trusted
507    keys.  ROOTCA_FLAGS is guaranteed to be cleared on error. */
508 int
509 gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert,
510                        struct rootca_flags_s *rootca_flags)
511 {
512   int rc;
513   char *fpr;
514   char line[ASSUAN_LINELENGTH];
515
516   memset (rootca_flags, 0, sizeof *rootca_flags);
517
518   rc = start_agent (ctrl);
519   if (rc)
520     return rc;
521
522   fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
523   if (!fpr)
524     {
525       log_error ("error getting the fingerprint\n");
526       return gpg_error (GPG_ERR_GENERAL);
527     }
528
529   snprintf (line, DIM(line)-1, "ISTRUSTED %s", fpr);
530   line[DIM(line)-1] = 0;
531   xfree (fpr);
532
533   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL,
534                         istrusted_status_cb, rootca_flags);
535   if (!rc)
536     rootca_flags->valid = 1;
537   return rc;
538 }
539
540 /* Ask the agent to mark CERT as a trusted Root-CA one */
541 int
542 gpgsm_agent_marktrusted (ctrl_t ctrl, ksba_cert_t cert)
543 {
544   int rc;
545   char *fpr, *dn;
546   char line[ASSUAN_LINELENGTH];
547
548   rc = start_agent (ctrl);
549   if (rc)
550     return rc;
551
552   fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
553   if (!fpr)
554     {
555       log_error ("error getting the fingerprint\n");
556       return gpg_error (GPG_ERR_GENERAL);
557     }
558
559   dn = ksba_cert_get_issuer (cert, 0);
560   if (!dn)
561     {
562       xfree (fpr);
563       return gpg_error (GPG_ERR_GENERAL);
564     }
565   snprintf (line, DIM(line)-1, "MARKTRUSTED %s S %s", fpr, dn);
566   line[DIM(line)-1] = 0;
567   ksba_free (dn);
568   xfree (fpr);
569
570   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
571   return rc;
572 }
573
574
575 \f
576 /* Ask the agent whether the a corresponding secret key is available
577    for the given keygrip */
578 int
579 gpgsm_agent_havekey (ctrl_t ctrl, const char *hexkeygrip)
580 {
581   int rc;
582   char line[ASSUAN_LINELENGTH];
583
584   rc = start_agent (ctrl);
585   if (rc)
586     return rc;
587
588   if (!hexkeygrip || strlen (hexkeygrip) != 40)
589     return gpg_error (GPG_ERR_INV_VALUE);
590
591   snprintf (line, DIM(line)-1, "HAVEKEY %s", hexkeygrip);
592   line[DIM(line)-1] = 0;
593
594   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
595   return rc;
596 }
597
598 \f
599 static int
600 learn_cb (void *opaque, const void *buffer, size_t length)
601 {
602   struct learn_parm_s *parm = opaque;
603   size_t len;
604   char *buf;
605   ksba_cert_t cert;
606   int rc;
607
608   if (parm->error)
609     return 0;
610
611   if (buffer)
612     {
613       put_membuf (parm->data, buffer, length);
614       return 0;
615     }
616   /* END encountered - process what we have */
617   buf = get_membuf (parm->data, &len);
618   if (!buf)
619     {
620       parm->error = gpg_error (GPG_ERR_ENOMEM);
621       return 0;
622     }
623
624
625   /* FIXME: this should go into import.c */
626   rc = ksba_cert_new (&cert);
627   if (rc)
628     {
629       parm->error = rc;
630       return 0;
631     }
632   rc = ksba_cert_init_from_mem (cert, buf, len);
633   if (rc)
634     {
635       log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc));
636       ksba_cert_release (cert);
637       parm->error = rc;
638       return 0;
639     }
640
641   rc = gpgsm_basic_cert_check (cert);
642   if (gpg_err_code (rc) == GPG_ERR_MISSING_CERT)
643     { /* For later use we store it in the ephemeral database. */
644       log_info ("issuer certificate missing - storing as ephemeral\n");
645       keydb_store_cert (cert, 1, NULL);
646     }
647   else if (rc)
648     log_error ("invalid certificate: %s\n", gpg_strerror (rc));
649   else
650     {
651       int existed;
652
653       if (!keydb_store_cert (cert, 0, &existed))
654         {
655           if (opt.verbose > 1 && existed)
656             log_info ("certificate already in DB\n");
657           else if (opt.verbose && !existed)
658             log_info ("certificate imported\n");
659         }
660     }
661
662   ksba_cert_release (cert);
663   init_membuf (parm->data, 4096);
664   return 0;
665 }
666   
667 /* Call the agent to learn about a smartcard */
668 int
669 gpgsm_agent_learn (ctrl_t ctrl)
670 {
671   int rc;
672   struct learn_parm_s learn_parm;
673   membuf_t data;
674   size_t len;
675
676   rc = start_agent (ctrl);
677   if (rc)
678     return rc;
679
680   init_membuf (&data, 4096);
681   learn_parm.error = 0;
682   learn_parm.ctx = agent_ctx;
683   learn_parm.data = &data;
684   rc = assuan_transact (agent_ctx, "LEARN --send",
685                         learn_cb, &learn_parm, 
686                         NULL, NULL, NULL, NULL);
687   xfree (get_membuf (&data, &len));
688   if (rc)
689     return rc;
690   return learn_parm.error;
691 }
692
693 \f
694 /* Ask the agent to change the passphrase of the key identified by
695    HEXKEYGRIP. If DESC is not NULL, display instead of the default
696    description message. */
697 int
698 gpgsm_agent_passwd (ctrl_t ctrl, const char *hexkeygrip, const char *desc)
699 {
700   int rc;
701   char line[ASSUAN_LINELENGTH];
702
703   rc = start_agent (ctrl);
704   if (rc)
705     return rc;
706
707   if (!hexkeygrip || strlen (hexkeygrip) != 40)
708     return gpg_error (GPG_ERR_INV_VALUE);
709
710   if (desc)
711     {
712       snprintf (line, DIM(line)-1, "SETKEYDESC %s", desc);
713       line[DIM(line)-1] = 0;
714       rc = assuan_transact (agent_ctx, line,
715                             NULL, NULL, NULL, NULL, NULL, NULL);
716       if (rc)
717         return rc;
718     }
719
720   snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip);
721   line[DIM(line)-1] = 0;
722
723   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
724   return rc;
725 }
726
727
728 \f
729 /* Ask the agent to pop up a confirmation dialog with the text DESC
730    and an okay and cancel button.  */
731 gpg_error_t
732 gpgsm_agent_get_confirmation (ctrl_t ctrl, const char *desc)
733 {
734   int rc;
735   char line[ASSUAN_LINELENGTH];
736
737   rc = start_agent (ctrl);
738   if (rc)
739     return rc;
740
741   snprintf (line, DIM(line)-1, "GET_CONFIRMATION %s", desc);
742   line[DIM(line)-1] = 0;
743
744   rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
745   return rc;
746 }