Anonymous avatar Anonymous committed e84f2a4

Support password input through assuan/pinentry.

Comments (0)

Files changed (3)

 PROG = mirage2iso
 OBJS = mirage-getopt.o mirage-wrapper.o mirage-password.o
 
-CONFIGTESTS = check-getopt.o check-sysexits.o check-mmapio.o
-CONFIGIN = check-getopt.c check-sysexits.c check-mmapio.c
+CONFIGTESTS = check-getopt.o check-sysexits.o check-mmapio.o check-assuan
+CONFIGIN = check-getopt.c check-sysexits.c check-mmapio.c check-assuan.c
 
 DESTDIR = 
 PREFIX = /usr/local
 check-mmapio.c:
 	printf '#define _POSIX_C_SOURCE 200112L\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/mman.h>\nint main(void) { return (ftruncate(0, 0) || mmap(0, 0, 0, 0, 0, 0) == MAP_FAILED); }\n' > $@
 
+check-assuan.c:
+	printf '#define _ISO_C99_SOURCE 1\n#include <assuan.h>\nint main(void) { assuan_context_t ctx; return assuan_pipe_connect(&ctx, "/bin/true", 0, 0); }\n' > $@
+
+check-assuan: check-assuan.c
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(ASSUAN_CPPFLAGS) $(LDFLAGS) -o $@ $< $(LIBS) $(ASSUAN_LIBS)
+
 confclean:
 	rm -f $(CONFIGTESTS) $(CONFIGIN)
 
 	conf_check check-getopt.o NO_GETOPT_LONG 'getopt_long()'
 	conf_check check-sysexits.o NO_SYSEXITS '<sysexits.h>'
 	conf_check check-mmapio.o NO_MMAPIO 'mmap() & ftruncate()'
+	conf_check check-assuan NO_ASSUAN 'assuan'
 }
 
 MAKEIN=Makefile.in
 
-make ${MAKEFLAGS:-${MAKEOPTS}} -f ${MAKEIN} -k configure
+# Both can fail
+ASSUAN_CPPFLAGS="$(libassuan-config --cflags 2>/dev/null)"
+ASSUAN_LIBS="$(libassuan-config --libs 2>/dev/null)"
+ASSUAN_LIBS="${ASSUAN_LIBS:--lassuan}" # fallback
+
+make ${MAKEFLAGS:-${MAKEOPTS}} -f ${MAKEIN} -k \
+	ASSUAN_CPPFLAGS="${ASSUAN_CPPFLAGS}" \
+	ASSUAN_LIBS="${ASSUAN_LIBS}" \
+	configure
+
+if [ ! -f check-assuan ]; then
+	ASSUAN_CPPFLAGS=
+	ASSUAN_LIBS=
+fi
+
 conf_results > mirage-config.h
 make -f ${MAKEIN} confclean
 
 cat - ${MAKEIN} << _EOF_ > ${MAKEIN%.in}
 # autogenerated by configure, please modify Makefile.in instead
 
-CONF_CPPFLAGS = $(pkg-config --cflags libmirage)
+CONF_CPPFLAGS = $(pkg-config --cflags libmirage) ${ASSUAN_CPPFLAGS}
 CONF_LDFLAGS = $(pkg-config --libs-only-L --libs-only-other libmirage)
-CONF_LIBS = $(pkg-config --libs-only-l libmirage)
+CONF_LIBS = $(pkg-config --libs-only-l libmirage) ${ASSUAN_LIBS}
 
 _EOF_
 

mirage-password.c

  * released under 3-clause BSD license
  */
 
+#include "mirage-config.h"
+
 #define _ISOC99_SOURCE 1
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <stdbool.h>
 #include <string.h>
 
+#ifndef NO_ASSUAN
+#	include <stddef.h>
+#	include <assuan.h>
+#endif
+
 static const int password_bufsize = 256;
 
 static char *buf = NULL;
 
-const char* const mirage_input_password(void) {
+/* We use this ternary state like that:
+ * - error means that something terrible has happened (I/O failure etc),
+ * - success means requested operation succedeed,
+ * - failure means operation failed but not because of critical error.
+ *
+ * Note that checking !var matches 'error' state only (that is intended).
+ */
+typedef enum mirage_tristate {
+	error = false,
+	success = true,
+	failure
+} mirage_tristate_t;
+
+void mirage_forget_password(void) {
+	if (buf) {
+		/* wipe out the buf */
+		memset(buf, 0, strlen(buf));
+		free(buf);
+		buf = NULL;
+	}
+}
+
+#ifndef NO_ASSUAN
+
+/* XXX: more portable solution? */
+static const char* const mirage_getshell(void) {
+	const char* const shvar = getenv("SHELL");
+
+	if (shvar && *shvar)
+		return shvar;
+
+	return "/bin/sh";
+}
+
+static const mirage_tristate_t mirage_pinentry_set(assuan_context_t ctx, const char* const cmd) {
+	assuan_error_t err;
+	char *rcvbuf;
+	size_t rcvlen;
+
+	if (((err = assuan_write_line(ctx, cmd))) != ASSUAN_No_Error) {
+		fprintf(stderr, "Failed to send a command to pinentry: %s\n", assuan_strerror(err));
+		return error;
+	}
+
+	if (((err = assuan_read_line(ctx, &rcvbuf, &rcvlen))) != ASSUAN_No_Error) {
+		fprintf(stderr, "Failed to receive a response from pinentry: %s\n", assuan_strerror(err));
+		return error;
+	}
+
+	/* this is not critical */
+	if (rcvlen != 2 || strncmp(rcvbuf, "OK", 2)) {
+		fprintf(stderr, "pinentry setting failed: %s\n", cmd);
+		return failure;
+	}
+
+	return success;
+}
+
+static const mirage_tristate_t mirage_input_password_pinentry(void) {
+	const char* const shell = mirage_getshell();
+	const char* const args[] = { shell, "-c", "exec pinentry", NULL };
+	int noclose[] = { -1 };
+
+	assuan_context_t ctx;
+	assuan_error_t err;
+
+	if (((err = assuan_pipe_connect(&ctx, shell, args, noclose))) != ASSUAN_No_Error) {
+		fprintf(stderr, "Failed to launch pinentry: %s\n", assuan_strerror(err));
+		return error;
+	}
+
+	if (!mirage_pinentry_set(ctx, "SETDESC Enter passphrase for the encrypted image:")
+			|| !mirage_pinentry_set(ctx, "SETPROMPT Pass:")
+			|| !mirage_pinentry_set(ctx, "SETTITLE mirage2iso")) {
+
+		assuan_disconnect(ctx);
+		return error;
+	}
+
+	assuan_begin_confidential(ctx);
+
+	char *rcvbuf;
+	size_t rcvlen;
+
+	if (((err = assuan_write_line(ctx, "GETPIN"))) != ASSUAN_No_Error) {
+		fprintf(stderr, "Failed to send the password request to pinentry: %s\n", assuan_strerror(err));
+		assuan_disconnect(ctx);
+		return error;
+	}
+
+	/* we get either 'D <password>' here or 'ERR nnnnn desc' */
+	if (((err = assuan_read_line(ctx, &rcvbuf, &rcvlen))) != ASSUAN_No_Error) {
+		fprintf(stderr, "Failed to receive the password response from pinentry: %s\n", assuan_strerror(err));
+		assuan_disconnect(ctx);
+		return error;
+	}
+
+	if (rcvlen <= 2 || strncmp(rcvbuf, "D ", 2)) {
+		fprintf(stderr, "No password supplied\n");
+		assuan_disconnect(ctx);
+		return failure;
+	}
+
+	buf = malloc(rcvlen - 1); /* two less for 'D ', one more for \0 */
+	if (!buf) {
+		fprintf(stderr, "malloc() for password buffer failed\n");
+		assuan_disconnect(ctx);
+		return error;
+	}
+	strncpy(buf, &rcvbuf[2], rcvlen - 2);
+	buf[rcvlen - 2] = 0;
+
+	/* and we should get one more 'OK' too; if we don't, we assume that connection was broken */
+	if (((err = assuan_read_line(ctx, &rcvbuf, &rcvlen))) != ASSUAN_No_Error) {
+		fprintf(stderr, "Failed to receive a confirmation from pinentry: %s\n", assuan_strerror(err));
+		mirage_forget_password();
+		assuan_disconnect(ctx);
+		return error;
+	}
+
+	if (rcvlen != 2 || strncmp(rcvbuf, "OK", 2)) {
+		fprintf(stderr, "pinentry didn't confirm sent password\n");
+		mirage_forget_password();
+		assuan_disconnect(ctx);
+		return error;
+	}
+
+	assuan_end_confidential(ctx);
+	assuan_disconnect(ctx);
+
+	return success;
+}
+
+#endif
+
+static const mirage_tristate_t mirage_input_password_stdio(void) {
 	if (!buf) {
 		buf = malloc(password_bufsize);
 		if (!buf) {
 			fprintf(stderr, "malloc() for password buffer failed\n");
-			return NULL;
+			return error;
 		}
 
 		fprintf(stderr, "Please input password to the encrypted image: ");
 
 		if (!fgets(buf, password_bufsize, stdin)) {
 			fprintf(stderr, "Password input failed\n");
-			free(buf);
-			return NULL;
+			mirage_forget_password();
+			return error;
 		}
 
 		/* remove trailing newline */
 		/* buf got wiped? */
 		if (!*buf) {
 			fprintf(stderr, "No password supplied\n");
-			free(buf);
-			return NULL;
+			mirage_forget_password();
+			return failure;
 		}
 	}
 
-	return buf;
+	return success;
 }
 
-void mirage_forget_password(void) {
-	if (buf) {
-		/* wipe out the buf */
-		memset(buf, 0, password_bufsize);
-		free(buf);
+const char* const mirage_input_password(void) {
+#ifndef NO_ASSUAN
+	switch (mirage_input_password_pinentry()) {
+		case error: break;
+		case success: return buf;
+		case failure: return NULL;
 	}
+#endif
+
+	switch (mirage_input_password_stdio()) {
+		case error: break;
+		case success: return buf;
+		case failure: return NULL;
+	}
+
+	fprintf(stderr, "All supported methods of password input have failed\n");
+	return NULL;
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.