[systemd-devel] [PATCH] add keyhandler support to cryptsetup

Benjamin SANS bs at ziirish.info
Mon Mar 17 08:38:32 PDT 2014


Hi,

As I said in my previous message, the patch I sent was untested but the goal is
to have some feedback about the way to do it.

I successfully patched the debian wheezy's systemd to make this new option work.

Here is what the patch does. Please let me know if it is the right way to do it
and if it may have any chance to get integrated upstream.



1. Add a new option "keyhandler" to cryptsetup



diff --git a/systemd-44.orig/src/cryptsetup/cryptsetup.c b/systemd-44/src/cryptsetup/cryptsetup.c
index ac7b6d6..ffc4303 100644
--- a/systemd-44.orig/src/cryptsetup/cryptsetup.c
+++ b/systemd-44/src/cryptsetup/cryptsetup.c
@@ -37,6 +37,7 @@ static const char *opt_type = NULL; /* LUKS1 or PLAIN */
 static char *opt_cipher = NULL;
 static unsigned opt_key_size = 0;
 static char *opt_hash = NULL;
+static char *opt_keyhandler = NULL;
 static unsigned opt_tries = 0;
 static bool opt_readonly = false;
 static bool opt_verify = false;
@@ -86,6 +87,23 @@ static int parse_one_option(const char *option) {
                 free(opt_hash);
                 opt_hash = t;

+         } else if (startswith(option, "keyhandler=")) {
+                 char *t;
+
+                 if (!option[11] || !path_is_absolute(option+11)) {
+                         log_error("keyhandler= path is invalid, ignoring.");
+                         free(opt_keyhandler);
+                         opt_keyhandler = NULL;
+                         return 0;
+                 }
+
+                 t = strdup(option+11);
+                 if (!t)
+                         return -ENOMEM;
+
+                 free(opt_keyhandler);
+                 opt_keyhandler = t;
+
         } else if (startswith(option, "tries=")) {

                 if (safe_atou(option+6, &opt_tries) < 0) {



2. If the keyhandler option is provided, we pass 2 "arguments" to the
ask_password_auto function. Actually, we fill the passwords array with 2
strings: the keyhandler path and the 3rd argument of the crypttab file



@@ -258,20 +276,22 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }

-                if (argc >= 5 &&
-                    argv[4][0] &&
-                    !streq(argv[4], "-") &&
-                    !streq(argv[4], "none")) {
-
-                        if (!path_is_absolute(argv[4]))
-                                log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
-                        else
-                                key_file = argv[4];
-                }
-
                 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
                         parse_options(argv[5]);

+                 if (argc >= 5) {
+                         if (opt_keyhandler)
+                                 key_file = argv[4]; /* used as arg to keyscript */
+                         else if (argv[4][0] &&
+                                  !streq(argv[4], "-") &&
+                                  !streq(argv[4], "none"))
+                                 /* do nothing */;
+                         else if (!path_is_absolute(argv[4]))
+                                 log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
+                         else
+                                 key_file = argv[4];
+                 }
+
                 /* A delicious drop of snake oil */
                 mlockall(MCL_FUTURE);

@@ -344,6 +364,13 @@ int main(int argc, char *argv[]) {
                         strv_free(passwords);
                         passwords = NULL;

+                       /* We only launch the handler once */
+                       if (opt_keyhandler && try == 0) {
+                               passwords = strv_new(opt_keyhandler, key_file, NULL);
+                               /* We unset the key_file as it is only an argument to our handler and it has been copied above */
+                               key_file = NULL;
+                       }
+
                         if (!key_file) {
                                 char *text;
                                 char **p;
@@ -453,9 +480,17 @@ int main(int argc, char *argv[]) {
                                  crypt_get_volume_key_size(cd)*8,
                                  argv[3]);

-                        if (key_file)
+                        if (key_file || (opt_keyhandler && try == 0)) {
+                               if (opt_keyhandler)
+                                       key_file = passwords[0];
                                 k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, keyfile_size, flags);
-                        else {
+
+                               /* Remove the temporary file created by our agent as it contains our key! */
+                               if (opt_keyhandler) {
+                                       unlink(key_file);
+                                       key_file = NULL;
+                               }
+                       } else {
                                 char **p;

                                 STRV_FOREACH(p, passwords) {



3. The ask-password API detects the passwords array is not empty and assumes the
keyhandler path and options are given. It then adds them in the ask.XXXXXX file.



diff --git a/systemd-44.orig/src/ask-password-api.c b/systemd-44/src/ask-password-api.c
index ce2f3cb..34239ac 100644
--- a/systemd-44.orig/src/ask-password-api.c
+++ b/systemd-44/src/ask-password-api.c
@@ -373,6 +373,17 @@ int ask_password_agent(
         if (icon)
                 fprintf(f, "Icon=%s\n", icon);

+         /* We assume the keyhandler path and options are provided */
+         if (strv_length(*_passphrases) == 2) {
+                 char **p = *_passphrases;
+                 fprintf(f, "KeyHandler=%s\n"
+                            "KeyOpts=%s\n",
+                            p[0],
+                            p[1]);
+                 strv_free(*_passphrases);
+                 *_passphrases = NULL;
+         }
+
         fflush(f);

         if (ferror(f)) {



4. Now the Agent can search for the KeyHandler and KeyOpts options in the
ask.XXXXXX file. If the KeyHandler is found, we create a temporary file in
/run/systemd/ask-password that will handle the key. We assume /run is a tmpfs
and then it is safe enough to temporary hold a keyfile generated by our
KeyHandler.
The Agent then launch the KeyHandler and redirects its standard output to the
temporary file.
Finally, the agent send the temporary file path to cryptsetup via the passwords
array.



diff --git a/systemd-44.orig/src/tty-ask-password-agent.c b/systemd-44/src/tty-ask-password-agent.c
index 13481b2..dabb467 100644
--- a/systemd-44.orig/src/tty-ask-password-agent.c
+++ b/systemd-44/src/tty-ask-password-agent.c
@@ -245,7 +245,7 @@ finish:
 }

 static int parse_password(const char *filename, char **wall) {
-        char *socket_name = NULL, *message = NULL, *packet = NULL;
+        char *socket_name = NULL, *message = NULL, *packet = NULL, *keyhandler = NULL, *keyopts = NULL;
         uint64_t not_after = 0;
         unsigned pid = 0;
         int socket_fd = -1;
@@ -257,6 +257,8 @@ static int parse_password(const char *filename, char **wall) {
                 { "Ask", "Message",      config_parse_string,   0, &message       },
                 { "Ask", "PID",          config_parse_unsigned, 0, &pid           },
                 { "Ask", "AcceptCached", config_parse_bool,     0, &accept_cached },
+               { "Ask", "KeyHandler",   config_parse_string,   0, &keyhandler    },
+               { "Ask", "KeyOpts",      config_parse_string,   0, &keyopts       },
                 { NULL, NULL, NULL, 0, NULL }
         };

@@ -338,7 +340,59 @@ static int parse_password(const char *filename, char **wall) {
                         goto finish;
                 }

-                if (arg_plymouth) {
+                 if (keyhandler) {
+                         char temp[] = "/run/systemd/ask-password/tmp.XXXXXX";
+                         int fd = -1;
+                         pid_t pid;
+
+                         fd = mkostemp(temp, O_WRONLY|O_CLOEXEC);
+                         if (fd < 0) {
+                                 log_error("Failed to create password file: %m");
+                                 r = -errno;
+                                 goto finish;
+                         }
+
+                         fchmod(fd, 0644);
+
+                         pid = fork();
+
+                         if (pid < 0) {
+                                 log_error("Failed to fork: %m");
+                                 r = -errno;
+                                 close_nointr_nofail(fd);
+                                 goto finish;
+                         } else if (pid > 0) {
+                                 close_nointr_nofail(fd);
+                                 waitpid(pid, NULL, 0);
+
+                                 /* send back the temporary filename that now contains our key */
+                                 packet_length = 2 + strlen(temp);
+
+                                 if (!(packet = new(char, packet_length)))
+                                         r = -ENOMEM;
+                                 else {
+                                         char *d;
+                                         packet[0] = '+';
+                                         d = packet+1;
+                                         d = stpcpy(d, temp) + 1;
+                                 }
+                         } else if (pid == 0) {
+                                 /* launch the keyhandler programm and redirect its output to our temporary file */
+                                 const char *args[3];
+
+                                 if (dup2(fd, STDOUT_FILENO) < 0)
+                                         _exit(EXIT_FAILURE);
+
+                                 close(fd);
+
+                                 args[0] = keyhandler;
+                                 args[1] = keyopts;
+                                 args[2] = NULL;
+
+                                 execv(args[0], (char **)args);
+                                 _exit(EXIT_FAILURE);
+                         }
+                 } else if (arg_plymouth) {
                         char **passwords = NULL;

                         if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {



I use a temporary file so that we can easily handle binary keys without
modifying systemd's way of handling keys.


Am I totally wrong?


Note: I made this patch for the debian wheezy's version of systemd which is
systemd-44. I'm working on a version for systemd-204 (debian Jessie's one).


-- 
Benjamin SANS
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 490 bytes
Desc: Digital signature
URL: <http://lists.freedesktop.org/archives/systemd-devel/attachments/20140317/69bf6ca4/attachment-0001.sig>


More information about the systemd-devel mailing list