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