tools,build: Build WKS tools against libintl.
[gnupg.git] / g13 / server.c
1 /* server.c - The G13 Assuan server
2  * Copyright (C) 2009 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <https://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdarg.h>
25 #include <errno.h>
26 #include <assert.h>
27
28 #include "g13.h"
29 #include <assuan.h>
30 #include "i18n.h"
31 #include "keyblob.h"
32 #include "server.h"
33 #include "create.h"
34 #include "mount.h"
35 #include "suspend.h"
36 #include "../common/server-help.h"
37 #include "../common/call-gpg.h"
38
39
40 /* The filepointer for status message used in non-server mode */
41 static FILE *statusfp;
42
43 /* Local data for this server module.  A pointer to this is stored in
44    the CTRL object of each connection.  */
45 struct server_local_s
46 {
47   /* The Assuan contect we are working on.  */
48   assuan_context_t assuan_ctx;
49
50   char *containername;  /* Malloced active containername.  */
51 };
52
53
54
55 \f
56 /* Local prototypes.  */
57 static int command_has_option (const char *cmd, const char *cmdopt);
58
59
60
61 \f
62 /*
63    Helper functions.
64  */
65
66 /* Set an error and a description.  */
67 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
68
69
70 /* Helper to print a message while leaving a command.  */
71 static gpg_error_t
72 leave_cmd (assuan_context_t ctx, gpg_error_t err)
73 {
74   if (err)
75     {
76       const char *name = assuan_get_command_name (ctx);
77       if (!name)
78         name = "?";
79       if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
80         log_error ("command '%s' failed: %s\n", name,
81                    gpg_strerror (err));
82       else
83         log_error ("command '%s' failed: %s <%s>\n", name,
84                    gpg_strerror (err), gpg_strsource (err));
85     }
86   return err;
87 }
88
89
90
91 \f
92 /* The handler for Assuan OPTION commands.  */
93 static gpg_error_t
94 option_handler (assuan_context_t ctx, const char *key, const char *value)
95 {
96   ctrl_t ctrl = assuan_get_pointer (ctx);
97   gpg_error_t err = 0;
98
99   (void)ctrl;
100
101   if (!strcmp (key, "putenv"))
102     {
103       /* Change the session's environment to be used for the
104          Pinentry.  Valid values are:
105           <NAME>            Delete envvar NAME
106           <KEY>=            Set envvar NAME to the empty string
107           <KEY>=<VALUE>     Set envvar NAME to VALUE
108       */
109       err = session_env_putenv (opt.session_env, value);
110     }
111   else if (!strcmp (key, "display"))
112     {
113       err = session_env_setenv (opt.session_env, "DISPLAY", value);
114     }
115   else if (!strcmp (key, "ttyname"))
116     {
117       err = session_env_setenv (opt.session_env, "GPG_TTY", value);
118     }
119   else if (!strcmp (key, "ttytype"))
120     {
121       err = session_env_setenv (opt.session_env, "TERM", value);
122     }
123   else if (!strcmp (key, "lc-ctype"))
124     {
125       xfree (opt.lc_ctype);
126       opt.lc_ctype = xtrystrdup (value);
127       if (!opt.lc_ctype)
128         err = gpg_error_from_syserror ();
129     }
130   else if (!strcmp (key, "lc-messages"))
131     {
132       xfree (opt.lc_messages);
133       opt.lc_messages = xtrystrdup (value);
134       if (!opt.lc_messages)
135         err = gpg_error_from_syserror ();
136     }
137   else if (!strcmp (key, "xauthority"))
138     {
139       err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
140     }
141   else if (!strcmp (key, "pinentry-user-data"))
142     {
143       err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
144     }
145   else if (!strcmp (key, "allow-pinentry-notify"))
146     {
147       ; /* We always allow it.  */
148     }
149   else
150     err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
151
152   return err;
153 }
154
155
156 /* The handler for an Assuan RESET command.  */
157 static gpg_error_t
158 reset_notify (assuan_context_t ctx, char *line)
159 {
160   ctrl_t ctrl = assuan_get_pointer (ctx);
161
162   (void)line;
163
164   xfree (ctrl->server_local->containername);
165   ctrl->server_local->containername = NULL;
166
167   FREE_STRLIST (ctrl->recipients);
168
169   assuan_close_input_fd (ctx);
170   assuan_close_output_fd (ctx);
171   return 0;
172 }
173
174
175 static const char hlp_open[] =
176   "OPEN [<options>] <filename>\n"
177   "\n"
178   "Open the container FILENAME.  FILENAME must be percent-plus\n"
179   "escaped.  A quick check to see whether this is a suitable G13\n"
180   "container file is done.  However no cryptographic check or any\n"
181   "other check is done.  This command is used to define the target for\n"
182   "further commands.  The filename is reset with the RESET command,\n"
183   "another OPEN or the CREATE command.";
184 static gpg_error_t
185 cmd_open (assuan_context_t ctx, char *line)
186 {
187   ctrl_t ctrl = assuan_get_pointer (ctx);
188   gpg_error_t err = 0;
189   char *p, *pend;
190   size_t len;
191
192   /* In any case reset the active container.  */
193   xfree (ctrl->server_local->containername);
194   ctrl->server_local->containername = NULL;
195
196   /* Parse the line.  */
197   line = skip_options (line);
198   for (p=line; *p && !spacep (p); p++)
199     ;
200   pend = p;
201   while (spacep(p))
202     p++;
203   if (*p || pend == line)
204     {
205       err = gpg_error (GPG_ERR_ASS_SYNTAX);
206       goto leave;
207     }
208   *pend = 0;
209
210   /* Unescape the line and check for embedded Nul bytes.  */
211   len = percent_plus_unescape_inplace (line, 0);
212   line[len] = 0;
213   if (!len || memchr (line, 0, len))
214     {
215       err = gpg_error (GPG_ERR_INV_NAME);
216       goto leave;
217     }
218
219   /* Do a basic check.  */
220   err = g13_is_container (ctrl, line);
221   if (err)
222     goto leave;
223
224   /* Store the filename.  */
225   ctrl->server_local->containername = xtrystrdup (line);
226   if (!ctrl->server_local->containername)
227     err = gpg_error_from_syserror ();
228
229
230  leave:
231   return leave_cmd (ctx, err);
232 }
233
234
235 static const char hlp_mount[] =
236   "MOUNT [options] [<mountpoint>]\n"
237   "\n"
238   "Mount the currently open file onto MOUNTPOINT.  If MOUNTPOINT is not\n"
239   "given the system picks an unused mountpoint.  MOUNTPOINT must\n"
240   "be percent-plus escaped to allow for arbitrary names.";
241 static gpg_error_t
242 cmd_mount (assuan_context_t ctx, char *line)
243 {
244   ctrl_t ctrl = assuan_get_pointer (ctx);
245   gpg_error_t err = 0;
246   char *p, *pend;
247   size_t len;
248
249   line = skip_options (line);
250   for (p=line; *p && !spacep (p); p++)
251     ;
252   pend = p;
253   while (spacep(p))
254     p++;
255   if (*p)
256     {
257       err = gpg_error (GPG_ERR_ASS_SYNTAX);
258       goto leave;
259     }
260   *pend = 0;
261
262   /* Unescape the line and check for embedded Nul bytes.  */
263   len = percent_plus_unescape_inplace (line, 0);
264   line[len] = 0;
265   if (memchr (line, 0, len))
266     {
267       err = gpg_error (GPG_ERR_INV_NAME);
268       goto leave;
269     }
270
271   if (!ctrl->server_local->containername)
272     {
273       err = gpg_error (GPG_ERR_MISSING_ACTION);
274       goto leave;
275     }
276
277   /* Perform the mount.  */
278   err = g13_mount_container (ctrl, ctrl->server_local->containername,
279                              *line? line : NULL);
280
281  leave:
282   return leave_cmd (ctx, err);
283 }
284
285
286 static const char hlp_umount[] =
287   "UMOUNT [options] [<mountpoint>]\n"
288   "\n"
289   "Unmount the currently open file or the one opened at MOUNTPOINT.\n"
290   "MOUNTPOINT must be percent-plus escaped.  On success the mountpoint\n"
291   "is returned via a \"MOUNTPOINT\" status line.";
292 static gpg_error_t
293 cmd_umount (assuan_context_t ctx, char *line)
294 {
295   ctrl_t ctrl = assuan_get_pointer (ctx);
296   gpg_error_t err = 0;
297   char *p, *pend;
298   size_t len;
299
300   line = skip_options (line);
301   for (p=line; *p && !spacep (p); p++)
302     ;
303   pend = p;
304   while (spacep(p))
305     p++;
306   if (*p)
307     {
308       err = gpg_error (GPG_ERR_ASS_SYNTAX);
309       goto leave;
310     }
311   *pend = 0;
312
313   /* Unescape the line and check for embedded Nul bytes.  */
314   len = percent_plus_unescape_inplace (line, 0);
315   line[len] = 0;
316   if (memchr (line, 0, len))
317     {
318       err = gpg_error (GPG_ERR_INV_NAME);
319       goto leave;
320     }
321
322   /* Perform the unmount.  */
323   err = g13_umount_container (ctrl, ctrl->server_local->containername,
324                               *line? line : NULL);
325
326  leave:
327   return leave_cmd (ctx, err);
328 }
329
330
331 static const char hlp_suspend[] =
332   "SUSPEND\n"
333   "\n"
334   "Suspend the currently set device.";
335 static gpg_error_t
336 cmd_suspend (assuan_context_t ctx, char *line)
337 {
338   ctrl_t ctrl = assuan_get_pointer (ctx);
339   gpg_error_t err;
340
341   line = skip_options (line);
342   if (*line)
343     {
344       err = gpg_error (GPG_ERR_ASS_SYNTAX);
345       goto leave;
346     }
347
348   /* Perform the suspend operation.  */
349   err = g13_suspend_container (ctrl, ctrl->server_local->containername);
350
351  leave:
352   return leave_cmd (ctx, err);
353 }
354
355
356 static const char hlp_resume[] =
357   "RESUME\n"
358   "\n"
359   "Resume the currently set device.";
360 static gpg_error_t
361 cmd_resume (assuan_context_t ctx, char *line)
362 {
363   ctrl_t ctrl = assuan_get_pointer (ctx);
364   gpg_error_t err;
365
366   line = skip_options (line);
367   if (*line)
368     {
369       err = gpg_error (GPG_ERR_ASS_SYNTAX);
370       goto leave;
371     }
372
373   /* Perform the suspend operation.  */
374   err = g13_resume_container (ctrl, ctrl->server_local->containername);
375
376  leave:
377   return leave_cmd (ctx, err);
378 }
379
380
381 static const char hlp_recipient[] =
382   "RECIPIENT <userID>\n"
383   "\n"
384   "Add USERID to the list of recipients to be used for the next CREATE\n"
385   "command.  All recipient commands are cumulative until a RESET or an\n"
386   "successful create command.";
387 static gpg_error_t
388 cmd_recipient (assuan_context_t ctx, char *line)
389 {
390   ctrl_t ctrl = assuan_get_pointer (ctx);
391   gpg_error_t err = 0;
392
393   line = skip_options (line);
394
395   if (!add_to_strlist_try (&ctrl->recipients, line))
396     err = gpg_error_from_syserror ();
397
398   return leave_cmd (ctx, err);
399 }
400
401
402 static const char hlp_signer[] =
403   "SIGNER <userID>\n"
404   "\n"
405   "Not yet implemented.";
406 static gpg_error_t
407 cmd_signer (assuan_context_t ctx, char *line)
408 {
409   ctrl_t ctrl = assuan_get_pointer (ctx);
410   gpg_error_t err;
411
412   (void)ctrl;
413   (void)line;
414
415   err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
416   return leave_cmd (ctx, err);
417 }
418
419
420 static const char hlp_create[] =
421   "CREATE [options] <filename>\n"
422   "\n"
423   "Create a new container.  On success the OPEN command is \n"
424   "implictly done for the new container.";
425 static gpg_error_t
426 cmd_create (assuan_context_t ctx, char *line)
427 {
428   ctrl_t ctrl = assuan_get_pointer (ctx);
429   gpg_error_t err;
430   char *p, *pend;
431   size_t len;
432
433   /* First we close the active container.  */
434   xfree (ctrl->server_local->containername);
435   ctrl->server_local->containername = NULL;
436
437   /* Parse the line.  */
438   line = skip_options (line);
439   for (p=line; *p && !spacep (p); p++)
440     ;
441   pend = p;
442   while (spacep(p))
443     p++;
444   if (*p || pend == line)
445     {
446       err = gpg_error (GPG_ERR_ASS_SYNTAX);
447       goto leave;
448     }
449   *pend = 0;
450
451   /* Unescape the line and check for embedded Nul bytes.  */
452   len = percent_plus_unescape_inplace (line, 0);
453   line[len] = 0;
454   if (!len || memchr (line, 0, len))
455     {
456       err = gpg_error (GPG_ERR_INV_NAME);
457       goto leave;
458     }
459
460   /* Create container.  */
461   err = g13_create_container (ctrl, line);
462
463   if (!err)
464     {
465       FREE_STRLIST (ctrl->recipients);
466
467       /* Store the filename.  */
468       ctrl->server_local->containername = xtrystrdup (line);
469       if (!ctrl->server_local->containername)
470         err = gpg_error_from_syserror ();
471
472     }
473  leave:
474   return leave_cmd (ctx, err);
475 }
476
477
478 static const char hlp_getinfo[] =
479   "GETINFO <what>\n"
480   "\n"
481   "Multipurpose function to return a variety of information.\n"
482   "Supported values for WHAT are:\n"
483   "\n"
484   "  version     - Return the version of the program.\n"
485   "  pid         - Return the process id of the server.\n"
486   "  cmd_has_option CMD OPT\n"
487   "              - Return OK if the command CMD implements the option OPT.";
488 static gpg_error_t
489 cmd_getinfo (assuan_context_t ctx, char *line)
490 {
491   gpg_error_t err = 0;
492
493   if (!strcmp (line, "version"))
494     {
495       const char *s = PACKAGE_VERSION;
496       err = assuan_send_data (ctx, s, strlen (s));
497     }
498   else if (!strcmp (line, "pid"))
499     {
500       char numbuf[50];
501
502       snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
503       err = assuan_send_data (ctx, numbuf, strlen (numbuf));
504     }
505   else if (!strncmp (line, "cmd_has_option", 14)
506            && (line[14] == ' ' || line[14] == '\t' || !line[14]))
507     {
508       char *cmd, *cmdopt;
509       line += 14;
510       while (*line == ' ' || *line == '\t')
511         line++;
512       if (!*line)
513         err = gpg_error (GPG_ERR_MISSING_VALUE);
514       else
515         {
516           cmd = line;
517           while (*line && (*line != ' ' && *line != '\t'))
518             line++;
519           if (!*line)
520             err = gpg_error (GPG_ERR_MISSING_VALUE);
521           else
522             {
523               *line++ = 0;
524               while (*line == ' ' || *line == '\t')
525                 line++;
526               if (!*line)
527                 err = gpg_error (GPG_ERR_MISSING_VALUE);
528               else
529                 {
530                   cmdopt = line;
531                   if (!command_has_option (cmd, cmdopt))
532                     err = gpg_error (GPG_ERR_GENERAL);
533                 }
534             }
535         }
536     }
537   else
538     err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
539
540   return leave_cmd (ctx, err);
541 }
542
543
544 \f
545 /* Return true if the command CMD implements the option CMDOPT.  */
546 static int
547 command_has_option (const char *cmd, const char *cmdopt)
548 {
549   (void)cmd;
550   (void)cmdopt;
551
552   return 0;
553 }
554
555
556 /* Tell the Assuan library about our commands.  */
557 static int
558 register_commands (assuan_context_t ctx)
559 {
560   static struct {
561     const char *name;
562     assuan_handler_t handler;
563     const char * const help;
564   } table[] =  {
565     { "OPEN",          cmd_open,   hlp_open },
566     { "MOUNT",         cmd_mount,  hlp_mount},
567     { "UMOUNT",        cmd_umount, hlp_umount },
568     { "SUSPEND",       cmd_suspend, hlp_suspend },
569     { "RESUME",        cmd_resume,  hlp_resume },
570     { "RECIPIENT",     cmd_recipient, hlp_recipient },
571     { "SIGNER",        cmd_signer, hlp_signer },
572     { "CREATE",        cmd_create, hlp_create },
573     { "INPUT",         NULL },
574     { "OUTPUT",        NULL },
575     { "GETINFO",       cmd_getinfo,hlp_getinfo },
576     { NULL }
577   };
578   gpg_error_t err;
579   int i;
580
581   for (i=0; table[i].name; i++)
582     {
583       err = assuan_register_command (ctx, table[i].name, table[i].handler,
584                                      table[i].help);
585       if (err)
586         return err;
587     }
588   return 0;
589 }
590
591
592 /* Startup the server. DEFAULT_RECPLIST is the list of recipients as
593    set from the command line or config file.  We only require those
594    marked as encrypt-to. */
595 gpg_error_t
596 g13_server (ctrl_t ctrl)
597 {
598   gpg_error_t err;
599   assuan_fd_t filedes[2];
600   assuan_context_t ctx = NULL;
601   static const char hello[] = ("GNU Privacy Guard's G13 server "
602                                PACKAGE_VERSION " ready");
603
604   /* We use a pipe based server so that we can work from scripts.
605      assuan_init_pipe_server will automagically detect when we are
606      called with a socketpair and ignore FIELDES in this case. */
607   filedes[0] = assuan_fdopen (0);
608   filedes[1] = assuan_fdopen (1);
609   err = assuan_new (&ctx);
610   if (err)
611     {
612       log_error ("failed to allocate an Assuan context: %s\n",
613                  gpg_strerror (err));
614       goto leave;
615     }
616
617   err = assuan_init_pipe_server (ctx, filedes);
618   if (err)
619     {
620       log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
621       goto leave;
622     }
623
624   err = register_commands (ctx);
625   if (err)
626     {
627       log_error ("failed to the register commands with Assuan: %s\n",
628                  gpg_strerror (err));
629       goto leave;
630     }
631
632   assuan_set_pointer (ctx, ctrl);
633
634   if (opt.verbose || opt.debug)
635     {
636       char *tmp;
637
638       tmp = xtryasprintf ("Home: %s\n"
639                           "Config: %s\n"
640                           "%s",
641                           gnupg_homedir (),
642                           opt.config_filename,
643                           hello);
644       if (tmp)
645         {
646           assuan_set_hello_line (ctx, tmp);
647           xfree (tmp);
648         }
649     }
650   else
651     assuan_set_hello_line (ctx, hello);
652
653   assuan_register_reset_notify (ctx, reset_notify);
654   assuan_register_option_handler (ctx, option_handler);
655
656   ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
657   if (!ctrl->server_local)
658     {
659       err = gpg_error_from_syserror ();
660       goto leave;
661     }
662   ctrl->server_local->assuan_ctx = ctx;
663
664   while ( !(err = assuan_accept (ctx)) )
665     {
666       err = assuan_process (ctx);
667       if (err)
668         log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
669     }
670   if (err == -1)
671     err = 0;
672   else
673     log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
674
675  leave:
676   reset_notify (ctx, NULL);  /* Release all items hold by SERVER_LOCAL.  */
677   if (ctrl->server_local)
678     {
679       xfree (ctrl->server_local);
680       ctrl->server_local = NULL;
681     }
682
683   assuan_release (ctx);
684   return err;
685 }
686
687
688 /* Send a status line with status ID NO.  The arguments are a list of
689    strings terminated by a NULL argument.  */
690 gpg_error_t
691 g13_status (ctrl_t ctrl, int no, ...)
692 {
693   gpg_error_t err = 0;
694   va_list arg_ptr;
695   const char *text;
696
697   va_start (arg_ptr, no);
698
699   if (ctrl->no_server && ctrl->status_fd == -1)
700     ; /* No status wanted. */
701   else if (ctrl->no_server)
702     {
703       if (!statusfp)
704         {
705           if (ctrl->status_fd == 1)
706             statusfp = stdout;
707           else if (ctrl->status_fd == 2)
708             statusfp = stderr;
709           else
710             statusfp = fdopen (ctrl->status_fd, "w");
711
712           if (!statusfp)
713             {
714               log_fatal ("can't open fd %d for status output: %s\n",
715                          ctrl->status_fd, strerror(errno));
716             }
717         }
718
719       fputs ("[GNUPG:] ", statusfp);
720       fputs (get_status_string (no), statusfp);
721
722       while ( (text = va_arg (arg_ptr, const char*) ))
723         {
724           putc ( ' ', statusfp );
725           for (; *text; text++)
726             {
727               if (*text == '\n')
728                 fputs ( "\\n", statusfp );
729               else if (*text == '\r')
730                 fputs ( "\\r", statusfp );
731               else
732                 putc ( *(const byte *)text,  statusfp );
733             }
734         }
735       putc ('\n', statusfp);
736       fflush (statusfp);
737     }
738   else
739     {
740       assuan_context_t ctx = ctrl->server_local->assuan_ctx;
741       char buf[950], *p;
742       size_t n;
743
744       p = buf;
745       n = 0;
746       while ( (text = va_arg (arg_ptr, const char *)) )
747         {
748           if (n)
749             {
750               *p++ = ' ';
751               n++;
752             }
753           for ( ; *text && n < DIM (buf)-2; n++)
754             *p++ = *text++;
755         }
756       *p = 0;
757       err = assuan_write_status (ctx, get_status_string (no), buf);
758     }
759
760   va_end (arg_ptr);
761   return err;
762 }
763
764
765 /* Helper to notify the client about Pinentry events.  Returns an gpg
766    error code. */
767 gpg_error_t
768 g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
769 {
770   if (!ctrl || !ctrl->server_local)
771     return 0;
772   return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
773 }
774
775
776 /*
777  * Decrypt the keyblob (ENCKEYBLOB,ENCKEYBLOBLEN) and store the result
778  * at (R_KEYBLOB, R_KEYBLOBLEN).  Returns 0 on success or an error
779  * code.  On error R_KEYBLOB is set to NULL.
780  *
781  * This actually does not belong here but for that simple wrapper it
782  * does not make sense to add another source file.  Note that we do
783  * not want to have this in keyblob.c, because that code is also used
784  * by the syshelp.
785  */
786 gpg_error_t
787 g13_keyblob_decrypt (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen,
788                      void **r_keyblob, size_t *r_keybloblen)
789 {
790   gpg_error_t err;
791
792   /* FIXME:  For now we only implement OpenPGP.  */
793   err = gpg_decrypt_blob (ctrl, opt.gpg_program, opt.gpg_arguments,
794                           enckeyblob, enckeybloblen,
795                           r_keyblob, r_keybloblen);
796
797   return err;
798 }