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