diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index daac97b9..0fab407c 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -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 := +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 := +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 index 00000000..03c75284 --- /dev/null +++ b/src/crypto/clientcert.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2012 Michael Brown . + * + * 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 +#include + +/** @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 index 00000000..08f62eb7 --- /dev/null +++ b/src/include/ipxe/clientcert.h @@ -0,0 +1,43 @@ +#ifndef _IPXE_CLIENTCERT_H +#define _IPXE_CLIENTCERT_H + +/** @file + * + * Client certificate store + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include + +/** 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 */