1 /* sh-cmd.c - The Assuan server for g13-syshelp
2 * Copyright (C) 2015 Werner Koch
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/>.
28 #include "g13-syshelp.h"
30 #include "../common/i18n.h"
31 #include "../common/asshelp.h"
35 /* Local data for this server module. A pointer to this is stored in
36 the CTRL object of each connection. */
39 /* The Assuan context we are working on. */
40 assuan_context_t assuan_ctx;
42 /* The malloced name of the device. */
45 /* A stream open for read of the device set by the DEVICE command or
46 NULL if no DEVICE command has been used. */
53 /* Local prototypes. */
62 /* Set an error and a description. */
63 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
64 #define set_error_fail_cmd() set_error (GPG_ERR_NOT_INITIALIZED, \
65 "not called via userv or unknown user")
68 /* Skip over options. Blanks after the options are also removed. */
70 skip_options (const char *line)
74 while ( *line == '-' && line[1] == '-' )
76 while (*line && !spacep (line))
85 /* Check whether the option NAME appears in LINE. */
87 /* has_option (const char *line, const char *name) */
90 /* int n = strlen (name); */
92 /* s = strstr (line, name); */
93 /* if (s && s >= skip_options (line)) */
95 /* return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); */
99 /* Helper to print a message while leaving a command. */
101 leave_cmd (assuan_context_t ctx, gpg_error_t err)
105 const char *name = assuan_get_command_name (ctx);
108 if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
109 log_error ("command '%s' failed: %s\n", name,
112 log_error ("command '%s' failed: %s <%s>\n", name,
113 gpg_strerror (err), gpg_strsource (err));
121 /* The handler for Assuan OPTION commands. */
123 option_handler (assuan_context_t ctx, const char *key, const char *value)
125 ctrl_t ctrl = assuan_get_pointer (ctx);
132 if (ctrl->fail_all_cmds)
133 err = set_error_fail_cmd ();
135 err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
141 /* The handler for an Assuan RESET command. */
143 reset_notify (assuan_context_t ctx, char *line)
145 ctrl_t ctrl = assuan_get_pointer (ctx);
149 xfree (ctrl->server_local->devicename);
150 ctrl->server_local->devicename = NULL;
151 es_fclose (ctrl->server_local->devicefp);
152 ctrl->server_local->devicefp = NULL;
155 assuan_close_input_fd (ctx);
156 assuan_close_output_fd (ctx);
161 static const char hlp_finddevice[] =
162 "FINDDEVICE <name>\n"
164 "Find the device matching NAME. NAME be any identifier from\n"
165 "g13tab permissible for the user. The corresponding block\n"
166 "device is returned using a status line.";
168 cmd_finddevice (assuan_context_t ctx, char *line)
170 ctrl_t ctrl = assuan_get_pointer (ctx);
176 name = skip_options (line);
178 /* Are we allowed to use the given device? We check several names:
179 * 1. The full block device
181 * 3. The final part of the block device if NAME does not have a slash.
184 for (ti=ctrl->client.tab; ti; ti = ti->next)
185 if (!strcmp (name, ti->blockdev))
189 for (ti=ctrl->client.tab; ti; ti = ti->next)
190 if (ti->label && !strcmp (name, ti->label))
193 if (!ti && !strchr (name, '/'))
195 for (ti=ctrl->client.tab; ti; ti = ti->next)
197 s = strrchr (ti->blockdev, '/');
198 if (s && s[1] && !strcmp (name, s+1))
204 for (ti=ctrl->client.tab; ti; ti = ti->next)
205 if (ti->mountpoint && !strcmp (name, ti->mountpoint))
211 err = set_error (GPG_ERR_NOT_FOUND, "device not configured for user");
215 /* Check whether we have permissions to open the device. */
217 estream_t fp = es_fopen (ti->blockdev, "rb");
220 err = gpg_error_from_syserror ();
221 log_error ("error opening '%s': %s\n",
222 ti->blockdev, gpg_strerror (err));
228 err = g13_status (ctrl, STATUS_BLOCKDEV, ti->blockdev, NULL);
233 return leave_cmd (ctx, err);
237 static const char hlp_device[] =
240 "Set the device used by further commands.\n"
241 "A device name or a PARTUUID string may be used.\n"
242 "Access to that device (by the g13 system) is locked\n"
243 "until a new DEVICE command or end of this process\n";
245 cmd_device (assuan_context_t ctx, char *line)
247 ctrl_t ctrl = assuan_get_pointer (ctx);
252 line = skip_options (line);
254 /* # warning hardwired to /dev/sdb1 ! */
255 /* if (strcmp (line, "/dev/sdb1")) */
257 /* err = gpg_error (GPG_ERR_ENOENT); */
261 /* Always close an open device stream of this session. */
262 xfree (ctrl->server_local->devicename);
263 ctrl->server_local->devicename = NULL;
264 es_fclose (ctrl->server_local->devicefp);
265 ctrl->server_local->devicefp = NULL;
267 /* Are we allowed to use the given device? */
268 for (ti=ctrl->client.tab; ti; ti = ti->next)
269 if (!strcmp (line, ti->blockdev))
273 err = set_error (GPG_ERR_EACCES, "device not configured for user");
277 ctrl->server_local->devicename = xtrystrdup (line);
278 if (!ctrl->server_local->devicename)
280 err = gpg_error_from_syserror ();
285 /* Check whether we have permissions to open the device and keep an
287 fp = es_fopen (ctrl->server_local->devicename, "rb");
290 err = gpg_error_from_syserror ();
291 log_error ("error opening '%s': %s\n",
292 ctrl->server_local->devicename, gpg_strerror (err));
296 es_fclose (ctrl->server_local->devicefp);
297 ctrl->server_local->devicefp = fp;
301 /* Fixme: Take some kind of lock. */
307 xfree (ctrl->server_local->devicename);
308 ctrl->server_local->devicename = NULL;
311 return leave_cmd (ctx, err);
315 static const char hlp_create[] =
318 "Create a new encrypted partition on the current device.\n"
319 "<type> must be \"dm-crypt\" for now.";
321 cmd_create (assuan_context_t ctx, char *line)
323 ctrl_t ctrl = assuan_get_pointer (ctx);
327 line = skip_options (line);
328 if (strcmp (line, "dm-crypt"))
330 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
334 if (!ctrl->server_local->devicename
335 || !ctrl->server_local->devicefp
338 err = set_error (GPG_ERR_ENOENT, "No device has been set");
342 err = sh_is_empty_partition (ctrl->server_local->devicename);
345 if (gpg_err_code (err) == GPG_ERR_FALSE)
346 err = gpg_error (GPG_ERR_CONFLICT);
347 err = assuan_set_error (ctx, err, "Partition is not empty");
351 /* We need a writeable stream to create the container. */
352 fp = es_fopen (ctrl->server_local->devicename, "r+b");
355 err = gpg_error_from_syserror ();
356 log_error ("error opening '%s': %s\n",
357 ctrl->server_local->devicename, gpg_strerror (err));
360 if (es_setvbuf (fp, NULL, _IONBF, 0))
362 err = gpg_error_from_syserror ();
363 log_error ("error setting '%s' to _IONBF: %s\n",
364 ctrl->server_local->devicename, gpg_strerror (err));
368 err = sh_dmcrypt_create_container (ctrl,
369 ctrl->server_local->devicename,
373 gpg_error_t err2 = gpg_error_from_syserror ();
374 log_error ("error closing '%s': %s\n",
375 ctrl->server_local->devicename, gpg_strerror (err2));
383 return leave_cmd (ctx, err);
387 static const char hlp_getkeyblob[] =
390 "Return the encrypted keyblob of the current device.";
392 cmd_getkeyblob (assuan_context_t ctx, char *line)
394 ctrl_t ctrl = assuan_get_pointer (ctx);
396 void *enckeyblob = NULL;
397 size_t enckeybloblen;
399 line = skip_options (line);
401 if (!ctrl->server_local->devicename
402 || !ctrl->server_local->devicefp
405 err = set_error (GPG_ERR_ENOENT, "No device has been set");
409 err = sh_is_empty_partition (ctrl->server_local->devicename);
412 err = gpg_error (GPG_ERR_ENODEV);
413 assuan_set_error (ctx, err, "Partition is empty");
418 err = g13_keyblob_read (ctrl->server_local->devicename,
419 &enckeyblob, &enckeybloblen);
423 err = assuan_send_data (ctx, enckeyblob, enckeybloblen);
425 err = assuan_send_data (ctx, NULL, 0); /* Flush */
429 return leave_cmd (ctx, err);
433 static const char hlp_mount[] =
436 "Mount an encrypted partition on the current device.\n"
437 "<type> must be \"dm-crypt\" for now.";
439 cmd_mount (assuan_context_t ctx, char *line)
441 ctrl_t ctrl = assuan_get_pointer (ctx);
443 unsigned char *keyblob = NULL;
445 tupledesc_t tuples = NULL;
447 line = skip_options (line);
449 if (strcmp (line, "dm-crypt"))
451 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
455 if (!ctrl->server_local->devicename
456 || !ctrl->server_local->devicefp
459 err = set_error (GPG_ERR_ENOENT, "No device has been set");
463 err = sh_is_empty_partition (ctrl->server_local->devicename);
466 err = gpg_error (GPG_ERR_ENODEV);
467 assuan_set_error (ctx, err, "Partition is empty");
472 /* We expect that the client already decrypted the keyblob.
473 * Eventually we should move reading of the keyblob to here and ask
474 * the client to decrypt it. */
475 assuan_begin_confidential (ctx);
476 err = assuan_inquire (ctx, "KEYBLOB",
477 &keyblob, &keybloblen, 4 * 1024);
478 assuan_end_confidential (ctx);
481 log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
484 err = create_tupledesc (&tuples, keyblob, keybloblen);
489 if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
490 log_error ("unknown keyblob version received\n");
494 err = sh_dmcrypt_mount_container (ctrl,
495 ctrl->server_local->devicename,
499 destroy_tupledesc (tuples);
500 return leave_cmd (ctx, err);
504 static const char hlp_umount[] =
507 "Unmount an encrypted partition and wipe the key.\n"
508 "<type> must be \"dm-crypt\" for now.";
510 cmd_umount (assuan_context_t ctx, char *line)
512 ctrl_t ctrl = assuan_get_pointer (ctx);
515 line = skip_options (line);
517 if (strcmp (line, "dm-crypt"))
519 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
523 if (!ctrl->server_local->devicename
524 || !ctrl->server_local->devicefp
527 err = set_error (GPG_ERR_ENOENT, "No device has been set");
531 err = sh_dmcrypt_umount_container (ctrl, ctrl->server_local->devicename);
534 return leave_cmd (ctx, err);
538 static const char hlp_suspend[] =
541 "Suspend an encrypted partition and wipe the key.\n"
542 "<type> must be \"dm-crypt\" for now.";
544 cmd_suspend (assuan_context_t ctx, char *line)
546 ctrl_t ctrl = assuan_get_pointer (ctx);
549 line = skip_options (line);
551 if (strcmp (line, "dm-crypt"))
553 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
557 if (!ctrl->server_local->devicename
558 || !ctrl->server_local->devicefp
561 err = set_error (GPG_ERR_ENOENT, "No device has been set");
565 err = sh_is_empty_partition (ctrl->server_local->devicename);
568 err = gpg_error (GPG_ERR_ENODEV);
569 assuan_set_error (ctx, err, "Partition is empty");
574 err = sh_dmcrypt_suspend_container (ctrl, ctrl->server_local->devicename);
577 return leave_cmd (ctx, err);
581 static const char hlp_resume[] =
584 "Resume an encrypted partition and set the key.\n"
585 "<type> must be \"dm-crypt\" for now.";
587 cmd_resume (assuan_context_t ctx, char *line)
589 ctrl_t ctrl = assuan_get_pointer (ctx);
591 unsigned char *keyblob = NULL;
593 tupledesc_t tuples = NULL;
595 line = skip_options (line);
597 if (strcmp (line, "dm-crypt"))
599 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
603 if (!ctrl->server_local->devicename
604 || !ctrl->server_local->devicefp
607 err = set_error (GPG_ERR_ENOENT, "No device has been set");
611 err = sh_is_empty_partition (ctrl->server_local->devicename);
614 err = gpg_error (GPG_ERR_ENODEV);
615 assuan_set_error (ctx, err, "Partition is empty");
620 /* We expect that the client already decrypted the keyblob.
621 * Eventually we should move reading of the keyblob to here and ask
622 * the client to decrypt it. */
623 assuan_begin_confidential (ctx);
624 err = assuan_inquire (ctx, "KEYBLOB",
625 &keyblob, &keybloblen, 4 * 1024);
626 assuan_end_confidential (ctx);
629 log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
632 err = create_tupledesc (&tuples, keyblob, keybloblen);
637 if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
638 log_error ("unknown keyblob version received\n");
642 err = sh_dmcrypt_resume_container (ctrl,
643 ctrl->server_local->devicename,
647 destroy_tupledesc (tuples);
648 return leave_cmd (ctx, err);
652 static const char hlp_getinfo[] =
655 "Multipurpose function to return a variety of information.\n"
656 "Supported values for WHAT are:\n"
658 " version - Return the version of the program.\n"
659 " pid - Return the process id of the server.\n"
660 " showtab - Show the table for the user.";
662 cmd_getinfo (assuan_context_t ctx, char *line)
664 ctrl_t ctrl = assuan_get_pointer (ctx);
668 if (!strcmp (line, "version"))
670 const char *s = PACKAGE_VERSION;
671 err = assuan_send_data (ctx, s, strlen (s));
673 else if (!strcmp (line, "pid"))
677 snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
678 err = assuan_send_data (ctx, numbuf, strlen (numbuf));
680 else if (!strncmp (line, "getsz", 5))
682 unsigned long long nblocks;
683 err = sh_blockdev_getsz (line+6, &nblocks);
685 log_debug ("getsz=%llu\n", nblocks);
687 else if (!strcmp (line, "showtab"))
691 for (ti=ctrl->client.tab; !err && ti; ti = ti->next)
693 buf = es_bsprintf ("%s %s%s %s %s%s\n",
695 *ti->blockdev=='/'? "":"partuuid=",
697 ti->label? ti->label : "-",
698 ti->mountpoint? " ":"",
699 ti->mountpoint? ti->mountpoint:"");
701 err = gpg_error_from_syserror ();
704 err = assuan_send_data (ctx, buf, strlen (buf));
706 err = assuan_send_data (ctx, NULL, 0); /* Flush */
712 err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
714 return leave_cmd (ctx, err);
718 /* This command handler is used for all commands if this process has
719 not been started as expected. */
721 fail_command (assuan_context_t ctx, char *line)
724 const char *name = assuan_get_command_name (ctx);
731 err = set_error_fail_cmd ();
732 log_error ("command '%s' failed: %s\n", name, gpg_strerror (err));
737 /* Tell the Assuan library about our commands. */
739 register_commands (assuan_context_t ctx, int fail_all)
743 assuan_handler_t handler;
744 const char * const help;
746 { "FINDDEVICE", cmd_finddevice, hlp_finddevice },
747 { "DEVICE", cmd_device, hlp_device },
748 { "CREATE", cmd_create, hlp_create },
749 { "GETKEYBLOB", cmd_getkeyblob, hlp_getkeyblob },
750 { "MOUNT", cmd_mount, hlp_mount },
751 { "UMOUNT", cmd_umount, hlp_umount },
752 { "SUSPEND", cmd_suspend,hlp_suspend},
753 { "RESUME", cmd_resume, hlp_resume },
756 { "GETINFO", cmd_getinfo, hlp_getinfo },
762 for (i=0; table[i].name; i++)
764 err = assuan_register_command (ctx, table[i].name,
765 fail_all ? fail_command : table[i].handler,
774 /* Startup the server. */
776 syshelp_server (ctrl_t ctrl)
779 assuan_fd_t filedes[2];
780 assuan_context_t ctx = NULL;
782 /* We use a pipe based server so that we can work from scripts.
783 assuan_init_pipe_server will automagically detect when we are
784 called with a socketpair and ignore FILEDES in this case. */
785 filedes[0] = assuan_fdopen (0);
786 filedes[1] = assuan_fdopen (1);
787 err = assuan_new (&ctx);
790 log_error ("failed to allocate an Assuan context: %s\n",
795 err = assuan_init_pipe_server (ctx, filedes);
798 log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
802 err = register_commands (ctx, 0 /*FIXME:ctrl->fail_all_cmds*/);
805 log_error ("failed to the register commands with Assuan: %s\n",
810 assuan_set_pointer (ctx, ctrl);
813 char *tmp = xtryasprintf ("G13-syshelp %s ready to serve requests "
816 (unsigned long)ctrl->client.uid,
820 assuan_set_hello_line (ctx, tmp);
825 assuan_register_reset_notify (ctx, reset_notify);
826 assuan_register_option_handler (ctx, option_handler);
828 ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
829 if (!ctrl->server_local)
831 err = gpg_error_from_syserror ();
834 ctrl->server_local->assuan_ctx = ctx;
836 while ( !(err = assuan_accept (ctx)) )
838 err = assuan_process (ctx);
840 log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
845 log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
848 reset_notify (ctx, NULL); /* Release all items hold by SERVER_LOCAL. */
849 if (ctrl->server_local)
851 xfree (ctrl->server_local);
852 ctrl->server_local = NULL;
855 assuan_release (ctx);
861 sh_encrypt_keyblob (ctrl_t ctrl, const void *keyblob, size_t keybloblen,
862 char **r_enckeyblob, size_t *r_enckeybloblen)
864 assuan_context_t ctx = ctrl->server_local->assuan_ctx;
866 unsigned char *enckeyblob;
867 size_t enckeybloblen;
869 *r_enckeyblob = NULL;
871 /* Send the plaintext. */
872 err = g13_status (ctrl, STATUS_PLAINTEXT_FOLLOWS, NULL);
875 assuan_begin_confidential (ctx);
876 err = assuan_send_data (ctx, keyblob, keybloblen);
878 err = assuan_send_data (ctx, NULL, 0);
879 assuan_end_confidential (ctx);
881 err = assuan_write_line (ctx, "END");
884 log_error (_("error sending data: %s\n"), gpg_strerror (err));
888 /* Inquire the ciphertext. */
889 err = assuan_inquire (ctx, "ENCKEYBLOB",
890 &enckeyblob, &enckeybloblen, 16 * 1024);
893 log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
897 *r_enckeyblob = enckeyblob;
898 *r_enckeybloblen = enckeybloblen;
903 /* Send a status line with status ID NO. The arguments are a list of
904 strings terminated by a NULL argument. */
906 g13_status (ctrl_t ctrl, int no, ...)
911 va_start (arg_ptr, no);
913 err = vprint_assuan_status_strings (ctrl->server_local->assuan_ctx,
914 get_status_string (no), arg_ptr);