From 7316783f35a1c210007951d74f959e003088da8e Mon Sep 17 00:00:00 2001 From: Vladimir 'phcoder' Serbinenko Date: Wed, 23 Dec 2009 17:33:35 +0100 Subject: [PATCH] Crypto module autoloading --- Makefile.in | 5 +- conf/any-emu.rmk | 2 +- conf/common.rmk | 2 +- include/grub/crypto.h | 2 + include/grub/normal.h | 2 + lib/crypto.c | 46 +++++++++---- normal/crypto.c | 153 ++++++++++++++++++++++++++++++++++++++++++ normal/main.c | 1 + util/import_gcry.py | 38 +++++++++-- 9 files changed, 230 insertions(+), 21 deletions(-) create mode 100644 normal/crypto.c diff --git a/Makefile.in b/Makefile.in index 46b380cd5..b28b660da 100644 --- a/Makefile.in +++ b/Makefile.in @@ -169,7 +169,7 @@ endif ### General targets. CLEANFILES += $(pkglib_DATA) $(pkgdata_DATA) po/*.mo -pkglib_DATA += moddep.lst command.lst fs.lst partmap.lst parttool.lst handler.lst video.lst +pkglib_DATA += moddep.lst command.lst fs.lst partmap.lst parttool.lst handler.lst video.lst crypto.lst moddep.lst: $(DEFSYMFILES) $(UNDSYMFILES) genmoddep.awk cat $(DEFSYMFILES) /dev/null \ | $(AWK) -f $(srcdir)/genmoddep.awk $(UNDSYMFILES) > $@ \ @@ -193,6 +193,9 @@ parttool.lst: $(PARTTOOLFILES) video.lst: $(VIDEOFILES) cat $^ /dev/null | sort | uniq > $@ +crypto.lst: lib/libgcrypt-grub/cipher/crypto.lst + cp $^ $@ + ifneq (true, $(MAKEINFO)) info_INFOS += docs/grub.info endif diff --git a/conf/any-emu.rmk b/conf/any-emu.rmk index 451ab3393..a3b3c84d9 100644 --- a/conf/any-emu.rmk +++ b/conf/any-emu.rmk @@ -27,7 +27,7 @@ grub_emu_SOURCES = commands/minicmd.c commands/cat.c commands/cmp.c \ normal/handler.c normal/auth.c lib/crypto.c normal/autofs.c \ normal/completion.c normal/main.c normal/color.c \ normal/menu.c normal/menu_entry.c normal/menu_viewer.c \ - normal/menu_text.c \ + normal/menu_text.c normal/crypto.c \ script/main.c script/execute.c script/function.c \ script/lexer.c script/script.c grub_script.tab.c \ partmap/amiga.c partmap/apple.c partmap/msdos.c partmap/sun.c \ diff --git a/conf/common.rmk b/conf/common.rmk index 81372d20c..64be988b9 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -543,7 +543,7 @@ normal_mod_SOURCES = normal/main.c normal/cmdline.c normal/dyncmd.c \ normal/auth.c normal/autofs.c normal/handler.c \ normal/color.c normal/completion.c normal/datetime.c normal/menu.c \ normal/menu_entry.c normal/menu_text.c normal/menu_viewer.c \ - normal/misc.c + normal/misc.c normal/crypto.c normal_mod_CFLAGS = $(COMMON_CFLAGS) normal_mod_LDFLAGS = $(COMMON_LDFLAGS) diff --git a/include/grub/crypto.h b/include/grub/crypto.h index 3129131cb..48b52ee65 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -270,4 +270,6 @@ grub_password_get (char buf[], unsigned buf_size); /* For indistinguishibility. */ #define GRUB_ACCESS_DENIED grub_error (GRUB_ERR_ACCESS_DENIED, "Access denied.") +extern void (*grub_crypto_autoload_hook) (const char *name); + #endif diff --git a/include/grub/normal.h b/include/grub/normal.h index e55553de5..66b427bcb 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -95,6 +95,8 @@ void read_command_list (void); /* Defined in `autofs.c'. */ void read_fs_list (void); +void read_crypto_list (void); + #ifdef GRUB_UTIL void grub_normal_init (void); diff --git a/lib/crypto.c b/lib/crypto.c index 06104bd4d..021560d6c 100644 --- a/lib/crypto.c +++ b/lib/crypto.c @@ -32,6 +32,8 @@ struct grub_crypto_hmac_handle static gcry_cipher_spec_t *grub_ciphers = NULL; static gcry_md_spec_t *grub_digests = NULL; +void (*grub_crypto_autoload_hook) (const char *name) = NULL; + /* Based on libgcrypt-1.4.4/src/misc.c. */ void grub_burn_stack (grub_size_t size) @@ -91,28 +93,44 @@ const gcry_md_spec_t * grub_crypto_lookup_md_by_name (const char *name) { const gcry_md_spec_t *md; - for (md = grub_digests; md; md = md->next) - if (grub_strcasecmp (name, md->name) == 0) - return md; - return NULL; + int first = 1; + while (1) + { + for (md = grub_digests; md; md = md->next) + if (grub_strcasecmp (name, md->name) == 0) + return md; + if (grub_crypto_autoload_hook && first) + grub_crypto_autoload_hook (name); + else + return NULL; + first = 0; + } } const gcry_cipher_spec_t * grub_crypto_lookup_cipher_by_name (const char *name) { const gcry_cipher_spec_t *ciph; - for (ciph = grub_ciphers; ciph; ciph = ciph->next) + int first = 1; + while (1) { - const char **alias; - if (grub_strcasecmp (name, ciph->name) == 0) - return ciph; - if (!ciph->aliases) - continue; - for (alias = ciph->aliases; *alias; alias++) - if (grub_strcasecmp (name, *alias) == 0) - return ciph; + for (ciph = grub_ciphers; ciph; ciph = ciph->next) + { + const char **alias; + if (grub_strcasecmp (name, ciph->name) == 0) + return ciph; + if (!ciph->aliases) + continue; + for (alias = ciph->aliases; *alias; alias++) + if (grub_strcasecmp (name, *alias) == 0) + return ciph; + } + if (grub_crypto_autoload_hook && first) + grub_crypto_autoload_hook (name); + else + return NULL; + first = 0; } - return NULL; } diff --git a/normal/crypto.c b/normal/crypto.c new file mode 100644 index 000000000..67330272a --- /dev/null +++ b/normal/crypto.c @@ -0,0 +1,153 @@ +/* crypto.c - support crypto autoload */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +struct load_spec +{ + struct load_spec *next; + char *name; + char *modname; +}; + +struct load_spec *crypto_specs = NULL; + +static void +grub_crypto_autoload (const char *name) +{ + struct load_spec *cur; + grub_dl_t mod; + + for (cur = crypto_specs; cur; cur = cur->next) + if (grub_strcasecmp (name, cur->name) == 0) + { + mod = grub_dl_load (cur->modname); + if (mod) + grub_dl_ref (mod); + grub_errno = GRUB_ERR_NONE; + } +} + +static void +grub_crypto_spec_free (void) +{ + struct load_spec *cur, *next; + for (cur = crypto_specs; cur; cur = next) + { + next = cur->next; + grub_free (cur->name); + grub_free (cur->modname); + grub_free (cur); + } + crypto_specs = NULL; +} + + +/* Read the file crypto.lst for auto-loading. */ +void +read_crypto_list (void) +{ + const char *prefix; + char *filename; + grub_file_t file; + char *buf = NULL; + + prefix = grub_env_get ("prefix"); + if (!prefix) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + filename = grub_malloc (grub_strlen (prefix) + sizeof ("/crypto.lst")); + if (!filename) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + grub_sprintf (filename, "%s/crypto.lst", prefix); + file = grub_file_open (filename); + if (!file) + { + grub_errno = GRUB_ERR_NONE; + return; + } + + /* Override previous commands.lst. */ + grub_crypto_spec_free (); + + for (;; grub_free (buf)) + { + char *p, *name; + struct load_spec *cur; + + buf = grub_file_getline (file); + + if (! buf) + break; + + name = buf; + + p = grub_strchr (name, ':'); + if (! p) + continue; + + *p = '\0'; + while (*++p == ' ') + ; + + cur = grub_malloc (sizeof (*cur)); + if (!cur) + { + grub_errno = GRUB_ERR_NONE; + continue; + } + + cur->name = grub_strdup (name); + if (! name) + { + grub_errno = GRUB_ERR_NONE; + grub_free (cur); + continue; + } + + cur->modname = grub_strdup (p); + if (! cur->modname) + { + grub_errno = GRUB_ERR_NONE; + grub_free (cur); + grub_free (cur->name); + continue; + } + cur->next = crypto_specs; + crypto_specs = cur; + } + + grub_file_close (file); + + grub_errno = GRUB_ERR_NONE; + + grub_crypto_autoload_hook = grub_crypto_autoload; +} diff --git a/normal/main.c b/normal/main.c index 3166ea146..189244aa0 100644 --- a/normal/main.c +++ b/normal/main.c @@ -428,6 +428,7 @@ grub_normal_execute (const char *config, int nested, int batch) read_command_list (); read_fs_list (); read_handler_list (); + read_crypto_list (); grub_command_execute ("parser.grub", 0, 0); reader_nested = nested; diff --git a/util/import_gcry.py b/util/import_gcry.py index fe68c85a4..93bf5982f 100644 --- a/util/import_gcry.py +++ b/util/import_gcry.py @@ -61,6 +61,18 @@ mdblocksizes = {"_gcry_digest_spec_crc32" : 64, "_gcry_digest_spec_tiger" : 64, "_gcry_digest_spec_whirlpool" : 64} +cryptolist = open (os.path.join (cipher_dir_out, "crypto.lst"), "w") + +# rijndael is the only cipher using aliases. So no need for mangling, just +# hardcode it +cryptolist.write ("RIJNDAEL: gcry_rijndael\n"); +cryptolist.write ("RIJNDAEL192: gcry_rijndael\n"); +cryptolist.write ("RIJNDAEL256: gcry_rijndael\n"); +cryptolist.write ("AES128: gcry_rijndael\n"); +cryptolist.write ("AES-128: gcry_rijndael\n"); +cryptolist.write ("AES-192: gcry_rijndael\n"); +cryptolist.write ("AES-256: gcry_rijndael\n"); + for cipher_file in cipher_files: infile = os.path.join (cipher_dir_in, cipher_file) outfile = os.path.join (cipher_dir_out, cipher_file) @@ -86,7 +98,15 @@ for cipher_file in cipher_files: skip = False skip2 = False ismd = False + iscryptostart = False iscomma = False + isglue = False + if isc: + modname = cipher_file [0:len(cipher_file) - 2] + if re.match (".*-glue$", modname): + modname = modname.replace ("-glue", "") + isglue = True + modname = "gcry_%s" % modname for line in f: if skip: if line[0] == "}": @@ -96,6 +116,12 @@ for cipher_file in cipher_files: if not re.search (" *};", line) is None: skip2 = False continue + if iscryptostart: + s = re.search (" *\"([A-Z0-9_a-z]*)\"", line) + if not s is None: + sg = s.groups()[0] + cryptolist.write (("%s: %s\n") % (sg, modname)) + iscryptostart = False if ismd: if not re.search (" *};", line) is None: if not mdblocksizes.has_key (mdname): @@ -133,16 +159,20 @@ for cipher_file in cipher_files: continue m = re.match ("gcry_cipher_spec_t", line) if isc and not m is None: + assert (not iscryptostart) ciphername = line [len ("gcry_cipher_spec_t"):].strip () ciphername = re.match("[a-zA-Z0-9_]*",ciphername).group () ciphernames.append (ciphername) + iscryptostart = True m = re.match ("gcry_md_spec_t", line) if isc and not m is None: assert (not ismd) + assert (not iscryptostart) mdname = line [len ("gcry_md_spec_t"):].strip () mdname = re.match("[a-zA-Z0-9_]*",mdname).group () mdnames.append (mdname) ismd = True + iscryptostart = True m = re.match ("static const char \*selftest.*;$", line) if not m is None: fname = line[len ("static const char \*"):] @@ -185,14 +215,11 @@ for cipher_file in cipher_files: continue fw.write (line) if len (ciphernames) > 0 or len (mdnames) > 0: - modname = cipher_file [0:len(cipher_file) - 2] - if re.match (".*-glue$", modname): + if isglue: modfiles = "lib/libgcrypt-grub/cipher/%s lib/libgcrypt-grub/cipher/%s" \ % (cipher_file, cipher_file.replace ("-glue.c", ".c")) - modname = modname.replace ("-glue", "") else: modfiles = "lib/libgcrypt-grub/cipher/%s" % cipher_file - modname = "gcry_%s" % modname chmsg = "(GRUB_MOD_INIT(%s)): New function\n" % modname if nch: chlognew = "%s\n %s" % (chlognew, chmsg) @@ -238,6 +265,9 @@ for cipher_file in cipher_files: chlog = "%s%sSkipped unknown file\n" % (chlog, chlognew) print ("WARNING: unknown file %s" % cipher_file) +cryptolist.close () +chlog = "%s * crypto.lst: New file.\n" % chlog + outfile = os.path.join (cipher_dir_out, "types.h") fw=open (outfile, "w") fw.write ("#include \n")