• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2021 - Google LLC
4  * Author: Ard Biesheuvel <ardb@google.com>
5  *
6  * This is a host tool that is intended to be used to take the HMAC digest of
7  * the .text and .rodata sections of the fips140.ko module, and store it inside
8  * the module. The module will perform an integrity selfcheck at module_init()
9  * time, by recalculating the digest and comparing it with the value calculated
10  * here.
11  *
12  * Note that the peculiar way an HMAC is being used as a digest with a public
13  * key rather than as a symmetric key signature is mandated by FIPS 140-2.
14  */
15 
16 #include <elf.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/mman.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 
26 #include <openssl/hmac.h>
27 
28 static Elf64_Ehdr *ehdr;
29 static Elf64_Shdr *shdr;
30 static int num_shdr;
31 static const char *strtab, *shstrtab;
32 static Elf64_Sym *syms;
33 static int num_syms;
34 
find_symtab_section(void)35 static Elf64_Shdr *find_symtab_section(void)
36 {
37 	int i;
38 
39 	for (i = 0; i < num_shdr; i++)
40 		if (shdr[i].sh_type == SHT_SYMTAB)
41 			return &shdr[i];
42 	return NULL;
43 }
44 
get_section_idx(const char * name)45 static int get_section_idx(const char *name)
46 {
47 	int i;
48 
49 	for (i = 0; i < num_shdr; i++)
50 		if (!strcmp(shstrtab + shdr[i].sh_name, name))
51 			return i;
52 	return -1;
53 }
54 
get_sym_idx(const char * sym_name)55 static int get_sym_idx(const char *sym_name)
56 {
57 	int i;
58 
59 	for (i = 0; i < num_syms; i++)
60 		if (!strcmp(strtab + syms[i].st_name, sym_name))
61 			return i;
62 	return -1;
63 }
64 
get_sym_addr(const char * sym_name)65 static void *get_sym_addr(const char *sym_name)
66 {
67 	int i = get_sym_idx(sym_name);
68 
69 	if (i >= 0)
70 		return (void *)ehdr + shdr[syms[i].st_shndx].sh_offset +
71 		       syms[i].st_value;
72 	return NULL;
73 }
74 
update_rela_ref(const char * name)75 static int update_rela_ref(const char *name)
76 {
77 	/*
78 	 * We need to do a couple of things to ensure that the copied RELA data
79 	 * is accessible to the module itself at module init time:
80 	 * - the associated entry in the symbol table needs to refer to the
81 	 *   correct section index, and have SECTION type and GLOBAL linkage.
82 	 * - the 'count' global variable in the module need to be set to the
83 	 *   right value based on the size of the RELA section.
84 	 */
85 	unsigned int *size_var;
86 	int sec_idx, sym_idx;
87 	char str[32];
88 
89 	sprintf(str, "fips140_rela_%s", name);
90 	size_var = get_sym_addr(str);
91 	if (!size_var) {
92 		printf("variable '%s' not found, disregarding .%s section\n",
93 		       str, name);
94 		return 1;
95 	}
96 
97 	sprintf(str, "__sec_rela_%s", name);
98 	sym_idx = get_sym_idx(str);
99 
100 	sprintf(str, ".init.rela.%s", name);
101 	sec_idx = get_section_idx(str);
102 
103 	if (sec_idx < 0 || sym_idx < 0) {
104 		fprintf(stderr, "failed to locate metadata for .%s section in binary\n",
105 			name);
106 		return 0;
107 	}
108 
109 	syms[sym_idx].st_shndx = sec_idx;
110 	syms[sym_idx].st_info = (STB_GLOBAL << 4) | STT_SECTION;
111 
112 	size_var[1] = shdr[sec_idx].sh_size / sizeof(Elf64_Rela);
113 
114 	return 1;
115 }
116 
hmac_section(HMAC_CTX * hmac,const char * start,const char * end)117 static void hmac_section(HMAC_CTX *hmac, const char *start, const char *end)
118 {
119 	void *start_addr = get_sym_addr(start);
120 	void *end_addr = get_sym_addr(end);
121 
122 	HMAC_Update(hmac, start_addr, end_addr - start_addr);
123 }
124 
main(int argc,char ** argv)125 int main(int argc, char **argv)
126 {
127 	Elf64_Shdr *symtab_shdr;
128 	const char *hmac_key;
129 	unsigned char *dg;
130 	unsigned int dglen;
131 	struct stat stat;
132 	HMAC_CTX *hmac;
133 	int fd, ret;
134 
135 	if (argc < 2) {
136 		fprintf(stderr, "file argument missing\n");
137 		exit(EXIT_FAILURE);
138 	}
139 
140 	fd = open(argv[1], O_RDWR);
141 	if (fd < 0) {
142 		fprintf(stderr, "failed to open %s\n", argv[1]);
143 		exit(EXIT_FAILURE);
144 	}
145 
146 	ret = fstat(fd, &stat);
147 	if (ret < 0) {
148 		fprintf(stderr, "failed to stat() %s\n", argv[1]);
149 		exit(EXIT_FAILURE);
150 	}
151 
152 	ehdr = mmap(0, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
153 	if (ehdr == MAP_FAILED) {
154 		fprintf(stderr, "failed to mmap() %s\n", argv[1]);
155 		exit(EXIT_FAILURE);
156 	}
157 
158 	shdr = (void *)ehdr + ehdr->e_shoff;
159 	num_shdr = ehdr->e_shnum;
160 
161 	symtab_shdr = find_symtab_section();
162 
163 	syms = (void *)ehdr + symtab_shdr->sh_offset;
164 	num_syms = symtab_shdr->sh_size / sizeof(Elf64_Sym);
165 
166 	strtab = (void *)ehdr + shdr[symtab_shdr->sh_link].sh_offset;
167 	shstrtab = (void *)ehdr + shdr[ehdr->e_shstrndx].sh_offset;
168 
169 	if (!update_rela_ref("text") || !update_rela_ref("rodata"))
170 		exit(EXIT_FAILURE);
171 
172 	hmac_key = get_sym_addr("fips140_integ_hmac_key");
173 	if (!hmac_key) {
174 		fprintf(stderr, "failed to locate HMAC key in binary\n");
175 		exit(EXIT_FAILURE);
176 	}
177 
178 	dg = get_sym_addr("fips140_integ_hmac_digest");
179 	if (!dg) {
180 		fprintf(stderr, "failed to locate HMAC digest in binary\n");
181 		exit(EXIT_FAILURE);
182 	}
183 
184 	hmac = HMAC_CTX_new();
185 	HMAC_Init_ex(hmac, hmac_key, strlen(hmac_key), EVP_sha256(), NULL);
186 
187 	hmac_section(hmac, "__fips140_text_start", "__fips140_text_end");
188 	hmac_section(hmac, "__fips140_rodata_start", "__fips140_rodata_end");
189 
190 	HMAC_Final(hmac, dg, &dglen);
191 
192 	close(fd);
193 	return 0;
194 }
195