Created
January 8, 2016 13:22
-
-
Save chris-se/9c0def7dca60d023d188 to your computer and use it in GitHub Desktop.
systemd generator for the use with keyscript=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Parts of this code is based on systemd's cryptsetup-generator.c. | |
* (under LGPLv2.1+, see https://github.com/systemd/systemd/) | |
*/ | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <errno.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include "sd-functions.h" | |
#ifndef KEYSCRIPT_CRYPTSETUP_PATH | |
#define KEYSCRIPT_CRYPTSETUP_PATH "/lib/cryptsetup/systemd-keyscript-cryptsetup" | |
#endif | |
#ifndef CRYPTTAB_FILE | |
#define CRYPTTAB_FILE "/etc/crypttab" | |
#endif | |
static char *to_device_node(const char *name) | |
{ | |
const char *tag = NULL; | |
char *buf; | |
size_t len; | |
if (strncmp(name, "LABEL=", 6) == 0) { | |
tag = "label"; | |
name += 6; | |
} else if (strncmp(name, "UUID=", 5) == 0) { | |
tag = "uuid"; | |
name += 5; | |
} else if (strncmp(name, "PARTLABEL=", 10) == 0) { | |
tag = "partlabel"; | |
name += 10; | |
} else if (strncmp(name, "PARTUUID=", 9) == 0) { | |
tag = "partuuid"; | |
name += 9; | |
} else { | |
return strdup(name); | |
} | |
len = strlen("/dev/disk/by-") + strlen(tag) + 1 + strlen(name) + 1; | |
buf = calloc(1, len); | |
snprintf(buf, len, "/dev/disk/by-%s/%s", tag, name); | |
return buf; | |
} | |
static void write_keyscript_helper(const char *basepath, char *name, char *device_name, char *password, char *options, char *keyscript) | |
{ | |
FILE *f; | |
char *escaped_name; | |
char *dropin_path_name; | |
char *ptr; | |
char *filtered_options; | |
char *token, *saveptr; | |
const char *unit_format = "%s/systemd-cryptsetup@%s.service.d/keyscript.conf"; | |
size_t len; | |
int r; | |
filtered_options = malloc(strlen(options) + 1); | |
if (!filtered_options) { | |
/* FIXME: OOM */ | |
return; | |
} | |
/* systemd's generator filters this, so so must we */ | |
ptr = filtered_options; | |
for (token = strtok_r(options ? options : "", ",", &saveptr); token != NULL; token = strtok_r(NULL, ",", &saveptr)) { | |
if (strncmp(token, "x-systemd.device-timeout=", 25) == 0 || strncmp(token, "comment=systemd.device-timeout=", 31) == 0) | |
continue; | |
if (ptr != filtered_options) | |
*ptr++ = ','; | |
strcpy(ptr, token); | |
ptr += strlen(token); | |
} | |
*ptr = '\0'; | |
escaped_name = unit_name_escape(name); | |
if (!escaped_name) { | |
/* FIXME: OOM */ | |
return; | |
} | |
len = strlen(unit_format) - 4 + strlen(basepath) + strlen(escaped_name) + 1; | |
dropin_path_name = calloc(1, len); | |
if (!dropin_path_name) { | |
/* FIXME: OOM */ | |
return; | |
} | |
snprintf(dropin_path_name, len, unit_format, basepath, escaped_name); | |
free(escaped_name); | |
escaped_name = NULL; | |
/* Create directory */ | |
ptr = strrchr(dropin_path_name, '/'); | |
*ptr = '\0'; | |
r = mkdir(dropin_path_name, 0755); | |
if (r < 0 && errno != EEXIST) { | |
/* FIXME: error handling */ | |
return; | |
} | |
*ptr = '/'; | |
/* Create file, but we want to make sure it's not world-readable */ | |
umask(0077); | |
f = fopen(dropin_path_name, "w"); | |
if (!f) { | |
/* FIXME: error handling */ | |
umask(0022); | |
return; | |
} | |
fprintf(f, "[Service]\n" | |
"ExecStart=\n" | |
"ExecStart=" KEYSCRIPT_CRYPTSETUP_PATH " '%s' '%s' '%s' '%s' '%s'\n", | |
name, device_name, password ? password : "", filtered_options, keyscript); | |
fclose(f); | |
free(dropin_path_name); | |
umask(0022); | |
} | |
int main(int argc, char **argv) | |
{ | |
FILE *f; | |
const char *basepath = "/tmp"; | |
umask(0022); | |
if (argc > 1 && argc != 4) { | |
fprintf(stderr, "This program takes three or no arguments.\n"); | |
return 1; | |
} | |
if (argc > 1) | |
basepath = argv[1]; | |
f = fopen(CRYPTTAB_FILE, "r"); | |
if (!f) | |
return 0; | |
for (;;) { | |
char line[2048]; | |
char *token, *saveptr, *l, *keyscript; | |
int k; | |
char *name = NULL, *device = NULL, *password = NULL, *options = NULL, *device_name, *p_options; | |
if (!fgets(line, sizeof(line), f)) | |
break; | |
l = strstrip(line); | |
if (*l == '#' || *l == '\0') | |
continue; | |
k = sscanf(l, "%ms %ms %ms %ms", &name, &device, &password, &options); | |
if (k < 2 || k > 4) { | |
free(name); | |
free(device); | |
free(password); | |
free(options); | |
continue; | |
} | |
device_name = to_device_node(device); | |
if (!device_name) { | |
/* FIXME: out of memory */ | |
free(name); | |
free(device); | |
free(password); | |
free(options); | |
continue; | |
} | |
/* don't need that anymore */ | |
free(device); | |
device = NULL; | |
/* FIXME: cryptsetup-generator.c also parsers /proc/cmdline and looks for | |
* UUID-specific options - currently that isn't supported */ | |
keyscript = NULL; | |
p_options = strdup(options); | |
if (!p_options) { | |
/* FIXME: out of memory */ | |
free(name); | |
free(device_name); | |
free(password); | |
free(options); | |
continue; | |
} | |
for (token = strtok_r(p_options, ",", &saveptr); token != NULL; token = strtok_r(NULL, ",", &saveptr)) { | |
if (strncmp(token, "keyscript=", 10) == 0) { | |
keyscript = token + 10; | |
} | |
} | |
if (keyscript) | |
write_keyscript_helper(basepath, name, device_name, password, options, keyscript); | |
free(name); | |
free(device_name); | |
free(password); | |
free(options); | |
free(p_options); | |
} | |
fclose(f); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CC=gcc | |
CFLAGS=-Wall -Wextra -O2 -ggdb | |
#CPPFLAGS=-DCRYPTTAB_FILE=\"/tmp/crypttab\" | |
all: keyscript-generator systemd-keyscript-cryptsetup | |
keyscript-generator: keyscript-generator.o sd-functions.o | |
systemd-keyscript-cryptsetup: systemd-keyscript-cryptsetup.o | |
clean: | |
rm -f keyscript-generator systemd-keyscript-cryptsetup *.o *~ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This code is from systemd source, slightly modified. | |
* | |
* Look at src/shared/unit-name.c and src/shared/util.c for the | |
* original code. | |
* | |
* Reference: https://github.com/systemd/systemd/ | |
* | |
* License: LGPLv2.1+ | |
*/ | |
#include <assert.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#define DIGITS "0123456789" | |
#define LOWERCASE_LETTERS "abcdefghijklmnopqrstuvwxyz" | |
#define UPPERCASE_LETTERS "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
#define LETTERS LOWERCASE_LETTERS UPPERCASE_LETTERS | |
#define WHITESPACE " \t\r\n" | |
#define VALID_CHARS \ | |
DIGITS LETTERS \ | |
":-_.\\" | |
char hexchar(int x) { | |
static const char table[16] = "0123456789abcdef"; | |
return table[x & 15]; | |
} | |
static char *do_escape_char(char c, char *t) { | |
assert(t); | |
*(t++) = '\\'; | |
*(t++) = 'x'; | |
*(t++) = hexchar(c >> 4); | |
*(t++) = hexchar(c); | |
return t; | |
} | |
static char *do_escape(const char *f, char *t) { | |
assert(f); | |
assert(t); | |
/* do not create units with a leading '.', like for "/.dotdir" mount points */ | |
if (*f == '.') { | |
t = do_escape_char(*f, t); | |
f++; | |
} | |
for (; *f; f++) { | |
if (*f == '/') | |
*(t++) = '-'; | |
else if (*f == '-' || *f == '\\' || !strchr(VALID_CHARS, *f)) | |
t = do_escape_char(*f, t); | |
else | |
*(t++) = *f; | |
} | |
return t; | |
} | |
char *unit_name_escape(const char *f) { | |
char *r, *t; | |
assert(f); | |
r = calloc(1, strlen(f)*4+1); | |
if (!r) | |
return NULL; | |
t = do_escape(f, r); | |
*t = 0; | |
return r; | |
} | |
char *strstrip(char *s) { | |
char *e; | |
/* Drops trailing whitespace. Modifies the string in | |
* place. Returns pointer to first non-space character */ | |
s += strspn(s, WHITESPACE); | |
for (e = strchr(s, 0); e > s; e --) | |
if (!strchr(WHITESPACE, e[-1])) | |
break; | |
*e = 0; | |
return s; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef SD_FUNCTIONS_H | |
#define SD_FUNCTIONS_H | |
char *strstrip(char *s); | |
char *unit_name_escape(const char *f); | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <errno.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#ifndef SYSTEMD_CRYPTSETUP_PATH | |
#define SYSTEMD_CRYPTSETUP_PATH "/lib/systemd/systemd-cryptsetup" | |
#endif | |
extern char **environ; | |
static void freev(char **p) | |
{ | |
char **ptr; | |
if (!p) | |
return; | |
for (ptr = p; *ptr; ptr++) | |
free(*ptr); | |
free(p); | |
} | |
static char *concat(const char *a, const char *b) | |
{ | |
char *p = malloc(strlen(a) + strlen(b) + 1); | |
if (!p) | |
return NULL; | |
snprintf(p, strlen(a) + strlen(b) + 1, "%s%s", a, b); | |
return p; | |
} | |
static char *concat_paths(const char *a, const char *b) | |
{ | |
char *p = malloc(strlen(a) + strlen(b) + 2); | |
if (!p) | |
return NULL; | |
snprintf(p, strlen(a) + strlen(b) + 2, "%s%s%s", a, a[strlen(a) - 1] == '/' ? "" : "/", b); | |
return p; | |
} | |
static char **get_script_environment(const char *name, const char *device, const char *password, const char *options) | |
{ | |
char **ptr; | |
char **result; | |
char *p_options; | |
char *token, *saveptr; | |
char *val; | |
size_t i; | |
p_options = strdup(options); | |
if (!p_options) | |
return NULL; | |
/* Count cryptsetup options + other environment variables */ | |
for (i = 0, ptr = environ; *ptr; ptr++, i++); | |
if (strlen(options)) | |
i++; | |
for (val = strchr(options, ','); val; val = strchr(val + 1, ','), i++); | |
i += 5; | |
result = calloc(sizeof(char *), i + 1); | |
if (!result) { | |
free(p_options); | |
return NULL; | |
} | |
i = 0; | |
for (ptr = environ; *ptr; ptr++) { | |
char *p = strdup(*ptr); | |
if (!p) { | |
freev(result); | |
free(p_options); | |
return NULL; | |
} | |
result[i++] = p; | |
} | |
for (token = strtok_r(p_options, ",", &saveptr); token != NULL; token = strtok_r(NULL, ",", &saveptr)) { | |
val = malloc(strlen("CRYPTTAB_OPTION_") + strlen(token) + 4 + 1); | |
if (!val) { | |
freev(result); | |
free(p_options); | |
return NULL; | |
} | |
snprintf(val, strlen("CRYPTTAB_OPTION_") + strlen(token) + 4 + 1, "CRYPTTAB_OPTION_%s%s", token, strchr(token, '=') ? "" : "=yes"); | |
result[i++] = val; | |
} | |
free(p_options); | |
p_options = NULL; | |
val = concat("CRYPTTAB_OPTIONS=", options); | |
if (!val) { | |
freev(result); | |
return NULL; | |
} | |
result[i++] = val; | |
val = concat("CRYPTTAB_NAME=", name); | |
if (!val) { | |
freev(result); | |
return NULL; | |
} | |
result[i++] = val; | |
val = concat("CRYPTTAB_SOURCE=", device); | |
if (!val) { | |
freev(result); | |
return NULL; | |
} | |
result[i++] = val; | |
val = concat("CRYPTTAB_KEY=", password); | |
if (!val) { | |
freev(result); | |
return NULL; | |
} | |
result[i++] = val; | |
val = strdup("CRYPTTAB_TRIED=0"); | |
if (!val) { | |
freev(result); | |
return NULL; | |
} | |
result[i++] = val; | |
return result; | |
} | |
static char *get_system_path(void) | |
{ | |
char *buf = getenv("PATH"); | |
size_t len; | |
if (buf) | |
return strdup(buf); | |
len = confstr(_CS_PATH, NULL, (size_t) 0); | |
buf = malloc(len); | |
if (!buf) | |
return NULL; | |
(void) confstr(_CS_PATH, buf, len); | |
return buf; | |
} | |
static char *find_in_path(const char *additional_path, const char *executable) | |
{ | |
char *system_path = get_system_path(); | |
char *buf = NULL; | |
char *token, *saveptr = NULL; | |
buf = concat_paths(additional_path, executable); | |
if (access(buf, X_OK) == 0) | |
return buf; | |
free(buf); | |
buf = NULL; | |
for (token = strtok_r(system_path, ":", &saveptr); token != NULL; token = strtok_r(NULL, ":", &saveptr)) { | |
buf = concat_paths(token, executable); | |
if (access(buf, X_OK) == 0) | |
return buf; | |
free(buf); | |
} | |
return NULL; | |
} | |
int main(int argc, char **argv) | |
{ | |
pid_t pid; | |
int pipefd[2]; | |
int r; | |
const char *name; | |
const char *device; | |
const char *keyfile; | |
const char *options; | |
const char *keyscript; | |
const char *real_keyscript; | |
char **script_environment; | |
if (argc != 6) { | |
fprintf(stderr, "%s: may only be called with 5 arguments\n", argv[0]); | |
return 1; | |
} | |
name = argv[1]; | |
device = argv[2]; | |
keyfile = argv[3]; | |
options = argv[4]; | |
keyscript = argv[5]; | |
if (access(keyscript, X_OK) == 0) { | |
real_keyscript = keyscript; | |
} else { | |
/* not a direct path */ | |
real_keyscript = find_in_path("/lib/cryptsetup/scripts", keyscript); | |
if (!real_keyscript) { | |
fprintf(stderr, "%s: keyscript %s does not exist\n", argv[0], keyscript); | |
return 1; | |
} | |
} | |
script_environment = get_script_environment(name, device, keyfile, options); | |
if (!script_environment) { | |
fprintf(stderr, "%s: memory allocation error\n", argv[0]); | |
return 1; | |
} | |
r = pipe(pipefd); | |
if (r < 0) { | |
fprintf(stderr, "%s: error setting up pipe: %m\n", argv[0]); | |
return 1; | |
} | |
pid = fork(); | |
if (pid < 0) { | |
fprintf(stderr, "%s: fork() failed: %m\n", argv[0]); | |
return 1; | |
} | |
if (pid == 0) { | |
char *script_argv[] = { | |
(char *)keyscript, | |
(char *)keyfile, | |
NULL | |
}; | |
/* child process */ | |
close(pipefd[0]); | |
r = dup2(pipefd[1], 1); | |
if (r < 0) { | |
fprintf(stderr," %s: dup2() failed: %m\n", argv[0]); | |
return 1; | |
} | |
close(pipefd[1]); | |
execve(real_keyscript, script_argv, script_environment); | |
fprintf(stderr,"%s: couldn't execute %s: %m\n", argv[0], real_keyscript); | |
} else { | |
char *cryptsetup_argv[] = { | |
(char *)SYSTEMD_CRYPTSETUP_PATH, | |
(char *)"attach", | |
(char *)name, | |
(char *)device, | |
(char *)"/proc/self/fd/0", | |
(char *)options, | |
NULL | |
}; | |
/* parent process */ | |
close(pipefd[1]); | |
r = dup2(pipefd[0], 0); | |
if (r < 0) { | |
fprintf(stderr," %s: dup2() failed: %m\n", argv[0]); | |
return 1; | |
} | |
close(pipefd[0]); | |
execv(SYSTEMD_CRYPTSETUP_PATH, cryptsetup_argv); | |
fprintf(stderr,"%s: couldn't execute " SYSTEMD_CRYPTSETUP_PATH ": %m\n", argv[0]); | |
} | |
return 1; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment