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