42cadcca12ef9d9f37a93c384debb88cf9a99efd
[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, 2009,
3  *               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   estream_t 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 = es_fdopen_nc (out_fd, "w");
467   if (!out_fp)
468     return set_error (gpg_err_code_from_syserror (), "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   es_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   estream_t 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 = es_fdopen_nc (out_fd, "w");
524   if (!out_fp)
525     return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
526
527   rc = start_audit_session (ctrl);
528   if (!rc)
529     rc = gpgsm_decrypt (ctrl, inp_fd, out_fp); 
530   es_fclose (out_fp);
531
532   /* Close and reset the fds. */
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   estream_t 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 = es_fdopen_nc (out_fd, "w");
567       if (!out_fp)
568         return set_error (gpg_err_code_from_syserror (), "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   es_fclose (out_fp);
576
577   /* Close and reset the fd.  */
578   close_message_fd (ctrl);
579   assuan_close_input_fd (ctx);
580   assuan_close_output_fd (ctx);
581
582   return rc;
583 }
584
585
586 static const char hlp_sign[] = 
587   "SIGN [--detached]\n"
588   "\n"
589   "Sign the data set with the INPUT command and write it to the sink\n"
590   "set by OUTPUT.  With \"--detached\", a detached signature is\n"
591   "created (surprise).";
592 static gpg_error_t
593 cmd_sign (assuan_context_t ctx, char *line)
594 {
595   ctrl_t ctrl = assuan_get_pointer (ctx);
596   int inp_fd, out_fd;
597   estream_t out_fp;
598   int detached;
599   int rc;
600
601   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
602   if (inp_fd == -1)
603     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
604   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
605   if (out_fd == -1)
606     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
607
608   detached = has_option (line, "--detached"); 
609
610   out_fp = es_fdopen_nc (out_fd, "w");
611   if (!out_fp)
612     return set_error (GPG_ERR_ASS_GENERAL, "fdopen() failed");
613
614   rc = start_audit_session (ctrl);
615   if (!rc)
616     rc = gpgsm_sign (assuan_get_pointer (ctx), ctrl->server_local->signerlist,
617                      inp_fd, detached, out_fp);
618   es_fclose (out_fp);
619
620   /* close and reset the fd */
621   close_message_fd (ctrl);
622   assuan_close_input_fd (ctx);
623   assuan_close_output_fd (ctx);
624
625   return rc;
626 }
627
628
629 static const char hlp_import[] = 
630   "IMPORT [--re-import]\n"
631   "\n"
632   "Import the certificates read form the input-fd, return status\n"
633   "message for each imported one.  The import checks the validity of\n"
634   "the certificate but not of the entire chain.  It is possible to\n"
635   "import expired certificates.\n"
636   "\n"
637   "With the option --re-import the input data is expected to a be a LF\n"
638   "separated list of fingerprints.  The command will re-import these\n"
639   "certificates, meaning that they are made permanent by removing\n"
640   "their ephemeral flag.";
641 static gpg_error_t
642 cmd_import (assuan_context_t ctx, char *line)
643 {
644   ctrl_t ctrl = assuan_get_pointer (ctx);
645   int rc;
646   int fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
647   int reimport = has_option (line, "--re-import"); 
648
649   (void)line;
650
651   if (fd == -1)
652     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
653
654   rc = gpgsm_import (assuan_get_pointer (ctx), fd, reimport);
655
656   /* close and reset the fd */
657   close_message_fd (ctrl);
658   assuan_close_input_fd (ctx);
659   assuan_close_output_fd (ctx);
660
661   return rc;
662 }
663
664
665 static const char hlp_export[] =
666   "EXPORT [--data [--armor|--base64]] [--] <pattern>\n"
667   "\n"
668   "Export the certificates selected by PATTERN.  With --data the output\n"
669   "is returned using Assuan D lines; the default is to use the sink given\n"
670   "by the last \"OUTPUT\" command.  The options --armor or --base64 encode \n"
671   "the output using the PEM respective a plain base-64 format; the default\n"
672   "is a binary format which is only suitable for a single certificate.";
673 static gpg_error_t
674 cmd_export (assuan_context_t ctx, char *line)
675 {
676   ctrl_t ctrl = assuan_get_pointer (ctx);
677   char *p;
678   strlist_t list, sl;
679   int use_data;
680   
681   use_data = has_option (line, "--data");
682
683   if (use_data)
684     {
685       /* We need to override any possible setting done by an OUTPUT command. */
686       ctrl->create_pem = has_option (line, "--armor");
687       ctrl->create_base64 = has_option (line, "--base64");
688     }
689
690   line = skip_options (line);
691
692   /* Break the line down into an strlist_t. */
693   list = NULL;
694   for (p=line; *p; line = p)
695     {
696       while (*p && *p != ' ')
697         p++;
698       if (*p)
699         *p++ = 0;
700       if (*line)
701         {
702           sl = xtrymalloc (sizeof *sl + strlen (line));
703           if (!sl)
704             {
705               free_strlist (list);
706               return out_of_core ();
707             }
708           sl->flags = 0;
709           strcpy_escaped_plus (sl->d, line);
710           sl->next = list;
711           list = sl;
712         }
713     }
714
715   if (use_data)
716     {
717       estream_t stream;
718
719       stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
720       if (!stream)
721         {
722           free_strlist (list);
723           return set_error (GPG_ERR_ASS_GENERAL, 
724                             "error setting up a data stream");
725         }
726       gpgsm_export (ctrl, list, stream);
727       es_fclose (stream);
728     }
729   else
730     {
731       int fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
732       estream_t out_fp;
733
734       if (fd == -1)
735         {
736           free_strlist (list);
737           return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
738         }
739       out_fp = es_fdopen_nc (fd, "w");
740       if (!out_fp)
741         {
742           free_strlist (list);
743           return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
744         }
745       
746       gpgsm_export (ctrl, list, out_fp);
747       es_fclose (out_fp);
748     }
749
750   free_strlist (list);
751   /* Close and reset the fds. */
752   close_message_fd (ctrl);
753   assuan_close_input_fd (ctx);
754   assuan_close_output_fd (ctx);
755   return 0;
756 }
757
758
759
760 static const char hlp_delkeys[] =
761   "DELKEYS <patterns>\n"
762   "\n"
763   "Delete the certificates specified by PATTERNS.  Each pattern shall be\n"
764   "a percent-plus escaped certificate specification.  Usually a\n"
765   "fingerprint will be used for this.";
766 static gpg_error_t
767 cmd_delkeys (assuan_context_t ctx, char *line)
768 {
769   ctrl_t ctrl = assuan_get_pointer (ctx);
770   char *p;
771   strlist_t list, sl;
772   int rc;
773
774   /* break the line down into an strlist_t */
775   list = NULL;
776   for (p=line; *p; line = p)
777     {
778       while (*p && *p != ' ')
779         p++;
780       if (*p)
781         *p++ = 0;
782       if (*line)
783         {
784           sl = xtrymalloc (sizeof *sl + strlen (line));
785           if (!sl)
786             {
787               free_strlist (list);
788               return out_of_core ();
789             }
790           sl->flags = 0;
791           strcpy_escaped_plus (sl->d, line);
792           sl->next = list;
793           list = sl;
794         }
795     }
796
797   rc = gpgsm_delete (ctrl, list);
798   free_strlist (list);
799
800   /* close and reset the fd */
801   close_message_fd (ctrl);
802   assuan_close_input_fd (ctx);
803   assuan_close_output_fd (ctx);
804
805   return rc;
806 }
807
808
809
810 static const char hlp_output[] =
811   "OUTPUT FD[=<n>]\n"
812   "\n"
813   "Set the file descriptor to write the output data to N.  If N is not\n"
814   "given and the operating system supports file descriptor passing, the\n"
815   "file descriptor currently in flight will be used.  See also the\n"
816   "\"INPUT\" and \"MESSAGE\" commands.";
817 static const char hlp_input[] =
818   "INPUT FD[=<n>]\n"
819   "\n"
820   "Set the file descriptor to read the input data to N.  If N is not\n"
821   "given and the operating system supports file descriptor passing, the\n"
822   "file descriptor currently in flight will be used.  See also the\n"
823   "\"MESSAGE\" and \"OUTPUT\" commands.";
824 static const char hlp_message[] =
825   "MESSAGE FD[=<n>]\n"
826   "\n"
827   "Set the file descriptor to read the message for a detached\n"
828   "signatures to N.  If N is not given and the operating system\n"
829   "supports file descriptor passing, the file descriptor currently in\n"
830   "flight will be used.  See also the \"INPUT\" and \"OUTPUT\" commands.";
831 static gpg_error_t
832 cmd_message (assuan_context_t ctx, char *line)
833 {
834   int rc;
835   gnupg_fd_t sysfd;
836   int fd;
837   ctrl_t ctrl = assuan_get_pointer (ctx);
838
839   rc = assuan_command_parse_fd (ctx, line, &sysfd);
840   if (rc)
841     return rc;
842   fd = translate_sys2libc_fd (sysfd, 0);
843   if (fd == -1)
844     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
845   ctrl->server_local->message_fd = fd;
846   return 0;
847 }
848
849
850
851 static const char hlp_listkeys[] = 
852   "LISTKEYS [<patterns>]\n"
853   "LISTSECRETKEYS [<patterns>]\n"
854   "DUMPKEYS [<patterns>]\n"
855   "DUMPSECRETKEYS [<patterns>]\n"
856   "\n"
857   "List all certificates or only those specified by PATTERNS.  Each\n"
858   "pattern shall be a percent-plus escaped certificate specification.\n"
859   "The \"SECRET\" versions of the command filter the output to include\n"
860   "only certificates where the secret key is available or a corresponding\n"
861   "smartcard has been registered.  The \"DUMP\" versions of the command\n"
862   "are only useful for debugging.  The output format is a percent escaped\n"
863   "colon delimited listing as described in the manual.\n"
864   "\n"
865   "These \"OPTION\" command keys effect the output::\n"
866   "\n"
867   "  \"list-mode\" set to 0: List only local certificates (default).\n"
868   "                     1: Ditto.\n"
869   "                     2: List only external certificates.\n"
870   "                     3: List local and external certificates.\n"
871   "\n"
872   "  \"with-validation\" set to true: Validate each certificate.\n"
873   "\n"
874   "  \"with-ephemeral-key\" set to true: Always include ephemeral\n"
875   "                                    certificates.\n"
876   "\n"
877   "  \"list-to-output\" set to true: Write output to the file descriptor\n"
878   "                                given by the last \"OUTPUT\" command.";
879 static int 
880 do_listkeys (assuan_context_t ctx, char *line, int mode)
881 {
882   ctrl_t ctrl = assuan_get_pointer (ctx);
883   estream_t fp;
884   char *p;
885   strlist_t list, sl;
886   unsigned int listmode;
887   gpg_error_t err;
888
889   /* Break the line down into an strlist. */
890   list = NULL;
891   for (p=line; *p; line = p)
892     {
893       while (*p && *p != ' ')
894         p++;
895       if (*p)
896         *p++ = 0;
897       if (*line)
898         {
899           sl = xtrymalloc (sizeof *sl + strlen (line));
900           if (!sl)
901             {
902               free_strlist (list);
903               return out_of_core ();
904             }
905           sl->flags = 0;
906           strcpy_escaped_plus (sl->d, line);
907           sl->next = list;
908           list = sl;
909         }
910     }
911
912   if (ctrl->server_local->list_to_output)
913     {
914       int outfd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
915
916       if ( outfd == -1 )
917         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
918       fp = es_fdopen_nc (outfd, "w");
919       if (!fp)
920         return set_error (gpg_err_code_from_syserror (), "es_fdopen() failed");
921     }
922   else
923     {
924       fp = es_fopencookie (ctx, "w", data_line_cookie_functions);
925       if (!fp)
926         return set_error (GPG_ERR_ASS_GENERAL, 
927                           "error setting up a data stream");
928     }
929   
930   ctrl->with_colons = 1;
931   listmode = mode; 
932   if (ctrl->server_local->list_internal)
933     listmode |= (1<<6);
934   if (ctrl->server_local->list_external)
935     listmode |= (1<<7);
936   err = gpgsm_list_keys (assuan_get_pointer (ctx), list, fp, listmode);
937   free_strlist (list);
938   es_fclose (fp);
939   if (ctrl->server_local->list_to_output)
940     assuan_close_output_fd (ctx);
941   return err;
942 }
943
944 static gpg_error_t
945 cmd_listkeys (assuan_context_t ctx, char *line)
946 {
947   return do_listkeys (ctx, line, 3);
948 }
949
950 static gpg_error_t
951 cmd_dumpkeys (assuan_context_t ctx, char *line)
952 {
953   return do_listkeys (ctx, line, 259);
954 }
955
956 static gpg_error_t
957 cmd_listsecretkeys (assuan_context_t ctx, char *line)
958 {
959   return do_listkeys (ctx, line, 2);
960 }
961
962 static gpg_error_t
963 cmd_dumpsecretkeys (assuan_context_t ctx, char *line)
964 {
965   return do_listkeys (ctx, line, 258);
966 }
967
968
969 \f
970 static const char hlp_genkey[] =
971   "GENKEY\n"
972   "\n"
973   "Read the parameters in native format from the input fd and write a\n"
974   "certificate request to the output.";
975 static gpg_error_t
976 cmd_genkey (assuan_context_t ctx, char *line)
977 {
978   ctrl_t ctrl = assuan_get_pointer (ctx);
979   int inp_fd, out_fd;
980   estream_t in_stream, out_stream;
981   int rc;
982
983   (void)line;
984
985   inp_fd = translate_sys2libc_fd (assuan_get_input_fd (ctx), 0);
986   if (inp_fd == -1)
987     return set_error (GPG_ERR_ASS_NO_INPUT, NULL);
988   out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
989   if (out_fd == -1)
990     return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
991
992   in_stream = es_fdopen_nc (inp_fd, "r");
993   if (!in_stream)
994     return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen failed");
995
996   out_stream = es_fdopen_nc (out_fd, "w");
997   if (!out_stream)
998     {
999       es_fclose (in_stream);
1000       return set_error (gpg_err_code_from_syserror (), "fdopen() failed");
1001     }
1002   rc = gpgsm_genkey (ctrl, in_stream, out_stream);
1003   es_fclose (out_stream);
1004   es_fclose (in_stream);
1005
1006   /* close and reset the fds */
1007   assuan_close_input_fd (ctx);
1008   assuan_close_output_fd (ctx);
1009
1010   return rc;
1011 }
1012
1013
1014 \f
1015 static const char hlp_getauditlog[] =
1016   "GETAUDITLOG [--data] [--html]\n"
1017   "\n"
1018   "If --data is used, the output is send using D-lines and not to the\n"
1019   "file descriptor given by an OUTPUT command.\n"
1020   "\n"
1021   "If --html is used the output is formated as an XHTML block. This is\n"
1022   "designed to be incorporated into a HTML document.";
1023 static gpg_error_t
1024 cmd_getauditlog (assuan_context_t ctx, char *line)
1025 {
1026   ctrl_t ctrl = assuan_get_pointer (ctx);
1027   int  out_fd;
1028   estream_t out_stream;
1029   int opt_data, opt_html;
1030   int rc;
1031
1032   opt_data = has_option (line, "--data"); 
1033   opt_html = has_option (line, "--html"); 
1034   line = skip_options (line);
1035
1036   if (!ctrl->audit)
1037     return gpg_error (GPG_ERR_NO_DATA);
1038
1039   if (opt_data)
1040     {
1041       out_stream = es_fopencookie (ctx, "w", data_line_cookie_functions);
1042       if (!out_stream)
1043         return set_error (GPG_ERR_ASS_GENERAL, 
1044                           "error setting up a data stream");
1045     }
1046   else
1047     {
1048       out_fd = translate_sys2libc_fd (assuan_get_output_fd (ctx), 1);
1049       if (out_fd == -1)
1050         return set_error (GPG_ERR_ASS_NO_OUTPUT, NULL);
1051       
1052       out_stream = es_fdopen_nc (out_fd, "w");
1053       if (!out_stream)
1054         {
1055           return set_error (GPG_ERR_ASS_GENERAL, "es_fdopen() failed");
1056         }
1057     }
1058
1059   audit_print_result (ctrl->audit, out_stream, opt_html);
1060   rc = 0;
1061
1062   es_fclose (out_stream);
1063
1064   /* Close and reset the fd. */
1065   if (!opt_data)
1066     assuan_close_output_fd (ctx);
1067   return rc;
1068 }
1069
1070 static const char hlp_getinfo[] = 
1071   "GETINFO <what>\n"
1072   "\n"
1073   "Multipurpose function to return a variety of information.\n"
1074   "Supported values for WHAT are:\n"
1075   "\n"
1076   "  version     - Return the version of the program.\n"
1077   "  pid         - Return the process id of the server.\n"
1078   "  agent-check - Return success if the agent is running.\n"
1079   "  cmd_has_option CMD OPT\n"
1080   "              - Returns OK if the command CMD implements the option OPT.";
1081 static gpg_error_t
1082 cmd_getinfo (assuan_context_t ctx, char *line)
1083 {
1084   int rc = 0;
1085
1086   if (!strcmp (line, "version"))
1087     {
1088       const char *s = VERSION;
1089       rc = assuan_send_data (ctx, s, strlen (s));
1090     }
1091   else if (!strcmp (line, "pid"))
1092     {
1093       char numbuf[50];
1094
1095       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
1096       rc = assuan_send_data (ctx, numbuf, strlen (numbuf));
1097     }
1098   else if (!strcmp (line, "agent-check"))
1099     {
1100       ctrl_t ctrl = assuan_get_pointer (ctx);
1101       rc = gpgsm_agent_send_nop (ctrl);
1102     }
1103   else if (!strncmp (line, "cmd_has_option", 14)
1104            && (line[14] == ' ' || line[14] == '\t' || !line[14]))
1105     {
1106       char *cmd, *cmdopt;
1107       line += 14;
1108       while (*line == ' ' || *line == '\t')
1109         line++;
1110       if (!*line)
1111         rc = gpg_error (GPG_ERR_MISSING_VALUE);
1112       else
1113         {
1114           cmd = line;
1115           while (*line && (*line != ' ' && *line != '\t'))
1116             line++;
1117           if (!*line)
1118             rc = gpg_error (GPG_ERR_MISSING_VALUE);
1119           else
1120             {
1121               *line++ = 0;
1122               while (*line == ' ' || *line == '\t')
1123                 line++;
1124               if (!*line)
1125                 rc = gpg_error (GPG_ERR_MISSING_VALUE);
1126               else
1127                 {
1128                   cmdopt = line;
1129                   if (!command_has_option (cmd, cmdopt))
1130                     rc = gpg_error (GPG_ERR_GENERAL);
1131                 }
1132             }
1133         }
1134     }
1135   else
1136     rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
1137
1138   return rc;
1139 }
1140
1141
1142 static const char hlp_passwd[] =
1143   "PASSWD <userID>\n"
1144   "\n"
1145   "Change the passphrase of the secret key for USERID.";
1146 static gpg_error_t
1147 cmd_passwd (assuan_context_t ctx, char *line)
1148 {
1149   ctrl_t ctrl = assuan_get_pointer (ctx);
1150   gpg_error_t err;
1151   ksba_cert_t cert = NULL;
1152   char *grip = NULL;
1153
1154   line = skip_options (line);
1155
1156   err = gpgsm_find_cert (line, NULL, &cert);
1157   if (err)
1158     ;
1159   else if (!(grip = gpgsm_get_keygrip_hexstring (cert)))
1160     err = gpg_error (GPG_ERR_INTERNAL);
1161   else 
1162     {
1163       char *desc = gpgsm_format_keydesc (cert);
1164       err = gpgsm_agent_passwd (ctrl, grip, desc);
1165       xfree (desc);
1166     }
1167
1168   xfree (grip);
1169   ksba_cert_release (cert);
1170
1171   return err;
1172 }
1173
1174
1175 \f
1176 /* Return true if the command CMD implements the option OPT.  */
1177 static int
1178 command_has_option (const char *cmd, const char *cmdopt)
1179 {
1180   if (!strcmp (cmd, "IMPORT"))
1181     {
1182       if (!strcmp (cmdopt, "re-import"))
1183         return 1;
1184     }
1185       
1186   return 0;
1187 }
1188
1189
1190 /* Tell the assuan library about our commands */
1191 static int
1192 register_commands (assuan_context_t ctx)
1193 {
1194   static struct {
1195     const char *name;
1196     assuan_handler_t handler;
1197     const char * const help;
1198   } table[] = {
1199     { "RECIPIENT",     cmd_recipient, hlp_recipient },
1200     { "SIGNER",        cmd_signer,    hlp_signer },
1201     { "ENCRYPT",       cmd_encrypt,   hlp_encrypt },
1202     { "DECRYPT",       cmd_decrypt,   hlp_decrypt },
1203     { "VERIFY",        cmd_verify,    hlp_verify },
1204     { "SIGN",          cmd_sign,      hlp_sign },
1205     { "IMPORT",        cmd_import,    hlp_import },
1206     { "EXPORT",        cmd_export,    hlp_export },
1207     { "INPUT",         NULL,          hlp_input }, 
1208     { "OUTPUT",        NULL,          hlp_output }, 
1209     { "MESSAGE",       cmd_message,   hlp_message },
1210     { "LISTKEYS",      cmd_listkeys,  hlp_listkeys },
1211     { "DUMPKEYS",      cmd_dumpkeys,  hlp_listkeys },
1212     { "LISTSECRETKEYS",cmd_listsecretkeys, hlp_listkeys },
1213     { "DUMPSECRETKEYS",cmd_dumpsecretkeys, hlp_listkeys },
1214     { "GENKEY",        cmd_genkey,    hlp_genkey },
1215     { "DELKEYS",       cmd_delkeys,   hlp_delkeys },
1216     { "GETAUDITLOG",   cmd_getauditlog,    hlp_getauditlog },
1217     { "GETINFO",       cmd_getinfo,   hlp_getinfo },
1218     { "PASSWD",        cmd_passwd,    hlp_passwd },
1219     { NULL }
1220   };
1221   int i, rc;
1222
1223   for (i=0; table[i].name; i++)
1224     {
1225       rc = assuan_register_command (ctx, table[i].name, table[i].handler,
1226                                     table[i].help);
1227       if (rc)
1228         return rc;
1229     } 
1230   return 0;
1231 }
1232
1233 /* Startup the server. DEFAULT_RECPLIST is the list of recipients as
1234    set from the command line or config file.  We only require those
1235    marked as encrypt-to. */
1236 void
1237 gpgsm_server (certlist_t default_recplist)
1238 {
1239   int rc;
1240   assuan_fd_t filedes[2];
1241   assuan_context_t ctx;
1242   struct server_control_s ctrl;
1243   static const char hello[] = ("GNU Privacy Guard's S/M server "
1244                                VERSION " ready");
1245
1246   memset (&ctrl, 0, sizeof ctrl);
1247   gpgsm_init_default_ctrl (&ctrl);
1248
1249   /* We use a pipe based server so that we can work from scripts.
1250      assuan_init_pipe_server will automagically detect when we are
1251      called with a socketpair and ignore FIELDES in this case. */
1252   filedes[0] = assuan_fdopen (0);
1253   filedes[1] = assuan_fdopen (1);
1254   rc = assuan_new (&ctx);
1255   if (rc)
1256     {
1257       log_error ("failed to allocate assuan context: %s\n",
1258                  gpg_strerror (rc));
1259       gpgsm_exit (2);
1260     }
1261
1262   rc = assuan_init_pipe_server (ctx, filedes);
1263   if (rc)
1264     {
1265       log_error ("failed to initialize the server: %s\n",
1266                  gpg_strerror (rc));
1267       gpgsm_exit (2);
1268     }
1269   rc = register_commands (ctx);
1270   if (rc)
1271     {
1272       log_error ("failed to the register commands with Assuan: %s\n",
1273                  gpg_strerror(rc));
1274       gpgsm_exit (2);
1275     }
1276   if (opt.verbose || opt.debug)
1277     {
1278       char *tmp = NULL;
1279       const char *s1 = getenv ("GPG_AGENT_INFO");
1280       const char *s2 = getenv ("DIRMNGR_INFO");
1281
1282       if (asprintf (&tmp,
1283                     "Home: %s\n"
1284                     "Config: %s\n"
1285                     "AgentInfo: %s\n"
1286                     "DirmngrInfo: %s\n"
1287                     "%s",
1288                     opt.homedir,
1289                     opt.config_filename,
1290                     s1?s1:"[not set]",
1291                     s2?s2:"[not set]",
1292                     hello) > 0)
1293         {
1294           assuan_set_hello_line (ctx, tmp);
1295           free (tmp);
1296         }
1297     }
1298   else
1299     assuan_set_hello_line (ctx, hello);
1300
1301   assuan_register_reset_notify (ctx, reset_notify);
1302   assuan_register_input_notify (ctx, input_notify);
1303   assuan_register_output_notify (ctx, output_notify);
1304   assuan_register_option_handler (ctx, option_handler);
1305
1306   assuan_set_pointer (ctx, &ctrl);
1307   ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
1308   ctrl.server_local->assuan_ctx = ctx;
1309   ctrl.server_local->message_fd = -1;
1310   ctrl.server_local->list_internal = 1;
1311   ctrl.server_local->list_external = 0;
1312   ctrl.server_local->default_recplist = default_recplist;
1313
1314   if (DBG_ASSUAN)
1315     assuan_set_log_stream (ctx, log_get_stream ());
1316
1317   for (;;)
1318     {
1319       rc = assuan_accept (ctx);
1320       if (rc == -1)
1321         {
1322           break;
1323         }
1324       else if (rc)
1325         {
1326           log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
1327           break;
1328         }
1329       
1330       rc = assuan_process (ctx);
1331       if (rc)
1332         {
1333           log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
1334           continue;
1335         }
1336     }
1337
1338   gpgsm_release_certlist (ctrl.server_local->recplist);
1339   ctrl.server_local->recplist = NULL;
1340   gpgsm_release_certlist (ctrl.server_local->signerlist);
1341   ctrl.server_local->signerlist = NULL;
1342   xfree (ctrl.server_local);
1343
1344   audit_release (ctrl.audit);
1345   ctrl.audit = NULL;
1346
1347   assuan_release (ctx);
1348 }
1349
1350
1351
1352 gpg_error_t
1353 gpgsm_status2 (ctrl_t ctrl, int no, ...)
1354 {
1355   gpg_error_t err = 0;
1356   va_list arg_ptr;
1357   const char *text;
1358
1359   va_start (arg_ptr, no);
1360
1361   if (ctrl->no_server && ctrl->status_fd == -1)
1362     ; /* No status wanted. */
1363   else if (ctrl->no_server)
1364     {
1365       if (!statusfp)
1366         {
1367           if (ctrl->status_fd == 1)
1368             statusfp = stdout;
1369           else if (ctrl->status_fd == 2)
1370             statusfp = stderr;
1371           else
1372             statusfp = fdopen (ctrl->status_fd, "w");
1373       
1374           if (!statusfp)
1375             {
1376               log_fatal ("can't open fd %d for status output: %s\n",
1377                          ctrl->status_fd, strerror(errno));
1378             }
1379         }
1380       
1381       fputs ("[GNUPG:] ", statusfp);
1382       fputs (get_status_string (no), statusfp);
1383     
1384       while ( (text = va_arg (arg_ptr, const char*) ))
1385         {
1386           putc ( ' ', statusfp );
1387           for (; *text; text++) 
1388             {
1389               if (*text == '\n')
1390                 fputs ( "\\n", statusfp );
1391               else if (*text == '\r')
1392                 fputs ( "\\r", statusfp );
1393               else 
1394                 putc ( *(const byte *)text,  statusfp );
1395             }
1396         }
1397       putc ('\n', statusfp);
1398       fflush (statusfp);
1399     }
1400   else 
1401     {
1402       assuan_context_t ctx = ctrl->server_local->assuan_ctx;
1403       char buf[950], *p;
1404       size_t n;
1405
1406       p = buf; 
1407       n = 0;
1408       while ( (text = va_arg (arg_ptr, const char *)) )
1409         {
1410           if (n)
1411             {
1412               *p++ = ' ';
1413               n++;
1414             }
1415           for ( ; *text && n < DIM (buf)-2; n++)
1416             *p++ = *text++;
1417         }
1418       *p = 0;
1419       err = assuan_write_status (ctx, get_status_string (no), buf);
1420     }
1421
1422   va_end (arg_ptr);
1423   return err;
1424 }
1425
1426 gpg_error_t
1427 gpgsm_status (ctrl_t ctrl, int no, const char *text)
1428 {
1429   return gpgsm_status2 (ctrl, no, text, NULL);
1430 }
1431
1432 gpg_error_t
1433 gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text,
1434                             gpg_err_code_t ec)
1435 {
1436   char buf[30];
1437
1438   sprintf (buf, "%u", (unsigned int)ec);
1439   if (text)
1440     return gpgsm_status2 (ctrl, no, text, buf, NULL);
1441   else
1442     return gpgsm_status2 (ctrl, no, buf, NULL);
1443 }
1444
1445
1446 /* Helper to notify the client about Pinentry events.  Because that
1447    might disturb some older clients, this is only done when enabled
1448    via an option.  Returns an gpg error code. */
1449 gpg_error_t
1450 gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
1451 {
1452   if (!ctrl || !ctrl->server_local 
1453       || !ctrl->server_local->allow_pinentry_notify)
1454     return 0;
1455   return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
1456 }
1457
1458
1459