]> git.ozlabs.org Git - petitboot/blob - lib/security/gpg.c
lib/security: hard_lockdown flag to stop runtime disable of signed boot
[petitboot] / lib / security / gpg.c
1 /*
2  *  Copyright (C) 2016 Raptor Engineering, LLC
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; version 2 of the License.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  *
13  *  You should have received a copy of the GNU General Public License
14  *  along with this program; if not, write to the Free Software
15  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #if defined(HAVE_CONFIG_H)
19 #include "config.h"
20 #endif
21
22 #include <stdbool.h>
23 #include <stdlib.h>
24 #include <assert.h>
25 #include <dirent.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <locale.h>
29 #include <sys/types.h>
30
31 #include <log/log.h>
32 #include <file/file.h>
33 #include <talloc/talloc.h>
34 #include <url/url.h>
35 #include <util/util.h>
36 #include <i18n/i18n.h>
37
38 #include <gpgme.h>
39
40 #include "security.h"
41
42 /*
43  * If --with-signed-boot is enabled lib/security provides the ability to handle
44  * gpg-signed and/or encrypted boot sources (kernel, initrd, etc).
45  * This can be used to enable a form of secure boot, but it is important to
46  * recognise that it depends on the security of the entire system, for example
47  * a full trusted-boot implementation. Petitboot can not and will not be able
48  * to guarantee secure boot by itself.
49  */
50
51 int decrypt_file(const char *filename,
52         FILE *authorized_signatures_handle, const char *keyring_path)
53 {
54         int result = 0;
55         int valid = 0;
56         size_t bytes_read = 0;
57         unsigned char buffer[8192];
58
59         if (filename == NULL)
60                 return -1;
61
62         gpgme_signature_t verification_signatures;
63         gpgme_verify_result_t verification_result;
64         gpgme_data_t ciphertext_data;
65         gpgme_data_t plaintext_data;
66         gpgme_engine_info_t enginfo;
67         gpgme_ctx_t gpg_context;
68         gpgme_error_t err;
69
70         /* Initialize gpgme */
71         setlocale (LC_ALL, "");
72         gpgme_check_version(NULL);
73         gpgme_set_locale(NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
74         err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
75         if (err != GPG_ERR_NO_ERROR) {
76                 pb_log("%s: OpenPGP support not available\n", __func__);
77                 return -1;
78         }
79         err = gpgme_get_engine_info(&enginfo);
80         if (err != GPG_ERR_NO_ERROR) {
81                 pb_log("%s: GPG engine failed to initialize\n", __func__);
82                 return -1;
83         }
84         err = gpgme_new(&gpg_context);
85         if (err != GPG_ERR_NO_ERROR) {
86                 pb_log("%s: GPG context could not be created\n", __func__);
87                 return -1;
88         }
89         err = gpgme_set_protocol(gpg_context, GPGME_PROTOCOL_OpenPGP);
90         if (err != GPG_ERR_NO_ERROR) {
91                 pb_log("%s: GPG protocol could not be set\n", __func__);
92                 return -1;
93         }
94         if (keyring_path)
95                 err = gpgme_ctx_set_engine_info (gpg_context,
96                         GPGME_PROTOCOL_OpenPGP,
97                         enginfo->file_name, keyring_path);
98         else
99                 err = gpgme_ctx_set_engine_info (gpg_context,
100                         GPGME_PROTOCOL_OpenPGP,
101                         enginfo->file_name, enginfo->home_dir);
102         if (err != GPG_ERR_NO_ERROR) {
103                 pb_log("%s: Could not set GPG engine information\n", __func__);
104                 return -1;
105         }
106         err = gpgme_data_new(&plaintext_data);
107         if (err != GPG_ERR_NO_ERROR) {
108                 pb_log("%s: Could not create GPG plaintext data buffer\n",
109                         __func__);
110                 return -1;
111         }
112         err = gpgme_data_new_from_file(&ciphertext_data, filename, 1);
113         if (err != GPG_ERR_NO_ERROR) {
114                 pb_log("%s: Could not create GPG ciphertext data buffer"
115                         " from file '%s'\n", __func__, filename);
116                 return -1;
117         }
118
119         /* Decrypt and verify file */
120         err = gpgme_op_decrypt_verify(gpg_context, ciphertext_data,
121                 plaintext_data);
122         if (err != GPG_ERR_NO_ERROR) {
123                 pb_log("%s: Could not decrypt file\n", __func__);
124                 return -1;
125         }
126         verification_result = gpgme_op_verify_result(gpg_context);
127         verification_signatures = verification_result->signatures;
128         while (verification_signatures) {
129                 if (verification_signatures->status == GPG_ERR_NO_ERROR) {
130                         pb_log("%s: Good signature for key ID '%s' ('%s')\n",
131                                 __func__,
132                                 verification_signatures->fpr, filename);
133                         /* Verify fingerprint is present in authorized
134                          * signatures file
135                          */
136                         char *auth_sig_line = NULL;
137                         size_t auth_sig_len = 0;
138                         ssize_t auth_sig_read;
139                         rewind(authorized_signatures_handle);
140                         while ((auth_sig_read = getline(&auth_sig_line,
141                                 &auth_sig_len,
142                                 authorized_signatures_handle)) != -1) {
143                                 auth_sig_len = strlen(auth_sig_line);
144                                 while ((auth_sig_line[auth_sig_len-1] == '\n')
145                                         || (auth_sig_line[auth_sig_len-1] == '\r'))
146                                         auth_sig_len--;
147                                 auth_sig_line[auth_sig_len] = 0;
148                                 if (strcmp(auth_sig_line,
149                                         verification_signatures->fpr) == 0)
150                                         valid = 1;
151                         }
152                         free(auth_sig_line);
153                 }
154                 else {
155                         pb_log("%s: Signature for key ID '%s' ('%s') invalid."
156                                 "  Status: %08x\n", __func__,
157                                 verification_signatures->fpr, filename,
158                                 verification_signatures->status);
159                 }
160                 verification_signatures = verification_signatures->next;
161         }
162
163         gpgme_data_release(ciphertext_data);
164
165         if (valid) {
166                 /* Write decrypted file over ciphertext */
167                 FILE *plaintext_file_handle = NULL;
168                 plaintext_file_handle = fopen(filename, "wb");
169                 if (!plaintext_file_handle) {
170                         pb_log("%s: Could not create GPG plaintext file '%s'\n",
171                                 __func__, filename);
172                         gpgme_data_release(plaintext_data);
173                         gpgme_release(gpg_context);
174                         return -1;
175                 }
176                 gpgme_data_seek(plaintext_data, 0, SEEK_SET);
177                 if (err != GPG_ERR_NO_ERROR) {
178                         pb_log("%s: Could not seek in GPG plaintext buffer\n",
179                                 __func__);
180                         gpgme_data_release(plaintext_data);
181                         gpgme_release(gpg_context);
182                         fclose(plaintext_file_handle);
183                         return -1;
184                 }
185                 while ((bytes_read = gpgme_data_read(plaintext_data, buffer,
186                         8192)) > 0) {
187                         size_t l2 = fwrite(buffer, 1, bytes_read,
188                                 plaintext_file_handle);
189                         if (l2 < bytes_read) {
190                                 if (ferror(plaintext_file_handle)) {
191                                         /* General error */
192                                         result = -1;
193                                         pb_log("%s: failed: unknown fault\n",
194                                                 __func__);
195                                 }
196                                 else {
197                                         /* No space on destination device */
198                                         result = -1;
199                                         pb_log("%s: failed: temporary storage"
200                                                 " full\n", __func__);
201                                 }
202                                 break;
203                         }
204                 }
205                 fclose(plaintext_file_handle);
206         }
207
208         /* Clean up */
209         gpgme_data_release(plaintext_data);
210         gpgme_release(gpg_context);
211
212         if (!valid) {
213                 pb_log("%s: Incorrect GPG signature\n", __func__);
214                 return -1;
215         }
216
217         pb_log("%s: GPG signature for decrypted file '%s' verified\n",
218                 __func__, filename);
219
220         return result;
221 }
222
223 int verify_file_signature(const char *plaintext_filename,
224         const char *signature_filename, FILE *authorized_signatures_handle,
225         const char *keyring_path)
226 {
227         int valid = 0;
228         gpgme_signature_t verification_signatures;
229         gpgme_verify_result_t verification_result;
230         gpgme_data_t plaintext_data;
231         gpgme_data_t signature_data;
232         gpgme_engine_info_t enginfo;
233         gpgme_ctx_t gpg_context;
234         gpgme_error_t err;
235
236         if (signature_filename == NULL)
237                 return -1;
238
239         /* Initialize gpgme */
240         setlocale (LC_ALL, "");
241         gpgme_check_version(NULL);
242         gpgme_set_locale(NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
243         err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
244         if (err != GPG_ERR_NO_ERROR) {
245                 pb_log("%s: OpenPGP support not available\n", __func__);
246                 return -1;
247         }
248         err = gpgme_get_engine_info(&enginfo);
249         if (err != GPG_ERR_NO_ERROR) {
250                 pb_log("%s: GPG engine failed to initialize\n", __func__);
251                 return -1;
252         }
253         err = gpgme_new(&gpg_context);
254         if (err != GPG_ERR_NO_ERROR) {
255                 pb_log("%s: GPG context could not be created\n", __func__);
256                 return -1;
257         }
258         err = gpgme_set_protocol(gpg_context, GPGME_PROTOCOL_OpenPGP);
259         if (err != GPG_ERR_NO_ERROR) {
260                 pb_log("%s: GPG protocol could not be set\n", __func__);
261                 return -1;
262         }
263         if (keyring_path)
264                 err = gpgme_ctx_set_engine_info (gpg_context,
265                         GPGME_PROTOCOL_OpenPGP, enginfo->file_name,
266                         keyring_path);
267         else
268                 err = gpgme_ctx_set_engine_info (gpg_context,
269                         GPGME_PROTOCOL_OpenPGP, enginfo->file_name,
270                         enginfo->home_dir);
271         if (err != GPG_ERR_NO_ERROR) {
272                 pb_log("%s: Could not set GPG engine information\n", __func__);
273                 return -1;
274         }
275         err = gpgme_data_new_from_file(&plaintext_data, plaintext_filename, 1);
276         if (err != GPG_ERR_NO_ERROR) {
277                 pb_log("%s: Could not create GPG plaintext data buffer"
278                         " from file '%s'\n", __func__, plaintext_filename);
279                 return -1;
280         }
281         err = gpgme_data_new_from_file(&signature_data, signature_filename, 1);
282         if (err != GPG_ERR_NO_ERROR) {
283                 pb_log("%s: Could not create GPG signature data buffer"
284                         " from file '%s'\n", __func__, signature_filename);
285                 return -1;
286         }
287
288         /* Check signature */
289         err = gpgme_op_verify(gpg_context, signature_data, plaintext_data,
290                 NULL);
291         if (err != GPG_ERR_NO_ERROR) {
292                 pb_log("%s: Could not verify file using GPG signature '%s'\n",
293                         __func__, signature_filename);
294                 return -1;
295         }
296         verification_result = gpgme_op_verify_result(gpg_context);
297         verification_signatures = verification_result->signatures;
298         while (verification_signatures) {
299                 if (verification_signatures->status != GPG_ERR_NO_ERROR) {
300                         /* Signature verification failure */
301                         pb_log("%s: Signature for key ID '%s' ('%s') invalid."
302                                 "  Status: %08x\n", __func__,
303                                 verification_signatures->fpr,
304                                 signature_filename,
305                                 verification_signatures->status);
306                         verification_signatures = verification_signatures->next;
307                         continue;
308                 }
309
310                 /* Signature check passed with no error */
311                 pb_log("%s: Good signature for key ID '%s' ('%s')\n",
312                         __func__, verification_signatures->fpr,
313                         signature_filename);
314                 /* Verify fingerprint is present in
315                         * authorized signatures file
316                         */
317                 char *auth_sig_line = NULL;
318                 size_t auth_sig_len = 0;
319                 ssize_t auth_sig_read;
320                 rewind(authorized_signatures_handle);
321                 while ((auth_sig_read = getline(&auth_sig_line,
322                         &auth_sig_len,
323                         authorized_signatures_handle)) != -1) {
324                         auth_sig_len = strlen(auth_sig_line);
325                         while ((auth_sig_line[auth_sig_len-1] == '\n')
326                                 || (auth_sig_line[auth_sig_len-1] == '\r'))
327                                 auth_sig_len--;
328                         auth_sig_line[auth_sig_len] = '\0';
329                         if (strcmp(auth_sig_line,
330                                 verification_signatures->fpr) == 0)
331                                 valid = 1;
332                 }
333                 free(auth_sig_line);
334                 verification_signatures = verification_signatures->next;
335         }
336
337         /* Clean up */
338         gpgme_data_release(plaintext_data);
339         gpgme_data_release(signature_data);
340         gpgme_release(gpg_context);
341
342         if (!valid) {
343                 pb_log("%s: Incorrect GPG signature\n", __func__);
344                 return -1;
345         }
346
347         pb_log("%s: GPG signature '%s' for file '%s' verified\n",
348                 __func__, signature_filename, plaintext_filename);
349
350         return 0;
351 }
352
353 int lockdown_status() {
354         /* assume most restrictive lockdown type */
355         int ret = PB_LOCKDOWN_SIGN;
356
357 #if !defined(HARD_LOCKDOWN)
358         if (access(LOCKDOWN_FILE, F_OK) == -1)
359                 return PB_LOCKDOWN_NONE;
360 #endif
361
362         /* determine lockdown type */
363         FILE *authorized_signatures_handle = NULL;
364         authorized_signatures_handle = fopen(LOCKDOWN_FILE, "r");
365         if (!authorized_signatures_handle)
366                 return ret;
367
368         char *auth_sig_line = NULL;
369         size_t auth_sig_len = 0;
370         ssize_t auth_sig_read;
371         rewind(authorized_signatures_handle);
372         if ((auth_sig_read = getline(&auth_sig_line,
373                 &auth_sig_len,
374                 authorized_signatures_handle)) != -1) {
375                 auth_sig_len = strlen(auth_sig_line);
376                 while ((auth_sig_line[auth_sig_len-1] == '\n')
377                     || (auth_sig_line[auth_sig_len-1] == '\r'))
378                         auth_sig_len--;
379                 auth_sig_line[auth_sig_len] = 0;
380                 if (strcmp(auth_sig_line, "ENCRYPTED") == 0) {
381                         /* first line indicates encrypted files
382                          * expected.  enable decryption.
383                          */
384                         ret = PB_LOCKDOWN_DECRYPT;
385                 }
386         }
387         free(auth_sig_line);
388
389     return ret;
390 }
391