]> git.ozlabs.org Git - petitboot/blob - lib/security/openssl.c
lib/crypt: Add helpers for operating on /etc/shadow
[petitboot] / lib / security / openssl.c
1 /*
2  *  Copyright (C) 2018 Opengear
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 <openssl/conf.h>
39 #include <openssl/bio.h>
40 #include <openssl/pem.h>
41 #include <openssl/rsa.h>
42 #include <openssl/evp.h>
43 #include <openssl/err.h>
44 #include <openssl/cms.h>
45 #include <openssl/pkcs12.h>
46
47 #include "security.h"
48
49 static const EVP_MD *s_verify_md = NULL;
50
51 static  __attribute__((constructor)) void crypto_init(void)
52 {
53 #if OPENSSL_VERSION_NUMBER < 0x10100000L
54         OPENSSL_no_config();
55         OpenSSL_add_all_algorithms();
56         ERR_load_crypto_strings();
57         ERR_load_CMS_strings();
58 #endif
59
60         s_verify_md = EVP_get_digestbyname(VERIFY_DIGEST);
61         if (!s_verify_md)
62                 pb_log("Specified OpenSSL digest '%s' not found\n", VERIFY_DIGEST);
63
64 }
65
66 #if OPENSSL_VERSION_NUMBER < 0x10100000L
67 static __attribute__((destructor)) void crypto_fini(void)
68 {
69         EVP_cleanup();
70         ERR_free_strings();
71 }
72 #endif
73
74 static int pb_log_print_errors_cb(const char *str,
75                                   size_t len __attribute__((unused)),
76                                   void *u __attribute__((unused)))
77 {
78         pb_log("    %s\n", str);
79         return 0;
80 }
81
82 static int get_pkcs12(FILE *keyfile, X509 **cert, EVP_PKEY **priv)
83 {
84         PKCS12 *p12 = NULL;
85         int ok = 0;
86
87         rewind(keyfile);
88
89         p12 = d2i_PKCS12_fp(keyfile, NULL);
90         if (p12) {
91                 /*
92                  * annoying but NULL and "" are two valid but different
93                  * default passwords
94                  */
95                 if (!PKCS12_parse(p12, NULL, priv, cert, NULL) &&
96                     !PKCS12_parse(p12,   "", priv, cert, NULL)) {
97                         pb_log_fn("Error parsing OpenSSL PKCS12:\n");
98                         ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
99                 } else
100                         ok = 1;
101
102                 PKCS12_free(p12);
103         }
104
105         return ok;
106 }
107
108 static X509 *get_cert(FILE *keyfile)
109 {
110         EVP_PKEY *priv = NULL;
111         X509 *cert = NULL;
112
113         if (get_pkcs12(keyfile, &cert, &priv)) {
114                 EVP_PKEY_free(priv);
115         } else {
116                 rewind(keyfile);
117                 ERR_clear_error();
118                 cert = PEM_read_X509(keyfile, NULL, NULL, NULL);
119         }
120
121         return cert;
122 }
123
124 static STACK_OF(X509) *get_cert_stack(FILE *keyfile)
125 {
126         STACK_OF(X509) *certs = sk_X509_new_null();
127         X509 *cert = NULL;
128
129         if (certs) {
130                 cert = get_cert(keyfile);
131                 if (cert)
132                         sk_X509_push(certs, get_cert(keyfile));
133         } else {
134                 pb_log_fn("Error allocating OpenSSL X509 stack:\n");
135                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
136         }
137
138         return certs;
139 }
140
141
142 static EVP_PKEY *get_public_key(FILE *keyfile)
143 {
144         EVP_PKEY *pkey = NULL;
145         X509 *cert = NULL;
146
147         /*
148          * walk through supported file types looking for a public key:
149          *
150          * 1. PKCS12
151          * 2. PEM encoded X509
152          * 3. PEM encoded raw public key
153          *
154          * someday in the future maybe utilize the keyring_path
155          * as an input for X509_STORE_load_locations for certificate
156          * validity checking
157          */
158
159         cert = get_cert(keyfile);
160         if (cert) {
161                 pkey = X509_get_pubkey(cert);
162                 X509_free(cert);
163         } else {
164                 rewind(keyfile);
165                 ERR_clear_error();
166                 pkey = PEM_read_PUBKEY(keyfile, NULL, NULL, NULL);
167         }
168
169         /* handles both cases */
170         if (!pkey) {
171                 pb_log_fn("Error loading OpenSSL public key:\n");
172                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
173         }
174
175         return pkey;
176 }
177
178 int decrypt_file(const char *filename,
179                  FILE *authorized_signatures_handle,
180                  const char *keyring_path __attribute__((unused)))
181 {
182         BIO *content_bio = NULL, *file_bio = NULL, *out_bio = NULL;
183         STACK_OF(X509) *certs = NULL;
184         CMS_ContentInfo *cms = NULL;
185         EVP_PKEY *priv = NULL;
186         X509 *cert = NULL;
187         int nok = -1;
188         char *outptr;
189         long outl;
190         int bytes;
191
192         if (!get_pkcs12(authorized_signatures_handle, &cert, &priv)) {
193                 pb_log("%s: Error opening OpenSSL decrypt authorization file:\n",
194                        __func__);
195                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
196                 goto out;
197         }
198
199         file_bio = BIO_new_file(filename, "r");
200         if (!file_bio) {
201                 pb_log("%s: Error opening OpenSSL decrypt cipher file '%s':\n",
202                        __func__, filename);
203                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
204                 goto out;
205         }
206
207         out_bio = BIO_new(BIO_s_mem());
208         if (!out_bio) {
209                 pb_log("%s: Error allocating OpenSSL decrypt output buffer:\n",
210                        __func__);
211                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
212                 goto out;
213         }
214
215         /* right now only support signed-envelope CMS */
216
217         cms = SMIME_read_CMS(file_bio, &content_bio);
218         if (!cms) {
219                 pb_log("%s: Error parsing OpenSSL CMS decrypt '%s'\n",
220                        __func__, filename);
221                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
222                 goto out;
223         }
224
225         BIO_free(content_bio);
226         content_bio = BIO_new(BIO_s_mem());
227         if (!content_bio) {
228                 pb_log("%s: Error allocating OpenSSL decrypt content buffer:\n",
229                        __func__);
230                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
231                 goto out;
232         }
233
234         if (!CMS_decrypt(cms, priv, cert, NULL, out_bio, 0)) {
235                 pb_log("%s: Error in OpenSSL CMS decrypt '%s'\n",
236                        __func__, filename);
237                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
238                 goto out;
239         }
240
241         certs = sk_X509_new_null();
242         if (!certs) {
243                 pb_log_fn("Error allocating OpenSSL X509 stack:\n");
244                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
245                 goto out;
246         }
247
248         sk_X509_push(certs, cert);
249
250         CMS_ContentInfo_free(cms);
251
252         cms = SMIME_read_CMS(out_bio, &content_bio);
253         if (!cms) {
254                 pb_log("%s: Error parsing OpenSSL CMS decrypt verify:\n",
255                        __func__);
256                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
257                 goto out;
258         }
259
260         /* this is a mem BIO so failure is 0 or -1 */
261         if (BIO_reset(out_bio) < 1) {
262                 pb_log("%s: Error resetting OpenSSL decrypt output buffer:\n",
263                        __func__);
264                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
265                 goto out;
266         }
267
268         /* in this mode its attached content */
269         if (!CMS_verify(cms, certs, NULL, content_bio, out_bio,
270                         CMS_NO_SIGNER_CERT_VERIFY | CMS_BINARY)) {
271                 pb_log_fn("Failed OpenSSL CMS decrypt verify:\n");
272                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
273                 goto out;
274         }
275
276         /* reopen the file so we force a truncation */
277         BIO_free(file_bio);
278         file_bio = BIO_new_file(filename, "w");
279         if (!file_bio) {
280                 pb_log("%s: Error opening OpenSSL decrypt output file '%s'\n",
281                        __func__, filename);
282                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
283                 goto out;
284         }
285
286         outl = BIO_get_mem_data(out_bio, &outptr);
287
288         while (outl) {
289                 bytes = BIO_write(file_bio, outptr, outl);
290                 if (bytes > 0) {
291                         outl -= (long)bytes;
292                         outptr += bytes;
293
294                 } else if (bytes < 0) {
295                         pb_log("%s: OpenSSL decrypt output write failure on file '%s':\n",
296                                        __func__, filename);
297                         ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
298                         goto out;
299                 }
300         }
301
302         if (!outl)
303                 nok = 0;
304
305 out:
306         if (cms)
307                 CMS_ContentInfo_free(cms);
308         BIO_free(file_bio);
309         BIO_free(content_bio);
310         BIO_free(out_bio);
311         X509_free(cert);
312         sk_X509_free(certs);
313         EVP_PKEY_free(priv);
314         return nok;
315 }
316
317 int verify_file_signature(const char *plaintext_filename,
318                           const char *signature_filename,
319                           FILE *authorized_signatures_handle,
320                           const char *keyring_path __attribute__((unused)))
321 {
322         BIO *signature_bio = NULL, *plaintext_bio = NULL, *content_bio = NULL;
323         STACK_OF(X509) *certs = NULL;
324         CMS_ContentInfo *cms = NULL;
325         ssize_t bytes_read = -1;
326         EVP_MD_CTX *ctx = NULL;
327         EVP_PKEY *pkey = NULL;
328         char *sigbuf = NULL;
329         char rdbuf[8192];
330         int nok = -1;
331         int siglen;
332
333         plaintext_bio = BIO_new_file(plaintext_filename, "r");
334         if (!plaintext_bio) {
335                 pb_log("%s: Error opening OpenSSL verify plaintext file '%s'\n",
336                        __func__, plaintext_filename);
337                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
338                 goto out;
339         }
340
341         signature_bio = BIO_new_file(signature_filename, "r");
342         if (!signature_bio) {
343                 pb_log("%s: Error opening OpenSSL verify signature file '%s'\n",
344                        __func__, signature_filename);
345                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
346                 goto out;
347         }
348
349         /* first check CMS */
350         cms = SMIME_read_CMS(signature_bio, &content_bio);
351         if (cms) {
352                 certs = get_cert_stack(authorized_signatures_handle);
353
354                 /*
355                  * this has to always be detached, which means we always
356                  * ignore content_bio and we have to set the NO_SIGNER_CERT_VERIFY
357                  * until such time we implement the keyring_path as a X509_STORE
358                  */
359
360                 if (!CMS_verify(cms, certs, NULL, plaintext_bio, NULL,
361                                 CMS_DETACHED | CMS_NO_SIGNER_CERT_VERIFY | CMS_BINARY)) {
362                         pb_log_fn("Failed OpenSSL CMS verify:\n");
363                         ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
364                         goto out;
365                 }
366
367                 nok = 0;
368
369         } else {
370
371                 /* for explicit dgst mode we need an explicit md defined */
372                 if (!s_verify_md)
373                         goto out;
374
375                 ctx = EVP_MD_CTX_create();
376
377                 if (!ctx) {
378                         pb_log_fn("Error allocating OpenSSL MD ctx:\n");
379                         ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
380                         goto out;
381                 }
382
383                 pkey = get_public_key(authorized_signatures_handle);
384                 if (!pkey)
385                         goto out;
386
387                 if (EVP_DigestVerifyInit(ctx, NULL, s_verify_md, NULL, pkey) < 1) {
388                         pb_log_fn("Error initializing OpenSSL verify:\n");
389                         ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
390                         goto out;
391                 }
392
393                 while (bytes_read) {
394                         bytes_read = BIO_read(plaintext_bio, rdbuf, 8192);
395                         if (bytes_read > 0) {
396                                 if (EVP_DigestVerifyUpdate(ctx, rdbuf, (size_t)(bytes_read)) < 1) {
397                                         pb_log("%s: OpenSSL digest update failure on file '%s':\n",
398                                                __func__, plaintext_filename);
399                                         ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
400                                         goto out;
401                                 }
402                         } else if (bytes_read < 0) {
403                                 pb_log("%s: OpenSSL read failure on file '%s':\n",
404                                        __func__, plaintext_filename);
405                                 ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
406                                 goto out;
407                         }
408                 }
409
410                 /*
411                  * can't do signature buffer as an update so have to read in whole file
412                  * would be handy if there was some sort of BIO_read_all but there
413                  * doesn't seem to be so rather than reinvent the wheel close it and
414                  * use the existing support
415                  */
416                 BIO_free(signature_bio);
417                 signature_bio = NULL;
418
419                 if (read_file(NULL, signature_filename, &sigbuf, &siglen)) {
420                         pb_log("%s: Error reading OpenSSL signature file '%s'\n",
421                                __func__, signature_filename);
422                         goto out;
423                 }
424
425                 if (EVP_DigestVerifyFinal(ctx, (unsigned char*)sigbuf, siglen))
426                         nok = 0;
427                 else {
428                         pb_log_fn("Error finalizing OpenSSL verify:\n");
429                         ERR_print_errors_cb(&pb_log_print_errors_cb, NULL);
430                 }
431         }
432
433 out:
434         if (cms)
435                 CMS_ContentInfo_free(cms);
436         talloc_free(sigbuf);
437         sk_X509_free(certs);
438         BIO_free(plaintext_bio);
439         BIO_free(signature_bio);
440         BIO_free(content_bio);
441         EVP_PKEY_free(pkey);
442         EVP_MD_CTX_destroy(ctx);
443         return nok;
444 }
445
446 int lockdown_status(void)
447 {
448         /*
449          * if it's a PKCS12 then we're in decrypt mode since we have the
450          * private key, otherwise it's sign mode
451          *
452          * someday add in support for runtime determination based on what
453          * files come back in the async sig file load?
454          */
455         FILE *authorized_signatures_handle = NULL;
456         int ret = PB_LOCKDOWN_SIGN;
457         PKCS12 *p12 = NULL;
458
459 #if !defined(HARD_LOCKDOWN)
460         if (access(LOCKDOWN_FILE, F_OK) == -1)
461                 return PB_LOCKDOWN_NONE;
462 #endif
463
464         /* determine lockdown type */
465
466         authorized_signatures_handle = fopen(LOCKDOWN_FILE, "r");
467         if (authorized_signatures_handle) {
468                 p12 = d2i_PKCS12_fp(authorized_signatures_handle, NULL);
469                 if (p12) {
470                         ret = PB_LOCKDOWN_DECRYPT;
471                         PKCS12_free(p12);
472                 }
473                 fclose(authorized_signatures_handle);
474         }
475
476         return ret;
477 }
478