[systemd-devel] [PATCH 4/4] Add binary password agent protocol
David Härdeman
david at hardeman.nu
Mon Feb 3 15:57:46 PST 2014
Add binary string handling functions and extend the password agent
protocol to support binary strings (using "=" as a string prefix
instead of "+").
---
Makefile.am | 2
src/ask-password/ask-password.c | 33 +--
src/cryptsetup/cryptsetup.c | 60 +++---
src/shared/ask-password-api.c | 66 +++----
src/shared/ask-password-api.h | 7 -
src/shared/bstrv.c | 194 ++++++++++++++++++++
src/shared/bstrv.h | 50 +++++
.../tty-ask-password-agent.c | 11 +
8 files changed, 325 insertions(+), 98 deletions(-)
create mode 100644 src/shared/bstrv.c
create mode 100644 src/shared/bstrv.h
diff --git a/Makefile.am b/Makefile.am
index 84e3b35..eb01399 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -692,6 +692,8 @@ libsystemd_shared_la_SOURCES = \
src/shared/sleep-config.h \
src/shared/strv.c \
src/shared/strv.h \
+ src/shared/bstrv.c \
+ src/shared/bstrv.h \
src/shared/env-util.c \
src/shared/env-util.h \
src/shared/strbuf.c \
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index ca337b6..01b9905 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -39,6 +39,7 @@
#include "macro.h"
#include "util.h"
#include "strv.h"
+#include "bstrv.h"
#include "ask-password-api.h"
#include "def.h"
@@ -155,6 +156,7 @@ static int parse_argv(int argc, char *argv[]) {
int main(int argc, char *argv[]) {
int r;
usec_t timeout;
+ _cleanup_bstrv_free_ bstr **passwords = NULL;
log_parse_environment();
log_open();
@@ -167,28 +169,19 @@ int main(int argc, char *argv[]) {
else
timeout = 0;
- if (arg_use_tty && isatty(STDIN_FILENO)) {
- char *password = NULL;
-
- if ((r = ask_password_tty(arg_message, timeout, NULL, &password)) >= 0) {
- puts(password);
- free(password);
- }
-
- } else {
- char **l;
-
- if ((r = ask_password_agent(arg_message, arg_icon, arg_purpose, arg_target, timeout, arg_accept_cached, &l)) >= 0) {
- char **p;
-
- STRV_FOREACH(p, l) {
- puts(*p);
+ if (arg_use_tty && isatty(STDIN_FILENO))
+ r = ask_password_tty(arg_message, timeout, NULL, &passwords);
+ else
+ r = ask_password_agent(arg_message, arg_icon, arg_purpose, arg_target, timeout, arg_accept_cached, &passwords);
- if (!arg_multiple)
- break;
- }
+ if (r >= 0) {
+ bstr **p;
- strv_free(l);
+ BSTRV_FOREACH(p, passwords) {
+ fwrite(bstr_data(*p), bstr_length(*p), 1, stdout);
+ putchar('\n');
+ if (!arg_multiple)
+ break;
}
}
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index c01ed01..686132f 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -31,6 +31,7 @@
#include "util.h"
#include "path-util.h"
#include "strv.h"
+#include "bstrv.h"
#include "ask-password-api.h"
#include "def.h"
#include "libudev.h"
@@ -256,9 +257,8 @@ static char *disk_mount_point(const char *label) {
return NULL;
}
-static int get_password(const char *name, usec_t until, bool accept_cached, char ***passwords) {
+static int get_password(const char *name, usec_t until, bool accept_cached, bstr ***passwords) {
int r;
- char **p;
_cleanup_free_ char *text = NULL;
assert(name);
@@ -274,9 +274,9 @@ static int get_password(const char *name, usec_t until, bool accept_cached, char
}
if (opt_verify) {
- _cleanup_strv_free_ char **passwords2 = NULL;
+ _cleanup_bstrv_free_ bstr **passwords2 = NULL;
- assert(strv_length(*passwords) == 1);
+ assert(bstrv_length(*passwords) == 1);
if (asprintf(&text, "Please enter passphrase for disk %s! (verification)", name) < 0)
return log_oom();
@@ -287,30 +287,15 @@ static int get_password(const char *name, usec_t until, bool accept_cached, char
return r;
}
- assert(strv_length(passwords2) == 1);
+ assert(bstrv_length(passwords2) == 1);
- if (!streq(*passwords[0], passwords2[0])) {
+ if (!bstreq(*passwords[0], passwords2[0])) {
log_warning("Passwords did not match, retrying.");
return -EAGAIN;
}
}
- strv_uniq(*passwords);
-
- STRV_FOREACH(p, *passwords) {
- char *c;
-
- if (strlen(*p)+1 >= opt_key_size)
- continue;
-
- /* Pad password if necessary */
- if (!(c = new(char, opt_key_size)))
- return log_oom();
-
- strncpy(c, *p, opt_key_size);
- free(*p);
- *p = c;
- }
+ bstrv_uniq(*passwords);
return 0;
}
@@ -318,7 +303,7 @@ static int get_password(const char *name, usec_t until, bool accept_cached, char
static int attach_tcrypt(struct crypt_device *cd,
const char *name,
const char *key_file,
- char **passwords,
+ bstr **passwords,
uint32_t flags) {
int r = 0;
_cleanup_free_ char *passphrase = NULL;
@@ -346,9 +331,11 @@ static int attach_tcrypt(struct crypt_device *cd,
}
params.passphrase = passphrase;
- } else
- params.passphrase = passwords[0];
- params.passphrase_size = strlen(params.passphrase);
+ params.passphrase_size = strlen(passphrase);
+ } else {
+ params.passphrase = bstr_cdata(passwords[0]);
+ params.passphrase_size = bstr_length(passwords[0]);
+ }
r = crypt_load(cd, CRYPT_TCRYPT, ¶ms);
if (r < 0) {
@@ -365,7 +352,7 @@ static int attach_tcrypt(struct crypt_device *cd,
static int attach_luks_or_plain(struct crypt_device *cd,
const char *name,
const char *key_file,
- char **passwords,
+ bstr **passwords,
uint32_t flags) {
int r = 0;
bool pass_volume_key = false;
@@ -442,13 +429,18 @@ static int attach_luks_or_plain(struct crypt_device *cd,
return -EAGAIN;
}
} else {
- char **p;
+ bstr **p;
- STRV_FOREACH(p, passwords) {
- if (pass_volume_key)
- r = crypt_activate_by_volume_key(cd, name, *p, opt_key_size, flags);
- else
- r = crypt_activate_by_passphrase(cd, name, opt_key_slot, *p, strlen(*p), flags);
+ BSTRV_FOREACH(p, passwords) {
+ if (pass_volume_key) {
+ uint8_t key[opt_key_size];
+ unsigned l = MIN(bstr_length(*p), opt_key_size);
+
+ memcpy(key, bstr_data(*p), l);
+ memzero(key + l, opt_key_size - l);
+ r = crypt_activate_by_volume_key(cd, name, bstr_cdata(*p), sizeof(key), flags);
+ } else
+ r = crypt_activate_by_passphrase(cd, name, opt_key_slot, bstr_cdata(*p), bstr_length(*p), flags);
if (r >= 0)
break;
@@ -586,7 +578,7 @@ int main(int argc, char *argv[]) {
}
for (tries = 0; opt_tries == 0 || tries < opt_tries; tries++) {
- _cleanup_strv_free_ char **passwords = NULL;
+ _cleanup_bstrv_free_ bstr **passwords = NULL;
if (!key_file) {
k = get_password(name, until, tries == 0 && !opt_verify, &passwords);
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 499ec84..f98bea9 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -34,6 +34,7 @@
#include "util.h"
#include "mkdir.h"
#include "strv.h"
+#include "bstrv.h"
#include "ask-password-api.h"
@@ -53,10 +54,10 @@ int ask_password_tty(
const char *message,
usec_t until,
const char *flag_file,
- char **_passphrase) {
+ bstr ***_passphrases) {
struct termios old_termios, new_termios;
- char passphrase[LINE_MAX];
+ uint8_t passphrase[LINE_MAX];
size_t p = 0;
int r, ttyfd = -1, notify = -1;
struct pollfd pollfd[2];
@@ -68,8 +69,8 @@ int ask_password_tty(
POLL_INOTIFY
};
+ assert(_passphrases);
assert(message);
- assert(_passphrase);
if (flag_file) {
if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
@@ -214,16 +215,14 @@ int ask_password_tty(
dirty = true;
- if (p >= (sizeof(passphrase) - 1)) {
- loop_write(ttyfd, "\n", 1, false);
- break;
- }
+ if (p >= sizeof(passphrase)) {
+ loop_write(ttyfd, "\n", 1, false);
+ break;
+ }
}
}
- passphrase[p] = 0;
-
- if (!(*_passphrase = strdup(passphrase))) {
+ if (bstrv_push(_passphrases, passphrase, p) < 0) {
r = -ENOMEM;
goto finish;
}
@@ -307,7 +306,7 @@ int ask_password_agent(
const char *target,
usec_t until,
bool accept_cached,
- char ***_passphrases) {
+ bstr ***_passphrases) {
enum {
FD_SOCKET,
@@ -410,7 +409,7 @@ int ask_password_agent(
pollfd[FD_SIGNAL].events = POLLIN;
for (;;) {
- char passphrase[LINE_MAX+1];
+ uint8_t passphrase[LINE_MAX+1];
struct msghdr msghdr;
struct iovec iovec;
struct ucred *ucred;
@@ -499,12 +498,12 @@ int ask_password_agent(
}
if (passphrase[0] == '+') {
- char **l;
+ bstr **l;
if (n == 1)
- l = strv_new("", NULL);
+ l = bstrv_new((uint8_t *)"", 1);
else
- l = strv_parse_nulstr(passphrase+1, n-1);
+ l = bstrv_parse_nulstr(passphrase+1, n-1);
/* An empty message refers to the empty password */
if (!l) {
@@ -512,14 +511,25 @@ int ask_password_agent(
goto finish;
}
- if (strv_length(l) <= 0) {
- strv_free(l);
+ if (bstrv_length(l) <= 0) {
+ bstrv_free(l);
log_error("Invalid packet");
continue;
}
*_passphrases = l;
+ } else if (passphrase[0] == '=') {
+ bstr **l;
+
+ l = bstrv_new(passphrase + 1, n - 1);
+ if (!l) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ *_passphrases = l;
+
} else if (passphrase[0] == '-') {
r = -ECANCELED;
goto finish;
@@ -562,26 +572,12 @@ finish:
}
int ask_password_auto(const char *message, const char *icon, const char *purpose, const char *target,
- usec_t until, bool accept_cached, char ***_passphrases) {
+ usec_t until, bool accept_cached, bstr ***_passphrases) {
assert(message);
assert(_passphrases);
- if (isatty(STDIN_FILENO)) {
- int r;
- char *s = NULL, **l = NULL;
-
- if ((r = ask_password_tty(message, until, NULL, &s)) < 0)
- return r;
-
- l = strv_new(s, NULL);
- free(s);
-
- if (!l)
- return -ENOMEM;
-
- *_passphrases = l;
- return r;
-
- } else
+ if (isatty(STDIN_FILENO))
+ return ask_password_tty(message, until, NULL, _passphrases);
+ else
return ask_password_agent(message, icon, purpose, target, until, accept_cached, _passphrases);
}
diff --git a/src/shared/ask-password-api.h b/src/shared/ask-password-api.h
index d85d18e..1a413ac 100644
--- a/src/shared/ask-password-api.h
+++ b/src/shared/ask-password-api.h
@@ -21,12 +21,13 @@
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "bstrv.h"
#include "util.h"
-int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase);
+int ask_password_tty(const char *message, usec_t until, const char *flag_file, bstr ***_passphrases);
int ask_password_agent(const char *message, const char *icon, const char *purpose, const char *target,
- usec_t until, bool accept_cached, char ***_passphrases);
+ usec_t until, bool accept_cached, bstr ***_passphrases);
int ask_password_auto(const char *message, const char *icon, const char *purpose, const char *target,
- usec_t until, bool accept_cached, char ***_passphrases);
+ usec_t until, bool accept_cached, bstr ***_passphrases);
diff --git a/src/shared/bstrv.c b/src/shared/bstrv.c
new file mode 100644
index 0000000..aaa395d
--- /dev/null
+++ b/src/shared/bstrv.c
@@ -0,0 +1,194 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Härdeman <david at hardeman.nu>
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+
+#include "util.h"
+#include "strv.h"
+#include "bstrv.h"
+
+struct bstr {
+ size_t len;
+ uint8_t data[];
+};
+
+void bstrv_free(bstr **l) {
+ bstr **k;
+
+ if (!l)
+ return;
+
+ for (k = l; *k; k++) {
+ memzero((*k)->data, (*k)->len);
+ free(*k);
+ }
+
+ printf("Freeing %p\n", l);
+ free(l);
+}
+
+bstr **bstrv_new(const uint8_t *data, size_t len) {
+ size_t n = 1;
+ bstr **a;
+
+ if (data && len > 0)
+ n++;
+
+ a = new0(bstr *, n);
+ if (!a)
+ return NULL;
+
+ if (data && len > 0) {
+ a[0] = malloc(sizeof(bstr) + len);
+ if (!a[0]) {
+ free(a);
+ return NULL;
+ }
+ a[0]->len = len;
+ memcpy(a[0]->data, data, len);
+ }
+
+ return a;
+}
+
+bstr **bstrv_remove(bstr **l, const uint8_t *data, size_t len) {
+ bstr **f, **t;
+
+ if (!l)
+ return NULL;
+
+ assert(len > 0);
+ assert(data);
+
+ for (f = t = l; *f; f++) {
+ if ((*f)->len == len &&
+ memcmp((*f)->data, data, len) == 0)
+ free(*f);
+ else
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
+bstr **bstrv_uniq(bstr **l) {
+ bstr **i;
+
+ /* Drops duplicate entries. The first identical binary string will be
+ * kept, the others dropped */
+
+ BSTRV_FOREACH(i, l)
+ bstrv_remove(i + 1, (*i)->data, (*i)->len);
+
+ return l;
+}
+
+int bstrv_push(bstr ***l, const uint8_t *data, size_t len) {
+ bstr **c;
+ bstr *k;
+ size_t n;
+
+ if (!data || len < 1)
+ return 0;
+
+ k = malloc(sizeof(*k));
+ if (!k)
+ return -ENOMEM;
+ printf("Allocated new bstr %p\n", k);
+
+ n = bstrv_length(*l);
+ printf("bstrv_length is %zu, reallocing\n", n);
+ c = realloc(*l, sizeof(bstr *) * (n + 2));
+ if (!c) {
+ free(k);
+ return -ENOMEM;
+ }
+ printf("bstrv is now %p\n", c);
+ k->len = len;
+ memcpy(k->data, data, len);
+
+ c[n] = k;
+ c[n + 1] = NULL;
+
+ *l = c;
+ return 0;
+}
+
+bstr **bstrv_parse_nulstr(const uint8_t *s, size_t l) {
+ _cleanup_strv_free_ char **v = NULL;
+ char **k;
+ bstr **b = NULL;
+
+ v = strv_parse_nulstr((const char *)s, l);
+ if (!v)
+ return NULL;
+
+ STRV_FOREACH(k, v) {
+ if (bstrv_push(&b, (const uint8_t *)*k, strlen(*k)) < 0) {
+ bstrv_free(b);
+ return NULL;
+ }
+ }
+
+ return b;
+}
+
+unsigned bstrv_length(bstr * const *l) {
+ unsigned n = 0;
+
+ if (!l)
+ return 0;
+
+ for (; *l; l++)
+ n++;
+
+ return n;
+}
+
+unsigned bstr_length(const bstr *l) {
+ if (l)
+ return l->len;
+ return 0;
+}
+
+uint8_t *bstr_data(bstr *l) {
+ if (l)
+ return l->data;
+ return NULL;
+}
+
+char *bstr_cdata(bstr *l) {
+ return (char *)bstr_data(l);
+}
+
+bool bstreq(const bstr *a, const bstr *b) {
+ if (!a || !b)
+ return false;
+ if (a->len != b->len)
+ return false;
+ return memcmp(a->data, b->data, a->len) == 0;
+}
+
diff --git a/src/shared/bstrv.h b/src/shared/bstrv.h
new file mode 100644
index 0000000..e08c215
--- /dev/null
+++ b/src/shared/bstrv.h
@@ -0,0 +1,50 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#pragma once
+
+/***
+ This file is part of systemd.
+
+ Copyright 2014 David Härdeman
+
+ systemd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ systemd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "util.h"
+
+typedef struct bstr bstr;
+
+void bstrv_free(bstr **l);
+DEFINE_TRIVIAL_CLEANUP_FUNC(bstr **, bstrv_free);
+#define _cleanup_bstrv_free_ _cleanup_(bstrv_freep)
+
+bstr **bstrv_new(const uint8_t *data, size_t len);
+bstr **bstrv_remove(bstr **l, const uint8_t *data, size_t len);
+bstr **bstrv_uniq(bstr **l);
+int bstrv_push(bstr ***l, const uint8_t *data, size_t len);
+bstr **bstrv_parse_nulstr(const uint8_t *s, size_t l);
+
+unsigned bstrv_length(bstr * const *l) _pure_;
+
+unsigned bstr_length(const bstr *l) _pure_;
+uint8_t *bstr_data(bstr *l);
+char *bstr_cdata(bstr *l);
+bool bstreq(const bstr *a, const bstr *b) _pure_;
+
+#define BSTRV_FOREACH(s, l) \
+ for ((s) = (l); (s) && *(s); (s)++)
+
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
index c0451c0..79630d2 100644
--- a/src/tty-ask-password-agent/tty-ask-password-agent.c
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -40,6 +40,7 @@
#include "socket-util.h"
#include "ask-password-api.h"
#include "strv.h"
+#include "bstrv.h"
#include "build.h"
static enum {
@@ -363,7 +364,7 @@ static int parse_password(const char *filename, char **wall) {
} else {
int tty_fd = -1;
- char *password;
+ _cleanup_bstrv_free_ bstr **passwords = NULL;
if (arg_console)
if ((tty_fd = acquire_terminal("/dev/console", false, false, false, (usec_t) -1)) < 0) {
@@ -371,7 +372,7 @@ static int parse_password(const char *filename, char **wall) {
goto finish;
}
- r = ask_password_tty(message, not_after, filename, &password);
+ r = ask_password_tty(message, not_after, filename, &passwords);
if (arg_console) {
close_nointr_nofail(tty_fd);
@@ -379,15 +380,13 @@ static int parse_password(const char *filename, char **wall) {
}
if (r >= 0) {
- packet_length = 1+strlen(password)+1;
+ packet_length = 1+bstr_length(passwords[0])+1;
if (!(packet = new(char, packet_length)))
r = -ENOMEM;
else {
packet[0] = '+';
- strcpy(packet+1, password);
+ memcpy(packet+1, bstr_data(passwords[0]), bstr_length(passwords[0]));
}
-
- free(password);
}
}
More information about the systemd-devel
mailing list