1 /* server.c - The G13 Assuan server
2 * Copyright (C) 2009 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
36 #include "../common/server-help.h"
37 #include "../common/call-gpg.h"
40 /* The filepointer for status message used in non-server mode */
41 static FILE *statusfp;
43 /* Local data for this server module. A pointer to this is stored in
44 the CTRL object of each connection. */
47 /* The Assuan contect we are working on. */
48 assuan_context_t assuan_ctx;
50 char *containername; /* Malloced active containername. */
56 /* Local prototypes. */
57 static int command_has_option (const char *cmd, const char *cmdopt);
66 /* Set an error and a description. */
67 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
70 /* Helper to print a message while leaving a command. */
72 leave_cmd (assuan_context_t ctx, gpg_error_t err)
76 const char *name = assuan_get_command_name (ctx);
79 if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
80 log_error ("command '%s' failed: %s\n", name,
83 log_error ("command '%s' failed: %s <%s>\n", name,
84 gpg_strerror (err), gpg_strsource (err));
92 /* The handler for Assuan OPTION commands. */
94 option_handler (assuan_context_t ctx, const char *key, const char *value)
96 ctrl_t ctrl = assuan_get_pointer (ctx);
101 if (!strcmp (key, "putenv"))
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
109 err = session_env_putenv (opt.session_env, value);
111 else if (!strcmp (key, "display"))
113 err = session_env_setenv (opt.session_env, "DISPLAY", value);
115 else if (!strcmp (key, "ttyname"))
117 err = session_env_setenv (opt.session_env, "GPG_TTY", value);
119 else if (!strcmp (key, "ttytype"))
121 err = session_env_setenv (opt.session_env, "TERM", value);
123 else if (!strcmp (key, "lc-ctype"))
125 xfree (opt.lc_ctype);
126 opt.lc_ctype = xtrystrdup (value);
128 err = gpg_error_from_syserror ();
130 else if (!strcmp (key, "lc-messages"))
132 xfree (opt.lc_messages);
133 opt.lc_messages = xtrystrdup (value);
134 if (!opt.lc_messages)
135 err = gpg_error_from_syserror ();
137 else if (!strcmp (key, "xauthority"))
139 err = session_env_setenv (opt.session_env, "XAUTHORITY", value);
141 else if (!strcmp (key, "pinentry-user-data"))
143 err = session_env_setenv (opt.session_env, "PINENTRY_USER_DATA", value);
145 else if (!strcmp (key, "allow-pinentry-notify"))
147 ; /* We always allow it. */
150 err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
156 /* The handler for an Assuan RESET command. */
158 reset_notify (assuan_context_t ctx, char *line)
160 ctrl_t ctrl = assuan_get_pointer (ctx);
164 xfree (ctrl->server_local->containername);
165 ctrl->server_local->containername = NULL;
167 FREE_STRLIST (ctrl->recipients);
169 assuan_close_input_fd (ctx);
170 assuan_close_output_fd (ctx);
175 static const char hlp_open[] =
176 "OPEN [<options>] <filename>\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.";
185 cmd_open (assuan_context_t ctx, char *line)
187 ctrl_t ctrl = assuan_get_pointer (ctx);
192 /* In any case reset the active container. */
193 xfree (ctrl->server_local->containername);
194 ctrl->server_local->containername = NULL;
196 /* Parse the line. */
197 line = skip_options (line);
198 for (p=line; *p && !spacep (p); p++)
203 if (*p || pend == line)
205 err = gpg_error (GPG_ERR_ASS_SYNTAX);
210 /* Unescape the line and check for embedded Nul bytes. */
211 len = percent_plus_unescape_inplace (line, 0);
213 if (!len || memchr (line, 0, len))
215 err = gpg_error (GPG_ERR_INV_NAME);
219 /* Do a basic check. */
220 err = g13_is_container (ctrl, line);
224 /* Store the filename. */
225 ctrl->server_local->containername = xtrystrdup (line);
226 if (!ctrl->server_local->containername)
227 err = gpg_error_from_syserror ();
231 return leave_cmd (ctx, err);
235 static const char hlp_mount[] =
236 "MOUNT [options] [<mountpoint>]\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.";
242 cmd_mount (assuan_context_t ctx, char *line)
244 ctrl_t ctrl = assuan_get_pointer (ctx);
249 line = skip_options (line);
250 for (p=line; *p && !spacep (p); p++)
257 err = gpg_error (GPG_ERR_ASS_SYNTAX);
262 /* Unescape the line and check for embedded Nul bytes. */
263 len = percent_plus_unescape_inplace (line, 0);
265 if (memchr (line, 0, len))
267 err = gpg_error (GPG_ERR_INV_NAME);
271 if (!ctrl->server_local->containername)
273 err = gpg_error (GPG_ERR_MISSING_ACTION);
277 /* Perform the mount. */
278 err = g13_mount_container (ctrl, ctrl->server_local->containername,
282 return leave_cmd (ctx, err);
286 static const char hlp_umount[] =
287 "UMOUNT [options] [<mountpoint>]\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.";
293 cmd_umount (assuan_context_t ctx, char *line)
295 ctrl_t ctrl = assuan_get_pointer (ctx);
300 line = skip_options (line);
301 for (p=line; *p && !spacep (p); p++)
308 err = gpg_error (GPG_ERR_ASS_SYNTAX);
313 /* Unescape the line and check for embedded Nul bytes. */
314 len = percent_plus_unescape_inplace (line, 0);
316 if (memchr (line, 0, len))
318 err = gpg_error (GPG_ERR_INV_NAME);
322 /* Perform the unmount. */
323 err = g13_umount_container (ctrl, ctrl->server_local->containername,
327 return leave_cmd (ctx, err);
331 static const char hlp_suspend[] =
334 "Suspend the currently set device.";
336 cmd_suspend (assuan_context_t ctx, char *line)
338 ctrl_t ctrl = assuan_get_pointer (ctx);
341 line = skip_options (line);
344 err = gpg_error (GPG_ERR_ASS_SYNTAX);
348 /* Perform the suspend operation. */
349 err = g13_suspend_container (ctrl, ctrl->server_local->containername);
352 return leave_cmd (ctx, err);
356 static const char hlp_resume[] =
359 "Resume the currently set device.";
361 cmd_resume (assuan_context_t ctx, char *line)
363 ctrl_t ctrl = assuan_get_pointer (ctx);
366 line = skip_options (line);
369 err = gpg_error (GPG_ERR_ASS_SYNTAX);
373 /* Perform the suspend operation. */
374 err = g13_resume_container (ctrl, ctrl->server_local->containername);
377 return leave_cmd (ctx, err);
381 static const char hlp_recipient[] =
382 "RECIPIENT <userID>\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.";
388 cmd_recipient (assuan_context_t ctx, char *line)
390 ctrl_t ctrl = assuan_get_pointer (ctx);
393 line = skip_options (line);
395 if (!add_to_strlist_try (&ctrl->recipients, line))
396 err = gpg_error_from_syserror ();
398 return leave_cmd (ctx, err);
402 static const char hlp_signer[] =
405 "Not yet implemented.";
407 cmd_signer (assuan_context_t ctx, char *line)
409 ctrl_t ctrl = assuan_get_pointer (ctx);
415 err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
416 return leave_cmd (ctx, err);
420 static const char hlp_create[] =
421 "CREATE [options] <filename>\n"
423 "Create a new container. On success the OPEN command is \n"
424 "implictly done for the new container.";
426 cmd_create (assuan_context_t ctx, char *line)
428 ctrl_t ctrl = assuan_get_pointer (ctx);
433 /* First we close the active container. */
434 xfree (ctrl->server_local->containername);
435 ctrl->server_local->containername = NULL;
437 /* Parse the line. */
438 line = skip_options (line);
439 for (p=line; *p && !spacep (p); p++)
444 if (*p || pend == line)
446 err = gpg_error (GPG_ERR_ASS_SYNTAX);
451 /* Unescape the line and check for embedded Nul bytes. */
452 len = percent_plus_unescape_inplace (line, 0);
454 if (!len || memchr (line, 0, len))
456 err = gpg_error (GPG_ERR_INV_NAME);
460 /* Create container. */
461 err = g13_create_container (ctrl, line);
465 FREE_STRLIST (ctrl->recipients);
467 /* Store the filename. */
468 ctrl->server_local->containername = xtrystrdup (line);
469 if (!ctrl->server_local->containername)
470 err = gpg_error_from_syserror ();
474 return leave_cmd (ctx, err);
478 static const char hlp_getinfo[] =
481 "Multipurpose function to return a variety of information.\n"
482 "Supported values for WHAT are:\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.";
489 cmd_getinfo (assuan_context_t ctx, char *line)
493 if (!strcmp (line, "version"))
495 const char *s = PACKAGE_VERSION;
496 err = assuan_send_data (ctx, s, strlen (s));
498 else if (!strcmp (line, "pid"))
502 snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
503 err = assuan_send_data (ctx, numbuf, strlen (numbuf));
505 else if (!strncmp (line, "cmd_has_option", 14)
506 && (line[14] == ' ' || line[14] == '\t' || !line[14]))
510 while (*line == ' ' || *line == '\t')
513 err = gpg_error (GPG_ERR_MISSING_VALUE);
517 while (*line && (*line != ' ' && *line != '\t'))
520 err = gpg_error (GPG_ERR_MISSING_VALUE);
524 while (*line == ' ' || *line == '\t')
527 err = gpg_error (GPG_ERR_MISSING_VALUE);
531 if (!command_has_option (cmd, cmdopt))
532 err = gpg_error (GPG_ERR_GENERAL);
538 err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
540 return leave_cmd (ctx, err);
545 /* Return true if the command CMD implements the option CMDOPT. */
547 command_has_option (const char *cmd, const char *cmdopt)
556 /* Tell the Assuan library about our commands. */
558 register_commands (assuan_context_t ctx)
562 assuan_handler_t handler;
563 const char * const help;
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 },
575 { "GETINFO", cmd_getinfo,hlp_getinfo },
581 for (i=0; table[i].name; i++)
583 err = assuan_register_command (ctx, table[i].name, table[i].handler,
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. */
596 g13_server (ctrl_t ctrl)
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");
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);
612 log_error ("failed to allocate an Assuan context: %s\n",
617 err = assuan_init_pipe_server (ctx, filedes);
620 log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
624 err = register_commands (ctx);
627 log_error ("failed to the register commands with Assuan: %s\n",
632 assuan_set_pointer (ctx, ctrl);
634 if (opt.verbose || opt.debug)
638 tmp = xtryasprintf ("Home: %s\n"
646 assuan_set_hello_line (ctx, tmp);
651 assuan_set_hello_line (ctx, hello);
653 assuan_register_reset_notify (ctx, reset_notify);
654 assuan_register_option_handler (ctx, option_handler);
656 ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
657 if (!ctrl->server_local)
659 err = gpg_error_from_syserror ();
662 ctrl->server_local->assuan_ctx = ctx;
664 while ( !(err = assuan_accept (ctx)) )
666 err = assuan_process (ctx);
668 log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
673 log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
676 reset_notify (ctx, NULL); /* Release all items hold by SERVER_LOCAL. */
677 if (ctrl->server_local)
679 xfree (ctrl->server_local);
680 ctrl->server_local = NULL;
683 assuan_release (ctx);
688 /* Send a status line with status ID NO. The arguments are a list of
689 strings terminated by a NULL argument. */
691 g13_status (ctrl_t ctrl, int no, ...)
697 va_start (arg_ptr, no);
699 if (ctrl->no_server && ctrl->status_fd == -1)
700 ; /* No status wanted. */
701 else if (ctrl->no_server)
705 if (ctrl->status_fd == 1)
707 else if (ctrl->status_fd == 2)
710 statusfp = fdopen (ctrl->status_fd, "w");
714 log_fatal ("can't open fd %d for status output: %s\n",
715 ctrl->status_fd, strerror(errno));
719 fputs ("[GNUPG:] ", statusfp);
720 fputs (get_status_string (no), statusfp);
722 while ( (text = va_arg (arg_ptr, const char*) ))
724 putc ( ' ', statusfp );
725 for (; *text; text++)
728 fputs ( "\\n", statusfp );
729 else if (*text == '\r')
730 fputs ( "\\r", statusfp );
732 putc ( *(const byte *)text, statusfp );
735 putc ('\n', statusfp);
740 assuan_context_t ctx = ctrl->server_local->assuan_ctx;
746 while ( (text = va_arg (arg_ptr, const char *)) )
753 for ( ; *text && n < DIM (buf)-2; n++)
757 err = assuan_write_status (ctx, get_status_string (no), buf);
765 /* Helper to notify the client about Pinentry events. Returns an gpg
768 g13_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line)
770 if (!ctrl || !ctrl->server_local)
772 return assuan_inquire (ctrl->server_local->assuan_ctx, line, NULL, NULL, 0);
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.
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
787 g13_keyblob_decrypt (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen,
788 void **r_keyblob, size_t *r_keybloblen)
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);