sesame/tools Makefile.am,1.2,1.3 sesame-setup.c,1.1.1.1,1.2

David Zeuthen david at freedesktop.org
Fri Dec 31 05:35:49 PST 2004


Update of /cvs/hal/sesame/tools
In directory gabe:/tmp/cvs-serv25990/tools

Modified Files:
	Makefile.am sesame-setup.c 
Log Message:
2004-12-31  David Zeuthen  <davidz at redhat.com>

	Patch from W. Michael Petullo <mike at flyn.org>. I wrote a patch for
	the sesame code in CVS that adds a C implementation of
	sesame-setup.  I used OpenSSL's libraries for the crypto because
	this is what I am familiar with.  Cryptsetup is also required.  It
	seems to work.

	* configure.in: Check for openssl lib and cryptsetup
	
	* tools/Makefile.am: pass CRYPTSETUP to be the path of the cryptsetup
	binary
	
	* tools/sesame-setup.c: (print_usage), (sslerror), (hash_authtok),
	(decrypt), (decode), (strip_cr), (read_key), (msg),
	(run_cryptsetup), (main): New functions



Index: Makefile.am
===================================================================
RCS file: /cvs/hal/sesame/tools/Makefile.am,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -d -r1.2 -r1.3
--- Makefile.am	17 Dec 2004 17:02:42 -0000	1.2
+++ Makefile.am	31 Dec 2004 13:35:47 -0000	1.3
@@ -1,9 +1,9 @@
 
 INCLUDES = \
+	-DCRYPTSETUP=\""$(CRYPTSETUP)\"" \
 	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
 	-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
-	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
-	@PACKAGE_CFLAGS@
+	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\"
 
 sbin_PROGRAMS = sesame-setup
 

Index: sesame-setup.c
===================================================================
RCS file: /cvs/hal/sesame/tools/sesame-setup.c,v
retrieving revision 1.1.1.1
retrieving revision 1.2
diff -u -d -r1.1.1.1 -r1.2
--- sesame-setup.c	17 Dec 2004 16:56:52 -0000	1.1.1.1
+++ sesame-setup.c	31 Dec 2004 13:35:47 -0000	1.2
@@ -1,46 +1,409 @@
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <limits.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <openssl/ssl.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+#ifndef EVP_MAX_BLOCK_LENGTH
+#define EVP_MAX_BLOCK_LENGTH 32	/* some older openssl versions need this */
+#endif
 
 #include "../libsesame/libsesame.h"
 
-int 
-main (int argc, char *argv[])
+#define MAGICTAIL "SESAME0"
+
+int verbose = 0;
+char *device = NULL;
+
+static void print_usage(const int exitcode, const char *error,
+			const char *more)
 {
-	char md_file[] = "/dev/sda1";
-	int fd_metadata;
+	if (error)
+		assert(more);
+
+	fprintf(stderr, "sesame-setup [options] device\n\n"
+		"-h, --help\n"
+		"	print a list of options\n\n"
+		"-v, --verbose\n" "	verbose display of messages\n\n");
+	if (error)
+		fprintf(stderr, "%s: %s\n", error, more);
+
+	exit(exitcode);
+}
+
+static void sslerror(const char *msg)
+{
+	assert(msg);
+
+	unsigned long err = ERR_get_error();
+	if (err != 0)
+		fprintf(stderr, "%s: %s", msg,
+			ERR_error_string(err, NULL));
+}
+
+static int hash_authtok(const char *data, const EVP_CIPHER * const cipher,
+			const char *const authtok,
+			unsigned char *const hash, unsigned char *const iv)
+{
+	const EVP_MD *md;
+	unsigned char salt[PKCS5_SALT_LEN];
+
+	assert(data != NULL);
+	assert(cipher != NULL);	/* FIXME: is cipher is valid OpenSSL cipher? */
+	assert(authtok != NULL);
+	assert(hash != NULL);	/* FIXME: check hash is big enough? */
+	assert(iv != NULL);	/* FIXME: check iv is big enough? */
+
+	if (memcmp(data, "Salted__", sizeof "Salted__" - 1) != 0) {
+		fprintf(stderr, "magic string Salted__ not in stream\n");
+		return 0;
+	}
+	memcpy(salt, data + sizeof "Salted__" - 1, PKCS5_SALT_LEN);
+	md = EVP_md5();
+	if (EVP_BytesToKey
+	    (cipher, md, salt, authtok, strlen(authtok), 1,
+	     hash, iv) <= 0) {
+		fprintf(stderr, "failed to hash passphrase");
+		return 0;
+	}
+
+	return 1;
+}
+
+static int
+decrypt(char *const out, size_t * const out_len,
+	const char *const in, const size_t in_len,
+	const char *const cipher_name, const char *const authtok)
+{
+	int ret = 1;
+	int segment_len;
+	size_t data_len;
+	const char *data;
+	unsigned char hashed_authtok[EVP_MAX_KEY_LENGTH];
+	unsigned char iv[EVP_MAX_IV_LENGTH];
+	const EVP_CIPHER *cipher;
+	EVP_CIPHER_CTX ctx;
+
+	assert(out != NULL);
+	assert(out_len != NULL);
+	assert(cipher_name != NULL);
+	assert(in != NULL);
+	assert(authtok != NULL);
+
+	memset(out, 0x00, BUFSIZ + EVP_MAX_BLOCK_LENGTH);
+	OpenSSL_add_all_ciphers();
+	EVP_CIPHER_CTX_init(&ctx);
+	SSL_load_error_strings();
+	if (!(cipher = EVP_get_cipherbyname(cipher_name))) {
+		fprintf(stderr, "error getting cipher \"%s\"\n", (const char *)cipher);
+		ret = 0;
+		goto _return;
+	}
+	if (hash_authtok(in, cipher, authtok, hashed_authtok, iv) == 0) {
+		ret = 0;
+		goto _return;
+	}
+	if (EVP_DecryptInit_ex(&ctx, cipher, NULL, hashed_authtok, iv) ==
+	    0) {
+		sslerror("failed to initialize decryption code");
+		ret = 0;
+		goto _return;
+	}
+	data = in + (sizeof "Salted__" - 1) + PKCS5_SALT_LEN;
+	data_len = in_len - (sizeof "Salted__" - 1) - PKCS5_SALT_LEN;
+	/* assumes plaintexts is always <= ciphertext + EVP_MAX_BLOCK_LEN in length
+	 * OpenSSL's documentation seems to promise this */
+	if (EVP_DecryptUpdate
+	    (&ctx, out, &segment_len, data, data_len) == 0) {
+		sslerror("failed to decrypt key");
+		ret = 0;
+		goto _return;
+	}
+	*out_len = segment_len;
+	if (EVP_DecryptFinal_ex(&ctx, &out[*out_len], &segment_len) == 0) {
+		sslerror
+		    ("bad pad on end of encrypted file (wrong algorithm or key size?)");
+		ret = 0;
+		goto _return;
+	}
+	*out_len += segment_len;
+      _return:
+	if (EVP_CIPHER_CTX_cleanup(&ctx) == 0) {
+		sslerror("error cleaning up cipher context");
+		ret = 0;
+	}
+
+	ERR_free_strings();
+	/* out_len is unsigned */
+	assert(ret == 0 || *out_len <= BUFSIZ + EVP_MAX_BLOCK_LENGTH);
+
+	return ret;
+}
+
+static unsigned char *decode(char *data)
+{
+	size_t i;
+	unsigned char *decoded =
+	    (char *) malloc((strlen(data) / 2) * sizeof(char));
+	if (decoded == NULL)
+		return NULL;
+	for (i = 0; i < strlen(data); i += 2) {
+		decoded[i / 2] =
+		    isdigit(data[i]) ? (data[i] - 48) << 4 : (data[i] -
+							      87) << 4;
+		decoded[i / 2] +=
+		    isdigit(data[i + 1]) ? data[i + 1] - 48 : data[i + 1] -
+		    87;
+	}
+	return decoded;
+}
+
+static char *strip_cr(char *s)
+{
+	int len;
+
+	assert(s);
+
+	len = strlen(s);
+	s[len - 1] = s[len - 1] == '\n' ? 0x00 : s[len - 1];
+
+	return s;
+}
+
+static int read_key(char *buf, int size)
+{
+	int fnval = 1;
+
+	assert(buf);
+	assert(size > 0);
+
+	if (fgets(buf, size, stdin) == NULL) {
+		fnval = 0;
+		goto _return;
+	}
+
+	strip_cr(buf);
+
+      _return:
+	return fnval;
+}
+
+static void msg(const char *format, ...)
+{
+	assert(format != NULL);
+
+	if (verbose) {
+		/* Used to log issues that cause pam_mount to fail. */
+		va_list args;
+
+		va_start(args, format);
+		vfprintf(stdout, format, args);
+		va_end(args);
+	}
+}
+
+static int run_cryptsetup(const char *block_key_cipher, const char *device,
+			  const char *uuid, const char *key,
+			  const int key_len)
+{
+	pid_t child;
+	int fnval = 1, pipefd[2], child_exit;
+	char dmname[PATH_MAX + 1], *key_len_str;
+
+	assert(block_key_cipher != NULL);
+	assert(device != NULL);
+	assert(uuid != NULL);
+	assert(key != NULL);
+	assert(key_len > 0);
+
+	if (asprintf(&key_len_str, "%d", key_len) == -1) {
+		fprintf(stderr, "Failed to allocate memory, err=%s\n",
+			strerror(errno));
+		fnval = 0;
+		goto _return_no_free;
+	}
+
+	strcpy(dmname, "sesame_crypto_");
+	strncat(dmname, uuid, sizeof dmname - strlen(dmname));
+
+	if (pipe(pipefd) == -1) {
+		fprintf(stderr, "Failed to create pipe, err=%s\n",
+			strerror(errno));
+		fnval = 0;
+		goto _return;
+	}
+
+	child = fork();
+
+	if (child < 0) {
+		fprintf(stderr, "Failed to fork, err=%s\n",
+			strerror(errno));
+		fnval = 0;
+		goto _return;
+	} else if (child == 0) {
+		close(0);
+		dup(pipefd[0]);
+		close(pipefd[0]);
+		close(pipefd[1]);
+		execl(CRYPTSETUP, "cryptsetup", "-s", key_len_str, "-c",
+		      block_key_cipher, "create", dmname, device, NULL);
+		fprintf(stderr, "Failed to execute %s, err=%s\n",
+			CRYPTSETUP, strerror(errno));
+		exit(EXIT_FAILURE);
+	} else {
+		close(pipefd[0]);
+		write(pipefd[1], key, key_len);
+		close(pipefd[1]);
+		waitpid(child, &child_exit, 0);
+		fnval = !WEXITSTATUS(child_exit);
+		goto _return;
+	}
+
+      _return:
+	free(key_len_str);
+      _return_no_free:
+	return fnval;
+}
+
+int main(int argc, char *argv[])
+{
+	int c, opt_index = 0;
+	char passphrase[BUFSIZ + 1];
+	char dec_key[BUFSIZ + EVP_MAX_KEY_LENGTH];
+	int dec_key_len, real_dec_key_len;
 	char buf[1024];
+	int fd_metadata;
+	const char *uuid, *enc_key, *enc_key_cipher, *block_key_cipher,
+	    *block_key_sha1;
+	struct option opts[] = {
+		{"help", 0, 0, 'h'},
+		{"verbose", 0, 0, 'v'},
+		{0, 0, 0, 0}
+	};
 	SesameMetaData *md;
 
-	fd_metadata = open (md_file, O_RDONLY);
+	while ((c = getopt_long(argc, argv, "hv", opts, &opt_index)) >= 0) {
+		switch (c) {
+		case 'h':
+			print_usage(EXIT_SUCCESS, NULL, NULL);
+		case 'v':
+			verbose = 1;
+			break;
+		default:
+			print_usage(EXIT_FAILURE, NULL, NULL);
+		}
+	}
+
+	if (argv[optind] == NULL)
+		print_usage(EXIT_FAILURE, NULL, NULL);
+	device = argv[optind];
+
+	msg("opening %s\n", device);
+	fd_metadata = open(device, O_RDONLY);
 	if (fd_metadata == -1) {
-		printf ("Cannot open %s, err=%s\n", md_file, strerror (errno));
+		fprintf(stderr, "Cannot open %s, err=%s\n", device,
+			strerror(errno));
 		goto error;
 	}
-	if (read (fd_metadata, buf, sizeof (buf)) == -1) {
-		printf ("Cannot read from %s, err=%s\n", 
-			md_file, strerror (errno));
+
+	msg("reading from %s\n", device);
+	if (read(fd_metadata, buf, sizeof(buf)) == -1) {
+		fprintf(stderr, "Cannot read from %s, err=%s\n",
+			device, strerror(errno));
 		goto error1;
 	}
 
-	md = sesame_get_metadata_from_buf (buf);
+	msg("parsing metadata\n");
+	md = sesame_get_metadata_from_buf(buf);
 	if (md == NULL) {
-		printf ("Cannot not parse metadata\n");
+		fprintf(stderr, "Cannot not parse metadata\n");
 		goto error1;
 	}
 
-	printf ("uuid    = %s\n", sesame_get (md, "uuid"));
-	printf ("enc_key = %s\n", sesame_get (md, "enc_key"));
+	msg("getting uuid\n");
+	uuid = sesame_get(md, "uuid");
+	if (uuid == NULL) {
+		fprintf(stderr, "Cannot read uuid from %s\n", device);
+		goto error2;
+	}
 
-	sesame_free (md);
+	msg("getting enc_key\n");
+	enc_key = sesame_get(md, "enc_key");
+	if (enc_key == NULL) {
+		fprintf(stderr, "Cannot read enc_key from %s\n", device);
+		goto error2;
+	}
+
+	msg("getting enc_key_cipher\n");
+	enc_key_cipher = sesame_get(md, "enc_key_cipher");
+	if (enc_key == NULL) {
+		fprintf(stderr, "Cannot read enc_key_cipher from %s\n",
+			device);
+		goto error2;
+	}
+
+	msg("getting block_key_cipher\n");
+	block_key_cipher = sesame_get(md, "block_key_cipher");
+	if (block_key_cipher == NULL) {
+		fprintf(stderr, "Cannot read block_key_cipher from %s\n",
+			device);
+		goto error2;
+	}
+
+	msg("getting block_key_sha1\n");
+	block_key_sha1 = sesame_get(md, "block_key_sha1");
+	if (block_key_sha1 == NULL) {
+		fprintf(stderr, "Cannot read block_key_sha1 from %s\n",
+			device);
+		goto error2;
+	}
+
+	if (read_key(passphrase, BUFSIZ) == 0) {
+		fprintf(stderr, "Could not read key\n");
+		goto error2;
+	}
+
+	msg("decrypting key using passphrase\n");
+	if (decrypt
+	    (dec_key, &dec_key_len, decode(enc_key), strlen(enc_key) / 2,
+	     enc_key_cipher, passphrase) == 0) {
+		fprintf(stderr, "Cannot decrypt key\n");
+		goto error2;
+	}
+
+	msg("checking for MAGICTAIL tail in key\n");
+	real_dec_key_len = dec_key_len - strlen(MAGICTAIL);
+	if (memcmp
+	    (dec_key + real_dec_key_len, MAGICTAIL, strlen(MAGICTAIL))) {
+		fprintf(stderr, "Key does not end in %s\n", MAGICTAIL);
+		goto error2;
+	}
+
+	msg("executing cryptsetup\n");
+	if (run_cryptsetup(block_key_cipher, device, uuid, dec_key,
+			   real_dec_key_len) == 0) {
+		goto error2;
+	}
+
+error2:
+	msg("freeing memory\n");
+	sesame_free(md);
 
 error1:
-	close (fd_metadata);
+	close(fd_metadata);
 error:
 	return 0;
 }




More information about the hal-commit mailing list