diff --git a/.gitignore b/.gitignore index e46ec8a..586bc24 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ shim_cert.h *.so *.srl *.srl.old +*.tar.* +version.c diff --git a/Cryptlib/Cipher/CryptAes.c b/Cryptlib/Cipher/CryptAes.c index 45e4a43..753d798 100644 --- a/Cryptlib/Cipher/CryptAes.c +++ b/Cryptlib/Cipher/CryptAes.c @@ -38,7 +38,7 @@ AesGetContextSize ( Initializes user-supplied memory as AES context for subsequent use. This function initializes user-supplied memory pointed by AesContext as AES context. - In addtion, it sets up all AES key materials for subsequent encryption and decryption + In addition, it sets up all AES key materials for subsequent encryption and decryption operations. There are 3 options for key length, 128 bits, 192 bits, and 256 bits. @@ -241,7 +241,11 @@ AesCbcEncrypt ( // // Check input parameters. // - if (AesContext == NULL || Input == NULL || (InputSize % AES_BLOCK_SIZE) != 0 || Ivec == NULL || Output == NULL) { + if (AesContext == NULL || Input == NULL || (InputSize % AES_BLOCK_SIZE) != 0) { + return FALSE; + } + + if (Ivec == NULL || Output == NULL || InputSize > INT_MAX) { return FALSE; } @@ -299,7 +303,11 @@ AesCbcDecrypt ( // // Check input parameters. // - if (AesContext == NULL || Input == NULL || (InputSize % AES_BLOCK_SIZE) != 0 || Ivec == NULL || Output == NULL) { + if (AesContext == NULL || Input == NULL || (InputSize % AES_BLOCK_SIZE) != 0) { + return FALSE; + } + + if (Ivec == NULL || Output == NULL || InputSize > INT_MAX) { return FALSE; } diff --git a/Cryptlib/Cipher/CryptArc4.c b/Cryptlib/Cipher/CryptArc4.c index 6921418..f3c4d31 100644 --- a/Cryptlib/Cipher/CryptArc4.c +++ b/Cryptlib/Cipher/CryptArc4.c @@ -32,14 +32,14 @@ Arc4GetContextSize ( // for backup copy. When Arc4Reset() is called, we can use the backup copy to restore // the working copy to the initial state. // - return (UINTN) (2 * sizeof(RC4_KEY)); + return (UINTN) (2 * sizeof (RC4_KEY)); } /** Initializes user-supplied memory as ARC4 context for subsequent use. This function initializes user-supplied memory pointed by Arc4Context as ARC4 context. - In addtion, it sets up all ARC4 key materials for subsequent encryption and decryption + In addition, it sets up all ARC4 key materials for subsequent encryption and decryption operations. If Arc4Context is NULL, then return FALSE. @@ -75,7 +75,7 @@ Arc4Init ( RC4_set_key (Rc4Key, (UINT32) KeySize, Key); - CopyMem (Rc4Key + 1, Rc4Key, sizeof(RC4_KEY)); + CopyMem (Rc4Key + 1, Rc4Key, sizeof (RC4_KEY)); return TRUE; } @@ -115,7 +115,7 @@ Arc4Encrypt ( // // Check input parameters. // - if (Arc4Context == NULL || Input == NULL || Output == NULL) { + if (Arc4Context == NULL || Input == NULL || Output == NULL || InputSize > INT_MAX) { return FALSE; } @@ -161,7 +161,7 @@ Arc4Decrypt ( // // Check input parameters. // - if (Arc4Context == NULL || Input == NULL || Output == NULL) { + if (Arc4Context == NULL || Input == NULL || Output == NULL || InputSize > INT_MAX) { return FALSE; } @@ -205,7 +205,7 @@ Arc4Reset ( Rc4Key = (RC4_KEY *) Arc4Context; - CopyMem (Rc4Key, Rc4Key + 1, sizeof(RC4_KEY)); + CopyMem (Rc4Key, Rc4Key + 1, sizeof (RC4_KEY)); return TRUE; } diff --git a/Cryptlib/Cipher/CryptTdes.c b/Cryptlib/Cipher/CryptTdes.c index 8213718..f89094a 100644 --- a/Cryptlib/Cipher/CryptTdes.c +++ b/Cryptlib/Cipher/CryptTdes.c @@ -37,7 +37,7 @@ TdesGetContextSize ( Initializes user-supplied memory as TDES context for subsequent use. This function initializes user-supplied memory pointed by TdesContext as TDES context. - In addtion, it sets up all TDES key materials for subsequent encryption and decryption + In addition, it sets up all TDES key materials for subsequent encryption and decryption operations. There are 3 key options as follows: KeyLength = 64, Keying option 1: K1 == K2 == K3 (Backward compatibility with DES) @@ -76,9 +76,9 @@ TdesInit ( KeySchedule = (DES_key_schedule *) TdesContext; // - // + // If input Key is a weak key, return error. // - if (DES_is_weak_key ((const_DES_cblock *) Key)) { + if (DES_is_weak_key ((const_DES_cblock *) Key) == 1) { return FALSE; } @@ -90,7 +90,7 @@ TdesInit ( return TRUE; } - if (DES_is_weak_key ((const_DES_cblock *) Key + 8)) { + if (DES_is_weak_key ((const_DES_cblock *) Key + 8) == 1) { return FALSE; } @@ -101,7 +101,7 @@ TdesInit ( return TRUE; } - if (DES_is_weak_key ((const_DES_cblock *) Key + 16)) { + if (DES_is_weak_key ((const_DES_cblock *) Key + 16) == 1) { return FALSE; } @@ -275,7 +275,11 @@ TdesCbcEncrypt ( // // Check input parameters. // - if (TdesContext == NULL || Input == NULL || (InputSize % TDES_BLOCK_SIZE) != 0 || Ivec == NULL || Output == NULL) { + if (TdesContext == NULL || Input == NULL || (InputSize % TDES_BLOCK_SIZE) != 0) { + return FALSE; + } + + if (Ivec == NULL || Output == NULL || InputSize > INT_MAX) { return FALSE; } @@ -339,7 +343,11 @@ TdesCbcDecrypt ( // // Check input parameters. // - if (TdesContext == NULL || Input == NULL || (InputSize % TDES_BLOCK_SIZE) != 0 || Ivec == NULL || Output == NULL) { + if (TdesContext == NULL || Input == NULL || (InputSize % TDES_BLOCK_SIZE) != 0) { + return FALSE; + } + + if (Ivec == NULL || Output == NULL || InputSize > INT_MAX) { return FALSE; } diff --git a/Cryptlib/Cryptlib.diff b/Cryptlib/Cryptlib.diff index d30743f..9663d90 100644 --- a/Cryptlib/Cryptlib.diff +++ b/Cryptlib/Cryptlib.diff @@ -34,3 +34,24 @@ index 805e6b4..bb7bcba 100644 // // Years Handling +diff --git a/Cryptlib/SysCall/CrtWrapper.c b/Cryptlib/SysCall/CrtWrapper.c +index fb446b6..5a8322d 100644 +--- a/Cryptlib/SysCall/CrtWrapper.c ++++ b/Cryptlib/SysCall/CrtWrapper.c +@@ -293,16 +293,6 @@ size_t fwrite (const void *buffer, size_t size, size_t count, FILE *stream) + // -- Dummy OpenSSL Support Routines -- + // + +-int BIO_printf (void *bio, const char *format, ...) +-{ +- return 0; +-} +- +-int BIO_snprintf(char *buf, size_t n, const char *format, ...) +-{ +- return 0; +-} +- + void *UI_OpenSSL(void) + { + return NULL; diff --git a/Cryptlib/Hash/CryptMd4.c b/Cryptlib/Hash/CryptMd4.c index 31fc4dc..633d343 100644 --- a/Cryptlib/Hash/CryptMd4.c +++ b/Cryptlib/Hash/CryptMd4.c @@ -30,7 +30,7 @@ Md4GetContextSize ( // // Retrieves the OpenSSL MD4 Context Size // - return (UINTN)(sizeof (MD4_CTX)); + return (UINTN) (sizeof (MD4_CTX)); } /** @@ -61,7 +61,7 @@ Md4Init ( // // OpenSSL MD4 Context Initialization // - return (BOOLEAN) (MD4_Init ((MD4_CTX *)Md4Context)); + return (BOOLEAN) (MD4_Init ((MD4_CTX *) Md4Context)); } /** @@ -139,7 +139,7 @@ Md4Update ( // // OpenSSL MD4 Hash Update // - return (BOOLEAN) (MD4_Update ((MD4_CTX *)Md4Context, Data, DataSize)); + return (BOOLEAN) (MD4_Update ((MD4_CTX *) Md4Context, Data, DataSize)); } /** @@ -179,5 +179,5 @@ Md4Final ( // // OpenSSL MD4 Hash Finalization // - return (BOOLEAN) (MD4_Final (HashValue, (MD4_CTX *)Md4Context)); + return (BOOLEAN) (MD4_Final (HashValue, (MD4_CTX *) Md4Context)); } diff --git a/Cryptlib/Hash/CryptMd5.c b/Cryptlib/Hash/CryptMd5.c index 1d852c7..dcf7691 100644 --- a/Cryptlib/Hash/CryptMd5.c +++ b/Cryptlib/Hash/CryptMd5.c @@ -31,7 +31,7 @@ Md5GetContextSize ( // // Retrieves the OpenSSL MD5 Context Size // - return (UINTN)(sizeof (MD5_CTX)); + return (UINTN) (sizeof (MD5_CTX)); } @@ -63,7 +63,7 @@ Md5Init ( // // OpenSSL MD5 Context Initialization // - return (BOOLEAN) (MD5_Init ((MD5_CTX *)Md5Context)); + return (BOOLEAN) (MD5_Init ((MD5_CTX *) Md5Context)); } /** @@ -141,7 +141,7 @@ Md5Update ( // // OpenSSL MD5 Hash Update // - return (BOOLEAN) (MD5_Update ((MD5_CTX *)Md5Context, Data, DataSize)); + return (BOOLEAN) (MD5_Update ((MD5_CTX *) Md5Context, Data, DataSize)); } /** @@ -181,5 +181,5 @@ Md5Final ( // // OpenSSL MD5 Hash Finalization // - return (BOOLEAN) (MD5_Final (HashValue, (MD5_CTX *)Md5Context)); + return (BOOLEAN) (MD5_Final (HashValue, (MD5_CTX *) Md5Context)); } diff --git a/Cryptlib/Hash/CryptSha1.c b/Cryptlib/Hash/CryptSha1.c index 633028b..78c29c1 100644 --- a/Cryptlib/Hash/CryptSha1.c +++ b/Cryptlib/Hash/CryptSha1.c @@ -31,7 +31,7 @@ Sha1GetContextSize ( // // Retrieves OpenSSL SHA Context Size // - return (UINTN)(sizeof (SHA_CTX)); + return (UINTN) (sizeof (SHA_CTX)); } /** @@ -62,7 +62,7 @@ Sha1Init ( // // OpenSSL SHA-1 Context Initialization // - return (BOOLEAN) (SHA1_Init ((SHA_CTX *)Sha1Context)); + return (BOOLEAN) (SHA1_Init ((SHA_CTX *) Sha1Context)); } /** @@ -140,7 +140,7 @@ Sha1Update ( // // OpenSSL SHA-1 Hash Update // - return (BOOLEAN) (SHA1_Update ((SHA_CTX *)Sha1Context, Data, DataSize)); + return (BOOLEAN) (SHA1_Update ((SHA_CTX *) Sha1Context, Data, DataSize)); } /** @@ -180,5 +180,5 @@ Sha1Final ( // // OpenSSL SHA-1 Hash Finalization // - return (BOOLEAN) (SHA1_Final (HashValue, (SHA_CTX *)Sha1Context)); + return (BOOLEAN) (SHA1_Final (HashValue, (SHA_CTX *) Sha1Context)); } diff --git a/Cryptlib/Hash/CryptSha256.c b/Cryptlib/Hash/CryptSha256.c index ca0cb1a..56894ac 100644 --- a/Cryptlib/Hash/CryptSha256.c +++ b/Cryptlib/Hash/CryptSha256.c @@ -30,7 +30,7 @@ Sha256GetContextSize ( // // Retrieves OpenSSL SHA-256 Context Size // - return (UINTN)(sizeof (SHA256_CTX)); + return (UINTN) (sizeof (SHA256_CTX)); } /** @@ -61,7 +61,7 @@ Sha256Init ( // // OpenSSL SHA-256 Context Initialization // - return (BOOLEAN) (SHA256_Init ((SHA256_CTX *)Sha256Context)); + return (BOOLEAN) (SHA256_Init ((SHA256_CTX *) Sha256Context)); } /** @@ -139,7 +139,7 @@ Sha256Update ( // // OpenSSL SHA-256 Hash Update // - return (BOOLEAN) (SHA256_Update ((SHA256_CTX *)Sha256Context, Data, DataSize)); + return (BOOLEAN) (SHA256_Update ((SHA256_CTX *) Sha256Context, Data, DataSize)); } /** @@ -179,5 +179,5 @@ Sha256Final ( // // OpenSSL SHA-256 Hash Finalization // - return (BOOLEAN) (SHA256_Final (HashValue, (SHA256_CTX *)Sha256Context)); + return (BOOLEAN) (SHA256_Final (HashValue, (SHA256_CTX *) Sha256Context)); } diff --git a/Cryptlib/Hmac/CryptHmacMd5.c b/Cryptlib/Hmac/CryptHmacMd5.c index 0cdab7a..693cd32 100644 --- a/Cryptlib/Hmac/CryptHmacMd5.c +++ b/Cryptlib/Hmac/CryptHmacMd5.c @@ -30,7 +30,7 @@ HmacMd5GetContextSize ( // // Retrieves the OpenSSL HMAC-MD5 Context Size // - return (UINTN)(sizeof (HMAC_CTX)); + return (UINTN) (sizeof (HMAC_CTX)); } /** @@ -58,7 +58,7 @@ HmacMd5Init ( // // Check input parameters. // - if (HmacMd5Context == NULL) { + if (HmacMd5Context == NULL || KeySize > INT_MAX) { return FALSE; } diff --git a/Cryptlib/Hmac/CryptHmacSha1.c b/Cryptlib/Hmac/CryptHmacSha1.c index 58da2f3..881d26c 100644 --- a/Cryptlib/Hmac/CryptHmacSha1.c +++ b/Cryptlib/Hmac/CryptHmacSha1.c @@ -30,7 +30,7 @@ HmacSha1GetContextSize ( // // Retrieves the OpenSSL HMAC-SHA1 Context Size // - return (UINTN)(sizeof (HMAC_CTX)); + return (UINTN) (sizeof (HMAC_CTX)); } /** @@ -58,7 +58,7 @@ HmacSha1Init ( // // Check input parameters. // - if (HmacSha1Context == NULL) { + if (HmacSha1Context == NULL || KeySize > INT_MAX) { return FALSE; } diff --git a/Cryptlib/InternalCryptLib.h b/Cryptlib/InternalCryptLib.h index b047626..35a8eb1 100644 --- a/Cryptlib/InternalCryptLib.h +++ b/Cryptlib/InternalCryptLib.h @@ -21,6 +21,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include +#include "OpenSslSupport.h" + // // Environment Setting for OpenSSL-based UEFI Crypto Library. // @@ -28,25 +30,5 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define OPENSSL_SYSNAME_UWIN #endif -/** - Pop single certificate from STACK_OF(X509). - - If X509Stack, Cert, or CertSize is NULL, then return FALSE. - - @param[in] X509Stack Pointer to a X509 stack object. - @param[out] Cert Pointer to a X509 certificate. - @param[out] CertSize Length of output X509 certificate in bytes. - - @retval TRUE The X509 stack pop succeeded. - @retval FALSE The pop operation failed. - -**/ -BOOLEAN -X509PopCertificate ( - IN VOID *X509Stack, - OUT UINT8 **Cert, - OUT UINTN *CertSize - ); - #endif diff --git a/Cryptlib/Library/BaseCryptLib.h b/Cryptlib/Library/BaseCryptLib.h index 0f4b026..715a8e2 100644 --- a/Cryptlib/Library/BaseCryptLib.h +++ b/Cryptlib/Library/BaseCryptLib.h @@ -1400,7 +1400,7 @@ RsaPkcs1Verify ( IN VOID *RsaContext, IN CONST UINT8 *MessageHash, IN UINTN HashSize, - IN UINT8 *Signature, + IN CONST UINT8 *Signature, IN UINTN SigSize ); diff --git a/Cryptlib/Makefile b/Cryptlib/Makefile index 0890838..a05a4db 100644 --- a/Cryptlib/Makefile +++ b/Cryptlib/Makefile @@ -27,7 +27,8 @@ OBJS = Hash/CryptMd4.o \ Cipher/CryptTdes.o \ Cipher/CryptArc4.o \ Rand/CryptRand.o \ - Pk/CryptRsa.o \ + Pk/CryptRsaBasic.o \ + Pk/CryptRsaExt.o \ Pk/CryptPkcs7.o \ Pk/CryptDh.o \ Pk/CryptX509.o \ diff --git a/Cryptlib/OpenSSL/Makefile b/Cryptlib/OpenSSL/Makefile index 1960b6b..c93d5af 100644 --- a/Cryptlib/OpenSSL/Makefile +++ b/Cryptlib/OpenSSL/Makefile @@ -10,7 +10,7 @@ LIB_GCC = $(shell $(CC) -print-libgcc-file-name) EFI_LIBS = -lefi -lgnuefi $(LIB_GCC) CFLAGS = -ggdb -O0 -I. -I.. -I../Include/ -Icrypto -fno-stack-protector -fno-strict-aliasing -fpic -fshort-wchar -nostdinc -mno-mmx -mno-sse -mno-red-zone -maccumulate-outgoing-args \ - -Wall $(EFI_INCLUDES) -DOPENSSL_SYSNAME_UWIN -DOPENSSL_SYS_UEFI -DL_ENDIAN -DSIXTY_FOUR_BIT_LONG -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DOPENSSL_NO_CAMELLIA -DOPENSSL_NO_SEED -DOPENSSL_NO_RC5 -DOPENSSL_NO_MDC2 -DOPENSSL_NO_SOCK -DOPENSSL_NO_CMS -DOPENSSL_NO_JPAKE -DOPENSSL_NO_CAPIENG -DOPENSSL_NO_ERR -DOPENSSL_NO_KRB5 -DOPENSSL_NO_DYNAMIC_ENGINE -DGETPID_IS_MEANINGLESS -DOPENSSL_NO_STDIO -DOPENSSL_NO_FP_API -DOPENSSL_NO_DGRAM -DOPENSSL_NO_SHA0 -DOPENSSL_NO_SHA512 -DOPENSSL_NO_LHASH -DOPENSSL_NO_HW -DOPENSSL_NO_OCSP -DOPENSSL_NO_LOCKING -DOPENSSL_NO_DEPRECATED -DOPENSSL_SMALL_FOOTPRINT -DPEDANTIC + -Wall $(EFI_INCLUDES) -DOPENSSL_SYSNAME_UWIN -DOPENSSL_SYS_UEFI -DL_ENDIAN -DSIXTY_FOUR_BIT_LONG -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DOPENSSL_NO_CAMELLIA -DOPENSSL_NO_SEED -DOPENSSL_NO_RC5 -DOPENSSL_NO_MDC2 -DOPENSSL_NO_SOCK -DOPENSSL_NO_CMS -DOPENSSL_NO_JPAKE -DOPENSSL_NO_CAPIENG -DOPENSSL_NO_ERR -DOPENSSL_NO_KRB5 -DOPENSSL_NO_DYNAMIC_ENGINE -DGETPID_IS_MEANINGLESS -DOPENSSL_NO_STDIO -DOPENSSL_NO_FP_API -DOPENSSL_NO_DGRAM -DOPENSSL_NO_SHA0 -DOPENSSL_NO_LHASH -DOPENSSL_NO_HW -DOPENSSL_NO_OCSP -DOPENSSL_NO_LOCKING -DOPENSSL_NO_DEPRECATED -DOPENSSL_SMALL_FOOTPRINT -DPEDANTIC ifeq ($(ARCH),x86_64) CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI endif @@ -215,6 +215,7 @@ OBJS = crypto/cryptlib.o \ crypto/bio/bf_null.o \ crypto/bio/bf_buff.o \ crypto/bio/b_dump.o \ + crypto/bio/b_print.o \ crypto/bio/bf_nbio.o \ crypto/bio/bss_log.o \ crypto/bio/bss_bio.o \ diff --git a/Cryptlib/OpenSSL/crypto/bio/b_print.c b/Cryptlib/OpenSSL/crypto/bio/b_print.c new file mode 100644 index 0000000..b8b630c --- /dev/null +++ b/Cryptlib/OpenSSL/crypto/bio/b_print.c @@ -0,0 +1,850 @@ +/* crypto/bio/b_print.c */ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* disable assert() unless BIO_DEBUG has been defined */ +#ifndef BIO_DEBUG +# ifndef NDEBUG +# define NDEBUG +# endif +#endif + +/* + * Stolen from tjh's ssl/ssl_trc.c stuff. + */ + +#include +#include +#include +#include +#include +#include "cryptlib.h" +#ifndef NO_SYS_TYPES_H +#include +#endif +#include /* To get BN_LLONG properly defined */ +#include + +#if defined(BN_LLONG) || defined(SIXTY_FOUR_BIT) +# ifndef HAVE_LONG_LONG +# define HAVE_LONG_LONG 1 +# endif +#endif + +/***************************************************************************/ + +/* + * Copyright Patrick Powell 1995 + * This code is based on code written by Patrick Powell + * It may be used for any purpose as long as this notice remains intact + * on all source code distributions. + */ + +/* + * This code contains numerious changes and enhancements which were + * made by lots of contributors over the last years to Patrick Powell's + * original code: + * + * o Patrick Powell (1995) + * o Brandon Long (1996, for Mutt) + * o Thomas Roessler (1998, for Mutt) + * o Michael Elkins (1998, for Mutt) + * o Andrew Tridgell (1998, for Samba) + * o Luke Mewburn (1999, for LukemFTP) + * o Ralf S. Engelschall (1999, for Pth) + * o ... (for OpenSSL) + */ + +#ifdef HAVE_LONG_DOUBLE +#define LDOUBLE long double +#else +#define LDOUBLE double +#endif + +#if HAVE_LONG_LONG +# if defined(OPENSSL_SYS_WIN32) && !defined(__GNUC__) +# define LLONG __int64 +# else +# define LLONG long long +# endif +#else +#define LLONG long +#endif + +static void fmtstr (char **, char **, size_t *, size_t *, + const char *, int, int, int); +static void fmtint (char **, char **, size_t *, size_t *, + LLONG, int, int, int, int); +#ifndef OPENSSL_SYS_UEFI +static void fmtfp (char **, char **, size_t *, size_t *, + LDOUBLE, int, int, int); +#endif +static void doapr_outch (char **, char **, size_t *, size_t *, int); +static void _dopr(char **sbuffer, char **buffer, + size_t *maxlen, size_t *retlen, int *truncated, + const char *format, va_list args); + +/* format read states */ +#define DP_S_DEFAULT 0 +#define DP_S_FLAGS 1 +#define DP_S_MIN 2 +#define DP_S_DOT 3 +#define DP_S_MAX 4 +#define DP_S_MOD 5 +#define DP_S_CONV 6 +#define DP_S_DONE 7 + +/* format flags - Bits */ +#define DP_F_MINUS (1 << 0) +#define DP_F_PLUS (1 << 1) +#define DP_F_SPACE (1 << 2) +#define DP_F_NUM (1 << 3) +#define DP_F_ZERO (1 << 4) +#define DP_F_UP (1 << 5) +#define DP_F_UNSIGNED (1 << 6) + +/* conversion flags */ +#define DP_C_SHORT 1 +#define DP_C_LONG 2 +#define DP_C_LDOUBLE 3 +#define DP_C_LLONG 4 + +/* some handy macros */ +#define char_to_int(p) (p - '0') +#define OSSL_MAX(p,q) ((p >= q) ? p : q) + +static void +_dopr( + char **sbuffer, + char **buffer, + size_t *maxlen, + size_t *retlen, + int *truncated, + const char *format, + va_list args) +{ + char ch; + LLONG value; +#ifndef OPENSSL_SYS_UEFI + LDOUBLE fvalue; +#endif + char *strvalue; + int min; + int max; + int state; + int flags; + int cflags; + size_t currlen; + + state = DP_S_DEFAULT; + flags = currlen = cflags = min = 0; + max = -1; + ch = *format++; + + while (state != DP_S_DONE) { + if (ch == '\0' || (buffer == NULL && currlen >= *maxlen)) + state = DP_S_DONE; + + switch (state) { + case DP_S_DEFAULT: + if (ch == '%') + state = DP_S_FLAGS; + else + doapr_outch(sbuffer,buffer, &currlen, maxlen, ch); + ch = *format++; + break; + case DP_S_FLAGS: + switch (ch) { + case '-': + flags |= DP_F_MINUS; + ch = *format++; + break; + case '+': + flags |= DP_F_PLUS; + ch = *format++; + break; + case ' ': + flags |= DP_F_SPACE; + ch = *format++; + break; + case '#': + flags |= DP_F_NUM; + ch = *format++; + break; + case '0': + flags |= DP_F_ZERO; + ch = *format++; + break; + default: + state = DP_S_MIN; + break; + } + break; + case DP_S_MIN: + if (isdigit((unsigned char)ch)) { + min = 10 * min + char_to_int(ch); + ch = *format++; + } else if (ch == '*') { + min = va_arg(args, int); + ch = *format++; + state = DP_S_DOT; + } else + state = DP_S_DOT; + break; + case DP_S_DOT: + if (ch == '.') { + state = DP_S_MAX; + ch = *format++; + } else + state = DP_S_MOD; + break; + case DP_S_MAX: + if (isdigit((unsigned char)ch)) { + if (max < 0) + max = 0; + max = 10 * max + char_to_int(ch); + ch = *format++; + } else if (ch == '*') { + max = va_arg(args, int); + ch = *format++; + state = DP_S_MOD; + } else + state = DP_S_MOD; + break; + case DP_S_MOD: + switch (ch) { + case 'h': + cflags = DP_C_SHORT; + ch = *format++; + break; + case 'l': + if (*format == 'l') { + cflags = DP_C_LLONG; + format++; + } else + cflags = DP_C_LONG; + ch = *format++; + break; + case 'q': + cflags = DP_C_LLONG; + ch = *format++; + break; + case 'L': + cflags = DP_C_LDOUBLE; + ch = *format++; + break; + default: + break; + } + state = DP_S_CONV; + break; + case DP_S_CONV: + switch (ch) { + case 'd': + case 'i': + switch (cflags) { + case DP_C_SHORT: + value = (short int)va_arg(args, int); + break; + case DP_C_LONG: + value = va_arg(args, long int); + break; + case DP_C_LLONG: + value = va_arg(args, LLONG); + break; + default: + value = va_arg(args, int); + break; + } + fmtint(sbuffer, buffer, &currlen, maxlen, + value, 10, min, max, flags); + break; + case 'X': + flags |= DP_F_UP; + /* FALLTHROUGH */ + case 'x': + case 'o': + case 'u': + flags |= DP_F_UNSIGNED; + switch (cflags) { + case DP_C_SHORT: + value = (unsigned short int)va_arg(args, unsigned int); + break; + case DP_C_LONG: + value = (LLONG) va_arg(args, + unsigned long int); + break; + case DP_C_LLONG: + value = va_arg(args, unsigned LLONG); + break; + default: + value = (LLONG) va_arg(args, + unsigned int); + break; + } + fmtint(sbuffer, buffer, &currlen, maxlen, value, + ch == 'o' ? 8 : (ch == 'u' ? 10 : 16), + min, max, flags); + break; +#ifndef OPENSSL_SYS_UEFI + case 'f': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg(args, LDOUBLE); + else + fvalue = va_arg(args, double); + fmtfp(sbuffer, buffer, &currlen, maxlen, + fvalue, min, max, flags); + break; + case 'E': + flags |= DP_F_UP; + case 'e': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg(args, LDOUBLE); + else + fvalue = va_arg(args, double); + break; + case 'G': + flags |= DP_F_UP; + case 'g': + if (cflags == DP_C_LDOUBLE) + fvalue = va_arg(args, LDOUBLE); + else + fvalue = va_arg(args, double); + break; +#endif + case 'c': + doapr_outch(sbuffer, buffer, &currlen, maxlen, + va_arg(args, int)); + break; + case 's': + strvalue = va_arg(args, char *); + if (max < 0) { + if (buffer) + max = INT_MAX; + else + max = *maxlen; + } + fmtstr(sbuffer, buffer, &currlen, maxlen, strvalue, + flags, min, max); + break; + case 'p': + value = (long)va_arg(args, void *); + fmtint(sbuffer, buffer, &currlen, maxlen, + value, 16, min, max, flags|DP_F_NUM); + break; + case 'n': /* XXX */ + if (cflags == DP_C_SHORT) { + short int *num; + num = va_arg(args, short int *); + *num = currlen; + } else if (cflags == DP_C_LONG) { /* XXX */ + long int *num; + num = va_arg(args, long int *); + *num = (long int) currlen; + } else if (cflags == DP_C_LLONG) { /* XXX */ + LLONG *num; + num = va_arg(args, LLONG *); + *num = (LLONG) currlen; + } else { + int *num; + num = va_arg(args, int *); + *num = currlen; + } + break; + case '%': + doapr_outch(sbuffer, buffer, &currlen, maxlen, ch); + break; + case 'w': + /* not supported yet, treat as next char */ + ch = *format++; + break; + default: + /* unknown, skip */ + break; + } + ch = *format++; + state = DP_S_DEFAULT; + flags = cflags = min = 0; + max = -1; + break; + case DP_S_DONE: + break; + default: + break; + } + } + *truncated = (currlen > *maxlen - 1); + if (*truncated) + currlen = *maxlen - 1; + doapr_outch(sbuffer, buffer, &currlen, maxlen, '\0'); + *retlen = currlen - 1; + return; +} + +static void +fmtstr( + char **sbuffer, + char **buffer, + size_t *currlen, + size_t *maxlen, + const char *value, + int flags, + int min, + int max) +{ + int padlen, strln; + int cnt = 0; + + if (value == 0) + value = ""; + for (strln = 0; value[strln]; ++strln) + ; + padlen = min - strln; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; + + while ((padlen > 0) && (cnt < max)) { + doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + --padlen; + ++cnt; + } + while (*value && (cnt < max)) { + doapr_outch(sbuffer, buffer, currlen, maxlen, *value++); + ++cnt; + } + while ((padlen < 0) && (cnt < max)) { + doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + ++padlen; + ++cnt; + } +} + +static void +fmtint( + char **sbuffer, + char **buffer, + size_t *currlen, + size_t *maxlen, + LLONG value, + int base, + int min, + int max, + int flags) +{ + int signvalue = 0; + const char *prefix = ""; + unsigned LLONG uvalue; + char convert[DECIMAL_SIZE(value)+3]; + int place = 0; + int spadlen = 0; + int zpadlen = 0; + int caps = 0; + + if (max < 0) + max = 0; + uvalue = value; + if (!(flags & DP_F_UNSIGNED)) { + if (value < 0) { + signvalue = '-'; + uvalue = -value; + } else if (flags & DP_F_PLUS) + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + } + if (flags & DP_F_NUM) { + if (base == 8) prefix = "0"; + if (base == 16) prefix = "0x"; + } + if (flags & DP_F_UP) + caps = 1; + do { + convert[place++] = + (caps ? "0123456789ABCDEF" : "0123456789abcdef") + [uvalue % (unsigned) base]; + uvalue = (uvalue / (unsigned) base); + } while (uvalue && (place < (int)sizeof(convert))); + if (place == sizeof(convert)) + place--; + convert[place] = 0; + + zpadlen = max - place; + spadlen = min - OSSL_MAX(max, place) - (signvalue ? 1 : 0) - strlen(prefix); + if (zpadlen < 0) + zpadlen = 0; + if (spadlen < 0) + spadlen = 0; + if (flags & DP_F_ZERO) { + zpadlen = OSSL_MAX(zpadlen, spadlen); + spadlen = 0; + } + if (flags & DP_F_MINUS) + spadlen = -spadlen; + + /* spaces */ + while (spadlen > 0) { + doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + --spadlen; + } + + /* sign */ + if (signvalue) + doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue); + + /* prefix */ + while (*prefix) { + doapr_outch(sbuffer, buffer, currlen, maxlen, *prefix); + prefix++; + } + + /* zeros */ + if (zpadlen > 0) { + while (zpadlen > 0) { + doapr_outch(sbuffer, buffer, currlen, maxlen, '0'); + --zpadlen; + } + } + /* digits */ + while (place > 0) + doapr_outch(sbuffer, buffer, currlen, maxlen, convert[--place]); + + /* left justified spaces */ + while (spadlen < 0) { + doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + ++spadlen; + } + return; +} + +#ifndef OPENSSL_SYS_UEFI +static LDOUBLE +abs_val(LDOUBLE value) +{ + LDOUBLE result = value; + if (value < 0) + result = -value; + return result; +} + +static LDOUBLE +pow_10(int in_exp) +{ + LDOUBLE result = 1; + while (in_exp) { + result *= 10; + in_exp--; + } + return result; +} + +static long +roundv(LDOUBLE value) +{ + long intpart; + intpart = (long) value; + value = value - intpart; + if (value >= 0.5) + intpart++; + return intpart; +} + +static void +fmtfp( + char **sbuffer, + char **buffer, + size_t *currlen, + size_t *maxlen, + LDOUBLE fvalue, + int min, + int max, + int flags) +{ + int signvalue = 0; + LDOUBLE ufvalue; + char iconvert[20]; + char fconvert[20]; + int iplace = 0; + int fplace = 0; + int padlen = 0; + int zpadlen = 0; + int caps = 0; + long intpart; + long fracpart; + long max10; + + if (max < 0) + max = 6; + ufvalue = abs_val(fvalue); + if (fvalue < 0) + signvalue = '-'; + else if (flags & DP_F_PLUS) + signvalue = '+'; + else if (flags & DP_F_SPACE) + signvalue = ' '; + + intpart = (long)ufvalue; + + /* sorry, we only support 9 digits past the decimal because of our + conversion method */ + if (max > 9) + max = 9; + + /* we "cheat" by converting the fractional part to integer by + multiplying by a factor of 10 */ + max10 = roundv(pow_10(max)); + fracpart = roundv(pow_10(max) * (ufvalue - intpart)); + + if (fracpart >= max10) { + intpart++; + fracpart -= max10; + } + + /* convert integer part */ + do { + iconvert[iplace++] = + (caps ? "0123456789ABCDEF" + : "0123456789abcdef")[intpart % 10]; + intpart = (intpart / 10); + } while (intpart && (iplace < (int)sizeof(iconvert))); + if (iplace == sizeof iconvert) + iplace--; + iconvert[iplace] = 0; + + /* convert fractional part */ + do { + fconvert[fplace++] = + (caps ? "0123456789ABCDEF" + : "0123456789abcdef")[fracpart % 10]; + fracpart = (fracpart / 10); + } while (fplace < max); + if (fplace == sizeof fconvert) + fplace--; + fconvert[fplace] = 0; + + /* -1 for decimal point, another -1 if we are printing a sign */ + padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); + zpadlen = max - fplace; + if (zpadlen < 0) + zpadlen = 0; + if (padlen < 0) + padlen = 0; + if (flags & DP_F_MINUS) + padlen = -padlen; + + if ((flags & DP_F_ZERO) && (padlen > 0)) { + if (signvalue) { + doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue); + --padlen; + signvalue = 0; + } + while (padlen > 0) { + doapr_outch(sbuffer, buffer, currlen, maxlen, '0'); + --padlen; + } + } + while (padlen > 0) { + doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + --padlen; + } + if (signvalue) + doapr_outch(sbuffer, buffer, currlen, maxlen, signvalue); + + while (iplace > 0) + doapr_outch(sbuffer, buffer, currlen, maxlen, iconvert[--iplace]); + + /* + * Decimal point. This should probably use locale to find the correct + * char to print out. + */ + if (max > 0 || (flags & DP_F_NUM)) { + doapr_outch(sbuffer, buffer, currlen, maxlen, '.'); + + while (fplace > 0) + doapr_outch(sbuffer, buffer, currlen, maxlen, fconvert[--fplace]); + } + while (zpadlen > 0) { + doapr_outch(sbuffer, buffer, currlen, maxlen, '0'); + --zpadlen; + } + + while (padlen < 0) { + doapr_outch(sbuffer, buffer, currlen, maxlen, ' '); + ++padlen; + } +} +#endif + +static void +doapr_outch( + char **sbuffer, + char **buffer, + size_t *currlen, + size_t *maxlen, + int c) +{ + /* If we haven't at least one buffer, someone has doe a big booboo */ + assert(*sbuffer != NULL || buffer != NULL); + + if (buffer) { + while (*currlen >= *maxlen) { + if (*buffer == NULL) { + if (*maxlen == 0) + *maxlen = 1024; + *buffer = OPENSSL_malloc(*maxlen); + if (*currlen > 0) { + assert(*sbuffer != NULL); + memcpy(*buffer, *sbuffer, *currlen); + } + *sbuffer = NULL; + } else { + *maxlen += 1024; + *buffer = OPENSSL_realloc(*buffer, *maxlen); + } + } + /* What to do if *buffer is NULL? */ + assert(*sbuffer != NULL || *buffer != NULL); + } + + if (*currlen < *maxlen) { + if (*sbuffer) + (*sbuffer)[(*currlen)++] = (char)c; + else + (*buffer)[(*currlen)++] = (char)c; + } + + return; +} + +/***************************************************************************/ + +int BIO_printf (BIO *bio, const char *format, ...) + { + va_list args; + int ret; + + va_start(args, format); + + ret = BIO_vprintf(bio, format, args); + + va_end(args); + return(ret); + } + +int BIO_vprintf (BIO *bio, const char *format, va_list args) + { + int ret; + size_t retlen; + char hugebuf[1024*2]; /* Was previously 10k, which is unreasonable + in small-stack environments, like threads + or DOS programs. */ + char *hugebufp = hugebuf; + size_t hugebufsize = sizeof(hugebuf); + char *dynbuf = NULL; + int ignored; + + dynbuf = NULL; + CRYPTO_push_info("doapr()"); + _dopr(&hugebufp, &dynbuf, &hugebufsize, + &retlen, &ignored, format, args); + if (dynbuf) + { + ret=BIO_write(bio, dynbuf, (int)retlen); + OPENSSL_free(dynbuf); + } + else + { + ret=BIO_write(bio, hugebuf, (int)retlen); + } + CRYPTO_pop_info(); + return(ret); + } + +/* As snprintf is not available everywhere, we provide our own implementation. + * This function has nothing to do with BIOs, but it's closely related + * to BIO_printf, and we need *some* name prefix ... + * (XXX the function should be renamed, but to what?) */ +int BIO_snprintf(char *buf, size_t n, const char *format, ...) + { + va_list args; + int ret; + + va_start(args, format); + + ret = BIO_vsnprintf(buf, n, format, args); + + va_end(args); + return(ret); + } + +int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args) + { + size_t retlen; + int truncated; + + _dopr(&buf, NULL, &n, &retlen, &truncated, format, args); + + if (truncated) + /* In case of truncation, return -1 like traditional snprintf. + * (Current drafts for ISO/IEC 9899 say snprintf should return + * the number of characters that would have been written, + * had the buffer been large enough.) */ + return -1; + else + return (retlen <= INT_MAX) ? (int)retlen : -1; + } diff --git a/Cryptlib/OpenSSL/crypto/pkcs7/pk7_smime.c b/Cryptlib/OpenSSL/crypto/pkcs7/pk7_smime.c index d6db27c..b0ff89a 100755 --- a/Cryptlib/OpenSSL/crypto/pkcs7/pk7_smime.c +++ b/Cryptlib/OpenSSL/crypto/pkcs7/pk7_smime.c @@ -176,7 +176,8 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, STACK_OF(PKCS7_SIGNER_INFO) *sinfos; PKCS7_SIGNER_INFO *si; X509_STORE_CTX cert_ctx; - char buf[4096]; + char *buf = NULL; + int bufsiz; int i, j=0, k, ret = 0; BIO *p7bio; BIO *tmpin, *tmpout; @@ -287,10 +288,16 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, BIO_set_mem_eof_return(tmpout, 0); } else tmpout = out; + bufsiz = 4096; + buf = OPENSSL_malloc (bufsiz); + if (buf == NULL) { + goto err; + } + /* We now have to 'read' from p7bio to calculate digests etc. */ for (;;) { - i=BIO_read(p7bio,buf,sizeof(buf)); + i=BIO_read(p7bio,buf,bufsiz); if (i <= 0) break; if (tmpout) BIO_write(tmpout, buf, i); } @@ -329,6 +336,10 @@ int PKCS7_verify(PKCS7 *p7, STACK_OF(X509) *certs, X509_STORE *store, sk_X509_free(signers); + if (buf != NULL) { + OPENSSL_free (buf); + } + return ret; } diff --git a/Cryptlib/Pem/CryptPem.c b/Cryptlib/Pem/CryptPem.c index 2c3a97b..51e648b 100644 --- a/Cryptlib/Pem/CryptPem.c +++ b/Cryptlib/Pem/CryptPem.c @@ -1,7 +1,7 @@ /** @file PEM (Privacy Enhanced Mail) Format Handler Wrapper Implementation over OpenSSL. -Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.
+Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -36,7 +36,7 @@ PasswordCallback ( { INTN KeyLength; - ZeroMem ((VOID *)Buf, (UINTN)Size); + ZeroMem ((VOID *) Buf, (UINTN) Size); if (Key != NULL) { // // Duplicate key phrase directly. @@ -86,31 +86,41 @@ RsaGetPrivateKeyFromPem ( return FALSE; } - Status = FALSE; - PemBio = NULL; - // // Add possible block-cipher descriptor for PEM data decryption. // NOTE: Only support most popular ciphers (3DES, AES) for the encrypted PEM. // - EVP_add_cipher (EVP_des_ede3_cbc()); - EVP_add_cipher (EVP_aes_128_cbc()); - EVP_add_cipher (EVP_aes_192_cbc()); - EVP_add_cipher (EVP_aes_256_cbc()); + if (EVP_add_cipher (EVP_des_ede3_cbc ()) == 0) { + return FALSE; + } + if (EVP_add_cipher (EVP_aes_128_cbc ()) == 0) { + return FALSE; + } + if (EVP_add_cipher (EVP_aes_192_cbc ()) == 0) { + return FALSE; + } + if (EVP_add_cipher (EVP_aes_256_cbc ()) == 0) { + return FALSE; + } + + Status = FALSE; // // Read encrypted PEM Data. // PemBio = BIO_new (BIO_s_mem ()); - BIO_write (PemBio, PemData, (int)PemSize); if (PemBio == NULL) { goto _Exit; } + if (BIO_write (PemBio, PemData, (int) PemSize) <= 0) { + goto _Exit; + } + // // Retrieve RSA Private Key from encrypted PEM data. // - *RsaContext = PEM_read_bio_RSAPrivateKey (PemBio, NULL, (pem_password_cb *)&PasswordCallback, (void *)Password); + *RsaContext = PEM_read_bio_RSAPrivateKey (PemBio, NULL, (pem_password_cb *) &PasswordCallback, (void *) Password); if (*RsaContext != NULL) { Status = TRUE; } diff --git a/Cryptlib/Pk/CryptAuthenticode.c b/Cryptlib/Pk/CryptAuthenticode.c index a4f62b2..bb5f6d4 100644 --- a/Cryptlib/Pk/CryptAuthenticode.c +++ b/Cryptlib/Pk/CryptAuthenticode.c @@ -26,6 +26,12 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include +// +// OID ASN.1 Value for SPC_INDIRECT_DATA_OBJID +// +UINT8 mSpcIndirectOidValue[] = { + 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04 + }; /** Verifies the validility of a PE/COFF Authenticode Signature as described in "Windows @@ -70,6 +76,7 @@ AuthenticodeVerify ( UINT8 *SpcIndirectDataContent; UINT8 Asn1Byte; UINTN ContentSize; + UINT8 *SpcIndirectDataOid; // // Check input parameters. @@ -106,6 +113,19 @@ AuthenticodeVerify ( // some authenticode-specific structure. Use opaque ASN.1 string to retrieve // PKCS#7 ContentInfo here. // + SpcIndirectDataOid = (UINT8 *)(Pkcs7->d.sign->contents->type->data); + if (CompareMem ( + SpcIndirectDataOid, + mSpcIndirectOidValue, + sizeof (mSpcIndirectOidValue) + ) != 0) { + // + // Un-matched SPC_INDIRECT_DATA_OBJID. + // + goto _Exit; + } + + SpcIndirectDataContent = (UINT8 *)(Pkcs7->d.sign->contents->d.other->value.asn1_string->data); // diff --git a/Cryptlib/Pk/CryptDh.c b/Cryptlib/Pk/CryptDh.c index 20f1346..942b3d1 100644 --- a/Cryptlib/Pk/CryptDh.c +++ b/Cryptlib/Pk/CryptDh.c @@ -32,7 +32,7 @@ DhNew ( // // Allocates & Initializes DH Context by OpenSSL DH_new() // - return (VOID *)DH_new (); + return (VOID *) DH_new (); } /** @@ -52,7 +52,7 @@ DhFree ( // // Free OpenSSL DH Context // - DH_free ((DH *)DhContext); + DH_free ((DH *) DhContext); } /** @@ -91,7 +91,7 @@ DhGenerateParameter ( // // Check input parameters. // - if (DhContext == NULL || Prime == NULL) { + if (DhContext == NULL || Prime == NULL || PrimeLength > INT_MAX) { return FALSE; } @@ -139,12 +139,13 @@ DhSetParameter ( IN CONST UINT8 *Prime ) { - DH *Dh; + DH *Dh; + BIGNUM *Bn; // // Check input parameters. // - if (DhContext == NULL || Prime == NULL) { + if (DhContext == NULL || Prime == NULL || PrimeLength > INT_MAX) { return FALSE; } @@ -152,14 +153,46 @@ DhSetParameter ( return FALSE; } - Dh = (DH *) DhContext; - Dh->p = BN_new(); - Dh->g = BN_new(); + Bn = NULL; - BN_bin2bn (Prime, (UINT32) (PrimeLength / 8), Dh->p); - BN_set_word (Dh->g, (UINT32) Generator); + Dh = (DH *) DhContext; + Dh->g = NULL; + Dh->p = BN_new (); + if (Dh->p == NULL) { + goto Error; + } + + Dh->g = BN_new (); + if (Dh->g == NULL) { + goto Error; + } + + Bn = BN_bin2bn (Prime, (UINT32) (PrimeLength / 8), Dh->p); + if (Bn == NULL) { + goto Error; + } + + if (BN_set_word (Dh->g, (UINT32) Generator) == 0) { + goto Error; + } return TRUE; + +Error: + + if (Dh->p != NULL) { + BN_free (Dh->p); + } + + if (Dh->g != NULL) { + BN_free (Dh->g); + } + + if (Bn != NULL) { + BN_free (Bn); + } + + return FALSE; } /** @@ -194,6 +227,7 @@ DhGenerateKey ( { BOOLEAN RetVal; DH *Dh; + INTN Size; // // Check input parameters. @@ -207,12 +241,17 @@ DhGenerateKey ( } Dh = (DH *) DhContext; - *PublicKeySize = 0; RetVal = (BOOLEAN) DH_generate_key (DhContext); if (RetVal) { + Size = BN_num_bytes (Dh->pub_key); + if ((Size > 0) && (*PublicKeySize < (UINTN) Size)) { + *PublicKeySize = Size; + return FALSE; + } + BN_bn2bin (Dh->pub_key, PublicKey); - *PublicKeySize = BN_num_bytes (Dh->pub_key); + *PublicKeySize = Size; } return RetVal; @@ -227,7 +266,8 @@ DhGenerateKey ( If DhContext is NULL, then return FALSE. If PeerPublicKey is NULL, then return FALSE. If KeySize is NULL, then return FALSE. - If KeySize is large enough but Key is NULL, then return FALSE. + If Key is NULL, then return FALSE. + If KeySize is not large enough, then return FALSE. @param[in, out] DhContext Pointer to the DH context. @param[in] PeerPublicKey Pointer to the peer's public key. @@ -252,23 +292,37 @@ DhComputeKey ( ) { BIGNUM *Bn; + INTN Size; // // Check input parameters. // - if (DhContext == NULL || PeerPublicKey == NULL || KeySize == NULL) { + if (DhContext == NULL || PeerPublicKey == NULL || KeySize == NULL || Key == NULL) { return FALSE; } - if (Key == NULL && *KeySize != 0) { + if (PeerPublicKeySize > INT_MAX) { return FALSE; } Bn = BN_bin2bn (PeerPublicKey, (UINT32) PeerPublicKeySize, NULL); + if (Bn == NULL) { + return FALSE; + } - *KeySize = (BOOLEAN) DH_compute_key (Key, Bn, DhContext); + Size = DH_compute_key (Key, Bn, DhContext); + if (Size < 0) { + BN_free (Bn); + return FALSE; + } + if (*KeySize < (UINTN) Size) { + *KeySize = Size; + BN_free (Bn); + return FALSE; + } + + *KeySize = Size; BN_free (Bn); - return TRUE; } diff --git a/Cryptlib/Pk/CryptRsa.c b/Cryptlib/Pk/CryptRsa.c deleted file mode 100644 index 0483353..0000000 --- a/Cryptlib/Pk/CryptRsa.c +++ /dev/null @@ -1,722 +0,0 @@ -/** @file - RSA Asymmetric Cipher Wrapper Implementation over OpenSSL. - -Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#include "InternalCryptLib.h" - -#include -#include - -// -// ASN.1 value for Hash Algorithm ID with the Distringuished Encoding Rules (DER) -// Refer to Section 9.2 of PKCS#1 v2.1 -// -CONST UINT8 Asn1IdMd5[] = { - 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, - 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 - }; - -CONST UINT8 Asn1IdSha1[] = { - 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, - 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 - }; - -CONST UINT8 Asn1IdSha256[] = { - 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, - 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, - 0x00, 0x04, 0x20 - }; - - -/** - Allocates and initializes one RSA context for subsequent use. - - @return Pointer to the RSA context that has been initialized. - If the allocations fails, RsaNew() returns NULL. - -**/ -VOID * -EFIAPI -RsaNew ( - VOID - ) -{ - // - // Allocates & Initializes RSA Context by OpenSSL RSA_new() - // - return (VOID *)RSA_new (); -} - -/** - Release the specified RSA context. - - If RsaContext is NULL, then return FALSE. - - @param[in] RsaContext Pointer to the RSA context to be released. - -**/ -VOID -EFIAPI -RsaFree ( - IN VOID *RsaContext - ) -{ - // - // Free OpenSSL RSA Context - // - RSA_free ((RSA *)RsaContext); -} - -/** - Sets the tag-designated key component into the established RSA context. - - This function sets the tag-designated RSA key component into the established - RSA context from the user-specified non-negative integer (octet string format - represented in RSA PKCS#1). - If BigNumber is NULL, then the specified key componenet in RSA context is cleared. - - If RsaContext is NULL, then return FALSE. - - @param[in, out] RsaContext Pointer to RSA context being set. - @param[in] KeyTag Tag of RSA key component being set. - @param[in] BigNumber Pointer to octet integer buffer. - If NULL, then the specified key componenet in RSA - context is cleared. - @param[in] BnSize Size of big number buffer in bytes. - If BigNumber is NULL, then it is ignored. - - @retval TRUE RSA key component was set successfully. - @retval FALSE Invalid RSA key component tag. - -**/ -BOOLEAN -EFIAPI -RsaSetKey ( - IN OUT VOID *RsaContext, - IN RSA_KEY_TAG KeyTag, - IN CONST UINT8 *BigNumber, - IN UINTN BnSize - ) -{ - RSA *RsaKey; - - // - // Check input parameters. - // - if (RsaContext == NULL) { - return FALSE; - } - - RsaKey = (RSA *)RsaContext; - // - // Set RSA Key Components by converting octet string to OpenSSL BN representation. - // NOTE: For RSA public key (used in signature verification), only public components - // (N, e) are needed. - // - switch (KeyTag) { - - // - // RSA Public Modulus (N) - // - case RsaKeyN: - if (RsaKey->n != NULL) { - BN_free (RsaKey->n); - } - RsaKey->n = NULL; - if (BigNumber == NULL) { - break; - } - RsaKey->n = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->n); - break; - - // - // RSA Public Exponent (e) - // - case RsaKeyE: - if (RsaKey->e != NULL) { - BN_free (RsaKey->e); - } - RsaKey->e = NULL; - if (BigNumber == NULL) { - break; - } - RsaKey->e = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->e); - break; - - // - // RSA Private Exponent (d) - // - case RsaKeyD: - if (RsaKey->d != NULL) { - BN_free (RsaKey->d); - } - RsaKey->d = NULL; - if (BigNumber == NULL) { - break; - } - RsaKey->d = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->d); - break; - - // - // RSA Secret Prime Factor of Modulus (p) - // - case RsaKeyP: - if (RsaKey->p != NULL) { - BN_free (RsaKey->p); - } - RsaKey->p = NULL; - if (BigNumber == NULL) { - break; - } - RsaKey->p = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->p); - break; - - // - // RSA Secret Prime Factor of Modules (q) - // - case RsaKeyQ: - if (RsaKey->q != NULL) { - BN_free (RsaKey->q); - } - RsaKey->q = NULL; - if (BigNumber == NULL) { - break; - } - RsaKey->q = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->q); - break; - - // - // p's CRT Exponent (== d mod (p - 1)) - // - case RsaKeyDp: - if (RsaKey->dmp1 != NULL) { - BN_free (RsaKey->dmp1); - } - RsaKey->dmp1 = NULL; - if (BigNumber == NULL) { - break; - } - RsaKey->dmp1 = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->dmp1); - break; - - // - // q's CRT Exponent (== d mod (q - 1)) - // - case RsaKeyDq: - if (RsaKey->dmq1 != NULL) { - BN_free (RsaKey->dmq1); - } - RsaKey->dmq1 = NULL; - if (BigNumber == NULL) { - break; - } - RsaKey->dmq1 = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->dmq1); - break; - - // - // The CRT Coefficient (== 1/q mod p) - // - case RsaKeyQInv: - if (RsaKey->iqmp != NULL) { - BN_free (RsaKey->iqmp); - } - RsaKey->iqmp = NULL; - if (BigNumber == NULL) { - break; - } - RsaKey->iqmp = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->iqmp); - break; - - default: - return FALSE; - } - - return TRUE; -} - -/** - Gets the tag-designated RSA key component from the established RSA context. - - This function retrieves the tag-designated RSA key component from the - established RSA context as a non-negative integer (octet string format - represented in RSA PKCS#1). - If specified key component has not been set or has been cleared, then returned - BnSize is set to 0. - If the BigNumber buffer is too small to hold the contents of the key, FALSE - is returned and BnSize is set to the required buffer size to obtain the key. - - If RsaContext is NULL, then return FALSE. - If BnSize is NULL, then return FALSE. - If BnSize is large enough but BigNumber is NULL, then return FALSE. - - @param[in, out] RsaContext Pointer to RSA context being set. - @param[in] KeyTag Tag of RSA key component being set. - @param[out] BigNumber Pointer to octet integer buffer. - @param[in, out] BnSize On input, the size of big number buffer in bytes. - On output, the size of data returned in big number buffer in bytes. - - @retval TRUE RSA key component was retrieved successfully. - @retval FALSE Invalid RSA key component tag. - @retval FALSE BnSize is too small. - -**/ -BOOLEAN -EFIAPI -RsaGetKey ( - IN OUT VOID *RsaContext, - IN RSA_KEY_TAG KeyTag, - OUT UINT8 *BigNumber, - IN OUT UINTN *BnSize - ) -{ - RSA *RsaKey; - BIGNUM *BnKey; - UINTN Size; - - // - // Check input parameters. - // - if (RsaContext == NULL || BnSize == NULL) { - return FALSE; - } - - RsaKey = (RSA *) RsaContext; - Size = *BnSize; - *BnSize = 0; - - switch (KeyTag) { - - // - // RSA Public Modulus (N) - // - case RsaKeyN: - if (RsaKey->n == NULL) { - return TRUE; - } - BnKey = RsaKey->n; - break; - - // - // RSA Public Exponent (e) - // - case RsaKeyE: - if (RsaKey->e == NULL) { - return TRUE; - } - BnKey = RsaKey->e; - break; - - // - // RSA Private Exponent (d) - // - case RsaKeyD: - if (RsaKey->d == NULL) { - return TRUE; - } - BnKey = RsaKey->d; - break; - - // - // RSA Secret Prime Factor of Modulus (p) - // - case RsaKeyP: - if (RsaKey->p == NULL) { - return TRUE; - } - BnKey = RsaKey->p; - break; - - // - // RSA Secret Prime Factor of Modules (q) - // - case RsaKeyQ: - if (RsaKey->q == NULL) { - return TRUE; - } - BnKey = RsaKey->q; - break; - - // - // p's CRT Exponent (== d mod (p - 1)) - // - case RsaKeyDp: - if (RsaKey->dmp1 == NULL) { - return TRUE; - } - BnKey = RsaKey->dmp1; - break; - - // - // q's CRT Exponent (== d mod (q - 1)) - // - case RsaKeyDq: - if (RsaKey->dmq1 == NULL) { - return TRUE; - } - BnKey = RsaKey->dmq1; - break; - - // - // The CRT Coefficient (== 1/q mod p) - // - case RsaKeyQInv: - if (RsaKey->iqmp == NULL) { - return TRUE; - } - BnKey = RsaKey->iqmp; - break; - - default: - return FALSE; - } - - *BnSize = Size; - Size = BN_num_bytes (BnKey); - - if (*BnSize < Size) { - *BnSize = Size; - return FALSE; - } - - if (BigNumber == NULL) { - return FALSE; - } - *BnSize = BN_bn2bin (BnKey, BigNumber) ; - - return TRUE; -} - -/** - Generates RSA key components. - - This function generates RSA key components. It takes RSA public exponent E and - length in bits of RSA modulus N as input, and generates all key components. - If PublicExponent is NULL, the default RSA public exponent (0x10001) will be used. - - Before this function can be invoked, pseudorandom number generator must be correctly - initialized by RandomSeed(). - - If RsaContext is NULL, then return FALSE. - - @param[in, out] RsaContext Pointer to RSA context being set. - @param[in] ModulusLength Length of RSA modulus N in bits. - @param[in] PublicExponent Pointer to RSA public exponent. - @param[in] PublicExponentSize Size of RSA public exponent buffer in bytes. - - @retval TRUE RSA key component was generated successfully. - @retval FALSE Invalid RSA key component tag. - -**/ -BOOLEAN -EFIAPI -RsaGenerateKey ( - IN OUT VOID *RsaContext, - IN UINTN ModulusLength, - IN CONST UINT8 *PublicExponent, - IN UINTN PublicExponentSize - ) -{ - BIGNUM *KeyE; - BOOLEAN RetVal; - - // - // Check input parameters. - // - if (RsaContext == NULL) { - return FALSE; - } - - KeyE = BN_new (); - if (PublicExponent == NULL) { - BN_set_word (KeyE, 0x10001); - } else { - BN_bin2bn (PublicExponent, (UINT32) PublicExponentSize, KeyE); - } - - RetVal = FALSE; - if (RSA_generate_key_ex ((RSA *) RsaContext, (UINT32) ModulusLength, KeyE, NULL) == 1) { - RetVal = TRUE; - } - - BN_free (KeyE); - return RetVal; -} - -/** - Validates key components of RSA context. - - This function validates key compoents of RSA context in following aspects: - - Whether p is a prime - - Whether q is a prime - - Whether n = p * q - - Whether d*e = 1 mod lcm(p-1,q-1) - - If RsaContext is NULL, then return FALSE. - - @param[in] RsaContext Pointer to RSA context to check. - - @retval TRUE RSA key components are valid. - @retval FALSE RSA key components are not valid. - -**/ -BOOLEAN -EFIAPI -RsaCheckKey ( - IN VOID *RsaContext - ) -{ - UINTN Reason; - - // - // Check input parameters. - // - if (RsaContext == NULL) { - return FALSE; - } - - if (RSA_check_key ((RSA *) RsaContext) != 1) { - Reason = ERR_GET_REASON (ERR_peek_last_error ()); - if (Reason == RSA_R_P_NOT_PRIME || - Reason == RSA_R_Q_NOT_PRIME || - Reason == RSA_R_N_DOES_NOT_EQUAL_P_Q || - Reason == RSA_R_D_E_NOT_CONGRUENT_TO_1) { - return FALSE; - } - } - - return TRUE; -} - -/** - Performs the PKCS1-v1_5 encoding methods defined in RSA PKCS #1. - - @param Message Message buffer to be encoded. - @param MessageSize Size of message buffer in bytes. - @param DigestInfo Pointer to buffer of digest info for output. - - @return Size of DigestInfo in bytes. - -**/ -UINTN -DigestInfoEncoding ( - IN CONST UINT8 *Message, - IN UINTN MessageSize, - OUT UINT8 *DigestInfo - ) -{ - CONST UINT8 *HashDer; - UINTN DerSize; - - // - // Check input parameters. - // - if (Message == NULL || DigestInfo == NULL) { - return FALSE; - } - - // - // The original message length is used to determine the hash algorithm since - // message is digest value hashed by the specified algorithm. - // - switch (MessageSize) { - case MD5_DIGEST_SIZE: - HashDer = Asn1IdMd5; - DerSize = sizeof (Asn1IdMd5); - break; - - case SHA1_DIGEST_SIZE: - HashDer = Asn1IdSha1; - DerSize = sizeof (Asn1IdSha1); - break; - - case SHA256_DIGEST_SIZE: - HashDer = Asn1IdSha256; - DerSize = sizeof (Asn1IdSha256); - break; - - default: - return FALSE; - } - - CopyMem (DigestInfo, HashDer, DerSize); - CopyMem (DigestInfo + DerSize, Message, MessageSize); - - return (DerSize + MessageSize); -} - -/** - Carries out the RSA-SSA signature generation with EMSA-PKCS1-v1_5 encoding scheme. - - This function carries out the RSA-SSA signature generation with EMSA-PKCS1-v1_5 encoding scheme defined in - RSA PKCS#1. - If the Signature buffer is too small to hold the contents of signature, FALSE - is returned and SigSize is set to the required buffer size to obtain the signature. - - If RsaContext is NULL, then return FALSE. - If MessageHash is NULL, then return FALSE. - If HashSize is not equal to the size of MD5, SHA-1 or SHA-256 digest, then return FALSE. - If SigSize is large enough but Signature is NULL, then return FALSE. - - @param[in] RsaContext Pointer to RSA context for signature generation. - @param[in] MessageHash Pointer to octet message hash to be signed. - @param[in] HashSize Size of the message hash in bytes. - @param[out] Signature Pointer to buffer to receive RSA PKCS1-v1_5 signature. - @param[in, out] SigSize On input, the size of Signature buffer in bytes. - On output, the size of data returned in Signature buffer in bytes. - - @retval TRUE Signature successfully generated in PKCS1-v1_5. - @retval FALSE Signature generation failed. - @retval FALSE SigSize is too small. - -**/ -BOOLEAN -EFIAPI -RsaPkcs1Sign ( - IN VOID *RsaContext, - IN CONST UINT8 *MessageHash, - IN UINTN HashSize, - OUT UINT8 *Signature, - IN OUT UINTN *SigSize - ) -{ - RSA *Rsa; - UINTN Size; - INTN ReturnVal; - - // - // Check input parameters. - // - if (RsaContext == NULL || MessageHash == NULL || - (HashSize != MD5_DIGEST_SIZE && HashSize != SHA1_DIGEST_SIZE && HashSize != SHA256_DIGEST_SIZE)) { - return FALSE; - } - - Rsa = (RSA *) RsaContext; - Size = BN_num_bytes (Rsa->n); - - if (*SigSize < Size) { - *SigSize = Size; - return FALSE; - } - - if (Signature == NULL) { - return FALSE; - } - - Size = DigestInfoEncoding (MessageHash, HashSize, Signature); - - ReturnVal = RSA_private_encrypt ( - (UINT32) Size, - Signature, - Signature, - Rsa, - RSA_PKCS1_PADDING - ); - - if (ReturnVal < (INTN) Size) { - return FALSE; - } - - *SigSize = (UINTN)ReturnVal; - return TRUE; -} - -/** - Verifies the RSA-SSA signature with EMSA-PKCS1-v1_5 encoding scheme defined in - RSA PKCS#1. - - If RsaContext is NULL, then return FALSE. - If MessageHash is NULL, then return FALSE. - If Signature is NULL, then return FALSE. - If HashSize is not equal to the size of MD5, SHA-1 or SHA-256 digest, then return FALSE. - - @param[in] RsaContext Pointer to RSA context for signature verification. - @param[in] MessageHash Pointer to octet message hash to be checked. - @param[in] HashSize Size of the message hash in bytes. - @param[in] Signature Pointer to RSA PKCS1-v1_5 signature to be verified. - @param[in] SigSize Size of signature in bytes. - - @retval TRUE Valid signature encoded in PKCS1-v1_5. - @retval FALSE Invalid signature or invalid RSA context. - -**/ -BOOLEAN -EFIAPI -RsaPkcs1Verify ( - IN VOID *RsaContext, - IN CONST UINT8 *MessageHash, - IN UINTN HashSize, - IN UINT8 *Signature, - IN UINTN SigSize - ) -{ - INTN Length; - - // - // Check input parameters. - // - if (RsaContext == NULL || MessageHash == NULL || Signature == NULL) { - return FALSE; - } - - - // - // Check for unsupported hash size: - // Only MD5, SHA-1 or SHA-256 digest size is supported - // - if (HashSize != MD5_DIGEST_SIZE && HashSize != SHA1_DIGEST_SIZE && HashSize != SHA256_DIGEST_SIZE) { - return FALSE; - } - - // - // RSA PKCS#1 Signature Decoding using OpenSSL RSA Decryption with Public Key - // - Length = RSA_public_decrypt ( - (UINT32) SigSize, - Signature, - Signature, - RsaContext, - RSA_PKCS1_PADDING - ); - - // - // Invalid RSA Key or PKCS#1 Padding Checking Failed (if Length < 0) - // NOTE: Length should be the addition of HashSize and some DER value. - // Ignore more strict length checking here. - // - if (Length < (INTN) HashSize) { - return FALSE; - } - - // - // Validate the MessageHash and Decoded Signature - // NOTE: The decoded Signature should be the DER encoding of the DigestInfo value - // DigestInfo ::= SEQUENCE { - // digestAlgorithm AlgorithmIdentifier - // digest OCTET STRING - // } - // Then Memory Comparing should skip the DER value of the underlying SEQUENCE - // type and AlgorithmIdentifier. - // - if (CompareMem (MessageHash, Signature + Length - HashSize, HashSize) == 0) { - // - // Valid RSA PKCS#1 Signature - // - return TRUE; - } else { - // - // Failed to verification - // - return FALSE; - } -} diff --git a/Cryptlib/Pk/CryptRsaBasic.c b/Cryptlib/Pk/CryptRsaBasic.c new file mode 100644 index 0000000..3e43098 --- /dev/null +++ b/Cryptlib/Pk/CryptRsaBasic.c @@ -0,0 +1,335 @@ +/** @file + RSA Asymmetric Cipher Wrapper Implementation over OpenSSL. + + This file implements following APIs which provide basic capabilities for RSA: + 1) RsaNew + 2) RsaFree + 3) RsaSetKey + 4) RsaPkcs1Verify + +Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalCryptLib.h" + +#include +#include + +/** + Allocates and initializes one RSA context for subsequent use. + + @return Pointer to the RSA context that has been initialized. + If the allocations fails, RsaNew() returns NULL. + +**/ +VOID * +EFIAPI +RsaNew ( + VOID + ) +{ + // + // Allocates & Initializes RSA Context by OpenSSL RSA_new() + // + return (VOID *) RSA_new (); +} + +/** + Release the specified RSA context. + + @param[in] RsaContext Pointer to the RSA context to be released. + +**/ +VOID +EFIAPI +RsaFree ( + IN VOID *RsaContext + ) +{ + // + // Free OpenSSL RSA Context + // + RSA_free ((RSA *) RsaContext); +} + +/** + Sets the tag-designated key component into the established RSA context. + + This function sets the tag-designated RSA key component into the established + RSA context from the user-specified non-negative integer (octet string format + represented in RSA PKCS#1). + If BigNumber is NULL, then the specified key componenet in RSA context is cleared. + + If RsaContext is NULL, then return FALSE. + + @param[in, out] RsaContext Pointer to RSA context being set. + @param[in] KeyTag Tag of RSA key component being set. + @param[in] BigNumber Pointer to octet integer buffer. + If NULL, then the specified key componenet in RSA + context is cleared. + @param[in] BnSize Size of big number buffer in bytes. + If BigNumber is NULL, then it is ignored. + + @retval TRUE RSA key component was set successfully. + @retval FALSE Invalid RSA key component tag. + +**/ +BOOLEAN +EFIAPI +RsaSetKey ( + IN OUT VOID *RsaContext, + IN RSA_KEY_TAG KeyTag, + IN CONST UINT8 *BigNumber, + IN UINTN BnSize + ) +{ + RSA *RsaKey; + + // + // Check input parameters. + // + if (RsaContext == NULL || BnSize > INT_MAX) { + return FALSE; + } + + RsaKey = (RSA *) RsaContext; + // + // Set RSA Key Components by converting octet string to OpenSSL BN representation. + // NOTE: For RSA public key (used in signature verification), only public components + // (N, e) are needed. + // + switch (KeyTag) { + + // + // RSA Public Modulus (N) + // + case RsaKeyN: + if (RsaKey->n != NULL) { + BN_free (RsaKey->n); + } + RsaKey->n = NULL; + if (BigNumber == NULL) { + break; + } + RsaKey->n = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->n); + if (RsaKey->n == NULL) { + return FALSE; + } + + break; + + // + // RSA Public Exponent (e) + // + case RsaKeyE: + if (RsaKey->e != NULL) { + BN_free (RsaKey->e); + } + RsaKey->e = NULL; + if (BigNumber == NULL) { + break; + } + RsaKey->e = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->e); + if (RsaKey->e == NULL) { + return FALSE; + } + + break; + + // + // RSA Private Exponent (d) + // + case RsaKeyD: + if (RsaKey->d != NULL) { + BN_free (RsaKey->d); + } + RsaKey->d = NULL; + if (BigNumber == NULL) { + break; + } + RsaKey->d = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->d); + if (RsaKey->d == NULL) { + return FALSE; + } + + break; + + // + // RSA Secret Prime Factor of Modulus (p) + // + case RsaKeyP: + if (RsaKey->p != NULL) { + BN_free (RsaKey->p); + } + RsaKey->p = NULL; + if (BigNumber == NULL) { + break; + } + RsaKey->p = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->p); + if (RsaKey->p == NULL) { + return FALSE; + } + + break; + + // + // RSA Secret Prime Factor of Modules (q) + // + case RsaKeyQ: + if (RsaKey->q != NULL) { + BN_free (RsaKey->q); + } + RsaKey->q = NULL; + if (BigNumber == NULL) { + break; + } + RsaKey->q = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->q); + if (RsaKey->q == NULL) { + return FALSE; + } + + break; + + // + // p's CRT Exponent (== d mod (p - 1)) + // + case RsaKeyDp: + if (RsaKey->dmp1 != NULL) { + BN_free (RsaKey->dmp1); + } + RsaKey->dmp1 = NULL; + if (BigNumber == NULL) { + break; + } + RsaKey->dmp1 = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->dmp1); + if (RsaKey->dmp1 == NULL) { + return FALSE; + } + + break; + + // + // q's CRT Exponent (== d mod (q - 1)) + // + case RsaKeyDq: + if (RsaKey->dmq1 != NULL) { + BN_free (RsaKey->dmq1); + } + RsaKey->dmq1 = NULL; + if (BigNumber == NULL) { + break; + } + RsaKey->dmq1 = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->dmq1); + if (RsaKey->dmq1 == NULL) { + return FALSE; + } + + break; + + // + // The CRT Coefficient (== 1/q mod p) + // + case RsaKeyQInv: + if (RsaKey->iqmp != NULL) { + BN_free (RsaKey->iqmp); + } + RsaKey->iqmp = NULL; + if (BigNumber == NULL) { + break; + } + RsaKey->iqmp = BN_bin2bn (BigNumber, (UINT32) BnSize, RsaKey->iqmp); + if (RsaKey->iqmp == NULL) { + return FALSE; + } + + break; + + default: + return FALSE; + } + + return TRUE; +} + +/** + Verifies the RSA-SSA signature with EMSA-PKCS1-v1_5 encoding scheme defined in + RSA PKCS#1. + + If RsaContext is NULL, then return FALSE. + If MessageHash is NULL, then return FALSE. + If Signature is NULL, then return FALSE. + If HashSize is not equal to the size of MD5, SHA-1 or SHA-256 digest, then return FALSE. + + @param[in] RsaContext Pointer to RSA context for signature verification. + @param[in] MessageHash Pointer to octet message hash to be checked. + @param[in] HashSize Size of the message hash in bytes. + @param[in] Signature Pointer to RSA PKCS1-v1_5 signature to be verified. + @param[in] SigSize Size of signature in bytes. + + @retval TRUE Valid signature encoded in PKCS1-v1_5. + @retval FALSE Invalid signature or invalid RSA context. + +**/ +BOOLEAN +EFIAPI +RsaPkcs1Verify ( + IN VOID *RsaContext, + IN CONST UINT8 *MessageHash, + IN UINTN HashSize, + IN CONST UINT8 *Signature, + IN UINTN SigSize + ) +{ + INT32 DigestType; + UINT8 *SigBuf; + + // + // Check input parameters. + // + if (RsaContext == NULL || MessageHash == NULL || Signature == NULL) { + return FALSE; + } + + if (SigSize > INT_MAX || SigSize == 0) { + return FALSE; + } + + // + // Determine the message digest algorithm according to digest size. + // Only MD5, SHA-1 or SHA-256 algorithm is supported. + // + switch (HashSize) { + case MD5_DIGEST_SIZE: + DigestType = NID_md5; + break; + + case SHA1_DIGEST_SIZE: + DigestType = NID_sha1; + break; + + case SHA256_DIGEST_SIZE: + DigestType = NID_sha256; + break; + + default: + return FALSE; + } + + SigBuf = (UINT8 *) Signature; + return (BOOLEAN) RSA_verify ( + DigestType, + MessageHash, + (UINT32) HashSize, + SigBuf, + (UINT32) SigSize, + (RSA *) RsaContext + ); +} diff --git a/Cryptlib/Pk/CryptRsaExt.c b/Cryptlib/Pk/CryptRsaExt.c new file mode 100644 index 0000000..5c21d12 --- /dev/null +++ b/Cryptlib/Pk/CryptRsaExt.c @@ -0,0 +1,377 @@ +/** @file + RSA Asymmetric Cipher Wrapper Implementation over OpenSSL. + + This file implements following APIs which provide more capabilities for RSA: + 1) RsaGetKey + 2) RsaGenerateKey + 3) RsaCheckKey + 4) RsaPkcs1Sign + +Copyright (c) 2009 - 2013, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "InternalCryptLib.h" + +#include +#include +#include + +/** + Gets the tag-designated RSA key component from the established RSA context. + + This function retrieves the tag-designated RSA key component from the + established RSA context as a non-negative integer (octet string format + represented in RSA PKCS#1). + If specified key component has not been set or has been cleared, then returned + BnSize is set to 0. + If the BigNumber buffer is too small to hold the contents of the key, FALSE + is returned and BnSize is set to the required buffer size to obtain the key. + + If RsaContext is NULL, then return FALSE. + If BnSize is NULL, then return FALSE. + If BnSize is large enough but BigNumber is NULL, then return FALSE. + + @param[in, out] RsaContext Pointer to RSA context being set. + @param[in] KeyTag Tag of RSA key component being set. + @param[out] BigNumber Pointer to octet integer buffer. + @param[in, out] BnSize On input, the size of big number buffer in bytes. + On output, the size of data returned in big number buffer in bytes. + + @retval TRUE RSA key component was retrieved successfully. + @retval FALSE Invalid RSA key component tag. + @retval FALSE BnSize is too small. + +**/ +BOOLEAN +EFIAPI +RsaGetKey ( + IN OUT VOID *RsaContext, + IN RSA_KEY_TAG KeyTag, + OUT UINT8 *BigNumber, + IN OUT UINTN *BnSize + ) +{ + RSA *RsaKey; + BIGNUM *BnKey; + UINTN Size; + + // + // Check input parameters. + // + if (RsaContext == NULL || BnSize == NULL) { + return FALSE; + } + + RsaKey = (RSA *) RsaContext; + Size = *BnSize; + *BnSize = 0; + + switch (KeyTag) { + + // + // RSA Public Modulus (N) + // + case RsaKeyN: + if (RsaKey->n == NULL) { + return TRUE; + } + BnKey = RsaKey->n; + break; + + // + // RSA Public Exponent (e) + // + case RsaKeyE: + if (RsaKey->e == NULL) { + return TRUE; + } + BnKey = RsaKey->e; + break; + + // + // RSA Private Exponent (d) + // + case RsaKeyD: + if (RsaKey->d == NULL) { + return TRUE; + } + BnKey = RsaKey->d; + break; + + // + // RSA Secret Prime Factor of Modulus (p) + // + case RsaKeyP: + if (RsaKey->p == NULL) { + return TRUE; + } + BnKey = RsaKey->p; + break; + + // + // RSA Secret Prime Factor of Modules (q) + // + case RsaKeyQ: + if (RsaKey->q == NULL) { + return TRUE; + } + BnKey = RsaKey->q; + break; + + // + // p's CRT Exponent (== d mod (p - 1)) + // + case RsaKeyDp: + if (RsaKey->dmp1 == NULL) { + return TRUE; + } + BnKey = RsaKey->dmp1; + break; + + // + // q's CRT Exponent (== d mod (q - 1)) + // + case RsaKeyDq: + if (RsaKey->dmq1 == NULL) { + return TRUE; + } + BnKey = RsaKey->dmq1; + break; + + // + // The CRT Coefficient (== 1/q mod p) + // + case RsaKeyQInv: + if (RsaKey->iqmp == NULL) { + return TRUE; + } + BnKey = RsaKey->iqmp; + break; + + default: + return FALSE; + } + + *BnSize = Size; + Size = BN_num_bytes (BnKey); + + if (*BnSize < Size) { + *BnSize = Size; + return FALSE; + } + + if (BigNumber == NULL) { + return FALSE; + } + *BnSize = BN_bn2bin (BnKey, BigNumber) ; + + return TRUE; +} + +/** + Generates RSA key components. + + This function generates RSA key components. It takes RSA public exponent E and + length in bits of RSA modulus N as input, and generates all key components. + If PublicExponent is NULL, the default RSA public exponent (0x10001) will be used. + + Before this function can be invoked, pseudorandom number generator must be correctly + initialized by RandomSeed(). + + If RsaContext is NULL, then return FALSE. + + @param[in, out] RsaContext Pointer to RSA context being set. + @param[in] ModulusLength Length of RSA modulus N in bits. + @param[in] PublicExponent Pointer to RSA public exponent. + @param[in] PublicExponentSize Size of RSA public exponent buffer in bytes. + + @retval TRUE RSA key component was generated successfully. + @retval FALSE Invalid RSA key component tag. + +**/ +BOOLEAN +EFIAPI +RsaGenerateKey ( + IN OUT VOID *RsaContext, + IN UINTN ModulusLength, + IN CONST UINT8 *PublicExponent, + IN UINTN PublicExponentSize + ) +{ + BIGNUM *KeyE; + BOOLEAN RetVal; + + // + // Check input parameters. + // + if (RsaContext == NULL || ModulusLength > INT_MAX || PublicExponentSize > INT_MAX) { + return FALSE; + } + + KeyE = BN_new (); + if (KeyE == NULL) { + return FALSE; + } + + RetVal = FALSE; + + if (PublicExponent == NULL) { + if (BN_set_word (KeyE, 0x10001) == 0) { + goto _Exit; + } + } else { + if (BN_bin2bn (PublicExponent, (UINT32) PublicExponentSize, KeyE) == NULL) { + goto _Exit; + } + } + + if (RSA_generate_key_ex ((RSA *) RsaContext, (UINT32) ModulusLength, KeyE, NULL) == 1) { + RetVal = TRUE; + } + +_Exit: + BN_free (KeyE); + return RetVal; +} + +/** + Validates key components of RSA context. + + This function validates key compoents of RSA context in following aspects: + - Whether p is a prime + - Whether q is a prime + - Whether n = p * q + - Whether d*e = 1 mod lcm(p-1,q-1) + + If RsaContext is NULL, then return FALSE. + + @param[in] RsaContext Pointer to RSA context to check. + + @retval TRUE RSA key components are valid. + @retval FALSE RSA key components are not valid. + +**/ +BOOLEAN +EFIAPI +RsaCheckKey ( + IN VOID *RsaContext + ) +{ + UINTN Reason; + + // + // Check input parameters. + // + if (RsaContext == NULL) { + return FALSE; + } + + if (RSA_check_key ((RSA *) RsaContext) != 1) { + Reason = ERR_GET_REASON (ERR_peek_last_error ()); + if (Reason == RSA_R_P_NOT_PRIME || + Reason == RSA_R_Q_NOT_PRIME || + Reason == RSA_R_N_DOES_NOT_EQUAL_P_Q || + Reason == RSA_R_D_E_NOT_CONGRUENT_TO_1) { + return FALSE; + } + } + + return TRUE; +} + +/** + Carries out the RSA-SSA signature generation with EMSA-PKCS1-v1_5 encoding scheme. + + This function carries out the RSA-SSA signature generation with EMSA-PKCS1-v1_5 encoding scheme defined in + RSA PKCS#1. + If the Signature buffer is too small to hold the contents of signature, FALSE + is returned and SigSize is set to the required buffer size to obtain the signature. + + If RsaContext is NULL, then return FALSE. + If MessageHash is NULL, then return FALSE. + If HashSize is not equal to the size of MD5, SHA-1 or SHA-256 digest, then return FALSE. + If SigSize is large enough but Signature is NULL, then return FALSE. + + @param[in] RsaContext Pointer to RSA context for signature generation. + @param[in] MessageHash Pointer to octet message hash to be signed. + @param[in] HashSize Size of the message hash in bytes. + @param[out] Signature Pointer to buffer to receive RSA PKCS1-v1_5 signature. + @param[in, out] SigSize On input, the size of Signature buffer in bytes. + On output, the size of data returned in Signature buffer in bytes. + + @retval TRUE Signature successfully generated in PKCS1-v1_5. + @retval FALSE Signature generation failed. + @retval FALSE SigSize is too small. + +**/ +BOOLEAN +EFIAPI +RsaPkcs1Sign ( + IN VOID *RsaContext, + IN CONST UINT8 *MessageHash, + IN UINTN HashSize, + OUT UINT8 *Signature, + IN OUT UINTN *SigSize + ) +{ + RSA *Rsa; + UINTN Size; + INT32 DigestType; + + // + // Check input parameters. + // + if (RsaContext == NULL || MessageHash == NULL) { + return FALSE; + } + + Rsa = (RSA *) RsaContext; + Size = BN_num_bytes (Rsa->n); + + if (*SigSize < Size) { + *SigSize = Size; + return FALSE; + } + + if (Signature == NULL) { + return FALSE; + } + + // + // Determine the message digest algorithm according to digest size. + // Only MD5, SHA-1 or SHA-256 algorithm is supported. + // + switch (HashSize) { + case MD5_DIGEST_SIZE: + DigestType = NID_md5; + break; + + case SHA1_DIGEST_SIZE: + DigestType = NID_sha1; + break; + + case SHA256_DIGEST_SIZE: + DigestType = NID_sha256; + break; + + default: + return FALSE; + } + + return (BOOLEAN) RSA_sign ( + DigestType, + MessageHash, + (UINT32) HashSize, + Signature, + (UINT32 *) SigSize, + (RSA *) RsaContext + ); +} diff --git a/Cryptlib/Pk/CryptX509.c b/Cryptlib/Pk/CryptX509.c index a0c5a2a..5abe970 100644 --- a/Cryptlib/Pk/CryptX509.c +++ b/Cryptlib/Pk/CryptX509.c @@ -38,9 +38,7 @@ X509ConstructCertificate ( OUT UINT8 **SingleX509Cert ) { - BIO *CertBio; X509 *X509Cert; - BOOLEAN Status; // // Check input parameters. @@ -49,31 +47,17 @@ X509ConstructCertificate ( return FALSE; } - Status = FALSE; - // // Read DER-encoded X509 Certificate and Construct X509 object. // - CertBio = BIO_new (BIO_s_mem ()); - BIO_write (CertBio, Cert, (int) CertSize); - if (CertBio == NULL) { - goto _Exit; - } - X509Cert = d2i_X509_bio (CertBio, NULL); + X509Cert = d2i_X509 (NULL, &Cert, (long) CertSize); if (X509Cert == NULL) { - goto _Exit; + return FALSE; } *SingleX509Cert = (UINT8 *) X509Cert; - Status = TRUE; -_Exit: - // - // Release Resources. - // - BIO_free (CertBio); - - return Status; + return TRUE; } /** @@ -224,91 +208,6 @@ X509StackFree ( sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free); } -/** - Pop single certificate from STACK_OF(X509). - - If X509Stack, Cert, or CertSize is NULL, then return FALSE. - - @param[in] X509Stack Pointer to a X509 stack object. - @param[out] Cert Pointer to a X509 certificate. - @param[out] CertSize Length of output X509 certificate in bytes. - - @retval TRUE The X509 stack pop succeeded. - @retval FALSE The pop operation failed. - -**/ -BOOLEAN -X509PopCertificate ( - IN VOID *X509Stack, - OUT UINT8 **Cert, - OUT UINTN *CertSize - ) -{ - BIO *CertBio; - X509 *X509Cert; - STACK_OF(X509) *CertStack; - BOOLEAN Status; - int Result; - int Length; - VOID *Buffer; - - Status = FALSE; - - if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) { - return Status; - } - - CertStack = (STACK_OF(X509) *) X509Stack; - - X509Cert = sk_X509_pop (CertStack); - - if (X509Cert == NULL) { - return Status; - } - - Buffer = NULL; - - CertBio = BIO_new (BIO_s_mem ()); - if (CertBio == NULL) { - return Status; - } - - Result = i2d_X509_bio (CertBio, X509Cert); - if (Result == 0) { - goto _Exit; - } - - Length = ((BUF_MEM *) CertBio->ptr)->length; - if (Length <= 0) { - goto _Exit; - } - - Buffer = malloc (Length); - if (Buffer == NULL) { - goto _Exit; - } - - Result = BIO_read (CertBio, Buffer, Length); - if (Result != Length) { - goto _Exit; - } - - *Cert = Buffer; - *CertSize = Length; - - Status = TRUE; - -_Exit: - - BIO_free (CertBio); - - if (!Status && (Buffer != NULL)) { - free (Buffer); - } - - return Status; -} - /** Retrieve the subject bytes from one X.509 certificate. @@ -346,7 +245,6 @@ X509GetSubjectName ( return FALSE; } - Status = FALSE; X509Cert = NULL; // @@ -354,20 +252,27 @@ X509GetSubjectName ( // Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); if ((X509Cert == NULL) || (!Status)) { + Status = FALSE; goto _Exit; } + Status = FALSE; + // // Retrieve subject name from certificate object. // X509Name = X509_get_subject_name (X509Cert); + if (X509Name == NULL) { + goto _Exit; + } + if (*SubjectSize < (UINTN) X509Name->bytes->length) { *SubjectSize = (UINTN) X509Name->bytes->length; goto _Exit; } *SubjectSize = (UINTN) X509Name->bytes->length; if (CertSubject != NULL) { - CopyMem (CertSubject, (UINT8 *)X509Name->bytes->data, *SubjectSize); + CopyMem (CertSubject, (UINT8 *) X509Name->bytes->data, *SubjectSize); Status = TRUE; } @@ -375,7 +280,9 @@ _Exit: // // Release Resources. // - X509_free (X509Cert); + if (X509Cert != NULL) { + X509_free (X509Cert); + } return Status; } @@ -415,7 +322,6 @@ RsaGetPublicKeyFromX509 ( return FALSE; } - Status = FALSE; Pkey = NULL; X509Cert = NULL; @@ -424,9 +330,12 @@ RsaGetPublicKeyFromX509 ( // Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); if ((X509Cert == NULL) || (!Status)) { + Status = FALSE; goto _Exit; } + Status = FALSE; + // // Retrieve and check EVP_PKEY data from X509 Certificate. // @@ -446,8 +355,13 @@ _Exit: // // Release Resources. // - X509_free (X509Cert); - EVP_PKEY_free (Pkey); + if (X509Cert != NULL) { + X509_free (X509Cert); + } + + if (Pkey != NULL) { + EVP_PKEY_free (Pkey); + } return Status; } @@ -498,15 +412,22 @@ X509VerifyCert ( // // Register & Initialize necessary digest algorithms for certificate verification. // - EVP_add_digest (EVP_md5()); - EVP_add_digest (EVP_sha1()); - EVP_add_digest (EVP_sha256()); + if (EVP_add_digest (EVP_md5 ()) == 0) { + goto _Exit; + } + if (EVP_add_digest (EVP_sha1 ()) == 0) { + goto _Exit; + } + if (EVP_add_digest (EVP_sha256 ()) == 0) { + goto _Exit; + } // // Read DER-encoded certificate to be verified and Construct X509 object. // Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); if ((X509Cert == NULL) || (!Status)) { + Status = FALSE; goto _Exit; } @@ -515,9 +436,12 @@ X509VerifyCert ( // Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert); if ((X509CACert == NULL) || (!Status)) { + Status = FALSE; goto _Exit; } + Status = FALSE; + // // Set up X509 Store for trusted certificate. // @@ -546,9 +470,17 @@ _Exit: // // Release Resources. // - X509_free (X509Cert); - X509_free (X509CACert); - X509_STORE_free (CertStore); + if (X509Cert != NULL) { + X509_free (X509Cert); + } + if (X509CACert != NULL) { + X509_free (X509CACert); + } + + if (CertStore != NULL) { + X509_STORE_free (CertStore); + } + return Status; } diff --git a/Cryptlib/Rand/CryptRand.c b/Cryptlib/Rand/CryptRand.c index 4b27595..dc3ab99 100644 --- a/Cryptlib/Rand/CryptRand.c +++ b/Cryptlib/Rand/CryptRand.c @@ -43,6 +43,10 @@ RandomSeed ( IN UINTN SeedSize ) { + if (SeedSize > INT_MAX) { + return FALSE; + } + // // Seed the pseudorandom number generator with user-supplied value. // NOTE: A cryptographic PRNG must be seeded with unpredictable data. @@ -53,7 +57,11 @@ RandomSeed ( RAND_seed (DefaultSeed, sizeof (DefaultSeed)); } - return TRUE; + if (RAND_status () == 1) { + return TRUE; + } + + return FALSE; } /** @@ -78,7 +86,7 @@ RandomBytes ( // // Check input parameters. // - if (Output == NULL) { + if (Output == NULL || Size > INT_MAX) { return FALSE; } diff --git a/Cryptlib/SysCall/BaseMemAllocation.c b/Cryptlib/SysCall/BaseMemAllocation.c index 1abe78e..75da1dd 100644 --- a/Cryptlib/SysCall/BaseMemAllocation.c +++ b/Cryptlib/SysCall/BaseMemAllocation.c @@ -2,7 +2,7 @@ Base Memory Allocation Routines Wrapper for Crypto library over OpenSSL during PEI & DXE phases. -Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.
+Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at @@ -22,7 +22,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. /* Allocates memory blocks */ void *malloc (size_t size) { - return AllocatePool ((UINTN)size); + return AllocatePool ((UINTN) size); } /* Reallocate memory blocks */ @@ -32,7 +32,7 @@ void *realloc (void *ptr, size_t size) // BUG: hardcode OldSize == size! We have no any knowledge about // memory size of original pointer ptr. // - return ReallocatePool (ptr, (UINTN)size, (UINTN)size); + return ReallocatePool (ptr, (UINTN) size, (UINTN) size); } /* De-allocates or frees a memory block */ diff --git a/Cryptlib/SysCall/CrtWrapper.c b/Cryptlib/SysCall/CrtWrapper.c index fb446b6..5a8322d 100644 --- a/Cryptlib/SysCall/CrtWrapper.c +++ b/Cryptlib/SysCall/CrtWrapper.c @@ -293,16 +293,6 @@ size_t fwrite (const void *buffer, size_t size, size_t count, FILE *stream) // -- Dummy OpenSSL Support Routines -- // -int BIO_printf (void *bio, const char *format, ...) -{ - return 0; -} - -int BIO_snprintf(char *buf, size_t n, const char *format, ...) -{ - return 0; -} - void *UI_OpenSSL(void) { return NULL; diff --git a/Cryptlib/SysCall/TimerWrapper.c b/Cryptlib/SysCall/TimerWrapper.c index bb7bcba..cee72ba 100644 --- a/Cryptlib/SysCall/TimerWrapper.c +++ b/Cryptlib/SysCall/TimerWrapper.c @@ -146,14 +146,14 @@ struct tm * gmtime (const time_t *timer) GmTime->tm_yday = (int) DayNo; for (MonthNo = 12; MonthNo > 1; MonthNo--) { - if (DayNo > CumulativeDays[IsLeap(Year)][MonthNo]) { + if (DayNo >= CumulativeDays[IsLeap(Year)][MonthNo]) { DayNo = (UINT16) (DayNo - (UINT16) (CumulativeDays[IsLeap(Year)][MonthNo])); break; } } - GmTime->tm_mon = (int) MonthNo; - GmTime->tm_mday = (int) DayNo; + GmTime->tm_mon = (int) MonthNo - 1; + GmTime->tm_mday = (int) DayNo + 1; GmTime->tm_isdst = 0; GmTime->tm_gmtoff = 0; diff --git a/Makefile b/Makefile index 4bdd603..a22c6b3 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ ARCH = $(shell uname -m | sed s,i[3456789]86,ia32,) -SUBDIRS = Cryptlib +SUBDIRS = Cryptlib lib LIB_PATH = /usr/lib64 EFI_INCLUDE = /usr/include/efi -EFI_INCLUDES = -nostdinc -ICryptlib -ICryptlib/Include -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol -EFI_PATH = /usr/lib64/gnuefi +EFI_INCLUDES = -nostdinc -ICryptlib -ICryptlib/Include -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol -Iinclude +EFI_PATH := /usr/lib64/gnuefi LIB_GCC = $(shell $(CC) -print-libgcc-file-name) EFI_LIBS = -lefi -lgnuefi --start-group Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a --end-group $(LIB_GCC) @@ -14,10 +14,17 @@ EFI_LIBS = -lefi -lgnuefi --start-group Cryptlib/libcryptlib.a Cryptlib/OpenSSL/ EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o EFI_LDS = elf_$(ARCH)_efi.lds +DEFAULT_LOADER := \\\\grub.efi CFLAGS = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic \ - -fshort-wchar -Wall -mno-red-zone -maccumulate-outgoing-args \ - -mno-mmx -mno-sse \ + -fshort-wchar -Wall -Werror -mno-red-zone -maccumulate-outgoing-args \ + -mno-mmx -mno-sse -fno-builtin \ + "-DDEFAULT_LOADER=L\"$(DEFAULT_LOADER)\"" \ + "-DDEFAULT_LOADER_CHAR=\"$(DEFAULT_LOADER)\"" \ $(EFI_INCLUDES) + +ifneq ($(origin OVERRIDE_SECURITY_POLICY), undefined) + CFLAGS += -DOVERRIDE_SECURITY_POLICY +endif ifeq ($(ARCH),x86_64) CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI endif @@ -30,14 +37,14 @@ endif LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS) -VERSION = 0.4 +VERSION = 0.7 TARGET = shim.efi MokManager.efi.signed fallback.efi.signed -OBJS = shim.o netboot.o cert.o dbx.o -KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key -SOURCES = shim.c shim.h netboot.c signature.h PeImage.h -MOK_OBJS = MokManager.o -MOK_SOURCES = MokManager.c shim.h +OBJS = shim.o netboot.o cert.o replacements.o version.o +KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer +SOURCES = shim.c shim.h netboot.c include/PeImage.h include/wincert.h include/console.h replacements.c replacements.h version.c version.h +MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o +MOK_SOURCES = MokManager.c shim.h include/console.h PasswordCrypt.c PasswordCrypt.h crypt_blowfish.c crypt_blowfish.h FALLBACK_OBJS = fallback.o FALLBACK_SRCS = fallback.c @@ -54,6 +61,12 @@ shim_cert.h: shim.cer hexdump -v -e '1/1 "0x%02x, "' $< >> $@ echo "};" >> $@ +version.c : version.c.in + sed -e "s,@@VERSION@@,$(VERSION)," \ + -e "s,@@UNAME@@,$(shell uname -a)," \ + -e "s,@@COMMIT@@,$(shell if [ -d .git ] ; then git log -1 --pretty=format:%H ; elif [ -f commit ]; then cat commit ; else echo commit id not available; fi)," \ + < version.c.in > version.c + certdb/secmod.db: shim.crt -mkdir certdb certutil -A -n 'my CA' -d certdb/ -t CT,CT,CT -i ca.crt @@ -65,10 +78,7 @@ shim.o: $(SOURCES) shim_cert.h cert.o : cert.S $(CC) $(CFLAGS) -c -o $@ $< -dbx.o : dbx.S - $(CC) $(CFLAGS) -c -o $@ $< - -shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a +shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) fallback.o: $(FALLBACK_SRCS) @@ -76,10 +86,10 @@ fallback.o: $(FALLBACK_SRCS) fallback.so: $(FALLBACK_OBJS) $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) -MokManager.o: $(SOURCES) +MokManager.o: $(MOK_SOURCES) -MokManager.so: $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a - $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) +MokManager.so: $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a + $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) lib/lib.a Cryptlib/libcryptlib.a: $(MAKE) -C Cryptlib @@ -87,6 +97,9 @@ Cryptlib/libcryptlib.a: Cryptlib/OpenSSL/libopenssl.a: $(MAKE) -C Cryptlib/OpenSSL +lib/lib.a: + $(MAKE) -C lib EFI_PATH=$(EFI_PATH) + %.efi: %.so objcopy -j .text -j .sdata -j .data \ -j .dynamic -j .dynsym -j .rel \ @@ -106,8 +119,9 @@ Cryptlib/OpenSSL/libopenssl.a: clean: $(MAKE) -C Cryptlib clean $(MAKE) -C Cryptlib/OpenSSL clean + $(MAKE) -C lib clean rm -rf $(TARGET) $(OBJS) $(MOK_OBJS) $(FALLBACK_OBJS) $(KEYS) certdb - rm -f *.debug *.so *.efi + rm -f *.debug *.so *.efi *.tar.* version.c GITTAG = $(VERSION) @@ -117,16 +131,20 @@ test-archive: @git archive --format=tar $(shell git branch | awk '/^*/ { print $$2 }') | ( cd /tmp/shim-$(VERSION)-tmp/ ; tar x ) @git diff | ( cd /tmp/shim-$(VERSION)-tmp/ ; patch -s -p1 -b -z .gitdiff ) @mv /tmp/shim-$(VERSION)-tmp/ /tmp/shim-$(VERSION)/ + @git log -1 --pretty=format:%H > /tmp/shim-$(VERSION)/commit @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/shim-$(VERSION).tar.bz2 shim-$(VERSION) @rm -rf /tmp/shim-$(VERSION) @echo "The archive is in shim-$(VERSION).tar.bz2" -archive: - git tag $(GITTAG) refs/heads/master +tag: + git tag --sign $(GITTAG) refs/heads/master + +archive: tag @rm -rf /tmp/shim-$(VERSION) /tmp/shim-$(VERSION)-tmp @mkdir -p /tmp/shim-$(VERSION)-tmp @git archive --format=tar $(GITTAG) | ( cd /tmp/shim-$(VERSION)-tmp/ ; tar x ) @mv /tmp/shim-$(VERSION)-tmp/ /tmp/shim-$(VERSION)/ + @git log -1 --pretty=format:%H > /tmp/shim-$(VERSION)/commit @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/shim-$(VERSION).tar.bz2 shim-$(VERSION) @rm -rf /tmp/shim-$(VERSION) @echo "The archive is in shim-$(VERSION).tar.bz2" diff --git a/MokManager.c b/MokManager.c index 97588cb..f5ed379 100644 --- a/MokManager.c +++ b/MokManager.c @@ -3,12 +3,20 @@ #include #include #include "shim.h" -#include "signature.h" #include "PeImage.h" +#include "PasswordCrypt.h" -#define PASSWORD_MAX 16 -#define PASSWORD_MIN 8 -#define SB_PASSWORD_LEN 8 +#include "guid.h" +#include "console.h" +#include "variables.h" +#include "simple_file.h" +#include "efiauthenticated.h" + +#define PASSWORD_MAX 256 +#define PASSWORD_MIN 1 +#define SB_PASSWORD_LEN 16 + +#define NAME_LINE_MAX 70 #ifndef SHIM_VENDOR #define SHIM_VENDOR L"Shim" @@ -16,6 +24,8 @@ #define EFI_VARIABLE_APPEND_WRITE 0x00000040 +EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; + #define CERT_STRING L"Select an X509 certificate to enroll:\n\n" #define HASH_STRING L"Select a file to trust:\n\n" @@ -37,46 +47,14 @@ typedef struct { typedef struct { UINT32 MokSBState; UINT32 PWLen; - CHAR16 Password[PASSWORD_MAX]; + CHAR16 Password[SB_PASSWORD_LEN]; } __attribute__ ((packed)) MokSBvar; -static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes, - UINTN *size, void **buffer) -{ - EFI_STATUS efi_status; - char allocate = !(*size); - - efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, - attributes, size, buffer); - - if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) { - return efi_status; - } - - *buffer = AllocatePool(*size); - - if (!*buffer) { - Print(L"Unable to allocate variable buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - - efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, - attributes, size, *buffer); - - return efi_status; -} - -static EFI_INPUT_KEY get_keystroke (void) -{ - EFI_INPUT_KEY key; - UINTN EventIndex; - - uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, - &EventIndex); - uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key); - - return key; -} +typedef struct { + UINT32 MokDBState; + UINT32 PWLen; + CHAR16 Password[SB_PASSWORD_LEN]; +} __attribute__ ((packed)) MokDBvar; static EFI_STATUS get_sha1sum (void *Data, int DataSize, UINT8 *hash) { @@ -88,24 +66,24 @@ static EFI_STATUS get_sha1sum (void *Data, int DataSize, UINT8 *hash) ctx = AllocatePool(ctxsize); if (!ctx) { - Print(L"Unable to allocate memory for hash context\n"); + console_notify(L"Unable to allocate memory for hash context"); return EFI_OUT_OF_RESOURCES; } if (!Sha1Init(ctx)) { - Print(L"Unable to initialise hash\n"); + console_notify(L"Unable to initialise hash"); status = EFI_OUT_OF_RESOURCES; goto done; } if (!(Sha1Update(ctx, Data, DataSize))) { - Print(L"Unable to generate hash\n"); + console_notify(L"Unable to generate hash"); status = EFI_OUT_OF_RESOURCES; goto done; } if (!(Sha1Final(ctx, hash))) { - Print(L"Unable to finalise hash\n"); + console_notify(L"Unable to finalise hash"); status = EFI_OUT_OF_RESOURCES; goto done; } @@ -118,15 +96,15 @@ done: static UINT32 count_keys(void *Data, UINTN DataSize) { EFI_SIGNATURE_LIST *CertList = Data; - EFI_GUID CertType = EfiCertX509Guid; - EFI_GUID HashType = EfiHashSha256Guid; + EFI_GUID CertType = X509_GUID; + EFI_GUID HashType = EFI_CERT_SHA256_GUID; UINTN dbsize = DataSize; UINT32 MokNum = 0; while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) && (CompareGuid (&CertList->SignatureType, &HashType) != 0)) { - Print(L"Doesn't look like a key or hash\n"); + console_notify(L"Doesn't look like a key or hash"); dbsize -= CertList->SignatureListSize; CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); @@ -135,7 +113,7 @@ static UINT32 count_keys(void *Data, UINTN DataSize) if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) && (CertList->SignatureSize != 48)) { - Print(L"Doesn't look like a valid hash\n"); + console_notify(L"Doesn't look like a valid hash"); dbsize -= CertList->SignatureListSize; CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize); @@ -155,15 +133,15 @@ static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) { MokListNode *list; EFI_SIGNATURE_LIST *CertList = Data; EFI_SIGNATURE_DATA *Cert; - EFI_GUID CertType = EfiCertX509Guid; - EFI_GUID HashType = EfiHashSha256Guid; + EFI_GUID CertType = X509_GUID; + EFI_GUID HashType = EFI_CERT_SHA256_GUID; UINTN dbsize = DataSize; UINTN count = 0; list = AllocatePool(sizeof(MokListNode) * num); if (!list) { - Print(L"Unable to allocate MOK list\n"); + console_notify(L"Unable to allocate MOK list"); return NULL; } @@ -200,272 +178,273 @@ static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) { return list; } -static void print_x509_name (X509_NAME *X509Name, CHAR16 *name) -{ - char *str; +typedef struct { + int nid; + CHAR16 *name; +} NidName; - str = X509_NAME_oneline(X509Name, NULL, 0); - if (str) { - Print(L" %s:\n %a\n", name, str); - OPENSSL_free(str); - } -} - -static const char *mon[12]= { -"Jan","Feb","Mar","Apr","May","Jun", -"Jul","Aug","Sep","Oct","Nov","Dec" +static NidName nidname[] = { + {NID_commonName, L"CN"}, + {NID_organizationName, L"O"}, + {NID_countryName, L"C"}, + {NID_stateOrProvinceName, L"ST"}, + {NID_localityName, L"L"}, + {-1, NULL} }; -static void print_x509_GENERALIZEDTIME_time (ASN1_TIME *time, CHAR16 *time_string) +static CHAR16* get_x509_name (X509_NAME *X509Name) { - char *v; - int gmt = 0; - int i; - int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0; - char *f = NULL; - int f_len = 0; + CHAR16 name[NAME_LINE_MAX+1]; + CHAR16 part[NAME_LINE_MAX+1]; + char str[NAME_LINE_MAX]; + int i, len, rest, first; - i=time->length; - v=(char *)time->data; + name[0] = '\0'; + rest = NAME_LINE_MAX; + first = 1; + for (i = 0; nidname[i].name != NULL; i++) { + int add; + len = X509_NAME_get_text_by_NID (X509Name, nidname[i].nid, + str, NAME_LINE_MAX); + if (len <= 0) + continue; - if (i < 12) - goto error; + if (first) + add = len + (int)StrLen(nidname[i].name) + 1; + else + add = len + (int)StrLen(nidname[i].name) + 3; - if (v[i-1] == 'Z') - gmt=1; + if (add > rest) + continue; - for (i=0; i<12; i++) { - if ((v[i] > '9') || (v[i] < '0')) - goto error; - } - - y = (v[0]-'0')*1000+(v[1]-'0')*100 + (v[2]-'0')*10+(v[3]-'0'); - M = (v[4]-'0')*10+(v[5]-'0'); - - if ((M > 12) || (M < 1)) - goto error; - - d = (v[6]-'0')*10+(v[7]-'0'); - h = (v[8]-'0')*10+(v[9]-'0'); - m = (v[10]-'0')*10+(v[11]-'0'); - - if (time->length >= 14 && - (v[12] >= '0') && (v[12] <= '9') && - (v[13] >= '0') && (v[13] <= '9')) { - s = (v[12]-'0')*10+(v[13]-'0'); - /* Check for fractions of seconds. */ - if (time->length >= 15 && v[14] == '.') { - int l = time->length; - f = &v[14]; /* The decimal point. */ - f_len = 1; - while (14 + f_len < l && f[f_len] >= '0' && - f[f_len] <= '9') - ++f_len; + if (first) { + SPrint(part, NAME_LINE_MAX * sizeof(CHAR16), L"%s=%a", + nidname[i].name, str); + } else { + SPrint(part, NAME_LINE_MAX * sizeof(CHAR16), L", %s=%a", + nidname[i].name, str); } + StrCat(name, part); + rest -= add; + first = 0; } - SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d%.*a %d%a", - mon[M-1], d, h, m, s, f_len, f, y, (gmt)?" GMT":""); -error: - return; + if (rest >= 0 && rest < NAME_LINE_MAX) + return PoolPrint(L"%s", name); + + return NULL; } -static void print_x509_UTCTIME_time (ASN1_TIME *time, CHAR16 *time_string) +static CHAR16* get_x509_time (ASN1_TIME *time) { - char *v; - int gmt=0; - int i; - int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0; + BIO *bio = BIO_new (BIO_s_mem()); + char str[30]; + int len; - i=time->length; - v=(char *)time->data; + ASN1_TIME_print (bio, time); + len = BIO_read(bio, str, 29); + if (len < 0) + len = 0; + str[len] = '\0'; + BIO_free (bio); - if (i < 10) - goto error; - - if (v[i-1] == 'Z') - gmt=1; - - for (i=0; i<10; i++) - if ((v[i] > '9') || (v[i] < '0')) - goto error; - - y = (v[0]-'0')*10+(v[1]-'0'); - - if (y < 50) - y+=100; - - M = (v[2]-'0')*10+(v[3]-'0'); - - if ((M > 12) || (M < 1)) - goto error; - - d = (v[4]-'0')*10+(v[5]-'0'); - h = (v[6]-'0')*10+(v[7]-'0'); - m = (v[8]-'0')*10+(v[9]-'0'); - - if (time->length >=12 && - (v[10] >= '0') && (v[10] <= '9') && - (v[11] >= '0') && (v[11] <= '9')) - s = (v[10]-'0')*10+(v[11]-'0'); - - SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d %d%a", - mon[M-1], d, h, m, s, y+1900, (gmt)?" GMT":""); -error: - return; + return PoolPrint(L"%a", str); } -static void print_x509_time (ASN1_TIME *time, CHAR16 *name) -{ - CHAR16 time_string[30]; - - if (time->type == V_ASN1_UTCTIME) { - print_x509_UTCTIME_time(time, time_string); - } else if (time->type == V_ASN1_GENERALIZEDTIME) { - print_x509_GENERALIZEDTIME_time(time, time_string); - } else { - time_string[0] = '\0'; - } - - Print(L" %s:\n %s\n", name, time_string); -} - -static void show_x509_info (X509 *X509Cert) +static void show_x509_info (X509 *X509Cert, UINT8 *hash) { ASN1_INTEGER *serial; BIGNUM *bnser; unsigned char hexbuf[30]; X509_NAME *X509Name; ASN1_TIME *time; + CHAR16 *issuer = NULL; + CHAR16 *subject = NULL; + CHAR16 *from = NULL; + CHAR16 *until = NULL; + POOL_PRINT hash_string1; + POOL_PRINT hash_string2; + POOL_PRINT serial_string; + int fields = 0; + CHAR16 **text; + int i = 0; + + ZeroMem(&hash_string1, sizeof(hash_string1)); + ZeroMem(&hash_string2, sizeof(hash_string2)); + ZeroMem(&serial_string, sizeof(serial_string)); serial = X509_get_serialNumber(X509Cert); if (serial) { int i, n; bnser = ASN1_INTEGER_to_BN(serial, NULL); n = BN_bn2bin(bnser, hexbuf); - Print(L" Serial Number:\n "); - for (i = 0; i < n-1; i++) { - Print(L"%02x:", hexbuf[i]); + for (i = 0; i < n; i++) { + CatPrint(&serial_string, L"%02x:", hexbuf[i]); } - Print(L"%02x\n", hexbuf[n-1]); } + if (serial_string.str) + fields++; + X509Name = X509_get_issuer_name(X509Cert); if (X509Name) { - print_x509_name(X509Name, L"Issuer"); + issuer = get_x509_name(X509Name); + if (issuer) + fields++; } X509Name = X509_get_subject_name(X509Cert); if (X509Name) { - print_x509_name(X509Name, L"Subject"); + subject = get_x509_name(X509Name); + if (subject) + fields++; } time = X509_get_notBefore(X509Cert); if (time) { - print_x509_time(time, L"Validity from"); + from = get_x509_time(time); + if (from) + fields++; } time = X509_get_notAfter(X509Cert); if (time) { - print_x509_time(time, L"Validity till"); + until = get_x509_time(time); + if (until) + fields++; } + + for (i=0; i<10; i++) + CatPrint(&hash_string1, L"%02x ", hash[i]); + for (i=10; i<20; i++) + CatPrint(&hash_string2, L"%02x ", hash[i]); + + if (hash_string1.str) + fields++; + + if (hash_string2.str) + fields++; + + if (!fields) + return; + + i = 0; + text = AllocateZeroPool(sizeof(CHAR16 *) * (fields*3 + 1)); + if (serial_string.str) { + text[i++] = StrDuplicate(L"[Serial Number]"); + text[i++] = serial_string.str; + text[i++] = StrDuplicate(L""); + } + if (issuer) { + text[i++] = StrDuplicate(L"[Issuer]"); + text[i++] = issuer; + text[i++] = StrDuplicate(L""); + } + if (subject) { + text[i++] = StrDuplicate(L"[Subject]"); + text[i++] = subject; + text[i++] = StrDuplicate(L""); + } + if (from) { + text[i++] = StrDuplicate(L"[Valid Not Before]"); + text[i++] = from; + text[i++] = StrDuplicate(L""); + } + if (until) { + text[i++] = StrDuplicate(L"[Valid Not After]"); + text[i++] = until; + text[i++] = StrDuplicate(L""); + } + if (hash_string1.str) { + text[i++] = StrDuplicate(L"[Fingerprint]"); + text[i++] = hash_string1.str; + } + if (hash_string2.str) { + text[i++] = hash_string2.str; + text[i++] = StrDuplicate(L""); + } + text[i] = NULL; + + console_print_box(text, -1); + + for (i=0; text[i] != NULL; i++) + FreePool(text[i]); + + FreePool(text); +} + +static void show_efi_hash (UINT8 *hash) +{ + CHAR16 *text[5]; + POOL_PRINT hash_string1; + POOL_PRINT hash_string2; + int i; + + ZeroMem(&hash_string1, sizeof(hash_string1)); + ZeroMem(&hash_string2, sizeof(hash_string2)); + + text[0] = L"SHA256 hash"; + text[1] = L""; + + for (i=0; i<16; i++) + CatPrint(&hash_string1, L"%02x ", hash[i]); + for (i=16; i<32; i++) + CatPrint(&hash_string2, L"%02x ", hash[i]); + + text[2] = hash_string1.str; + text[3] = hash_string2.str; + text[4] = NULL; + + console_print_box(text, -1); + + if (hash_string1.str) + FreePool(hash_string1.str); + + if (hash_string2.str) + FreePool(hash_string2.str); } static void show_mok_info (void *Mok, UINTN MokSize) { EFI_STATUS efi_status; UINT8 hash[SHA1_DIGEST_SIZE]; - unsigned int i; X509 *X509Cert; if (!Mok || MokSize == 0) return; if (MokSize != SHA256_DIGEST_SIZE) { - if (X509ConstructCertificate(Mok, MokSize, - (UINT8 **) &X509Cert) && X509Cert != NULL) { - show_x509_info(X509Cert); - X509_free(X509Cert); - } else { - Print(L" Not a valid X509 certificate: %x\n\n", - ((UINT32 *)Mok)[0]); - return; - } - efi_status = get_sha1sum(Mok, MokSize, hash); if (efi_status != EFI_SUCCESS) { - Print(L"Failed to compute MOK fingerprint\n"); + console_notify(L"Failed to compute MOK fingerprint"); return; } - Print(L" Fingerprint (SHA1):\n "); - for (i = 0; i < SHA1_DIGEST_SIZE; i++) { - Print(L" %02x", hash[i]); - if (i % 10 == 9) - Print(L"\n "); + if (X509ConstructCertificate(Mok, MokSize, + (UINT8 **) &X509Cert) && X509Cert != NULL) { + show_x509_info(X509Cert, hash); + X509_free(X509Cert); + } else { + console_notify(L"Not a valid X509 certificate"); + return; } } else { - Print(L"SHA256 hash:\n "); - for (i = 0; i < SHA256_DIGEST_SIZE; i++) { - Print(L" %02x", ((UINT8 *)Mok)[i]); - if (i % 10 == 9) - Print(L"\n "); - } - Print(L"\n"); + show_efi_hash(Mok); } - - Print(L"\n"); } -static INTN get_number () -{ - EFI_INPUT_KEY input_key; - CHAR16 input[10]; - int count = 0; - - do { - input_key = get_keystroke(); - - if ((input_key.UnicodeChar < '0' || - input_key.UnicodeChar > '9' || - count >= 10) && - input_key.UnicodeChar != CHAR_BACKSPACE) { - continue; - } - - if (count == 0 && input_key.UnicodeChar == CHAR_BACKSPACE) - continue; - - Print(L"%c", input_key.UnicodeChar); - - if (input_key.UnicodeChar == CHAR_BACKSPACE) { - input[--count] = '\0'; - continue; - } - - input[count++] = input_key.UnicodeChar; - } while (input_key.UnicodeChar != CHAR_CARRIAGE_RETURN); - - if (count == 0) - return -1; - - input[count] = '\0'; - - return (INTN)Atoi(input); -} - -static UINT8 list_keys (void *KeyList, UINTN KeyListSize, CHAR16 *title) +static EFI_STATUS list_keys (void *KeyList, UINTN KeyListSize, CHAR16 *title) { UINT32 MokNum = 0; MokListNode *keys = NULL; INTN key_num = 0; - UINT8 initial = 1; + CHAR16 **menu_strings; + int i; if (KeyListSize < (sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA))) { - Print(L"No keys\n"); - Pause(); + console_notify(L"No MOK keys found"); return 0; } @@ -473,41 +452,40 @@ static UINT8 list_keys (void *KeyList, UINTN KeyListSize, CHAR16 *title) keys = build_mok_list(MokNum, KeyList, KeyListSize); if (!keys) { - Print(L"Failed to construct key list\n"); + console_notify(L"Failed to construct key list"); return 0; } - do { - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - if (title) - Print(L"%s\n", title); - Print(L"Input the key number to show the details of the key or\n" - L"type \'0\' to continue\n\n"); - Print(L"%d key(s) in the key list\n\n", MokNum); + menu_strings = AllocateZeroPool(sizeof(CHAR16 *) * (MokNum + 2)); - if (key_num > MokNum) { - Print(L"[Key %d]\n", key_num); - Print(L"No such key\n\n"); - } else if (initial != 1 && key_num > 0){ - Print(L"[Key %d]\n", key_num); - show_mok_info(keys[key_num-1].Mok, keys[key_num-1].MokSize); - } + if (!menu_strings) + return EFI_OUT_OF_RESOURCES; - Print(L"Key Number: "); + for (i=0; i= line_max && key.UnicodeChar != CHAR_BACKSPACE) || @@ -550,8 +528,8 @@ static UINT8 get_line (UINT32 *length, CHAR16 *line, UINT32 line_max, UINT8 show return 1; } -static EFI_STATUS compute_pw_hash (void *MokNew, UINTN MokNewSize, CHAR16 *password, - UINT32 pw_length, UINT8 *hash) +static EFI_STATUS compute_pw_hash (void *Data, UINTN DataSize, UINT8 *password, + UINT32 pw_length, UINT8 *hash) { EFI_STATUS status; unsigned int ctxsize; @@ -561,32 +539,32 @@ static EFI_STATUS compute_pw_hash (void *MokNew, UINTN MokNewSize, CHAR16 *passw ctx = AllocatePool(ctxsize); if (!ctx) { - Print(L"Unable to allocate memory for hash context\n"); + console_notify(L"Unable to allocate memory for hash context"); return EFI_OUT_OF_RESOURCES; } if (!Sha256Init(ctx)) { - Print(L"Unable to initialise hash\n"); + console_notify(L"Unable to initialise hash"); status = EFI_OUT_OF_RESOURCES; goto done; } - if (MokNew && MokNewSize) { - if (!(Sha256Update(ctx, MokNew, MokNewSize))) { - Print(L"Unable to generate hash\n"); + if (Data && DataSize) { + if (!(Sha256Update(ctx, Data, DataSize))) { + console_notify(L"Unable to generate hash"); status = EFI_OUT_OF_RESOURCES; goto done; } } - if (!(Sha256Update(ctx, password, pw_length * sizeof(CHAR16)))) { - Print(L"Unable to generate hash\n"); + if (!(Sha256Update(ctx, password, pw_length))) { + console_notify(L"Unable to generate hash"); status = EFI_OUT_OF_RESOURCES; goto done; } if (!(Sha256Final(ctx, hash))) { - Print(L"Unable to finalise hash\n"); + console_notify(L"Unable to finalise hash"); status = EFI_OUT_OF_RESOURCES; goto done; } @@ -596,41 +574,120 @@ done: return status; } -static EFI_STATUS match_password (void *Data, UINTN DataSize, - UINT8 auth[SHA256_DIGEST_SIZE], - CHAR16 *prompt) +static void console_save_and_set_mode (SIMPLE_TEXT_OUTPUT_MODE *SavedMode) { - EFI_STATUS efi_status; - UINT8 hash[SHA256_DIGEST_SIZE]; + if (!SavedMode) { + Print(L"Invalid parameter: SavedMode\n"); + return; + } + + CopyMem(SavedMode, ST->ConOut->Mode, sizeof(SIMPLE_TEXT_OUTPUT_MODE)); + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, FALSE); + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, + EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE); +} + +static void console_restore_mode (SIMPLE_TEXT_OUTPUT_MODE *SavedMode) +{ + uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, + SavedMode->CursorVisible); + uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, + SavedMode->CursorColumn, SavedMode->CursorRow); + uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, + SavedMode->Attribute); +} + +static UINT32 get_password (CHAR16 *prompt, CHAR16 *password, UINT32 max) +{ + SIMPLE_TEXT_OUTPUT_MODE SavedMode; + CHAR16 *str; + CHAR16 *message[2]; + UINTN length; + UINT32 pw_length; + + if (!prompt) + prompt = L"Password:"; + + console_save_and_set_mode(&SavedMode); + + str = PoolPrint(L"%s ", prompt); + if (!str) { + console_errorbox(L"Failed to allocate prompt"); + return 0; + } + + message[0] = str; + message[1] = NULL; + length = StrLen(message[0]); + console_print_box_at(message, -1, -length-4, -5, length+4, 3, 0, 1); + get_line(&pw_length, password, max, 0); + + console_restore_mode(&SavedMode); + + FreePool(str); + + return pw_length; +} + +static EFI_STATUS match_password (PASSWORD_CRYPT *pw_crypt, + void *Data, UINTN DataSize, + UINT8 *auth, CHAR16 *prompt) +{ + EFI_STATUS status; + UINT8 hash[128]; + UINT8 *auth_hash; + UINT32 auth_size; CHAR16 password[PASSWORD_MAX]; UINT32 pw_length; UINT8 fail_count = 0; + int i; + + if (pw_crypt) { + auth_hash = pw_crypt->hash; + auth_size = get_hash_size (pw_crypt->method); + if (auth_size == 0) + return EFI_INVALID_PARAMETER; + } else if (auth) { + auth_hash = auth; + auth_size = SHA256_DIGEST_SIZE; + } else { + return EFI_INVALID_PARAMETER; + } while (fail_count < 3) { - if (prompt) { - Print(L"%s", prompt); - } else { - Print(L"Password: "); - } - get_line(&pw_length, password, PASSWORD_MAX, 0); + pw_length = get_password(prompt, password, PASSWORD_MAX); if (pw_length < PASSWORD_MIN || pw_length > PASSWORD_MAX) { - Print(L"Invalid password length\n"); + console_errorbox(L"Invalid password length"); fail_count++; continue; } - efi_status = compute_pw_hash(Data, DataSize, password, - pw_length, hash); + /* + * Compute password hash + */ + if (pw_crypt) { + char pw_ascii[PASSWORD_MAX + 1]; + for (i = 0; i < pw_length; i++) + pw_ascii[i] = (char)password[i]; + pw_ascii[pw_length] = '\0'; - if (efi_status != EFI_SUCCESS) { - Print(L"Unable to generate password hash\n"); + status = password_crypt(pw_ascii, pw_length, pw_crypt, hash); + } else { + /* + * For backward compatibility + */ + status = compute_pw_hash(Data, DataSize, (UINT8 *)password, + pw_length * sizeof(CHAR16), hash); + } + if (status != EFI_SUCCESS) { + console_errorbox(L"Unable to generate password hash"); fail_count++; continue; } - if (CompareMem(auth, hash, SHA256_DIGEST_SIZE) != 0) { - Print(L"Password doesn't match\n"); + if (CompareMem(auth_hash, hash, auth_size) != 0) { + console_errorbox(L"Password doesn't match"); fail_count++; continue; } @@ -648,23 +705,29 @@ static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; - UINT8 auth[SHA256_DIGEST_SIZE]; - UINTN auth_size; + UINT8 auth[PASSWORD_CRYPT_SIZE]; + UINTN auth_size = PASSWORD_CRYPT_SIZE; UINT32 attributes; if (authenticate) { - auth_size = SHA256_DIGEST_SIZE; efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth", &shim_lock_guid, &attributes, &auth_size, auth); - - if (efi_status != EFI_SUCCESS || auth_size != SHA256_DIGEST_SIZE) { - Print(L"Failed to get MokAuth %d\n", efi_status); + if (efi_status != EFI_SUCCESS || + (auth_size != SHA256_DIGEST_SIZE && + auth_size != PASSWORD_CRYPT_SIZE)) { + console_error(L"Failed to get MokAuth", efi_status); return efi_status; } - efi_status = match_password(MokNew, MokNewSize, auth, NULL); + if (auth_size == PASSWORD_CRYPT_SIZE) { + efi_status = match_password((PASSWORD_CRYPT *)auth, + NULL, 0, NULL, NULL); + } else { + efi_status = match_password(NULL, MokNew, MokNewSize, + auth, NULL); + } if (efi_status != EFI_SUCCESS) return EFI_ACCESS_DENIED; } @@ -687,7 +750,7 @@ static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate) } if (efi_status != EFI_SUCCESS) { - Print(L"Failed to set variable %d\n", efi_status); + console_error(L"Failed to set variable", efi_status); return efi_status; } @@ -696,86 +759,62 @@ static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate) static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - CHAR16 line[1]; - UINT32 length; EFI_STATUS efi_status; - do { - if (!list_keys(MokNew, MokNewSize, L"[Enroll MOK]")) { - return 0; - } + if (list_keys(MokNew, MokNewSize, L"[Enroll MOK]") != EFI_SUCCESS) + return 0; - Print(L"Enroll the key(s)? (y/n): "); + if (console_yes_no((CHAR16 *[]){L"Enroll the key(s)?", NULL}) == 0) + return 0; - get_line (&length, line, 1, 1); + efi_status = store_keys(MokNew, MokNewSize, auth); - if (line[0] == 'Y' || line[0] == 'y') { - efi_status = store_keys(MokNew, MokNewSize, auth); - - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to enroll keys\n"); - return -1; - } - - if (auth) { - LibDeleteVariable(L"MokNew", &shim_lock_guid); - LibDeleteVariable(L"MokAuth", &shim_lock_guid); - - Print(L"\nPress a key to reboot system\n"); - Pause(); - uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, - EFI_SUCCESS, 0, NULL); - Print(L"Failed to reboot\n"); - return -1; - } - - return 0; - } - } while (line[0] != 'N' && line[0] != 'n'); - return -1; -} - -static INTN mok_enrollment_prompt_callback (void *MokNew, void *data2, - void *data3) -{ - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - return mok_enrollment_prompt(MokNew, (UINTN)data2, TRUE); -} - -static INTN mok_reset_prompt (void *MokNew, void *data2, void *data3) -{ - EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - CHAR16 line[1]; - UINT32 length; - EFI_STATUS efi_status; - - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - Print(L"Erase all stored keys? (y/N): "); - - get_line (&length, line, 1, 1); - - if (line[0] == 'Y' || line[0] == 'y') { - efi_status = store_keys(NULL, 0, TRUE); - - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to erase keys\n"); - return -1; - } + if (efi_status != EFI_SUCCESS) { + console_notify(L"Failed to enroll keys\n"); + return -1; + } + if (auth) { LibDeleteVariable(L"MokNew", &shim_lock_guid); LibDeleteVariable(L"MokAuth", &shim_lock_guid); - Print(L"\nPress a key to reboot system\n"); - Pause(); + console_notify(L"The system must now be rebooted"); uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, EFI_SUCCESS, 0, NULL); - Print(L"Failed to reboot\n"); + console_notify(L"Failed to reboot"); return -1; } return 0; } +static INTN mok_reset_prompt () +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + + if (console_yes_no((CHAR16 *[]){L"Erase all stored keys?", NULL }) == 0) + return 0; + + efi_status = store_keys(NULL, 0, TRUE); + + if (efi_status != EFI_SUCCESS) { + console_notify(L"Failed to erase keys\n"); + return -1; + } + + LibDeleteVariable(L"MokNew", &shim_lock_guid); + LibDeleteVariable(L"MokAuth", &shim_lock_guid); + + console_notify(L"The system must now be rebooted"); + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, + EFI_SUCCESS, 0, NULL); + console_notify(L"Failed to reboot\n"); + return -1; +} + static EFI_STATUS write_back_mok_list (MokListNode *list, INTN key_num) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; @@ -831,7 +870,7 @@ static EFI_STATUS write_back_mok_list (MokListNode *list, INTN key_num) FreePool(Data); if (efi_status != EFI_SUCCESS) { - Print(L"Failed to set variable %d\n", efi_status); + console_error(L"Failed to set variable", efi_status); return efi_status; } @@ -842,10 +881,10 @@ static EFI_STATUS delete_keys (void *MokDel, UINTN MokDelSize) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; - UINT8 auth[SHA256_DIGEST_SIZE]; - UINTN auth_size = SHA256_DIGEST_SIZE; + UINT8 auth[PASSWORD_CRYPT_SIZE]; + UINTN auth_size = PASSWORD_CRYPT_SIZE; UINT32 attributes; - void *MokListData = NULL; + UINT8 *MokListData = NULL; UINTN MokListDataSize = 0; MokListNode *mok, *del_key; INTN mok_num, del_num; @@ -855,22 +894,29 @@ static EFI_STATUS delete_keys (void *MokDel, UINTN MokDelSize) &shim_lock_guid, &attributes, &auth_size, auth); - if (efi_status != EFI_SUCCESS || auth_size != SHA256_DIGEST_SIZE) { - Print(L"Failed to get MokDelAuth %d\n", efi_status); + if (efi_status != EFI_SUCCESS || + (auth_size != SHA256_DIGEST_SIZE && auth_size != PASSWORD_CRYPT_SIZE)) { + console_error(L"Failed to get MokDelAuth", efi_status); return efi_status; } - efi_status = match_password(MokDel, MokDelSize, auth, NULL); + if (auth_size == PASSWORD_CRYPT_SIZE) { + efi_status = match_password((PASSWORD_CRYPT *)auth, NULL, 0, + NULL, NULL); + } else { + efi_status = match_password(NULL, MokDel, MokDelSize, auth, NULL); + } if (efi_status != EFI_SUCCESS) return EFI_ACCESS_DENIED; - efi_status = get_variable(L"MokList", shim_lock_guid, &attributes, - &MokListDataSize, &MokListData); - + efi_status = get_variable_attr (L"MokList", &MokListData, &MokListDataSize, + shim_lock_guid, &attributes); if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { - Print(L"MokList is compromised!\nErase all keys in MokList!\n"); + console_alertbox((CHAR16 *[]){L"MokList is compromised!", + L"Erase all keys in MokList!", + NULL}); if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) { - Print(L"Failed to erase MokList\n"); + console_notify(L"Failed to erase MokList"); } return EFI_ACCESS_DENIED; } @@ -911,64 +957,86 @@ static EFI_STATUS delete_keys (void *MokDel, UINTN MokDelSize) return efi_status; } -static INTN mok_deletion_prompt (void *MokDel, void *data2, void *data3) +static INTN mok_deletion_prompt (void *MokDel, UINTN MokDelSize) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - UINTN MokDelSize = (UINTN)data2; - CHAR16 line[1]; - UINT32 length; EFI_STATUS efi_status; - do { - if (!list_keys(MokDel, MokDelSize, L"[Delete MOK]")) { - return 0; - } + if (list_keys(MokDel, MokDelSize, L"[Delete MOK]") != EFI_SUCCESS) { + return 0; + } - Print(L"Delete the key(s)? (y/n): "); + if (console_yes_no((CHAR16 *[]){L"Delete the key(s)?", NULL}) == 0) + return 0; - get_line (&length, line, 1, 1); + efi_status = delete_keys(MokDel, MokDelSize); - if (line[0] == 'Y' || line[0] == 'y') { - efi_status = delete_keys(MokDel, MokDelSize); + if (efi_status != EFI_SUCCESS) { + console_notify(L"Failed to delete keys"); + return -1; + } - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to delete keys\n"); - return -1; - } + LibDeleteVariable(L"MokDel", &shim_lock_guid); + LibDeleteVariable(L"MokDelAuth", &shim_lock_guid); - LibDeleteVariable(L"MokDel", &shim_lock_guid); - LibDeleteVariable(L"MokDelAuth", &shim_lock_guid); - - Print(L"\nPress a key to reboot system\n"); - Pause(); - uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, - EFI_SUCCESS, 0, NULL); - Print(L"Failed to reboot\n"); - return -1; - } - } while (line[0] != 'N' && line[0] != 'n'); + console_notify(L"The system must now be rebooted"); + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, + EFI_SUCCESS, 0, NULL); + console_notify(L"Failed to reboot"); return -1; } -static INTN mok_sb_prompt (void *MokSB, void *data2, void *data3) { +static CHAR16 get_password_charater (CHAR16 *prompt) +{ + SIMPLE_TEXT_OUTPUT_MODE SavedMode; + CHAR16 *message[2]; + CHAR16 character; + UINTN length; + UINT32 pw_length; + + if (!prompt) + prompt = L"Password charater: "; + + console_save_and_set_mode(&SavedMode); + + message[0] = prompt; + message[1] = NULL; + length = StrLen(message[0]); + console_print_box_at(message, -1, -length-4, -5, length+4, 3, 0, 1); + get_line(&pw_length, &character, 1, 0); + + console_restore_mode(&SavedMode); + + return character; +} + +static INTN mok_sb_prompt (void *MokSB, UINTN MokSBSize) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; - UINTN MokSBSize = (UINTN)data2; + SIMPLE_TEXT_OUTPUT_MODE SavedMode; MokSBvar *var = MokSB; + CHAR16 *message[4]; CHAR16 pass1, pass2, pass3; + CHAR16 *str; UINT8 fail_count = 0; - UINT32 length; - CHAR16 line[1]; UINT8 sbval = 1; UINT8 pos1, pos2, pos3; + int ret; if (MokSBSize != sizeof(MokSBvar)) { - Print(L"Invalid MokSB variable contents\n"); + console_notify(L"Invalid MokSB variable contents"); return -1; } uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + message[0] = L"Change Secure Boot state"; + message[1] = NULL; + + console_save_and_set_mode(&SavedMode); + console_print_box_at(message, -1, 0, 0, -1, -1, 1, 1); + console_restore_mode(&SavedMode); + while (fail_count < 3) { RandomBytes (&pos1, sizeof(pos1)); pos1 = (pos1 % var->PWLen); @@ -983,14 +1051,29 @@ static INTN mok_sb_prompt (void *MokSB, void *data2, void *data3) { pos3 = (pos3 % var->PWLen) ; } while (pos3 == pos2 || pos3 == pos1); - Print(L"Enter password character %d: ", pos1 + 1); - get_line(&length, &pass1, 1, 0); + str = PoolPrint(L"Enter password character %d: ", pos1 + 1); + if (!str) { + console_errorbox(L"Failed to allocate buffer"); + return -1; + } + pass1 = get_password_charater(str); + FreePool(str); - Print(L"Enter password character %d: ", pos2 + 1); - get_line(&length, &pass2, 1, 0); + str = PoolPrint(L"Enter password character %d: ", pos2 + 1); + if (!str) { + console_errorbox(L"Failed to allocate buffer"); + return -1; + } + pass2 = get_password_charater(str); + FreePool(str); - Print(L"Enter password character %d: ", pos3 + 1); - get_line(&length, &pass3, 1, 0); + str = PoolPrint(L"Enter password character %d: ", pos3 + 1); + if (!str) { + console_errorbox(L"Failed to allocate buffer"); + return -1; + } + pass3 = get_password_charater(str); + FreePool(str); if (pass1 != var->Password[pos1] || pass2 != var->Password[pos2] || @@ -1003,256 +1086,227 @@ static INTN mok_sb_prompt (void *MokSB, void *data2, void *data3) { } if (fail_count >= 3) { - Print(L"Password limit reached\n"); + console_notify(L"Password limit reached"); + return -1; + } + + if (var->MokSBState == 0) + ret = console_yes_no((CHAR16 *[]){L"Disable Secure Boot", NULL}); + else + ret = console_yes_no((CHAR16 *[]){L"Enable Secure Boot", NULL}); + + if (ret == 0) { + LibDeleteVariable(L"MokSB", &shim_lock_guid); return -1; } if (var->MokSBState == 0) { - Print(L"Disable Secure Boot? (y/n): "); - } else { - Print(L"Enable Secure Boot? (y/n): "); - } - - do { - get_line (&length, line, 1, 1); - - if (line[0] == 'Y' || line[0] == 'y') { - if (var->MokSBState == 0) { - efi_status = uefi_call_wrapper(RT->SetVariable, - 5, L"MokSBState", - &shim_lock_guid, + efi_status = uefi_call_wrapper(RT->SetVariable, + 5, L"MokSBState", + &shim_lock_guid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - 1, &sbval); - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to set Secure Boot state\n"); - return -1; - } - } else { - LibDeleteVariable(L"MokSBState", - &shim_lock_guid); - } - - LibDeleteVariable(L"MokSB", &shim_lock_guid); - - Print(L"Press a key to reboot system\n"); - Pause(); - uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, - EFI_SUCCESS, 0, NULL); - Print(L"Failed to reboot\n"); + 1, &sbval); + if (efi_status != EFI_SUCCESS) { + console_notify(L"Failed to set Secure Boot state"); return -1; } - } while (line[0] != 'N' && line[0] != 'n'); + } else { + LibDeleteVariable(L"MokSBState", &shim_lock_guid); + } + console_notify(L"The system must now be rebooted"); + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, + EFI_SUCCESS, 0, NULL); + console_notify(L"Failed to reboot"); return -1; } - -static INTN mok_pw_prompt (void *MokPW, void *data2, void *data3) { +static INTN mok_db_prompt (void *MokDB, UINTN MokDBSize) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; - UINTN MokPWSize = (UINTN)data2; - UINT8 hash[SHA256_DIGEST_SIZE]; - UINT32 length; - CHAR16 line[1]; + SIMPLE_TEXT_OUTPUT_MODE SavedMode; + MokDBvar *var = MokDB; + CHAR16 *message[4]; + CHAR16 pass1, pass2, pass3; + CHAR16 *str; + UINT8 fail_count = 0; + UINT8 dbval = 1; + UINT8 pos1, pos2, pos3; + int ret; - if (MokPWSize != SHA256_DIGEST_SIZE) { - Print(L"Invalid MokPW variable contents\n"); + if (MokDBSize != sizeof(MokDBvar)) { + console_notify(L"Invalid MokDB variable contents"); return -1; } uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - SetMem(hash, SHA256_DIGEST_SIZE, 0); + message[0] = L"Change DB state"; + message[1] = NULL; - if (CompareMem(MokPW, hash, SHA256_DIGEST_SIZE) == 0) { - Print(L"Clear MOK password? (y/n): "); + console_save_and_set_mode(&SavedMode); + console_print_box_at(message, -1, 0, 0, -1, -1, 1, 1); + console_restore_mode(&SavedMode); + + while (fail_count < 3) { + RandomBytes (&pos1, sizeof(pos1)); + pos1 = (pos1 % var->PWLen); do { - get_line (&length, line, 1, 1); + RandomBytes (&pos2, sizeof(pos2)); + pos2 = (pos2 % var->PWLen); + } while (pos2 == pos1); - if (line[0] == 'Y' || line[0] == 'y') { - LibDeleteVariable(L"MokPWStore", &shim_lock_guid); - LibDeleteVariable(L"MokPW", &shim_lock_guid); - } - } while (line[0] != 'N' && line[0] != 'n'); + do { + RandomBytes (&pos3, sizeof(pos3)); + pos3 = (pos3 % var->PWLen) ; + } while (pos3 == pos2 || pos3 == pos1); - return 0; + str = PoolPrint(L"Enter password character %d: ", pos1 + 1); + if (!str) { + console_errorbox(L"Failed to allocate buffer"); + return -1; + } + pass1 = get_password_charater(str); + FreePool(str); + + str = PoolPrint(L"Enter password character %d: ", pos2 + 1); + if (!str) { + console_errorbox(L"Failed to allocate buffer"); + return -1; + } + pass2 = get_password_charater(str); + FreePool(str); + + str = PoolPrint(L"Enter password character %d: ", pos3 + 1); + if (!str) { + console_errorbox(L"Failed to allocate buffer"); + return -1; + } + pass3 = get_password_charater(str); + FreePool(str); + + if (pass1 != var->Password[pos1] || + pass2 != var->Password[pos2] || + pass3 != var->Password[pos3]) { + Print(L"Invalid character\n"); + fail_count++; + } else { + break; + } } - efi_status = match_password(NULL, 0, MokPW, L"Confirm MOK passphrase: "); - if (efi_status != EFI_SUCCESS) { - Print(L"Password limit reached\n"); + if (fail_count >= 3) { + console_notify(L"Password limit reached"); return -1; } - Print(L"Set MOK password? (y/n): "); + if (var->MokDBState == 0) + ret = console_yes_no((CHAR16 *[]){L"Ignore DB certs/hashes", NULL}); + else + ret = console_yes_no((CHAR16 *[]){L"Use DB certs/hashes", NULL}); - do { - get_line (&length, line, 1, 1); + if (ret == 0) { + LibDeleteVariable(L"MokDB", &shim_lock_guid); + return -1; + } - if (line[0] == 'Y' || line[0] == 'y') { - efi_status = uefi_call_wrapper(RT->SetVariable, 5, - L"MokPWStore", - &shim_lock_guid, + if (var->MokDBState == 0) { + efi_status = uefi_call_wrapper(RT->SetVariable, + 5, L"MokDBState", + &shim_lock_guid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS, - MokPWSize, MokPW); - if (efi_status != EFI_SUCCESS) { - Print(L"Failed to set MOK password\n"); - return -1; - } - - LibDeleteVariable(L"MokPW", &shim_lock_guid); - - Print(L"Press a key to reboot system\n"); - Pause(); - uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, - EFI_SUCCESS, 0, NULL); - Print(L"Failed to reboot\n"); + 1, &dbval); + if (efi_status != EFI_SUCCESS) { + console_notify(L"Failed to set DB state"); return -1; } - } while (line[0] != 'N' && line[0] != 'n'); + } else { + LibDeleteVariable(L"MokDBState", &shim_lock_guid); + } - return 0; + console_notify(L"The system must now be rebooted"); + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, + EFI_SUCCESS, 0, NULL); + console_notify(L"Failed to reboot"); + return -1; } -static UINTN draw_menu (CHAR16 *header, UINTN lines, struct menu_item *items, - UINTN count) { - UINTN i; +static INTN mok_pw_prompt (void *MokPW, UINTN MokPWSize) { + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + UINT8 hash[PASSWORD_CRYPT_SIZE]; + UINT8 clear = 0; + + if (MokPWSize != SHA256_DIGEST_SIZE && MokPWSize != PASSWORD_CRYPT_SIZE) { + console_notify(L"Invalid MokPW variable contents"); + return -1; + } uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, - EFI_WHITE | EFI_BACKGROUND_BLACK); + SetMem(hash, PASSWORD_CRYPT_SIZE, 0); - Print(L"%s UEFI key management\n\n", SHIM_VENDOR); - - if (header) - Print(L"%s", header); - - for (i = 0; i < count; i++) { - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, - items[i].colour | EFI_BACKGROUND_BLACK); - Print(L" %s\n", items[i].text); + if (MokPWSize == PASSWORD_CRYPT_SIZE) { + if (CompareMem(MokPW, hash, PASSWORD_CRYPT_SIZE) == 0) + clear = 1; + } else { + if (CompareMem(MokPW, hash, SHA256_DIGEST_SIZE) == 0) + clear = 1; } - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 0); - uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE); + if (clear) { + if (console_yes_no((CHAR16 *[]){L"Clear MOK password?", NULL}) == 0) + return 0; - return 2 + lines; -} - -static void free_menu (struct menu_item *items, UINTN count) { - UINTN i; - - for (i=0; iResetSystem, 4, EfiResetWarm, EFI_SUCCESS, 0, + NULL); + console_notify(L"Failed to reboot"); + return -1; } - FreePool(items); -} - -static void update_time (UINTN position, UINTN timeout) -{ - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, - position); - - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, - EFI_BLACK | EFI_BACKGROUND_BLACK); - - Print(L" ", timeout); - - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, - position); - - uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut, - EFI_WHITE | EFI_BACKGROUND_BLACK); - - if (timeout > 1) - Print(L"Booting in %d seconds\n", timeout); - else if (timeout) - Print(L"Booting in %d second\n", timeout); -} - -static void run_menu (CHAR16 *header, UINTN lines, struct menu_item *items, - UINTN count, UINTN timeout) { - UINTN index, pos = 0, wait = 0, offset; - EFI_INPUT_KEY key; - EFI_STATUS status; - INTN ret; - - if (timeout) - wait = 10000000; - - offset = draw_menu (header, lines, items, count); - - while (1) { - update_time(count + offset + 1, timeout); - - uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, - 0, pos + offset); - status = WaitForSingleEvent(ST->ConIn->WaitForKey, wait); - - if (status == EFI_TIMEOUT) { - timeout--; - if (!timeout) { - free_menu(items, count); - return; - } - continue; - } - - wait = 0; - timeout = 0; - - uefi_call_wrapper(BS->WaitForEvent, 3, 1, - &ST->ConIn->WaitForKey, &index); - uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, - &key); - - switch(key.ScanCode) { - case SCAN_UP: - if (pos == 0) - continue; - pos--; - continue; - break; - case SCAN_DOWN: - if (pos == (count - 1)) - continue; - pos++; - continue; - break; - } - - switch(key.UnicodeChar) { - case CHAR_LINEFEED: - case CHAR_CARRIAGE_RETURN: - if (items[pos].callback == NULL) { - free_menu(items, count); - return; - } - - ret = items[pos].callback(items[pos].data, - items[pos].data2, - items[pos].data3); - if (ret < 0) { - Print(L"Press a key to continue\n"); - Pause(); - /* Clear the key in the queue */ - uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, - ST->ConIn, &key); - } - draw_menu (header, lines, items, count); - pos = 0; - break; - } + if (MokPWSize == PASSWORD_CRYPT_SIZE) { + efi_status = match_password((PASSWORD_CRYPT *)MokPW, NULL, 0, + NULL, L"Confirm MOK passphrase: "); + } else { + efi_status = match_password(NULL, NULL, 0, MokPW, + L"Confirm MOK passphrase: "); } + + if (efi_status != EFI_SUCCESS) { + console_notify(L"Password limit reached"); + return -1; + } + + if (console_yes_no((CHAR16 *[]){L"Set MOK password?", NULL}) == 0) + return 0; + + efi_status = uefi_call_wrapper(RT->SetVariable, 5, + L"MokPWStore", + &shim_lock_guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS, + MokPWSize, MokPW); + if (efi_status != EFI_SUCCESS) { + console_notify(L"Failed to set MOK password"); + return -1; + } + + LibDeleteVariable(L"MokPW", &shim_lock_guid); + + console_notify(L"The system must now be rebooted"); + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, EFI_SUCCESS, 0, + NULL); + console_notify(L"Failed to reboot"); + return -1; } -static UINTN verify_certificate(void *cert, UINTN size) +static BOOLEAN verify_certificate(void *cert, UINTN size) { X509 *X509Cert; if (!cert || size == 0) @@ -1260,8 +1314,7 @@ static UINTN verify_certificate(void *cert, UINTN size) if (!(X509ConstructCertificate(cert, size, (UINT8 **) &X509Cert)) || X509Cert == NULL) { - Print(L"Invalid X509 certificate\n"); - Pause(); + console_notify(L"Invalid X509 certificate"); return FALSE; } @@ -1269,49 +1322,22 @@ static UINTN verify_certificate(void *cert, UINTN size) return TRUE; } -static INTN file_callback (void *data, void *data2, void *data3) { - EFI_FILE_INFO *buffer = NULL; - UINTN buffersize = 0, mokbuffersize; - EFI_STATUS status; - EFI_FILE *file; - CHAR16 *filename = data; - EFI_FILE *parent = data2; - BOOLEAN hash = !!data3; - EFI_GUID file_info_guid = EFI_FILE_INFO_ID; +static EFI_STATUS enroll_file (void *data, UINTN datasize, BOOLEAN hash) +{ + EFI_STATUS status = EFI_SUCCESS; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_SIGNATURE_LIST *CertList; EFI_SIGNATURE_DATA *CertData; + UINTN mokbuffersize; void *mokbuffer = NULL; - status = uefi_call_wrapper(parent->Open, 5, parent, &file, filename, - EFI_FILE_MODE_READ, 0); - - if (status != EFI_SUCCESS) - return 1; - - status = uefi_call_wrapper(file->GetInfo, 4, file, &file_info_guid, - &buffersize, buffer); - - if (status == EFI_BUFFER_TOO_SMALL) { - buffer = AllocatePool(buffersize); - status = uefi_call_wrapper(file->GetInfo, 4, file, - &file_info_guid, &buffersize, - buffer); - } - - if (!buffer) - return 0; - - buffersize = buffer->FileSize; - if (hash) { - void *binary; UINT8 sha256[SHA256_DIGEST_SIZE]; UINT8 sha1[SHA1_DIGEST_SIZE]; SHIM_LOCK *shim_lock; EFI_GUID shim_guid = SHIM_LOCK_GUID; PE_COFF_LOADER_IMAGE_CONTEXT context; - + status = LibLocateProtocol(&shim_guid, (VOID **)&shim_lock); if (status != EFI_SUCCESS) @@ -1325,33 +1351,25 @@ static INTN file_callback (void *data, void *data2, void *data3) { if (!mokbuffer) goto out; - binary = AllocatePool(buffersize); - - status = uefi_call_wrapper(file->Read, 3, file, &buffersize, - binary); + status = shim_lock->Context(data, datasize, &context); if (status != EFI_SUCCESS) goto out; - - status = shim_lock->Context(binary, buffersize, &context); - - if (status != EFI_SUCCESS) - goto out; - - status = shim_lock->Hash(binary, buffersize, &context, sha256, + + status = shim_lock->Hash(data, datasize, &context, sha256, sha1); if (status != EFI_SUCCESS) goto out; CertList = mokbuffer; - CertList->SignatureType = EfiHashSha256Guid; + CertList->SignatureType = EFI_CERT_SHA256_GUID; CertList->SignatureSize = 16 + SHA256_DIGEST_SIZE; CertData = (EFI_SIGNATURE_DATA *)(((UINT8 *)mokbuffer) + sizeof(EFI_SIGNATURE_LIST)); CopyMem(CertData->SignatureData, sha256, SHA256_DIGEST_SIZE); } else { - mokbuffersize = buffersize + sizeof(EFI_SIGNATURE_LIST) + + mokbuffersize = datasize + sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID); mokbuffer = AllocatePool(mokbuffersize); @@ -1359,13 +1377,12 @@ static INTN file_callback (void *data, void *data2, void *data3) { goto out; CertList = mokbuffer; - CertList->SignatureType = EfiCertX509Guid; - CertList->SignatureSize = 16 + buffersize; - status = uefi_call_wrapper(file->Read, 3, file, &buffersize, - mokbuffer + sizeof(EFI_SIGNATURE_LIST) + 16); + CertList->SignatureType = X509_GUID; + CertList->SignatureSize = 16 + datasize; + + memcpy(mokbuffer + sizeof(EFI_SIGNATURE_LIST) + 16, data, + datasize); - if (status != EFI_SUCCESS) - goto out; CertData = (EFI_SIGNATURE_DATA *)(((UINT8 *)mokbuffer) + sizeof(EFI_SIGNATURE_LIST)); } @@ -1375,325 +1392,150 @@ static INTN file_callback (void *data, void *data2, void *data3) { CertData->SignatureOwner = shim_lock_guid; if (!hash) { - if (!verify_certificate(CertData->SignatureData, buffersize)) + if (!verify_certificate(CertData->SignatureData, datasize)) goto out; } mok_enrollment_prompt(mokbuffer, mokbuffersize, FALSE); out: - if (buffer) - FreePool(buffer); - if (mokbuffer) FreePool(mokbuffer); - return 0; + return status; } -static INTN directory_callback (void *data, void *data2, void *data3) { - EFI_FILE_INFO *buffer = NULL; - UINTN buffersize = 0; - EFI_STATUS status; - UINTN dircount = 0, i = 0; - struct menu_item *dircontent; - EFI_FILE *dir; - CHAR16 *filename = data; - EFI_FILE *root = data2; - BOOLEAN hash = !!data3; +static void mok_hash_enroll(void) +{ + EFI_STATUS efi_status; + CHAR16 *file_name = NULL; + EFI_HANDLE im = NULL; + EFI_FILE *file = NULL; + UINTN filesize; + void *data; - status = uefi_call_wrapper(root->Open, 5, root, &dir, filename, - EFI_FILE_MODE_READ, 0); + simple_file_selector(&im, (CHAR16 *[]){ + L"Select Binary", + L"", + L"The Selected Binary will have its hash Enrolled", + L"This means it will Subsequently Boot with no prompting", + L"Remember to make sure it is a genuine binary before Enroling its hash", + NULL + }, L"\\", L"", &file_name); - if (status != EFI_SUCCESS) - return 1; + if (!file_name) + return; - while (1) { - status = uefi_call_wrapper(dir->Read, 3, dir, &buffersize, - buffer); + efi_status = simple_file_open(im, file_name, &file, EFI_FILE_MODE_READ); - if (status == EFI_BUFFER_TOO_SMALL) { - buffer = AllocatePool(buffersize); - status = uefi_call_wrapper(dir->Read, 3, dir, - &buffersize, buffer); - } - - if (status != EFI_SUCCESS) - return 1; - - if (!buffersize) - break; - - if ((StrCmp(buffer->FileName, L".") == 0) || - (StrCmp(buffer->FileName, L"..") == 0)) - continue; - - dircount++; - - FreePool(buffer); - buffersize = 0; + if (efi_status != EFI_SUCCESS) { + console_error(L"Unable to open file", efi_status); + return; } - dircount++; + simple_file_read_all(file, &filesize, &data); + simple_file_close(file); - dircontent = AllocatePool(sizeof(struct menu_item) * dircount); - - dircontent[0].text = StrDuplicate(L".."); - dircontent[0].callback = NULL; - dircontent[0].colour = EFI_YELLOW; - i++; - - uefi_call_wrapper(dir->SetPosition, 2, dir, 0); - - while (1) { - status = uefi_call_wrapper(dir->Read, 3, dir, &buffersize, - buffer); - - if (status == EFI_BUFFER_TOO_SMALL) { - buffer = AllocatePool(buffersize); - status = uefi_call_wrapper(dir->Read, 3, dir, - &buffersize, buffer); - } - - if (status != EFI_SUCCESS) - return 1; - - if (!buffersize) - break; - - if ((StrCmp(buffer->FileName, L".") == 0) || - (StrCmp(buffer->FileName, L"..") == 0)) - continue; - - if (buffer->Attribute & EFI_FILE_DIRECTORY) { - dircontent[i].text = StrDuplicate(buffer->FileName); - dircontent[i].callback = directory_callback; - dircontent[i].data = dircontent[i].text; - dircontent[i].data2 = dir; - dircontent[i].data3 = data3; - dircontent[i].colour = EFI_YELLOW; - } else { - dircontent[i].text = StrDuplicate(buffer->FileName); - dircontent[i].callback = file_callback; - dircontent[i].data = dircontent[i].text; - dircontent[i].data2 = dir; - dircontent[i].data3 = data3; - dircontent[i].colour = EFI_WHITE; - } - - i++; - FreePool(buffer); - buffersize = 0; - buffer = NULL; + if (!filesize) { + console_error(L"Unable to read file", efi_status); + return; } - if (hash) - run_menu(HASH_STRING, 2, dircontent, dircount, 0); - else - run_menu(CERT_STRING, 2, dircontent, dircount, 0); + efi_status = enroll_file(data, filesize, TRUE); - return 0; + if (efi_status != EFI_SUCCESS) + console_error(L"Hash failed (did you select a valid EFI binary?)", efi_status); + + FreePool(data); } -static INTN filesystem_callback (void *data, void *data2, void *data3) { - EFI_FILE_INFO *buffer = NULL; - UINTN buffersize = 0; - EFI_STATUS status; - UINTN dircount = 0, i = 0; - struct menu_item *dircontent; - EFI_FILE *root = data; - BOOLEAN hash = !!data3; +static CHAR16 *der_suffix[] = { + L".cer", + L".der", + L".crt", + NULL +}; - uefi_call_wrapper(root->SetPosition, 2, root, 0); +static BOOLEAN check_der_suffix (CHAR16 *file_name) +{ + CHAR16 suffix[5]; + int i; - while (1) { - status = uefi_call_wrapper(root->Read, 3, root, &buffersize, - buffer); + if (!file_name || StrLen(file_name) <= 4) + return FALSE; - if (status == EFI_BUFFER_TOO_SMALL) { - buffer = AllocatePool(buffersize); - status = uefi_call_wrapper(root->Read, 3, root, - &buffersize, buffer); + suffix[0] = '\0'; + StrCat(suffix, file_name + StrLen(file_name) - 4); + + StrLwr (suffix); + for (i = 0; der_suffix[i] != NULL; i++) { + if (StrCmp(suffix, der_suffix[i]) == 0) { + return TRUE; } - - if (status != EFI_SUCCESS) - return 1; - - if (!buffersize) - break; - - if ((StrCmp(buffer->FileName, L".") == 0) || - (StrCmp(buffer->FileName, L"..") == 0)) - continue; - - dircount++; - - FreePool(buffer); - buffersize = 0; } - dircount++; - - dircontent = AllocatePool(sizeof(struct menu_item) * dircount); - - dircontent[0].text = StrDuplicate(L"Return to filesystem list"); - dircontent[0].callback = NULL; - dircontent[0].colour = EFI_YELLOW; - i++; - - uefi_call_wrapper(root->SetPosition, 2, root, 0); - - while (1) { - status = uefi_call_wrapper(root->Read, 3, root, &buffersize, - buffer); - - if (status == EFI_BUFFER_TOO_SMALL) { - buffer = AllocatePool(buffersize); - status = uefi_call_wrapper(root->Read, 3, root, - &buffersize, buffer); - } - - if (status != EFI_SUCCESS) - return 1; - - if (!buffersize) - break; - - if ((StrCmp(buffer->FileName, L".") == 0) || - (StrCmp(buffer->FileName, L"..") == 0)) - continue; - - if (buffer->Attribute & EFI_FILE_DIRECTORY) { - dircontent[i].text = StrDuplicate(buffer->FileName); - dircontent[i].callback = directory_callback; - dircontent[i].data = dircontent[i].text; - dircontent[i].data2 = root; - dircontent[i].data3 = data3; - dircontent[i].colour = EFI_YELLOW; - } else { - dircontent[i].text = StrDuplicate(buffer->FileName); - dircontent[i].callback = file_callback; - dircontent[i].data = dircontent[i].text; - dircontent[i].data2 = root; - dircontent[i].data3 = data3; - dircontent[i].colour = EFI_WHITE; - } - - i++; - FreePool(buffer); - buffer = NULL; - buffersize = 0; - } - - if (hash) - run_menu(HASH_STRING, 2, dircontent, dircount, 0); - else - run_menu(CERT_STRING, 2, dircontent, dircount, 0); - - return 0; + return FALSE; } -static INTN find_fs (void *data, void *data2, void *data3) { - EFI_GUID fs_guid = SIMPLE_FILE_SYSTEM_PROTOCOL; - UINTN count, i; - UINTN OldSize, NewSize; - EFI_HANDLE *filesystem_handles = NULL; - struct menu_item *filesystems; - BOOLEAN hash = !!data3; +static void mok_key_enroll(void) +{ + EFI_STATUS efi_status; + CHAR16 *file_name = NULL; + EFI_HANDLE im = NULL; + EFI_FILE *file = NULL; + UINTN filesize; + void *data; - uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &fs_guid, - NULL, &count, &filesystem_handles); + simple_file_selector(&im, (CHAR16 *[]){ + L"Select Key", + L"", + L"The selected key will be enrolled into the MOK database", + L"This means any binaries signed with it will be run without prompting", + L"Remember to make sure it is a genuine key before Enroling it", + NULL + }, L"\\", L"", &file_name); - if (!count || !filesystem_handles) { - Print(L"No filesystems?\n"); - return 1; + if (!file_name) + return; + + if (!check_der_suffix(file_name)) { + console_alertbox((CHAR16 *[]){ + L"Unsupported Format", + L"", + L"Only DER encoded certificate (*.cer/der/crt) is supported", + NULL}); + return; } - count++; + efi_status = simple_file_open(im, file_name, &file, EFI_FILE_MODE_READ); - filesystems = AllocatePool(sizeof(struct menu_item) * count); - - filesystems[0].text = StrDuplicate(L"Exit"); - filesystems[0].callback = NULL; - filesystems[0].colour = EFI_YELLOW; - - for (i=1; iHandleProtocol, 3, fs, &fs_guid, - (void **)&fs_interface); - - if (status != EFI_SUCCESS || !fs_interface) - continue; - - path = DevicePathFromHandle(fs); - - status = uefi_call_wrapper(fs_interface->OpenVolume, 2, - fs_interface, &root); - - if (status != EFI_SUCCESS || !root) - continue; - - status = uefi_call_wrapper(root->GetInfo, 4, root, - &file_info_guid, &buffersize, - buffer); - - if (status == EFI_BUFFER_TOO_SMALL) { - buffer = AllocatePool(buffersize); - status = uefi_call_wrapper(root->GetInfo, 4, root, - &file_info_guid, - &buffersize, buffer); - } - - if (status == EFI_SUCCESS) - VolumeLabel = buffer->VolumeLabel; - - if (path) - filesystems[i].text = DevicePathToStr(path); - else - filesystems[i].text = StrDuplicate(L"Unknown device\n"); - if (VolumeLabel) { - OldSize = (StrLen(filesystems[i].text) + 1) * sizeof(CHAR16); - NewSize = OldSize + StrLen(VolumeLabel) * sizeof(CHAR16); - filesystems[i].text = ReallocatePool(filesystems[i].text, - OldSize, NewSize); - StrCat(filesystems[i].text, VolumeLabel); - } - - if (buffersize) - FreePool(buffer); - - filesystems[i].data = root; - filesystems[i].data2 = NULL; - filesystems[i].data3 = data3; - filesystems[i].callback = filesystem_callback; - filesystems[i].colour = EFI_YELLOW; + if (efi_status != EFI_SUCCESS) { + console_error(L"Unable to open file", efi_status); + return; } - uefi_call_wrapper(BS->FreePool, 1, filesystem_handles); + simple_file_read_all(file, &filesize, &data); + simple_file_close(file); - if (hash) - run_menu(HASH_STRING, 2, filesystems, count, 0); - else - run_menu(CERT_STRING, 2, filesystems, count, 0); + if (!filesize) { + console_error(L"Unable to read file", efi_status); + return; + } - return 0; + enroll_file(data, filesize, FALSE); + FreePool(data); } -static BOOLEAN verify_pw(void) +static BOOLEAN verify_pw(BOOLEAN *protected) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; - UINT8 pwhash[SHA256_DIGEST_SIZE]; - UINTN size = SHA256_DIGEST_SIZE; + SIMPLE_TEXT_OUTPUT_MODE SavedMode; + UINT8 pwhash[PASSWORD_CRYPT_SIZE]; + UINTN size = PASSWORD_CRYPT_SIZE; UINT32 attributes; + CHAR16 *message[2]; + + *protected = FALSE; efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokPWStore", &shim_lock_guid, &attributes, &size, @@ -1704,7 +1546,8 @@ static BOOLEAN verify_pw(void) * known value, so there's no safety advantage in failing to validate * purely because of a failure to read the variable */ - if (efi_status != EFI_SUCCESS) + if (efi_status != EFI_SUCCESS || + (size != SHA256_DIGEST_SIZE && size != PASSWORD_CRYPT_SIZE)) return TRUE; if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) @@ -1712,46 +1555,129 @@ static BOOLEAN verify_pw(void) uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - efi_status = match_password(NULL, 0, pwhash, L"Enter MOK password: "); + /* Draw the background */ + console_save_and_set_mode(&SavedMode); + message[0] = PoolPrint (L"%s UEFI key management", SHIM_VENDOR); + message[1] = NULL; + console_print_box_at(message, -1, 0, 0, -1, -1, 1, 1); + FreePool(message[0]); + console_restore_mode(&SavedMode); + + if (size == PASSWORD_CRYPT_SIZE) { + efi_status = match_password((PASSWORD_CRYPT *)pwhash, NULL, 0, + NULL, L"Enter MOK password:"); + } else { + efi_status = match_password(NULL, NULL, 0, pwhash, + L"Enter MOK password:"); + } if (efi_status != EFI_SUCCESS) { - Print(L"Password limit reached\n"); + console_notify(L"Password limit reached"); return FALSE; } + *protected = TRUE; + return TRUE; } +static int draw_countdown() +{ + SIMPLE_TEXT_OUTPUT_MODE SavedMode; + EFI_INPUT_KEY key; + EFI_STATUS status; + UINTN cols, rows; + CHAR16 *title[2]; + CHAR16 *message = L"Press any key to perform MOK management"; + int timeout = 10, wait = 10000000; + + console_save_and_set_mode (&SavedMode); + + title[0] = PoolPrint (L"%s UEFI key management", SHIM_VENDOR); + title[1] = NULL; + + console_print_box_at(title, -1, 0, 0, -1, -1, 1, 1); + + uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, + ST->ConOut->Mode->Mode, &cols, &rows); + + PrintAt((cols - StrLen(message))/2, rows/2, message); + while (1) { + if (timeout > 1) + PrintAt(2, rows - 3, L"Booting in %d seconds ", timeout); + else if (timeout) + PrintAt(2, rows - 3, L"Booting in %d second ", timeout); + + status = WaitForSingleEvent(ST->ConIn->WaitForKey, wait); + + if (status != EFI_TIMEOUT) { + /* Clear the key in the queue */ + uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key); + break; + } + + timeout--; + if (!timeout) + break; + } + + FreePool(title[0]); + + console_restore_mode(&SavedMode); + + return timeout; +} + +typedef enum { + MOK_CONTINUE_BOOT, + MOK_RESET_MOK, + MOK_ENROLL_MOK, + MOK_DELETE_MOK, + MOK_CHANGE_SB, + MOK_SET_PW, + MOK_CHANGE_DB, + MOK_KEY_ENROLL, + MOK_HASH_ENROLL +} mok_menu_item; + static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew, UINTN MokNewSize, void *MokDel, UINTN MokDelSize, void *MokSB, UINTN MokSBSize, - void *MokPW, UINTN MokPWSize) + void *MokPW, UINTN MokPWSize, + void *MokDB, UINTN MokDBSize) { - struct menu_item *menu_item; + CHAR16 **menu_strings; + mok_menu_item *menu_item; + int choice = 0; UINT32 MokAuth = 0; UINT32 MokDelAuth = 0; UINTN menucount = 3, i = 0; EFI_STATUS efi_status; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - UINT8 auth[SHA256_DIGEST_SIZE]; - UINTN auth_size = SHA256_DIGEST_SIZE; + UINT8 auth[PASSWORD_CRYPT_SIZE]; + UINTN auth_size = PASSWORD_CRYPT_SIZE; UINT32 attributes; + BOOLEAN protected; + EFI_STATUS ret = EFI_SUCCESS; - if (verify_pw() == FALSE) + if (verify_pw(&protected) == FALSE) return EFI_ACCESS_DENIED; - + efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth", &shim_lock_guid, &attributes, &auth_size, auth); - if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE)) + if ((efi_status == EFI_SUCCESS) && + (auth_size == SHA256_DIGEST_SIZE || auth_size == PASSWORD_CRYPT_SIZE)) MokAuth = 1; efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDelAuth", &shim_lock_guid, &attributes, &auth_size, auth); - if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE)) + if ((efi_status == EFI_SUCCESS) && + (auth_size == SHA256_DIGEST_SIZE || auth_size == PASSWORD_CRYPT_SIZE)) MokDelAuth = 1; if (MokNew || MokAuth) @@ -1766,127 +1692,201 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, if (MokPW) menucount++; - menu_item = AllocateZeroPool(sizeof(struct menu_item) * menucount); + if (MokDB) + menucount++; - if (!menu_item) + menu_strings = AllocateZeroPool(sizeof(CHAR16 *) * (menucount + 1)); + + if (!menu_strings) return EFI_OUT_OF_RESOURCES; - menu_item[i].text = StrDuplicate(L"Continue boot"); - menu_item[i].colour = EFI_WHITE; - menu_item[i].callback = NULL; + menu_item = AllocateZeroPool(sizeof(mok_menu_item) * menucount); + + if (!menu_item) { + FreePool(menu_strings); + return EFI_OUT_OF_RESOURCES; + } + + menu_strings[i] = L"Continue boot"; + menu_item[i] = MOK_CONTINUE_BOOT; i++; if (MokNew || MokAuth) { if (!MokNew) { - menu_item[i].text = StrDuplicate(L"Reset MOK"); - menu_item[i].colour = EFI_WHITE; - menu_item[i].callback = mok_reset_prompt; + menu_strings[i] = L"Reset MOK"; + menu_item[i] = MOK_RESET_MOK; } else { - menu_item[i].text = StrDuplicate(L"Enroll MOK"); - menu_item[i].colour = EFI_WHITE; - menu_item[i].data = MokNew; - menu_item[i].data2 = (void *)MokNewSize; - menu_item[i].callback = mok_enrollment_prompt_callback; + menu_strings[i] = L"Enroll MOK"; + menu_item[i] = MOK_ENROLL_MOK; } i++; } - if (MokDel || MokDelAuth) { - menu_item[i].text = StrDuplicate(L"Delete MOK"); - menu_item[i].colour = EFI_WHITE; - menu_item[i].data = MokDel; - menu_item[i].data2 = (void *)MokDelSize; - menu_item[i].callback = mok_deletion_prompt; + if (MokDel || MokDelAuth) { + menu_strings[i] = L"Delete MOK"; + menu_item[i] = MOK_DELETE_MOK; i++; } if (MokSB) { - menu_item[i].text = StrDuplicate(L"Change Secure Boot state"); - menu_item[i].colour = EFI_WHITE; - menu_item[i].callback = mok_sb_prompt; - menu_item[i].data = MokSB; - menu_item[i].data2 = (void *)MokSBSize; + menu_strings[i] = L"Change Secure Boot state"; + menu_item[i] = MOK_CHANGE_SB; i++; } if (MokPW) { - menu_item[i].text = StrDuplicate(L"Set MOK password"); - menu_item[i].colour = EFI_WHITE; - menu_item[i].callback = mok_pw_prompt; - menu_item[i].data = MokPW; - menu_item[i].data2 = (void *)MokPWSize; + menu_strings[i] = L"Set MOK password"; + menu_item[i] = MOK_SET_PW; i++; } - menu_item[i].text = StrDuplicate(L"Enroll key from disk"); - menu_item[i].colour = EFI_WHITE; - menu_item[i].callback = find_fs; - menu_item[i].data3 = (void *)FALSE; + if (MokDB) { + menu_strings[i] = L"Change DB state"; + menu_item[i] = MOK_CHANGE_DB; + i++; + } + menu_strings[i] = L"Enroll key from disk"; + menu_item[i] = MOK_KEY_ENROLL; i++; - menu_item[i].text = StrDuplicate(L"Enroll hash from disk"); - menu_item[i].colour = EFI_WHITE; - menu_item[i].callback = find_fs; - menu_item[i].data3 = (void *)TRUE; - + menu_strings[i] = L"Enroll hash from disk"; + menu_item[i] = MOK_HASH_ENROLL; i++; - run_menu(NULL, 0, menu_item, menucount, 10); + menu_strings[i] = NULL; - uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + if (protected == FALSE && draw_countdown() == 0) + goto out; - return 0; + while (choice >= 0) { + choice = console_select((CHAR16 *[]){ L"Perform MOK management", NULL }, + menu_strings, 0); + + if (choice < 0) + goto out; + + switch (menu_item[choice]) { + case MOK_CONTINUE_BOOT: + goto out; + case MOK_RESET_MOK: + mok_reset_prompt(); + break; + case MOK_ENROLL_MOK: + mok_enrollment_prompt(MokNew, MokNewSize, TRUE); + break; + case MOK_DELETE_MOK: + mok_deletion_prompt(MokDel, MokDelSize); + break; + case MOK_CHANGE_SB: + mok_sb_prompt(MokSB, MokSBSize); + break; + case MOK_SET_PW: + mok_pw_prompt(MokPW, MokPWSize); + break; + case MOK_CHANGE_DB: + mok_db_prompt(MokDB, MokDBSize); + break; + case MOK_KEY_ENROLL: + mok_key_enroll(); + break; + case MOK_HASH_ENROLL: + mok_hash_enroll(); + break; + } + } + +out: + console_reset(); + + FreePool(menu_strings); + + if (menu_item) + FreePool(menu_item); + + return ret; } static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - UINTN MokNewSize = 0, MokDelSize = 0, MokSBSize = 0, MokPWSize = 0; + UINTN MokNewSize = 0, MokDelSize = 0, MokSBSize = 0, MokPWSize = 0, + MokDBSize = 0; void *MokNew = NULL; void *MokDel = NULL; void *MokSB = NULL; void *MokPW = NULL; + void *MokDB = NULL; + EFI_STATUS status; - MokNew = LibGetVariableAndSize(L"MokNew", &shim_lock_guid, &MokNewSize); + status = get_variable(L"MokNew", (UINT8 **)&MokNew, &MokNewSize, + shim_lock_guid); + if (status == EFI_SUCCESS) { + if (LibDeleteVariable(L"MokNew", &shim_lock_guid) != EFI_SUCCESS) { + console_notify(L"Failed to delete MokNew"); + } + } else if (EFI_ERROR(status) && status != EFI_NOT_FOUND) { + console_error(L"Could not retrieve MokNew", status); + } - MokDel = LibGetVariableAndSize(L"MokDel", &shim_lock_guid, &MokDelSize); + status = get_variable(L"MokDel", (UINT8 **)&MokDel, &MokDelSize, + shim_lock_guid); + if (status == EFI_SUCCESS) { + if (LibDeleteVariable(L"MokDel", &shim_lock_guid) != EFI_SUCCESS) { + console_notify(L"Failed to delete MokDel"); + } + } else if (EFI_ERROR(status) && status != EFI_NOT_FOUND) { + console_error(L"Could not retrieve MokDel", status); + } - MokSB = LibGetVariableAndSize(L"MokSB", &shim_lock_guid, &MokSBSize); + status = get_variable(L"MokSB", (UINT8 **)&MokSB, &MokSBSize, + shim_lock_guid); + if (status == EFI_SUCCESS) { + if (LibDeleteVariable(L"MokSB", &shim_lock_guid) != EFI_SUCCESS) { + console_notify(L"Failed to delete MokSB"); + } + } else if (EFI_ERROR(status) && status != EFI_NOT_FOUND) { + console_error(L"Could not retrieve MokSB", status); + } - MokPW = LibGetVariableAndSize(L"MokPW", &shim_lock_guid, &MokPWSize); + status = get_variable(L"MokPW", (UINT8 **)&MokPW, &MokPWSize, + shim_lock_guid); + if (status == EFI_SUCCESS) { + if (LibDeleteVariable(L"MokPW", &shim_lock_guid) != EFI_SUCCESS) { + console_notify(L"Failed to delete MokPW"); + } + } else if (EFI_ERROR(status) && status != EFI_NOT_FOUND) { + console_error(L"Could not retrieve MokPW", status); + } + + status = get_variable(L"MokDB", (UINT8 **)&MokDB, &MokDBSize, + shim_lock_guid); + if (status == EFI_SUCCESS) { + if (LibDeleteVariable(L"MokDB", &shim_lock_guid) != EFI_SUCCESS) { + console_notify(L"Failed to delete MokDB"); + } + } else if (EFI_ERROR(status) && status != EFI_NOT_FOUND) { + console_error(L"Could not retrieve MokDB", status); + } enter_mok_menu(image_handle, MokNew, MokNewSize, MokDel, MokDelSize, - MokSB, MokSBSize, MokPW, MokPWSize); + MokSB, MokSBSize, MokPW, MokPWSize, MokDB, MokDBSize); - if (MokNew) { - if (LibDeleteVariable(L"MokNew", &shim_lock_guid) != EFI_SUCCESS) { - Print(L"Failed to delete MokNew\n"); - } + if (MokNew) FreePool (MokNew); - } - if (MokDel) { - if (LibDeleteVariable(L"MokDel", &shim_lock_guid) != EFI_SUCCESS) { - Print(L"Failed to delete MokDel\n"); - } + if (MokDel) FreePool (MokDel); - } - if (MokSB) { - if (LibDeleteVariable(L"MokSB", &shim_lock_guid) != EFI_SUCCESS) { - Print(L"Failed to delete MokSB\n"); - } - FreePool (MokNew); - } + if (MokSB) + FreePool (MokSB); - if (MokPW) { - if (LibDeleteVariable(L"MokPW", &shim_lock_guid) != EFI_SUCCESS) { - Print(L"Failed to delete MokPW\n"); - } - FreePool (MokNew); - } + if (MokPW) + FreePool (MokPW); + + if (MokDB) + FreePool (MokDB); LibDeleteVariable(L"MokAuth", &shim_lock_guid); LibDeleteVariable(L"MokDelAuth", &shim_lock_guid); @@ -1925,9 +1925,12 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab) InitializeLib(image_handle, systab); + setup_console(1); + setup_rand(); efi_status = check_mok_request(image_handle); + setup_console(0); return efi_status; } diff --git a/MokVars.txt b/MokVars.txt index 74f0908..cac5349 100644 --- a/MokVars.txt +++ b/MokVars.txt @@ -25,6 +25,23 @@ three randomly chosen characters from the password. If successful, they will then be prompted to change the signature validation according to MokSBState. BS,RT,NV +MokDB: Set by MokUtil when requesting a change in state of validation +using db hashes and certs. A packed structure as follows: + +typedef struct { + UINT32 MokDBState; + UINT32 PWLen; + CHAR16 Password[PASSWORD_MAX]; +} __attribute__ ((packed)) MokDBvar; + +If MokDBState is 0, the user will be prompted to disable usage of db for +validation. Otherwise, the user will be prompted to allow it. PWLen +is the length of the password, in characters. Password is a UCS-2 +representation of the password. The user will be prompted to enter +three randomly chosen characters from the password. If successful, +they will then be prompted to change the signature validation +according to MokDBState. BS,RT,NV + MokNew: Set by MokUtil when requesting the addition or removal of keys from MokList. Is an EFI_SIGNATURE_LIST as described in the UEFI specification. BS,RT,NV @@ -46,6 +63,12 @@ MokListRT: A copy of MokList made available to the kernel at runtime. RT MokSBState: An 8-bit unsigned integer. If 1, shim will switch to insecure mode. BS,NV +MokDBState: An 8-bit unsigned integer. If 1, shim will not use db for +verification. BS,NV + +MokIgnoreDB: An 8-bit unsigned integer. This allows the OS to query whether +or not to import DB certs for its own verification purposes. + MokPWStore: A SHA-256 representation of the password set by the user via MokPW. The user will be prompted to enter this password in order to interact with MokManager. diff --git a/PasswordCrypt.c b/PasswordCrypt.c new file mode 100644 index 0000000..8d72a82 --- /dev/null +++ b/PasswordCrypt.c @@ -0,0 +1,342 @@ +#include +#include +#include +#include +#include +#include +#include "PasswordCrypt.h" +#include "crypt_blowfish.h" + +#define TRAD_DES_HASH_SIZE 13 /* (64/6+1) + (12/6) */ +#define BSDI_DES_HASH_SIZE 20 /* (64/6+1) + (24/6) + 4 + 1 */ +#define BLOWFISH_HASH_SIZE 31 /* 184/6+1 */ + +UINT16 get_hash_size (const UINT16 method) +{ + switch (method) { + case TRADITIONAL_DES: + return TRAD_DES_HASH_SIZE; + case EXTEND_BSDI_DES: + return BSDI_DES_HASH_SIZE; + case MD5_BASED: + return MD5_DIGEST_LENGTH; + case SHA256_BASED: + return SHA256_DIGEST_LENGTH; + case SHA512_BASED: + return SHA512_DIGEST_LENGTH; + case BLOWFISH_BASED: + return BLOWFISH_HASH_SIZE; + } + + return 0; +} + +static EFI_STATUS trad_des_crypt (const char *key, const char *salt, UINT8 *hash) +{ + char result[TRAD_DES_HASH_SIZE + 1]; + char *ret; + + ret = DES_fcrypt(key, salt, result); + if (ret) { + CopyMem(hash, result, TRAD_DES_HASH_SIZE); + return EFI_SUCCESS; + } + + return EFI_UNSUPPORTED; +} + +static const char md5_salt_prefix[] = "$1$"; + +static EFI_STATUS md5_crypt (const char *key, UINT32 key_len, + const char *salt, UINT32 salt_size, + UINT8 *hash) +{ + MD5_CTX ctx, alt_ctx; + UINT8 alt_result[MD5_DIGEST_LENGTH]; + UINTN cnt; + + MD5_Init(&ctx); + MD5_Update(&ctx, key, key_len); + MD5_Update(&ctx, md5_salt_prefix, sizeof(md5_salt_prefix) - 1); + MD5_Update(&ctx, salt, salt_size); + + MD5_Init(&alt_ctx); + MD5_Update(&alt_ctx, key, key_len); + MD5_Update(&alt_ctx, salt, salt_size); + MD5_Update(&alt_ctx, key, key_len); + MD5_Final(alt_result, &alt_ctx); + + for (cnt = key_len; cnt > 16; cnt -= 16) + MD5_Update(&ctx, alt_result, 16); + MD5_Update(&ctx, alt_result, cnt); + + *alt_result = '\0'; + + for (cnt = key_len; cnt > 0; cnt >>= 1) { + if ((cnt & 1) != 0) { + MD5_Update(&ctx, alt_result, 1); + } else { + MD5_Update(&ctx, key, 1); + } + } + MD5_Final(alt_result, &ctx); + + for (cnt = 0; cnt < 1000; ++cnt) { + MD5_Init(&ctx); + + if ((cnt & 1) != 0) + MD5_Update(&ctx, key, key_len); + else + MD5_Update(&ctx, alt_result, 16); + + if (cnt % 3 != 0) + MD5_Update(&ctx, salt, salt_size); + + if (cnt % 7 != 0) + MD5_Update(&ctx, key, key_len); + + if ((cnt & 1) != 0) + MD5_Update(&ctx, alt_result, 16); + else + MD5_Update(&ctx, key, key_len); + + MD5_Final(alt_result, &ctx); + } + + CopyMem(hash, alt_result, MD5_DIGEST_LENGTH); + + return EFI_SUCCESS; +} + +static EFI_STATUS sha256_crypt (const char *key, UINT32 key_len, + const char *salt, UINT32 salt_size, + const UINT32 rounds, UINT8 *hash) +{ + SHA256_CTX ctx, alt_ctx; + UINT8 alt_result[SHA256_DIGEST_SIZE]; + UINT8 tmp_result[SHA256_DIGEST_SIZE]; + UINT8 *cp, *p_bytes, *s_bytes; + UINTN cnt; + + SHA256_Init(&ctx); + SHA256_Update(&ctx, key, key_len); + SHA256_Update(&ctx, salt, salt_size); + + SHA256_Init(&alt_ctx); + SHA256_Update(&alt_ctx, key, key_len); + SHA256_Update(&alt_ctx, salt, salt_size); + SHA256_Update(&alt_ctx, key, key_len); + SHA256_Final(alt_result, &alt_ctx); + + for (cnt = key_len; cnt > 32; cnt -= 32) + SHA256_Update(&ctx, alt_result, 32); + SHA256_Update(&ctx, alt_result, cnt); + + for (cnt = key_len; cnt > 0; cnt >>= 1) { + if ((cnt & 1) != 0) { + SHA256_Update(&ctx, alt_result, 32); + } else { + SHA256_Update(&ctx, key, key_len); + } + } + SHA256_Final(alt_result, &ctx); + + SHA256_Init(&alt_ctx); + for (cnt = 0; cnt < key_len; ++cnt) + SHA256_Update(&alt_ctx, key, key_len); + SHA256_Final(tmp_result, &alt_ctx); + + cp = p_bytes = AllocatePool(key_len); + for (cnt = key_len; cnt >= 32; cnt -= 32) { + CopyMem(cp, tmp_result, 32); + cp += 32; + } + CopyMem(cp, tmp_result, cnt); + + SHA256_Init(&alt_ctx); + for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) + SHA256_Update(&alt_ctx, salt, salt_size); + SHA256_Final(tmp_result, &alt_ctx); + + cp = s_bytes = AllocatePool(salt_size); + for (cnt = salt_size; cnt >= 32; cnt -= 32) { + CopyMem(cp, tmp_result, 32); + cp += 32; + } + CopyMem(cp, tmp_result, cnt); + + for (cnt = 0; cnt < rounds; ++cnt) { + SHA256_Init(&ctx); + + if ((cnt & 1) != 0) + SHA256_Update(&ctx, p_bytes, key_len); + else + SHA256_Update(&ctx, alt_result, 32); + + if (cnt % 3 != 0) + SHA256_Update(&ctx, s_bytes, salt_size); + + if (cnt % 7 != 0) + SHA256_Update(&ctx, p_bytes, key_len); + + if ((cnt & 1) != 0) + SHA256_Update(&ctx, alt_result, 32); + else + SHA256_Update(&ctx, p_bytes, key_len); + + SHA256_Final(alt_result, &ctx); + } + + CopyMem(hash, alt_result, SHA256_DIGEST_SIZE); + + FreePool(p_bytes); + FreePool(s_bytes); + + return EFI_SUCCESS; +} + +static EFI_STATUS sha512_crypt (const char *key, UINT32 key_len, + const char *salt, UINT32 salt_size, + const UINT32 rounds, UINT8 *hash) +{ + SHA512_CTX ctx, alt_ctx; + UINT8 alt_result[SHA512_DIGEST_LENGTH]; + UINT8 tmp_result[SHA512_DIGEST_LENGTH]; + UINT8 *cp, *p_bytes, *s_bytes; + UINTN cnt; + + SHA512_Init(&ctx); + SHA512_Update(&ctx, key, key_len); + SHA512_Update(&ctx, salt, salt_size); + + SHA512_Init(&alt_ctx); + SHA512_Update(&alt_ctx, key, key_len); + SHA512_Update(&alt_ctx, salt, salt_size); + SHA512_Update(&alt_ctx, key, key_len); + + SHA512_Final(alt_result, &alt_ctx); + + for (cnt = key_len; cnt > 64; cnt -= 64) + SHA512_Update(&ctx, alt_result, 64); + SHA512_Update(&ctx, alt_result, cnt); + + for (cnt = key_len; cnt > 0; cnt >>= 1) { + if ((cnt & 1) != 0) { + SHA512_Update(&ctx, alt_result, 64); + } else { + SHA512_Update(&ctx, key, key_len); + } + } + SHA512_Final(alt_result, &ctx); + + SHA512_Init(&alt_ctx); + for (cnt = 0; cnt < key_len; ++cnt) + SHA512_Update(&alt_ctx, key, key_len); + SHA512_Final(tmp_result, &alt_ctx); + + cp = p_bytes = AllocatePool(key_len); + for (cnt = key_len; cnt >= 64; cnt -= 64) { + CopyMem(cp, tmp_result, 64); + cp += 64; + } + CopyMem(cp, tmp_result, cnt); + + SHA512_Init(&alt_ctx); + for (cnt = 0; cnt < 16 + alt_result[0]; ++cnt) + SHA512_Update(&alt_ctx, salt, salt_size); + SHA512_Final(tmp_result, &alt_ctx); + + cp = s_bytes = AllocatePool(salt_size); + for (cnt = salt_size; cnt >= 64; cnt -= 64) { + CopyMem(cp, tmp_result, 64); + cp += 64; + } + CopyMem(cp, tmp_result, cnt); + + for (cnt = 0; cnt < rounds; ++cnt) { + SHA512_Init(&ctx); + + if ((cnt & 1) != 0) + SHA512_Update(&ctx, p_bytes, key_len); + else + SHA512_Update(&ctx, alt_result, 64); + + if (cnt % 3 != 0) + SHA512_Update(&ctx, s_bytes, salt_size); + + if (cnt % 7 != 0) + SHA512_Update(&ctx, p_bytes, key_len); + + if ((cnt & 1) != 0) + SHA512_Update(&ctx, alt_result, 64); + else + SHA512_Update(&ctx, p_bytes, key_len); + + SHA512_Final(alt_result, &ctx); + } + + CopyMem(hash, alt_result, SHA512_DIGEST_LENGTH); + + FreePool(p_bytes); + FreePool(s_bytes); + + return EFI_SUCCESS; +} + +#define BF_RESULT_SIZE (7 + 22 + 31 + 1) + +static EFI_STATUS blowfish_crypt (const char *key, const char *salt, UINT8 *hash) +{ + char *retval, result[BF_RESULT_SIZE]; + + retval = crypt_blowfish_rn (key, salt, result, BF_RESULT_SIZE); + if (!retval) + return EFI_UNSUPPORTED; + + CopyMem(hash, result + 7 + 22, BF_RESULT_SIZE); + + return EFI_SUCCESS; +} + +EFI_STATUS password_crypt (const char *password, UINT32 pw_length, + const PASSWORD_CRYPT *pw_crypt, UINT8 *hash) +{ + EFI_STATUS status; + + if (!pw_crypt) + return EFI_INVALID_PARAMETER; + + switch (pw_crypt->method) { + case TRADITIONAL_DES: + status = trad_des_crypt (password, (char *)pw_crypt->salt, hash); + break; + case EXTEND_BSDI_DES: + status = EFI_UNSUPPORTED; + break; + case MD5_BASED: + status = md5_crypt (password, pw_length, (char *)pw_crypt->salt, + pw_crypt->salt_size, hash); + break; + case SHA256_BASED: + status = sha256_crypt(password, pw_length, (char *)pw_crypt->salt, + pw_crypt->salt_size, pw_crypt->iter_count, + hash); + break; + case SHA512_BASED: + status = sha512_crypt(password, pw_length, (char *)pw_crypt->salt, + pw_crypt->salt_size, pw_crypt->iter_count, + hash); + break; + case BLOWFISH_BASED: + if (pw_crypt->salt_size != (7 + 22 + 1)) { + status = EFI_INVALID_PARAMETER; + break; + } + status = blowfish_crypt(password, (char *)pw_crypt->salt, hash); + break; + default: + return EFI_INVALID_PARAMETER; + } + + return status; +} diff --git a/PasswordCrypt.h b/PasswordCrypt.h new file mode 100644 index 0000000..b726f32 --- /dev/null +++ b/PasswordCrypt.h @@ -0,0 +1,27 @@ +#ifndef __PASSWORD_CRYPT_H__ +#define __PASSWORD_CRYPT_H__ + +enum HashMethod { + TRADITIONAL_DES = 0, + EXTEND_BSDI_DES, + MD5_BASED, + SHA256_BASED, + SHA512_BASED, + BLOWFISH_BASED +}; + +typedef struct { + UINT16 method; + UINT64 iter_count; + UINT16 salt_size; + UINT8 salt[32]; + UINT8 hash[128]; +} __attribute__ ((packed)) PASSWORD_CRYPT; + +#define PASSWORD_CRYPT_SIZE sizeof(PASSWORD_CRYPT) + +EFI_STATUS password_crypt (const char *password, UINT32 pw_length, + const PASSWORD_CRYPT *pw_hash, UINT8 *hash); +UINT16 get_hash_size (const UINT16 method); + +#endif /* __PASSWORD_CRYPT_H__ */ diff --git a/TODO b/TODO index 2de89ba..029b0bf 100644 --- a/TODO +++ b/TODO @@ -1 +1,23 @@ -Support for netbooting \ No newline at end of file +Versioned protocol: +- Make shim and the bootloaders using it express how enlightened they + are to one another, so we can stop earlier without tricks like + the one above +MokListRT signing: +- For kexec and hybernate to work right, MokListRT probably needs to + be an authenticated variable. It's probable this needs to be done + in the kernel boot stub instead, just because it'll need an + ephemeral key to be generated, and that means we need some entropy + to build up. +New security protocol: +- TBD +kexec MoK Management: +Modsign enforcement mgmt MoK: +- This is part of the plan for SecureBoot patches. Basically these + features need to be disableable/enableable in MokManager. +Variable for debug: +- basically we need to be able to set a UEFI variable and get debug + output. Right now some code uses SHIM_VERBOSE but that needs a fair + amount of work to actually be useful. +Hashing of option roms: +- hash option roms and add them to MokListRT +- probably belongs in MokManager diff --git a/cert.S b/cert.S index 2ed9b6d..3cfd665 100644 --- a/cert.S +++ b/cert.S @@ -1,36 +1,67 @@ + .globl cert_table + .data + .align 16 + .type cert_table, @object + .size cert_table, 4 + .section .vendor_cert, "a", @progbits +cert_table: #if defined(VENDOR_CERT_FILE) - .globl vendor_cert_size - .data - .align 1 - .type vendor_cert_size, @object - .size vendor_cert_size, 4 - .section .vendor_cert, "a", @progbits -vendor_cert_size: - .long .L0 - vendor_cert - .globl vendor_cert - .data - .align 1 - .type vendor_cert, @object - .size vendor_cert, .L0-vendor_cert - .section .vendor_cert, "a", @progbits -vendor_cert: -.incbin VENDOR_CERT_FILE -.L0: + .long vendor_cert_priv_end - vendor_cert_priv #else - .globl vendor_cert - .bss - .type vendor_cert, @object - .size vendor_cert, 1 + .long 0 +#endif +#if defined(VENDOR_DBX_FILE) + .long vendor_dbx_priv_end - vendor_dbx_priv +#else + .long 0 +#endif + .long vendor_cert_priv - cert_table + .long vendor_dbx_priv - cert_table +#if defined(VENDOR_CERT_FILE) + .data + .align 1 + .type vendor_cert_priv, @object + .size vendor_cert_priv, vendor_cert_priv_end-vendor_cert_priv .section .vendor_cert, "a", @progbits -vendor_cert: +vendor_cert_priv: +.incbin VENDOR_CERT_FILE +vendor_cert_priv_end: +#else + .bss + .type vendor_cert_priv, @object + .size vendor_cert_priv, 1 + .section .vendor_cert, "a", @progbits +vendor_cert_priv: .zero 1 - .globl vendor_cert_size .data .align 4 - .type vendor_cert_size, @object - .size vendor_cert_size, 4 + .type vendor_cert_size_priv, @object + .size vendor_cert_size_priv, 4 .section .vendor_cert, "a", @progbits -vendor_cert_size: - .long 1 +vendor_cert_priv_end: +#endif +#if defined(VENDOR_DBX_FILE) + .data + .align 1 + .type vendor_dbx_priv, @object + .size vendor_dbx_priv, vendor_dbx_priv_end-vendor_dbx_priv + .section .vendor_cert, "a", @progbits +vendor_dbx_priv: +.incbin VENDOR_DBX_FILE +vendor_dbx_priv_end: +#else + .bss + .type vendor_dbx_priv, @object + .size vendor_dbx_priv, 1 + .section .vendor_cert, "a", @progbits +vendor_dbx_priv: + .zero 1 + + .data + .align 4 + .type vendor_dbx_size_priv, @object + .size vendor_dbx_size_priv, 4 + .section .vendor_cert, "a", @progbits +vendor_dbx_priv_end: #endif diff --git a/crypt_blowfish.c b/crypt_blowfish.c new file mode 100644 index 0000000..366a81a --- /dev/null +++ b/crypt_blowfish.c @@ -0,0 +1,822 @@ +/* + * The crypt_blowfish homepage is: + * + * http://www.openwall.com/crypt/ + * + * This code comes from John the Ripper password cracker, with reentrant + * and crypt(3) interfaces added, but optimizations specific to password + * cracking removed. + * + * Written by Solar Designer in 1998-2011. + * No copyright is claimed, and the software is hereby placed in the public + * domain. In case this attempt to disclaim copyright and place the software + * in the public domain is deemed null and void, then the software is + * Copyright (c) 1998-2011 Solar Designer and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * It is my intent that you should be able to use this on your system, + * as part of a software package, or anywhere else to improve security, + * ensure compatibility, or for any other purpose. I would appreciate + * it if you give credit where it is due and keep your modifications in + * the public domain as well, but I don't require that in order to let + * you place this code and any modifications you make under a license + * of your choice. + * + * This implementation is mostly compatible with OpenBSD's bcrypt.c (prefix + * "$2a$") by Niels Provos , and uses some of his + * ideas. The password hashing algorithm was designed by David Mazieres + * . For more information on the level of compatibility, + * prefer refer to the comments in BF_set_key() below and to the included + * crypt(3) man page. + * + * There's a paper on the algorithm that explains its design decisions: + * + * http://www.usenix.org/events/usenix99/provos.html + * + * Some of the tricks in BF_ROUND might be inspired by Eric Young's + * Blowfish library (I can't be sure if I would think of something if I + * hadn't seen his code). + */ + +#include +#include + +/* Just to make sure the prototypes match the actual definitions */ +#include "crypt_blowfish.h" + +typedef unsigned int BF_word; +typedef signed int BF_word_signed; + +/* Number of Blowfish rounds, this is also hardcoded into a few places */ +#define BF_N 16 + +typedef BF_word BF_key[BF_N + 2]; + +typedef struct { + BF_word S[4][0x100]; + BF_key P; +} BF_ctx; + +/* + * Magic IV for 64 Blowfish encryptions that we do at the end. + * The string is "OrpheanBeholderScryDoubt" on big-endian. + */ +static BF_word BF_magic_w[6] = { + 0x4F727068, 0x65616E42, 0x65686F6C, + 0x64657253, 0x63727944, 0x6F756274 +}; + +/* + * P-box and S-box tables initialized with digits of Pi. + */ +static BF_ctx BF_init_state = { + { + { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, + 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, + 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, + 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, + 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, + 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, + 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, + 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, + 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, + 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, + 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + }, { + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, + 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, + 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, + 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, + 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, + 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, + 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, + 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, + 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, + 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, + 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, + 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, + 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, + 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, + 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, + 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, + 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, + 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, + 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, + 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, + 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, + 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + }, { + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, + 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, + 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, + 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, + 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, + 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, + 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, + 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, + 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, + 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, + 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, + 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + }, { + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, + 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, + 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, + 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, + 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, + 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, + 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, + 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, + 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, + 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, + 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + } + }, { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + } +}; + +static unsigned char BF_itoa64[64 + 1] = + "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +static unsigned char BF_atoi64[0x60] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, 1, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 64, 64, 64, 64, 64, + 64, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 64, 64, 64, 64, 64, + 64, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 64, 64, 64, 64, 64 +}; + +#define BF_safe_atoi64(dst, src) \ +{ \ + tmp = (unsigned char)(src); \ + if ((unsigned int)(tmp -= 0x20) >= 0x60) return -1; \ + tmp = BF_atoi64[tmp]; \ + if (tmp > 63) return -1; \ + (dst) = tmp; \ +} + +static int BF_decode(BF_word *dst, const char *src, int size) +{ + unsigned char *dptr = (unsigned char *)dst; + unsigned char *end = dptr + size; + const unsigned char *sptr = (const unsigned char *)src; + unsigned int tmp, c1, c2, c3, c4; + + do { + BF_safe_atoi64(c1, *sptr++); + BF_safe_atoi64(c2, *sptr++); + *dptr++ = (c1 << 2) | ((c2 & 0x30) >> 4); + if (dptr >= end) break; + + BF_safe_atoi64(c3, *sptr++); + *dptr++ = ((c2 & 0x0F) << 4) | ((c3 & 0x3C) >> 2); + if (dptr >= end) break; + + BF_safe_atoi64(c4, *sptr++); + *dptr++ = ((c3 & 0x03) << 6) | c4; + } while (dptr < end); + + return 0; +} + +static void BF_encode(char *dst, const BF_word *src, int size) +{ + const unsigned char *sptr = (const unsigned char *)src; + const unsigned char *end = sptr + size; + unsigned char *dptr = (unsigned char *)dst; + unsigned int c1, c2; + + do { + c1 = *sptr++; + *dptr++ = BF_itoa64[c1 >> 2]; + c1 = (c1 & 0x03) << 4; + if (sptr >= end) { + *dptr++ = BF_itoa64[c1]; + break; + } + + c2 = *sptr++; + c1 |= c2 >> 4; + *dptr++ = BF_itoa64[c1]; + c1 = (c2 & 0x0f) << 2; + if (sptr >= end) { + *dptr++ = BF_itoa64[c1]; + break; + } + + c2 = *sptr++; + c1 |= c2 >> 6; + *dptr++ = BF_itoa64[c1]; + *dptr++ = BF_itoa64[c2 & 0x3f]; + } while (sptr < end); +} + +static void BF_swap(BF_word *x, int count) +{ + static int endianness_check = 1; + char *is_little_endian = (char *)&endianness_check; + BF_word tmp; + + if (*is_little_endian) + do { + tmp = *x; + tmp = (tmp << 16) | (tmp >> 16); + *x++ = ((tmp & 0x00FF00FF) << 8) | ((tmp >> 8) & 0x00FF00FF); + } while (--count); +} + +/* Architectures which can shift addresses left by 2 bits with no extra cost */ +#define BF_ROUND(L, R, N) \ + tmp1 = L & 0xFF; \ + tmp2 = L >> 8; \ + tmp2 &= 0xFF; \ + tmp3 = L >> 16; \ + tmp3 &= 0xFF; \ + tmp4 = L >> 24; \ + tmp1 = data.ctx.S[3][tmp1]; \ + tmp2 = data.ctx.S[2][tmp2]; \ + tmp3 = data.ctx.S[1][tmp3]; \ + tmp3 += data.ctx.S[0][tmp4]; \ + tmp3 ^= tmp2; \ + R ^= data.ctx.P[N + 1]; \ + tmp3 += tmp1; \ + R ^= tmp3; + +/* + * Encrypt one block, BF_N is hardcoded here. + */ +#define BF_ENCRYPT \ + L ^= data.ctx.P[0]; \ + BF_ROUND(L, R, 0); \ + BF_ROUND(R, L, 1); \ + BF_ROUND(L, R, 2); \ + BF_ROUND(R, L, 3); \ + BF_ROUND(L, R, 4); \ + BF_ROUND(R, L, 5); \ + BF_ROUND(L, R, 6); \ + BF_ROUND(R, L, 7); \ + BF_ROUND(L, R, 8); \ + BF_ROUND(R, L, 9); \ + BF_ROUND(L, R, 10); \ + BF_ROUND(R, L, 11); \ + BF_ROUND(L, R, 12); \ + BF_ROUND(R, L, 13); \ + BF_ROUND(L, R, 14); \ + BF_ROUND(R, L, 15); \ + tmp4 = R; \ + R = L; \ + L = tmp4 ^ data.ctx.P[BF_N + 1]; + +#define BF_body() \ + L = R = 0; \ + ptr = data.ctx.P; \ + do { \ + ptr += 2; \ + BF_ENCRYPT; \ + *(ptr - 2) = L; \ + *(ptr - 1) = R; \ + } while (ptr < &data.ctx.P[BF_N + 2]); \ +\ + ptr = data.ctx.S[0]; \ + do { \ + ptr += 2; \ + BF_ENCRYPT; \ + *(ptr - 2) = L; \ + *(ptr - 1) = R; \ + } while (ptr < &data.ctx.S[3][0xFF]); + +static void BF_set_key(const char *key, BF_key expanded, BF_key initial, + unsigned char flags) +{ + const char *ptr = key; + unsigned int bug, i, j; + BF_word safety, sign, diff, tmp[2]; + +/* + * There was a sign extension bug in older revisions of this function. While + * we would have liked to simply fix the bug and move on, we have to provide + * a backwards compatibility feature (essentially the bug) for some systems and + * a safety measure for some others. The latter is needed because for certain + * multiple inputs to the buggy algorithm there exist easily found inputs to + * the correct algorithm that produce the same hash. Thus, we optionally + * deviate from the correct algorithm just enough to avoid such collisions. + * While the bug itself affected the majority of passwords containing + * characters with the 8th bit set (although only a percentage of those in a + * collision-producing way), the anti-collision safety measure affects + * only a subset of passwords containing the '\xff' character (not even all of + * those passwords, just some of them). This character is not found in valid + * UTF-8 sequences and is rarely used in popular 8-bit character encodings. + * Thus, the safety measure is unlikely to cause much annoyance, and is a + * reasonable tradeoff to use when authenticating against existing hashes that + * are not reliably known to have been computed with the correct algorithm. + * + * We use an approach that tries to minimize side-channel leaks of password + * information - that is, we mostly use fixed-cost bitwise operations instead + * of branches or table lookups. (One conditional branch based on password + * length remains. It is not part of the bug aftermath, though, and is + * difficult and possibly unreasonable to avoid given the use of C strings by + * the caller, which results in similar timing leaks anyway.) + * + * For actual implementation, we set an array index in the variable "bug" + * (0 means no bug, 1 means sign extension bug emulation) and a flag in the + * variable "safety" (bit 16 is set when the safety measure is requested). + * Valid combinations of settings are: + * + * Prefix "$2a$": bug = 0, safety = 0x10000 + * Prefix "$2x$": bug = 1, safety = 0 + * Prefix "$2y$": bug = 0, safety = 0 + */ + bug = (unsigned int)flags & 1; + safety = ((BF_word)flags & 2) << 15; + + sign = diff = 0; + + for (i = 0; i < BF_N + 2; i++) { + tmp[0] = tmp[1] = 0; + for (j = 0; j < 4; j++) { + tmp[0] <<= 8; + tmp[0] |= (unsigned char)*ptr; /* correct */ + tmp[1] <<= 8; + tmp[1] |= (BF_word_signed)(signed char)*ptr; /* bug */ +/* + * Sign extension in the first char has no effect - nothing to overwrite yet, + * and those extra 24 bits will be fully shifted out of the 32-bit word. For + * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign + * extension in tmp[1] occurs. Once this flag is set, it remains set. + */ + if (j) + sign |= tmp[1] & 0x80; + if (!*ptr) + ptr = key; + else + ptr++; + } + diff |= tmp[0] ^ tmp[1]; /* Non-zero on any differences */ + + expanded[i] = tmp[bug]; + initial[i] = BF_init_state.P[i] ^ tmp[bug]; + } + +/* + * At this point, "diff" is zero iff the correct and buggy algorithms produced + * exactly the same result. If so and if "sign" is non-zero, which indicates + * that there was a non-benign sign extension, this means that we have a + * collision between the correctly computed hash for this password and a set of + * passwords that could be supplied to the buggy algorithm. Our safety measure + * is meant to protect from such many-buggy to one-correct collisions, by + * deviating from the correct algorithm in such cases. Let's check for this. + */ + diff |= diff >> 16; /* still zero iff exact match */ + diff &= 0xffff; /* ditto */ + diff += 0xffff; /* bit 16 set iff "diff" was non-zero (on non-match) */ + sign <<= 9; /* move the non-benign sign extension flag to bit 16 */ + sign &= ~diff & safety; /* action needed? */ + +/* + * If we have determined that we need to deviate from the correct algorithm, + * flip bit 16 in initial expanded key. (The choice of 16 is arbitrary, but + * let's stick to it now. It came out of the approach we used above, and it's + * not any worse than any other choice we could make.) + * + * It is crucial that we don't do the same to the expanded key used in the main + * Eksblowfish loop. By doing it to only one of these two, we deviate from a + * state that could be directly specified by a password to the buggy algorithm + * (and to the fully correct one as well, but that's a side-effect). + */ + initial[0] ^= sign; +} + +static char *BF_crypt(const char *key, const char *setting, + char *output, int size, + BF_word min) +{ + static const unsigned char flags_by_subtype[26] = + {2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0}; + struct { + BF_ctx ctx; + BF_key expanded_key; + union { + BF_word salt[4]; + BF_word output[6]; + } binary; + } data; + BF_word L, R; + BF_word tmp1, tmp2, tmp3, tmp4; + BF_word *ptr; + BF_word count; + int i; + + if (size < 7 + 22 + 31 + 1) { + return NULL; + } + + if (setting[0] != '$' || + setting[1] != '2' || + setting[2] < 'a' || setting[2] > 'z' || + !flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a'] || + setting[3] != '$' || + setting[4] < '0' || setting[4] > '3' || + setting[5] < '0' || setting[5] > '9' || + (setting[4] == '3' && setting[5] > '1') || + setting[6] != '$') { + return NULL; + } + + count = (BF_word)1 << ((setting[4] - '0') * 10 + (setting[5] - '0')); + if (count < min || BF_decode(data.binary.salt, &setting[7], 16)) { + return NULL; + } + BF_swap(data.binary.salt, 4); + + BF_set_key(key, data.expanded_key, data.ctx.P, + flags_by_subtype[(unsigned int)(unsigned char)setting[2] - 'a']); + + CopyMem(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S)); + + L = R = 0; + for (i = 0; i < BF_N + 2; i += 2) { + L ^= data.binary.salt[i & 2]; + R ^= data.binary.salt[(i & 2) + 1]; + BF_ENCRYPT; + data.ctx.P[i] = L; + data.ctx.P[i + 1] = R; + } + + ptr = data.ctx.S[0]; + do { + ptr += 4; + L ^= data.binary.salt[(BF_N + 2) & 3]; + R ^= data.binary.salt[(BF_N + 3) & 3]; + BF_ENCRYPT; + *(ptr - 4) = L; + *(ptr - 3) = R; + + L ^= data.binary.salt[(BF_N + 4) & 3]; + R ^= data.binary.salt[(BF_N + 5) & 3]; + BF_ENCRYPT; + *(ptr - 2) = L; + *(ptr - 1) = R; + } while (ptr < &data.ctx.S[3][0xFF]); + + do { + int done; + + for (i = 0; i < BF_N + 2; i += 2) { + data.ctx.P[i] ^= data.expanded_key[i]; + data.ctx.P[i + 1] ^= data.expanded_key[i + 1]; + } + + done = 0; + do { + BF_body(); + if (done) + break; + done = 1; + + tmp1 = data.binary.salt[0]; + tmp2 = data.binary.salt[1]; + tmp3 = data.binary.salt[2]; + tmp4 = data.binary.salt[3]; + for (i = 0; i < BF_N; i += 4) { + data.ctx.P[i] ^= tmp1; + data.ctx.P[i + 1] ^= tmp2; + data.ctx.P[i + 2] ^= tmp3; + data.ctx.P[i + 3] ^= tmp4; + } + data.ctx.P[16] ^= tmp1; + data.ctx.P[17] ^= tmp2; + } while (1); + } while (--count); + + for (i = 0; i < 6; i += 2) { + L = BF_magic_w[i]; + R = BF_magic_w[i + 1]; + + count = 64; + do { + BF_ENCRYPT; + } while (--count); + + data.binary.output[i] = L; + data.binary.output[i + 1] = R; + } + + CopyMem(output, (void *)setting, 7 + 22 - 1); + output[7 + 22 - 1] = BF_itoa64[(int) + BF_atoi64[(int)setting[7 + 22 - 1] - 0x20] & 0x30]; + +/* This has to be bug-compatible with the original implementation, so + * only encode 23 of the 24 bytes. :-) */ + BF_swap(data.binary.output, 6); + BF_encode(&output[7 + 22], data.binary.output, 23); + output[7 + 22 + 31] = '\0'; + + return output; +} + +int _crypt_output_magic(const char *setting, char *output, int size) +{ + if (size < 3) + return -1; + + output[0] = '*'; + output[1] = '0'; + output[2] = '\0'; + + if (setting[0] == '*' && setting[1] == '0') + output[1] = '1'; + + return 0; +} + +/* + * Please preserve the runtime self-test. It serves two purposes at once: + * + * 1. We really can't afford the risk of producing incompatible hashes e.g. + * when there's something like gcc bug 26587 again, whereas an application or + * library integrating this code might not also integrate our external tests or + * it might not run them after every build. Even if it does, the miscompile + * might only occur on the production build, but not on a testing build (such + * as because of different optimization settings). It is painful to recover + * from incorrectly-computed hashes - merely fixing whatever broke is not + * enough. Thus, a proactive measure like this self-test is needed. + * + * 2. We don't want to leave sensitive data from our actual password hash + * computation on the stack or in registers. Previous revisions of the code + * would do explicit cleanups, but simply running the self-test after hash + * computation is more reliable. + * + * The performance cost of this quick self-test is around 0.6% at the "$2a$08" + * setting. + */ +char *crypt_blowfish_rn(const char *key, const char *setting, + char *output, int size) +{ + const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8"; + const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu"; + static const char * const test_hash[2] = + {"VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55", /* $2x$ */ + "i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55"}; /* $2a$, $2y$ */ + char *retval; + const char *p; + int ok; + struct { + char s[7 + 22 + 1]; + char o[7 + 22 + 31 + 1 + 1 + 1]; + } buf; + +/* Hash the supplied password */ + _crypt_output_magic(setting, output, size); + retval = BF_crypt(key, setting, output, size, 16); + +/* + * Do a quick self-test. It is important that we make both calls to BF_crypt() + * from the same scope such that they likely use the same stack locations, + * which makes the second call overwrite the first call's sensitive data on the + * stack and makes it more likely that any alignment related issues would be + * detected by the self-test. + */ + CopyMem(buf.s, (void *)test_setting, sizeof(buf.s)); + if (retval) + buf.s[2] = setting[2]; + SetMem(buf.o, sizeof(buf.o), 0x55); + buf.o[sizeof(buf.o) - 1] = 0; + p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1); + + ok = (p == buf.o && + !CompareMem((void *)p, (void *)buf.s, 7 + 22) && + !CompareMem((void *)(p + (7 + 22)), + (void *)test_hash[(unsigned int)(unsigned char)buf.s[2] & 1], + 31 + 1 + 1 + 1)); + + { + const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345"; + BF_key ae, ai, ye, yi; + BF_set_key(k, ae, ai, 2); /* $2a$ */ + BF_set_key(k, ye, yi, 4); /* $2y$ */ + ai[0] ^= 0x10000; /* undo the safety (for comparison) */ + ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 && + !CompareMem(ae, ye, sizeof(ae)) && + !CompareMem(ai, yi, sizeof(ai)); + } + + if (ok) + return retval; + +/* Should not happen */ + _crypt_output_magic(setting, output, size); + return NULL; +} diff --git a/crypt_blowfish.h b/crypt_blowfish.h new file mode 100644 index 0000000..dc3bd56 --- /dev/null +++ b/crypt_blowfish.h @@ -0,0 +1,22 @@ +/* + * Written by Solar Designer in 2000-2011. + * No copyright is claimed, and the software is hereby placed in the public + * domain. In case this attempt to disclaim copyright and place the software + * in the public domain is deemed null and void, then the software is + * Copyright (c) 2000-2011 Solar Designer and it is hereby released to the + * general public under the following terms: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted. + * + * There's ABSOLUTELY NO WARRANTY, express or implied. + * + * See crypt_blowfish.c for more information. + */ + +#ifndef _CRYPT_BLOWFISH_H +#define _CRYPT_BLOWFISH_H + +char *crypt_blowfish_rn(const char *key, const char *setting, + char *output, int size); +#endif diff --git a/dbx.S b/dbx.S deleted file mode 100644 index 7b19c5c..0000000 --- a/dbx.S +++ /dev/null @@ -1,36 +0,0 @@ -#if defined(VENDOR_DBX_FILE) - .globl vendor_dbx_size - .data - .align 1 - .type vendor_dbx_size, @object - .size vendor_dbx_size, 4 - .section .vendor_cert, "a", @progbits -vendor_dbx_size: - .long .L0 - vendor_dbx - .globl vendor_dbx - .data - .align 1 - .type vendor_dbx, @object - .size vendor_dbx, .L0-vendor_dbx - .section .vendor_cert, "a", @progbits -vendor_dbx: -.incbin VENDOR_DBX_FILE -.L0: -#else - .globl vendor_dbx - .bss - .type vendor_dbx, @object - .size vendor_dbx, 1 - .section .vendor_cert, "a", @progbits -vendor_dbx: - .zero 1 - - .globl vendor_dbx_size - .data - .align 4 - .type vendor_dbx_size, @object - .size vendor_dbx_size, 4 - .section .vendor_cert, "a", @progbits -vendor_dbx_size: - .long 0 -#endif diff --git a/PeImage.h b/include/PeImage.h similarity index 97% rename from PeImage.h rename to include/PeImage.h index 6f8e25f..ec13404 100644 --- a/PeImage.h +++ b/include/PeImage.h @@ -1,792 +1,787 @@ -/** @file - EFI image format for PE32, PE32+ and TE. Please note some data structures are - different for PE32 and PE32+. EFI_IMAGE_NT_HEADERS32 is for PE32 and - EFI_IMAGE_NT_HEADERS64 is for PE32+. - - This file is coded to the Visual Studio, Microsoft Portable Executable and - Common Object File Format Specification, Revision 8.0 - May 16, 2006. - This file also includes some definitions in PI Specification, Revision 1.0. - -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
-Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php. - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -**/ - -#ifndef __PE_IMAGE_H__ -#define __PE_IMAGE_H__ - -#define SIGNATURE_16(A, B) ((A) | (B << 8)) -#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) -#define SIGNATURE_64(A, B, C, D, E, F, G, H) \ - (SIGNATURE_32 (A, B, C, D) | ((UINT64) (SIGNATURE_32 (E, F, G, H)) << 32)) - -#define ALIGN_VALUE(Value, Alignment) ((Value) + (((Alignment) - (Value)) & ((Alignment) - 1))) -#define ALIGN_POINTER(Pointer, Alignment) ((VOID *) (ALIGN_VALUE ((UINTN)(Pointer), (Alignment)))) - -// -// PE32+ Subsystem type for EFI images -// -#define EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION 10 -#define EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 -#define EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 -#define EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER 13 ///< defined PI Specification, 1.0 - - -// -// PE32+ Machine type for EFI images -// -#define IMAGE_FILE_MACHINE_I386 0x014c -#define IMAGE_FILE_MACHINE_IA64 0x0200 -#define IMAGE_FILE_MACHINE_EBC 0x0EBC -#define IMAGE_FILE_MACHINE_X64 0x8664 -#define IMAGE_FILE_MACHINE_ARMTHUMB_MIXED 0x01c2 - -// -// EXE file formats -// -#define EFI_IMAGE_DOS_SIGNATURE SIGNATURE_16('M', 'Z') -#define EFI_IMAGE_OS2_SIGNATURE SIGNATURE_16('N', 'E') -#define EFI_IMAGE_OS2_SIGNATURE_LE SIGNATURE_16('L', 'E') -#define EFI_IMAGE_NT_SIGNATURE SIGNATURE_32('P', 'E', '\0', '\0') - -/// -/// PE images can start with an optional DOS header, so if an image is run -/// under DOS it can print an error message. -/// -typedef struct { - UINT16 e_magic; ///< Magic number. - UINT16 e_cblp; ///< Bytes on last page of file. - UINT16 e_cp; ///< Pages in file. - UINT16 e_crlc; ///< Relocations. - UINT16 e_cparhdr; ///< Size of header in paragraphs. - UINT16 e_minalloc; ///< Minimum extra paragraphs needed. - UINT16 e_maxalloc; ///< Maximum extra paragraphs needed. - UINT16 e_ss; ///< Initial (relative) SS value. - UINT16 e_sp; ///< Initial SP value. - UINT16 e_csum; ///< Checksum. - UINT16 e_ip; ///< Initial IP value. - UINT16 e_cs; ///< Initial (relative) CS value. - UINT16 e_lfarlc; ///< File address of relocation table. - UINT16 e_ovno; ///< Overlay number. - UINT16 e_res[4]; ///< Reserved words. - UINT16 e_oemid; ///< OEM identifier (for e_oeminfo). - UINT16 e_oeminfo; ///< OEM information; e_oemid specific. - UINT16 e_res2[10]; ///< Reserved words. - UINT32 e_lfanew; ///< File address of new exe header. -} EFI_IMAGE_DOS_HEADER; - -/// -/// COFF File Header (Object and Image). -/// -typedef struct { - UINT16 Machine; - UINT16 NumberOfSections; - UINT32 TimeDateStamp; - UINT32 PointerToSymbolTable; - UINT32 NumberOfSymbols; - UINT16 SizeOfOptionalHeader; - UINT16 Characteristics; -} EFI_IMAGE_FILE_HEADER; - -/// -/// Size of EFI_IMAGE_FILE_HEADER. -/// -#define EFI_IMAGE_SIZEOF_FILE_HEADER 20 - -// -// Characteristics -// -#define EFI_IMAGE_FILE_RELOCS_STRIPPED (1 << 0) ///< 0x0001 Relocation info stripped from file. -#define EFI_IMAGE_FILE_EXECUTABLE_IMAGE (1 << 1) ///< 0x0002 File is executable (i.e. no unresolved externel references). -#define EFI_IMAGE_FILE_LINE_NUMS_STRIPPED (1 << 2) ///< 0x0004 Line nunbers stripped from file. -#define EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED (1 << 3) ///< 0x0008 Local symbols stripped from file. -#define EFI_IMAGE_FILE_BYTES_REVERSED_LO (1 << 7) ///< 0x0080 Bytes of machine word are reversed. -#define EFI_IMAGE_FILE_32BIT_MACHINE (1 << 8) ///< 0x0100 32 bit word machine. -#define EFI_IMAGE_FILE_DEBUG_STRIPPED (1 << 9) ///< 0x0200 Debugging info stripped from file in .DBG file. -#define EFI_IMAGE_FILE_SYSTEM (1 << 12) ///< 0x1000 System File. -#define EFI_IMAGE_FILE_DLL (1 << 13) ///< 0x2000 File is a DLL. -#define EFI_IMAGE_FILE_BYTES_REVERSED_HI (1 << 15) ///< 0x8000 Bytes of machine word are reversed. - -/// -/// Header Data Directories. -/// -typedef struct { - UINT32 VirtualAddress; - UINT32 Size; -} EFI_IMAGE_DATA_DIRECTORY; - -// -// Directory Entries -// -#define EFI_IMAGE_DIRECTORY_ENTRY_EXPORT 0 -#define EFI_IMAGE_DIRECTORY_ENTRY_IMPORT 1 -#define EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE 2 -#define EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 -#define EFI_IMAGE_DIRECTORY_ENTRY_SECURITY 4 -#define EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC 5 -#define EFI_IMAGE_DIRECTORY_ENTRY_DEBUG 6 -#define EFI_IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 -#define EFI_IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 -#define EFI_IMAGE_DIRECTORY_ENTRY_TLS 9 -#define EFI_IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 - -#define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16 - -/// -/// @attention -/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and -/// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary -/// after NT additional fields. -/// -#define EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b - -/// -/// Optional Header Standard Fields for PE32. -/// -typedef struct { - /// - /// Standard fields. - /// - UINT16 Magic; - UINT8 MajorLinkerVersion; - UINT8 MinorLinkerVersion; - UINT32 SizeOfCode; - UINT32 SizeOfInitializedData; - UINT32 SizeOfUninitializedData; - UINT32 AddressOfEntryPoint; - UINT32 BaseOfCode; - UINT32 BaseOfData; ///< PE32 contains this additional field, which is absent in PE32+. - /// - /// Optional Header Windows-Specific Fields. - /// - UINT32 ImageBase; - UINT32 SectionAlignment; - UINT32 FileAlignment; - UINT16 MajorOperatingSystemVersion; - UINT16 MinorOperatingSystemVersion; - UINT16 MajorImageVersion; - UINT16 MinorImageVersion; - UINT16 MajorSubsystemVersion; - UINT16 MinorSubsystemVersion; - UINT32 Win32VersionValue; - UINT32 SizeOfImage; - UINT32 SizeOfHeaders; - UINT32 CheckSum; - UINT16 Subsystem; - UINT16 DllCharacteristics; - UINT32 SizeOfStackReserve; - UINT32 SizeOfStackCommit; - UINT32 SizeOfHeapReserve; - UINT32 SizeOfHeapCommit; - UINT32 LoaderFlags; - UINT32 NumberOfRvaAndSizes; - EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; -} EFI_IMAGE_OPTIONAL_HEADER32; - -/// -/// @attention -/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and -/// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary -/// after NT additional fields. -/// -#define EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b - -/// -/// Optional Header Standard Fields for PE32+. -/// -typedef struct { - /// - /// Standard fields. - /// - UINT16 Magic; - UINT8 MajorLinkerVersion; - UINT8 MinorLinkerVersion; - UINT32 SizeOfCode; - UINT32 SizeOfInitializedData; - UINT32 SizeOfUninitializedData; - UINT32 AddressOfEntryPoint; - UINT32 BaseOfCode; - /// - /// Optional Header Windows-Specific Fields. - /// - UINT64 ImageBase; - UINT32 SectionAlignment; - UINT32 FileAlignment; - UINT16 MajorOperatingSystemVersion; - UINT16 MinorOperatingSystemVersion; - UINT16 MajorImageVersion; - UINT16 MinorImageVersion; - UINT16 MajorSubsystemVersion; - UINT16 MinorSubsystemVersion; - UINT32 Win32VersionValue; - UINT32 SizeOfImage; - UINT32 SizeOfHeaders; - UINT32 CheckSum; - UINT16 Subsystem; - UINT16 DllCharacteristics; - UINT64 SizeOfStackReserve; - UINT64 SizeOfStackCommit; - UINT64 SizeOfHeapReserve; - UINT64 SizeOfHeapCommit; - UINT32 LoaderFlags; - UINT32 NumberOfRvaAndSizes; - EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; -} EFI_IMAGE_OPTIONAL_HEADER64; - - -/// -/// @attention -/// EFI_IMAGE_NT_HEADERS32 is for use ONLY by tools. -/// -typedef struct { - UINT32 Signature; - EFI_IMAGE_FILE_HEADER FileHeader; - EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader; -} EFI_IMAGE_NT_HEADERS32; - -#define EFI_IMAGE_SIZEOF_NT_OPTIONAL32_HEADER sizeof (EFI_IMAGE_NT_HEADERS32) - -/// -/// @attention -/// EFI_IMAGE_HEADERS64 is for use ONLY by tools. -/// -typedef struct { - UINT32 Signature; - EFI_IMAGE_FILE_HEADER FileHeader; - EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader; -} EFI_IMAGE_NT_HEADERS64; - -#define EFI_IMAGE_SIZEOF_NT_OPTIONAL64_HEADER sizeof (EFI_IMAGE_NT_HEADERS64) - -// -// Other Windows Subsystem Values -// -#define EFI_IMAGE_SUBSYSTEM_UNKNOWN 0 -#define EFI_IMAGE_SUBSYSTEM_NATIVE 1 -#define EFI_IMAGE_SUBSYSTEM_WINDOWS_GUI 2 -#define EFI_IMAGE_SUBSYSTEM_WINDOWS_CUI 3 -#define EFI_IMAGE_SUBSYSTEM_OS2_CUI 5 -#define EFI_IMAGE_SUBSYSTEM_POSIX_CUI 7 - -/// -/// Length of ShortName. -/// -#define EFI_IMAGE_SIZEOF_SHORT_NAME 8 - -/// -/// Section Table. This table immediately follows the optional header. -/// -typedef struct { - UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME]; - union { - UINT32 PhysicalAddress; - UINT32 VirtualSize; - } Misc; - UINT32 VirtualAddress; - UINT32 SizeOfRawData; - UINT32 PointerToRawData; - UINT32 PointerToRelocations; - UINT32 PointerToLinenumbers; - UINT16 NumberOfRelocations; - UINT16 NumberOfLinenumbers; - UINT32 Characteristics; -} EFI_IMAGE_SECTION_HEADER; - -/// -/// Size of EFI_IMAGE_SECTION_HEADER. -/// -#define EFI_IMAGE_SIZEOF_SECTION_HEADER 40 - -// -// Section Flags Values -// -#define EFI_IMAGE_SCN_TYPE_NO_PAD BIT3 ///< 0x00000008 ///< Reserved. -#define EFI_IMAGE_SCN_CNT_CODE BIT5 ///< 0x00000020 -#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA BIT6 ///< 0x00000040 -#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA BIT7 ///< 0x00000080 - -#define EFI_IMAGE_SCN_LNK_OTHER BIT8 ///< 0x00000100 ///< Reserved. -#define EFI_IMAGE_SCN_LNK_INFO BIT9 ///< 0x00000200 ///< Section contains comments or some other type of information. -#define EFI_IMAGE_SCN_LNK_REMOVE BIT11 ///< 0x00000800 ///< Section contents will not become part of image. -#define EFI_IMAGE_SCN_LNK_COMDAT BIT12 ///< 0x00001000 - -#define EFI_IMAGE_SCN_ALIGN_1BYTES BIT20 ///< 0x00100000 -#define EFI_IMAGE_SCN_ALIGN_2BYTES BIT21 ///< 0x00200000 -#define EFI_IMAGE_SCN_ALIGN_4BYTES (BIT20|BIT21) ///< 0x00300000 -#define EFI_IMAGE_SCN_ALIGN_8BYTES BIT22 ///< 0x00400000 -#define EFI_IMAGE_SCN_ALIGN_16BYTES (BIT20|BIT22) ///< 0x00500000 -#define EFI_IMAGE_SCN_ALIGN_32BYTES (BIT21|BIT22) ///< 0x00600000 -#define EFI_IMAGE_SCN_ALIGN_64BYTES (BIT20|BIT21|BIT22) ///< 0x00700000 - -#define EFI_IMAGE_SCN_MEM_DISCARDABLE BIT25 ///< 0x02000000 -#define EFI_IMAGE_SCN_MEM_NOT_CACHED BIT26 ///< 0x04000000 -#define EFI_IMAGE_SCN_MEM_NOT_PAGED BIT27 ///< 0x08000000 -#define EFI_IMAGE_SCN_MEM_SHARED BIT28 ///< 0x10000000 -#define EFI_IMAGE_SCN_MEM_EXECUTE BIT29 ///< 0x20000000 -#define EFI_IMAGE_SCN_MEM_READ BIT30 ///< 0x40000000 -#define EFI_IMAGE_SCN_MEM_WRITE BIT31 ///< 0x80000000 - -/// -/// Size of a Symbol Table Record. -/// -#define EFI_IMAGE_SIZEOF_SYMBOL 18 - -// -// Symbols have a section number of the section in which they are -// defined. Otherwise, section numbers have the following meanings: -// -#define EFI_IMAGE_SYM_UNDEFINED (UINT16) 0 ///< Symbol is undefined or is common. -#define EFI_IMAGE_SYM_ABSOLUTE (UINT16) -1 ///< Symbol is an absolute value. -#define EFI_IMAGE_SYM_DEBUG (UINT16) -2 ///< Symbol is a special debug item. - -// -// Symbol Type (fundamental) values. -// -#define EFI_IMAGE_SYM_TYPE_NULL 0 ///< no type. -#define EFI_IMAGE_SYM_TYPE_VOID 1 ///< no valid type. -#define EFI_IMAGE_SYM_TYPE_CHAR 2 ///< type character. -#define EFI_IMAGE_SYM_TYPE_SHORT 3 ///< type short integer. -#define EFI_IMAGE_SYM_TYPE_INT 4 -#define EFI_IMAGE_SYM_TYPE_LONG 5 -#define EFI_IMAGE_SYM_TYPE_FLOAT 6 -#define EFI_IMAGE_SYM_TYPE_DOUBLE 7 -#define EFI_IMAGE_SYM_TYPE_STRUCT 8 -#define EFI_IMAGE_SYM_TYPE_UNION 9 -#define EFI_IMAGE_SYM_TYPE_ENUM 10 ///< enumeration. -#define EFI_IMAGE_SYM_TYPE_MOE 11 ///< member of enumeration. -#define EFI_IMAGE_SYM_TYPE_BYTE 12 -#define EFI_IMAGE_SYM_TYPE_WORD 13 -#define EFI_IMAGE_SYM_TYPE_UINT 14 -#define EFI_IMAGE_SYM_TYPE_DWORD 15 - -// -// Symbol Type (derived) values. -// -#define EFI_IMAGE_SYM_DTYPE_NULL 0 ///< no derived type. -#define EFI_IMAGE_SYM_DTYPE_POINTER 1 -#define EFI_IMAGE_SYM_DTYPE_FUNCTION 2 -#define EFI_IMAGE_SYM_DTYPE_ARRAY 3 - -// -// Storage classes. -// -#define EFI_IMAGE_SYM_CLASS_END_OF_FUNCTION ((UINT8) -1) -#define EFI_IMAGE_SYM_CLASS_NULL 0 -#define EFI_IMAGE_SYM_CLASS_AUTOMATIC 1 -#define EFI_IMAGE_SYM_CLASS_EXTERNAL 2 -#define EFI_IMAGE_SYM_CLASS_STATIC 3 -#define EFI_IMAGE_SYM_CLASS_REGISTER 4 -#define EFI_IMAGE_SYM_CLASS_EXTERNAL_DEF 5 -#define EFI_IMAGE_SYM_CLASS_LABEL 6 -#define EFI_IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 -#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 -#define EFI_IMAGE_SYM_CLASS_ARGUMENT 9 -#define EFI_IMAGE_SYM_CLASS_STRUCT_TAG 10 -#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 -#define EFI_IMAGE_SYM_CLASS_UNION_TAG 12 -#define EFI_IMAGE_SYM_CLASS_TYPE_DEFINITION 13 -#define EFI_IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 -#define EFI_IMAGE_SYM_CLASS_ENUM_TAG 15 -#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 -#define EFI_IMAGE_SYM_CLASS_REGISTER_PARAM 17 -#define EFI_IMAGE_SYM_CLASS_BIT_FIELD 18 -#define EFI_IMAGE_SYM_CLASS_BLOCK 100 -#define EFI_IMAGE_SYM_CLASS_FUNCTION 101 -#define EFI_IMAGE_SYM_CLASS_END_OF_STRUCT 102 -#define EFI_IMAGE_SYM_CLASS_FILE 103 -#define EFI_IMAGE_SYM_CLASS_SECTION 104 -#define EFI_IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 - -// -// type packing constants -// -#define EFI_IMAGE_N_BTMASK 017 -#define EFI_IMAGE_N_TMASK 060 -#define EFI_IMAGE_N_TMASK1 0300 -#define EFI_IMAGE_N_TMASK2 0360 -#define EFI_IMAGE_N_BTSHFT 4 -#define EFI_IMAGE_N_TSHIFT 2 - -// -// Communal selection types. -// -#define EFI_IMAGE_COMDAT_SELECT_NODUPLICATES 1 -#define EFI_IMAGE_COMDAT_SELECT_ANY 2 -#define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE 3 -#define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH 4 -#define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 - -// -// the following values only be referred in PeCoff, not defined in PECOFF. -// -#define EFI_IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1 -#define EFI_IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2 -#define EFI_IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3 - -/// -/// Relocation format. -/// -typedef struct { - UINT32 VirtualAddress; - UINT32 SymbolTableIndex; - UINT16 Type; -} EFI_IMAGE_RELOCATION; - -/// -/// Size of EFI_IMAGE_RELOCATION -/// -#define EFI_IMAGE_SIZEOF_RELOCATION 10 - -// -// I386 relocation types. -// -#define EFI_IMAGE_REL_I386_ABSOLUTE 0x0000 ///< Reference is absolute, no relocation is necessary. -#define EFI_IMAGE_REL_I386_DIR16 0x0001 ///< Direct 16-bit reference to the symbols virtual address. -#define EFI_IMAGE_REL_I386_REL16 0x0002 ///< PC-relative 16-bit reference to the symbols virtual address. -#define EFI_IMAGE_REL_I386_DIR32 0x0006 ///< Direct 32-bit reference to the symbols virtual address. -#define EFI_IMAGE_REL_I386_DIR32NB 0x0007 ///< Direct 32-bit reference to the symbols virtual address, base not included. -#define EFI_IMAGE_REL_I386_SEG12 0x0009 ///< Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address. -#define EFI_IMAGE_REL_I386_SECTION 0x000A -#define EFI_IMAGE_REL_I386_SECREL 0x000B -#define EFI_IMAGE_REL_I386_REL32 0x0014 ///< PC-relative 32-bit reference to the symbols virtual address. - -// -// x64 processor relocation types. -// -#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 -#define IMAGE_REL_AMD64_ADDR64 0x0001 -#define IMAGE_REL_AMD64_ADDR32 0x0002 -#define IMAGE_REL_AMD64_ADDR32NB 0x0003 -#define IMAGE_REL_AMD64_REL32 0x0004 -#define IMAGE_REL_AMD64_REL32_1 0x0005 -#define IMAGE_REL_AMD64_REL32_2 0x0006 -#define IMAGE_REL_AMD64_REL32_3 0x0007 -#define IMAGE_REL_AMD64_REL32_4 0x0008 -#define IMAGE_REL_AMD64_REL32_5 0x0009 -#define IMAGE_REL_AMD64_SECTION 0x000A -#define IMAGE_REL_AMD64_SECREL 0x000B -#define IMAGE_REL_AMD64_SECREL7 0x000C -#define IMAGE_REL_AMD64_TOKEN 0x000D -#define IMAGE_REL_AMD64_SREL32 0x000E -#define IMAGE_REL_AMD64_PAIR 0x000F -#define IMAGE_REL_AMD64_SSPAN32 0x0010 - -/// -/// Based relocation format. -/// -typedef struct { - UINT32 VirtualAddress; - UINT32 SizeOfBlock; -} EFI_IMAGE_BASE_RELOCATION; - -/// -/// Size of EFI_IMAGE_BASE_RELOCATION. -/// -#define EFI_IMAGE_SIZEOF_BASE_RELOCATION 8 - -// -// Based relocation types. -// -#define EFI_IMAGE_REL_BASED_ABSOLUTE 0 -#define EFI_IMAGE_REL_BASED_HIGH 1 -#define EFI_IMAGE_REL_BASED_LOW 2 -#define EFI_IMAGE_REL_BASED_HIGHLOW 3 -#define EFI_IMAGE_REL_BASED_HIGHADJ 4 -#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR 5 -#define EFI_IMAGE_REL_BASED_ARM_MOV32A 5 -#define EFI_IMAGE_REL_BASED_ARM_MOV32T 7 -#define EFI_IMAGE_REL_BASED_IA64_IMM64 9 -#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR16 9 -#define EFI_IMAGE_REL_BASED_DIR64 10 - -/// -/// Line number format. -/// -typedef struct { - union { - UINT32 SymbolTableIndex; ///< Symbol table index of function name if Linenumber is 0. - UINT32 VirtualAddress; ///< Virtual address of line number. - } Type; - UINT16 Linenumber; ///< Line number. -} EFI_IMAGE_LINENUMBER; - -/// -/// Size of EFI_IMAGE_LINENUMBER. -/// -#define EFI_IMAGE_SIZEOF_LINENUMBER 6 - -// -// Archive format. -// -#define EFI_IMAGE_ARCHIVE_START_SIZE 8 -#define EFI_IMAGE_ARCHIVE_START "!\n" -#define EFI_IMAGE_ARCHIVE_END "`\n" -#define EFI_IMAGE_ARCHIVE_PAD "\n" -#define EFI_IMAGE_ARCHIVE_LINKER_MEMBER "/ " -#define EFI_IMAGE_ARCHIVE_LONGNAMES_MEMBER "// " - -/// -/// Archive Member Headers -/// -typedef struct { - UINT8 Name[16]; ///< File member name - `/' terminated. - UINT8 Date[12]; ///< File member date - decimal. - UINT8 UserID[6]; ///< File member user id - decimal. - UINT8 GroupID[6]; ///< File member group id - decimal. - UINT8 Mode[8]; ///< File member mode - octal. - UINT8 Size[10]; ///< File member size - decimal. - UINT8 EndHeader[2]; ///< String to end header. (0x60 0x0A). -} EFI_IMAGE_ARCHIVE_MEMBER_HEADER; - -/// -/// Size of EFI_IMAGE_ARCHIVE_MEMBER_HEADER. -/// -#define EFI_IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 - - -// -// DLL Support -// - -/// -/// Export Directory Table. -/// -typedef struct { - UINT32 Characteristics; - UINT32 TimeDateStamp; - UINT16 MajorVersion; - UINT16 MinorVersion; - UINT32 Name; - UINT32 Base; - UINT32 NumberOfFunctions; - UINT32 NumberOfNames; - UINT32 AddressOfFunctions; - UINT32 AddressOfNames; - UINT32 AddressOfNameOrdinals; -} EFI_IMAGE_EXPORT_DIRECTORY; - -/// -/// Hint/Name Table. -/// -typedef struct { - UINT16 Hint; - UINT8 Name[1]; -} EFI_IMAGE_IMPORT_BY_NAME; - -/// -/// Import Address Table RVA (Thunk Table). -/// -typedef struct { - union { - UINT32 Function; - UINT32 Ordinal; - EFI_IMAGE_IMPORT_BY_NAME *AddressOfData; - } u1; -} EFI_IMAGE_THUNK_DATA; - -#define EFI_IMAGE_ORDINAL_FLAG BIT31 ///< Flag for PE32. -#define EFI_IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & EFI_IMAGE_ORDINAL_FLAG) != 0) -#define EFI_IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) - -/// -/// Import Directory Table -/// -typedef struct { - UINT32 Characteristics; - UINT32 TimeDateStamp; - UINT32 ForwarderChain; - UINT32 Name; - EFI_IMAGE_THUNK_DATA *FirstThunk; -} EFI_IMAGE_IMPORT_DESCRIPTOR; - - -/// -/// Debug Directory Format. -/// -typedef struct { - UINT32 Characteristics; - UINT32 TimeDateStamp; - UINT16 MajorVersion; - UINT16 MinorVersion; - UINT32 Type; - UINT32 SizeOfData; - UINT32 RVA; ///< The address of the debug data when loaded, relative to the image base. - UINT32 FileOffset; ///< The file pointer to the debug data. -} EFI_IMAGE_DEBUG_DIRECTORY_ENTRY; - -#define EFI_IMAGE_DEBUG_TYPE_CODEVIEW 2 ///< The Visual C++ debug information. - -/// -/// Debug Data Structure defined in Microsoft C++. -/// -#define CODEVIEW_SIGNATURE_NB10 SIGNATURE_32('N', 'B', '1', '0') -typedef struct { - UINT32 Signature; ///< "NB10" - UINT32 Unknown; - UINT32 Unknown2; - UINT32 Unknown3; - // - // Filename of .PDB goes here - // -} EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY; - -/// -/// Debug Data Structure defined in Microsoft C++. -/// -#define CODEVIEW_SIGNATURE_RSDS SIGNATURE_32('R', 'S', 'D', 'S') -typedef struct { - UINT32 Signature; ///< "RSDS". - UINT32 Unknown; - UINT32 Unknown2; - UINT32 Unknown3; - UINT32 Unknown4; - UINT32 Unknown5; - // - // Filename of .PDB goes here - // -} EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY; - - -/// -/// Debug Data Structure defined by Apple Mach-O to Coff utility. -/// -#define CODEVIEW_SIGNATURE_MTOC SIGNATURE_32('M', 'T', 'O', 'C') -typedef struct { - UINT32 Signature; ///< "MTOC". - EFI_GUID MachOUuid; - // - // Filename of .DLL (Mach-O with debug info) goes here - // -} EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY; - -/// -/// Resource format. -/// -typedef struct { - UINT32 Characteristics; - UINT32 TimeDateStamp; - UINT16 MajorVersion; - UINT16 MinorVersion; - UINT16 NumberOfNamedEntries; - UINT16 NumberOfIdEntries; - // - // Array of EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY entries goes here. - // -} EFI_IMAGE_RESOURCE_DIRECTORY; - -/// -/// Resource directory entry format. -/// -typedef struct { - union { - struct { - UINT32 NameOffset:31; - UINT32 NameIsString:1; - } s; - UINT32 Id; - } u1; - union { - UINT32 OffsetToData; - struct { - UINT32 OffsetToDirectory:31; - UINT32 DataIsDirectory:1; - } s; - } u2; -} EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY; - -/// -/// Resource directory entry for string. -/// -typedef struct { - UINT16 Length; - CHAR16 String[1]; -} EFI_IMAGE_RESOURCE_DIRECTORY_STRING; - -/// -/// Resource directory entry for data array. -/// -typedef struct { - UINT32 OffsetToData; - UINT32 Size; - UINT32 CodePage; - UINT32 Reserved; -} EFI_IMAGE_RESOURCE_DATA_ENTRY; - -/// -/// Header format for TE images, defined in the PI Specification, 1.0. -/// -typedef struct { - UINT16 Signature; ///< The signature for TE format = "VZ". - UINT16 Machine; ///< From the original file header. - UINT8 NumberOfSections; ///< From the original file header. - UINT8 Subsystem; ///< From original optional header. - UINT16 StrippedSize; ///< Number of bytes we removed from the header. - UINT32 AddressOfEntryPoint; ///< Offset to entry point -- from original optional header. - UINT32 BaseOfCode; ///< From original image -- required for ITP debug. - UINT64 ImageBase; ///< From original file header. - EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; ///< Only base relocation and debug directory. -} EFI_TE_IMAGE_HEADER; - - -#define EFI_TE_IMAGE_HEADER_SIGNATURE SIGNATURE_16('V', 'Z') - -// -// Data directory indexes in our TE image header -// -#define EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC 0 -#define EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG 1 - - -/// -/// Union of PE32, PE32+, and TE headers. -/// -typedef union { - EFI_IMAGE_NT_HEADERS32 Pe32; - EFI_IMAGE_NT_HEADERS64 Pe32Plus; - EFI_TE_IMAGE_HEADER Te; -} EFI_IMAGE_OPTIONAL_HEADER_UNION; - -typedef union { - EFI_IMAGE_NT_HEADERS32 *Pe32; - EFI_IMAGE_NT_HEADERS64 *Pe32Plus; - EFI_TE_IMAGE_HEADER *Te; - EFI_IMAGE_OPTIONAL_HEADER_UNION *Union; -} EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION; - -typedef struct _WIN_CERTIFICATE { - UINT32 dwLength; - UINT16 wRevision; - UINT16 wCertificateType; - //UINT8 bCertificate[ANYSIZE_ARRAY]; -} WIN_CERTIFICATE; - -typedef struct { - WIN_CERTIFICATE Hdr; - UINT8 CertData[1]; -} WIN_CERTIFICATE_EFI_PKCS; - -#define SHA256_DIGEST_SIZE 32 -#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 - -typedef struct { - UINT64 ImageAddress; - UINT64 ImageSize; - UINT64 EntryPoint; - UINTN SizeOfHeaders; - UINT16 ImageType; - UINT16 NumberOfSections; - EFI_IMAGE_SECTION_HEADER *FirstSection; - EFI_IMAGE_DATA_DIRECTORY *RelocDir; - EFI_IMAGE_DATA_DIRECTORY *SecDir; - UINT64 NumberOfRvaAndSizes; - EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr; -} PE_COFF_LOADER_IMAGE_CONTEXT; - -#endif +/** @file + EFI image format for PE32, PE32+ and TE. Please note some data structures are + different for PE32 and PE32+. EFI_IMAGE_NT_HEADERS32 is for PE32 and + EFI_IMAGE_NT_HEADERS64 is for PE32+. + + This file is coded to the Visual Studio, Microsoft Portable Executable and + Common Object File Format Specification, Revision 8.0 - May 16, 2006. + This file also includes some definitions in PI Specification, Revision 1.0. + +Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __PE_IMAGE_H__ +#define __PE_IMAGE_H__ + +#include + +#define SIGNATURE_16(A, B) ((A) | (B << 8)) +#define SIGNATURE_32(A, B, C, D) (SIGNATURE_16 (A, B) | (SIGNATURE_16 (C, D) << 16)) +#define SIGNATURE_64(A, B, C, D, E, F, G, H) \ + (SIGNATURE_32 (A, B, C, D) | ((UINT64) (SIGNATURE_32 (E, F, G, H)) << 32)) + +#define ALIGN_VALUE(Value, Alignment) ((Value) + (((Alignment) - (Value)) & ((Alignment) - 1))) +#define ALIGN_POINTER(Pointer, Alignment) ((VOID *) (ALIGN_VALUE ((UINTN)(Pointer), (Alignment)))) + +// +// PE32+ Subsystem type for EFI images +// +#define EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 +#define EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER 13 ///< defined PI Specification, 1.0 + + +// +// PE32+ Machine type for EFI images +// +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_IA64 0x0200 +#define IMAGE_FILE_MACHINE_EBC 0x0EBC +#define IMAGE_FILE_MACHINE_X64 0x8664 +#define IMAGE_FILE_MACHINE_ARMTHUMB_MIXED 0x01c2 + +// +// EXE file formats +// +#define EFI_IMAGE_DOS_SIGNATURE SIGNATURE_16('M', 'Z') +#define EFI_IMAGE_OS2_SIGNATURE SIGNATURE_16('N', 'E') +#define EFI_IMAGE_OS2_SIGNATURE_LE SIGNATURE_16('L', 'E') +#define EFI_IMAGE_NT_SIGNATURE SIGNATURE_32('P', 'E', '\0', '\0') + +/// +/// PE images can start with an optional DOS header, so if an image is run +/// under DOS it can print an error message. +/// +typedef struct { + UINT16 e_magic; ///< Magic number. + UINT16 e_cblp; ///< Bytes on last page of file. + UINT16 e_cp; ///< Pages in file. + UINT16 e_crlc; ///< Relocations. + UINT16 e_cparhdr; ///< Size of header in paragraphs. + UINT16 e_minalloc; ///< Minimum extra paragraphs needed. + UINT16 e_maxalloc; ///< Maximum extra paragraphs needed. + UINT16 e_ss; ///< Initial (relative) SS value. + UINT16 e_sp; ///< Initial SP value. + UINT16 e_csum; ///< Checksum. + UINT16 e_ip; ///< Initial IP value. + UINT16 e_cs; ///< Initial (relative) CS value. + UINT16 e_lfarlc; ///< File address of relocation table. + UINT16 e_ovno; ///< Overlay number. + UINT16 e_res[4]; ///< Reserved words. + UINT16 e_oemid; ///< OEM identifier (for e_oeminfo). + UINT16 e_oeminfo; ///< OEM information; e_oemid specific. + UINT16 e_res2[10]; ///< Reserved words. + UINT32 e_lfanew; ///< File address of new exe header. +} EFI_IMAGE_DOS_HEADER; + +/// +/// COFF File Header (Object and Image). +/// +typedef struct { + UINT16 Machine; + UINT16 NumberOfSections; + UINT32 TimeDateStamp; + UINT32 PointerToSymbolTable; + UINT32 NumberOfSymbols; + UINT16 SizeOfOptionalHeader; + UINT16 Characteristics; +} EFI_IMAGE_FILE_HEADER; + +/// +/// Size of EFI_IMAGE_FILE_HEADER. +/// +#define EFI_IMAGE_SIZEOF_FILE_HEADER 20 + +// +// Characteristics +// +#define EFI_IMAGE_FILE_RELOCS_STRIPPED (1 << 0) ///< 0x0001 Relocation info stripped from file. +#define EFI_IMAGE_FILE_EXECUTABLE_IMAGE (1 << 1) ///< 0x0002 File is executable (i.e. no unresolved externel references). +#define EFI_IMAGE_FILE_LINE_NUMS_STRIPPED (1 << 2) ///< 0x0004 Line nunbers stripped from file. +#define EFI_IMAGE_FILE_LOCAL_SYMS_STRIPPED (1 << 3) ///< 0x0008 Local symbols stripped from file. +#define EFI_IMAGE_FILE_BYTES_REVERSED_LO (1 << 7) ///< 0x0080 Bytes of machine word are reversed. +#define EFI_IMAGE_FILE_32BIT_MACHINE (1 << 8) ///< 0x0100 32 bit word machine. +#define EFI_IMAGE_FILE_DEBUG_STRIPPED (1 << 9) ///< 0x0200 Debugging info stripped from file in .DBG file. +#define EFI_IMAGE_FILE_SYSTEM (1 << 12) ///< 0x1000 System File. +#define EFI_IMAGE_FILE_DLL (1 << 13) ///< 0x2000 File is a DLL. +#define EFI_IMAGE_FILE_BYTES_REVERSED_HI (1 << 15) ///< 0x8000 Bytes of machine word are reversed. + +/// +/// Header Data Directories. +/// +typedef struct { + UINT32 VirtualAddress; + UINT32 Size; +} EFI_IMAGE_DATA_DIRECTORY; + +// +// Directory Entries +// +#define EFI_IMAGE_DIRECTORY_ENTRY_EXPORT 0 +#define EFI_IMAGE_DIRECTORY_ENTRY_IMPORT 1 +#define EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE 2 +#define EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 +#define EFI_IMAGE_DIRECTORY_ENTRY_SECURITY 4 +#define EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC 5 +#define EFI_IMAGE_DIRECTORY_ENTRY_DEBUG 6 +#define EFI_IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 +#define EFI_IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 +#define EFI_IMAGE_DIRECTORY_ENTRY_TLS 9 +#define EFI_IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 + +#define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16 + +/// +/// @attention +/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and +/// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary +/// after NT additional fields. +/// +#define EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b + +/// +/// Optional Header Standard Fields for PE32. +/// +typedef struct { + /// + /// Standard fields. + /// + UINT16 Magic; + UINT8 MajorLinkerVersion; + UINT8 MinorLinkerVersion; + UINT32 SizeOfCode; + UINT32 SizeOfInitializedData; + UINT32 SizeOfUninitializedData; + UINT32 AddressOfEntryPoint; + UINT32 BaseOfCode; + UINT32 BaseOfData; ///< PE32 contains this additional field, which is absent in PE32+. + /// + /// Optional Header Windows-Specific Fields. + /// + UINT32 ImageBase; + UINT32 SectionAlignment; + UINT32 FileAlignment; + UINT16 MajorOperatingSystemVersion; + UINT16 MinorOperatingSystemVersion; + UINT16 MajorImageVersion; + UINT16 MinorImageVersion; + UINT16 MajorSubsystemVersion; + UINT16 MinorSubsystemVersion; + UINT32 Win32VersionValue; + UINT32 SizeOfImage; + UINT32 SizeOfHeaders; + UINT32 CheckSum; + UINT16 Subsystem; + UINT16 DllCharacteristics; + UINT32 SizeOfStackReserve; + UINT32 SizeOfStackCommit; + UINT32 SizeOfHeapReserve; + UINT32 SizeOfHeapCommit; + UINT32 LoaderFlags; + UINT32 NumberOfRvaAndSizes; + EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; +} EFI_IMAGE_OPTIONAL_HEADER32; + +/// +/// @attention +/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and +/// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary +/// after NT additional fields. +/// +#define EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b + +/// +/// Optional Header Standard Fields for PE32+. +/// +typedef struct { + /// + /// Standard fields. + /// + UINT16 Magic; + UINT8 MajorLinkerVersion; + UINT8 MinorLinkerVersion; + UINT32 SizeOfCode; + UINT32 SizeOfInitializedData; + UINT32 SizeOfUninitializedData; + UINT32 AddressOfEntryPoint; + UINT32 BaseOfCode; + /// + /// Optional Header Windows-Specific Fields. + /// + UINT64 ImageBase; + UINT32 SectionAlignment; + UINT32 FileAlignment; + UINT16 MajorOperatingSystemVersion; + UINT16 MinorOperatingSystemVersion; + UINT16 MajorImageVersion; + UINT16 MinorImageVersion; + UINT16 MajorSubsystemVersion; + UINT16 MinorSubsystemVersion; + UINT32 Win32VersionValue; + UINT32 SizeOfImage; + UINT32 SizeOfHeaders; + UINT32 CheckSum; + UINT16 Subsystem; + UINT16 DllCharacteristics; + UINT64 SizeOfStackReserve; + UINT64 SizeOfStackCommit; + UINT64 SizeOfHeapReserve; + UINT64 SizeOfHeapCommit; + UINT32 LoaderFlags; + UINT32 NumberOfRvaAndSizes; + EFI_IMAGE_DATA_DIRECTORY DataDirectory[EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES]; +} EFI_IMAGE_OPTIONAL_HEADER64; + + +/// +/// @attention +/// EFI_IMAGE_NT_HEADERS32 is for use ONLY by tools. +/// +typedef struct { + UINT32 Signature; + EFI_IMAGE_FILE_HEADER FileHeader; + EFI_IMAGE_OPTIONAL_HEADER32 OptionalHeader; +} EFI_IMAGE_NT_HEADERS32; + +#define EFI_IMAGE_SIZEOF_NT_OPTIONAL32_HEADER sizeof (EFI_IMAGE_NT_HEADERS32) + +/// +/// @attention +/// EFI_IMAGE_HEADERS64 is for use ONLY by tools. +/// +typedef struct { + UINT32 Signature; + EFI_IMAGE_FILE_HEADER FileHeader; + EFI_IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} EFI_IMAGE_NT_HEADERS64; + +#define EFI_IMAGE_SIZEOF_NT_OPTIONAL64_HEADER sizeof (EFI_IMAGE_NT_HEADERS64) + +// +// Other Windows Subsystem Values +// +#define EFI_IMAGE_SUBSYSTEM_UNKNOWN 0 +#define EFI_IMAGE_SUBSYSTEM_NATIVE 1 +#define EFI_IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define EFI_IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define EFI_IMAGE_SUBSYSTEM_OS2_CUI 5 +#define EFI_IMAGE_SUBSYSTEM_POSIX_CUI 7 + +/// +/// Length of ShortName. +/// +#define EFI_IMAGE_SIZEOF_SHORT_NAME 8 + +/// +/// Section Table. This table immediately follows the optional header. +/// +typedef struct { + UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME]; + union { + UINT32 PhysicalAddress; + UINT32 VirtualSize; + } Misc; + UINT32 VirtualAddress; + UINT32 SizeOfRawData; + UINT32 PointerToRawData; + UINT32 PointerToRelocations; + UINT32 PointerToLinenumbers; + UINT16 NumberOfRelocations; + UINT16 NumberOfLinenumbers; + UINT32 Characteristics; +} EFI_IMAGE_SECTION_HEADER; + +/// +/// Size of EFI_IMAGE_SECTION_HEADER. +/// +#define EFI_IMAGE_SIZEOF_SECTION_HEADER 40 + +// +// Section Flags Values +// +#define EFI_IMAGE_SCN_TYPE_NO_PAD BIT3 ///< 0x00000008 ///< Reserved. +#define EFI_IMAGE_SCN_CNT_CODE BIT5 ///< 0x00000020 +#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA BIT6 ///< 0x00000040 +#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA BIT7 ///< 0x00000080 + +#define EFI_IMAGE_SCN_LNK_OTHER BIT8 ///< 0x00000100 ///< Reserved. +#define EFI_IMAGE_SCN_LNK_INFO BIT9 ///< 0x00000200 ///< Section contains comments or some other type of information. +#define EFI_IMAGE_SCN_LNK_REMOVE BIT11 ///< 0x00000800 ///< Section contents will not become part of image. +#define EFI_IMAGE_SCN_LNK_COMDAT BIT12 ///< 0x00001000 + +#define EFI_IMAGE_SCN_ALIGN_1BYTES BIT20 ///< 0x00100000 +#define EFI_IMAGE_SCN_ALIGN_2BYTES BIT21 ///< 0x00200000 +#define EFI_IMAGE_SCN_ALIGN_4BYTES (BIT20|BIT21) ///< 0x00300000 +#define EFI_IMAGE_SCN_ALIGN_8BYTES BIT22 ///< 0x00400000 +#define EFI_IMAGE_SCN_ALIGN_16BYTES (BIT20|BIT22) ///< 0x00500000 +#define EFI_IMAGE_SCN_ALIGN_32BYTES (BIT21|BIT22) ///< 0x00600000 +#define EFI_IMAGE_SCN_ALIGN_64BYTES (BIT20|BIT21|BIT22) ///< 0x00700000 + +#define EFI_IMAGE_SCN_MEM_DISCARDABLE BIT25 ///< 0x02000000 +#define EFI_IMAGE_SCN_MEM_NOT_CACHED BIT26 ///< 0x04000000 +#define EFI_IMAGE_SCN_MEM_NOT_PAGED BIT27 ///< 0x08000000 +#define EFI_IMAGE_SCN_MEM_SHARED BIT28 ///< 0x10000000 +#define EFI_IMAGE_SCN_MEM_EXECUTE BIT29 ///< 0x20000000 +#define EFI_IMAGE_SCN_MEM_READ BIT30 ///< 0x40000000 +#define EFI_IMAGE_SCN_MEM_WRITE BIT31 ///< 0x80000000 + +/// +/// Size of a Symbol Table Record. +/// +#define EFI_IMAGE_SIZEOF_SYMBOL 18 + +// +// Symbols have a section number of the section in which they are +// defined. Otherwise, section numbers have the following meanings: +// +#define EFI_IMAGE_SYM_UNDEFINED (UINT16) 0 ///< Symbol is undefined or is common. +#define EFI_IMAGE_SYM_ABSOLUTE (UINT16) -1 ///< Symbol is an absolute value. +#define EFI_IMAGE_SYM_DEBUG (UINT16) -2 ///< Symbol is a special debug item. + +// +// Symbol Type (fundamental) values. +// +#define EFI_IMAGE_SYM_TYPE_NULL 0 ///< no type. +#define EFI_IMAGE_SYM_TYPE_VOID 1 ///< no valid type. +#define EFI_IMAGE_SYM_TYPE_CHAR 2 ///< type character. +#define EFI_IMAGE_SYM_TYPE_SHORT 3 ///< type short integer. +#define EFI_IMAGE_SYM_TYPE_INT 4 +#define EFI_IMAGE_SYM_TYPE_LONG 5 +#define EFI_IMAGE_SYM_TYPE_FLOAT 6 +#define EFI_IMAGE_SYM_TYPE_DOUBLE 7 +#define EFI_IMAGE_SYM_TYPE_STRUCT 8 +#define EFI_IMAGE_SYM_TYPE_UNION 9 +#define EFI_IMAGE_SYM_TYPE_ENUM 10 ///< enumeration. +#define EFI_IMAGE_SYM_TYPE_MOE 11 ///< member of enumeration. +#define EFI_IMAGE_SYM_TYPE_BYTE 12 +#define EFI_IMAGE_SYM_TYPE_WORD 13 +#define EFI_IMAGE_SYM_TYPE_UINT 14 +#define EFI_IMAGE_SYM_TYPE_DWORD 15 + +// +// Symbol Type (derived) values. +// +#define EFI_IMAGE_SYM_DTYPE_NULL 0 ///< no derived type. +#define EFI_IMAGE_SYM_DTYPE_POINTER 1 +#define EFI_IMAGE_SYM_DTYPE_FUNCTION 2 +#define EFI_IMAGE_SYM_DTYPE_ARRAY 3 + +// +// Storage classes. +// +#define EFI_IMAGE_SYM_CLASS_END_OF_FUNCTION ((UINT8) -1) +#define EFI_IMAGE_SYM_CLASS_NULL 0 +#define EFI_IMAGE_SYM_CLASS_AUTOMATIC 1 +#define EFI_IMAGE_SYM_CLASS_EXTERNAL 2 +#define EFI_IMAGE_SYM_CLASS_STATIC 3 +#define EFI_IMAGE_SYM_CLASS_REGISTER 4 +#define EFI_IMAGE_SYM_CLASS_EXTERNAL_DEF 5 +#define EFI_IMAGE_SYM_CLASS_LABEL 6 +#define EFI_IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 +#define EFI_IMAGE_SYM_CLASS_ARGUMENT 9 +#define EFI_IMAGE_SYM_CLASS_STRUCT_TAG 10 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 +#define EFI_IMAGE_SYM_CLASS_UNION_TAG 12 +#define EFI_IMAGE_SYM_CLASS_TYPE_DEFINITION 13 +#define EFI_IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 +#define EFI_IMAGE_SYM_CLASS_ENUM_TAG 15 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 +#define EFI_IMAGE_SYM_CLASS_REGISTER_PARAM 17 +#define EFI_IMAGE_SYM_CLASS_BIT_FIELD 18 +#define EFI_IMAGE_SYM_CLASS_BLOCK 100 +#define EFI_IMAGE_SYM_CLASS_FUNCTION 101 +#define EFI_IMAGE_SYM_CLASS_END_OF_STRUCT 102 +#define EFI_IMAGE_SYM_CLASS_FILE 103 +#define EFI_IMAGE_SYM_CLASS_SECTION 104 +#define EFI_IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 + +// +// type packing constants +// +#define EFI_IMAGE_N_BTMASK 017 +#define EFI_IMAGE_N_TMASK 060 +#define EFI_IMAGE_N_TMASK1 0300 +#define EFI_IMAGE_N_TMASK2 0360 +#define EFI_IMAGE_N_BTSHFT 4 +#define EFI_IMAGE_N_TSHIFT 2 + +// +// Communal selection types. +// +#define EFI_IMAGE_COMDAT_SELECT_NODUPLICATES 1 +#define EFI_IMAGE_COMDAT_SELECT_ANY 2 +#define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE 3 +#define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH 4 +#define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 + +// +// the following values only be referred in PeCoff, not defined in PECOFF. +// +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1 +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2 +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3 + +/// +/// Relocation format. +/// +typedef struct { + UINT32 VirtualAddress; + UINT32 SymbolTableIndex; + UINT16 Type; +} EFI_IMAGE_RELOCATION; + +/// +/// Size of EFI_IMAGE_RELOCATION +/// +#define EFI_IMAGE_SIZEOF_RELOCATION 10 + +// +// I386 relocation types. +// +#define EFI_IMAGE_REL_I386_ABSOLUTE 0x0000 ///< Reference is absolute, no relocation is necessary. +#define EFI_IMAGE_REL_I386_DIR16 0x0001 ///< Direct 16-bit reference to the symbols virtual address. +#define EFI_IMAGE_REL_I386_REL16 0x0002 ///< PC-relative 16-bit reference to the symbols virtual address. +#define EFI_IMAGE_REL_I386_DIR32 0x0006 ///< Direct 32-bit reference to the symbols virtual address. +#define EFI_IMAGE_REL_I386_DIR32NB 0x0007 ///< Direct 32-bit reference to the symbols virtual address, base not included. +#define EFI_IMAGE_REL_I386_SEG12 0x0009 ///< Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address. +#define EFI_IMAGE_REL_I386_SECTION 0x000A +#define EFI_IMAGE_REL_I386_SECREL 0x000B +#define EFI_IMAGE_REL_I386_REL32 0x0014 ///< PC-relative 32-bit reference to the symbols virtual address. + +// +// x64 processor relocation types. +// +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B +#define IMAGE_REL_AMD64_SECREL7 0x000C +#define IMAGE_REL_AMD64_TOKEN 0x000D +#define IMAGE_REL_AMD64_SREL32 0x000E +#define IMAGE_REL_AMD64_PAIR 0x000F +#define IMAGE_REL_AMD64_SSPAN32 0x0010 + +/// +/// Based relocation format. +/// +typedef struct { + UINT32 VirtualAddress; + UINT32 SizeOfBlock; +} EFI_IMAGE_BASE_RELOCATION; + +/// +/// Size of EFI_IMAGE_BASE_RELOCATION. +/// +#define EFI_IMAGE_SIZEOF_BASE_RELOCATION 8 + +// +// Based relocation types. +// +#define EFI_IMAGE_REL_BASED_ABSOLUTE 0 +#define EFI_IMAGE_REL_BASED_HIGH 1 +#define EFI_IMAGE_REL_BASED_LOW 2 +#define EFI_IMAGE_REL_BASED_HIGHLOW 3 +#define EFI_IMAGE_REL_BASED_HIGHADJ 4 +#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define EFI_IMAGE_REL_BASED_ARM_MOV32A 5 +#define EFI_IMAGE_REL_BASED_ARM_MOV32T 7 +#define EFI_IMAGE_REL_BASED_IA64_IMM64 9 +#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define EFI_IMAGE_REL_BASED_DIR64 10 + +/// +/// Line number format. +/// +typedef struct { + union { + UINT32 SymbolTableIndex; ///< Symbol table index of function name if Linenumber is 0. + UINT32 VirtualAddress; ///< Virtual address of line number. + } Type; + UINT16 Linenumber; ///< Line number. +} EFI_IMAGE_LINENUMBER; + +/// +/// Size of EFI_IMAGE_LINENUMBER. +/// +#define EFI_IMAGE_SIZEOF_LINENUMBER 6 + +// +// Archive format. +// +#define EFI_IMAGE_ARCHIVE_START_SIZE 8 +#define EFI_IMAGE_ARCHIVE_START "!\n" +#define EFI_IMAGE_ARCHIVE_END "`\n" +#define EFI_IMAGE_ARCHIVE_PAD "\n" +#define EFI_IMAGE_ARCHIVE_LINKER_MEMBER "/ " +#define EFI_IMAGE_ARCHIVE_LONGNAMES_MEMBER "// " + +/// +/// Archive Member Headers +/// +typedef struct { + UINT8 Name[16]; ///< File member name - `/' terminated. + UINT8 Date[12]; ///< File member date - decimal. + UINT8 UserID[6]; ///< File member user id - decimal. + UINT8 GroupID[6]; ///< File member group id - decimal. + UINT8 Mode[8]; ///< File member mode - octal. + UINT8 Size[10]; ///< File member size - decimal. + UINT8 EndHeader[2]; ///< String to end header. (0x60 0x0A). +} EFI_IMAGE_ARCHIVE_MEMBER_HEADER; + +/// +/// Size of EFI_IMAGE_ARCHIVE_MEMBER_HEADER. +/// +#define EFI_IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 + + +// +// DLL Support +// + +/// +/// Export Directory Table. +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT32 Name; + UINT32 Base; + UINT32 NumberOfFunctions; + UINT32 NumberOfNames; + UINT32 AddressOfFunctions; + UINT32 AddressOfNames; + UINT32 AddressOfNameOrdinals; +} EFI_IMAGE_EXPORT_DIRECTORY; + +/// +/// Hint/Name Table. +/// +typedef struct { + UINT16 Hint; + UINT8 Name[1]; +} EFI_IMAGE_IMPORT_BY_NAME; + +/// +/// Import Address Table RVA (Thunk Table). +/// +typedef struct { + union { + UINT32 Function; + UINT32 Ordinal; + EFI_IMAGE_IMPORT_BY_NAME *AddressOfData; + } u1; +} EFI_IMAGE_THUNK_DATA; + +#define EFI_IMAGE_ORDINAL_FLAG BIT31 ///< Flag for PE32. +#define EFI_IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & EFI_IMAGE_ORDINAL_FLAG) != 0) +#define EFI_IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) + +/// +/// Import Directory Table +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT32 ForwarderChain; + UINT32 Name; + EFI_IMAGE_THUNK_DATA *FirstThunk; +} EFI_IMAGE_IMPORT_DESCRIPTOR; + + +/// +/// Debug Directory Format. +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT32 Type; + UINT32 SizeOfData; + UINT32 RVA; ///< The address of the debug data when loaded, relative to the image base. + UINT32 FileOffset; ///< The file pointer to the debug data. +} EFI_IMAGE_DEBUG_DIRECTORY_ENTRY; + +#define EFI_IMAGE_DEBUG_TYPE_CODEVIEW 2 ///< The Visual C++ debug information. + +/// +/// Debug Data Structure defined in Microsoft C++. +/// +#define CODEVIEW_SIGNATURE_NB10 SIGNATURE_32('N', 'B', '1', '0') +typedef struct { + UINT32 Signature; ///< "NB10" + UINT32 Unknown; + UINT32 Unknown2; + UINT32 Unknown3; + // + // Filename of .PDB goes here + // +} EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY; + +/// +/// Debug Data Structure defined in Microsoft C++. +/// +#define CODEVIEW_SIGNATURE_RSDS SIGNATURE_32('R', 'S', 'D', 'S') +typedef struct { + UINT32 Signature; ///< "RSDS". + UINT32 Unknown; + UINT32 Unknown2; + UINT32 Unknown3; + UINT32 Unknown4; + UINT32 Unknown5; + // + // Filename of .PDB goes here + // +} EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY; + + +/// +/// Debug Data Structure defined by Apple Mach-O to Coff utility. +/// +#define CODEVIEW_SIGNATURE_MTOC SIGNATURE_32('M', 'T', 'O', 'C') +typedef struct { + UINT32 Signature; ///< "MTOC". + EFI_GUID MachOUuid; + // + // Filename of .DLL (Mach-O with debug info) goes here + // +} EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY; + +/// +/// Resource format. +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT16 NumberOfNamedEntries; + UINT16 NumberOfIdEntries; + // + // Array of EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY entries goes here. + // +} EFI_IMAGE_RESOURCE_DIRECTORY; + +/// +/// Resource directory entry format. +/// +typedef struct { + union { + struct { + UINT32 NameOffset:31; + UINT32 NameIsString:1; + } s; + UINT32 Id; + } u1; + union { + UINT32 OffsetToData; + struct { + UINT32 OffsetToDirectory:31; + UINT32 DataIsDirectory:1; + } s; + } u2; +} EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY; + +/// +/// Resource directory entry for string. +/// +typedef struct { + UINT16 Length; + CHAR16 String[1]; +} EFI_IMAGE_RESOURCE_DIRECTORY_STRING; + +/// +/// Resource directory entry for data array. +/// +typedef struct { + UINT32 OffsetToData; + UINT32 Size; + UINT32 CodePage; + UINT32 Reserved; +} EFI_IMAGE_RESOURCE_DATA_ENTRY; + +/// +/// Header format for TE images, defined in the PI Specification, 1.0. +/// +typedef struct { + UINT16 Signature; ///< The signature for TE format = "VZ". + UINT16 Machine; ///< From the original file header. + UINT8 NumberOfSections; ///< From the original file header. + UINT8 Subsystem; ///< From original optional header. + UINT16 StrippedSize; ///< Number of bytes we removed from the header. + UINT32 AddressOfEntryPoint; ///< Offset to entry point -- from original optional header. + UINT32 BaseOfCode; ///< From original image -- required for ITP debug. + UINT64 ImageBase; ///< From original file header. + EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; ///< Only base relocation and debug directory. +} EFI_TE_IMAGE_HEADER; + + +#define EFI_TE_IMAGE_HEADER_SIGNATURE SIGNATURE_16('V', 'Z') + +// +// Data directory indexes in our TE image header +// +#define EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC 0 +#define EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG 1 + + +/// +/// Union of PE32, PE32+, and TE headers. +/// +typedef union { + EFI_IMAGE_NT_HEADERS32 Pe32; + EFI_IMAGE_NT_HEADERS64 Pe32Plus; + EFI_TE_IMAGE_HEADER Te; +} EFI_IMAGE_OPTIONAL_HEADER_UNION; + +typedef union { + EFI_IMAGE_NT_HEADERS32 *Pe32; + EFI_IMAGE_NT_HEADERS64 *Pe32Plus; + EFI_TE_IMAGE_HEADER *Te; + EFI_IMAGE_OPTIONAL_HEADER_UNION *Union; +} EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION; + +typedef struct { + WIN_CERTIFICATE Hdr; + UINT8 CertData[1]; +} WIN_CERTIFICATE_EFI_PKCS; + +#define SHA256_DIGEST_SIZE 32 +#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 + +typedef struct { + UINT64 ImageAddress; + UINT64 ImageSize; + UINT64 EntryPoint; + UINTN SizeOfHeaders; + UINT16 ImageType; + UINT16 NumberOfSections; + EFI_IMAGE_SECTION_HEADER *FirstSection; + EFI_IMAGE_DATA_DIRECTORY *RelocDir; + EFI_IMAGE_DATA_DIRECTORY *SecDir; + UINT64 NumberOfRvaAndSizes; + EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr; +} PE_COFF_LOADER_IMAGE_CONTEXT; + +#endif diff --git a/include/configtable.h b/include/configtable.h new file mode 100644 index 0000000..fa2b505 --- /dev/null +++ b/include/configtable.h @@ -0,0 +1,68 @@ +/* definitions straight from TianoCore */ + +typedef UINT32 EFI_IMAGE_EXECUTION_ACTION; + +#define EFI_IMAGE_EXECUTION_AUTHENTICATION 0x00000007 +#define EFI_IMAGE_EXECUTION_AUTH_UNTESTED 0x00000000 +#define EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED 0x00000001 +#define EFI_IMAGE_EXECUTION_AUTH_SIG_PASSED 0x00000002 +#define EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND 0x00000003 +#define EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND 0x00000004 +#define EFI_IMAGE_EXECUTION_POLICY_FAILED 0x00000005 +#define EFI_IMAGE_EXECUTION_INITIALIZED 0x00000008 + +typedef struct { + /// + /// Describes the action taken by the firmware regarding this image. + /// + EFI_IMAGE_EXECUTION_ACTION Action; + /// + /// Size of all of the entire structure. + /// + UINT32 InfoSize; + /// + /// If this image was a UEFI device driver (for option ROM, for example) this is the + /// null-terminated, user-friendly name for the device. If the image was for an application, + /// then this is the name of the application. If this cannot be determined, then a simple + /// NULL character should be put in this position. + /// CHAR16 Name[]; + /// + + /// + /// For device drivers, this is the device path of the device for which this device driver + /// was intended. In some cases, the driver itself may be stored as part of the system + /// firmware, but this field should record the device's path, not the firmware path. For + /// applications, this is the device path of the application. If this cannot be determined, + /// a simple end-of-path device node should be put in this position. + /// EFI_DEVICE_PATH_PROTOCOL DevicePath; + /// + + /// + /// Zero or more image signatures. If the image contained no signatures, + /// then this field is empty. + /// + ///EFI_SIGNATURE_LIST Signature; + UINT8 Data[]; +} EFI_IMAGE_EXECUTION_INFO; + +typedef struct { + /// + /// Number of EFI_IMAGE_EXECUTION_INFO structures. + /// + UINTN NumberOfImages; + /// + /// Number of image instances of EFI_IMAGE_EXECUTION_INFO structures. + /// + EFI_IMAGE_EXECUTION_INFO InformationInfo[]; +} EFI_IMAGE_EXECUTION_INFO_TABLE; + + +void * +configtable_get_table(EFI_GUID *guid); +EFI_IMAGE_EXECUTION_INFO_TABLE * +configtable_get_image_table(void); +EFI_IMAGE_EXECUTION_INFO * +configtable_find_image(const EFI_DEVICE_PATH *DevicePath); +int +configtable_image_is_forbidden(const EFI_DEVICE_PATH *DevicePath); + diff --git a/include/console.h b/include/console.h new file mode 100644 index 0000000..e6c2818 --- /dev/null +++ b/include/console.h @@ -0,0 +1,88 @@ +#ifndef _SHIM_LIB_CONSOLE_H +#define _SHIM_LIB_CONSOLE_H 1 + +EFI_INPUT_KEY +console_get_keystroke(void); +void +console_print_box_at(CHAR16 *str_arr[], int highlight, int start_col, int start_row, int size_cols, int size_rows, int offset, int lines); +void +console_print_box(CHAR16 *str_arr[], int highlight); +int +console_yes_no(CHAR16 *str_arr[]); +int +console_select(CHAR16 *title[], CHAR16* selectors[], int start); +void +console_errorbox(CHAR16 *err); +void +console_error(CHAR16 *err, EFI_STATUS); +void +console_alertbox(CHAR16 **title); +void +console_notify(CHAR16 *string); +void +console_reset(void); +#define NOSEL 0x7fffffff + +#define EFI_CONSOLE_CONTROL_PROTOCOL_GUID \ + { 0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21} } + +typedef struct _EFI_CONSOLE_CONTROL_PROTOCOL EFI_CONSOLE_CONTROL_PROTOCOL; + +typedef enum { + EfiConsoleControlScreenText, + EfiConsoleControlScreenGraphics, + EfiConsoleControlScreenMaxValue +} EFI_CONSOLE_CONTROL_SCREEN_MODE; + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + OUT EFI_CONSOLE_CONTROL_SCREEN_MODE *Mode, + OUT BOOLEAN *GopUgaExists, OPTIONAL + OUT BOOLEAN *StdInLocked OPTIONAL + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode + ); + +typedef +EFI_STATUS +(EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN) ( + IN EFI_CONSOLE_CONTROL_PROTOCOL *This, + IN CHAR16 *Password + ); + +struct _EFI_CONSOLE_CONTROL_PROTOCOL { + EFI_CONSOLE_CONTROL_PROTOCOL_GET_MODE GetMode; + EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode; + EFI_CONSOLE_CONTROL_PROTOCOL_LOCK_STD_IN LockStdIn; +}; + +extern VOID setup_console (int text); +extern VOID setup_verbosity(VOID); +extern UINT8 verbose; +#define dprint(fmt, ...) ({ \ + UINTN __dprint_ret = 0; \ + if (verbose) \ + __dprint_ret = Print((fmt), ##__VA_ARGS__); \ + __dprint_ret; \ + }) +#define dprinta(fmt, ...) ({ \ + UINTN __dprinta_ret = 0; \ + if (verbose) { \ + UINTN __dprinta_i; \ + CHAR16 *__dprinta_str = AllocateZeroPool((strlena(fmt) + 1) * 2); \ + for (__dprinta_i = 0; fmt[__dprinta_i] != '\0'; __dprinta_i++) \ + __dprinta_str[__dprinta_i] = fmt[__dprinta_i]; \ + __dprinta_ret = Print((__dprinta_str), ##__VA_ARGS__); \ + FreePool(__dprinta_str); \ + } \ + __dprinta_ret; \ + }) + +#endif /* _SHIM_LIB_CONSOLE_H */ diff --git a/include/efiauthenticated.h b/include/efiauthenticated.h new file mode 100644 index 0000000..f7d6bcb --- /dev/null +++ b/include/efiauthenticated.h @@ -0,0 +1,222 @@ +#ifndef _INC_EFIAUTHENTICATED_H +#define _INC_EFIAUTHENTICATED_H +#include +//*********************************************************************** +// Signature Database +//*********************************************************************** +/// +/// The format of a signature database. +/// +#pragma pack(1) + +typedef struct { + /// + /// An identifier which identifies the agent which added the signature to the list. + /// + EFI_GUID SignatureOwner; + /// + /// The format of the signature is defined by the SignatureType. + /// + UINT8 SignatureData[1]; +} EFI_SIGNATURE_DATA; + +typedef struct { + /// + /// Type of the signature. GUID signature types are defined in below. + /// + EFI_GUID SignatureType; + /// + /// Total size of the signature list, including this header. + /// + UINT32 SignatureListSize; + /// + /// Size of the signature header which precedes the array of signatures. + /// + UINT32 SignatureHeaderSize; + /// + /// Size of each signature. + /// + UINT32 SignatureSize; + /// + /// Header before the array of signatures. The format of this header is specified + /// by the SignatureType. + /// UINT8 SignatureHeader[SignatureHeaderSize]; + /// + /// An array of signatures. Each signature is SignatureSize bytes in length. + /// EFI_SIGNATURE_DATA Signatures[][SignatureSize]; + /// +} EFI_SIGNATURE_LIST; + +#pragma pack() + +// +// _WIN_CERTIFICATE.wCertificateType +// +#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 +#define WIN_CERT_TYPE_EFI_PKCS115 0x0EF0 +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 + +#define EFI_CERT_X509_GUID \ + (EFI_GUID){ \ + 0xa5c059a1, 0x94e4, 0x4aa7, {0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72} \ + } + +#define EFI_CERT_RSA2048_GUID \ + (EFI_GUID){ \ + 0x3c5766e8, 0x269c, 0x4e34, {0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6} \ + } + + +#define EFI_CERT_TYPE_PKCS7_GUID \ + (EFI_GUID){ \ + 0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7} \ + } + +/// +/// WIN_CERTIFICATE_UEFI_GUID.CertType +/// +#define EFI_CERT_TYPE_RSA2048_SHA256_GUID \ + {0xa7717414, 0xc616, 0x4977, {0x94, 0x20, 0x84, 0x47, 0x12, 0xa7, 0x35, 0xbf } } + +/// +/// WIN_CERTIFICATE_UEFI_GUID.CertData +/// +typedef struct { + EFI_GUID HashType; + UINT8 PublicKey[256]; + UINT8 Signature[256]; +} EFI_CERT_BLOCK_RSA_2048_SHA256; + + +/// +/// Certificate which encapsulates a GUID-specific digital signature +/// +typedef struct { + /// + /// This is the standard WIN_CERTIFICATE header, where + /// wCertificateType is set to WIN_CERT_TYPE_UEFI_GUID. + /// + WIN_CERTIFICATE Hdr; + /// + /// This is the unique id which determines the + /// format of the CertData. . + /// + EFI_GUID CertType; + /// + /// The following is the certificate data. The format of + /// the data is determined by the CertType. + /// If CertType is EFI_CERT_TYPE_RSA2048_SHA256_GUID, + /// the CertData will be EFI_CERT_BLOCK_RSA_2048_SHA256 structure. + /// + UINT8 CertData[1]; +} WIN_CERTIFICATE_UEFI_GUID; + + +/// +/// Certificate which encapsulates the RSASSA_PKCS1-v1_5 digital signature. +/// +/// The WIN_CERTIFICATE_UEFI_PKCS1_15 structure is derived from +/// WIN_CERTIFICATE and encapsulate the information needed to +/// implement the RSASSA-PKCS1-v1_5 digital signature algorithm as +/// specified in RFC2437. +/// +typedef struct { + /// + /// This is the standard WIN_CERTIFICATE header, where + /// wCertificateType is set to WIN_CERT_TYPE_UEFI_PKCS1_15. + /// + WIN_CERTIFICATE Hdr; + /// + /// This is the hashing algorithm which was performed on the + /// UEFI executable when creating the digital signature. + /// + EFI_GUID HashAlgorithm; + /// + /// The following is the actual digital signature. The + /// size of the signature is the same size as the key + /// (1024-bit key is 128 bytes) and can be determined by + /// subtracting the length of the other parts of this header + /// from the total length of the certificate as found in + /// Hdr.dwLength. + /// + /// UINT8 Signature[]; + /// +} WIN_CERTIFICATE_EFI_PKCS1_15; + +#define OFFSET_OF(TYPE, Field) ((UINTN) &(((TYPE *)0)->Field)) + +/// +/// Attributes of Authenticated Variable +/// +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x00000020 +#define EFI_VARIABLE_APPEND_WRITE 0x00000040 + +/// +/// AuthInfo is a WIN_CERTIFICATE using the wCertificateType +/// WIN_CERTIFICATE_UEFI_GUID and the CertType +/// EFI_CERT_TYPE_RSA2048_SHA256_GUID. If the attribute specifies +/// authenticated access, then the Data buffer should begin with an +/// authentication descriptor prior to the data payload and DataSize +/// should reflect the the data.and descriptor size. The caller +/// shall digest the Monotonic Count value and the associated data +/// for the variable update using the SHA-256 1-way hash algorithm. +/// The ensuing the 32-byte digest will be signed using the private +/// key associated w/ the public/private 2048-bit RSA key-pair. The +/// WIN_CERTIFICATE shall be used to describe the signature of the +/// Variable data *Data. In addition, the signature will also +/// include the MonotonicCount value to guard against replay attacks. +/// +typedef struct { + /// + /// Included in the signature of + /// AuthInfo.Used to ensure freshness/no + /// replay. Incremented during each + /// "Write" access. + /// + UINT64 MonotonicCount; + /// + /// Provides the authorization for the variable + /// access. It is a signature across the + /// variable data and the Monotonic Count + /// value. Caller uses Private key that is + /// associated with a public key that has been + /// provisioned via the key exchange. + /// + WIN_CERTIFICATE_UEFI_GUID AuthInfo; +} EFI_VARIABLE_AUTHENTICATION; + +/// +/// When the attribute EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS is +/// set, then the Data buffer shall begin with an instance of a complete (and serialized) +/// EFI_VARIABLE_AUTHENTICATION_2 descriptor. The descriptor shall be followed by the new +/// variable value and DataSize shall reflect the combined size of the descriptor and the new +/// variable value. The authentication descriptor is not part of the variable data and is not +/// returned by subsequent calls to GetVariable(). +/// +typedef struct { + /// + /// For the TimeStamp value, components Pad1, Nanosecond, TimeZone, Daylight and + /// Pad2 shall be set to 0. This means that the time shall always be expressed in GMT. + /// + EFI_TIME TimeStamp; + /// + /// Only a CertType of EFI_CERT_TYPE_PKCS7_GUID is accepted. + /// + WIN_CERTIFICATE_UEFI_GUID AuthInfo; + } EFI_VARIABLE_AUTHENTICATION_2; + +/// +/// Size of AuthInfo prior to the data payload. +/// +#define AUTHINFO_SIZE ((OFFSET_OF (EFI_VARIABLE_AUTHENTICATION, AuthInfo)) + \ + (OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) + \ + sizeof (EFI_CERT_BLOCK_RSA_2048_SHA256)) + +#define AUTHINFO2_SIZE(VarAuth2) ((OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo)) + \ + (UINTN) ((EFI_VARIABLE_AUTHENTICATION_2 *) (VarAuth2))->AuthInfo.Hdr.dwLength) + +#define OFFSET_OF_AUTHINFO2_CERT_DATA ((OFFSET_OF (EFI_VARIABLE_AUTHENTICATION_2, AuthInfo)) + \ + (OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData))) + +#endif diff --git a/include/errors.h b/include/errors.h new file mode 100644 index 0000000..0da4bb5 --- /dev/null +++ b/include/errors.h @@ -0,0 +1,9 @@ +#include + +#ifndef EFI_INCOMPATIBLE_VERSION +#define EFI_INCOMPATIBLE_VERSION EFIERR(25) +#endif +#ifndef EFI_SECURITY_VIOLATION +#define EFI_SECURITY_VIOLATION EFIERR(26) +#endif + diff --git a/include/execute.h b/include/execute.h new file mode 100644 index 0000000..9aecbff --- /dev/null +++ b/include/execute.h @@ -0,0 +1,5 @@ +EFI_STATUS +generate_path(CHAR16* name, EFI_LOADED_IMAGE *li, + EFI_DEVICE_PATH **path, CHAR16 **PathName); +EFI_STATUS +execute(EFI_HANDLE image, CHAR16 *name); diff --git a/include/guid.h b/include/guid.h new file mode 100644 index 0000000..3c58be0 --- /dev/null +++ b/include/guid.h @@ -0,0 +1,19 @@ +#include + +#ifndef BUILD_EFI +const char *guid_to_str(EFI_GUID *guid); +void str_to_guid(const char *str, EFI_GUID *guid); +#endif + +extern EFI_GUID GV_GUID; +extern EFI_GUID SIG_DB; +extern EFI_GUID X509_GUID; +extern EFI_GUID RSA2048_GUID; +extern EFI_GUID PKCS7_GUID; +extern EFI_GUID IMAGE_PROTOCOL; +extern EFI_GUID SIMPLE_FS_PROTOCOL; +extern EFI_GUID EFI_CERT_SHA1_GUID; +extern EFI_GUID EFI_CERT_SHA256_GUID; +extern EFI_GUID MOK_OWNER; +extern EFI_GUID SECURITY_PROTOCOL_GUID; +extern EFI_GUID SECURITY2_PROTOCOL_GUID; diff --git a/include/security_policy.h b/include/security_policy.h new file mode 100644 index 0000000..7854db1 --- /dev/null +++ b/include/security_policy.h @@ -0,0 +1,15 @@ +#ifndef _SHIM_LIB_SECURITY_POLICY_H +#define _SHIM_LIB_SECURITY_POLICY_H 1 + +#if defined(OVERRIDE_SECURITY_POLICY) +typedef EFI_STATUS (*SecurityHook) (void *data, UINT32 len); + +EFI_STATUS +security_policy_install(SecurityHook authentication); +EFI_STATUS +security_policy_uninstall(void); +void +security_protocol_set_hashes(unsigned char *esl, int len); +#endif /* OVERRIDE_SECURITY_POLICY */ + +#endif /* SHIM_LIB_SECURITY_POLICY_H */ diff --git a/include/shell.h b/include/shell.h new file mode 100644 index 0000000..9cb5d47 --- /dev/null +++ b/include/shell.h @@ -0,0 +1,2 @@ +EFI_STATUS +argsplit(EFI_HANDLE image, int *argc, CHAR16*** ARGV); diff --git a/include/simple_file.h b/include/simple_file.h new file mode 100644 index 0000000..fe4fd97 --- /dev/null +++ b/include/simple_file.h @@ -0,0 +1,21 @@ +EFI_STATUS +simple_file_open (EFI_HANDLE image, CHAR16 *name, EFI_FILE **file, UINT64 mode); +EFI_STATUS +simple_file_open_by_handle(EFI_HANDLE device, CHAR16 *name, EFI_FILE **file, UINT64 mode); +EFI_STATUS +simple_file_read_all(EFI_FILE *file, UINTN *size, void **buffer); +EFI_STATUS +simple_file_write_all(EFI_FILE *file, UINTN size, void *buffer); +void +simple_file_close(EFI_FILE *file); +EFI_STATUS +simple_dir_read_all(EFI_HANDLE image, CHAR16 *name, EFI_FILE_INFO **Entries, + int *count); +EFI_STATUS +simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter, + CHAR16 ***result, int *count, EFI_FILE_INFO **entries); +void +simple_file_selector(EFI_HANDLE *im, CHAR16 **title, CHAR16 *name, + CHAR16 *filter, CHAR16 **result); +EFI_STATUS +simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h); diff --git a/include/variables.h b/include/variables.h new file mode 100644 index 0000000..b207dbf --- /dev/null +++ b/include/variables.h @@ -0,0 +1,59 @@ +#include + +#include /* for SHA256_DIGEST_SIZE */ + +#define certlist_for_each_certentry(cl, cl_init, s, s_init) \ + for (cl = (EFI_SIGNATURE_LIST *)(cl_init), s = (s_init); \ + s > 0 && s >= cl->SignatureListSize; \ + s -= cl->SignatureListSize, \ + cl = (EFI_SIGNATURE_LIST *) ((UINT8 *)cl + cl->SignatureListSize)) + +/* + * Warning: this assumes (cl)->SignatureHeaderSize is zero. It is for all + * the signatures we process (X509, RSA2048, SHA256) + */ +#define certentry_for_each_cert(c, cl) \ + for (c = (EFI_SIGNATURE_DATA *)((UINT8 *) (cl) + sizeof(EFI_SIGNATURE_LIST) + (cl)->SignatureHeaderSize); \ + (UINT8 *)c < ((UINT8 *)(cl)) + (cl)->SignatureListSize; \ + c = (EFI_SIGNATURE_DATA *)((UINT8 *)c + (cl)->SignatureSize)) + +EFI_STATUS +CreatePkX509SignatureList ( + IN UINT8 *X509Data, + IN UINTN X509DataSize, + IN EFI_GUID owner, + OUT EFI_SIGNATURE_LIST **PkCert + ); +EFI_STATUS +CreateTimeBasedPayload ( + IN OUT UINTN *DataSize, + IN OUT UINT8 **Data + ); +EFI_STATUS +SetSecureVariable(CHAR16 *var, UINT8 *Data, UINTN len, EFI_GUID owner, UINT32 options, int createtimebased); +EFI_STATUS +get_variable(CHAR16 *var, UINT8 **data, UINTN *len, EFI_GUID owner); +EFI_STATUS +get_variable_attr(CHAR16 *var, UINT8 **data, UINTN *len, EFI_GUID owner, + UINT32 *attributes); +EFI_STATUS +find_in_esl(UINT8 *Data, UINTN DataSize, UINT8 *key, UINTN keylen); +EFI_STATUS +find_in_variable_esl(CHAR16* var, EFI_GUID owner, UINT8 *key, UINTN keylen); + +#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001 + +UINT64 +GetOSIndications(void); +EFI_STATUS +SETOSIndicationsAndReboot(UINT64 indications); +int +variable_is_secureboot(void); +int +variable_is_setupmode(void); +EFI_STATUS +variable_enroll_hash(CHAR16 *var, EFI_GUID owner, + UINT8 hash[SHA256_DIGEST_SIZE]); +EFI_STATUS +variable_create_esl(void *cert, int cert_len, EFI_GUID *type, EFI_GUID *owner, + void **out, int *outlen); diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..09fd44a --- /dev/null +++ b/include/version.h @@ -0,0 +1,8 @@ +#define VERSION "1.3.4" + +static void +version(const char *progname) +{ + printf("%s " VERSION "\n", progname); +} + diff --git a/include/wincert.h b/include/wincert.h new file mode 100644 index 0000000..68d1974 --- /dev/null +++ b/include/wincert.h @@ -0,0 +1,33 @@ +#ifndef _INC_WINCERT_H +#define _INC_WINCERT_H + +/// +/// The WIN_CERTIFICATE structure is part of the PE/COFF specification. +/// +typedef struct { + /// + /// The length of the entire certificate, + /// including the length of the header, in bytes. + /// + UINT32 dwLength; + /// + /// The revision level of the WIN_CERTIFICATE + /// structure. The current revision level is 0x0200. + /// + UINT16 wRevision; + /// + /// The certificate type. See WIN_CERT_TYPE_xxx for the UEFI + /// certificate types. The UEFI specification reserves the range of + /// certificate type values from 0x0EF0 to 0x0EFF. + /// + UINT16 wCertificateType; + /// + /// The following is the actual certificate. The format of + /// the certificate depends on wCertificateType. + /// + /// UINT8 bCertificate[ANYSIZE_ARRAY]; + /// +} WIN_CERTIFICATE; + + +#endif diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..adb0347 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,29 @@ +TARGET = lib.a + +LIBFILES = simple_file.o guid.o console.o execute.o configtable.o shell.o variables.o security_policy.o + +ARCH = $(shell uname -m | sed s,i[3456789]86,ia32,) + +EFI_INCLUDE = /usr/include/efi +EFI_INCLUDES = -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol -I../include + +EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o +EFI_LDS = $(EFI_PATH)/elf_$(ARCH)_efi.lds + +CFLAGS = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic \ + -fshort-wchar -Wall -mno-red-zone -DBUILD_EFI -fno-builtin \ + -Werror \ + $(EFI_INCLUDES) +ifeq ($(ARCH),x86_64) + CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI +endif + +lib.a: $(LIBFILES) + ar rcs lib.a $(LIBFILES) + +all: $(TARGET) + +clean: + rm -f lib.a + rm -f $(LIBFILES) + diff --git a/lib/configtable.c b/lib/configtable.c new file mode 100644 index 0000000..e2d92bf --- /dev/null +++ b/lib/configtable.c @@ -0,0 +1,144 @@ +/* + * Copyright 2013 + * + * see COPYING file + * + * read some platform configuration tables + */ +#include +#include + +#include +#include + +void * +configtable_get_table(EFI_GUID *guid) +{ + int i; + + for (i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_CONFIGURATION_TABLE *CT = &ST->ConfigurationTable[i]; + + if (CompareGuid(guid, &CT->VendorGuid) == 0) { + return CT->VendorTable; + } + } + return NULL; +} + +EFI_IMAGE_EXECUTION_INFO_TABLE * +configtable_get_image_table(void) +{ + return configtable_get_table(&SIG_DB); +} + +EFI_IMAGE_EXECUTION_INFO * +configtable_find_image(const EFI_DEVICE_PATH *DevicePath) +{ + EFI_IMAGE_EXECUTION_INFO_TABLE *t = configtable_get_image_table(); + + if (!t) + return NULL; + + int entries = t->NumberOfImages; + EFI_IMAGE_EXECUTION_INFO *e = t->InformationInfo; + + int i; + for (i = 0; i < entries; i++) { +#ifdef DEBUG_CONFIG + Print(L"InfoSize = %d Action = %d\n", e->InfoSize, e->Action); + + /* print what we have for debugging */ + UINT8 *d = (UINT8 *)e; // + sizeof(UINT32)*2; + Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + d += 16; + Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + d += 16; + Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + d += 16; + Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + d += 16; + Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + d += 16; + Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); +#endif + CHAR16 *name = (CHAR16 *)(e->Data); + int skip = 0; + + /* There's a bug in a lot of EFI platforms and they forget to + * put the name here. The only real way of detecting it is to + * look for either a UC16 NULL or ASCII as UC16 */ + if (name[0] == '\0' || (e->Data[1] == 0 && e->Data[3] == 0)) { + skip = StrSize(name); +#ifdef DEBUG_CONFIG + Print(L"FOUND NAME %s (%d)\n", name, skip); +#endif + } + EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *)(e->Data + skip), *dpn = dp; + if (dp->Type == 0 || dp->Type > 6 || dp->SubType == 0 + || (((dp->Length[1] << 8) + dp->Length[0]) > e->InfoSize)) { + /* Parse error, table corrupt, bail */ + Print(L"Image Execution Information table corrupt\n"); + break; + } + + UINTN Size; + DevicePathInstance(&dpn, &Size); +#ifdef DEBUG_CONFIG + Print(L"Path: %s\n", DevicePathToStr(dp)); + Print(L"Device Path Size %d\n", Size); +#endif + if (Size > e->InfoSize) { + /* parse error; the platform obviously has a + * corrupted image table; bail */ + Print(L"Image Execution Information table corrupt\n"); + break; + } + + if (CompareMem(dp, (void *)DevicePath, Size) == 0) { +#ifdef DEBUG_CONFIG + Print(L"***FOUND\n"); + console_get_keystroke(); +#endif + return e; + } + e = (EFI_IMAGE_EXECUTION_INFO *)((UINT8 *)e + e->InfoSize); + } + +#ifdef DEBUG_CONFIG + Print(L"***NOT FOUND\n"); + console_get_keystroke(); +#endif + + return NULL; +} + +int +configtable_image_is_forbidden(const EFI_DEVICE_PATH *DevicePath) +{ + EFI_IMAGE_EXECUTION_INFO *e = configtable_find_image(DevicePath); + + /* Image may not be in DB if it gets executed successfully If it is, + * and EFI_IMAGE_EXECUTION_INITIALIZED is not set, then the image + * isn't authenticated. If there's no signature, usually + * EFI_IMAGE_EXECUTION_AUTH_UNTESTED is set, if the hash is in dbx, + * EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND is returned, and if the key is + * in dbx, EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED is returned*/ + + if (e && (e->Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND + || e->Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED)) { + /* this means the images signing key is in dbx */ +#ifdef DEBUG_CONFIG + Print(L"SIGNATURE IS IN DBX, FORBIDDING EXECUTION\n"); +#endif + return 1; + } + + return 0; +} diff --git a/lib/console.c b/lib/console.c new file mode 100644 index 0000000..2fc8db3 --- /dev/null +++ b/lib/console.c @@ -0,0 +1,452 @@ +/* + * Copyright 2012 + * Copyright 2013 Red Hat Inc. + * + * see COPYING file + */ +#include +#include + +#include +#include +#include + +static EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; + +static int min(int a, int b) +{ + if (a < b) + return a; + return b; +} + +static int +count_lines(CHAR16 *str_arr[]) +{ + int i = 0; + + while (str_arr[i]) + i++; + return i; +} + +static void +SetMem16(CHAR16 *dst, UINT32 n, CHAR16 c) +{ + int i; + + for (i = 0; i < n/2; i++) { + dst[i] = c; + } +} + +EFI_INPUT_KEY +console_get_keystroke(void) +{ + EFI_INPUT_KEY key; + UINTN EventIndex; + + uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey, &EventIndex); + uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key); + + return key; +} + +void +console_print_box_at(CHAR16 *str_arr[], int highlight, int start_col, int start_row, int size_cols, int size_rows, int offset, int lines) +{ + int i; + SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; + UINTN rows, cols; + CHAR16 *Line; + + if (lines == 0) + return; + + uefi_call_wrapper(co->QueryMode, 4, co, co->Mode->Mode, &cols, &rows); + + /* last row on screen is unusable without scrolling, so ignore it */ + rows--; + + if (size_rows < 0) + size_rows = rows + size_rows + 1; + if (size_cols < 0) + size_cols = cols + size_cols + 1; + + if (start_col < 0) + start_col = (cols + start_col + 2)/2; + if (start_row < 0) + start_row = (rows + start_row + 2)/2; + if (start_col < 0) + start_col = 0; + if (start_row < 0) + start_row = 0; + + if (start_col > cols || start_row > rows) { + Print(L"Starting Position (%d,%d) is off screen\n", + start_col, start_row); + return; + } + if (size_cols + start_col > cols) + size_cols = cols - start_col; + if (size_rows + start_row > rows) + size_rows = rows - start_row; + + if (lines > size_rows - 2) + lines = size_rows - 2; + + Line = AllocatePool((size_cols+1)*sizeof(CHAR16)); + if (!Line) { + Print(L"Failed Allocation\n"); + return; + } + + SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL); + + Line[0] = BOXDRAW_DOWN_RIGHT; + Line[size_cols - 1] = BOXDRAW_DOWN_LEFT; + Line[size_cols] = L'\0'; + uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, start_row); + uefi_call_wrapper(co->OutputString, 2, co, Line); + + int start; + if (offset == 0) + /* middle */ + start = (size_rows - lines)/2 + start_row + offset; + else if (offset < 0) + /* from bottom */ + start = start_row + size_rows - lines + offset - 1; + else + /* from top */ + start = start_row + offset; + + + for (i = start_row + 1; i < size_rows + start_row - 1; i++) { + int line = i - start; + + SetMem16 (Line, size_cols*2, L' '); + Line[0] = BOXDRAW_VERTICAL; + Line[size_cols - 1] = BOXDRAW_VERTICAL; + Line[size_cols] = L'\0'; + if (line >= 0 && line < lines) { + CHAR16 *s = str_arr[line]; + int len = StrLen(s); + int col = (size_cols - 2 - len)/2; + + if (col < 0) + col = 0; + + CopyMem(Line + col + 1, s, min(len, size_cols - 2)*2); + } + if (line >= 0 && line == highlight) + uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK); + uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, i); + uefi_call_wrapper(co->OutputString, 2, co, Line); + if (line >= 0 && line == highlight) + uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE); + + } + SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL); + Line[0] = BOXDRAW_UP_RIGHT; + Line[size_cols - 1] = BOXDRAW_UP_LEFT; + Line[size_cols] = L'\0'; + uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, i); + uefi_call_wrapper(co->OutputString, 2, co, Line); + + FreePool (Line); + +} + +void +console_print_box(CHAR16 *str_arr[], int highlight) +{ + SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode; + SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; + CopyMem(&SavedConsoleMode, co->Mode, sizeof(SavedConsoleMode)); + uefi_call_wrapper(co->EnableCursor, 2, co, FALSE); + uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE); + + console_print_box_at(str_arr, highlight, 0, 0, -1, -1, 0, + count_lines(str_arr)); + + console_get_keystroke(); + + uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible); + + uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible); + uefi_call_wrapper(co->SetCursorPosition, 3, co, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow); + uefi_call_wrapper(co->SetAttribute, 2, co, SavedConsoleMode.Attribute); +} + +int +console_select(CHAR16 *title[], CHAR16* selectors[], int start) +{ + SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode; + SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; + EFI_INPUT_KEY k; + int selector; + int selector_lines = count_lines(selectors); + int selector_max_cols = 0; + int i, offs_col, offs_row, size_cols, size_rows, lines; + int selector_offset; + UINTN cols, rows; + + uefi_call_wrapper(co->QueryMode, 4, co, co->Mode->Mode, &cols, &rows); + + for (i = 0; i < selector_lines; i++) { + int len = StrLen(selectors[i]); + + if (len > selector_max_cols) + selector_max_cols = len; + } + + if (start < 0) + start = 0; + if (start >= selector_lines) + start = selector_lines - 1; + + offs_col = - selector_max_cols - 4; + size_cols = selector_max_cols + 4; + + if (selector_lines > rows - 10) { + int title_lines = count_lines(title); + offs_row = title_lines + 1; + size_rows = rows - 3 - title_lines; + lines = size_rows - 2; + } else { + offs_row = - selector_lines - 4; + size_rows = selector_lines + 2; + lines = selector_lines; + } + + if (start > lines) { + selector = lines; + selector_offset = start - lines; + } else { + selector = start; + selector_offset = 0; + } + + CopyMem(&SavedConsoleMode, co->Mode, sizeof(SavedConsoleMode)); + uefi_call_wrapper(co->EnableCursor, 2, co, FALSE); + uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE); + + console_print_box_at(title, -1, 0, 0, -1, -1, 1, count_lines(title)); + + console_print_box_at(selectors, selector, offs_col, offs_row, + size_cols, size_rows, 0, lines); + + do { + k = console_get_keystroke(); + + if (k.ScanCode == SCAN_ESC) { + selector = -1; + break; + } + + if (k.ScanCode == SCAN_UP) { + if (selector > 0) + selector--; + else if (selector_offset > 0) + selector_offset--; + } else if (k.ScanCode == SCAN_DOWN) { + if (selector < lines - 1) + selector++; + else if (selector_offset < (selector_lines - lines)) + selector_offset++; + } + + console_print_box_at(&selectors[selector_offset], selector, + offs_col, offs_row, + size_cols, size_rows, 0, lines); + } while (!(k.ScanCode == SCAN_NULL + && k.UnicodeChar == CHAR_CARRIAGE_RETURN)); + + uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible); + + uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible); + uefi_call_wrapper(co->SetCursorPosition, 3, co, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow); + uefi_call_wrapper(co->SetAttribute, 2, co, SavedConsoleMode.Attribute); + + if (selector < 0) + /* ESC pressed */ + return selector; + return selector + selector_offset; +} + + +int +console_yes_no(CHAR16 *str_arr[]) +{ + return console_select(str_arr, (CHAR16 *[]){ L"No", L"Yes", NULL }, 0); +} + +void +console_alertbox(CHAR16 **title) +{ + console_select(title, (CHAR16 *[]){ L"OK", 0 }, 0); +} + +void +console_errorbox(CHAR16 *err) +{ + CHAR16 **err_arr = (CHAR16 *[]){ + L"ERROR", + L"", + 0, + 0, + }; + + err_arr[2] = err; + + console_alertbox(err_arr); +} + +void +console_notify(CHAR16 *string) +{ + CHAR16 **str_arr = (CHAR16 *[]){ + 0, + 0, + }; + + str_arr[0] = string; + + console_alertbox(str_arr); +} + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) + +/* Copy of gnu-efi-3.0 with the added secure boot strings */ +static struct { + EFI_STATUS Code; + WCHAR *Desc; +} error_table[] = { + { EFI_SUCCESS, L"Success"}, + { EFI_LOAD_ERROR, L"Load Error"}, + { EFI_INVALID_PARAMETER, L"Invalid Parameter"}, + { EFI_UNSUPPORTED, L"Unsupported"}, + { EFI_BAD_BUFFER_SIZE, L"Bad Buffer Size"}, + { EFI_BUFFER_TOO_SMALL, L"Buffer Too Small"}, + { EFI_NOT_READY, L"Not Ready"}, + { EFI_DEVICE_ERROR, L"Device Error"}, + { EFI_WRITE_PROTECTED, L"Write Protected"}, + { EFI_OUT_OF_RESOURCES, L"Out of Resources"}, + { EFI_VOLUME_CORRUPTED, L"Volume Corrupt"}, + { EFI_VOLUME_FULL, L"Volume Full"}, + { EFI_NO_MEDIA, L"No Media"}, + { EFI_MEDIA_CHANGED, L"Media changed"}, + { EFI_NOT_FOUND, L"Not Found"}, + { EFI_ACCESS_DENIED, L"Access Denied"}, + { EFI_NO_RESPONSE, L"No Response"}, + { EFI_NO_MAPPING, L"No mapping"}, + { EFI_TIMEOUT, L"Time out"}, + { EFI_NOT_STARTED, L"Not started"}, + { EFI_ALREADY_STARTED, L"Already started"}, + { EFI_ABORTED, L"Aborted"}, + { EFI_ICMP_ERROR, L"ICMP Error"}, + { EFI_TFTP_ERROR, L"TFTP Error"}, + { EFI_PROTOCOL_ERROR, L"Protocol Error"}, + { EFI_INCOMPATIBLE_VERSION, L"Incompatible Version"}, + { EFI_SECURITY_VIOLATION, L"Security Violation"}, + + // warnings + { EFI_WARN_UNKOWN_GLYPH, L"Warning Unknown Glyph"}, + { EFI_WARN_DELETE_FAILURE, L"Warning Delete Failure"}, + { EFI_WARN_WRITE_FAILURE, L"Warning Write Failure"}, + { EFI_WARN_BUFFER_TOO_SMALL, L"Warning Buffer Too Small"}, + { 0, NULL} +} ; + + +static CHAR16 * +err_string ( + IN EFI_STATUS Status + ) +{ + UINTN Index; + + for (Index = 0; error_table[Index].Desc; Index +=1) { + if (error_table[Index].Code == Status) { + return error_table[Index].Desc; + } + } + + return L""; +} + + +void +console_error(CHAR16 *err, EFI_STATUS status) +{ + CHAR16 **err_arr = (CHAR16 *[]){ + L"ERROR", + L"", + 0, + 0, + }; + CHAR16 str[512]; + + SPrint(str, sizeof(str), L"%s: (%d) %s", err, status, err_string(status)); + + err_arr[2] = str; + + console_alertbox(err_arr); +} + +void +console_reset(void) +{ + SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut; + + uefi_call_wrapper(co->Reset, 2, co, TRUE); + /* set mode 0 - required to be 80x25 */ + uefi_call_wrapper(co->SetMode, 2, co, 0); + uefi_call_wrapper(co->ClearScreen, 1, co); +} + +UINT8 verbose; + +VOID +setup_verbosity(VOID) +{ + EFI_STATUS status; + EFI_GUID guid = SHIM_LOCK_GUID; + UINT8 verbose_check; + UINTN verbose_check_size; + + verbose_check_size = 1; + status = get_variable(L"SHIM_VERBOSE", (void *)&verbose_check, + &verbose_check_size, guid); + verbose = 0; + if (!EFI_ERROR(status)) + verbose = verbose_check; +} + +VOID setup_console (int text) +{ + EFI_STATUS status; + EFI_GUID console_control_guid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; + EFI_CONSOLE_CONTROL_PROTOCOL *concon; + static EFI_CONSOLE_CONTROL_SCREEN_MODE mode = + EfiConsoleControlScreenGraphics; + EFI_CONSOLE_CONTROL_SCREEN_MODE new_mode; + + status = LibLocateProtocol(&console_control_guid, (VOID **)&concon); + if (status != EFI_SUCCESS) + return; + + if (text) { + new_mode = EfiConsoleControlScreenText; + + status = uefi_call_wrapper(concon->GetMode, 4, concon, &mode, + 0, 0); + /* If that didn't work, assume it's graphics */ + if (status != EFI_SUCCESS) + mode = EfiConsoleControlScreenGraphics; + } else { + new_mode = mode; + } + + uefi_call_wrapper(concon->SetMode, 2, concon, new_mode); +} diff --git a/lib/execute.c b/lib/execute.c new file mode 100644 index 0000000..42d71c8 --- /dev/null +++ b/lib/execute.c @@ -0,0 +1,127 @@ +/* + * Copyright 2012 + * + * see COPYING file + * + * -- + * + * generate_path is a cut and paste from + * + * git://github.com/mjg59/shim.git + * + * Code Copyright 2012 Red Hat, Inc + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include +#include + +EFI_STATUS +generate_path(CHAR16* name, EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **path, CHAR16 **PathName) +{ + unsigned int pathlen; + EFI_STATUS efi_status = EFI_SUCCESS; + CHAR16 *devpathstr = DevicePathToStr(li->FilePath), + *found = NULL; + int i; + + for (i = 0; i < StrLen(devpathstr); i++) { + if (devpathstr[i] == '/') + devpathstr[i] = '\\'; + if (devpathstr[i] == '\\') + found = &devpathstr[i]; + } + if (!found) { + pathlen = 0; + } else { + while (*(found - 1) == '\\') + --found; + *found = '\0'; + pathlen = StrLen(devpathstr); + } + + if (name[0] != '\\') + pathlen++; + + *PathName = AllocatePool((pathlen + 1 + StrLen(name))*sizeof(CHAR16)); + + if (!*PathName) { + Print(L"Failed to allocate path buffer\n"); + efi_status = EFI_OUT_OF_RESOURCES; + goto error; + } + + StrCpy(*PathName, devpathstr); + + if (name[0] != '\\') + StrCat(*PathName, L"\\"); + StrCat(*PathName, name); + + *path = FileDevicePath(li->DeviceHandle, *PathName); + +error: + FreePool(devpathstr); + + return efi_status; +} + +EFI_STATUS +execute(EFI_HANDLE image, CHAR16 *name) +{ + EFI_STATUS status; + EFI_HANDLE h; + EFI_LOADED_IMAGE *li; + EFI_DEVICE_PATH *devpath; + CHAR16 *PathName; + + status = uefi_call_wrapper(BS->HandleProtocol, 3, image, + &IMAGE_PROTOCOL, (void **)&li); + if (status != EFI_SUCCESS) + return status; + + + status = generate_path(name, li, &devpath, &PathName); + if (status != EFI_SUCCESS) + return status; + + status = uefi_call_wrapper(BS->LoadImage, 6, FALSE, image, + devpath, NULL, 0, &h); + if (status != EFI_SUCCESS) + goto out; + + status = uefi_call_wrapper(BS->StartImage, 3, h, NULL, NULL); + uefi_call_wrapper(BS->UnloadImage, 1, h); + + out: + FreePool(PathName); + FreePool(devpath); + return status; +} diff --git a/lib/guid.c b/lib/guid.c new file mode 100644 index 0000000..56ec952 --- /dev/null +++ b/lib/guid.c @@ -0,0 +1,48 @@ +/* + * Copyright 2012 + * + * see COPYING file + */ + +#include +#include + +#ifndef BUILD_EFI +/* EFI has %g for this, so it's only needed in platform c */ +const char *guid_to_str(EFI_GUID *guid) +{ + static char str[256]; + + sprintf(str, "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx", + guid->Data1, guid->Data2, guid->Data3, + guid->Data4[0], guid->Data4[1], guid->Data4[2], + guid->Data4[3], guid->Data4[4], guid->Data4[5], + guid->Data4[6], guid->Data4[7]); + + return str; +} + +void str_to_guid(const char *str, EFI_GUID *guid) +{ + sscanf(str, "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", + &guid->Data1, &guid->Data2, &guid->Data3, + guid->Data4, guid->Data4 + 1, guid->Data4 + 2, + guid->Data4 + 3, guid->Data4 + 4, guid->Data4 + 5, + guid->Data4 + 6, guid->Data4 + 7); +} +#endif + +/* all the necessary guids */ +EFI_GUID GV_GUID = EFI_GLOBAL_VARIABLE; +EFI_GUID SIG_DB = { 0xd719b2cb, 0x3d3a, 0x4596, {0xa3, 0xbc, 0xda, 0xd0, 0xe, 0x67, 0x65, 0x6f }}; + +EFI_GUID X509_GUID = { 0xa5c059a1, 0x94e4, 0x4aa7, {0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72} }; +EFI_GUID RSA2048_GUID = { 0x3c5766e8, 0x269c, 0x4e34, {0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6} }; +EFI_GUID PKCS7_GUID = { 0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7} }; +EFI_GUID IMAGE_PROTOCOL = LOADED_IMAGE_PROTOCOL; +EFI_GUID SIMPLE_FS_PROTOCOL = SIMPLE_FILE_SYSTEM_PROTOCOL; +EFI_GUID EFI_CERT_SHA1_GUID = { 0x826ca512, 0xcf10, 0x4ac9, {0xb1, 0x87, 0xbe, 0x1, 0x49, 0x66, 0x31, 0xbd }}; +EFI_GUID EFI_CERT_SHA256_GUID = { 0xc1c41626, 0x504c, 0x4092, { 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 } }; +EFI_GUID MOK_OWNER = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; +EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD1, 0xBF, 0xA9, 0x11, 0x58, 0x39 } }; +EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } }; diff --git a/lib/security_policy.c b/lib/security_policy.c new file mode 100644 index 0000000..9af3a10 --- /dev/null +++ b/lib/security_policy.c @@ -0,0 +1,352 @@ +/* + * Copyright 2012 + * + * see COPYING file + * + * Install and remove a platform security2 override policy + */ + +#include +#include + +#include +#include +#include +#include + +#if defined(OVERRIDE_SECURITY_POLICY) +#include + +/* + * See the UEFI Platform Initialization manual (Vol2: DXE) for this + */ +struct _EFI_SECURITY2_PROTOCOL; +struct _EFI_SECURITY_PROTOCOL; +typedef struct _EFI_SECURITY2_PROTOCOL EFI_SECURITY2_PROTOCOL; +typedef struct _EFI_SECURITY_PROTOCOL EFI_SECURITY_PROTOCOL; +typedef EFI_DEVICE_PATH EFI_DEVICE_PATH_PROTOCOL; + +typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE) ( + const EFI_SECURITY_PROTOCOL *This, + UINT32 AuthenticationStatus, + const EFI_DEVICE_PATH_PROTOCOL *File + ); +typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION) ( + const EFI_SECURITY2_PROTOCOL *This, + const EFI_DEVICE_PATH_PROTOCOL *DevicePath, + VOID *FileBuffer, + UINTN FileSize, + BOOLEAN BootPolicy + ); + +struct _EFI_SECURITY2_PROTOCOL { + EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication; +}; + +struct _EFI_SECURITY_PROTOCOL { + EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState; +}; + + +static UINT8 *security_policy_esl = NULL; +static UINTN security_policy_esl_len; +static SecurityHook extra_check = NULL; + +static EFI_SECURITY_FILE_AUTHENTICATION_STATE esfas = NULL; +static EFI_SECURITY2_FILE_AUTHENTICATION es2fa = NULL; + +static EFI_STATUS thunk_security_policy_authentication( + const EFI_SECURITY_PROTOCOL *This, + UINT32 AuthenticationStatus, + const EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +__attribute__((unused)); + +static EFI_STATUS thunk_security2_policy_authentication( + const EFI_SECURITY2_PROTOCOL *This, + const EFI_DEVICE_PATH_PROTOCOL *DevicePath, + VOID *FileBuffer, + UINTN FileSize, + BOOLEAN BootPolicy + ) +__attribute__((unused)); + +static __attribute__((used)) EFI_STATUS +security2_policy_authentication ( + const EFI_SECURITY2_PROTOCOL *This, + const EFI_DEVICE_PATH_PROTOCOL *DevicePath, + VOID *FileBuffer, + UINTN FileSize, + BOOLEAN BootPolicy + ) +{ + EFI_STATUS status, auth; + + /* Chain original security policy */ + + status = uefi_call_wrapper(es2fa, 5, This, DevicePath, FileBuffer, + FileSize, BootPolicy); + + /* if OK, don't bother with MOK check */ + if (status == EFI_SUCCESS) + return status; + + if (extra_check) + auth = extra_check(FileBuffer, FileSize); + else + return EFI_SECURITY_VIOLATION; + + if (auth == EFI_SECURITY_VIOLATION || auth == EFI_ACCESS_DENIED) + /* return previous status, which is the correct one + * for the platform: may be either EFI_ACCESS_DENIED + * or EFI_SECURITY_VIOLATION */ + return status; + + return auth; +} + +static __attribute__((used)) EFI_STATUS +security_policy_authentication ( + const EFI_SECURITY_PROTOCOL *This, + UINT32 AuthenticationStatus, + const EFI_DEVICE_PATH_PROTOCOL *DevicePathConst + ) +{ + EFI_STATUS status, fail_status; + EFI_DEVICE_PATH *DevPath + = DuplicateDevicePath((EFI_DEVICE_PATH *)DevicePathConst), + *OrigDevPath = DevPath; + EFI_HANDLE h; + EFI_FILE *f; + VOID *FileBuffer; + UINTN FileSize; + CHAR16* DevPathStr; + + /* Chain original security policy */ + status = uefi_call_wrapper(esfas, 3, This, AuthenticationStatus, + DevicePathConst); + + /* if OK avoid checking MOK: It's a bit expensive to + * read the whole file in again (esfas already did this) */ + if (status == EFI_SUCCESS) + goto out; + + /* capture failure status: may be either EFI_ACCESS_DENIED or + * EFI_SECURITY_VIOLATION */ + fail_status = status; + + status = uefi_call_wrapper(BS->LocateDevicePath, 3, + &SIMPLE_FS_PROTOCOL, &DevPath, &h); + if (status != EFI_SUCCESS) + goto out; + + DevPathStr = DevicePathToStr(DevPath); + + status = simple_file_open_by_handle(h, DevPathStr, &f, + EFI_FILE_MODE_READ); + FreePool(DevPathStr); + if (status != EFI_SUCCESS) + goto out; + + status = simple_file_read_all(f, &FileSize, &FileBuffer); + simple_file_close(f); + if (status != EFI_SUCCESS) + goto out; + + if (extra_check) + status = extra_check(FileBuffer, FileSize); + else + status = EFI_SECURITY_VIOLATION; + FreePool(FileBuffer); + + if (status == EFI_ACCESS_DENIED || status == EFI_SECURITY_VIOLATION) + /* return what the platform originally said */ + status = fail_status; + out: + FreePool(OrigDevPath); + return status; +} + + +/* Nasty: ELF and EFI have different calling conventions. Here is the map for + * calling ELF -> EFI + * + * 1) rdi -> rcx (32 saved) + * 2) rsi -> rdx (32 saved) + * 3) rdx -> r8 ( 32 saved) + * 4) rcx -> r9 (32 saved) + * 5) r8 -> 32(%rsp) (48 saved) + * 6) r9 -> 40(%rsp) (48 saved) + * 7) pad+0(%rsp) -> 48(%rsp) (64 saved) + * 8) pad+8(%rsp) -> 56(%rsp) (64 saved) + * 9) pad+16(%rsp) -> 64(%rsp) (80 saved) + * 10) pad+24(%rsp) -> 72(%rsp) (80 saved) + * 11) pad+32(%rsp) -> 80(%rsp) (96 saved) + + * + * So for a five argument callback, the map is ignore the first two arguments + * and then map (EFI -> ELF) assuming pad = 0. + * + * ARG4 -> ARG1 + * ARG3 -> ARG2 + * ARG5 -> ARG3 + * ARG6 -> ARG4 + * ARG11 -> ARG5 + * + * Calling conventions also differ over volatile and preserved registers in + * MS: RBX, RBP, RDI, RSI, R12, R13, R14, and R15 are considered nonvolatile . + * In ELF: Registers %rbp, %rbx and %r12 through %r15 “belong” to the calling + * function and the called function is required to preserve their values. + * + * This means when accepting a function callback from MS -> ELF, we have to do + * separate preservation on %rdi, %rsi before swizzling the arguments and + * handing off to the ELF function. + */ + +asm ( +".type security2_policy_authentication,@function\n" +"thunk_security2_policy_authentication:\n\t" + "mov 0x28(%rsp), %r10 # ARG5\n\t" + "push %rdi\n\t" + "push %rsi\n\t" + "mov %r10, %rdi\n\t" + "subq $8, %rsp # space for storing stack pad\n\t" + "mov $0x08, %rax\n\t" + "mov $0x10, %r10\n\t" + "and %rsp, %rax\n\t" + "cmovnz %rax, %r11\n\t" + "cmovz %r10, %r11\n\t" + "subq %r11, %rsp\n\t" + "addq $8, %r11\n\t" + "mov %r11, (%rsp)\n\t" +"# five argument swizzle\n\t" + "mov %rdi, %r10\n\t" + "mov %rcx, %rdi\n\t" + "mov %rdx, %rsi\n\t" + "mov %r8, %rdx\n\t" + "mov %r9, %rcx\n\t" + "mov %r10, %r8\n\t" + "callq security2_policy_authentication@PLT\n\t" + "mov (%rsp), %r11\n\t" + "addq %r11, %rsp\n\t" + "pop %rsi\n\t" + "pop %rdi\n\t" + "ret\n" +); + +asm ( +".type security_policy_authentication,@function\n" +"thunk_security_policy_authentication:\n\t" + "push %rdi\n\t" + "push %rsi\n\t" + "subq $8, %rsp # space for storing stack pad\n\t" + "mov $0x08, %rax\n\t" + "mov $0x10, %r10\n\t" + "and %rsp, %rax\n\t" + "cmovnz %rax, %r11\n\t" + "cmovz %r10, %r11\n\t" + "subq %r11, %rsp\n\t" + "addq $8, %r11\n\t" + "mov %r11, (%rsp)\n\t" +"# three argument swizzle\n\t" + "mov %rcx, %rdi\n\t" + "mov %rdx, %rsi\n\t" + "mov %r8, %rdx\n\t" + "callq security_policy_authentication@PLT\n\t" + "mov (%rsp), %r11\n\t" + "addq %r11, %rsp\n\t" + "pop %rsi\n\t" + "pop %rdi\n\t" + "ret\n" +); + +EFI_STATUS +security_policy_install(SecurityHook hook) +{ + EFI_SECURITY_PROTOCOL *security_protocol; + EFI_SECURITY2_PROTOCOL *security2_protocol = NULL; + EFI_STATUS status; + + if (esfas) + /* Already Installed */ + return EFI_ALREADY_STARTED; + + /* Don't bother with status here. The call is allowed + * to fail, since SECURITY2 was introduced in PI 1.2.1 + * If it fails, use security2_protocol == NULL as indicator */ + uefi_call_wrapper(BS->LocateProtocol, 3, + &SECURITY2_PROTOCOL_GUID, NULL, + &security2_protocol); + + status = uefi_call_wrapper(BS->LocateProtocol, 3, + &SECURITY_PROTOCOL_GUID, NULL, + &security_protocol); + if (status != EFI_SUCCESS) + /* This one is mandatory, so there's a serious problem */ + return status; + + if (security2_protocol) { + es2fa = security2_protocol->FileAuthentication; + security2_protocol->FileAuthentication = + thunk_security2_policy_authentication; + } + + esfas = security_protocol->FileAuthenticationState; + security_protocol->FileAuthenticationState = + thunk_security_policy_authentication; + + if (hook) + extra_check = hook; + + return EFI_SUCCESS; +} + +EFI_STATUS +security_policy_uninstall(void) +{ + EFI_STATUS status; + + if (esfas) { + EFI_SECURITY_PROTOCOL *security_protocol; + + status = uefi_call_wrapper(BS->LocateProtocol, 3, + &SECURITY_PROTOCOL_GUID, NULL, + &security_protocol); + + if (status != EFI_SUCCESS) + return status; + + security_protocol->FileAuthenticationState = esfas; + esfas = NULL; + } else { + /* nothing installed */ + return EFI_NOT_STARTED; + } + + if (es2fa) { + EFI_SECURITY2_PROTOCOL *security2_protocol; + + status = uefi_call_wrapper(BS->LocateProtocol, 3, + &SECURITY2_PROTOCOL_GUID, NULL, + &security2_protocol); + + if (status != EFI_SUCCESS) + return status; + + security2_protocol->FileAuthentication = es2fa; + es2fa = NULL; + } + + if (extra_check) + extra_check = NULL; + + return EFI_SUCCESS; +} + +void +security_protocol_set_hashes(unsigned char *esl, int len) +{ + security_policy_esl = esl; + security_policy_esl_len = len; +} +#endif /* OVERRIDE_SECURITY_POLICY */ diff --git a/lib/shell.c b/lib/shell.c new file mode 100644 index 0000000..51de4e0 --- /dev/null +++ b/lib/shell.c @@ -0,0 +1,57 @@ +/* + * Copyright 2012 + * + * see COPYING file + * + * misc shell helper functions + */ +#include +#include + +#include + +EFI_STATUS +argsplit(EFI_HANDLE image, int *argc, CHAR16*** ARGV) +{ + int i, count = 1; + EFI_STATUS status; + EFI_LOADED_IMAGE *info; + CHAR16 *start; + + *argc = 0; + + status = uefi_call_wrapper(BS->HandleProtocol, 3, image, &LoadedImageProtocol, (VOID **) &info); + if (EFI_ERROR(status)) { + Print(L"Failed to get arguments\n"); + return status; + } + + for (i = 0; i < info->LoadOptionsSize; i += 2) { + CHAR16 *c = (CHAR16 *)(info->LoadOptions + i); + if (*c == L' ' && *(c+1) != '\0') { + (*argc)++; + } + } + + (*argc)++; /* we counted spaces, so add one for initial */ + + *ARGV = AllocatePool(*argc * sizeof(*ARGV)); + if (!*ARGV) { + return EFI_OUT_OF_RESOURCES; + } + (*ARGV)[0] = (CHAR16 *)info->LoadOptions; + for (i = 0; i < info->LoadOptionsSize; i += 2) { + CHAR16 *c = (CHAR16 *)(info->LoadOptions + i); + if (*c == L' ') { + *c = L'\0'; + if (*(c + 1) == '\0') + /* strip trailing space */ + break; + start = c + 1; + (*ARGV)[count++] = start; + } + } + + return EFI_SUCCESS; +} + diff --git a/lib/simple_file.c b/lib/simple_file.c new file mode 100644 index 0000000..3af0ec8 --- /dev/null +++ b/lib/simple_file.c @@ -0,0 +1,528 @@ +/* + * Copyright 2012 + * + * see COPYING file + */ + +#include +#include + +#include +#include +#include +#include /* for generate_path() */ + +static EFI_GUID IMAGE_PROTOCOL = LOADED_IMAGE_PROTOCOL; +static EFI_GUID SIMPLE_FS_PROTOCOL = SIMPLE_FILE_SYSTEM_PROTOCOL; +static EFI_GUID FILE_INFO = EFI_FILE_INFO_ID; +static EFI_GUID FS_INFO = EFI_FILE_SYSTEM_INFO_ID; + +EFI_STATUS +simple_file_open_by_handle(EFI_HANDLE device, CHAR16 *name, EFI_FILE **file, UINT64 mode) +{ + EFI_STATUS efi_status; + EFI_FILE_IO_INTERFACE *drive; + EFI_FILE *root; + + efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, + &SIMPLE_FS_PROTOCOL, (void **)&drive); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to find simple file protocol (%d)\n", efi_status); + goto error; + } + + efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to open drive volume (%d)\n", efi_status); + goto error; + } + + efi_status = uefi_call_wrapper(root->Open, 5, root, file, name, + mode, 0); + + error: + return efi_status; +} + +EFI_STATUS +simple_file_open(EFI_HANDLE image, CHAR16 *name, EFI_FILE **file, UINT64 mode) +{ + EFI_STATUS efi_status; + EFI_HANDLE device; + EFI_LOADED_IMAGE *li; + EFI_DEVICE_PATH *loadpath = NULL; + CHAR16 *PathName = NULL; + + efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image, + &IMAGE_PROTOCOL, (void **)&li); + + if (efi_status != EFI_SUCCESS) + return simple_file_open_by_handle(image, name, file, mode); + + efi_status = generate_path(name, li, &loadpath, &PathName); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to generate load path for %s\n", name); + return efi_status; + } + + device = li->DeviceHandle; + + efi_status = simple_file_open_by_handle(device, PathName, file, mode); + + FreePool(PathName); + FreePool(loadpath); + + return efi_status; +} + +EFI_STATUS +simple_dir_read_all_by_handle(EFI_HANDLE image, EFI_FILE *file, CHAR16* name, EFI_FILE_INFO **entries, + int *count) +{ + EFI_STATUS status; + char buf[4096]; + UINTN size = sizeof(buf); + EFI_FILE_INFO *fi = (void *)buf; + + status = uefi_call_wrapper(file->GetInfo, 4, file, &FILE_INFO, + &size, fi); + if (status != EFI_SUCCESS) { + Print(L"Failed to get file info\n"); + goto out; + } + if ((fi->Attribute & EFI_FILE_DIRECTORY) == 0) { + Print(L"Not a directory %s\n", name); + status = EFI_INVALID_PARAMETER; + goto out; + } + size = 0; + *count = 0; + for (;;) { + UINTN len = sizeof(buf); + status = uefi_call_wrapper(file->Read, 3, file, &len, buf); + if (status != EFI_SUCCESS || len == 0) + break; + (*count)++; + size += len; + } + uefi_call_wrapper(file->SetPosition, 2, file, 0); + + char *ptr = AllocatePool(size); + *entries = (EFI_FILE_INFO *)ptr; + if (!*entries) + return EFI_OUT_OF_RESOURCES; + int i; + for (i = 0; i < *count; i++) { + UINTN len = size; + uefi_call_wrapper(file->Read, 3, file, &len, ptr); + ptr += len; + size -= len; + } + status = EFI_SUCCESS; + out: + simple_file_close(file); + if (status != EFI_SUCCESS && *entries) { + FreePool(*entries); + *entries = NULL; + } + return status; +} + +EFI_STATUS +simple_dir_read_all(EFI_HANDLE image, CHAR16 *name, EFI_FILE_INFO **entries, + int *count) +{ + EFI_FILE *file; + EFI_STATUS status; + + status = simple_file_open(image, name, &file, EFI_FILE_MODE_READ); + if (status != EFI_SUCCESS) { + Print(L"failed to open file %s: %d\n", name, status); + return status; + } + + return simple_dir_read_all_by_handle(image, file, name, entries, count); +} + +EFI_STATUS +simple_file_read_all(EFI_FILE *file, UINTN *size, void **buffer) +{ + EFI_STATUS efi_status; + EFI_FILE_INFO *fi; + char buf[1024]; + + *size = sizeof(buf); + fi = (void *)buf; + + + efi_status = uefi_call_wrapper(file->GetInfo, 4, file, &FILE_INFO, + size, fi); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to get file info\n"); + return efi_status; + } + + *size = fi->FileSize; + + *buffer = AllocatePool(*size); + if (!*buffer) { + Print(L"Failed to allocate buffer of size %d\n", *size); + return EFI_OUT_OF_RESOURCES; + } + efi_status = uefi_call_wrapper(file->Read, 3, file, size, *buffer); + + return efi_status; +} + + +EFI_STATUS +simple_file_write_all(EFI_FILE *file, UINTN size, void *buffer) +{ + EFI_STATUS efi_status; + + efi_status = uefi_call_wrapper(file->Write, 3, file, &size, buffer); + + return efi_status; +} + +void +simple_file_close(EFI_FILE *file) +{ + uefi_call_wrapper(file->Close, 1, file); +} + +EFI_STATUS +simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h) +{ + UINTN count, i; + EFI_HANDLE *vol_handles = NULL; + EFI_STATUS status; + CHAR16 **entries; + int val; + + uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, + &SIMPLE_FS_PROTOCOL, NULL, &count, &vol_handles); + + if (!count || !vol_handles) + return EFI_NOT_FOUND; + + entries = AllocatePool(sizeof(CHAR16 *) * (count+1)); + if (!entries) + return EFI_OUT_OF_RESOURCES; + + for (i = 0; i < count; i++) { + char buf[4096]; + UINTN size = sizeof(buf); + EFI_FILE_SYSTEM_INFO *fi = (void *)buf; + EFI_FILE *root; + CHAR16 *name; + EFI_FILE_IO_INTERFACE *drive; + + status = uefi_call_wrapper(BS->HandleProtocol, 3, + vol_handles[i], + &SIMPLE_FS_PROTOCOL, + (void **)&drive); + if (status != EFI_SUCCESS || !drive) + continue; + + status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root); + if (status != EFI_SUCCESS) + continue; + + status = uefi_call_wrapper(root->GetInfo, 4, root, &FS_INFO, + &size, fi); + if (status != EFI_SUCCESS) + continue; + + name = fi->VolumeLabel; + + if (!name || StrLen(name) == 0 || StrCmp(name, L" ") == 0) + name = DevicePathToStr(DevicePathFromHandle(vol_handles[i])); + + entries[i] = AllocatePool((StrLen(name) + 2) * sizeof(CHAR16)); + if (!entries[i]) + break; + StrCpy(entries[i], name); + } + entries[i] = NULL; + + val = console_select(title, entries, 0); + + if (val >= 0) { + *selected = AllocatePool((StrLen(entries[val]) + 1) * sizeof(CHAR16)); + if (*selected) { + StrCpy(*selected , entries[val]); + } + *h = vol_handles[val]; + } else { + *selected = NULL; + *h = 0; + } + + for (i = 0; i < count; i++) { + if (entries[i]) + FreePool(entries[i]); + } + FreePool(entries); + FreePool(vol_handles); + + + return EFI_SUCCESS; +} + +EFI_STATUS +simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter, + CHAR16 ***result, int *count, EFI_FILE_INFO **entries) +{ + EFI_STATUS status; + int tot, offs = StrLen(filter), i, c, filtercount = 1; + EFI_FILE_INFO *next; + void *ptr; + CHAR16 *newfilter = AllocatePool((StrLen(filter) + 1) * sizeof(CHAR16)), + **filterarr; + + if (!newfilter) + return EFI_OUT_OF_RESOURCES; + + /* just in case efi ever stops writeable strings */ + StrCpy(newfilter, filter); + + for (i = 0; i < offs; i++) { + if (filter[i] == '|') + filtercount++; + } + filterarr = AllocatePool(filtercount * sizeof(void *)); + if (!filterarr) + return EFI_OUT_OF_RESOURCES; + c = 0; + filterarr[c++] = newfilter; + for (i = 0; i < offs; i++) { + if (filter[i] == '|') { + newfilter[i] = '\0'; + filterarr[c++] = &newfilter[i+1]; + } + } + + *count = 0; + + status = simple_dir_read_all(image, name, entries, &tot); + + if (status != EFI_SUCCESS) + goto out; + ptr = next = *entries; + + for (i = 0; i < tot; i++) { + int len = StrLen(next->FileName); + + for (c = 0; c < filtercount; c++) { + offs = StrLen(filterarr[c]); + + if (StrCmp(&next->FileName[len - offs], filterarr[c]) == 0 + || (next->Attribute & EFI_FILE_DIRECTORY)) { + (*count)++; + break; + } + } + ptr += OFFSET_OF(EFI_FILE_INFO, FileName) + (len + 1)*sizeof(CHAR16); + next = ptr; + } + if (*count) + *result = AllocatePool(((*count) + 1) * sizeof(void *)); + else + *result = AllocatePool(2 * sizeof(void *)); + + *count = 0; + ptr = next = *entries; + + for (i = 0; i < tot; i++) { + int len = StrLen(next->FileName); + + if (StrCmp(next->FileName, L".") == 0) + /* ignore . directory */ + goto next; + + if (next->Attribute & EFI_FILE_DIRECTORY) { + (*result)[(*count)] = PoolPrint(L"%s/", next->FileName); + if (!(*result)[(*count)]) { + Print(L"Failed to allocate buffer"); + return EFI_OUT_OF_RESOURCES; + } + (*count)++; + goto next; + } + + for (c = 0; c < filtercount; c++) { + offs = StrLen(filterarr[c]); + + if (StrCmp(&next->FileName[len - offs], filterarr[c]) == 0) { + (*result)[(*count)] = StrDuplicate(next->FileName); + if (!(*result)[(*count)]) { + Print(L"Failed to allocate buffer"); + return EFI_OUT_OF_RESOURCES; + } + (*count)++; + } else { + continue; + } + break; + } + + next: + if (StrCmp(next->FileName, L"..") == 0) { + /* place .. directory first */ + CHAR16 *tmp = (*result)[(*count) - 1]; + + (*result)[(*count) - 1] = (*result)[0]; + (*result)[0] = tmp; + } + + ptr += OFFSET_OF(EFI_FILE_INFO, FileName) + (len + 1)*sizeof(CHAR16); + next = ptr; + } + if (*count == 0) { + /* no entries at all ... can happen because top level dir has no . or .. */ + (*result)[(*count)++] = L"./"; + } + (*result)[*count] = NULL; + status = EFI_SUCCESS; + + out: + if (status != EFI_SUCCESS) { + if (*entries) + FreePool(*entries); + *entries = NULL; + if (*result) + FreePool(*result); + *result = NULL; + } + return status; +} + +static void +free_entries(CHAR16 **entries, int count) +{ + int i; + + for (i = 0; i 0; --i) { + if (name[i] == '\\') + break; + } + if (i == 0) + i = 1; + + if (StrCmp(name, L"\\") != 0 + && StrCmp(&name[i], L"..") != 0) { + name[i] = '\0'; + free_entries(entries, count); + FreePool(entries); + entries = NULL; + FreePool(dmp); + goto redo; + } + } + newname = AllocatePool((StrLen(name) + len + 2)*sizeof(CHAR16)); + if (!newname) + goto out_free; + StrCpy(newname, name); + + if (name[StrLen(name) - 1] != '\\') + StrCat(newname, L"\\"); + StrCat(newname, selected); + /* remove trailing / */ + newname[StrLen(newname) - 1] = '\0'; + + free_entries(entries, count); + FreePool(entries); + entries = NULL; + FreePool(dmp); + FreePool(name); + name = newname; + + goto redo; + } + *result = AllocatePool((StrLen(name) + len + 2)*sizeof(CHAR16)); + if (*result) { + StrCpy(*result, name); + if (name[StrLen(name) - 1] != '\\') + StrCat(*result, L"\\"); + StrCat(*result, selected); + } + + out_free: + FreePool(dmp); + if (entries) { + free_entries(entries, count); + FreePool(entries); + } + out_free_name: + FreePool(name); +} diff --git a/lib/variables.c b/lib/variables.c new file mode 100644 index 0000000..81bd34d --- /dev/null +++ b/lib/variables.c @@ -0,0 +1,339 @@ +/* + * Copyright 2012 + * + * see COPYING file + * + * Portions of this file are a direct cut and paste from Tianocore + * (http://tianocore.sf.net) + * + * SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.c + * + * Copyright (c) 2011 - 2012, Intel Corporation. All rights reserved.
+ * This program and the accompanying materials + * are licensed and made available under the terms and conditions of the BSD License + * which accompanies this distribution. The full text of the license may be found + * at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + */ +#include +#include + +#include + +#include +#include +#include +#include + +EFI_STATUS +variable_create_esl(void *cert, int cert_len, EFI_GUID *type, EFI_GUID *owner, + void **out, int *outlen) +{ + *outlen = cert_len + sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID); + + *out = AllocateZeroPool(*outlen); + if (!*out) + return EFI_OUT_OF_RESOURCES; + + EFI_SIGNATURE_LIST *sl = *out; + + sl->SignatureHeaderSize = 0; + sl->SignatureType = *type; + sl->SignatureSize = cert_len + sizeof(EFI_GUID); + sl->SignatureListSize = *outlen; + + EFI_SIGNATURE_DATA *sd = *out + sizeof(EFI_SIGNATURE_LIST); + + if (owner) + sd->SignatureOwner = *owner; + + CopyMem(sd->SignatureData, cert, cert_len); + + return EFI_SUCCESS; +} + + +EFI_STATUS +CreateTimeBasedPayload ( + IN OUT UINTN *DataSize, + IN OUT UINT8 **Data + ) +{ + EFI_STATUS Status; + UINT8 *NewData; + UINT8 *Payload; + UINTN PayloadSize; + EFI_VARIABLE_AUTHENTICATION_2 *DescriptorData; + UINTN DescriptorSize; + EFI_TIME Time; + EFI_GUID efi_cert_type = EFI_CERT_TYPE_PKCS7_GUID; + + if (Data == NULL || DataSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // In Setup mode or Custom mode, the variable does not need to be signed but the + // parameters to the SetVariable() call still need to be prepared as authenticated + // variable. So we create EFI_VARIABLE_AUTHENTICATED_2 descriptor without certificate + // data in it. + // + Payload = *Data; + PayloadSize = *DataSize; + + DescriptorSize = OFFSET_OF(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) + OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData); + NewData = (UINT8*) AllocateZeroPool (DescriptorSize + PayloadSize); + if (NewData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if ((Payload != NULL) && (PayloadSize != 0)) { + CopyMem (NewData + DescriptorSize, Payload, PayloadSize); + } + + DescriptorData = (EFI_VARIABLE_AUTHENTICATION_2 *) (NewData); + + ZeroMem (&Time, sizeof (EFI_TIME)); + Status = uefi_call_wrapper(RT->GetTime,2, &Time, NULL); + if (EFI_ERROR (Status)) { + FreePool(NewData); + return Status; + } + Time.Pad1 = 0; + Time.Nanosecond = 0; + Time.TimeZone = 0; + Time.Daylight = 0; + Time.Pad2 = 0; + CopyMem (&DescriptorData->TimeStamp, &Time, sizeof (EFI_TIME)); + + DescriptorData->AuthInfo.Hdr.dwLength = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData); + DescriptorData->AuthInfo.Hdr.wRevision = 0x0200; + DescriptorData->AuthInfo.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID; + DescriptorData->AuthInfo.CertType = efi_cert_type; + + /* we're expecting an EFI signature list, so don't free the input since + * it might not be in a pool */ +#if 0 + if (Payload != NULL) { + FreePool(Payload); + } +#endif + + *DataSize = DescriptorSize + PayloadSize; + *Data = NewData; + return EFI_SUCCESS; +} + +EFI_STATUS +SetSecureVariable(CHAR16 *var, UINT8 *Data, UINTN len, EFI_GUID owner, + UINT32 options, int createtimebased) +{ + EFI_SIGNATURE_LIST *Cert; + UINTN DataSize; + EFI_STATUS efi_status; + + /* Microsoft request: Bugs in some UEFI platforms mean that PK or any + * other secure variable can be updated or deleted programmatically, + * so prevent */ + if (!variable_is_setupmode()) + return EFI_SECURITY_VIOLATION; + + if (createtimebased) { + int ds; + efi_status = variable_create_esl(Data, len, &X509_GUID, NULL, + (void **)&Cert, &ds); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to create %s certificate %d\n", var, efi_status); + return efi_status; + } + + DataSize = ds; + } else { + /* we expect an efi signature list rather than creating it */ + Cert = (EFI_SIGNATURE_LIST *)Data; + DataSize = len; + } + efi_status = CreateTimeBasedPayload(&DataSize, (UINT8 **)&Cert); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to create time based payload %d\n", efi_status); + return efi_status; + } + + efi_status = uefi_call_wrapper(RT->SetVariable, 5, var, &owner, + EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_RUNTIME_ACCESS + | EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS + | options, + DataSize, Cert); + + return efi_status; +} + +UINT64 +GetOSIndications(void) +{ + UINT64 indications; + UINTN DataSize = sizeof(indications); + EFI_STATUS efi_status; + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"OsIndicationsSupported", &GV_GUID, NULL, &DataSize, &indications); + if (efi_status != EFI_SUCCESS) + return 0; + + return indications; +} + +EFI_STATUS +SETOSIndicationsAndReboot(UINT64 indications) +{ + UINTN DataSize = sizeof(indications); + EFI_STATUS efi_status; + + efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"OsIndications", + &GV_GUID, + EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_RUNTIME_ACCESS + | EFI_VARIABLE_BOOTSERVICE_ACCESS, + DataSize, &indications); + + if (efi_status != EFI_SUCCESS) + return efi_status; + + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, EFI_SUCCESS, 0, NULL); + /* does not return */ + + return EFI_SUCCESS; +} + +EFI_STATUS +get_variable_attr(CHAR16 *var, UINT8 **data, UINTN *len, EFI_GUID owner, + UINT32 *attributes) +{ + EFI_STATUS efi_status; + + *len = 0; + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, var, &owner, + NULL, len, NULL); + if (efi_status != EFI_BUFFER_TOO_SMALL) + return efi_status; + + *data = AllocateZeroPool(*len); + if (!data) + return EFI_OUT_OF_RESOURCES; + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, var, &owner, + attributes, len, *data); + + if (efi_status != EFI_SUCCESS) { + FreePool(*data); + *data = NULL; + } + return efi_status; +} + +EFI_STATUS +get_variable(CHAR16 *var, UINT8 **data, UINTN *len, EFI_GUID owner) +{ + return get_variable_attr(var, data, len, owner, NULL); +} + +EFI_STATUS +find_in_esl(UINT8 *Data, UINTN DataSize, UINT8 *key, UINTN keylen) +{ + EFI_SIGNATURE_LIST *CertList; + + certlist_for_each_certentry(CertList, Data, DataSize, DataSize) { + if (CertList->SignatureSize != keylen + sizeof(EFI_GUID)) + continue; + EFI_SIGNATURE_DATA *Cert; + + certentry_for_each_cert(Cert, CertList) + if (CompareMem (Cert->SignatureData, key, keylen) == 0) + return EFI_SUCCESS; + } + return EFI_NOT_FOUND; +} + +EFI_STATUS +find_in_variable_esl(CHAR16* var, EFI_GUID owner, UINT8 *key, UINTN keylen) +{ + UINTN DataSize; + UINT8 *Data; + EFI_STATUS status; + + status = get_variable(var, &Data, &DataSize, owner); + if (status != EFI_SUCCESS) + return status; + + status = find_in_esl(Data, DataSize, key, keylen); + + FreePool(Data); + + return status; +} + +int +variable_is_setupmode(void) +{ + /* set to 1 because we return true if SetupMode doesn't exist */ + UINT8 SetupMode = 1; + UINTN DataSize = sizeof(SetupMode); + + uefi_call_wrapper(RT->GetVariable, 5, L"SetupMode", &GV_GUID, NULL, + &DataSize, &SetupMode); + + return SetupMode; +} + +int +variable_is_secureboot(void) +{ + /* return false if variable doesn't exist */ + UINT8 SecureBoot = 0; + UINTN DataSize; + + DataSize = sizeof(SecureBoot); + uefi_call_wrapper(RT->GetVariable, 5, L"SecureBoot", &GV_GUID, NULL, + &DataSize, &SecureBoot); + + return SecureBoot; +} + +EFI_STATUS +variable_enroll_hash(CHAR16 *var, EFI_GUID owner, + UINT8 hash[SHA256_DIGEST_SIZE]) +{ + EFI_STATUS status; + + if (find_in_variable_esl(var, owner, hash, SHA256_DIGEST_SIZE) + == EFI_SUCCESS) + /* hash already present */ + return EFI_ALREADY_STARTED; + + UINT8 sig[sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + SHA256_DIGEST_SIZE]; + EFI_SIGNATURE_LIST *l = (void *)sig; + EFI_SIGNATURE_DATA *d = (void *)sig + sizeof(EFI_SIGNATURE_LIST); + SetMem(sig, 0, sizeof(sig)); + l->SignatureType = EFI_CERT_SHA256_GUID; + l->SignatureListSize = sizeof(sig); + l->SignatureSize = 16 +32; /* UEFI defined */ + CopyMem(&d->SignatureData, hash, SHA256_DIGEST_SIZE); + d->SignatureOwner = MOK_OWNER; + + if (CompareGuid(&owner, &SIG_DB) == 0) + status = SetSecureVariable(var, sig, sizeof(sig), owner, + EFI_VARIABLE_APPEND_WRITE, 0); + else + status = uefi_call_wrapper(RT->SetVariable, 5, var, &owner, + EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_APPEND_WRITE, + sizeof(sig), sig); + return status; +} diff --git a/netboot.c b/netboot.c index c44aeac..a83c82a 100644 --- a/netboot.c +++ b/netboot.c @@ -39,7 +39,6 @@ #include "shim.h" #include "netboot.h" - static inline unsigned short int __swap16(unsigned short int x) { __asm__("xchgb %b0,%h0" @@ -53,7 +52,7 @@ static inline unsigned short int __swap16(unsigned short int x) static EFI_PXE_BASE_CODE *pxe; static EFI_IP_ADDRESS tftp_addr; -static char *full_path; +static CHAR8 *full_path; typedef struct { @@ -62,6 +61,24 @@ typedef struct { UINT8 Data[1]; } EFI_DHCP6_PACKET_OPTION; +static CHAR8 * +translate_slashes(char *str) +{ + int i; + int j; + if (str == NULL) + return (CHAR8 *)str; + + for (i = 0, j = 0; str[i] != '\0'; i++, j++) { + if (str[i] == '\\') { + str[j] = '/'; + if (str[i+1] == '\\') + i++; + } + } + return (CHAR8 *)str; +} + /* * usingNetboot * Returns TRUE if we identify a protocol that is enabled and Providing us with @@ -111,7 +128,7 @@ try_again: for (i=0; i < (bs / sizeof(EFI_HANDLE)); i++) { status = uefi_call_wrapper(BS->OpenProtocol, 6, hbuf[i], &pxe_base_code_protocol, - &pxe, image_handle, NULL, + (void **)&pxe, image_handle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (status != EFI_SUCCESS) { @@ -141,11 +158,11 @@ try_again: return rc; } -static char *get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET *pkt) +static CHAR8 *get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET *pkt) { void *optr; EFI_DHCP6_PACKET_OPTION *option; - char *url; + CHAR8 *url; UINT32 urllen; optr = pkt->DhcpOptions; @@ -159,10 +176,9 @@ static char *get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET *pkt) if (ntohs(option->OpCode) == 59) { /* This is the bootfile url option */ urllen = ntohs(option->Length); - url = AllocatePool(urllen+2); + url = AllocateZeroPool(urllen+1); if (!url) return NULL; - memset(url, 0, urllen+2); memcpy(url, option->Data, urllen); return url; } @@ -172,10 +188,10 @@ static char *get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET *pkt) return NULL; } -static UINT16 str2ns(UINT8 *str) +static CHAR16 str2ns(CHAR8 *str) { - UINT16 ret = 0; - UINT8 v; + CHAR16 ret = 0; + CHAR8 v; for(;*str;str++) { if ('0' <= *str && *str <= '9') v = *str - '0'; @@ -190,18 +206,18 @@ static UINT16 str2ns(UINT8 *str) return htons(ret); } -static UINT8 *str2ip6(char *str) +static CHAR8 *str2ip6(CHAR8 *str) { UINT8 i, j, p; size_t len; - UINT8 *a, *b, t; + CHAR8 *a, *b, t; static UINT16 ip[8]; for(i=0; i < 8; i++) { ip[i] = 0; } - len = strlen((UINT8 *)str); - a = b = (UINT8 *)str; + len = strlen(str); + a = b = str; for(i=p=0; i < len; i++, b++) { if (*b != ':') continue; @@ -212,7 +228,7 @@ static UINT8 *str2ip6(char *str) if ( *(b+1) == ':' ) break; } - a = b = (UINT8 *)(str + len); + a = b = (str + len); for(j=len, p=7; j > i; j--, a--) { if (*a != ':') continue; @@ -222,14 +238,14 @@ static UINT8 *str2ip6(char *str) *b = t; b = a; } - return (UINT8 *)ip; + return (CHAR8 *)ip; } -static BOOLEAN extract_tftp_info(char *url) +static BOOLEAN extract_tftp_info(CHAR8 *url) { - char *start, *end; - char ip6str[128]; - char *template = "/grubx64.efi"; + CHAR8 *start, *end; + CHAR8 ip6str[40]; + CHAR8 *template = (CHAR8 *)translate_slashes(DEFAULT_LOADER_CHAR); if (strncmp((UINT8 *)url, (UINT8 *)"tftp://", 7)) { Print(L"URLS MUST START WITH tftp://\n"); @@ -245,26 +261,28 @@ static BOOLEAN extract_tftp_info(char *url) end = start; while ((*end != '\0') && (*end != ']')) { end++; + if (end - start > 39) { + Print(L"TFTP URL includes malformed IPv6 address\n"); + return FALSE; + } } if (end == '\0') { Print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n"); return FALSE; } - *end = '\0'; - memset(ip6str, 0, 128); - memcpy(ip6str, start, strlen((UINT8 *)start)); - *end = ']'; + memset(ip6str, 0, 40); + memcpy(ip6str, start, end - start); end++; memcpy(&tftp_addr.v6, str2ip6(ip6str), 16); - full_path = AllocatePool(strlen((UINT8 *)end)+strlen((UINT8 *)template)+1); + full_path = AllocateZeroPool(strlen(end)+strlen(template)+1); if (!full_path) return FALSE; - memset(full_path, 0, strlen((UINT8 *)end)+strlen((UINT8 *)template)); - memcpy(full_path, end, strlen((UINT8 *)end)); - end = strrchr(full_path, '/'); + memcpy(full_path, end, strlen(end)); + end = (CHAR8 *)strrchr((char *)full_path, '/'); if (!end) - end = full_path; - memcpy(end, template, strlen((UINT8 *)template)); + end = (CHAR8 *)full_path; + memcpy(end, template, strlen(template)); + end[strlen(template)] = '\0'; return TRUE; } @@ -272,32 +290,30 @@ static BOOLEAN extract_tftp_info(char *url) static EFI_STATUS parseDhcp6() { EFI_PXE_BASE_CODE_DHCPV6_PACKET *packet = (EFI_PXE_BASE_CODE_DHCPV6_PACKET *)&pxe->Mode->DhcpAck.Raw; - char *bootfile_url; - + CHAR8 *bootfile_url; bootfile_url = get_v6_bootfile_url(packet); - if (extract_tftp_info(bootfile_url) == FALSE) - return EFI_NOT_FOUND; if (!bootfile_url) return EFI_NOT_FOUND; + if (extract_tftp_info(bootfile_url) == FALSE) { + FreePool(bootfile_url); + return EFI_NOT_FOUND; + } + FreePool(bootfile_url); return EFI_SUCCESS; } static EFI_STATUS parseDhcp4() { - char *template = "/grubx64.efi"; - char *tmp = AllocatePool(16); + CHAR8 *template = (CHAR8 *)DEFAULT_LOADER_CHAR; + full_path = AllocateZeroPool(strlen(template)+1); - - if (!tmp) + if (!full_path) return EFI_OUT_OF_RESOURCES; - memcpy(&tftp_addr.v4, pxe->Mode->DhcpAck.Dhcpv4.BootpSiAddr, 4); - memcpy(tmp, template, 12); - tmp[13] = '\0'; - full_path = tmp; + memcpy(full_path, template, strlen(template)); /* Note we don't capture the filename option here because we know its shim.efi * We instead assume the filename at the end of the path is going to be grubx64.efi @@ -326,7 +342,7 @@ EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle) return rc; } -EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINTN *bufsiz) +EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINT64 *bufsiz) { EFI_STATUS rc; EFI_PXE_BASE_CODE_TFTP_OPCODE read = EFI_PXE_BASE_CODE_TFTP_READ_FILE; @@ -344,7 +360,7 @@ EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINTN *bufs try_again: rc = uefi_call_wrapper(pxe->Mtftp, 10, pxe, read, *buffer, overwrite, - &bufsiz, &blksz, &tftp_addr, full_path, NULL, nobuffer); + bufsiz, &blksz, &tftp_addr, full_path, NULL, nobuffer); if (rc == EFI_BUFFER_TOO_SMALL) { /* try again, doubling buf size */ @@ -356,6 +372,8 @@ try_again: goto try_again; } + if (rc != EFI_SUCCESS && *buffer) { + FreePool(*buffer); + } return rc; - } diff --git a/replacements.c b/replacements.c new file mode 100644 index 0000000..bac5e5d --- /dev/null +++ b/replacements.c @@ -0,0 +1,147 @@ +/* + * shim - trivial UEFI first-stage bootloader + * + * Copyright 2012 Red Hat, Inc + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Significant portions of this code are derived from Tianocore + * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel + * Corporation. + */ + +/* Chemical agents lend themselves to covert use in sabotage against + * which it is exceedingly difficult to visualize any really effective + * defense... I will not dwell upon this use of CBW because, as one + * pursues the possibilities of such covert uses, one discovers that the + * scenarios resemble that in which the components of a nuclear weapon + * are smuggled into New York City and assembled in the basement of the + * Empire State Building. + * In other words, once the possibility is recognized to exist, about + * all that one can do is worry about it. + * -- Dr. Ivan L Bennett, Jr., testifying before the Subcommittee on + * National Security Policy and Scientific Developments, November 20, + * 1969. + */ + +#include +#include +#include +#include "shim.h" +#include "replacements.h" + +/* oh for fuck's sakes.*/ +#ifndef EFI_SECURITY_VIOLATION +#define EFI_SECURITY_VIOLATION 26 +#endif + +static EFI_SYSTEM_TABLE *systab; + +static typeof(systab->BootServices->StartImage) system_start_image; +static typeof(systab->BootServices->Exit) system_exit; +static typeof(systab->BootServices->ExitBootServices) system_exit_boot_services; + +extern UINT8 insecure_mode; + +void +unhook_system_services(void) +{ + if (insecure_mode) + return; + systab->BootServices->Exit = system_exit; + systab->BootServices->StartImage = system_start_image; + systab->BootServices->ExitBootServices = system_exit_boot_services; +} + +static EFI_STATUS EFIAPI +start_image(EFI_HANDLE image_handle, UINTN *exit_data_size, CHAR16 **exit_data) +{ + EFI_STATUS status; + unhook_system_services(); + status = systab->BootServices->StartImage(image_handle, exit_data_size, exit_data); + if (EFI_ERROR(status)) + hook_system_services(systab); + return status; +} + +static EFI_STATUS EFIAPI +exit_boot_services(EFI_HANDLE image_key, UINTN map_key) +{ + if (loader_is_participating || verification_method == VERIFIED_BY_HASH) { + unhook_system_services(); + EFI_STATUS status; + status = systab->BootServices->ExitBootServices(image_key, map_key); + if (status != EFI_SUCCESS) + hook_system_services(systab); + return status; + } + + Print(L"Bootloader has not verified loaded image.\n"); + Print(L"System is compromised. halting.\n"); + systab->BootServices->Stall(5000000); + systab->RuntimeServices->ResetSystem(EfiResetShutdown, EFI_SECURITY_VIOLATION, 0, NULL); + return EFI_SECURITY_VIOLATION; +} + +static EFI_STATUS EFIAPI +exit(EFI_HANDLE ImageHandle, EFI_STATUS ExitStatus, + UINTN ExitDataSize, CHAR16 *ExitData) +{ + EFI_STATUS status; + unhook_system_services(); + + status = systab->BootServices->Exit(ImageHandle, ExitStatus, ExitDataSize, ExitData); + if (EFI_ERROR(status)) + hook_system_services(systab); + return status; +} + + +void +hook_system_services(EFI_SYSTEM_TABLE *local_systab) +{ + if (insecure_mode) + return; + systab = local_systab; + + /* We need to hook various calls to make this work... */ + + /* we need StartImage() so that we can allow chain booting to an + * image trusted by the firmware */ + system_start_image = systab->BootServices->StartImage; + systab->BootServices->StartImage = start_image; + + /* we need to hook ExitBootServices() so a) we can enforce the policy + * and b) we can unwrap when we're done. */ + system_exit_boot_services = systab->BootServices->ExitBootServices; + systab->BootServices->ExitBootServices = exit_boot_services; + + /* we need to hook Exit() so that we can allow users to quit the + * bootloader and still e.g. start a new one or run an internal + * shell. */ + system_exit = systab->BootServices->Exit; + systab->BootServices->Exit = exit; +} diff --git a/replacements.h b/replacements.h new file mode 100644 index 0000000..5b57bc2 --- /dev/null +++ b/replacements.h @@ -0,0 +1,44 @@ +/* + * Copyright 2013 Red Hat, Inc + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef SHIM_REPLACEMENTS_H +#define SHIM_REPLACEMENTS_H 1 + +typedef enum { + VERIFIED_BY_NOTHING, + VERIFIED_BY_CERT, + VERIFIED_BY_HASH +} verification_method_t; + +extern verification_method_t verification_method; +extern int loader_is_participating; + +extern void hook_system_services(EFI_SYSTEM_TABLE *local_systab); +extern void unhook_system_services(void); + +#endif /* SHIM_REPLACEMENTS_H */ diff --git a/shim.c b/shim.c index 94b9710..23dd0ee 100644 --- a/shim.c +++ b/shim.c @@ -38,12 +38,18 @@ #include #include "PeImage.h" #include "shim.h" -#include "signature.h" #include "netboot.h" #include "shim_cert.h" +#include "replacements.h" #include "ucs2.h" -#define DEFAULT_LOADER L"\\grub.efi" +#include "guid.h" +#include "variables.h" +#include "efiauthenticated.h" +#include "security_policy.h" +#include "console.h" +#include "version.h" + #define FALLBACK L"\\fallback.efi" #define MOK_MANAGER L"\\MokManager.efi" @@ -54,17 +60,33 @@ static CHAR16 *second_stage; static void *load_options; static UINT32 load_options_size; +EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }; + /* * The vendor certificate used for validating the second stage loader */ -extern UINT8 vendor_cert[]; -extern UINT32 vendor_cert_size; -extern EFI_SIGNATURE_LIST *vendor_dbx; -extern UINT32 vendor_dbx_size; +extern struct { + UINT32 vendor_cert_size; + UINT32 vendor_dbx_size; + UINT32 vendor_cert_offset; + UINT32 vendor_dbx_offset; +} cert_table; + +UINT32 vendor_cert_size; +UINT32 vendor_dbx_size; +UINT8 *vendor_cert; +UINT8 *vendor_dbx; + +/* + * indicator of how an image has been verified + */ +verification_method_t verification_method; +int loader_is_participating; #define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }} -static UINT8 insecure_mode; +UINT8 insecure_mode; +UINT8 ignore_db; typedef enum { DATA_FOUND, @@ -77,32 +99,6 @@ typedef struct { UINT8 *Mok; } MokListNode; -static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes, - UINTN *size, void **buffer) -{ - EFI_STATUS efi_status; - char allocate = !(*size); - - efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, - attributes, size, buffer); - - if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) { - return efi_status; - } - - *buffer = AllocatePool(*size); - - if (!*buffer) { - Print(L"Unable to allocate variable buffer\n"); - return EFI_OUT_OF_RESOURCES; - } - - efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, - attributes, size, *buffer); - - return efi_status; -} - /* * Perform basic bounds checking of the intra-image pointers */ @@ -147,10 +143,18 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context, Adjust = (UINT64)data - context->ImageAddress; + if (Adjust == 0) + return EFI_SUCCESS; + while (RelocBase < RelocBaseEnd) { Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION)); - RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); + if ((RelocBase->SizeOfBlock == 0) || (RelocBase->SizeOfBlock > context->RelocDir->Size)) { + Print(L"Reloc block size is invalid\n"); + return EFI_UNSUPPORTED; + } + + RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock); if ((void *)RelocEnd < data || (void *)RelocEnd > ImageEnd) { Print(L"Reloc entry overflows binary\n"); return EFI_UNSUPPORTED; @@ -226,11 +230,11 @@ static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList, EFI_SIGNATURE_DATA *Cert; UINTN CertCount, Index; BOOLEAN IsFound = FALSE; - EFI_GUID CertType = EfiCertX509Guid; + EFI_GUID CertType = X509_GUID; while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { if (CompareGuid (&CertList->SignatureType, &CertType) == 0) { - CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize; + CertCount = (CertList->SignatureListSize - sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); for (Index = 0; Index < CertCount; Index++) { IsFound = AuthenticodeVerify (data->CertData, @@ -266,15 +270,14 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid, EFI_STATUS efi_status; EFI_SIGNATURE_LIST *CertList; UINTN dbsize = 0; - UINT32 attributes; - void *db; + UINT8 *db; - efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db); + efi_status = get_variable(dbname, &db, &dbsize, guid); if (efi_status != EFI_SUCCESS) return VAR_NOT_FOUND; - CertList = db; + CertList = (EFI_SIGNATURE_LIST *)db; rc = check_db_cert_in_ram(CertList, dbsize, data, hash); @@ -295,7 +298,7 @@ static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList, BOOLEAN IsFound = FALSE; while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { - CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize; + CertCount = (CertList->SignatureListSize -sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize; Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); if (CompareGuid(&CertList->SignatureType, &CertType) == 0) { for (Index = 0; Index < CertCount; Index++) { @@ -332,17 +335,16 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, { EFI_STATUS efi_status; EFI_SIGNATURE_LIST *CertList; - UINT32 attributes; UINTN dbsize = 0; - void *db; + UINT8 *db; - efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db); + efi_status = get_variable(dbname, &db, &dbsize, guid); if (efi_status != EFI_SUCCESS) { return VAR_NOT_FOUND; } - CertList = db; + CertList = (EFI_SIGNATURE_LIST *)db; CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data, SignatureSize, CertType); @@ -359,31 +361,39 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *sha256hash, UINT8 *sha1hash) { EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; + EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_dbx; - if (check_db_hash_in_ram(vendor_dbx, vendor_dbx_size, sha256hash, - SHA256_DIGEST_SIZE, EfiHashSha256Guid) == + if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha256hash, + SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) == DATA_FOUND) return EFI_ACCESS_DENIED; - if (check_db_hash_in_ram(vendor_dbx, vendor_dbx_size, sha1hash, - SHA1_DIGEST_SIZE, EfiHashSha1Guid) == + if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha1hash, + SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) == DATA_FOUND) return EFI_ACCESS_DENIED; - if (check_db_cert_in_ram(vendor_dbx, vendor_dbx_size, cert, - sha256hash) == DATA_FOUND) + if (cert && check_db_cert_in_ram(dbx, vendor_dbx_size, cert, + sha256hash) == DATA_FOUND) return EFI_ACCESS_DENIED; if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE, - EfiHashSha256Guid) == DATA_FOUND) + EFI_CERT_SHA256_GUID) == DATA_FOUND) return EFI_ACCESS_DENIED; if (check_db_hash(L"dbx", secure_var, sha1hash, SHA1_DIGEST_SIZE, - EfiHashSha1Guid) == DATA_FOUND) + EFI_CERT_SHA1_GUID) == DATA_FOUND) return EFI_ACCESS_DENIED; - if (check_db_cert(L"dbx", secure_var, cert, sha256hash) == DATA_FOUND) + if (cert && check_db_cert(L"dbx", secure_var, cert, sha256hash) == + DATA_FOUND) return EFI_ACCESS_DENIED; return EFI_SUCCESS; } +static void update_verification_method(verification_method_t method) +{ + if (verification_method == VERIFIED_BY_NOTHING) + verification_method = method; +} + /* * Check whether the binary signature or hash are present in db or MokList */ @@ -393,20 +403,40 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; EFI_GUID shim_var = SHIM_LOCK_GUID; - if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE, - EfiHashSha256Guid) == DATA_FOUND) - return EFI_SUCCESS; - if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE, - EfiHashSha1Guid) == DATA_FOUND) - return EFI_SUCCESS; - if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE, - EfiHashSha256Guid) == DATA_FOUND) - return EFI_SUCCESS; - if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND) - return EFI_SUCCESS; - if (check_db_cert(L"MokList", shim_var, cert, sha256hash) == DATA_FOUND) - return EFI_SUCCESS; + if (!ignore_db) { + if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE, + EFI_CERT_SHA256_GUID) == DATA_FOUND) { + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } + if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE, + EFI_CERT_SHA1_GUID) == DATA_FOUND) { + verification_method = VERIFIED_BY_HASH; + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } + if (cert && check_db_cert(L"db", secure_var, cert, sha256hash) + == DATA_FOUND) { + verification_method = VERIFIED_BY_CERT; + update_verification_method(VERIFIED_BY_CERT); + return EFI_SUCCESS; + } + } + if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE, + EFI_CERT_SHA256_GUID) == DATA_FOUND) { + verification_method = VERIFIED_BY_HASH; + update_verification_method(VERIFIED_BY_HASH); + return EFI_SUCCESS; + } + if (cert && check_db_cert(L"MokList", shim_var, cert, sha256hash) == + DATA_FOUND) { + verification_method = VERIFIED_BY_CERT; + update_verification_method(VERIFIED_BY_CERT); + return EFI_SUCCESS; + } + + update_verification_method(VERIFIED_BY_NOTHING); return EFI_ACCESS_DENIED; } @@ -418,27 +448,38 @@ static BOOLEAN secure_mode (void) { EFI_STATUS status; EFI_GUID global_var = EFI_GLOBAL_VARIABLE; - UINTN charsize = sizeof(char); + UINTN len; + UINT8 *Data; UINT8 sb, setupmode; - UINT32 attributes; if (insecure_mode) return FALSE; - status = get_variable(L"SecureBoot", global_var, &attributes, &charsize, - (void *)&sb); + status = get_variable(L"SecureBoot", &Data, &len, global_var); + if (status != EFI_SUCCESS) { + if (verbose) + console_notify(L"Secure boot not enabled\n"); + return FALSE; + } + sb = *Data; + FreePool(Data); - /* FIXME - more paranoia here? */ - if (status != EFI_SUCCESS || sb != 1) { - Print(L"Secure boot not enabled\n"); + if (sb != 1) { + if (verbose) + console_notify(L"Secure boot not enabled\n"); return FALSE; } - status = get_variable(L"SetupMode", global_var, &attributes, &charsize, - (void *)&setupmode); + status = get_variable(L"SetupMode", &Data, &len, global_var); + if (status != EFI_SUCCESS) + return TRUE; - if (status == EFI_SUCCESS && setupmode == 1) { - Print(L"Platform is in setup mode\n"); + setupmode = *Data; + FreePool(Data); + + if (setupmode == 1) { + if (verbose) + console_notify(L"Platform is in setup mode\n"); return FALSE; } @@ -622,12 +663,12 @@ done: static EFI_STATUS verify_mok (void) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS status = EFI_SUCCESS; - void *MokListData = NULL; + UINT8 *MokListData = NULL; UINTN MokListDataSize = 0; UINT32 attributes; - status = get_variable(L"MokList", shim_lock_guid, &attributes, - &MokListDataSize, &MokListData); + status = get_variable_attr(L"MokList", &MokListData, &MokListDataSize, + shim_lock_guid, &attributes); if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { Print(L"MokList is compromised!\nErase all keys in MokList!\n"); @@ -638,6 +679,9 @@ static EFI_STATUS verify_mok (void) { return status; } + if (MokListData) + FreePool(MokListData); + return EFI_SUCCESS; } @@ -650,25 +694,24 @@ static EFI_STATUS verify_buffer (char *data, int datasize, UINT8 sha256hash[SHA256_DIGEST_SIZE]; UINT8 sha1hash[SHA1_DIGEST_SIZE]; EFI_STATUS status = EFI_ACCESS_DENIED; - WIN_CERTIFICATE_EFI_PKCS *cert; + WIN_CERTIFICATE_EFI_PKCS *cert = NULL; unsigned int size = datasize; - if (context->SecDir->Size == 0) { - Print(L"Empty security header\n"); - return EFI_INVALID_PARAMETER; - } + if (context->SecDir->Size != 0) { + cert = ImageAddress (data, size, + context->SecDir->VirtualAddress); - cert = ImageAddress (data, size, context->SecDir->VirtualAddress); + if (!cert) { + Print(L"Certificate located outside the image\n"); + return EFI_INVALID_PARAMETER; + } - if (!cert) { - Print(L"Certificate located outside the image\n"); - return EFI_INVALID_PARAMETER; - } - - if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { - Print(L"Unsupported certificate type %x\n", - cert->Hdr.wCertificateType); - return EFI_UNSUPPORTED; + if (cert->Hdr.wCertificateType != + WIN_CERT_TYPE_PKCS_SIGNED_DATA) { + Print(L"Unsupported certificate type %x\n", + cert->Hdr.wCertificateType); + return EFI_UNSUPPORTED; + } } status = generate_hash(data, datasize, context, sha256hash, sha1hash); @@ -696,38 +739,34 @@ static EFI_STATUS verify_buffer (char *data, int datasize, * databases */ status = check_whitelist(cert, sha256hash, sha1hash); - - if (status == EFI_SUCCESS) { - Print(L"Binary is whitelisted\n"); + if (status == EFI_SUCCESS) return status; - } - /* - * Check against the shim build key - */ - if (AuthenticodeVerify(cert->CertData, + if (cert) { + /* + * Check against the shim build key + */ + if (AuthenticodeVerify(cert->CertData, context->SecDir->Size - sizeof(cert->Hdr), shim_cert, sizeof(shim_cert), sha256hash, SHA256_DIGEST_SIZE)) { - status = EFI_SUCCESS; - Print(L"Binary is verified by the vendor certificate\n"); - return status; - } + status = EFI_SUCCESS; + return status; + } - /* - * And finally, check against shim's built-in key - */ - if (AuthenticodeVerify(cert->CertData, + /* + * And finally, check against shim's built-in key + */ + if (AuthenticodeVerify(cert->CertData, context->SecDir->Size - sizeof(cert->Hdr), vendor_cert, vendor_cert_size, sha256hash, SHA256_DIGEST_SIZE)) { - status = EFI_SUCCESS; - Print(L"Binary is verified by the vendor certificate\n"); - return status; + status = EFI_SUCCESS; + return status; + } } - Print(L"Invalid signature\n"); status = EFI_ACCESS_DENIED; return status; @@ -741,6 +780,7 @@ static EFI_STATUS read_header(void *data, unsigned int datasize, { EFI_IMAGE_DOS_HEADER *DosHdr = data; EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data; + unsigned long HeaderWithoutDataDir, SectionHeaderOffset; if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) { Print(L"Invalid image\n"); @@ -750,6 +790,37 @@ static EFI_STATUS read_header(void *data, unsigned int datasize, if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew); + if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES + < PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes) { + Print(L"Image header too small\n"); + return EFI_UNSUPPORTED; + } + + HeaderWithoutDataDir = sizeof (EFI_IMAGE_OPTIONAL_HEADER64) + - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES; + if (((UINT32)PEHdr->Pe32Plus.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) != + PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes + * sizeof (EFI_IMAGE_DATA_DIRECTORY)) { + Print(L"Image header overflows data directory\n"); + return EFI_UNSUPPORTED; + } + + SectionHeaderOffset = DosHdr->e_lfanew + + sizeof (UINT32) + + sizeof (EFI_IMAGE_FILE_HEADER) + + PEHdr->Pe32Plus.FileHeader.SizeOfOptionalHeader; + if ((PEHdr->Pe32Plus.OptionalHeader.SizeOfImage - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER + <= PEHdr->Pe32Plus.FileHeader.NumberOfSections) { + Print(L"Image sections overflow image size\n"); + return EFI_UNSUPPORTED; + } + + if ((PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER + < (UINT32)PEHdr->Pe32Plus.FileHeader.NumberOfSections) { + Print(L"Image sections overflow section headers\n"); + return EFI_UNSUPPORTED; + } + if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) { Print(L"Invalid image\n"); return EFI_UNSUPPORTED; @@ -826,9 +897,12 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, if (secure_mode ()) { efi_status = verify_buffer(data, datasize, &context); - if (efi_status != EFI_SUCCESS) { - Print(L"Verification failed\n"); + if (EFI_ERROR(efi_status)) { + console_error(L"Verification failed", efi_status); return efi_status; + } else { + if (verbose) + console_notify(L"Verification succeeded"); } } @@ -859,6 +933,12 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, return EFI_UNSUPPORTED; } + if (Section->VirtualAddress < context.SizeOfHeaders || + Section->PointerToRawData < context.SizeOfHeaders) { + Print(L"Section is inside image headers\n"); + return EFI_UNSUPPORTED; + } + if (Section->SizeOfRawData > 0) CopyMem(base, data + Section->PointerToRawData, size); @@ -950,7 +1030,12 @@ should_use_fallback(EFI_HANDLE image_handle) rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK, EFI_FILE_MODE_READ, 0); if (EFI_ERROR(rc)) { - Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK, rc); + /* Do not print the error here - this is an acceptable case + * for removable media, where we genuinely don't want + * fallback.efi to exist. + * Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK, + * rc); + */ uefi_call_wrapper(vh->Close, 1, vh); return 0; } @@ -969,7 +1054,7 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath, { EFI_DEVICE_PATH *devpath; EFI_HANDLE device; - int i; + int i, j, last = -1; unsigned int pathlen = 0; EFI_STATUS efi_status = EFI_SUCCESS; CHAR16 *bootpath; @@ -981,15 +1066,38 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath, pathlen = StrLen(bootpath); - for (i=pathlen; i>0; i--) { - if (bootpath[i] == '\\') - break; + /* + * DevicePathToStr() concatenates two nodes with '/'. + * Convert '/' to '\\'. + */ + for (i = 0; i < pathlen; i++) { + if (bootpath[i] == '/') + bootpath[i] = '\\'; } - bootpath[i+1] = '\0'; + for (i=pathlen; i>0; i--) { + if (bootpath[i] == '\\' && bootpath[i-1] == '\\') + bootpath[i] = '/'; + else if (last == -1 && bootpath[i] == '\\') + last = i; + } - if (i == 0 || bootpath[i-i] == '\\') - bootpath[i] = '\0'; + if (last == -1 && bootpath[0] == '\\') + last = 0; + bootpath[last+1] = '\0'; + + if (last > 0) { + for (i = 0, j = 0; bootpath[i] != '\0'; i++) { + if (bootpath[i] != '/') { + bootpath[j] = bootpath[i]; + j++; + } + } + bootpath[j] = '\0'; + } + + while (*ImagePath == '\\') + ImagePath++; *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath)); @@ -1007,6 +1115,8 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath, *grubpath = FileDevicePath(device, *PathName); error: + FreePool(bootpath); + return efi_status; } @@ -1143,6 +1253,8 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size) EFI_STATUS status; PE_COFF_LOADER_IMAGE_CONTEXT context; + loader_is_participating = 1; + if (!secure_mode()) return EFI_SUCCESS; @@ -1167,7 +1279,7 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) EFI_DEVICE_PATH *path; CHAR16 *PathName = NULL; void *sourcebuffer = NULL; - UINTN sourcesize = 0; + UINT64 sourcesize = 0; void *data = NULL; int datasize; @@ -1236,6 +1348,8 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) goto done; } + loader_is_participating = 0; + /* * The binary is trusted and relocated. Run it */ @@ -1282,27 +1396,59 @@ EFI_STATUS mirror_mok_list() { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; - UINT32 attributes; - void *Data = NULL; + UINT8 *Data = NULL; UINTN DataSize = 0; + void *FullData = NULL; + UINTN FullDataSize = 0; + EFI_SIGNATURE_LIST *CertList = NULL; + EFI_SIGNATURE_DATA *CertData = NULL; + uint8_t *p = NULL; - efi_status = get_variable(L"MokList", shim_lock_guid, &attributes, - &DataSize, &Data); + efi_status = get_variable(L"MokList", &Data, &DataSize, shim_lock_guid); + if (efi_status != EFI_SUCCESS) + DataSize = 0; - if (efi_status != EFI_SUCCESS) { - goto done; + FullDataSize = DataSize + + sizeof (*CertList) + + sizeof (EFI_GUID) + + vendor_cert_size + ; + FullData = AllocatePool(FullDataSize); + if (!FullData) { + Print(L"Failed to allocate space for MokListRT\n"); + return EFI_OUT_OF_RESOURCES; } + p = FullData; + + if (efi_status == EFI_SUCCESS && DataSize > 0) { + CopyMem(p, Data, DataSize); + p += DataSize; + } + CertList = (EFI_SIGNATURE_LIST *)p; + p += sizeof (*CertList); + CertData = (EFI_SIGNATURE_DATA *)p; + p += sizeof (EFI_GUID); + + CertList->SignatureType = EFI_CERT_X509_GUID; + CertList->SignatureListSize = vendor_cert_size + + sizeof (*CertList) + + sizeof (*CertData) + -1; + CertList->SignatureHeaderSize = 0; + CertList->SignatureSize = vendor_cert_size + sizeof (EFI_GUID); + + CertData->SignatureOwner = SHIM_LOCK_GUID; + CopyMem(p, vendor_cert, vendor_cert_size); efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT", &shim_lock_guid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, - DataSize, Data); + FullDataSize, FullData); if (efi_status != EFI_SUCCESS) { Print(L"Failed to set MokListRT %d\n", efi_status); } -done: return efi_status; } @@ -1337,7 +1483,7 @@ EFI_STATUS check_mok_request(EFI_HANDLE image_handle) if (check_var(L"MokNew") || check_var(L"MokSB") || check_var(L"MokPW") || check_var(L"MokAuth") || - check_var(L"MokDel")) { + check_var(L"MokDel") || check_var(L"MokDB")) { efi_status = start_image(image_handle, MOK_MANAGER); if (efi_status != EFI_SUCCESS) { @@ -1357,12 +1503,15 @@ static EFI_STATUS check_mok_sb (void) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS status = EFI_SUCCESS; - void *MokSBState = NULL; + UINT8 *MokSBState = NULL; UINTN MokSBStateSize = 0; UINT32 attributes; - status = get_variable(L"MokSBState", shim_lock_guid, &attributes, - &MokSBStateSize, &MokSBState); + insecure_mode = 0; + ignore_db = 0; + + status = get_variable_attr(L"MokSBState", &MokSBState, &MokSBStateSize, + shim_lock_guid, &attributes); if (status != EFI_SUCCESS) return EFI_ACCESS_DENIED; @@ -1383,9 +1532,76 @@ static EFI_STATUS check_mok_sb (void) } } + FreePool(MokSBState); + return status; } +/* + * Verify that MokDBState is valid, and if appropriate set ignore db mode + */ + +static EFI_STATUS check_mok_db (void) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS status = EFI_SUCCESS; + UINT8 *MokDBState = NULL; + UINTN MokDBStateSize = 0; + UINT32 attributes; + + status = get_variable_attr(L"MokDBState", &MokDBState, &MokDBStateSize, + shim_lock_guid, &attributes); + + if (status != EFI_SUCCESS) + return EFI_ACCESS_DENIED; + + ignore_db = 0; + + /* + * Delete and ignore the variable if it's been set from or could be + * modified by the OS + */ + if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { + Print(L"MokDBState is compromised! Clearing it\n"); + if (LibDeleteVariable(L"MokDBState", &shim_lock_guid) != EFI_SUCCESS) { + Print(L"Failed to erase MokDBState\n"); + } + status = EFI_ACCESS_DENIED; + } else { + if (*(UINT8 *)MokDBState == 1) { + ignore_db = 1; + } + } + + FreePool(MokDBState); + + return status; +} + +static EFI_STATUS mok_ignore_db() +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status = EFI_SUCCESS; + UINT8 Data = 1; + UINTN DataSize = sizeof(UINT8); + + check_mok_db(); + + if (ignore_db) { + efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokIgnoreDB", + &shim_lock_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS, + DataSize, (void *)&Data); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to set MokIgnoreDB %d\n", efi_status); + } + } + + return efi_status; + +} + /* * Check the load options to specify the second stage loader */ @@ -1471,6 +1687,13 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) EFI_HANDLE handle = NULL; EFI_STATUS efi_status; + verification_method = VERIFIED_BY_NOTHING; + + vendor_cert_size = cert_table.vendor_cert_size; + vendor_dbx_size = cert_table.vendor_dbx_size; + vendor_cert = (UINT8 *)&cert_table + cert_table.vendor_cert_offset; + vendor_dbx = (UINT8 *)&cert_table + cert_table.vendor_dbx_offset; + /* * Set up the shim lock protocol so that grub and MokManager can * call back in and use shim functions @@ -1486,6 +1709,11 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) */ InitializeLib(image_handle, systab); + setup_console(1); + setup_verbosity(); + + dprinta(shim_version); + /* Set the second stage loader */ set_second_stage (image_handle); @@ -1498,17 +1726,35 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) /* * Tell the user that we're in insecure mode if necessary */ - if (insecure_mode) { + if (!secure_mode()) { Print(L"Booting in insecure mode\n"); uefi_call_wrapper(BS->Stall, 1, 2000000); + } else { + /* + * Install our hooks for ExitBootServices() and StartImage() + */ + hook_system_services(systab); + loader_is_participating = 0; } /* * Install the protocol */ - uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle, - &shim_lock_guid, EFI_NATIVE_INTERFACE, + efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4, + &handle, &shim_lock_guid, EFI_NATIVE_INTERFACE, &shim_lock_interface); + if (EFI_ERROR(efi_status)) { + console_error(L"Could not install security protocol", + efi_status); + return efi_status; + } + +#if defined(OVERRIDE_SECURITY_POLICY) + /* + * Install the security protocol hook + */ + security_policy_install(shim_verify); +#endif /* * Enter MokManager if necessary @@ -1521,23 +1767,44 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) */ efi_status = mirror_mok_list(); + /* + * Create the runtime MokIgnoreDB variable so the kernel can make + * use of it + */ + efi_status = mok_ignore_db(); + /* * Hand over control to the second stage bootloader */ efi_status = init_grub(image_handle); +#if defined(OVERRIDE_SECURITY_POLICY) + /* + * Clean up the security protocol hook + */ + security_policy_uninstall(); +#endif + /* * If we're back here then clean everything up before exiting */ uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, &shim_lock_guid, &shim_lock_interface); + + /* + * Remove our hooks from system services. + */ + unhook_system_services(); + /* * Free the space allocated for the alternative 2nd stage loader */ if (load_options_size > 0) FreePool(second_stage); + setup_console(0); + return efi_status; } diff --git a/shim.h b/shim.h index 0819259..f7a766a 100644 --- a/shim.h +++ b/shim.h @@ -1,7 +1,6 @@ #include "PeImage.h" -#define SHIM_LOCK_GUID \ - { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} } +extern EFI_GUID SHIM_LOCK_GUID; INTERFACE_DECL(_SHIM_LOCK); diff --git a/signature.h b/signature.h deleted file mode 100644 index 722dbe6..0000000 --- a/signature.h +++ /dev/null @@ -1,43 +0,0 @@ -#define SHA256_DIGEST_SIZE 32 - -EFI_GUID EfiHashSha1Guid = { 0x826ca512, 0xcf10, 0x4ac9, {0xb1, 0x87, 0xbe, 0x1, 0x49, 0x66, 0x31, 0xbd }}; -EFI_GUID EfiHashSha256Guid = { 0xc1c41626, 0x504c, 0x4092, {0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 }}; -EFI_GUID EfiCertX509Guid = { 0xa5c059a1, 0x94e4, 0x4aa7, {0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 }}; - -typedef struct { - /// - /// An identifier which identifies the agent which added the signature to the list. - /// - EFI_GUID SignatureOwner; - /// - /// The format of the signature is defined by the SignatureType. - /// - UINT8 SignatureData[1]; -} __attribute__ ((packed)) EFI_SIGNATURE_DATA; - -typedef struct { - /// - /// Type of the signature. GUID signature types are defined in below. - /// - EFI_GUID SignatureType; - /// - /// Total size of the signature list, including this header. - /// - UINT32 SignatureListSize; - /// - /// Size of the signature header which precedes the array of signatures. - /// - UINT32 SignatureHeaderSize; - /// - /// Size of each signature. - /// - UINT32 SignatureSize; - /// - /// Header before the array of signatures. The format of this header is specified - /// by the SignatureType. - /// UINT8 SignatureHeader[SignatureHeaderSize]; - /// - /// An array of signatures. Each signature is SignatureSize bytes in length. - /// EFI_SIGNATURE_DATA Signatures[][SignatureSize]; - /// -} __attribute__ ((packed)) EFI_SIGNATURE_LIST; diff --git a/version.c.in b/version.c.in new file mode 100644 index 0000000..9e71970 --- /dev/null +++ b/version.c.in @@ -0,0 +1,8 @@ + +#include "version.h" + +CHAR8 shim_version[] = + "UEFI SHIM\n" + "$Version: @@VERSION@@ $\n" + "$BuildMachine: @@UNAME@@ $\n" + "$Commit: @@COMMIT@@ $\n"; diff --git a/version.h b/version.h new file mode 100644 index 0000000..7fb3d81 --- /dev/null +++ b/version.h @@ -0,0 +1,8 @@ +#ifndef _SHIM_VERSION_H +#define _SHIM_VERSION_H 1 + +#include + +extern CHAR8 shim_version[]; + +#endif /* SHIM_VERSION_H */