Add support for GPG signature enforcement on booted
[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 <sys/types.h>
29
30 #include <log/log.h>
31 #include <file/file.h>
32 #include <talloc/talloc.h>
33 #include <url/url.h>
34 #include <util/util.h>
35 #include <i18n/i18n.h>
36
37 #include "gpg.h"
38
39 /*
40  * If --with-signed-boot is enabled lib/security provides the ability to handle
41  * gpg-signed and/or encrypted boot sources (kernel, initrd, etc).
42  * This can be used to enable a form of secure boot, but it is important to
43  * recognise that it depends on the security of the entire system, for example
44  * a full trusted-boot implementation. Petitboot can not and will not be able
45  * to guarantee secure boot by itself.
46  */
47
48 struct pb_url * gpg_get_signature_url(void *ctx, struct pb_url *base_file)
49 {
50         struct pb_url *signature_file = NULL;
51
52         signature_file = pb_url_copy(ctx, base_file);
53         talloc_free(signature_file->file);
54         signature_file->file = talloc_asprintf(signature_file,
55                 "%s.sig", base_file->file);
56         talloc_free(signature_file->path);
57         signature_file->path = talloc_asprintf(signature_file,
58                 "%s.sig", base_file->path);
59
60         return signature_file;
61 }
62
63 int verify_file_signature(const char *plaintext_filename,
64         const char *signature_filename, FILE *authorized_signatures_handle,
65         const char *keyring_path)
66 {
67         int valid = 0;
68         gpgme_signature_t verification_signatures;
69         gpgme_verify_result_t verification_result;
70         gpgme_data_t plaintext_data;
71         gpgme_data_t signature_data;
72         gpgme_engine_info_t enginfo;
73         gpgme_ctx_t gpg_context;
74         gpgme_error_t err;
75
76         if (signature_filename == NULL)
77                 return -1;
78
79         /* Initialize gpgme */
80         setlocale (LC_ALL, "");
81         gpgme_check_version(NULL);
82         gpgme_set_locale(NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
83         err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
84         if (err != GPG_ERR_NO_ERROR) {
85                 pb_log("%s: OpenPGP support not available\n", __func__);
86                 return -1;
87         }
88         err = gpgme_get_engine_info(&enginfo);
89         if (err != GPG_ERR_NO_ERROR) {
90                 pb_log("%s: GPG engine failed to initialize\n", __func__);
91                 return -1;
92         }
93         err = gpgme_new(&gpg_context);
94         if (err != GPG_ERR_NO_ERROR) {
95                 pb_log("%s: GPG context could not be created\n", __func__);
96                 return -1;
97         }
98         err = gpgme_set_protocol(gpg_context, GPGME_PROTOCOL_OpenPGP);
99         if (err != GPG_ERR_NO_ERROR) {
100                 pb_log("%s: GPG protocol could not be set\n", __func__);
101                 return -1;
102         }
103         if (keyring_path)
104                 err = gpgme_ctx_set_engine_info (gpg_context,
105                         GPGME_PROTOCOL_OpenPGP, enginfo->file_name,
106                         keyring_path);
107         else
108                 err = gpgme_ctx_set_engine_info (gpg_context,
109                         GPGME_PROTOCOL_OpenPGP, enginfo->file_name,
110                         enginfo->home_dir);
111         if (err != GPG_ERR_NO_ERROR) {
112                 pb_log("%s: Could not set GPG engine information\n", __func__);
113                 return -1;
114         }
115         err = gpgme_data_new_from_file(&plaintext_data, plaintext_filename, 1);
116         if (err != GPG_ERR_NO_ERROR) {
117                 pb_log("%s: Could not create GPG plaintext data buffer"
118                         " from file '%s'\n", __func__, plaintext_filename);
119                 return -1;
120         }
121         err = gpgme_data_new_from_file(&signature_data, signature_filename, 1);
122         if (err != GPG_ERR_NO_ERROR) {
123                 pb_log("%s: Could not create GPG signature data buffer"
124                         " from file '%s'\n", __func__, signature_filename);
125                 return -1;
126         }
127
128         /* Check signature */
129         err = gpgme_op_verify(gpg_context, signature_data, plaintext_data,
130                 NULL);
131         if (err != GPG_ERR_NO_ERROR) {
132                 pb_log("%s: Could not verify file using GPG signature '%s'\n",
133                         __func__, signature_filename);
134                 return -1;
135         }
136         verification_result = gpgme_op_verify_result(gpg_context);
137         verification_signatures = verification_result->signatures;
138         while (verification_signatures) {
139                 if (verification_signatures->status != GPG_ERR_NO_ERROR) {
140                         /* Signature verification failure */
141                         pb_log("%s: Signature for key ID '%s' ('%s') invalid."
142                                 "  Status: %08x\n", __func__,
143                                 verification_signatures->fpr,
144                                 signature_filename,
145                                 verification_signatures->status);
146                         verification_signatures = verification_signatures->next;
147                         continue;
148                 }
149
150                 /* Signature check passed with no error */
151                 pb_log("%s: Good signature for key ID '%s' ('%s')\n",
152                         __func__, verification_signatures->fpr,
153                         signature_filename);
154                 /* Verify fingerprint is present in
155                         * authorized signatures file
156                         */
157                 char *auth_sig_line = NULL;
158                 size_t auth_sig_len = 0;
159                 ssize_t auth_sig_read;
160                 rewind(authorized_signatures_handle);
161                 while ((auth_sig_read = getline(&auth_sig_line,
162                         &auth_sig_len,
163                         authorized_signatures_handle)) != -1) {
164                         auth_sig_len = strlen(auth_sig_line);
165                         while ((auth_sig_line[auth_sig_len-1] == '\n')
166                                 || (auth_sig_line[auth_sig_len-1] == '\r'))
167                                 auth_sig_len--;
168                         auth_sig_line[auth_sig_len] = '\0';
169                         if (strcmp(auth_sig_line,
170                                 verification_signatures->fpr) == 0)
171                                 valid = 1;
172                 }
173                 free(auth_sig_line);
174                 verification_signatures = verification_signatures->next;
175         }
176
177         /* Clean up */
178         gpgme_data_release(plaintext_data);
179         gpgme_data_release(signature_data);
180         gpgme_release(gpg_context);
181
182         if (!valid) {
183                 pb_log("%s: Incorrect GPG signature\n", __func__);
184                 return -1;
185         }
186
187         pb_log("%s: GPG signature '%s' for file '%s' verified\n",
188                 __func__, signature_filename, plaintext_filename);
189
190         return 0;
191 }
192
193 int gpg_validate_boot_files(struct boot_task *boot_task) {
194         int result = 0;
195         char *kernel_filename = NULL;
196         char *initrd_filename = NULL;
197         char *dtb_filename = NULL;
198
199         FILE *authorized_signatures_handle = NULL;
200
201         char cmdline_template[] = "/tmp/petitbootXXXXXX";
202         int cmdline_fd = mkstemp(cmdline_template);
203         FILE *cmdline_handle = NULL;
204
205         const char* local_initrd_signature = (boot_task->verify_signature) ?
206                 boot_task->local_initrd_signature : NULL;
207         const char* local_dtb_signature = (boot_task->verify_signature) ?
208                 boot_task->local_dtb_signature : NULL;
209         const char* local_image_signature = (boot_task->verify_signature) ?
210                 boot_task->local_image_signature : NULL;
211         const char* local_cmdline_signature = (boot_task->verify_signature) ?
212                 boot_task->local_cmdline_signature : NULL;
213
214         if (!boot_task->verify_signature)
215                 return result;
216
217         /* Load authorized signatures file */
218         authorized_signatures_handle = fopen(LOCKDOWN_FILE, "r");
219         if (!authorized_signatures_handle) {
220                 pb_log("%s: unable to read lockdown file\n", __func__);
221                 return KEXEC_LOAD_SIG_SETUP_INVALID;
222         }
223
224         /* Copy files to temporary directory for verification / boot */
225         result = copy_file_secure_dest(boot_task,
226                 boot_task->local_image,
227                 &kernel_filename);
228         if (result) {
229                 pb_log("%s: image copy failed: (%d)\n",
230                         __func__, result);
231                 return result;
232         }
233         if (boot_task->local_initrd) {
234                 result = copy_file_secure_dest(boot_task,
235                         boot_task->local_initrd,
236                         &initrd_filename);
237                 if (result) {
238                         pb_log("%s: initrd copy failed: (%d)\n",
239                                 __func__, result);
240                         return result;
241                 }
242         }
243         if (boot_task->local_dtb) {
244                 result = copy_file_secure_dest(boot_task,
245                         boot_task->local_dtb,
246                         &dtb_filename);
247                 if (result) {
248                         pb_log("%s: dtb copy failed: (%d)\n",
249                                 __func__, result);
250                         return result;
251                 }
252         }
253         boot_task->local_image_override = talloc_strdup(boot_task,
254                 kernel_filename);
255         if (boot_task->local_initrd)
256                 boot_task->local_initrd_override = talloc_strdup(boot_task,
257                         initrd_filename);
258         if (boot_task->local_dtb)
259                 boot_task->local_dtb_override = talloc_strdup(boot_task,
260                         dtb_filename);
261
262         /* Write command line to temporary file for verification */
263         if (cmdline_fd < 0) {
264                 /* mkstemp failed */
265                 pb_log("%s: failed: unable to create command line"
266                         " temporary file for verification\n",
267                         __func__);
268                 result = -1;
269         }
270         else {
271                 cmdline_handle = fdopen(cmdline_fd, "w");
272         }
273         if (!cmdline_handle) {
274                 /* Failed to open file */
275                 pb_log("%s: failed: unable to write command line"
276                         " temporary file for verification\n",
277                         __func__);
278                 result = -1;
279         }
280         else {
281                 fwrite(boot_task->args, sizeof(char),
282                         strlen(boot_task->args), cmdline_handle);
283                 fflush(cmdline_handle);
284         }
285
286         /* Check signatures */
287         if (verify_file_signature(kernel_filename,
288                 local_image_signature,
289                 authorized_signatures_handle, "/etc/gpg"))
290                 result = KEXEC_LOAD_SIGNATURE_FAILURE;
291         if (verify_file_signature(cmdline_template,
292                 local_cmdline_signature,
293                 authorized_signatures_handle, "/etc/gpg"))
294                 result = KEXEC_LOAD_SIGNATURE_FAILURE;
295         if (boot_task->local_initrd_signature)
296                 if (verify_file_signature(initrd_filename,
297                         local_initrd_signature,
298                         authorized_signatures_handle, "/etc/gpg"))
299                         result = KEXEC_LOAD_SIGNATURE_FAILURE;
300         if (boot_task->local_dtb_signature)
301                 if (verify_file_signature(dtb_filename,
302                         local_dtb_signature,
303                         authorized_signatures_handle, "/etc/gpg"))
304                         result = KEXEC_LOAD_SIGNATURE_FAILURE;
305
306         /* Clean up */
307         if (cmdline_handle) {
308                 fclose(cmdline_handle);
309                 unlink(cmdline_template);
310         }
311         fclose(authorized_signatures_handle);
312
313         return result;
314 }
315
316 void gpg_validate_boot_files_cleanup(struct boot_task *boot_task) {
317         if (boot_task->verify_signature) {
318                 unlink(boot_task->local_image_override);
319                 if (boot_task->local_initrd_override)
320                         unlink(boot_task->local_initrd_override);
321                 if (boot_task->local_dtb_override)
322                         unlink(boot_task->local_dtb_override);
323
324                 talloc_free(boot_task->local_image_override);
325                 if (boot_task->local_initrd_override)
326                         talloc_free(boot_task->local_initrd_override);
327                 if (boot_task->local_dtb_override)
328                         talloc_free(boot_task->local_dtb_override);
329         }
330 }
331
332 int lockdown_status() {
333         if (access(LOCKDOWN_FILE, F_OK) == -1)
334                 return PB_LOCKDOWN_NONE;
335         else
336                 return PB_LOCKDOWN_SIGN;
337 }