]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[build] Allow a client certificate to be specified at build time
authorMichael Brown <mcb30@ipxe.org>
Tue, 20 Mar 2012 13:32:20 +0000 (13:32 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 20 Mar 2012 20:39:11 +0000 (20:39 +0000)
Allow a client certificate and corresponding private key to be
specified at build time using the syntax

  make CERT=/path/to/certificate KEY=/path/to/key

The build process uses openssl to convert the files into DER format,
and includes them within the client certificate store in
clientcert.c.  The build process will prompt for the private key
password if applicable.

Note that the private key is stored unencrypted, and so the resulting
iPXE binary (and the temporary files created during the build process)
should be treated as being equivalent to an unencrypted private key
file.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/Makefile.housekeeping
src/crypto/clientcert.c [new file with mode: 0644]
src/include/ipxe/clientcert.h [new file with mode: 0644]

index daac97b9fd330109899dd0057fcce5673ef8e21e..0fab407c3ee2fbab479e83428ff5c2c6e22eabdd 100644 (file)
@@ -629,12 +629,6 @@ EMBED_ALL  := $(foreach i,$(call seq,1,$(words $(EMBEDDED_FILES))),\
 
 $(BIN)/embedded.o : $(EMBEDDED_FILES) $(EMBEDDED_LIST)
 
-# This file uses .incbin inline assembly to include a binary file.
-# Unfortunately ccache does not detect this dependency and caches builds even
-# when the binary file has changed.
-#
-$(BIN)/embedded.o : override CC := env CCACHE_DISABLE=1 $(CC)
-
 CFLAGS_embedded = -DEMBED_ALL="$(EMBED_ALL)"
 
 # List of trusted root certificates
@@ -665,6 +659,80 @@ $(BIN)/rootcert.o : $(TRUSTED_FILES) $(TRUSTED_LIST)
 
 CFLAGS_rootcert = $(if $(TRUSTED_FPS),-DTRUSTED="$(TRUSTED_FPS)")
 
+# (Single-element) list of client certificates
+#
+CERT_LIST := $(BIN)/.certificate.list
+ifeq ($(wildcard $(CERT_LIST)),)
+CERT_OLD := <invalid>
+else
+CERT_OLD := $(shell cat $(CERT_LIST))
+endif
+ifneq ($(CERT_OLD),$(CERT))
+$(shell $(ECHO) "$(CERT)" > $(CERT_LIST))
+endif
+
+$(CERT_LIST) :
+
+VERYCLEANUP += $(CERT_LIST)
+
+# Embedded client certificate
+#
+CERT_INC := $(BIN)/.certificate.der
+
+ifdef CERT
+$(CERT_INC) : $(CERT) $(CERT_LIST)
+       $(Q)$(OPENSSL) x509 -in $< -outform DER -out $@
+
+$(BIN)/clientcert.o : $(CERT_INC)
+endif
+
+CLEANUP += $(CERT_INC)
+
+$(BIN)/clientcert.o : $(CERT_LIST)
+
+CFLAGS_clientcert += $(if $(CERT),-DCERTIFICATE="\"$(CERT_INC)\"")
+
+# (Single-element) list of client private keys
+#
+KEY_LIST := $(BIN)/.private_key.list
+ifeq ($(wildcard $(KEY_LIST)),)
+KEY_OLD := <invalid>
+else
+KEY_OLD := $(shell cat $(KEY_LIST))
+endif
+ifneq ($(KEY_OLD),$(KEY))
+$(shell $(ECHO) "$(KEY)" > $(KEY_LIST))
+endif
+
+$(KEY_LIST) :
+
+VERYCLEANUP += $(KEY_LIST)
+
+# Embedded client private key
+#
+KEY_INC := $(BIN)/.private_key.der
+
+ifdef KEY
+$(KEY_INC) : $(KEY) $(KEY_LIST)
+       $(Q)$(OPENSSL) rsa -in $< -outform DER -out $@
+
+$(BIN)/clientcert.o : $(KEY_INC)
+endif
+
+CLEANUP += $(KEY_INC)
+
+$(BIN)/clientcert.o : $(KEY_LIST)
+
+CFLAGS_clientcert += $(if $(KEY),-DPRIVATE_KEY="\"$(KEY_INC)\"")
+
+# These files use .incbin inline assembly to include a binary file.
+# Unfortunately ccache does not detect this dependency and caches
+# builds even when the binary file has changed.
+#
+$(BIN)/embedded.o : override CC := env CCACHE_DISABLE=1 $(CC)
+
+$(BIN)/clientcert.o : override CC := env CCACHE_DISABLE=1 $(CC)
+
 # Generate error usage information
 #
 $(BIN)/%.einfo : $(BIN)/%.o
diff --git a/src/crypto/clientcert.c b/src/crypto/clientcert.c
new file mode 100644 (file)
index 0000000..03c7528
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <ipxe/clientcert.h>
+
+/** @file
+ *
+ * Client certificate store
+ *
+ * Life would in theory be easier if we could use a single file to
+ * hold both the certificate and corresponding private key.
+ * Unfortunately, the only common format which supports this is
+ * PKCS#12 (aka PFX), which is too ugly to be allowed anywhere near my
+ * codebase.  See, for reference and amusement:
+ *
+ *    http://www.cs.auckland.ac.nz/~pgut001/pubs/pfx.html
+ *
+ */
+
+/* Sanity checks */
+#if defined(CERTIFICATE) && ! defined(PRIVATE_KEY)
+#warning "Attempting to embed certificate with no corresponding private key"
+#endif
+#if defined(PRIVATE_KEY) && ! defined(CERTIFICATE)
+#warning "Attempting to embed private key with no corresponding certificate"
+#endif
+
+/* Raw client certificate data */
+extern char client_certificate_data[];
+extern char client_certificate_len[];
+__asm__ ( ".section \".rodata\", \"a\", @progbits\n\t"
+         "\nclient_certificate_data:\n\t"
+#ifdef CERTIFICATE
+         ".incbin \"" CERTIFICATE "\"\n\t"
+#endif /* CERTIFICATE */
+         ".size client_certificate_data, ( . - client_certificate_data )\n\t"
+         ".equ client_certificate_len, ( . - client_certificate_data )\n\t"
+         ".previous\n\t" );
+
+/** Client certificate */
+struct client_certificate client_certificate = {
+       .data = client_certificate_data,
+       .len = ( ( size_t ) client_certificate_len ),
+};
+
+/* Raw client private key data */
+extern char client_private_key_data[];
+extern char client_private_key_len[];
+__asm__ ( ".section \".rodata\", \"a\", @progbits\n\t"
+         "\nclient_private_key_data:\n\t"
+#ifdef PRIVATE_KEY
+         ".incbin \"" PRIVATE_KEY "\"\n\t"
+#endif /* PRIVATE_KEY */
+         ".size client_private_key_data, ( . - client_private_key_data )\n\t"
+         ".equ client_private_key_len, ( . - client_private_key_data )\n\t"
+         ".previous\n\t" );
+
+/** Client private key */
+struct client_private_key client_private_key = {
+       .data = client_private_key_data,
+       .len = ( ( size_t ) client_private_key_len ),
+};
diff --git a/src/include/ipxe/clientcert.h b/src/include/ipxe/clientcert.h
new file mode 100644 (file)
index 0000000..08f62eb
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _IPXE_CLIENTCERT_H
+#define _IPXE_CLIENTCERT_H
+
+/** @file
+ *
+ * Client certificate store
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+
+/** A client certificate */
+struct client_certificate {
+       /** Data */
+       const void *data;
+       /** Length */
+       size_t len;
+};
+
+/** A client private key */
+struct client_private_key {
+       /** Data */
+       const void *data;
+       /** Length */
+       size_t len;
+};
+
+extern struct client_certificate client_certificate;
+extern struct client_private_key client_private_key;
+
+/**
+ * Check for presence of a client certificate
+ *
+ * @ret have_cert      We have a client certificate and private key
+ */
+static inline int have_client_certificate ( void ) {
+       return ( ( client_certificate.len > 0 ) &&
+                ( client_private_key.len > 0 ) );
+}
+
+#endif /* _IPXE_CLIENTCERT_H */