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"
34 /* Local data for this server module. A pointer to this is stored in
35 the CTRL object of each connection. */
38 /* The Assuan contect we are working on. */
39 assuan_context_t assuan_ctx;
41 /* The malloced name of the device. */
44 /* A stream open for read of the device set by the DEVICE command or
45 NULL if no DEVICE command has been used. */
52 /* Local prototypes. */
61 /* Set an error and a description. */
62 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
63 #define set_error_fail_cmd() set_error (GPG_ERR_NOT_INITIALIZED, \
64 "not called via userv or unknown user")
67 /* Skip over options. Blanks after the options are also removed. */
69 skip_options (const char *line)
73 while ( *line == '-' && line[1] == '-' )
75 while (*line && !spacep (line))
84 /* Check whether the option NAME appears in LINE. */
86 /* has_option (const char *line, const char *name) */
89 /* int n = strlen (name); */
91 /* s = strstr (line, name); */
92 /* if (s && s >= skip_options (line)) */
94 /* return (s && (s == line || spacep (s-1)) && (!s[n] || spacep (s+n))); */
98 /* Helper to print a message while leaving a command. */
100 leave_cmd (assuan_context_t ctx, gpg_error_t err)
104 const char *name = assuan_get_command_name (ctx);
107 if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
108 log_error ("command '%s' failed: %s\n", name,
111 log_error ("command '%s' failed: %s <%s>\n", name,
112 gpg_strerror (err), gpg_strsource (err));
120 /* The handler for Assuan OPTION commands. */
122 option_handler (assuan_context_t ctx, const char *key, const char *value)
124 ctrl_t ctrl = assuan_get_pointer (ctx);
131 if (ctrl->fail_all_cmds)
132 err = set_error_fail_cmd ();
134 err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
140 /* The handler for an Assuan RESET command. */
142 reset_notify (assuan_context_t ctx, char *line)
144 ctrl_t ctrl = assuan_get_pointer (ctx);
148 xfree (ctrl->server_local->devicename);
149 ctrl->server_local->devicename = NULL;
150 es_fclose (ctrl->server_local->devicefp);
151 ctrl->server_local->devicefp = NULL;
154 assuan_close_input_fd (ctx);
155 assuan_close_output_fd (ctx);
160 static const char hlp_finddevice[] =
161 "FINDDEVICE <name>\n"
163 "Find the device matching NAME. NAME be any identifier from\n"
164 "g13tab permissible for the user. The corresponding block\n"
165 "device is returned using a status line.";
167 cmd_finddevice (assuan_context_t ctx, char *line)
169 ctrl_t ctrl = assuan_get_pointer (ctx);
175 name = skip_options (line);
177 /* Are we allowed to use the given device? We check several names:
178 * 1. The full block device
180 * 3. The final part of the block device if NAME does not have a slash.
183 for (ti=ctrl->client.tab; ti; ti = ti->next)
184 if (!strcmp (name, ti->blockdev))
188 for (ti=ctrl->client.tab; ti; ti = ti->next)
189 if (ti->label && !strcmp (name, ti->label))
192 if (!ti && !strchr (name, '/'))
194 for (ti=ctrl->client.tab; ti; ti = ti->next)
196 s = strrchr (ti->blockdev, '/');
197 if (s && s[1] && !strcmp (name, s+1))
203 for (ti=ctrl->client.tab; ti; ti = ti->next)
204 if (ti->mountpoint && !strcmp (name, ti->mountpoint))
210 err = set_error (GPG_ERR_NOT_FOUND, "device not configured for user");
214 /* Check whether we have permissions to open the device. */
216 estream_t fp = es_fopen (ti->blockdev, "rb");
219 err = gpg_error_from_syserror ();
220 log_error ("error opening '%s': %s\n",
221 ti->blockdev, gpg_strerror (err));
227 err = g13_status (ctrl, STATUS_BLOCKDEV, ti->blockdev, NULL);
232 return leave_cmd (ctx, err);
236 static const char hlp_device[] =
239 "Set the device used by further commands.\n"
240 "A device name or a PARTUUID string may be used.\n"
241 "Access to that device (by the g13 system) is locked\n"
242 "until a new DEVICE command or end of this process\n";
244 cmd_device (assuan_context_t ctx, char *line)
246 ctrl_t ctrl = assuan_get_pointer (ctx);
251 line = skip_options (line);
253 /* # warning hardwired to /dev/sdb1 ! */
254 /* if (strcmp (line, "/dev/sdb1")) */
256 /* err = gpg_error (GPG_ERR_ENOENT); */
260 /* Always close an open device stream of this session. */
261 xfree (ctrl->server_local->devicename);
262 ctrl->server_local->devicename = NULL;
263 es_fclose (ctrl->server_local->devicefp);
264 ctrl->server_local->devicefp = NULL;
266 /* Are we allowed to use the given device? */
267 for (ti=ctrl->client.tab; ti; ti = ti->next)
268 if (!strcmp (line, ti->blockdev))
272 err = set_error (GPG_ERR_EACCES, "device not configured for user");
276 ctrl->server_local->devicename = xtrystrdup (line);
277 if (!ctrl->server_local->devicename)
279 err = gpg_error_from_syserror ();
284 /* Check whether we have permissions to open the device and keep an
286 fp = es_fopen (ctrl->server_local->devicename, "rb");
289 err = gpg_error_from_syserror ();
290 log_error ("error opening '%s': %s\n",
291 ctrl->server_local->devicename, gpg_strerror (err));
295 es_fclose (ctrl->server_local->devicefp);
296 ctrl->server_local->devicefp = fp;
300 /* Fixme: Take some kind of lock. */
306 xfree (ctrl->server_local->devicename);
307 ctrl->server_local->devicename = NULL;
310 return leave_cmd (ctx, err);
314 static const char hlp_create[] =
317 "Create a new encrypted partition on the current device.\n"
318 "<type> must be \"dm-crypt\" for now.";
320 cmd_create (assuan_context_t ctx, char *line)
322 ctrl_t ctrl = assuan_get_pointer (ctx);
326 line = skip_options (line);
327 if (strcmp (line, "dm-crypt"))
329 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
333 if (!ctrl->server_local->devicename
334 || !ctrl->server_local->devicefp
337 err = set_error (GPG_ERR_ENOENT, "No device has been set");
341 err = sh_is_empty_partition (ctrl->server_local->devicename);
344 if (gpg_err_code (err) == GPG_ERR_FALSE)
345 err = gpg_error (GPG_ERR_CONFLICT);
346 err = assuan_set_error (ctx, err, "Partition is not empty");
350 /* We need a writeable stream to create the container. */
351 fp = es_fopen (ctrl->server_local->devicename, "r+b");
354 err = gpg_error_from_syserror ();
355 log_error ("error opening '%s': %s\n",
356 ctrl->server_local->devicename, gpg_strerror (err));
359 if (es_setvbuf (fp, NULL, _IONBF, 0))
361 err = gpg_error_from_syserror ();
362 log_error ("error setting '%s' to _IONBF: %s\n",
363 ctrl->server_local->devicename, gpg_strerror (err));
367 err = sh_dmcrypt_create_container (ctrl,
368 ctrl->server_local->devicename,
372 gpg_error_t err2 = gpg_error_from_syserror ();
373 log_error ("error closing '%s': %s\n",
374 ctrl->server_local->devicename, gpg_strerror (err2));
382 return leave_cmd (ctx, err);
386 static const char hlp_getkeyblob[] =
389 "Return the encrypted keyblob of the current device.";
391 cmd_getkeyblob (assuan_context_t ctx, char *line)
393 ctrl_t ctrl = assuan_get_pointer (ctx);
395 void *enckeyblob = NULL;
396 size_t enckeybloblen;
398 line = skip_options (line);
400 if (!ctrl->server_local->devicename
401 || !ctrl->server_local->devicefp
404 err = set_error (GPG_ERR_ENOENT, "No device has been set");
408 err = sh_is_empty_partition (ctrl->server_local->devicename);
411 err = gpg_error (GPG_ERR_ENODEV);
412 assuan_set_error (ctx, err, "Partition is empty");
417 err = g13_keyblob_read (ctrl->server_local->devicename,
418 &enckeyblob, &enckeybloblen);
422 err = assuan_send_data (ctx, enckeyblob, enckeybloblen);
424 err = assuan_send_data (ctx, NULL, 0); /* Flush */
428 return leave_cmd (ctx, err);
432 static const char hlp_mount[] =
435 "Mount an encrypted partition on the current device.\n"
436 "<type> must be \"dm-crypt\" for now.";
438 cmd_mount (assuan_context_t ctx, char *line)
440 ctrl_t ctrl = assuan_get_pointer (ctx);
442 unsigned char *keyblob = NULL;
444 tupledesc_t tuples = NULL;
446 line = skip_options (line);
448 if (strcmp (line, "dm-crypt"))
450 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
454 if (!ctrl->server_local->devicename
455 || !ctrl->server_local->devicefp
458 err = set_error (GPG_ERR_ENOENT, "No device has been set");
462 err = sh_is_empty_partition (ctrl->server_local->devicename);
465 err = gpg_error (GPG_ERR_ENODEV);
466 assuan_set_error (ctx, err, "Partition is empty");
471 /* We expect that the client already decrypted the keyblob.
472 * Eventually we should move reading of the keyblob to here and ask
473 * the client to decrypt it. */
474 assuan_begin_confidential (ctx);
475 err = assuan_inquire (ctx, "KEYBLOB",
476 &keyblob, &keybloblen, 4 * 1024);
477 assuan_end_confidential (ctx);
480 log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
483 err = create_tupledesc (&tuples, keyblob, keybloblen);
488 if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
489 log_error ("unknown keyblob version received\n");
493 err = sh_dmcrypt_mount_container (ctrl,
494 ctrl->server_local->devicename,
498 destroy_tupledesc (tuples);
499 return leave_cmd (ctx, err);
503 static const char hlp_umount[] =
506 "Unmount an encrypted partition and wipe the key.\n"
507 "<type> must be \"dm-crypt\" for now.";
509 cmd_umount (assuan_context_t ctx, char *line)
511 ctrl_t ctrl = assuan_get_pointer (ctx);
514 line = skip_options (line);
516 if (strcmp (line, "dm-crypt"))
518 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
522 if (!ctrl->server_local->devicename
523 || !ctrl->server_local->devicefp
526 err = set_error (GPG_ERR_ENOENT, "No device has been set");
530 err = sh_dmcrypt_umount_container (ctrl, ctrl->server_local->devicename);
533 return leave_cmd (ctx, err);
537 static const char hlp_suspend[] =
540 "Suspend an encrypted partition and wipe the key.\n"
541 "<type> must be \"dm-crypt\" for now.";
543 cmd_suspend (assuan_context_t ctx, char *line)
545 ctrl_t ctrl = assuan_get_pointer (ctx);
548 line = skip_options (line);
550 if (strcmp (line, "dm-crypt"))
552 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
556 if (!ctrl->server_local->devicename
557 || !ctrl->server_local->devicefp
560 err = set_error (GPG_ERR_ENOENT, "No device has been set");
564 err = sh_is_empty_partition (ctrl->server_local->devicename);
567 err = gpg_error (GPG_ERR_ENODEV);
568 assuan_set_error (ctx, err, "Partition is empty");
573 err = sh_dmcrypt_suspend_container (ctrl, ctrl->server_local->devicename);
576 return leave_cmd (ctx, err);
580 static const char hlp_resume[] =
583 "Resume an encrypted partition and set the key.\n"
584 "<type> must be \"dm-crypt\" for now.";
586 cmd_resume (assuan_context_t ctx, char *line)
588 ctrl_t ctrl = assuan_get_pointer (ctx);
590 unsigned char *keyblob = NULL;
592 tupledesc_t tuples = NULL;
594 line = skip_options (line);
596 if (strcmp (line, "dm-crypt"))
598 err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
602 if (!ctrl->server_local->devicename
603 || !ctrl->server_local->devicefp
606 err = set_error (GPG_ERR_ENOENT, "No device has been set");
610 err = sh_is_empty_partition (ctrl->server_local->devicename);
613 err = gpg_error (GPG_ERR_ENODEV);
614 assuan_set_error (ctx, err, "Partition is empty");
619 /* We expect that the client already decrypted the keyblob.
620 * Eventually we should move reading of the keyblob to here and ask
621 * the client to decrypt it. */
622 assuan_begin_confidential (ctx);
623 err = assuan_inquire (ctx, "KEYBLOB",
624 &keyblob, &keybloblen, 4 * 1024);
625 assuan_end_confidential (ctx);
628 log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
631 err = create_tupledesc (&tuples, keyblob, keybloblen);
636 if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
637 log_error ("unknown keyblob version received\n");
641 err = sh_dmcrypt_resume_container (ctrl,
642 ctrl->server_local->devicename,
646 destroy_tupledesc (tuples);
647 return leave_cmd (ctx, err);
651 static const char hlp_getinfo[] =
654 "Multipurpose function to return a variety of information.\n"
655 "Supported values for WHAT are:\n"
657 " version - Return the version of the program.\n"
658 " pid - Return the process id of the server.\n"
659 " showtab - Show the table for the user.";
661 cmd_getinfo (assuan_context_t ctx, char *line)
663 ctrl_t ctrl = assuan_get_pointer (ctx);
667 if (!strcmp (line, "version"))
669 const char *s = PACKAGE_VERSION;
670 err = assuan_send_data (ctx, s, strlen (s));
672 else if (!strcmp (line, "pid"))
676 snprintf (numbuf, sizeof numbuf, "%lu", (unsigned long)getpid ());
677 err = assuan_send_data (ctx, numbuf, strlen (numbuf));
679 else if (!strncmp (line, "getsz", 5))
681 unsigned long long nblocks;
682 err = sh_blockdev_getsz (line+6, &nblocks);
684 log_debug ("getsz=%llu\n", nblocks);
686 else if (!strcmp (line, "showtab"))
690 for (ti=ctrl->client.tab; !err && ti; ti = ti->next)
692 buf = es_bsprintf ("%s %s%s %s %s%s\n",
694 *ti->blockdev=='/'? "":"partuuid=",
696 ti->label? ti->label : "-",
697 ti->mountpoint? " ":"",
698 ti->mountpoint? ti->mountpoint:"");
700 err = gpg_error_from_syserror ();
703 err = assuan_send_data (ctx, buf, strlen (buf));
705 err = assuan_send_data (ctx, NULL, 0); /* Flush */
711 err = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT");
713 return leave_cmd (ctx, err);
717 /* This command handler is used for all commands if this process has
718 not been started as expected. */
720 fail_command (assuan_context_t ctx, char *line)
723 const char *name = assuan_get_command_name (ctx);
730 err = set_error_fail_cmd ();
731 log_error ("command '%s' failed: %s\n", name, gpg_strerror (err));
736 /* Tell the Assuan library about our commands. */
738 register_commands (assuan_context_t ctx, int fail_all)
742 assuan_handler_t handler;
743 const char * const help;
745 { "FINDDEVICE", cmd_finddevice, hlp_finddevice },
746 { "DEVICE", cmd_device, hlp_device },
747 { "CREATE", cmd_create, hlp_create },
748 { "GETKEYBLOB", cmd_getkeyblob, hlp_getkeyblob },
749 { "MOUNT", cmd_mount, hlp_mount },
750 { "UMOUNT", cmd_umount, hlp_umount },
751 { "SUSPEND", cmd_suspend,hlp_suspend},
752 { "RESUME", cmd_resume, hlp_resume },
755 { "GETINFO", cmd_getinfo, hlp_getinfo },
761 for (i=0; table[i].name; i++)
763 err = assuan_register_command (ctx, table[i].name,
764 fail_all ? fail_command : table[i].handler,
773 /* Startup the server. */
775 syshelp_server (ctrl_t ctrl)
778 assuan_fd_t filedes[2];
779 assuan_context_t ctx = NULL;
781 /* We use a pipe based server so that we can work from scripts.
782 assuan_init_pipe_server will automagically detect when we are
783 called with a socketpair and ignore FILEDES in this case. */
784 filedes[0] = assuan_fdopen (0);
785 filedes[1] = assuan_fdopen (1);
786 err = assuan_new (&ctx);
789 log_error ("failed to allocate an Assuan context: %s\n",
794 err = assuan_init_pipe_server (ctx, filedes);
797 log_error ("failed to initialize the server: %s\n", gpg_strerror (err));
801 err = register_commands (ctx, 0 /*FIXME:ctrl->fail_all_cmds*/);
804 log_error ("failed to the register commands with Assuan: %s\n",
809 assuan_set_pointer (ctx, ctrl);
812 char *tmp = xtryasprintf ("G13-syshelp %s ready to serve requests "
815 (unsigned long)ctrl->client.uid,
819 assuan_set_hello_line (ctx, tmp);
824 assuan_register_reset_notify (ctx, reset_notify);
825 assuan_register_option_handler (ctx, option_handler);
827 ctrl->server_local = xtrycalloc (1, sizeof *ctrl->server_local);
828 if (!ctrl->server_local)
830 err = gpg_error_from_syserror ();
833 ctrl->server_local->assuan_ctx = ctx;
835 while ( !(err = assuan_accept (ctx)) )
837 err = assuan_process (ctx);
839 log_info ("Assuan processing failed: %s\n", gpg_strerror (err));
844 log_info ("Assuan accept problem: %s\n", gpg_strerror (err));
847 reset_notify (ctx, NULL); /* Release all items hold by SERVER_LOCAL. */
848 if (ctrl->server_local)
850 xfree (ctrl->server_local);
851 ctrl->server_local = NULL;
854 assuan_release (ctx);
860 sh_encrypt_keyblob (ctrl_t ctrl, const void *keyblob, size_t keybloblen,
861 char **r_enckeyblob, size_t *r_enckeybloblen)
863 assuan_context_t ctx = ctrl->server_local->assuan_ctx;
865 unsigned char *enckeyblob;
866 size_t enckeybloblen;
868 *r_enckeyblob = NULL;
870 /* Send the plaintext. */
871 err = g13_status (ctrl, STATUS_PLAINTEXT_FOLLOWS, NULL);
874 assuan_begin_confidential (ctx);
875 err = assuan_send_data (ctx, keyblob, keybloblen);
877 err = assuan_send_data (ctx, NULL, 0);
878 assuan_end_confidential (ctx);
880 err = assuan_write_line (ctx, "END");
883 log_error (_("error sending data: %s\n"), gpg_strerror (err));
887 /* Inquire the ciphertext. */
888 err = assuan_inquire (ctx, "ENCKEYBLOB",
889 &enckeyblob, &enckeybloblen, 16 * 1024);
892 log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
896 *r_enckeyblob = enckeyblob;
897 *r_enckeybloblen = enckeybloblen;
902 /* Send a status line with status ID NO. The arguments are a list of
903 strings terminated by a NULL argument. */
905 g13_status (ctrl_t ctrl, int no, ...)
911 va_start (arg_ptr, no);
915 assuan_context_t ctx = ctrl->server_local->assuan_ctx;
921 while ( (text = va_arg (arg_ptr, const char *)) )
928 for ( ; *text && n < DIM (buf)-2; n++)
932 err = assuan_write_status (ctx, get_status_string (no), buf);