├── .editorconfig ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── binfmt_elf_signature_verification.c └── certs ├── kernel_key.pem └── x509.genkey /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{c,h}] 4 | indent_style = tab 5 | indent_size = tab 6 | tab_width = 4 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | .*.mk 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020, Jingtang Zhang, Hua Zong. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifneq ($(KERNELRELEASE),) 2 | 3 | obj-m := binfmt_elf_signature_verification.o 4 | 5 | else 6 | 7 | # KDIR := ../ 8 | KDIR := /lib/modules/$(shell uname -r)/build 9 | 10 | all: 11 | make -C $(KDIR) M=$(PWD) modules 12 | clean: 13 | $(RM) *.ko 14 | $(RM) *.o 15 | $(RM) *.mod* 16 | $(RM) *.symvers 17 | $(RM) *.order 18 | $(RM) .*.mk 19 | $(RM) .*.cmd 20 | $(RM) -r .tmp_versions 21 | endif 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # linux-kernel-elf-sig-verify-module 2 | 3 | 🔏 Kernel module for signature verification of ELF files. 4 | 5 | Created by : zSnow && Mr Dk. 6 | 7 | 2020 / 05 / 24 21:06 8 | 9 | --- 10 | 11 | ## Build the kernel module 12 | 13 | By default, the value of `KDIR` in `Makefile` points to the source code directory of **currently running kernel**, on which the kernel module will be installed. 14 | 15 | ```makefile 16 | KDIR := /lib/modules/$(shell uname -r)/build 17 | ``` 18 | 19 | Also, you can build the module for one kernel on another kernel by overriding the `KDIR` variable. Suppose your directory is a submodule of [linux-kernel-elf-sig-verify](https://github.com/NUAA-WatchDog/linux-kernel-elf-sig-verify) under its directory like `linux-kernel-elf-sig-verify/linux-kernel-elf-sig-verify-module`, then you can modify `KDIR` to: 20 | 21 | ```makefile 22 | KDIR := ../ 23 | ``` 24 | 25 | Then, build the kernel module by `make` command: 26 | 27 | ```console 28 | $ make 29 | make -C /lib/modules/5.3.0-53-generic/build M=/home/mrdrivingduck/Desktop/linux-kernel-elf-sig-verify/linux-kernel-elf-sig-verify-module modules 30 | make[1]: Entering directory '/usr/src/linux-headers-5.3.0-53-generic' 31 | CC [M] /home/mrdrivingduck/Desktop/linux-kernel-elf-sig-verify/linux-kernel-elf-sig-verify-module/binfmt_elf_signature_verification.o 32 | Building modules, stage 2. 33 | MODPOST 1 modules 34 | CC /home/mrdrivingduck/Desktop/linux-kernel-elf-sig-verify/linux-kernel-elf-sig-verify-module/binfmt_elf_signature_verification.mod.o 35 | LD [M] /home/mrdrivingduck/Desktop/linux-kernel-elf-sig-verify/linux-kernel-elf-sig-verify-module/binfmt_elf_signature_verification.ko 36 | make[1]: Leaving directory '/usr/src/linux-headers-5.3.0-53-generic' 37 | ``` 38 | 39 | The `binfmt_elf_signature_verification.ko` is the kernel module. You can verify the basic information of this module: 40 | 41 | ```console 42 | $ modinfo binfmt_elf_signature_verification.ko 43 | filename: /home/mrdrivingduck/Desktop/linux-kernel-elf-sig-verify/linux-kernel-elf-sig-verify-module/binfmt_elf_signature_verification.ko 44 | alias: fs-binfmt_elf_signature_verification 45 | version: 1.0 46 | description: Binary handler for verifying signature in ELF section 47 | author: zonghuaxiansheng 48 | author: mrdrivingduck 49 | license: Dual MIT/GPL 50 | srcversion: 24C778301DE1DD13C1BB3CF 51 | depends: 52 | retpoline: Y 53 | name: binfmt_elf_signature_verification 54 | vermagic: 5.3.0-53-generic SMP mod_unload 55 | ``` 56 | 57 | Install the module via `insmod` command: 58 | 59 | ```console 60 | $ sudo insmod binfmt_elf_signature_verification.ko 61 | ``` 62 | 63 | Remove the module via `rmmod` command: 64 | 65 | ```console 66 | $ sudo rmmod binfmt_elf_signature_verification 67 | ``` 68 | 69 | If the module is installed successfully, you cannot run an ELF file without signature any more. Through `dmesg` command you can see more information. 70 | 71 | ## Key for Verification 72 | 73 | ### Generate Keys By Yourself 74 | 75 | The `certs/kernel_key.pem` is the same as the key in [linux-elf-binary-signer](https://github.com/NUAA-WatchDog/linux-elf-binary-signer), and is only used for testing. To use the `binfmt_elf_signature_verification` module, you should compile the key into the kernel. 76 | 77 | Or you can use the configuration file to get your own key pair by modifying `certs/x509.genkey`: 78 | 79 | ``` 80 | [ req ] 81 | default_bits = 2048 82 | distinguished_name = req_distinguished_name 83 | prompt = no 84 | string_mask = utf8only 85 | x509_extensions = myexts 86 | 87 | [ req_distinguished_name ] 88 | O = WatchDog 89 | CN = ELF verification 90 | emailAddress = mrdrivingduck@gmail.com 91 | 92 | [ myexts ] 93 | basicConstraints=critical,CA:FALSE 94 | keyUsage=digitalSignature 95 | subjectKeyIdentifier=hash 96 | authorityKeyIdentifier=keyid 97 | ``` 98 | 99 | And generate the key with `openssl` tools: 100 | 101 | ```console 102 | $ cd certs 103 | $ openssl req -new -nodes -utf8 -sha256 -days 36500 -batch -x509 \ 104 | -config x509.genkey -outform PEM 105 | -out kernel_key.pem -keyout kernel_key.pem 106 | Generating a RSA private key 107 | ........+++++ 108 | ........................................+++++ 109 | writing new private key to 'kernel_key.pem' 110 | ----- 111 | $ cd .. 112 | ``` 113 | 114 | ### Generate Keys Through *Let's Encrypt* 115 | 116 | See the website of [*Let's Encrypt*](https://letsencrypt.org/) and use [*Certbot*](https://certbot.eff.org/) to generate private key and public key certificate. 117 | 118 | --- 119 | 120 | ## Contributors 121 | 122 | 123 | 124 | 125 | 126 | Made with [contributors-img](https://contributors-img.web.app). 127 | 128 | ## License 129 | 130 | Copyright © 2020, Jingtang Zhang, Hua Zong. ([MIT License](LICENSE)) 131 | 132 | --- 133 | 134 | -------------------------------------------------------------------------------- /binfmt_elf_signature_verification.c: -------------------------------------------------------------------------------- 1 | /***************************************************************** 2 | * 3 | * Copyright (C) 2020, Jingtang Zhang, Hua Zong. 4 | * All Rights Reserved. 5 | * 6 | * binfmt_elf_signature_verification.c 7 | * 8 | * Verify the ELF's signature with built-in key-ring. If the 9 | * signature is correct, return -ENOEXEC to invoke real ELF 10 | * binary handler; else, return the error code to do_execve() 11 | * and avoid the ELF being executed. 12 | * 13 | ****************************************************************/ 14 | 15 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | 39 | /* That's for binfmt_elf_fdpic to deal with */ 40 | #ifndef elf_check_fdpic 41 | #define elf_check_fdpic(ex) false 42 | #endif 43 | 44 | #if ELF_EXEC_PAGESIZE > PAGE_SIZE 45 | #define ELF_MIN_ALIGN ELF_EXEC_PAGESIZE 46 | #else 47 | #define ELF_MIN_ALIGN PAGE_SIZE 48 | #endif 49 | 50 | enum verify_signature_e { VPASS, VFAIL, VSKIP }; 51 | 52 | unsigned char SIG_SCN_SUFFIX[] = "_sig"; 53 | 54 | #define SCN_CHECKED 1 55 | #define SCN_UNCHECKED 0 56 | 57 | /** 58 | * Check list containing sections whose signature should be verified. 59 | * Make sure all the sections in check list is verified. 60 | */ 61 | struct scn_checklist { 62 | unsigned char s_name[8]; /* Section name */ 63 | int s_nlen; /* Length of section name */ 64 | int s_check; /* Check status */ 65 | }; 66 | 67 | /** 68 | * update_checklist() 69 | * 70 | * Update the check status of specific section in the check list of 71 | * all sections whose signature needs to be verified. 72 | * 73 | * @scn_cklt: Check list element structure. 74 | * @cklt_len: Length of check list. 75 | * @sname: The section name that needs to be updated. 76 | */ 77 | static inline int update_checklist(struct scn_checklist *scn_cklt, int cklt_len, 78 | unsigned char *sname) 79 | { 80 | int i, retval = 1; 81 | for (i = 0; i < cklt_len; i++) { 82 | if (!memcmp(scn_cklt[i].s_name, sname, scn_cklt[i].s_nlen)) { 83 | scn_cklt[i].s_check = SCN_CHECKED; 84 | retval = 0; 85 | goto out; 86 | } 87 | } 88 | out: 89 | return retval; 90 | } 91 | 92 | /** 93 | * lookup_checklist() 94 | * 95 | * Check whether all sections are verified. 96 | * 97 | * @scn_cklt: Check list element structure. 98 | * @cklt_len: Length of check list. 99 | */ 100 | static inline int lookup_checklist(struct scn_checklist *scn_cklt, int cklt_len) 101 | { 102 | int i, retval = 0; 103 | for (i = 0; i < cklt_len; i++) { 104 | if (SCN_UNCHECKED == scn_cklt[i].s_check) { 105 | printk(" Section '%s' must be signed !\n", scn_cklt[i].s_name); 106 | retval = 1; 107 | goto out; 108 | } 109 | } 110 | out: 111 | return retval; 112 | } 113 | 114 | #define LD_CACHE_MAGIC_OLD "ld.so-1.7.0" 115 | 116 | struct ld_cache_header { 117 | char magic[sizeof(LD_CACHE_MAGIC_OLD) - 1]; 118 | unsigned int n_libs; 119 | }; 120 | 121 | struct ld_cache_entry { 122 | int e_flags; /* 0x01 indicates ELF library. */ 123 | unsigned int e_key; /* Key string index. */ 124 | unsigned e_value; /* Value string index. */ 125 | }; 126 | 127 | /** 128 | * Metadata structure for holding /etc/ld.so.cache. Only 129 | * old version of "ld.so-1.7.0" supported currently. 130 | */ 131 | struct ld_so_cache { 132 | char *l_buf; /* Cache file content buffer */ 133 | loff_t l_len; /* Buffer length. */ 134 | 135 | unsigned int l_entrynum; /* Number of cache entries */ 136 | struct ld_cache_entry *l_entries; /* Start of entry table */ 137 | char *l_strtab; /* Start of string table */ 138 | }; 139 | 140 | /** 141 | * init_so_caches() 142 | * 143 | * Open up the dynamic linking cache file in "/etc/ld.so.cache", 144 | * and read the content into memory buffer. Also, set the pointers 145 | * to the cache data structures. Meanwhile, do some validation for 146 | * the cache structure. 147 | * 148 | * @so_cache: an allocated cache metadata structure. 149 | */ 150 | static inline int init_so_caches(struct ld_so_cache *so_cache) 151 | { 152 | struct file *f_cache = NULL; 153 | struct ld_cache_header *cache_header; 154 | void *cursor; 155 | loff_t pos = 0; 156 | int retval = 0; 157 | 158 | /* Open up the dynamic linking cache file. */ 159 | f_cache = filp_open("/etc/ld.so.cache", O_RDONLY, 0); 160 | if (IS_ERR(f_cache)) { 161 | retval = PTR_ERR(f_cache); 162 | goto close_file; 163 | } 164 | 165 | retval = deny_write_access(f_cache); 166 | if (retval) { 167 | goto close_file; 168 | } 169 | 170 | so_cache->l_len = f_cache->f_inode->i_size; 171 | 172 | /* Allocate a memory buffer. */ 173 | so_cache->l_buf = (char *) vmalloc(so_cache->l_len); 174 | if (!so_cache->l_buf) { 175 | retval = -ENOMEM; 176 | goto allow_write; 177 | } 178 | 179 | /* Read the cache file into memory buffer. */ 180 | retval = kernel_read(f_cache, so_cache->l_buf, so_cache->l_len, &pos); 181 | if (retval != so_cache->l_len) { 182 | retval = -EIO; 183 | goto allow_write; 184 | } 185 | 186 | printk("Dynamic linking cache: %d bytes.\n", retval); 187 | 188 | /* Cache header. */ 189 | cursor = so_cache->l_buf; 190 | cache_header = (struct ld_cache_header *) cursor; 191 | if (memcmp(cache_header->magic, LD_CACHE_MAGIC_OLD, sizeof(LD_CACHE_MAGIC_OLD) - 1)) { 192 | retval = -EBADMSG; 193 | goto allow_write; 194 | } 195 | so_cache->l_entrynum = cache_header->n_libs; 196 | 197 | /* Cache entry table. */ 198 | cursor += sizeof(struct ld_cache_header); 199 | so_cache->l_entries = (struct ld_cache_entry *) cursor; 200 | /* Cache string table. */ 201 | cursor += so_cache->l_entrynum * sizeof(struct ld_cache_entry); 202 | so_cache->l_strtab = (char *) cursor; 203 | 204 | /* Validity check. */ 205 | if ((char *) so_cache->l_entries - so_cache->l_buf >= so_cache->l_len) { 206 | retval = -EBADMSG; 207 | goto allow_write; 208 | } 209 | if (so_cache->l_strtab - so_cache->l_buf >= so_cache->l_len) { 210 | retval = -EBADMSG; 211 | goto allow_write; 212 | } 213 | /* Make sure the string table has an end, avoiding overflow. */ 214 | if ((so_cache->l_buf)[so_cache->l_len - 1] != '\0') { 215 | retval = -EBADMSG; 216 | goto allow_write; 217 | } 218 | 219 | printk("Cache init done.\n"); 220 | 221 | retval = 0; /* Cache initialization done. */ 222 | goto allow_write; 223 | 224 | ret: 225 | return retval; 226 | 227 | allow_write: 228 | allow_write_access(f_cache); 229 | close_file: 230 | filp_close(f_cache, NULL); 231 | goto ret; 232 | } 233 | 234 | /** 235 | * cleanup_so_caches() 236 | * 237 | * Free the memory buffer for dynamic linking cache. 238 | * 239 | * @so_cache: an allocated cache metadata structure. 240 | */ 241 | static inline void cleanup_so_caches(struct ld_so_cache *so_cache) 242 | { 243 | if (so_cache->l_buf) { 244 | vfree(so_cache->l_buf); 245 | } 246 | kfree(so_cache); 247 | } 248 | 249 | /** 250 | * get_so_file_path() 251 | * 252 | * Get the (absolute) file path of .so file through dynamic linking 253 | * cache. If no path found, return a null-pointer. 254 | * 255 | * @so_cache: an allocated cache metadata structure. 256 | * @so_key: the name of a .so file, e.g., "libcrypto.so.1.1". 257 | */ 258 | static inline char *get_so_file_path(struct ld_so_cache *so_cache, char *so_key) 259 | { 260 | struct ld_cache_entry *cache_entry; 261 | char *str_p; 262 | int i; 263 | 264 | for (i = 0, cache_entry = so_cache->l_entries; 265 | i < so_cache->l_entrynum; i++, cache_entry++) { 266 | str_p = so_cache->l_strtab + cache_entry->e_key; 267 | if (str_p >= so_cache->l_buf + so_cache->l_len) { 268 | goto not_found; 269 | } 270 | if (!memcmp(so_key, str_p, strlen(str_p))) { 271 | str_p = so_cache->l_strtab + cache_entry->e_value; 272 | return str_p >= so_cache->l_buf + so_cache->l_len ? NULL : str_p; 273 | } 274 | } 275 | 276 | not_found: 277 | return NULL; 278 | } 279 | 280 | /** 281 | * load_elf_shdrs() - load ELF section headers 282 | * 283 | * Loads ELF section headers from the binary file elf_file, which has the ELF 284 | * header pointed to by elf_ex, into a newly allocated array. The caller is 285 | * responsible for freeing the allocated data. Returns an ERR_PTR upon failure. 286 | * 287 | * @elf_ex: ELF header of the binary whose section headers shold be loaded. 288 | * @elf_file: the opened ELF binary file. 289 | */ 290 | /*{{{*/ // load_elf_shdrs 291 | static inline struct elf_shdr *load_elf_shdrs(struct elfhdr *elf_ex, 292 | struct file *elf_file) 293 | { 294 | struct elf_shdr *elf_shdata = NULL; 295 | int retval, size; 296 | loff_t pos = elf_ex->e_shoff; 297 | 298 | /* 299 | * If the size of this structure has changed, then punt, since 300 | * we will be doing the wrong thing. 301 | */ 302 | if (elf_ex->e_shentsize != sizeof(struct elf_shdr)) 303 | goto out_ret; 304 | 305 | /* Sanity check the number of section headers ... */ 306 | if (elf_ex->e_shnum < 1 || 307 | elf_ex->e_shnum > 65536U / sizeof(struct elf_shdr)) 308 | goto out_ret; 309 | 310 | /* ... and their total size. */ 311 | size = sizeof(struct elf_shdr) * elf_ex->e_shnum; 312 | if (size > ELF_MIN_ALIGN) 313 | goto out_ret; 314 | 315 | elf_shdata = vmalloc(size); 316 | if (!elf_shdata) 317 | goto out_ret; 318 | 319 | /* Read in the section headers */ 320 | retval = kernel_read(elf_file, elf_shdata, size, &pos); 321 | if (retval != size) { 322 | vfree(elf_shdata); 323 | elf_shdata = NULL; 324 | } 325 | 326 | /* Success! */ 327 | 328 | out_ret: 329 | return elf_shdata; 330 | } 331 | /*}}}*/ 332 | 333 | /** 334 | * load_elf_sdata() - load ELF section data 335 | * 336 | * Loads ELF section data from the binary file elf_file. 337 | * 338 | * @elf_shdata: ELF section header table. 339 | * @elf_file: The opened ELF binary file. 340 | */ 341 | /*{{{*/ // load_elf_sdata 342 | static inline unsigned char *load_elf_sdata(struct elf_shdr *elf_shdata, 343 | struct file *elf_file) 344 | { 345 | int size, retval = -EIO; 346 | unsigned char *elf_sdata = NULL; 347 | loff_t pos; 348 | 349 | /* If the section is empty, return NULL */ 350 | if (SHT_NOBITS == elf_shdata->sh_offset) 351 | goto out_ret; 352 | 353 | pos = elf_shdata->sh_offset; 354 | size = elf_shdata->sh_size; 355 | elf_sdata = vmalloc(size); 356 | if (!elf_sdata) 357 | goto out_ret; 358 | 359 | /* Read the secton data into new kernel memory space */ 360 | retval = kernel_read(elf_file, elf_sdata, size, &pos); 361 | if (retval != size) { 362 | vfree(elf_sdata); 363 | elf_sdata = NULL; 364 | } 365 | 366 | /* Success! */ 367 | 368 | out_ret: 369 | return elf_sdata; 370 | } 371 | /*}}}*/ 372 | 373 | /** 374 | * scn_name_match() - memory compare for section names. 375 | * 376 | * Firstly, compare the prefix of signed_scn_name and scn_name. 377 | * If signed_scn_name[prefix] == scn_name[prefix], then compare 378 | * the suffix; if signed_scn_name[suffix] == "_sig", comparison pass. 379 | * 380 | * @scn_name: The original section name, e.g. ".text". 381 | * @scn_name_len: The length of original section name. 382 | * @signed_scn_name: The signed section name, e.g. ".text_sig". 383 | * @signed_scn_name_len: The length of signed section name. 384 | * 385 | */ 386 | /*{{{*/ // scn_name_match 387 | static inline int scn_name_match(unsigned char *scn_name, 388 | int scn_name_len, 389 | unsigned char *signed_scn_name, 390 | int signed_scn_name_len) 391 | { 392 | int retval = 1; 393 | 394 | /** 395 | * 1. (len(.text_sig) - len(.text)) =? len(_sig) 396 | * 2. .text[_sig] =? .text 397 | * 3. [.text]_sig =? _sig 398 | */ 399 | if ((signed_scn_name_len - scn_name_len) != (sizeof(SIG_SCN_SUFFIX) - 1)) { 400 | goto out; 401 | } 402 | if (memcmp(signed_scn_name, scn_name, scn_name_len)) { 403 | goto out; 404 | } 405 | if (memcmp(signed_scn_name + scn_name_len, SIG_SCN_SUFFIX, sizeof(SIG_SCN_SUFFIX) - 1)) { 406 | goto out; 407 | } 408 | 409 | /* Success! */ 410 | retval = 0; 411 | out: 412 | return retval; 413 | } 414 | /*}}}*/ 415 | 416 | /* 417 | * free_bprm() 418 | * 419 | * Free linux_binprm structure. 420 | * 421 | */ 422 | static inline void free_bprm(struct linux_binprm *bprm) 423 | { 424 | if (bprm->file) { 425 | // fput(bprm->file); 426 | filp_close(bprm->file, NULL); 427 | } 428 | /* If a binfmt changed the interp, free it. */ 429 | if (bprm->interp != bprm->filename) { 430 | kfree(bprm->interp); 431 | } 432 | kfree(bprm); 433 | } 434 | 435 | /** 436 | * 437 | * verify_scn_signature() - verify the section signature. 438 | * 439 | * Use verify_pkcs7_signature(...) to verify the signature. 440 | * 441 | * @scn_data: Data of original section. 442 | * @scn_data_len: Length of original section data. 443 | * @sig_scn_data: Data of signature section. 444 | * @sig_scn_data_len: Length of signature section data. 445 | * 446 | */ 447 | /*{{{*/ // verify_scn_signature 448 | static inline int verify_scn_signature(unsigned char *scn_data, 449 | int scn_data_len, 450 | unsigned char *sig_scn_data, 451 | int sig_scn_data_len) 452 | { 453 | int retval; 454 | 455 | retval = verify_pkcs7_signature(scn_data, scn_data_len, 456 | sig_scn_data, sig_scn_data_len, 457 | NULL, VERIFYING_MODULE_SIGNATURE, NULL, NULL); 458 | printk("verify_pkcs7_signature return value: %d\n", retval); 459 | return retval; 460 | } 461 | /*}}}*/ 462 | 463 | static int elf_signature_verification(struct linux_binprm *bprm, 464 | struct ld_so_cache *so_cache); 465 | 466 | /** 467 | * so_signature_verification() 468 | * 469 | * Verify the signature of .so dependencies of an ELF 470 | * file (program OR shared object). 471 | * 472 | * @bprm: the original ELF file handler. 473 | * @so_cache: the dynamic linking cache. 474 | * @elf_dynamic: section data of ".dynamic". 475 | * @e_dynnum: number of ".dynamic" section entries. 476 | * @elf_dynstr: section data of ".dynstr". 477 | */ 478 | static inline int so_signature_verification(struct linux_binprm *bprm, 479 | struct ld_so_cache *so_cache, 480 | void *elf_dynamic, int e_dynnum, 481 | unsigned char *elf_dynstr) 482 | { 483 | Elf64_Dyn *dyn_ptr; 484 | char *so_file_path; 485 | struct linux_binprm *so_bprm; 486 | struct file *so_file; 487 | int i, retval = -ENOEXEC; 488 | loff_t pos = 0; 489 | 490 | // for (dyn_ptr = elf_dynamic, i = 0; i < e_dynnum; dyn_ptr++, i++) { 491 | // if (dyn_ptr->d_tag == DT_NEEDED) { 492 | // printk("Dependency library: %s\n", elf_dynstr + dyn_ptr->d_un.d_val); 493 | // so_file_path = get_so_file_path(so_cache, elf_dynstr + dyn_ptr->d_un.d_val); 494 | // if (so_file_path) { 495 | // printk("%s\n", so_file_path); 496 | // } 497 | // } 498 | // } 499 | 500 | // return retval; 501 | 502 | for (dyn_ptr = elf_dynamic, i = 0; i < e_dynnum; dyn_ptr++, i++) { 503 | if (dyn_ptr->d_tag == DT_NEEDED) { 504 | printk("Dependency library: %s\n", elf_dynstr + dyn_ptr->d_un.d_val); 505 | 506 | /* Get the absolute path of this dynamic lib.so. */ 507 | so_file_path = get_so_file_path(so_cache, elf_dynstr + dyn_ptr->d_un.d_val); 508 | if (!so_file_path) { 509 | retval = -ENOENT; 510 | goto out_ret; 511 | } 512 | 513 | /* Allocate for a new linux_binprm for shared object file. */ 514 | so_bprm = kzalloc(sizeof(*so_bprm), GFP_KERNEL); 515 | if (!so_bprm) { 516 | retval = -ENOMEM; 517 | goto out_ret; 518 | } 519 | 520 | /* Open the .so file. */ 521 | so_file = filp_open(so_file_path, O_RDONLY, 0); 522 | if (IS_ERR(so_file)) { 523 | retval = PTR_ERR(so_file); 524 | goto out_free; 525 | } 526 | 527 | retval = deny_write_access(so_file); 528 | if (retval) { 529 | goto out_free; 530 | } 531 | 532 | so_bprm->file = so_file; 533 | /* 534 | * Here is a fake check now, we can make sure the filename 535 | * is absolute path. 536 | */ 537 | if (so_file_path[0] == '/') { 538 | so_bprm->filename = so_file_path; 539 | } else { 540 | so_bprm->filename = so_file_path; 541 | } 542 | so_bprm->interp = so_bprm->filename; 543 | 544 | // so_bprm->argc = count(argv, MAX_ARG_STRINGS); 545 | // if ((retval = so_bprm->argc) < 0) 546 | // goto out; 547 | 548 | // so_bprm->envc = count(envp, MAX_ARG_STRINGS); 549 | // if ((retval = so_bprm->envc) < 0) 550 | // goto out; 551 | 552 | /* Read the first 128 bytes of the file. */ 553 | retval = kernel_read(so_bprm->file, so_bprm->buf, BINPRM_BUF_SIZE, &pos); 554 | if (retval != BINPRM_BUF_SIZE) { 555 | retval = -EIO; 556 | goto out_allow_write; 557 | } 558 | 559 | /* Verify this lib.so now ! */ 560 | retval = elf_signature_verification(so_bprm, so_cache); 561 | if (retval != -ENOEXEC) 562 | goto out_allow_write; 563 | 564 | allow_write_access(so_bprm->file); 565 | free_bprm(so_bprm); 566 | } 567 | } 568 | 569 | goto out_ret; 570 | 571 | out_allow_write: 572 | allow_write_access(so_bprm->file); 573 | out_free: 574 | free_bprm(so_bprm); 575 | out_ret: 576 | return retval; 577 | } 578 | 579 | /** 580 | * elf_format_validation() 581 | * 582 | * To check if the file conforms to ELF format, and whether we need to 583 | * skip the verification. A return value of -ENOEXEC means we will skip 584 | * the verification, and a zero return value means the file comforms to 585 | * ELF format and we need to verify its signature. 586 | * 587 | * @bprm: the binary program handler. 588 | */ 589 | static inline int elf_format_validation(struct linux_binprm *bprm) 590 | { 591 | struct elfhdr *elf_ex; 592 | int retval = -ENOEXEC; /* Skip verification for default. */ 593 | 594 | /** 595 | * Skip the verification of system ELF binaries. We use the name of 596 | * interpreter instead of the name of file because of: 597 | * 598 | * https://github.com/NUAA-WatchDog/linux-kernel-elf-sig-verify/pull/13 599 | * 600 | * ATTENTION: these code can be removed if all built-in ELF binaries 601 | * on system are signed. 602 | */ 603 | if (!memcmp(bprm->interp, "/bin/", 5) || 604 | (!memcmp(bprm->interp, "/lib/", 5) && 605 | memcmp(bprm->interp, "/lib/x86_64-linux-gnu/libtest.so", 32)) || 606 | !memcmp(bprm->interp, "/etc/", 5) || 607 | !memcmp(bprm->interp, "/sbin/", 6) || 608 | !memcmp(bprm->interp, "/usr/", 5) || 609 | !memcmp(bprm->interp, "/tmp/", 5) || 610 | !memcmp(bprm->interp, "/var/", 5)) { 611 | 612 | printk("Skip for verification of: %s\n", bprm->interp); 613 | goto out; /* Skip. */ 614 | } 615 | 616 | elf_ex = (struct elfhdr *) bprm->buf; 617 | 618 | /* Not a ELF file, return -ENOEXEC to skip this handler. */ 619 | if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0) { 620 | goto out; /* Skip. */ 621 | } 622 | 623 | /* Here we are sure it is an ELF file. */ 624 | if (ET_EXEC != elf_ex->e_type && ET_DYN != elf_ex->e_type) { 625 | goto out; 626 | } 627 | if (!elf_check_arch(elf_ex)) { 628 | goto out; 629 | } 630 | if (elf_check_fdpic(elf_ex)) { 631 | goto out; 632 | } 633 | if (!bprm->file->f_op->mmap) { 634 | goto out; 635 | } 636 | if (SHN_UNDEF == elf_ex->e_shstrndx) { 637 | retval = -EBADMSG; 638 | goto out; 639 | } 640 | 641 | retval = 0; /* Validation pass. We want to verify this file. */ 642 | 643 | out: 644 | return retval; 645 | } 646 | 647 | /** 648 | * elf_signature_verification() 649 | * 650 | * Entry function for verify single ELF file's signature. 651 | * 652 | * @bprm: the binary program handler. 653 | * @so_cache: the dynamic linking cache. 654 | */ 655 | static int elf_signature_verification(struct linux_binprm *bprm, 656 | struct ld_so_cache *so_cache) 657 | { 658 | enum verify_signature_e verify_e = VSKIP; 659 | 660 | int retval, i, j; 661 | int elf_slen, elf_sslen; 662 | int e_dynnum = 0; 663 | 664 | unsigned char *elf_shstrtab, *elf_sdata, *elf_ssdata; 665 | unsigned char *elf_dynstrtab = NULL, *elf_dynamic = NULL; 666 | unsigned char *scn_name, *signed_scn_name; 667 | size_t scn_name_len, signed_scn_name_len; 668 | 669 | struct elfhdr *elf_ex; 670 | struct elf_shdr *elf_shptr, *elf_shdata; 671 | 672 | /** 673 | * Section list that needs to be checked. 674 | * Only .text section currently. 675 | */ 676 | struct scn_checklist scn_cklt[] = { 677 | {".text", sizeof(".text") - 1, SCN_UNCHECKED}, 678 | // {".data", 5, 0} 679 | }; 680 | 681 | /** 682 | * Validate if it is an ELF file. Skip the verification 683 | * if it is not. 684 | */ 685 | retval = elf_format_validation(bprm); 686 | if (retval) { 687 | if (retval != -ENOEXEC) { 688 | verify_e = VFAIL; 689 | } 690 | goto out_ret; 691 | } 692 | 693 | /** 694 | * The default return value for search_binary_handler() to iterate the 695 | * next binary format's handler. It should be used in three cases: 696 | * 697 | * 1. Skip the binary file which is not in ELF format at all. 698 | * 2. Skip the verification of an ELF file, let binfmt_elf to execute 699 | * it directly. 700 | * 3. The ELF file passes the verification, and should be executed 701 | * by binfmt_elf normally. 702 | */ 703 | retval = -ENOEXEC; 704 | 705 | printk("Start to verify '%s' ...", bprm->interp); 706 | 707 | /** 708 | * Get the header of the file to be ELF header, check ELF format 709 | * and do some simple consistency checks. 710 | * 711 | * With the information in ELF header, load section header table 712 | * and section header string table into memory, and prepare for 713 | * the signature verification. 714 | */ 715 | elf_ex = (struct elfhdr *) bprm->buf; 716 | 717 | /* Section header table. */ 718 | elf_shdata = load_elf_shdrs(elf_ex, bprm->file); 719 | if (!elf_shdata) { 720 | retval = -ENOMEM; 721 | goto out_ret; 722 | } 723 | 724 | /* Section header string table. */ 725 | elf_shptr = elf_shdata + elf_ex->e_shstrndx; 726 | elf_shstrtab = load_elf_sdata(elf_shptr, bprm->file); 727 | if (!elf_shstrtab) { 728 | retval = -ENOMEM; 729 | goto out_free_shdata; 730 | } 731 | 732 | /** 733 | * Iterate over sections, find two sections matched with "_sig" suffix. 734 | * 735 | * At the same time, prepare ".dynstr" and ".dynamic" for verification 736 | * of shared objects dependencies of dynamic linking. 737 | */ 738 | for (i = 0; i < elf_ex->e_shnum; i++) { 739 | scn_name = elf_shstrtab + (elf_shdata + i)->sh_name; 740 | scn_name_len = strlen(scn_name); 741 | 742 | /* Prepare ".dynstr" and ".dynamic" section in memory. */ 743 | if (!elf_dynstrtab && !memcmp(".dynstr", scn_name, sizeof(".dynstr") - 1)) { 744 | elf_dynstrtab = load_elf_sdata(elf_shdata + i, bprm->file); 745 | if (!elf_dynstrtab) { 746 | retval = -ENOMEM; 747 | goto out_free_shdata; 748 | } 749 | } else if (!elf_dynamic && !memcmp(".dynamic", scn_name, sizeof(".dynamic") - 1)) { 750 | elf_dynamic = load_elf_sdata(elf_shdata + i, bprm->file); 751 | if (!elf_dynamic) { 752 | retval = -ENOMEM; 753 | goto out_free_shdata; 754 | } 755 | e_dynnum = (elf_shdata + i)->sh_size / sizeof(Elf64_Dyn); 756 | } 757 | 758 | /** 759 | * Find out the signature sections with suffix '_sig', 760 | * then verify the signature. 761 | */ 762 | for (j = 0; j < elf_ex->e_shnum; j++) { 763 | signed_scn_name = elf_shstrtab + (elf_shdata + j)->sh_name; 764 | signed_scn_name_len = strlen(signed_scn_name); 765 | 766 | /** 767 | * Find out two matched section like: 768 | * len(".text") < len(".text_sig") 769 | * where [i] stand for ".text", [j] stand for ".text_sig". 770 | */ 771 | if (scn_name_len >= signed_scn_name_len) { 772 | continue; 773 | } 774 | if (scn_name_match(scn_name, scn_name_len, signed_scn_name, signed_scn_name_len)) { 775 | continue; 776 | } 777 | 778 | /* 779 | * Found two sections with matching name, e.g. ".text" and 780 | * ".text_sig". Then, load the data of these two sections. 781 | */ 782 | printk("Found two matching sections : %s %s\n", 783 | scn_name, signed_scn_name); 784 | 785 | /* Load the original data section. */ 786 | elf_shptr = elf_shdata + i; 787 | elf_slen = elf_shptr->sh_size; 788 | elf_sdata = load_elf_sdata(elf_shptr, bprm->file); 789 | if (!elf_sdata) { 790 | retval = -ENOMEM; 791 | goto out_free_shstrtab; 792 | } 793 | 794 | /* Load the signature data section. */ 795 | elf_shptr = elf_shdata + j; 796 | elf_sslen = elf_shptr->sh_size; 797 | elf_ssdata = load_elf_sdata(elf_shptr, bprm->file); 798 | if (!elf_ssdata) { 799 | retval = -ENOMEM; 800 | goto out_free_sdata; 801 | } 802 | 803 | /* Verify the signature. */ 804 | retval = verify_scn_signature(elf_sdata, elf_slen, elf_ssdata, elf_sslen); 805 | if (retval) { 806 | goto out_free_ssdata; 807 | } 808 | 809 | /* Update check list status. */ 810 | update_checklist(scn_cklt, sizeof(scn_cklt) / sizeof(struct scn_checklist), scn_name); 811 | 812 | /* Clean up and prepare for the next iteration. */ 813 | vfree(elf_sdata); 814 | vfree(elf_ssdata); 815 | elf_sdata = NULL; 816 | elf_ssdata = NULL; 817 | } 818 | } 819 | 820 | /* Make sure all signature sections are successfully verified. */ 821 | if (!lookup_checklist(scn_cklt, sizeof(scn_cklt) / sizeof(struct scn_checklist))) { 822 | verify_e = VPASS; 823 | } else { 824 | retval = -ENODATA; 825 | verify_e = VFAIL; 826 | } 827 | 828 | goto out_free_shstrtab; 829 | 830 | out_ret: 831 | /* Start to verify dependencies of shared object. */ 832 | // if (elf_dynamic) { 833 | // if (elf_dynstrtab) { 834 | // if (VPASS == verify_e) { 835 | // retval = so_signature_verification(bprm, elf_dynamic, elf_dynstrtab); 836 | // } 837 | // vfree(elf_dynstrtab); 838 | // } 839 | // vfree(elf_dynamic); 840 | // } 841 | 842 | if (VPASS == verify_e && elf_dynamic && elf_dynstrtab) { 843 | retval = so_signature_verification(bprm, so_cache, elf_dynamic, e_dynnum, elf_dynstrtab); 844 | if (retval != -ENOEXEC) { 845 | verify_e = VFAIL; 846 | } 847 | } 848 | 849 | /* Free the dynamic linking data structure. */ 850 | if (elf_dynamic) { 851 | vfree(elf_dynamic); 852 | } 853 | if (elf_dynstrtab) { 854 | vfree(elf_dynstrtab); 855 | } 856 | 857 | if (VPASS == verify_e) { 858 | printk("Verifying pass ...\n"); 859 | retval = -ENOEXEC; 860 | } else if (VFAIL == verify_e) { 861 | printk("Verifying failed ...\n"); 862 | } else { 863 | retval = -ENOEXEC; /* skipped */ 864 | } 865 | 866 | return retval; 867 | 868 | out_free_ssdata: 869 | vfree(elf_ssdata); 870 | out_free_sdata: 871 | vfree(elf_sdata); 872 | out_free_shstrtab: 873 | vfree(elf_shstrtab); 874 | out_free_shdata: 875 | vfree(elf_shdata); 876 | goto out_ret; 877 | } 878 | 879 | /** 880 | * load_elf_signature_verification_binary() 881 | * 882 | * Interface function for binfmt_xxx. 883 | * 884 | * @bprm: the binary program handler 885 | */ 886 | static int load_elf_signature_verification_binary(struct linux_binprm *bprm) 887 | { 888 | int retval; 889 | struct ld_so_cache *so_cache; 890 | 891 | /** 892 | * Validatation of ELF format. 893 | * 894 | * If the function return 0, it means that it is an ELF file, and we want 895 | * to verify its signature; if -ENOEXEC returned, it means that it is not 896 | * an ELF file, and we don't verify, skip it; if other errores are returned, 897 | * it means that it is an ELF, but it is corrupted, we'll leave it to the 898 | * real ELF handler. skip it. 899 | */ 900 | retval = elf_format_validation(bprm); 901 | if (retval) { 902 | goto out; 903 | } 904 | 905 | so_cache = (struct ld_so_cache *) kzalloc(sizeof(*so_cache), GFP_KERNEL); 906 | if (!so_cache) { 907 | retval = -ENOMEM; 908 | goto out; 909 | } 910 | if ((retval = init_so_caches(so_cache))) { 911 | goto clean_up; 912 | } 913 | 914 | retval = elf_signature_verification(bprm, so_cache); 915 | 916 | clean_up: 917 | cleanup_so_caches(so_cache); 918 | out: 919 | return retval; 920 | } 921 | 922 | /* 923 | * \brief Register a new handler for signature verification. 924 | */ 925 | static struct linux_binfmt elf_signature_verification_format = { 926 | .module = THIS_MODULE, 927 | .load_binary = load_elf_signature_verification_binary, 928 | }; 929 | 930 | static int __init init_elf_signature_verification_binfmt(void) 931 | { 932 | insert_binfmt(&elf_signature_verification_format); 933 | return 0; 934 | } 935 | 936 | static void __exit exit_elf_signature_verification_binfmt(void) 937 | { 938 | unregister_binfmt(&elf_signature_verification_format); 939 | } 940 | 941 | module_init(init_elf_signature_verification_binfmt); 942 | module_exit(exit_elf_signature_verification_binfmt); 943 | MODULE_LICENSE("Dual MIT/GPL"); 944 | MODULE_AUTHOR("mrdrivingduck "); 945 | MODULE_AUTHOR("zonghuaxiansheng "); 946 | MODULE_DESCRIPTION("Binary handler for verifying signature in ELF sections"); 947 | MODULE_VERSION("1.20"); 948 | MODULE_ALIAS("binfmt_elf_signature_verification"); 949 | -------------------------------------------------------------------------------- /certs/kernel_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDquZ7imP4sqlM 3 | 8GGAev2Whq6uAQ9+JNY4s74XbKzUr5vxzybBWvtyg7+KzJkyV6YPQbAXLs7iB4xt 4 | phULfzSxqtdcMzW63/cgrrZ8XmmQp2fV+3MJiUt8Q+1mr0ZmZLxQ8rM5nr7el6WL 5 | vAZrcnRQCIHtdt3kz7sxeoaV7sUMCFStFUYLZ/FMBcXKLT2IFQOSyd2tOZKWxu6X 6 | 10U+7b+k2OTgEjgYg+hkFU37O9OyWyGB8RJK5BbUaVuQWG77FqhA0gY+GgsYyJTB 7 | oCGK9wmqySsARF+R7Kl7B/hjYYK9qQcmEzuazKKcWiBZOe7AMzpaZlA0WWu8DHfP 8 | x6XuRnf7AgMBAAECggEBAI2ocfspAx2vg2Cfe4eAA43x7g8t12CVHL6qasoctLe6 9 | d6VGOyGrU/XhKlA7tgG7cQ8EmI8AJWmA24savYu4SZcDP62Rqamksg7oTleWJslC 10 | WauSwwM5cfwCVaKM7OP+tFPKkjT6F+ehVIno06EAmPLEZgRclWMtfbHhqgRsfB4r 11 | uW53Xs5iQKi7rmMvOnxOqT/lLA86fT+UzgR0HrQ9ySojfWCx3Xjcd1tQbuJrLCEo 12 | PZE5M6Lo2WisAzGXXZPp5uTle0ccDFobCLdoiuegZljMhR53CCtwCUpzq6E83qOU 13 | Th4hKObb1KtPUpHK/RNaI4iPEVNar+yfaqalbiM0iIECgYEA6njTJuqYgLxO+FP4 14 | QwGKZgIiV1iFwDOJ470d5TAt+jUXzi3sQN+jjFl9Y/sYTMJ0pRigaUSbjTQ36qLQ 15 | mNpEbZ4Yvr1u+QSADYNuS4A/ET1fJ+/4XIQvpf6927BXUzT/opGWIf6zjZgulMy0 16 | TGqeiMGVYwF8JyccQlWEntrRO7sCgYEA1aH+L3pTnkcI8WQczv9JU7bEES/oWCnD 17 | 7pHfmJbzR6K3cFBllqZTB17/Ct7lltofQ6X2MHpSVYeJ8bLn0kTYJiDiciwhQEOK 18 | VSud2MgoflvR84IvJ0VlqtykdvCDau4vAM3hCQNbvvfbjVsUgIpdIa3duEspAMDc 19 | dx5rVqq6UMECgYBkHlOLOoED92SBBNntQqsmA6NGSLZT663lYMtzIa+AN0uC7nkC 20 | +nuLYmr4Vi96F05XcLyONo11LebyebM4kXrGk8tdJuZLznAfVQ+zjX9gjSQkmYMj 21 | fg4agzvuos2m6ep+MEw7F9bzjxfU0dPrDVvEaWSwceHhlgGrEXkfm6/cIQKBgESS 22 | VrCOp0BqtKgm5khbEQJZ691Ib1+9KDLvh8HG0hfwVskqRTFx+byn9V1b5n3hTZ+9 23 | KaSt3retZc27C0Ym6upyK71SBOpHw6NK+MOYm8bTWjS7WL9WOyf44qF62fP8uAGe 24 | E9l8xnopvMNGtPH9d59T25NILMUdIqbpkLKk4mEBAoGAKJTIOYVfZManf9jiAwK6 25 | NvUOvtS7GMzNXIYHUT24OiFJhuSQ9ucrA9cIIPHwk34AQ6dWnMGdIRT1Zhda/Zo5 26 | G+9xddpAf3xVN+6BuDZ3fH2Ph7lXm2sBEd+84IrDqv3bIy2fFxead9ibYpmCG0+d 27 | vQZAP9Of0pteMi587xib3kg= 28 | -----END PRIVATE KEY----- 29 | -----BEGIN CERTIFICATE----- 30 | MIIDmTCCAoGgAwIBAgIUSHnzIvFnHfNoFp9/PtD4QVCizIYwDQYJKoZIhvcNAQEL 31 | BQAwVjERMA8GA1UECgwIV2F0Y2hEb2cxGTAXBgNVBAMMEEVMRiB2ZXJpZmljYXRp 32 | b24xJjAkBgkqhkiG9w0BCQEWF21yZHJpdmluZ2R1Y2tAZ21haWwuY29tMCAXDTIw 33 | MDQxNDE1MTk0MloYDzIxMjAwMzIxMTUxOTQyWjBWMREwDwYDVQQKDAhXYXRjaERv 34 | ZzEZMBcGA1UEAwwQRUxGIHZlcmlmaWNhdGlvbjEmMCQGCSqGSIb3DQEJARYXbXJk 35 | cml2aW5nZHVja0BnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK 36 | AoIBAQDDquZ7imP4sqlM8GGAev2Whq6uAQ9+JNY4s74XbKzUr5vxzybBWvtyg7+K 37 | zJkyV6YPQbAXLs7iB4xtphULfzSxqtdcMzW63/cgrrZ8XmmQp2fV+3MJiUt8Q+1m 38 | r0ZmZLxQ8rM5nr7el6WLvAZrcnRQCIHtdt3kz7sxeoaV7sUMCFStFUYLZ/FMBcXK 39 | LT2IFQOSyd2tOZKWxu6X10U+7b+k2OTgEjgYg+hkFU37O9OyWyGB8RJK5BbUaVuQ 40 | WG77FqhA0gY+GgsYyJTBoCGK9wmqySsARF+R7Kl7B/hjYYK9qQcmEzuazKKcWiBZ 41 | Oe7AMzpaZlA0WWu8DHfPx6XuRnf7AgMBAAGjXTBbMAwGA1UdEwEB/wQCMAAwCwYD 42 | VR0PBAQDAgeAMB0GA1UdDgQWBBR+DhrJRuU1BGBJe6YRpHVTTJw+xDAfBgNVHSME 43 | GDAWgBR+DhrJRuU1BGBJe6YRpHVTTJw+xDANBgkqhkiG9w0BAQsFAAOCAQEAv0MG 44 | arQ3lgUSBuGDwtNPT28tVvKfbTkTF+omykIJmZH3N8Qsyx/ctQZLkyj9xz4Io0Mm 45 | s4qybt7Vro/bJo87If91iFOgaXOY8MsZLXCUEnd5aAyyIxWx8GiXAvfeNOOgyZFK 46 | vmTZ345gfrv71Dmz0dmSddyiQxlzO/mXsQWiY2ecLCrwB+/CwYaCHzsNBh/uICMU 47 | VnpeVonPuc2fbCG4QfEPLixf3yaX2fdVVGC2qf3aVn3MToHfVU9CoyKtAgNunXZf 48 | Vj/pepi9FP5y5fOPrVMOMIqOK1Vy596QW6ZJSlspXU60Q2EutyjlFjXdmzym6NOa 49 | z9GdzF0khNX4W6cr5w== 50 | -----END CERTIFICATE----- 51 | -------------------------------------------------------------------------------- /certs/x509.genkey: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 2048 3 | distinguished_name = req_distinguished_name 4 | prompt = no 5 | string_mask = utf8only 6 | x509_extensions = myexts 7 | 8 | [ req_distinguished_name ] 9 | #O = WatchDog 10 | CN = ELF verification 11 | #emailAddress = mrdrivingduck@gmail.com 12 | 13 | [ myexts ] 14 | basicConstraints=critical,CA:FALSE 15 | keyUsage=digitalSignature 16 | subjectKeyIdentifier=hash 17 | authorityKeyIdentifier=keyid --------------------------------------------------------------------------------