1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25
26 #include <fips/fips_checksum.h>
27
28
29 #ifdef _KERNEL
30 #define FIPS_ALLOC(size) kmem_alloc(size, KM_SLEEP)
31 #define FIPS_FREE(buf, size) kmem_free(buf, size)
32 #define FIPS_READ_FILE kobj_read_file
33 #define ERRLOG0(str) cmn_err(CE_NOTE, str)
34 #define ERRLOG1(fmt, arg) cmn_err(CE_NOTE, fmt, arg)
35 #include <sys/sunddi.h>
36
37 struct _buf *kobj_open_file(char *name);
38 int kobj_read_file(struct _buf *file, char *buf, uint_t size, uint_t off);
39 #else
40
41 #define FIPS_ALLOC(size) malloc(size)
42 #define FIPS_FREE(buf, size) free(buf)
43 #define FIPS_READ_FILE fips_read_file
44 #define ERRLOG0(str) (void) printf(str)
45 #define ERRLOG1(fmt, arg) (void) printf(fmt, arg)
46 #endif
47
48 #define NUM_SECTIONS (sizeof (checked_sec_names) / sizeof (char *))
49
50 static char *checked_sec_names[] = {
51 ".strtab",
52 ".dynamic",
53 ".compcom",
54 ".comment",
55 ".dynstr",
56 ".shstrtab",
57 ".rela.text",
58 ".rela.data",
59 ".text",
60 ".rodata",
61 ".rodata1",
62 ".data",
63 ".symtab",
64 ".SUNW_ctf",
65 ".bss"
66 };
67
68
69 static int
70 #ifdef _KERNEL
process_section(SHA1_CTX * shactx,Elf64_Shdr * section,struct _buf * file,char * shstrtab)71 process_section(SHA1_CTX *shactx, Elf64_Shdr *section, struct _buf *file,
72 char *shstrtab)
73 #else
74 process_section(SHA1_CTX *shactx, Elf64_Shdr *section, int file,
75 char *shstrtab)
76 #endif
77 {
78 size_t size, offs;
79 char *name;
80 int doit = 0;
81 char *buf;
82 int i;
83
84 size = section->sh_size;
85 offs = section->sh_offset;
86 name = shstrtab + section->sh_name;
87 for (i = 0; i < NUM_SECTIONS; i++) {
88 if (strncmp(name, checked_sec_names[i],
89 strlen(checked_sec_names[i]) + 1) == 0) {
90 doit++;
91 break;
92 }
93 }
94
95 if (!doit) {
96 return (0);
97 }
98
99 /* hash the size of .bss section */
100 if (strcmp(name, ".bss") == 0) {
101 char szstr[32];
102 (void) snprintf(szstr, sizeof (szstr), "%ld", size);
103 SHA1Update(shactx, szstr, strlen(szstr));
104 return (0);
105 }
106
107
108 /* hash the contents of the section */
109 if ((buf = FIPS_ALLOC(size)) == NULL) {
110 ERRLOG1("Not enough memory for section %s\n", name);
111 return (-1);
112 }
113
114 if (FIPS_READ_FILE(file, buf, size, offs) < 0) {
115 FIPS_FREE(buf, size);
116 return (-2);
117 }
118
119 SHA1Update(shactx, buf, size);
120
121 FIPS_FREE(buf, size);
122
123 return (0);
124 }
125
126 int
127 #ifdef _KERNEL
fips_calc_checksum(struct _buf * file,Elf64_Ehdr * ehdr,char * sha1buf)128 fips_calc_checksum(struct _buf *file, Elf64_Ehdr *ehdr, char *sha1buf)
129 #else
130 fips_calc_checksum(int file, Elf64_Ehdr *ehdr, char *sha1buf)
131 #endif
132 {
133 unsigned int size, numsec;
134 Elf64_Shdr *shdrs;
135 Elf64_Shdr *section;
136 SHA1_CTX sha1ctx;
137 char *shstrtab;
138 int i;
139
140 numsec = ehdr->e_shnum;
141 size = ehdr->e_shentsize * numsec;
142 if ((shdrs = (Elf64_Shdr *)FIPS_ALLOC(size)) == NULL) {
143 ERRLOG0("Not enough memory for shdrs\n");
144 return (FAILURE);
145 }
146 if (FIPS_READ_FILE(file, (char *)shdrs, size, ehdr->e_shoff) < 0) {
147 return (FAILURE);
148 }
149
150 /* Obtain the .shstrtab data buffer */
151 section = &(shdrs[ehdr->e_shstrndx]);
152 size = section->sh_size;
153 if ((shstrtab = (char *)FIPS_ALLOC(size)) == NULL) {
154 ERRLOG0("Not enough memory for shstrtab\n");
155 return (FAILURE);
156 }
157 if (FIPS_READ_FILE(file, shstrtab, size, section->sh_offset) < 0) {
158 return (FAILURE);
159 }
160
161 SHA1Init(&sha1ctx);
162 for (i = 0; i < numsec; i++) {
163 if (process_section(&sha1ctx, &(shdrs[i]),
164 file, shstrtab) < 0) {
165 return (FAILURE);
166 }
167 }
168 SHA1Final(sha1buf, &sha1ctx);
169
170 return (0);
171 }
172
173
174 #ifndef _KERNEL
175
176 int
fips_read_file(int fd,char * buf,int size,int offs)177 fips_read_file(int fd, char *buf, int size, int offs)
178 {
179 int i;
180
181 if (lseek(fd, offs, SEEK_SET) == (off_t)(-1)) {
182 (void) fprintf(stderr,
183 "lseek returned an error for file %d\n", fd);
184 return (-1);
185 }
186 while ((i = read(fd, buf, size)) >= 0) {
187 if (size == i) {
188 break;
189 } else {
190 size -= i;
191 buf += i;
192 }
193 }
194 if (i < 0) {
195 (void) fprintf(stderr, "read failed for file %d\n", fd);
196 return (-2);
197 }
198
199 return (0);
200 }
201
202 #else
203
204 static int
get_fips_section(Elf64_Ehdr * ehdr,struct _buf * file,char * expected_checksum)205 get_fips_section(Elf64_Ehdr *ehdr, struct _buf *file, char *expected_checksum)
206 {
207 unsigned int shdrssz, shstrtabsz, numsec;
208 Elf64_Shdr *shdrs = NULL;
209 Elf64_Shdr *section;
210 char *shstrtab = NULL;
211 char *name;
212 int rv = FAILURE;
213 int i;
214
215 numsec = ehdr->e_shnum;
216 shdrssz = ehdr->e_shentsize * numsec;
217 if ((shdrs = (Elf64_Shdr *)FIPS_ALLOC(shdrssz)) == NULL) {
218 ERRLOG0("Not enough memory for shdrs\n");
219 return (FAILURE);
220 }
221 if (FIPS_READ_FILE(file, (char *)shdrs, shdrssz, ehdr->e_shoff) < 0) {
222 goto exit;
223 }
224
225 /* Obtain the .shstrtab data buffer */
226 section = &(shdrs[ehdr->e_shstrndx]);
227 shstrtabsz = section->sh_size;
228 if ((shstrtab = (char *)FIPS_ALLOC(shstrtabsz)) == NULL) {
229 ERRLOG0("Not enough memory for shstrtab\n");
230 goto exit;
231 }
232 if (FIPS_READ_FILE(file, shstrtab, shstrtabsz,
233 section->sh_offset) < 0) {
234 goto exit;
235 }
236
237 for (i = 0; i < numsec; i++) {
238 section = &shdrs[i];
239 name = shstrtab + section->sh_name;
240 /* Get the checksum stored in the .SUNW_fips section */
241 if (strcmp(name, ".SUNW_fips") == 0) {
242 if (section->sh_size != SHA1_DIGEST_LENGTH) {
243 goto exit;
244 }
245 if (FIPS_READ_FILE(file, expected_checksum,
246 section->sh_size, section->sh_offset) < 0) {
247 goto exit;
248 }
249 rv = 0;
250 goto exit;
251 }
252 }
253
254
255 exit:
256 if (shdrs != NULL) {
257 FIPS_FREE(shdrs, shdrssz);
258 }
259 if (shstrtab != NULL) {
260 FIPS_FREE(shstrtab, shstrtabsz);
261 }
262
263 return (rv);
264 }
265
266
267 int
fips_check_module(char * modname,void * _initaddr)268 fips_check_module(char *modname, void *_initaddr)
269 {
270 struct modctl *modctlp = NULL;
271 struct module *mp = NULL;
272 struct _buf *file;
273 char *filename;
274 Elf64_Ehdr ehdr;
275 unsigned int size, i;
276 char sha1buf[SHA1_DIGEST_LENGTH];
277 char expected_checksum[SHA1_DIGEST_LENGTH];
278
279 modctlp = mod_find_by_filename(NULL, modname);
280 if (modctlp == NULL) {
281 ERRLOG1("module with modname %s not found\n", modname);
282 return (FAILURE);
283 }
284 mp = (struct module *)modctlp->mod_mp;
285 if (mp != NULL && mp->filename != NULL) {
286 filename = mp->filename;
287 } else {
288 /* filename does not exist */
289 return (FAILURE);
290 }
291 if ((mp->text > (char *)_initaddr) ||
292 (mp->text + mp->text_size < (char *)_initaddr)) {
293 ERRLOG1("_init() is not in module %s\n", modname);
294 return (FAILURE);
295 }
296
297 if ((file = kobj_open_file(filename)) == (struct _buf *)-1) {
298 ERRLOG1("Cannot open %s\n", filename);
299 return (FAILURE);
300 }
301 /* Read the ELF header */
302 size = sizeof (ehdr);
303 if (kobj_read_file(file, (char *)(&ehdr), size, 0) < 0) {
304 goto fail_exit;
305 }
306
307 /* check if it is an ELF file */
308 for (i = 0; i < SELFMAG; i++) {
309 if (ehdr.e_ident[i] != ELFMAG[i]) {
310 ERRLOG1("%s not an elf file\n", filename);
311 goto fail_exit;
312 }
313 }
314
315 /* check if it is relocatable */
316 if (ehdr.e_type != ET_REL) {
317 ERRLOG1("%s isn't a relocatable (ET_REL) "
318 "module\n", filename);
319 goto fail_exit;
320 }
321
322 if (fips_calc_checksum(file, &ehdr, sha1buf) < 0) {
323 goto fail_exit;
324 }
325
326 if (get_fips_section(&ehdr, file, expected_checksum) < 0) {
327 goto fail_exit;
328 }
329
330 if (memcmp(sha1buf, expected_checksum, SHA1_DIGEST_LENGTH) != 0) {
331 goto fail_exit;
332 }
333
334 kobj_close_file(file);
335
336 return (SUCCESS);
337
338 fail_exit:
339
340 kobj_close_file(file);
341
342 return (FAILURE);
343
344 }
345
346 #endif
347