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