FIPS140 mode is detected on run-time.
[gnutls:gnutls.git] / lib / fips.c
1 /*
2  * Copyright (C) 2013 Red Hat
3  *
4  * Author: Nikos Mavrogiannopoulos
5  *
6  * This file is part of GnuTLS.
7  *
8  * The GnuTLS is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>
20  *
21  */
22 #include <gnutls_int.h>
23 #include <gnutls/gnutls.h>
24 #include <gnutls/crypto.h>
25 #include <unistd.h>
26 #include <gnutls_errors.h>
27 #include <fips.h>
28 #include <stdio.h>
29 #include <random.h>
30 #include <dlfcn.h>
31
32 unsigned int _gnutls_lib_mode = LIB_STATE_POWERON;
33 #ifdef ENABLE_FIPS140
34
35 #define FIPS_KERNEL_FILE "/proc/sys/crypto/fips_enabled"
36 #define FIPS_SYSTEM_FILE "/etc/system-fips"
37
38 unsigned _gnutls_fips_mode_enabled(void)
39 {
40 unsigned f1p, f2p;
41 FILE* fd;
42 static int fips_mode = -1;
43
44         if (fips_mode != -1)
45                 return fips_mode;
46                 
47         fd = fopen(FIPS_KERNEL_FILE, "r");
48         if (fd != NULL) {
49                 f1p = fgetc(fd);
50                 fclose(fd);
51                 
52                 if (f1p == '1') f1p = 1;
53                 else f1p = 0;
54         }
55
56         f2p = !access(FIPS_SYSTEM_FILE, R_OK);
57
58         if (f1p != 0 && f2p != 0) {
59                 _gnutls_debug_log("FIPS140-2 mode enabled\n");
60                 fips_mode = 1;
61                 return fips_mode;
62         }
63
64         if (f2p != 0) {
65                 /* a funny state where self tests are performed
66                  * and ignored */
67                 _gnutls_debug_log("FIPS140-2 ZOMBIE mode enabled\n");
68                 fips_mode = 2;
69                 return fips_mode;
70         }
71
72         fips_mode = 0;
73         return fips_mode;
74 }
75
76 #define GNUTLS_LIBRARY_NAME "libgnutls.so.28"
77 #define TASN1_LIBRARY_NAME "libtasn1.so.6"
78 #define NETTLE_LIBRARY_NAME "libnettle.so.4"
79 #define HOGWEED_LIBRARY_NAME "libhogweed.so.2"
80
81 static const char fips_key[] = "I'd rather be skiing";
82
83 #define HMAC_SUFFIX ".hmac"
84 #define HMAC_SIZE 32
85 #define HMAC_ALGO GNUTLS_MAC_SHA256
86
87 static int get_library_path(const char* lib, const char* symbol, char* path, size_t path_size)
88 {
89 Dl_info info;
90 int ret;
91 void *dl, *sym;
92
93         dl = dlopen(lib, RTLD_LAZY);
94         if (dl == NULL)
95                 return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
96
97         sym = dlsym(dl, symbol);
98         if (sym == NULL) {
99                 ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR);
100                 goto cleanup;
101         }
102         
103         ret = dladdr(sym, &info);
104         if (ret == 0) {
105                 ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR);
106                 goto cleanup;
107         }
108         
109         snprintf(path, path_size, "%s", info.dli_fname);
110
111         ret = 0;
112 cleanup:
113         dlclose(dl);
114         return ret;
115 }
116
117 /* Run an HMAC using the key above on the library binary data. 
118  * Returns true on success and false on error.
119  */
120 static unsigned check_binary_integrity(const char* libname, const char* symbol)
121 {
122         int ret;
123         unsigned prev;
124         char mac_file[GNUTLS_PATH_MAX];
125         char file[GNUTLS_PATH_MAX];
126         uint8_t hmac[HMAC_SIZE];
127         uint8_t new_hmac[HMAC_SIZE];
128         size_t hmac_size;
129         gnutls_datum_t data;
130         
131         ret = get_library_path(libname, symbol, file, sizeof(file));
132         if (ret < 0) {
133                 _gnutls_debug_log("Could not get path for library %s\n", libname);
134                 return 0;
135         }
136
137         _gnutls_debug_log("Loading: %s\n", file);
138         ret = gnutls_load_file(file, &data);
139         if (ret < 0) {
140                 _gnutls_debug_log("Could not load: %s\n", file);
141                 return gnutls_assert_val(0);
142         }
143
144         prev = _gnutls_get_lib_state();
145         _gnutls_switch_lib_state(LIB_STATE_OPERATIONAL);
146         ret = gnutls_hmac_fast(HMAC_ALGO, fips_key, sizeof(fips_key)-1,
147                 data.data, data.size, new_hmac);
148         _gnutls_switch_lib_state(prev);
149         
150         gnutls_free(data.data);
151
152         if (ret < 0)
153                 return gnutls_assert_val(0);
154
155         /* now open the .hmac file and compare */
156         snprintf(mac_file, sizeof(mac_file), "%s"HMAC_SUFFIX, file);
157         
158         ret = gnutls_load_file(mac_file, &data);
159         if (ret < 0) {
160                 _gnutls_debug_log("Could not open %s"HMAC_SUFFIX" for MAC testing: %s\n", file, gnutls_strerror(ret));
161                 return gnutls_assert_val(0);
162         }
163
164         hmac_size = sizeof(hmac);
165         ret = _gnutls_hex2bin((void*)data.data, data.size, hmac, &hmac_size);
166         gnutls_free(data.data);
167
168         if (ret < 0) {
169                 _gnutls_debug_log("Could not convert hex data to binary for MAC testing for %s.\n", libname);
170                 return gnutls_assert_val(0);
171         }
172
173         if (hmac_size != sizeof(hmac) ||
174                         memcmp(hmac, new_hmac, sizeof(hmac)) != 0) {
175                 _gnutls_debug_log("Calculated MAC for %s does not match\n", libname);
176                 return gnutls_assert_val(0);
177         }
178         _gnutls_debug_log("Successfully verified library MAC for %s\n", libname);
179         
180         return 1;
181 }
182
183 int _gnutls_fips_perform_self_checks(void)
184 {
185         int ret;
186
187         _gnutls_switch_lib_state(LIB_STATE_SELFTEST);
188
189         /* Tests the FIPS algorithms */
190
191         /* ciphers */
192         ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_128_CBC);
193         if (ret < 0) {
194                 gnutls_assert();
195                 goto error;
196         }
197
198         ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_192_CBC);
199         if (ret < 0) {
200                 gnutls_assert();
201                 goto error;
202         }
203
204         ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_CBC);
205         if (ret < 0) {
206                 gnutls_assert();
207                 goto error;
208         }
209
210         ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_3DES_CBC);
211         if (ret < 0) {
212                 gnutls_assert();
213                 goto error;
214         }
215
216         ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_128_GCM);
217         if (ret < 0) {
218                 gnutls_assert();
219                 goto error;
220         }
221
222         ret = gnutls_cipher_self_test(0, GNUTLS_CIPHER_AES_256_GCM);
223         if (ret < 0) {
224                 gnutls_assert();
225                 goto error;
226         }
227
228         /* MAC (includes message digest test) */
229         ret = gnutls_mac_self_test(0, GNUTLS_MAC_MD5);
230         if (ret < 0) {
231                 gnutls_assert();
232                 goto error;
233         }
234
235         ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA1);
236         if (ret < 0) {
237                 gnutls_assert();
238                 goto error;
239         }
240
241         ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA224);
242         if (ret < 0) {
243                 gnutls_assert();
244                 goto error;
245         }
246
247         ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA256);
248         if (ret < 0) {
249                 gnutls_assert();
250                 goto error;
251         }
252
253         ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA384);
254         if (ret < 0) {
255                 gnutls_assert();
256                 goto error;
257         }
258
259         ret = gnutls_mac_self_test(0, GNUTLS_MAC_SHA512);
260         if (ret < 0) {
261                 gnutls_assert();
262                 goto error;
263         }
264
265         /* PK */
266         ret = gnutls_pk_self_test(0, GNUTLS_PK_RSA);
267         if (ret < 0) {
268                 gnutls_assert();
269                 goto error;
270         }
271
272         ret = gnutls_pk_self_test(0, GNUTLS_PK_DSA);
273         if (ret < 0) {
274                 gnutls_assert();
275                 goto error;
276         }
277
278         ret = gnutls_pk_self_test(0, GNUTLS_PK_EC);
279         if (ret < 0) {
280                 gnutls_assert();
281                 goto error;
282         }
283
284         ret = gnutls_pk_self_test(0, GNUTLS_PK_DH);
285         if (ret < 0) {
286                 gnutls_assert();
287                 goto error;
288         }
289
290         if (_gnutls_rnd_ops.self_test == NULL) {
291                 gnutls_assert();
292                 goto error;
293         }
294         
295         ret = _gnutls_rnd_ops.self_test();
296         if (ret < 0) {
297                 gnutls_assert();
298                 goto error;
299         }
300
301         ret = check_binary_integrity(GNUTLS_LIBRARY_NAME, "gnutls_global_init");
302         if (ret == 0) {
303                 gnutls_assert();
304                 goto error;
305         }
306
307         ret = check_binary_integrity(TASN1_LIBRARY_NAME, "asn1_check_version");
308         if (ret == 0) {
309                 gnutls_assert();
310                 goto error;
311         }
312
313         ret = check_binary_integrity(NETTLE_LIBRARY_NAME, "nettle_aes_set_encrypt_key");
314         if (ret == 0) {
315                 gnutls_assert();
316                 goto error;
317         }
318
319         ret = check_binary_integrity(HOGWEED_LIBRARY_NAME, "nettle_mpz_sizeinbase_256_u");
320         if (ret == 0) {
321                 gnutls_assert();
322                 goto error;
323         }
324         
325         return 0;
326
327 error:
328         _gnutls_switch_lib_state(LIB_STATE_ERROR);
329         _gnutls_audit_log(NULL, "FIPS140-2 self testing failed\n");
330
331         return GNUTLS_E_SELF_TEST_ERROR;
332 }
333 #endif
334
335 /**
336  * gnutls_fips140_mode_enabled:
337  *
338  * Checks whether this library is in FIPS140 mode.
339  *
340  * Returns: return non-zero if true or zero if false.
341  *
342  * Since: 3.3.0
343  **/
344 int gnutls_fips140_mode_enabled(void)
345 {
346 #ifdef ENABLE_FIPS140
347 int ret = _gnutls_fips_mode_enabled();
348
349         if (ret == 1)
350                 return ret;
351 #endif
352         return 0;
353 }
354
355 void _gnutls_lib_simulate_error(void)
356 {
357         _gnutls_switch_lib_state(LIB_STATE_ERROR);
358 }