gpg: No need to create a trustdb when encrypting with --always-trust.
[gnupg.git] / sm / server.c
1 /* server.c - Server mode and main entry point 
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 
3  *               2009, 2010 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 <stdarg.h>
27 #include <ctype.h>
28 #include <unistd.h>
29
30 #include "gpgsm.h"
31 #include <assuan.h>
32 #include "sysutils.h"
33
34 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
35
36
37 /* The filepointer for status message used in non-server mode */
38 static FILE *statusfp;
39
40 /* Data used to assuciate an Assuan context with local server data */
41 struct server_local_s {
42   assuan_context_t assuan_ctx;
43   int message_fd;
44   int list_internal;
45   int list_external;
46   int list_to_output;           /* Write keylistings to the output fd. */
47   int enable_audit_log;         /* Use an audit log.  */
48   certlist_t recplist;
49   certlist_t signerlist;
50   certlist_t default_recplist; /* As set by main() - don't release. */
51   int allow_pinentry_notify;   /* Set if pinentry notifications should
52                                   be passed back to the client. */
53   int no_encrypt_to;           /* Local version of option.  */
54 };
55
56
57 /* Cookie definition for assuan data line output.  */
58 static ssize_t data_line_cookie_write (void *cookie,
59                                        const void *buffer, size_t size);
60 static int data_line_cookie_close (void *cookie);
61 static es_cookie_io_functions_t data_line_cookie_functions =
62   {
63     NULL,
64     data_line_cookie_write,
65     NULL,
66     data_line_cookie_close
67   };
68
69
70 \f
71 static int command_has_option (const char *cmd, const char *cmdopt);
72
73
74
75 \f
76 /* Note that it is sufficient to allocate the target string D as
77    long as the source string S, i.e.: strlen(s)+1; */
78 static void
79 strcpy_escaped_plus (char *d, const char *s)
80 {
81   while (*s)
82     {
83       if (*s == '%' && s[1] && s[2])
84         { 
85           s++;
86           *d++ = xtoi_2 (s);
87           s += 2;
88         }
89       else if (*s == '+')
90         *d++ = ' ', s++;
91       else
92         *d++ = *s++;
93     }
94   *d = 0; 
95 }
96
97
98 /* Skip over options.  
99    Blanks after the options are also removed. */
100 static char *
101 skip_options (const char *line)
102 {
103   while (spacep (line))
104     line++;
105   while ( *line == '-' && line[1] == '-' )
106     {
107       while (*line && !spacep (line))
108         line++;
109       while (spacep (line))
110         line++;
111     }
112   return (char*)line;
113 }
114
115
116 /* Check whether the option NAME appears in LINE */
117 static int
118 has_option (const char *line, const char *name)
119 {
120   const char *s;
121   int n = strlen (name);
122
123   s = strstr (line, name);
124   if (s && s >= skip_options (line))
125     return 0;
126   return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n)));
127 }
128
129
130 /* A write handler used by es_fopencookie to write assuan data
131    lines.  */
132 static ssize_t
133 data_line_cookie_write (void *cookie, const void *buffer, size_t size)
134 {
135   assuan_context_t ctx = cookie;
136
137   if (assuan_send_data (ctx, buffer, size))
138     {
139       errno = EIO;
140       return -1;
141     }
142
143   return size;
144 }
145
146 static int
147 data_line_cookie_close (void *cookie)
148 {
149   assuan_context_t ctx = cookie;
150
151   if (assuan_send_data (ctx, NULL, 0))
152     {
153       errno = EIO;
154       return -1;
155     }
156
157   return 0;
158 }
159
160
161 static void 
162 close_message_fd (ctrl_t ctrl)
163 {
164   if (ctrl->server_local->message_fd != -1)
165     {
166       close (ctrl->server_local->message_fd);
167       ctrl->server_local->message_fd = -1;
168     }
169 }
170
171
172 /* Start a new audit session if this has been enabled.  */
173 static gpg_error_t
174 start_audit_session (ctrl_t ctrl)
175 {
176   audit_release (ctrl->audit);
177   ctrl->audit = NULL;
178   if (ctrl->server_local->enable_audit_log && !(ctrl->audit = audit_new ()) )
179     return gpg_error_from_syserror ();
180   
181   return 0;
182 }
183
184
185 static gpg_error_t
186 option_handler (assuan_context_t ctx, const char *key, const char *value)
187 {
188   ctrl_t ctrl = assuan_get_pointer (ctx);
189   gpg_error_t err = 0;
190
191   if (!strcmp (key, "putenv"))
192     {
193       /* Change the session's environment to be used for the
194          Pinentry.  Valid values are:
195           <NAME>            Delete envvar NAME
196           <KEY>=            Set envvar NAME to the empty string
197           <KEY>=<VALUE>     Set envvar NAME to VALUE
198       */
199       err = session_env_putenv (opt.session_env, value);
200     }
201   else if (!strcmp (key, "display"))
202     {
203       err = session_env_setenv (opt.session_env, "DISPLAY", value);
204     }
205   else if (!strcmp (key, "ttyname"))
206     {
207       err = session_env_setenv (opt.session_env, "GPG_TTY", value);
208     }
209   else if (!strcmp (key, "ttytype"))
210     {
211       err = session_env_setenv (opt.session_env, "TERM", value);
212     }
213   else if (!strcmp (key, "lc-ctype"))
214     {
215       xfree (opt.lc_ctype);
216       opt.lc_ctype = xtrystrdup (value);
217       if (!opt.lc_ctype)
218         err = gpg_error_from_syserror ();
219     }
220   else if (!strcmp (key, "lc-messages"))
221     {
222       xfree (opt.lc_messages);
223       opt.lc_messages = xtrystrdup (value);
224       if (!opt.lc_messages)
225         err = gpg_error_from_syserror ();
226     }
227   else if (!strcmp (key, "xauthority"))
228     {
229       err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
230     }
231   else if (!strcmp (key, "pinentry-user-data"))
232     {
233       err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
234     }
235   else if (!strcmp (key, "include-certs"))
236     {
237       int i = *value? atoi (value) : -1;
238       if (ctrl->include_certs < -2)
239         err = gpg_error (GPG_ERR_ASS_PARAMETER);
240       else
241         ctrl->include_certs = i;
242     }
243   else if (!strcmp (key, "list-mode"))
244     {
245       int i = *value? atoi (value) : 0;
246       if (!i || i == 1) /* default and mode 1 */
247         {
248           ctrl->server_local->list_internal = 1;
249           ctrl->server_local->list_external = 0;
250         }
251       else if (i == 2)
252         {
253           ctrl->server_local->list_internal = 0;
254           ctrl->server_local->list_external = 1;
255         }
256       else if (i == 3)
257         {
258           ctrl->server_local->list_internal = 1;
259           ctrl->server_local->list_external = 1;
260         }
261       else
262         err = gpg_error (GPG_ERR_ASS_PARAMETER);
263     }
264   else if (!strcmp (key, "list-to-output"))
265     {
266       int i = *value? atoi (value) : 0;
267       ctrl->server_local->list_to_output = i;
268     }
269   else if (!strcmp (key, "with-validation"))
270     {
271       int i = *value? atoi (value) : 0;
272       ctrl->with_validation = i;
273     }
274   else if (!strcmp (key, "validation-model"))
275     {
276       int i = gpgsm_parse_validation_model (value);
277       if ( i >= 0 && i <= 1 )
278         ctrl->validation_model = i;
279       else
280         err = gpg_error (GPG_ERR_ASS_PARAMETER);
281     }
282   else if (!strcmp (key, "with-key-data"))
283     {
284       opt.with_key_data = 1;
285     }
286   else if (!strcmp (key, "enable-audit-log"))
287     {
288       int i = *value? atoi (value) : 0;
289       ctrl->server_local->enable_audit_log = i;
290     }
291   else if (!strcmp (key, "allow-pinentry-notify"))
292     {
293       ctrl->server_local->allow_pinentry_notify = 1;
294     }
295   else if (!strcmp (key, "with-ephemeral-keys"))
296     {
297       int i = *value? atoi (value) : 0;
298       ctrl->with_ephemeral_keys = i;
299     }
300   else if (!strcmp (key, "no-encrypt-to"))
301     {
302       ctrl->server_local->no_encrypt_to = 1;
303     }
304   else
305     err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
306
307   return err;
308 }
309
310
311 static gpg_error_t
312 reset_notify (assuan_context_t ctx, char *line)
313 {
314   ctrl_t ctrl = assuan_get_pointer (ctx);
315
316   (void) line;
317
318   gpgsm_release_certlist (ctrl->server_local->recplist);
319   gpgsm_release_certlist (ctrl->server_local->signerlist);
320   ctrl->server_local->recplist = NULL;
321   ctrl->server_local->signerlist = NULL;
322   close_message_fd (ctrl);
323   assuan_close_input_fd (ctx);
324   assuan_close_output_fd (ctx);
325   return 0;
326 }
327
328
329 static gpg_error_t
330 input_notify (assuan_context_t ctx, char *line)
331 {
332   ctrl_t ctrl = assuan_get_pointer (ctx);
333
334   ctrl->autodetect_encoding = 0;
335   ctrl->is_pem = 0;
336   ctrl->is_base64 = 0;
337   if (strstr (line, "--armor"))
338     ctrl->is_pem = 1;  
339   else if (strstr (line, "--base64"))
340     ctrl->is_base64 = 1; 
341   else if (strstr (line, "--binary"))
342     ;
343   else
344     ctrl->autodetect_encoding = 1;
345   return 0;
346 }
347
348 static gpg_error_t
349 output_notify (assuan_context_t ctx, char *line)
350 {
351   ctrl_t ctrl = assuan_get_pointer (ctx);
352
353   ctrl->create_pem = 0;
354   ctrl->create_base64 = 0;
355   if (strstr (line, "--armor"))
356     ctrl->create_pem = 1;  
357   else if (strstr (line, "--base64"))
358     ctrl->create_base64 = 1; /* just the raw output */
359   return 0;
360 }
361
362
363 static const char hlp_recipient[] = 
364   "RECIPIENT <userID>\n"
365   "\n"
366   "Set the recipient for the encryption.  USERID shall be the\n"
367   "internal representation of the key; the server may accept any other\n"
368   "way of specification [we will support this].  If this is a valid and\n"
369   "trusted recipient the server does respond with OK, otherwise the\n"
370   "return is an ERR with the reason why the recipient can't be used,\n"
371   "the encryption will then not be done for this recipient.  If the\n"
372   "policy is not to encrypt at all if not all recipients are valid, the\n"
373   "client has to take care of this.  All RECIPIENT commands are\n"
374   "cumulative until a RESET or an successful ENCRYPT command.";
375 static gpg_error_t
376 cmd_recipient (assuan_context_t ctx, char *line)
377 {
378   ctrl_t ctrl = assuan_get_pointer (ctx);
379   int rc;
380
381   if (!ctrl->audit)
382     rc = start_audit_session (ctrl);
383   else
384     rc = 0;
385
386   if (!rc)
387     rc = gpgsm_add_to_certlist (ctrl, line, 0,
388                                 &ctrl->server_local->recplist, 0);
389   if (rc)
390     {
391       gpgsm_status2 (ctrl, STATUS_INV_RECP,
392                      get_inv_recpsgnr_code (rc), line, NULL);
393     }
394
395   return rc;
396 }
397
398
399 static const char hlp_signer[] = 
400   "SIGNER <userID>\n"
401   "\n"
402   "Set the signer's keys for the signature creation.  USERID should\n"
403   "be the internal representation of the key; the server may accept any\n"
404   "other way of specification [we will support this].  If this is a\n"
405   "valid and usable signing key the server does respond with OK,\n"
406   "otherwise it returns an ERR with the reason why the key can't be\n"
407   "used, the signing will then not be done for this key.  If the policy\n"
408   "is not to sign at all if not all signer keys are valid, the client\n"
409   "has to take care of this.  All SIGNER commands are cumulative until\n"
410   "a RESET but they are *not* reset by an SIGN command becuase it can\n"
411   "be expected that set of signers are used for more than one sign\n"
412   "operation.";
413 static gpg_error_t
414 cmd_signer (assuan_context_t ctx, char *line)
415 {
416   ctrl_t ctrl = assuan_get_pointer (ctx);
417   int rc;
418
419   rc = gpgsm_add_to_certlist (ctrl, line, 1,
420                               &ctrl->server_local->signerlist, 0);
421   if (rc)
422     {
423       gpgsm_status2 (ctrl, STATUS_INV_SGNR, 
424                      get_inv_recpsgnr_code (rc), line, NULL);
425       /* For compatibiliy reasons we also issue the old code after the
426          new one.  */
427       gpgsm_status2 (ctrl, STATUS_INV_RECP, 
428                      get_inv_recpsgnr_code (rc), line, NULL);
429     }
430   return rc;
431 }
432
433
434 static const char hlp_encrypt[] = 
435   "ENCRYPT \n"
436   "\n"
437   "Do the actual encryption process. Takes the plaintext from the INPUT\n"
438   "command, writes to the ciphertext to the file descriptor set with\n"
439   "the OUTPUT command, take the recipients form all the recipients set\n"
440   "so far.  If this command fails the clients should try to delete all\n"
441   "output currently done or otherwise mark it as invalid.  GPGSM does\n"
442   "ensure that there won't be any security problem with leftover data\n"
443   "on the output in this case.\n"
444   "\n"
445   "This command should in general not fail, as all necessary checks\n"
446   "have been done while setting the recipients.  The input and output\n"
447   "pipes are closed.";
448 static gpg_error_t
449 cmd_encrypt (assuan_context_t ctx, char *line)
450 {
451   ctrl_t ctrl = assuan_get_pointer (ctx);
452   certlist_t cl;
453   int inp_fd, out_fd;
454   FILE *out_fp;
455   int rc;
456
457   (void)line;
458
459   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
460   if (inp_fd == -1)
461     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
462   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
463   if (out_fd == -1)
464     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
465
466   out_fp = fdopen (dup (out_fd), "w");
467   if (!out_fp)
468     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
469   
470   /* Now add all encrypt-to marked recipients from the default
471      list. */
472   rc = 0;
473   if (!opt.no_encrypt_to && !ctrl->server_local->no_encrypt_to)
474     {
475       for (cl=ctrl->server_local->default_recplist; !rc && cl; cl = cl->next)
476         if (cl->is_encrypt_to)
477           rc = gpgsm_add_cert_to_certlist (ctrl, cl->cert,
478                                            &ctrl->server_local->recplist, 1);
479     }
480   if (!rc)
481     rc = ctrl->audit? 0 : start_audit_session (ctrl);
482   if (!rc)
483     rc = gpgsm_encrypt (assuan_get_pointer (ctx),
484                         ctrl->server_local->recplist,
485                         inp_fd, out_fp);
486   fclose (out_fp);
487
488   gpgsm_release_certlist (ctrl->server_local->recplist);
489   ctrl->server_local->recplist = NULL;
490   /* Close and reset the fd */
491   close_message_fd (ctrl);
492   assuan_close_input_fd (ctx);
493   assuan_close_output_fd (ctx);
494   return rc;
495 }
496
497
498 static const char hlp_decrypt[] = 
499   "DECRYPT\n"
500   "\n"
501   "This performs the decrypt operation after doing some check on the\n"
502   "internal state. (e.g. that only needed data has been set).  Because\n"
503   "it utilizes the GPG-Agent for the session key decryption, there is\n"
504   "no need to ask the client for a protecting passphrase - GPG-Agent\n"
505   "does take care of this by requesting this from the user.";
506 static gpg_error_t
507 cmd_decrypt (assuan_context_t ctx, char *line)
508 {
509   ctrl_t ctrl = assuan_get_pointer (ctx);
510   int inp_fd, out_fd;
511   FILE *out_fp;
512   int rc;
513
514   (void)line;
515
516   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
517   if (inp_fd == -1)
518     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
519   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
520   if (out_fd == -1)
521     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
522
523   out_fp = fdopen (dup(out_fd), "w");
524   if (!out_fp)
525     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
526
527   rc = start_audit_session (ctrl);
528   if (!rc)
529     rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); 
530   fclose (out_fp);
531
532   /* close and reset the fd */
533   close_message_fd (ctrl);
534   assuan_close_input_fd (ctx);
535   assuan_close_output_fd (ctx);
536
537   return rc;
538 }
539
540
541 static const char hlp_verify[] = 
542   "VERIFY\n"
543   "\n"
544   "This does a verify operation on the message send to the input FD.\n"
545   "The result is written out using status lines.  If an output FD was\n"
546   "given, the signed text will be written to that.\n"
547   "\n"
548   "If the signature is a detached one, the server will inquire about\n"
549   "the signed material and the client must provide it.";
550 static gpg_error_t
551 cmd_verify (assuan_context_t ctx, char *line)
552 {
553   int rc;
554   ctrl_t ctrl = assuan_get_pointer (ctx);
555   int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
556   int out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
557   FILE *out_fp = NULL;
558
559   (void)line;
560
561   if (fd == -1)
562     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
563
564   if (out_fd != -1)
565     {
566       out_fp = fdopen ( dup(out_fd), "w");
567       if (!out_fp)
568         return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
569     }
570
571   rc = start_audit_session (ctrl);
572   if (!rc)
573     rc = gpgsm_verify (assuan_get_pointer (ctx), fd,
574                        ctrl->server_local->message_fd, out_fp);
575   if (out_fp)
576     fclose (out_fp);
577
578   /* close and reset the fd */
579   close_message_fd (ctrl);
580   assuan_close_input_fd (ctx);
581   assuan_close_output_fd (ctx);
582
583   return rc;
584 }
585
586
587 static const char hlp_sign[] = 
588   "SIGN [--detached]\n"
589   "\n"
590   "Sign the data set with the INPUT command and write it to the sink\n"
591   "set by OUTPUT.  With \"--detached\", a detached signature is\n"
592   "created (surprise).";
593 static gpg_error_t
594 cmd_sign (assuan_context_t ctx, char *line)
595 {
596   ctrl_t ctrl = assuan_get_pointer (ctx);
597   int inp_fd, out_fd;
598   FILE *out_fp;
599   int detached;
600   int rc;
601
602   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
603   if (inp_fd == -1)
604     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
605   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
606   if (out_fd == -1)
607     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
608
609   detached = has_option (line, "--detached"); 
610
611   out_fp = fdopen ( dup(out_fd), "w");
612   if (!out_fp)
613     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
614
615   rc = start_audit_session (ctrl);
616   if (!rc)
617     rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
618                      inp_fd, detached, out_fp);
619   fclose (out_fp);
620
621   /* close and reset the fd */
622   close_message_fd (ctrl);
623   assuan_close_input_fd (ctx);
624   assuan_close_output_fd (ctx);
625
626   return rc;
627 }
628
629
630 static const char hlp_import[] = 
631   "IMPORT [--re-import]\n"
632   "\n"
633   "Import the certificates read form the input-fd, return status\n"
634   "message for each imported one.  The import checks the validity of\n"
635   "the certificate but not of the entire chain.  It is possible to\n"
636   "import expired certificates.\n"
637   "\n"
638   "With the option --re-import the input data is expected to a be a LF\n"
639   "separated list of fingerprints.  The command will re-import these\n"
640   "certificates, meaning that they are made permanent by removing\n"
641   "their ephemeral flag.";
642 static gpg_error_t
643 cmd_import (assuan_context_t ctx, char *line)
644 {
645   ctrl_t ctrl = assuan_get_pointer (ctx);
646   int rc;
647   int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
648   int reimport = has_option (line, "--re-import"); 
649
650   (void)line;
651
652   if (fd == -1)
653     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
654
655   rc = gpgsm_import (assuan_get_pointer (ctx), fd, reimport);
656
657   /* close and reset the fd */
658   close_message_fd (ctrl);
659   assuan_close_input_fd (ctx);
660   assuan_close_output_fd (ctx);
661
662   return rc;
663 }
664
665
666 static const char hlp_export[] =
667   "EXPORT [--data [--armor|--base64]] [--] <pattern>\n"
668   "\n"
669   "Export the certificates selected by PATTERN.  With --data the output\n"
670   "is returned using Assuan D lines; the default is to use the sink given\n"
671   "by the last \"OUTPUT\" command.  The options --armor or --base64 encode \n"
672   "the output using the PEM respective a plain base-64 format; the default\n"
673   "is a binary format which is only suitable for a single certificate.";
674 static gpg_error_t
675 cmd_export (assuan_context_t ctx, char *line)
676 {
677   ctrl_t ctrl = assuan_get_pointer (ctx);
678   char *p;
679   strlist_t list, sl;
680   int use_data;
681   
682   use_data = has_option (line, "--data");
683
684   if (use_data)
685     {
686       /* We need to override any possible setting done by an OUTPUT command. */
687       ctrl->create_pem = has_option (line, "--armor");
688       ctrl->create_base64 = has_option (line, "--base64");
689     }
690
691   line = skip_options (line);
692
693   /* Break the line down into an strlist_t. */
694   list = NULL;
695   for (p=line; *p; line = p)
696     {
697       while (*p && *p != ' ')
698         p++;
699       if (*p)
700         *p++ = 0;
701       if (*line)
702         {
703           sl = xtrymalloc (sizeof *sl + strlen (line));
704           if (!sl)
705             {
706               free_strlist (list);
707               return out_of_core ();
708             }
709           sl->flags = 0;
710           strcpy_escaped_plus (sl->d, line);
711           sl->next = list;
712           list = sl;
713         }
714     }
715
716   if (use_data)
717     {
718       estream_t stream;
719
720       stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
721       if (!stream)
722         {
723           free_strlist (list);
724           return set_error (GPG_ERR_ASS_GENERAL, 
725                             "error setting up a data stream");
726         }
727       gpgsm_export (ctrl, list, NULL, stream);
728       es_fclose (stream);
729     }
730   else
731     {
732       int fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
733       FILE *out_fp;
734
735       if (fd == -1)
736         {
737           free_strlist (list);
738           return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
739         }
740       out_fp = fdopen ( dup(fd), "w");
741       if (!out_fp)
742         {
743           free_strlist (list);
744           return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
745         }
746       
747       gpgsm_export (ctrl, list, out_fp, NULL);
748       fclose (out_fp);
749     }
750
751   free_strlist (list);
752   /* Close and reset the fds. */
753   close_message_fd (ctrl);
754   assuan_close_input_fd (ctx);
755   assuan_close_output_fd (ctx);
756   return 0;
757 }
758
759
760
761 static const char hlp_delkeys[] =
762   "DELKEYS <patterns>\n"
763   "\n"
764   "Delete the certificates specified by PATTERNS.  Each pattern shall be\n"
765   "a percent-plus escaped certificate specification.  Usually a\n"
766   "fingerprint will be used for this.";
767 static gpg_error_t
768 cmd_delkeys (assuan_context_t ctx, char *line)
769 {
770   ctrl_t ctrl = assuan_get_pointer (ctx);
771   char *p;
772   strlist_t list, sl;
773   int rc;
774
775   /* break the line down into an strlist_t */
776   list = NULL;
777   for (p=line; *p; line = p)
778     {
779       while (*p && *p != ' ')
780         p++;
781       if (*p)
782         *p++ = 0;
783       if (*line)
784         {
785           sl = xtrymalloc (sizeof *sl + strlen (line));
786           if (!sl)
787             {
788               free_strlist (list);
789               return out_of_core ();
790             }
791           sl->flags = 0;
792           strcpy_escaped_plus (sl->d, line);
793           sl->next = list;
794           list = sl;
795         }
796     }
797
798   rc = gpgsm_delete (ctrl, list);
799   free_strlist (list);
800
801   /* close and reset the fd */
802   close_message_fd (ctrl);
803   assuan_close_input_fd (ctx);
804   assuan_close_output_fd (ctx);
805
806   return rc;
807 }
808
809
810
811 static const char hlp_output[] =
812   "OUTPUT FD[=<n>]\n"
813   "\n"
814   "Set the file descriptor to write the output data to N.  If N is not\n"
815   "given and the operating system supports file descriptor passing, the\n"
816   "file descriptor currently in flight will be used.  See also the\n"
817   "\"INPUT\" and \"MESSAGE\" commands.";
818 static const char hlp_input[] =
819   "INPUT FD[=<n>]\n"
820   "\n"
821   "Set the file descriptor to read the input data to N.  If N is not\n"
822   "given and the operating system supports file descriptor passing, the\n"
823   "file descriptor currently in flight will be used.  See also the\n"
824   "\"MESSAGE\" and \"OUTPUT\" commands.";
825 static const char hlp_message[] =
826   "MESSAGE FD[=<n>]\n"
827   "\n"
828   "Set the file descriptor to read the message for a detached\n"
829   "signatures to N.  If N is not given and the operating system\n"
830   "supports file descriptor passing, the file descriptor currently in\n"
831   "flight will be used.  See also the \"INPUT\" and \"OUTPUT\" commands.";
832 static gpg_error_t
833 cmd_message (assuan_context_t ctx, char *line)
834 {
835   int rc;
836   gnupg_fd_t sysfd;
837   int fd;
838   ctrl_t ctrl = assuan_get_pointer (ctx);
839
840   rc = assuan_command_parse_fd (ctx, line, &sysfd);
841   if (rc)
842     return rc;
843   fd = translate_sys2libc_fd (sysfd, 0);
844   if (fd == -1)
845     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
846   ctrl->server_local->message_fd = fd;
847   return 0;
848 }
849
850
851
852 static const char hlp_listkeys[] = 
853   "LISTKEYS [<patterns>]\n"
854   "LISTSECRETKEYS [<patterns>]\n"
855   "DUMPKEYS [<patterns>]\n"
856   "DUMPSECRETKEYS [<patterns>]\n"
857   "\n"
858   "List all certificates or only those specified by PATTERNS.  Each\n"
859   "pattern shall be a percent-plus escaped certificate specification.\n"
860   "The \"SECRET\" versions of the command filter the output to include\n"
861   "only certificates where the secret key is available or a corresponding\n"
862   "smartcard has been registered.  The \"DUMP\" versions of the command\n"
863   "are only useful for debugging.  The output format is a percent escaped\n"
864   "colon delimited listing as described in the manual.\n"
865   "\n"
866   "These \"OPTION\" command keys effect the output::\n"
867   "\n"
868   "  \"list-mode\" set to 0: List only local certificates (default).\n"
869   "                     1: Ditto.\n"
870   "                     2: List only external certificates.\n"
871   "                     3: List local and external certificates.\n"
872   "\n"
873   "  \"with-validation\" set to true: Validate each certificate.\n"
874   "\n"
875   "  \"with-ephemeral-key\" set to true: Always include ephemeral\n"
876   "                                    certificates.\n"
877   "\n"
878   "  \"list-to-output\" set to true: Write output to the file descriptor\n"
879   "                                given by the last \"OUTPUT\" command.";
880 static int 
881 do_listkeys (assuan_context_t ctx, char *line, int mode)
882 {
883   ctrl_t ctrl = assuan_get_pointer (ctx);
884   estream_t fp;
885   char *p;
886   strlist_t list, sl;
887   unsigned int listmode;
888   gpg_error_t err;
889
890   /* Break the line down into an strlist. */
891   list = NULL;
892   for (p=line; *p; line = p)
893     {
894       while (*p && *p != ' ')
895         p++;
896       if (*p)
897         *p++ = 0;
898       if (*line)
899         {
900           sl = xtrymalloc (sizeof *sl + strlen (line));
901           if (!sl)
902             {
903               free_strlist (list);
904               return out_of_core ();
905             }
906           sl->flags = 0;
907           strcpy_escaped_plus (sl->d, line);
908           sl->next = list;
909           list = sl;
910         }
911     }
912
913   if (ctrl->server_local->list_to_output)
914     {
915       int outfd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
916
917       if ( outfd == -1 )
918         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
919       fp = es_fdopen ( dup (outfd), "w");
920       if (!fp)
921         return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
922     }
923   else
924     {
925       fp = es_fopencookie (ctx, "w", data_line_cookie_functions);
926       if (!fp)
927         return set_error (GPG_ERR_ASS_GENERAL, 
928                           "error setting up a data stream");
929     }
930   
931   ctrl->with_colons = 1;
932   listmode = mode; 
933   if (ctrl->server_local->list_internal)
934     listmode |= (1<<6);
935   if (ctrl->server_local->list_external)
936     listmode |= (1<<7);
937   err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
938   free_strlist (list);
939   es_fclose (fp);
940   if (ctrl->server_local->list_to_output)
941     assuan_close_output_fd (ctx);
942   return err;
943 }
944
945 static gpg_error_t
946 cmd_listkeys (assuan_context_t ctx, char *line)
947 {
948   return do_listkeys (ctx, line, 3);
949 }
950
951 static gpg_error_t
952 cmd_dumpkeys (assuan_context_t ctx, char *line)
953 {
954   return do_listkeys (ctx, line, 259);
955 }
956
957 static gpg_error_t
958 cmd_listsecretkeys (assuan_context_t ctx, char *line)
959 {
960   return do_listkeys (ctx, line, 2);
961 }
962
963 static gpg_error_t
964 cmd_dumpsecretkeys (assuan_context_t ctx, char *line)
965 {
966   return do_listkeys (ctx, line, 258);
967 }
968
969
970 \f
971 static const char hlp_genkey[] =
972   "GENKEY\n"
973   "\n"
974   "Read the parameters in native format from the input fd and write a\n"
975   "certificate request to the output.";
976 static gpg_error_t
977 cmd_genkey (assuan_context_t ctx, char *line)
978 {
979   ctrl_t ctrl = assuan_get_pointer (ctx);
980   int inp_fd, out_fd;
981   FILE *out_fp;
982   int rc;
983   estream_t in_stream;
984
985   (void)line;
986
987   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
988   if (inp_fd == -1)
989     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
990   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
991   if (out_fd == -1)
992     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
993
994   in_stream = es_fdopen_nc (inp_fd, "r");
995   if (!in_stream)
996     return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed");
997
998   out_fp = fdopen ( dup(out_fd), "w");
999   if (!out_fp)
1000     {
1001       es_fclose (in_stream);
1002       return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
1003     }
1004   rc = gpgsm_genkey (ctrl, in_stream, out_fp);
1005   fclose (out_fp);
1006
1007   /* close and reset the fds */
1008   assuan_close_input_fd (ctx);
1009   assuan_close_output_fd (ctx);
1010
1011   return rc;
1012 }
1013
1014
1015 \f
1016 static const char hlp_getauditlog[] =
1017   "GETAUDITLOG [--data] [--html]\n"
1018   "\n"
1019   "If --data is used, the output is send using D-lines and not to the\n"
1020   "file descriptor given by an OUTPUT command.\n"
1021   "\n"
1022   "If --html is used the output is formated as an XHTML block. This is\n"
1023   "designed to be incorporated into a HTML document.";
1024 static gpg_error_t
1025 cmd_getauditlog (assuan_context_t ctx, char *line)
1026 {
1027   ctrl_t ctrl = assuan_get_pointer (ctx);
1028   int  out_fd;
1029   estream_t out_stream;
1030   int opt_data, opt_html;
1031   int rc;
1032
1033   opt_data = has_option (line, "--data"); 
1034   opt_html = has_option (line, "--html"); 
1035   line = skip_options (line);
1036
1037   if (!ctrl->audit)
1038     return gpg_error (GPG_ERR_NO_DATA);
1039
1040   if (opt_data)
1041     {
1042       out_stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
1043       if (!out_stream)
1044         return set_error (GPG_ERR_ASS_GENERAL, 
1045                           "error setting up a data stream");
1046     }
1047   else
1048     {
1049       out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
1050       if (out_fd == -1)
1051         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
1052       
1053       out_stream = es_fdopen_nc ( dup (out_fd), "w");
1054       if (!out_stream)
1055         {
1056           return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
1057         }
1058     }
1059
1060   audit_print_result (ctrl->audit, out_stream, opt_html);
1061   rc = 0;
1062
1063   es_fclose (out_stream);
1064
1065   /* Close and reset the fd. */
1066   if (!opt_data)
1067     assuan_close_output_fd (ctx);
1068   return rc;
1069 }
1070
1071
1072 static const char hlp_getinfo[] = 
1073   "GETINFO <what>\n"
1074   "\n"
1075   "Multipurpose function to return a variety of information.\n"
1076   "Supported values for WHAT are:\n"
1077   "\n"
1078   "  version     - Return the version of the program.\n"
1079   "  pid         - Return the process id of the server.\n"
1080   "  agent-check - Return success if the agent is running.\n"
1081   "  cmd_has_option CMD OPT\n"
1082   "              - Returns OK if the command CMD implements the option OPT.";
1083 static gpg_error_t
1084 cmd_getinfo (assuan_context_t ctx, char *line)
1085 {
1086   int rc = 0;
1087
1088   if (!strcmp (line, "version"))
1089     {
1090       const char *s = VERSION;
1091       rc = assuan_send_data (ctx, s, strlen (s));
1092     }
1093   else if (!strcmp (line, "pid"))
1094     {
1095       char numbuf[50];
1096
1097       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
1098       rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
1099     }
1100   else if (!strcmp (line, "agent-check"))
1101     {
1102       ctrl_t ctrl = assuan_get_pointer (ctx);
1103       rc = gpgsm_agent_send_nop (ctrl);
1104     }
1105   else if (!strncmp (line, "cmd_has_option", 14)
1106            && (line[14] == ' ' || line[14] == '\t' || !line[14]))
1107     {
1108       char *cmd, *cmdopt;
1109       line += 14;
1110       while (*line == ' ' || *line == '\t')
1111         line++;
1112       if (!*line)
1113         rc = gpg_error (GPG_ERR_MISSING_VALUE);
1114       else
1115         {
1116           cmd = line;
1117           while (*line && (*line != ' ' && *line != '\t'))
1118             line++;
1119           if (!*line)
1120             rc = gpg_error (GPG_ERR_MISSING_VALUE);
1121           else
1122             {
1123               *line++ = 0;
1124               while (*line == ' ' || *line == '\t')
1125                 line++;
1126               if (!*line)
1127                 rc = gpg_error (GPG_ERR_MISSING_VALUE);
1128               else
1129                 {
1130                   cmdopt = line;
1131                   if (!command_has_option (cmd, cmdopt))
1132                     rc = gpg_error (GPG_ERR_GENERAL);
1133                 }
1134             }
1135         }
1136     }
1137   else
1138     rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
1139
1140   return rc;
1141 }
1142
1143
1144 \f
1145 static const char hlp_passwd[] =
1146   "PASSWD <userID>\n"
1147   "\n"
1148   "Change the passphrase of the secret key for USERID.";
1149 static gpg_error_t
1150 cmd_passwd (assuan_context_t ctx, char *line)
1151 {
1152   ctrl_t ctrl = assuan_get_pointer (ctx);
1153   gpg_error_t err;
1154   ksba_cert_t cert = NULL;
1155   char *grip = NULL;
1156
1157   line = skip_options (line);
1158
1159   err = gpgsm_find_cert (line, NULL, &cert);
1160   if (err)
1161     ;
1162   else if (!(grip = gpgsm_get_keygrip_hexstring (cert)))
1163     err = gpg_error (GPG_ERR_INTERNAL);
1164   else 
1165     {
1166       char *desc = gpgsm_format_keydesc (cert);
1167       err = gpgsm_agent_passwd (ctrl, grip, desc);
1168       xfree (desc);
1169     }
1170
1171   xfree (grip);
1172   ksba_cert_release (cert);
1173
1174   return err;
1175 }
1176
1177
1178
1179
1180 \f
1181 /* Return true if the command CMD implements the option OPT.  */
1182 static int
1183 command_has_option (const char *cmd, const char *cmdopt)
1184 {
1185   if (!strcmp (cmd, "IMPORT"))
1186     {
1187       if (!strcmp (cmdopt, "re-import"))
1188         return 1;
1189     }
1190       
1191   return 0;
1192 }
1193
1194
1195 /* Tell the assuan library about our commands */
1196 static int
1197 register_commands (assuan_context_t ctx)
1198 {
1199   static struct {
1200     const char *name;
1201     assuan_handler_t handler;
1202     const char * const help;
1203   } table[] = {
1204     { "RECIPIENT",     cmd_recipient, hlp_recipient },
1205     { "SIGNER",        cmd_signer,    hlp_signer },
1206     { "ENCRYPT",       cmd_encrypt,   hlp_encrypt },
1207     { "DECRYPT",       cmd_decrypt,   hlp_decrypt },
1208     { "VERIFY",        cmd_verify,    hlp_verify },
1209     { "SIGN",          cmd_sign,      hlp_sign },
1210     { "IMPORT",        cmd_import,    hlp_import },
1211     { "EXPORT",        cmd_export,    hlp_export },
1212     { "INPUT",         NULL,          hlp_input }, 
1213     { "OUTPUT",        NULL,          hlp_output }, 
1214     { "MESSAGE",       cmd_message,   hlp_message },
1215     { "LISTKEYS",      cmd_listkeys,  hlp_listkeys },
1216     { "DUMPKEYS",      cmd_dumpkeys,  hlp_listkeys },
1217     { "LISTSECRETKEYS",cmd_listsecretkeys, hlp_listkeys },
1218     { "DUMPSECRETKEYS",cmd_dumpsecretkeys, hlp_listkeys },
1219     { "GENKEY",        cmd_genkey,    hlp_genkey },
1220     { "DELKEYS",       cmd_delkeys,   hlp_delkeys },
1221     { "GETAUDITLOG",   cmd_getauditlog,    hlp_getauditlog },
1222     { "GETINFO",       cmd_getinfo,   hlp_getinfo },
1223     { "PASSWD",        cmd_passwd,    hlp_passwd },
1224     { NULL }
1225   };
1226   int i, rc;
1227
1228   for (i=0; table[i].name; i++)
1229     {
1230       rc = assuan_register_command (ctx, table[i].name, table[i].handler,
1231                                     table[i].help);
1232       if (rc)
1233         return rc;
1234     } 
1235   return 0;
1236 }
1237
1238 /* Startup the server. DEFAULT_RECPLIST is the list of recipients as
1239    set from the command line or config file.  We only require those
1240    marked as encrypt-to. */
1241 void
1242 gpgsm_server (certlist_t default_recplist)
1243 {
1244   int rc;
1245   assuan_fd_t filedes[2];
1246   assuan_context_t ctx;
1247   struct server_control_s ctrl;
1248   static const char hello[] = ("GNU Privacy Guard's S/M server "
1249                                VERSION " ready");
1250
1251   memset (&ctrl, 0, sizeof ctrl);
1252   gpgsm_init_default_ctrl (&ctrl);
1253
1254   /* We use a pipe based server so that we can work from scripts.
1255      assuan_init_pipe_server will automagically detect when we are
1256      called with a socketpair and ignore FIELDES in this case. */
1257   filedes[0] = assuan_fdopen (0);
1258   filedes[1] = assuan_fdopen (1);
1259   rc = assuan_new (&ctx);
1260   if (rc)
1261     {
1262       log_error ("failed to allocate assuan context: %s\n",
1263                  gpg_strerror (rc));
1264       gpgsm_exit (2);
1265     }
1266
1267   rc = assuan_init_pipe_server (ctx, filedes);
1268   if (rc)
1269     {
1270       log_error ("failed to initialize the server: %s\n",
1271                  gpg_strerror (rc));
1272       gpgsm_exit (2);
1273     }
1274   rc = register_commands (ctx);
1275   if (rc)
1276     {
1277       log_error ("failed to the register commands with Assuan: %s\n",
1278                  gpg_strerror(rc));
1279       gpgsm_exit (2);
1280     }
1281   if (opt.verbose || opt.debug)
1282     {
1283       char *tmp = NULL;
1284       const char *s1 = getenv ("GPG_AGENT_INFO");
1285       const char *s2 = getenv ("DIRMNGR_INFO");
1286
1287       if (asprintf (&tmp,
1288                     "Home: %s\n"
1289                     "Config: %s\n"
1290                     "AgentInfo: %s\n"
1291                     "DirmngrInfo: %s\n"
1292                     "%s",
1293                     opt.homedir,
1294                     opt.config_filename,
1295                     s1?s1:"[not set]",
1296                     s2?s2:"[not set]",
1297                     hello) > 0)
1298         {
1299           assuan_set_hello_line (ctx, tmp);
1300           free (tmp);
1301         }
1302     }
1303   else
1304     assuan_set_hello_line (ctx, hello);
1305
1306   assuan_register_reset_notify (ctx, reset_notify);
1307   assuan_register_input_notify (ctx, input_notify);
1308   assuan_register_output_notify (ctx, output_notify);
1309   assuan_register_option_handler (ctx, option_handler);
1310
1311   assuan_set_pointer (ctx, &ctrl);
1312   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
1313   ctrl.server_local->assuan_ctx = ctx;
1314   ctrl.server_local->message_fd = -1;
1315   ctrl.server_local->list_internal = 1;
1316   ctrl.server_local->list_external = 0;
1317   ctrl.server_local->default_recplist = default_recplist;
1318
1319   if (DBG_ASSUAN)
1320     assuan_set_log_stream (ctx, log_get_stream ());
1321
1322   for (;;)
1323     {
1324       rc = assuan_accept (ctx);
1325       if (rc == -1)
1326         {
1327           break;
1328         }
1329       else if (rc)
1330         {
1331           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
1332           break;
1333         }
1334       
1335       rc = assuan_process (ctx);
1336       if (rc)
1337         {
1338           log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
1339           continue;
1340         }
1341     }
1342
1343   gpgsm_release_certlist (ctrl.server_local->recplist);
1344   ctrl.server_local->recplist = NULL;
1345   gpgsm_release_certlist (ctrl.server_local->signerlist);
1346   ctrl.server_local->signerlist = NULL;
1347   xfree (ctrl.server_local);
1348
1349   audit_release (ctrl.audit);
1350   ctrl.audit = NULL;
1351
1352   assuan_release (ctx);
1353 }
1354
1355
1356
1357 gpg_error_t
1358 gpgsm_status2 (ctrl_t ctrl, int no, ...)
1359 {
1360   gpg_error_t err = 0;
1361   va_list arg_ptr;
1362   const char *text;
1363
1364   va_start (arg_ptr, no);
1365
1366   if (ctrl->no_server && ctrl->status_fd == -1)
1367     ; /* No status wanted. */
1368   else if (ctrl->no_server)
1369     {
1370       if (!statusfp)
1371         {
1372           if (ctrl->status_fd == 1)
1373             statusfp = stdout;
1374           else if (ctrl->status_fd == 2)
1375             statusfp = stderr;
1376           else
1377             statusfp = fdopen (ctrl->status_fd, "w");
1378       
1379           if (!statusfp)
1380             {
1381               log_fatal ("can't open fd %d for status output: %s\n",
1382                          ctrl->status_fd, strerror(errno));
1383             }
1384         }
1385       
1386       fputs ("[GNUPG:] ", statusfp);
1387       fputs (get_status_string (no), statusfp);
1388     
1389       while ( (text = va_arg (arg_ptr, const char*) ))
1390         {
1391           putc ( ' ', statusfp );
1392           for (; *text; text++) 
1393             {
1394               if (*text == '\n')
1395                 fputs ( "\\n", statusfp );
1396               else if (*text == '\r')
1397                 fputs ( "\\r", statusfp );
1398               else 
1399                 putc ( *(const byte *)text,  statusfp );
1400             }
1401         }
1402       putc ('\n', statusfp);
1403       fflush (statusfp);
1404     }
1405   else 
1406     {
1407       assuan_context_t ctx = ctrl->server_local->assuan_ctx;
1408       char buf[950], *p;
1409       size_t n;
1410
1411       p = buf; 
1412       n = 0;
1413       while ( (text = va_arg (arg_ptr, const char *)) )
1414         {
1415           if (n)
1416             {
1417               *p++ = ' ';
1418               n++;
1419             }
1420           for ( ; *text && n < DIM (buf)-2; n++)
1421             *p++ = *text++;
1422         }
1423       *p = 0;
1424       err = assuan_write_status (ctx, get_status_string (no), buf);
1425     }
1426
1427   va_end (arg_ptr);
1428   return err;
1429 }
1430
1431 gpg_error_t
1432 gpgsm_status (ctrl_t ctrl, int no, const char *text)
1433 {
1434   return gpgsm_status2 (ctrl, no, text, NULL);
1435 }
1436
1437 gpg_error_t
1438 gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
1439                             gpg_err_code_t ec)
1440 {
1441   char buf[30];
1442
1443   sprintf (buf, "%u", (unsigned int)ec);
1444   if (text)
1445     return gpgsm_status2 (ctrl, no, text, buf, NULL);
1446   else
1447     return gpgsm_status2 (ctrl, no, buf, NULL);
1448 }
1449
1450
1451 /* Helper to notify the client about Pinentry events.  Because that
1452    might disturb some older clients, this is only done when enabled
1453    via an option.  Returns an gpg error code. */
1454 gpg_error_t
1455 gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
1456 {
1457   if (!ctrl || !ctrl->server_local 
1458       || !ctrl->server_local->allow_pinentry_notify)
1459     return 0;
1460   return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
1461 }
1462
1463
1464