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