diff --git a/mkinitcpio-install/sd-zfs b/mkinitcpio-install/sd-zfs index a6f3dd8..39f27bc 100644 --- a/mkinitcpio-install/sd-zfs +++ b/mkinitcpio-install/sd-zfs @@ -21,12 +21,15 @@ build() { zpool \ zfs \ modprobe \ + systemd-ask-password \ /usr/lib/libgcc_s.so.1 \ /usr/lib/udev/vdev_id \ /usr/lib/udev/zvol_id # units add_systemd_unit systemd-udev-settle.service + add_systemd_unit systemd-ask-password-console.path + add_systemd_unit systemd-ask-password-console.service # Our binaries add_binary /usr/lib/mkinitcpio-sd-zfs/initrd-zfs-generator /usr/lib/systemd/system-generators/ diff --git a/src/zfs-generator.c b/src/zfs-generator.c index 11f5804..1a1ffe0 100644 --- a/src/zfs-generator.c +++ b/src/zfs-generator.c @@ -316,6 +316,10 @@ int generateSysrootUnit(char *directory, int bootfs, char *dataset, char *snapsh return 1; } fprintf(fp, "# This file was generated by sd-zfs\n\ +[Unit]\n\ +Requires=systemd-ask-password-console.service\n\ +After=systemd-ask-password-console.service\n\ +\n\ [Mount]\n\ What=%s\n\ Type=initrd_zfs\n\ diff --git a/src/zfs-util.c b/src/zfs-util.c index de87cd5..0c72922 100644 --- a/src/zfs-util.c +++ b/src/zfs-util.c @@ -9,29 +9,75 @@ // Buffer for reading ZFS command output #define BUFSIZE 16 -int execute(char *command, char needOutput, char **output, char *param[]) { - int pip[2]; - pid_t pid; - int status; +int execute(char *command, char needOutput, char **output, char *param[], char needPassword, char *prompt) { + int pip[2], pwpip[2]; + pid_t pid, pwpid; + int status, pwstatus; char *linebuffer; size_t size; int nRead; + char **cmdline; + + if (needPassword == 1) { + // Execute password prompt + if (pipe(pwpip) == -1) { + perror("Cannot create password pipe"); + exit(1); + } + pwpid = fork(); + if (pwpid == 0) { + // Redirect stdout to pipe + while ((dup2(pwpip[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} + if (errno != 0) { + perror("Cannot connect stdout to parent password pipe"); + exit(1); + } + // Close pipe, we won't need it anymore + close(pwpip[1]); + // Close stdin to prevent weird redirection + close(0); + // Execute + if (prompt == NULL) { + cmdline = (char*[]) { SYSTEMD_ASK_PASS_CMD, NULL }; + } else { + cmdline = (char*[]) { SYSTEMD_ASK_PASS_CMD, prompt, NULL }; + } + execv(SYSTEMD_ASK_PASS_EXE, cmdline); + exit(254); + } else if (pwpid < 0) { + perror("Cannot fork"); + close(pwpip[0]); + close(pwpip[1]); + return pwpid; + } + } // Execute if (needOutput == 1) { if (pipe(pip) == -1) { - perror("Cannot create pipe\n"); + perror("Cannot create output pipe"); exit(1); } } pid = fork(); if (pid == 0) { + if (needPassword == 1) { + // Redirect password pipe to stdin + while ((dup2(pwpip[0], STDIN_FILENO) == -1) && (errno == EINTR)) {} + if (errno != 0) { + perror("Cannot connect stdin to parent password pipe"); + exit(1); + } + // Close pipe, we won't need them anymore + close(pwpip[0]); + } + // Set up pipe if (needOutput == 1) { // Redirect stdout to pipe while ((dup2(pip[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} if (errno != 0) { - perror("Cannot connect to parent pipe\n"); + perror("Cannot connect stdout to parent output pipe"); exit(1); } // Close pipe, we won't need it anymore @@ -45,13 +91,18 @@ int execute(char *command, char needOutput, char **output, char *param[]) { execv(command, param); exit(254); } else if (pid < 0) { - perror("Cannot fork\n"); + perror("Cannot fork"); if (needOutput == 1) { close(pip[0]); close(pip[1]); } return pid; } + // Close out the password pipe + if (needPassword == 1) { + close(pwpip[0]); + close(pwpip[1]); + } // Capture output if (needOutput == 1) { // We don't need to write to the pipe @@ -80,7 +131,13 @@ int execute(char *command, char needOutput, char **output, char *param[]) { free(linebuffer); } // Wait for quit + if (needPassword == 1) { + waitpid(pwpid, &pwstatus, 0); + } waitpid(pid, &status, 0); + if (needPassword == 1 && pwstatus != 0) { + return pwstatus; + } return status; } @@ -92,7 +149,19 @@ int execute(char *command, char needOutput, char **output, char *param[]) { * is ZFS_CMD */ int executeZfs(char needOutput, char **output, char *param[]) { - return execute(ZFS_EXE, needOutput, output, param); + return execute(ZFS_EXE, needOutput, output, param, 0, NULL); +} + +/* + * Executes a zfs command, feeding it the input from a systemd password + * prompt to stdin. + * If needOutput is 1, the output of the command is written to output + * which will be allocated. It must be NULL when passing in. + * param must be a null-terminated array of parameters where the first + * is ZFS_CMD + */ +int executeZfsWithPassword(char needOutput, char **output, char *param[], char *prompt) { + return execute(ZFS_EXE, needOutput, output, param, 1, prompt); } /* @@ -103,7 +172,7 @@ int executeZfs(char needOutput, char **output, char *param[]) { * is ZPOOL_CMD */ int executeZpool(char needOutput, char **output, char *param[]) { - return execute(ZPOOL_EXE, needOutput, output, param); + return execute(ZPOOL_EXE, needOutput, output, param, 0, NULL); } /* @@ -114,7 +183,7 @@ int executeZpool(char needOutput, char **output, char *param[]) { * is MOUNT_CMD */ int executeMount(char needOutput, char **output, char *param[]) { - return execute(MOUNT_EXE, needOutput, output, param); + return execute(MOUNT_EXE, needOutput, output, param, 0, NULL); } int zfs_destroy_recursively(char *dataset) { @@ -325,3 +394,52 @@ int zfs_get_alt_mp(char *dataset, char **mountpoint) { } return status; } + +int zfs_ds_requires_password(char *dataset) { + char *output = NULL; + char *cmdline[] = { ZFS_CMD, "get", "-Ho", "value", "encryption,keyformat", dataset, NULL }; + char *status = NULL; + char *keytype = NULL; + + if (executeZfs(1, &output, cmdline) != 0) { + return -1; + } + + status = strtok(output, "\n"); + if (strcmp(status, "off") != 0) { + keytype = strtok(NULL, "\n"); + if (strcmp(keytype, "passphrase") == 0) { + return 1; + } + } + + return 0; +} + +int zfs_decrypt_ds_with_password(char *dataset) { + int ret; + char *encroot = NULL; + char **cmdline; + const char *promptMessage = "Enter passphrase for '%s':"; + char *prompt; + + cmdline = (char*[]) { ZFS_CMD, "get", "-Ho", "value", "encryptionroot", dataset, NULL }; + if (executeZfs(1, &encroot, cmdline) != 0) { + return 0; + } + + if (encroot != NULL) { + (encroot)[strlen(encroot) - 1] = '\0'; + } + + cmdline = (char*[]) { ZFS_CMD, "load-key", encroot, NULL }; + prompt = (char*)malloc((strlen(promptMessage) - 2 + strlen(encroot)) * sizeof(char)); + sprintf(prompt, promptMessage, encroot); + + ret = executeZfsWithPassword(0, NULL, cmdline, prompt); + if (ret == 0) { + return 1; + } else { + return 0; + } +} diff --git a/src/zfs-util.h b/src/zfs-util.h index efae873..8252bff 100644 --- a/src/zfs-util.h +++ b/src/zfs-util.h @@ -13,6 +13,10 @@ #define MOUNT_EXE "/usr/bin/mount" #endif /* MOUNT_EXE */ +#ifndef SYSTEMD_ASK_PASS_EXE +#define SYSTEMD_ASK_PASS_EXE "/usr/bin/systemd-ask-password" +#endif /* SYSTEMD_ASK_PASS_EXE */ + #ifndef ZFS_CMD #define ZFS_CMD "zfs" #endif /* ZFS_CMD */ @@ -25,6 +29,10 @@ #define MOUNT_CMD "mount" #endif /* MOUNT_CMD */ +#ifndef SYSTEMD_ASK_PASS_CMD +#define SYSTEMD_ASK_PASS_CMD "systemd-ask-password" +#endif /* SYSTEMD_ASK_PASS_CMD */ + /* * Removes a dataset from a pool including * all children datasets.