diff --git a/zipl/include/job.h b/zipl/include/job.h index 4c9253f8d..fce811ce9 100644 --- a/zipl/include/job.h +++ b/zipl/include/job.h @@ -121,6 +121,7 @@ struct job_data { int add_files; int dry_run; int command_line; + int is_secure; }; diff --git a/zipl/include/misc.h b/zipl/include/misc.h index 37a8a87b2..5a349a7b6 100644 --- a/zipl/include/misc.h +++ b/zipl/include/misc.h @@ -51,6 +51,7 @@ int misc_check_readable_file(const char* filename); int misc_check_writable_device(const char* devno, int blockdev, int chardev); void misc_ebcdic_to_ascii(unsigned char *from, unsigned char *to); void misc_ascii_to_ebcdic(unsigned char *from, unsigned char *to); +unsigned int misc_check_secure_boot(void); #define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) diff --git a/zipl/include/scan.h b/zipl/include/scan.h index 0fe415c9b..4dcee0b00 100644 --- a/zipl/include/scan.h +++ b/zipl/include/scan.h @@ -16,7 +16,7 @@ #define SCAN_SECTION_NUM 9 -#define SCAN_KEYWORD_NUM 21 +#define SCAN_KEYWORD_NUM 22 #define SCAN_KEYWORD_ONLY_NUM 1 #define SCAN_AUTOMENU_NAME "zipl-automatic-menu" @@ -51,6 +51,7 @@ enum scan_keyword_id { scan_keyword_targetoffset = 18, scan_keyword_defaultauto = 19, scan_keyword_kdump = 20, + scan_keyword_secure = 21, }; enum scan_section_type { diff --git a/zipl/include/zipl.h b/zipl/include/zipl.h index 9b2dadb6f..d127b2970 100644 --- a/zipl/include/zipl.h +++ b/zipl/include/zipl.h @@ -45,12 +45,17 @@ #define ZIPL_DEFAULT_CONF "/etc/zipl.conf" #define ZIPL_DEFAULT_BLSDIR "/boot/loader/entries" #define ZIPL_STAGE3_PATH TOOLS_LIBDIR "/stage3.bin" +#define ZIPL_SIPL_PATH "/sys/firmware/ipl/has_secure" #define MENU_DEFAULT_PROMPT 0 #define MENU_DEFAULT_TIMEOUT 0 #define MAX_DUMP_VOLUMES 32 +#define SECURE_BOOT_DISABLED 0 +#define SECURE_BOOT_ENABLED 1 +#define SECURE_BOOT_AUTO 2 + /* Internal component load address type */ typedef uint64_t address_t; diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c index d10b15a35..c94180377 100644 --- a/zipl/src/bootmap.c +++ b/zipl/src/bootmap.c @@ -19,6 +19,7 @@ #include #include "lib/util_part.h" +#include "lib/util_path.h" #include "boot.h" #include "bootmap.h" @@ -117,6 +118,22 @@ check_menu_positions(struct job_menu_data* menu, char* name, return 0; } +static bool +check_secure_boot_support(void) +{ + unsigned int val; + FILE *fp; + + fp = fopen(ZIPL_SIPL_PATH, "r"); + if (!fp) + return false; + + fscanf(fp, "%d", &val); + fclose(fp); + + return val ? true : false; +} + /* Write COUNT elements of the blocklist specified by LIST as a linked list * of segment table blocks to the file identified by file descriptor FD. Upon @@ -452,7 +469,8 @@ check_remaining_filesize(size_t filesize, size_t signature_size, static int add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, int verbose, int add_files, component_header_type type, - struct disk_info* info, struct job_target_data* target) + struct disk_info* info, struct job_target_data* target, + int is_secure) { struct stat stats; void* table; @@ -467,6 +485,7 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, size_t signature_size; struct signature_header sig_head; int comp_nr = 0; + bool secure_boot_supported; memset(comp_loc, 0, sizeof(comp_loc)); memset(&sig_head, 0, sizeof(sig_head)); @@ -515,10 +534,12 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, return -1; } image_size = stats.st_size; - + secure_boot_supported = check_secure_boot_support(); signature_size = extract_signature(ZIPL_STAGE3_PATH, &signature, &sig_head); - if (signature_size) { + if (signature_size && + (is_secure == SECURE_BOOT_ENABLED || + (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { if (verbose) printf(" signature for.....: %s\n", ZIPL_STAGE3_PATH); @@ -536,6 +557,17 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, offset += sizeof(struct component_entry); comp_nr++; free(signature); + } else if (is_secure == SECURE_BOOT_ENABLED) { + /* + * If secure boot is forced and we have failed to extract a + * signature for the stage 3 loader zipl will abort with an + * error message + */ + error_text("Could not install Secure Boot IPL records"); + error_reason("Missing signature in internal loader file %s", + ZIPL_STAGE3_PATH); + free(table); + return -1; } /* Add stage 3 loader to bootmap */ @@ -584,7 +616,9 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, printf(" kernel image......: %s\n", ipl->image); } signature_size = extract_signature(ipl->image, &signature, &sig_head); - if (signature_size) { + if (signature_size && + (is_secure == SECURE_BOOT_ENABLED || + (is_secure == SECURE_BOOT_AUTO && secure_boot_supported))) { if (verbose) printf(" signature for.....: %s\n", ipl->image); @@ -604,6 +638,17 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, free(signature); check_remaining_filesize(image_size, signature_size, info, ipl->image); + } else if (is_secure == SECURE_BOOT_ENABLED) { + /* + * If secure boot is forced and we have failed to extract a + * signature for the kernel image zipl will abort with an + * error message + */ + error_text("Could not install Secure Boot IPL records"); + error_reason("Missing signature in image file %s", + ipl->image); + free(table); + return -1; } rc = add_component_file(fd, ipl->image, ipl->image_addr, @@ -644,7 +689,10 @@ add_ipl_program(int fd, struct job_ipl_data* ipl, disk_blockptr_t* program, if (ipl->ramdisk != NULL) { signature_size = extract_signature(ipl->ramdisk, &signature, &sig_head); - if (signature_size) { + if (signature_size && + (is_secure == SECURE_BOOT_ENABLED || + (is_secure == SECURE_BOOT_AUTO && + secure_boot_supported))) { if (verbose) { printf(" signature for.....: %s\n", ipl->ramdisk); @@ -830,7 +878,7 @@ add_dump_program(int fd, struct job_dump_data* dump, return rc; ipl.parm_addr = dump->parm_addr; return add_ipl_program(fd, &ipl, program, verbose, 1, - type, info, target); + type, info, target, SECURE_BOOT_DISABLED); } @@ -867,7 +915,7 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, rc = add_ipl_program(fd, &job->data.ipl, &table[0], verbose || job->command_line, job->add_files, component_header, - info, &job->target); + info, &job->target, job->is_secure); break; case job_segment: if (job->command_line) @@ -918,7 +966,8 @@ build_program_table(int fd, struct job_data* job, disk_blockptr_t* pointer, &table[job->data.menu.entry[i].pos], verbose || job->command_line, job->add_files, component_header, - info, &job->target); + info, &job->target, + job->is_secure); break; case job_print_usage: case job_print_version: diff --git a/zipl/src/job.c b/zipl/src/job.c index bdd1744c9..2d8de8f17 100644 --- a/zipl/src/job.c +++ b/zipl/src/job.c @@ -52,11 +52,12 @@ static struct option options[] = { { "dry-run", no_argument, NULL, '0'}, { "force", no_argument, NULL, 'f'}, { "kdump", required_argument, NULL, 'k'}, + { "secure", required_argument, NULL, 'S'}, { NULL, 0, NULL, 0 } }; /* Command line option abbreviations */ -static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:m:hHnVvaT:fk:"; +static const char option_string[] = "-c:b:t:i:r:p:P:d:D:M:s:S:m:hHnVvaT:fk:"; struct command_line { char* data[SCAN_KEYWORD_NUM]; @@ -215,6 +216,11 @@ get_command_line(int argc, char* argv[], struct command_line* line) } else cmdline.menu = optarg; break; + case 'S': + is_keyword = 1; + rc = store_option(&cmdline, scan_keyword_secure, + optarg); + break; case 'h': cmdline.help = 1; break; @@ -1263,6 +1269,26 @@ type_from_target(char *target, disk_type_t *type) } } +static int +set_secure_ipl(char *keyword, struct job_data *job) +{ + if (strcmp(keyword, "auto") == 0) { + job->is_secure = SECURE_BOOT_AUTO; + } else if (strcmp(keyword, "0") == 0) { + job->is_secure = SECURE_BOOT_DISABLED; + } else if (strcmp(keyword, "1") == 0) { + if (job->target.targettype != disk_type_scsi) { + error_reason("Secure boot forced for non-SCSI disk type"); + return -1; + } + job->is_secure = SECURE_BOOT_ENABLED; + } else { + error_reason("Invalid secure boot setting '%s'", + keyword); + return -1; + } + return 0; +} static int get_job_from_section_data(char* data[], struct job_data* job, char* section) @@ -1345,6 +1371,13 @@ get_job_from_section_data(char* data[], struct job_data* job, char* section) return -1; } } + /* Fill in secure boot */ + if (data[(int) scan_keyword_secure] != NULL) { + rc = set_secure_ipl(data[(int) scan_keyword_secure], + job); + if (rc) + return rc; + } break; case section_ipl_tape: /* Tape IPL job */ @@ -1502,6 +1535,13 @@ get_menu_job(struct scan_token* scan, char* menu, struct job_data* job) job->data.menu.timeout = atol(scan[i].content.keyword.value); break; + case scan_keyword_secure: + rc = set_secure_ipl( + scan[i].content.keyword.value, + job); + if (rc) + return rc; + break; case scan_keyword_target: job->target.bootmap_dir = misc_strdup( scan[i].content.keyword.value); diff --git a/zipl/src/misc.c b/zipl/src/misc.c index fd1681052..057c9a0be 100644 --- a/zipl/src/misc.c +++ b/zipl/src/misc.c @@ -22,7 +22,6 @@ #include "error.h" #include "misc.h" - /* Allocate SIZE bytes of memory. Upon success, return pointer to memory. * Return NULL otherwise. */ void * diff --git a/zipl/src/scan.c b/zipl/src/scan.c index 9160d083e..127d84856 100644 --- a/zipl/src/scan.c +++ b/zipl/src/scan.c @@ -45,45 +45,45 @@ enum scan_key_state scan_key_table[SCAN_SECTION_NUM][SCAN_KEYWORD_NUM] = { * ult to tofs e mete file isk ent et pt out ultm dump * rs enu * - * targ targ targ targ targ defa kdum - * etba etty etge etbl etof ulta p + * targ targ targ targ targ defa kdum secu + * etba etty etge etbl etof ulta p re * se pe omet ocks fset uto * ry ize */ /* default auto */ {opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv, - opt, opt, opt, opt, opt, opt, inv}, + opt, opt, opt, opt, opt, opt, inv, opt}, /* default menu */ {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, inv, inv, - inv, inv, inv, inv, inv, inv, inv}, + inv, inv, inv, inv, inv, inv, inv, opt}, /* default section */ {req, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, - inv, inv, inv, inv, inv, inv, inv}, + inv, inv, inv, inv, inv, inv, inv, opt}, /* ipl */ {inv, inv, inv, req, opt, opt, opt, inv, req, inv, inv, inv, inv, inv, - opt, opt, opt, opt, opt, inv, opt}, + opt, opt, opt, opt, opt, inv, opt, opt}, /* segment load */ {inv, inv, inv, inv, inv, inv, inv, req, req, inv, inv, inv, inv, inv, - inv, inv, inv, inv, inv, inv, inv}, + inv, inv, inv, inv, inv, inv, inv, inv}, /* part dump */ {inv, req, inv, inv, inv, inv, inv, inv, opt, inv, inv, inv, inv, inv, - inv, inv, inv, inv, inv, inv, inv}, + inv, inv, inv, inv, inv, inv, inv, inv}, /* fs dump */ {inv, inv, req, inv, opt, opt, inv, inv, req, inv, inv, inv, inv, inv, - inv, inv, inv, inv, inv, inv, inv}, + inv, inv, inv, inv, inv, inv, inv, inv}, /* ipl tape */ {inv, inv, inv, req, opt, opt, opt, inv, inv, inv, inv, inv, req, inv, - inv, inv, inv, inv, inv, inv, inv}, + inv, inv, inv, inv, inv, inv, inv, inv}, /* multi volume dump */ {inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, inv, req, - inv, inv, inv, inv, inv, inv, inv} + inv, inv, inv, inv, inv, inv, inv, inv} }; /* Determines which keyword may be present in a menu section */ enum scan_key_state scan_menu_key_table[SCAN_KEYWORD_NUM] = { /* menu section */ opt, inv, inv, inv, inv, inv, inv, inv, req, opt, opt, inv, inv, inv, - opt, opt, opt, opt, opt, inv, inv + opt, opt, opt, opt, opt, inv, inv, opt }; /* Mapping of keyword IDs to strings */ @@ -111,6 +111,7 @@ static const struct { { "timeout", scan_keyword_timeout}, { "tape", scan_keyword_tape}, { "kdump", scan_keyword_kdump}, + { "secure", scan_keyword_secure}, }; /* List of keywords that are used without an assignment */ @@ -1863,6 +1864,7 @@ scan_build_automenu(struct scan_token* scan) /* defaultmenu */ 1 + /* menu heading */ 1 + /* keyword default,prompt,timeout */ 3 + + /* keyword secure */ 1 + /* target keywords*/ num_targets + /* missing target definitions */ num_sections * num_targets + /* number assigment */ num_sections; @@ -1978,6 +1980,14 @@ scan_build_automenu(struct scan_token* scan) db_keyword[i])) goto err; } + /* secure= */ + i = (int) scan_keyword_secure; + if (db_keyword[i]) { + if (scan_append_keyword_assignment(new_scan, &i_new, + scan_keyword_secure, + db_keyword[i])) + goto err; + } /* target= */ /* targetbase= */ /* targetgeometry= */