diff --git a/include/swupd.h b/include/swupd.h index 2b902675..7c8f3b16 100644 --- a/include/swupd.h +++ b/include/swupd.h @@ -65,6 +65,7 @@ struct version_container { struct header; extern bool force; +extern bool sigcheck; extern int verbose; extern int update_count; extern int update_skip; diff --git a/src/check_update.c b/src/check_update.c index 05215677..6c084a96 100644 --- a/src/check_update.c +++ b/src/check_update.c @@ -41,6 +41,7 @@ static void print_help(const char *name) printf(" -P, --port=[port #] Port number to connect to at the url for version string and content file downloads\n"); printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n"); printf(" -x, --force Attempt to proceed even if non-critical errors found\n"); + printf(" -n, --nosigcheck Do not attempt to enforce certificate or signature checking\n"); printf(" -p, --path=[PATH...] Use [PATH...] as the path to verify (eg: a chroot or btrfs subvol\n"); printf(" -S, --statedir Specify alternate swupd state directory\n"); printf("\n"); @@ -53,6 +54,7 @@ static const struct option prog_opts[] = { { "port", required_argument, 0, 'P' }, { "format", required_argument, 0, 'F' }, { "force", no_argument, 0, 'x' }, + { "nosigcheck", no_argument, 0, 'n' }, { "path", required_argument, 0, 'p' }, { "statedir", required_argument, 0, 'S' }, { 0, 0, 0, 0 } @@ -62,7 +64,7 @@ static bool parse_options(int argc, char **argv) { int opt; - while ((opt = getopt_long(argc, argv, "hxu:v:P:F:p:S:", prog_opts, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hxnu:v:P:F:p:S:", prog_opts, NULL)) != -1) { switch (opt) { case '?': case 'h': @@ -111,6 +113,9 @@ static bool parse_options(int argc, char **argv) case 'x': force = true; break; + case 'n': + sigcheck = false; + break; default: printf("error: unrecognized option\n\n"); goto err; diff --git a/src/clr_bundle_add.c b/src/clr_bundle_add.c index 6c027944..e73faf21 100644 --- a/src/clr_bundle_add.c +++ b/src/clr_bundle_add.c @@ -52,6 +52,7 @@ static void print_help(const char *name) printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n"); printf(" -l, --list List all available bundles for the current version of Clear Linux\n"); printf(" -x, --force Attempt to proceed even if non-critical errors found\n"); + printf(" -n, --nosigcheck Do not attempt to enforce certificate or signature checking\n"); printf(" -S, --statedir Specify alternate swupd state directory\n"); printf(" -C, --certpath Specify alternate path to swupd certificates\n"); printf("\n"); @@ -67,6 +68,7 @@ static const struct option prog_opts[] = { { "path", required_argument, 0, 'p' }, { "format", required_argument, 0, 'F' }, { "force", no_argument, 0, 'x' }, + { "nosigcheck", no_argument, 0, 'n' }, { "statedir", required_argument, 0, 'S' }, { "certpath", required_argument, 0, 'C' }, { 0, 0, 0, 0 } @@ -76,7 +78,7 @@ static bool parse_options(int argc, char **argv) { int opt; - while ((opt = getopt_long(argc, argv, "hxu:c:v:P:p:F:lS:C:", prog_opts, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hxnu:c:v:P:p:F:lS:C:", prog_opts, NULL)) != -1) { switch (opt) { case '?': case 'h': @@ -134,6 +136,9 @@ static bool parse_options(int argc, char **argv) case 'x': force = true; break; + case 'n': + sigcheck = false; + break; case 'C': if (!optarg) { printf("Invalid --certpath argument\n\n"); diff --git a/src/clr_bundle_rm.c b/src/clr_bundle_rm.c index b8bb8be5..4784000c 100644 --- a/src/clr_bundle_rm.c +++ b/src/clr_bundle_rm.c @@ -51,6 +51,7 @@ static void print_help(const char *name) printf(" -P, --port=[port #] Port number to connect to at the url for version string and content file downloads\n"); printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n"); printf(" -x, --force Attempt to proceed even if non-critical errors found\n"); + printf(" -n, --nosigcheck Do not attempt to enforce certificate or signature checks\n"); printf(" -S, --statedir Specify alternate swupd state directory\n"); printf(" -C, --certpath Specify alternate path to swupd certificates\n"); printf("\n"); @@ -65,6 +66,7 @@ static const struct option prog_opts[] = { { "port", required_argument, 0, 'P' }, { "format", required_argument, 0, 'F' }, { "force", no_argument, 0, 'x' }, + { "nosigcheck", no_argument, 0, 'n' }, { "statedir", required_argument, 0, 'S' }, { "certpath", required_argument, 0, 'C' }, { 0, 0, 0, 0 } @@ -74,7 +76,7 @@ static bool parse_options(int argc, char **argv) { int opt; - while ((opt = getopt_long(argc, argv, "hxp:u:c:v:P:F:S:C:", prog_opts, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hxnp:u:c:v:P:F:S:C:", prog_opts, NULL)) != -1) { switch (opt) { case '?': case 'h': @@ -129,6 +131,9 @@ static bool parse_options(int argc, char **argv) case 'x': force = true; break; + case 'n': + sigcheck = false; + break; case 'C': if (!optarg) { printf("Invalid --certpath argument\n\n"); diff --git a/src/globals.c b/src/globals.c index e4f26c98..4d7ab922 100644 --- a/src/globals.c +++ b/src/globals.c @@ -32,6 +32,7 @@ #include "swupd.h" bool force = false; +bool sigcheck = true; bool verify_esp_only; bool verify_bundles_only = false; int update_count = 0; diff --git a/src/helpers.c b/src/helpers.c index b7829151..cdbe0d80 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -614,7 +614,8 @@ int swupd_init(int *lock_fd) goto out_close_lock; } - if (!initialize_signature()) { + /* If --nosigcheck, we do not attempt any signature checking */ + if (sigcheck && !initialize_signature()) { ret = ESIGNATURE; terminate_signature(); goto out_close_lock; diff --git a/src/main.c b/src/main.c index 48b634ef..66cace21 100644 --- a/src/main.c +++ b/src/main.c @@ -46,6 +46,7 @@ static const struct option prog_opts[] = { { "format", required_argument, 0, 'F' }, { "path", required_argument, 0, 'p' }, { "force", no_argument, 0, 'x' }, + { "nosigcheck", no_argument, 0, 'n' }, { "statedir", required_argument, 0, 'S' }, { "certpath", required_argument, 0, 'C' }, { 0, 0, 0, 0 } @@ -70,6 +71,7 @@ static void print_help(const char *name) printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n"); printf(" -p, --path=[PATH...] Use [PATH...] as the path to verify (eg: a chroot or btrfs subvol\n"); printf(" -x, --force Attempt to proceed even if non-critical errors found\n"); + printf(" -n, --nosigcheck Do not attempt to enforce certificate or signature checking\n"); printf(" -S, --statedir Specify alternate swupd state directory\n"); printf(" -C, --certpath Specify alternate path to swupd certificates\n"); printf("\n"); @@ -79,7 +81,7 @@ static bool parse_options(int argc, char **argv) { int opt; - while ((opt = getopt_long(argc, argv, "hxdu:P:c:v:sF:p:S:C:", prog_opts, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hxndu:P:c:v:sF:p:S:C:", prog_opts, NULL)) != -1) { switch (opt) { case '?': case 'h': @@ -140,6 +142,9 @@ static bool parse_options(int argc, char **argv) case 'x': force = true; break; + case 'n': + sigcheck = false; + break; case 'C': if (!optarg) { printf("Invalid --certpath argument\n\n"); diff --git a/src/manifest.c b/src/manifest.c index 7933ff1a..0ec70996 100644 --- a/src/manifest.c +++ b/src/manifest.c @@ -584,6 +584,7 @@ struct manifest *load_mom(int version) int ret = 0; char *filename; char *url; + char *log_cmd = NULL; ret = retrieve_manifests(version, version, "MoM", NULL); if (ret != 0) { @@ -594,7 +595,19 @@ struct manifest *load_mom(int version) string_or_die(&filename, "%s/%i/Manifest.MoM", state_dir, version); string_or_die(&url, "%s/%i/Manifest.MoM", content_url, version); if (!download_and_verify_signature(url, filename)) { - printf("WARNING!!! FAILED TO VERIFY SIGNATURE OF Manifest.MoM\n"); + if (sigcheck) { + printf("WARNING!!! FAILED TO VERIFY SIGNATURE OF Manifest.MoM\n"); + free(filename); + free(url); + return NULL; + } else { + printf("FAILED TO VERIFY SIGNATURE OF Manifest.MoM. Operation proceeding due to\n" + " --nosigcheck, but system security may be compromised\n"); + string_or_die(&log_cmd, "echo \"swupd security notice:" + " --nosigcheck used to bypass MoM signature verification failure\" | systemd-cat --priority=\"err\" --identifier=\"swupd\""); + (void)system(log_cmd); + free(log_cmd); + } } free(filename); free(url); diff --git a/src/signature.c b/src/signature.c index 7ea9b95f..658eb53e 100644 --- a/src/signature.c +++ b/src/signature.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,7 @@ static char *CERTNAME; -static bool validate_certificate(void); +static int validate_certificate(void); static int verify_callback(int, X509_STORE_CTX *); static bool get_pubkey(); @@ -64,6 +65,11 @@ static char *crl = NULL; * returns: true if can initialize and validate certificates, otherwise false */ bool initialize_signature(void) { + int ret = -1; + time_t mod_sec = 0; + struct tm *alttime; + struct stat statt; + string_or_die(&CERTNAME, "%s/%s", cert_path, SWUPDCERT); ERR_load_crypto_strings(); @@ -73,7 +79,20 @@ bool initialize_signature(void) if (!get_pubkey()) { goto fail; } - if (!validate_certificate()) { + + ret = validate_certificate(); + if (ret) { + printf("Failed to verify certificate: %s\n", X509_verify_cert_error_string(ret)); + if (ret == X509_V_ERR_CERT_NOT_YET_VALID) { + /* If we can retrieve an approx. good system time, report out to user */ + if (stat("/usr/lib/os-release", &statt) != -1) { + mod_sec = statt.st_mtim.tv_sec; + char timebuf[30]; + alttime = localtime(&mod_sec); + strftime(timebuf, sizeof(timebuf), "%F", alttime); + printf("System clock should be at least %s\n", timebuf); + } + } goto fail; } @@ -282,8 +301,8 @@ error: * keys have not been compromised or the CRL has not been generated by the * Certificate Authority (CA) * - * returns: true if certificate is valid, false otherwise */ -static bool validate_certificate(void) + * returns: 0 if certificate is valid, X509 store error code otherwise */ +static int validate_certificate(void) { X509_LOOKUP *lookup = NULL; X509_STORE_CTX *verify_ctx = NULL; @@ -353,7 +372,7 @@ static bool validate_certificate(void) X509_STORE_CTX_free(verify_ctx); /* Certificate verified correctly */ - return true; + return 0; error: ERR_print_errors_fp(stderr); @@ -362,14 +381,14 @@ error: X509_STORE_CTX_free(verify_ctx); } - return false; + return verify_ctx->error; } int verify_callback(int ok, X509_STORE_CTX *stor) { if (!ok) { - fprintf(stderr, "Certificate verification error: %s\n", - X509_verify_cert_error_string(stor->error)); + printf("Certificate verification error: %s\n", + X509_verify_cert_error_string(stor->error)); } return ok; } @@ -387,6 +406,10 @@ bool download_and_verify_signature(const char *data_url, const char *data_filena int ret; bool result; + if (!sigcheck) { + return false; + } + string_or_die(&sig_url, "%s.sig", data_url); string_or_die(&sig_filename, "%s.sig", data_filename); diff --git a/src/verify.c b/src/verify.c index de480325..8937c682 100644 --- a/src/verify.c +++ b/src/verify.c @@ -67,6 +67,7 @@ static const struct option prog_opts[] = { { "format", required_argument, 0, 'F' }, { "quick", no_argument, 0, 'q' }, { "force", no_argument, 0, 'x' }, + { "nosigcheck", no_argument, 0, 'n' }, { "statedir", required_argument, 0, 'S' }, { "certpath", required_argument, 0, 'C' }, { 0, 0, 0, 0 } @@ -90,6 +91,7 @@ static void print_help(const char *name) printf(" -F, --format=[staging,1,2,etc.] the format suffix for version file downloads\n"); printf(" -q, --quick Don't compare hashes, only fix missing files\n"); printf(" -x, --force Attempt to proceed even if non-critical errors found\n"); + printf(" -n, --nosigcheck Do not attempt to enforce certificate or signature checking\n"); printf(" -S, --statedir Specify alternate swupd state directory\n"); printf(" -C, --certpath Specify alternate path to swupd certificates\n"); printf("\n"); @@ -99,7 +101,7 @@ static bool parse_options(int argc, char **argv) { int opt; - while ((opt = getopt_long(argc, argv, "hxm:p:u:P:c:v:fiF:qS:C:", prog_opts, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hxnm:p:u:P:c:v:fiF:qS:C:", prog_opts, NULL)) != -1) { switch (opt) { case '?': case 'h': @@ -172,6 +174,9 @@ static bool parse_options(int argc, char **argv) case 'x': force = true; break; + case 'n': + sigcheck = false; + break; case 'C': if (!optarg) { printf("Invalid --certpath argument\n\n");