summaryrefslogtreecommitdiffstats
path: root/src/boot/bootctl-uki.c
blob: 1a439afaae83c0dea2023ab853b3947cd1209cf3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include "bootctl.h"
#include "bootctl-uki.h"
#include "fd-util.h"
#include "parse-util.h"
#include "pe-header.h"

#define MAX_SECTIONS 96

static const uint8_t dos_file_magic[2] = "MZ";
static const uint8_t pe_file_magic[4] = "PE\0\0";

static const uint8_t name_osrel[8] = ".osrel";
static const uint8_t name_linux[8] = ".linux";
static const uint8_t name_initrd[8] = ".initrd";

static int pe_sections(FILE *uki, struct PeSectionHeader **ret, size_t *ret_n) {
        _cleanup_free_ struct PeSectionHeader *sections = NULL;
        struct DosFileHeader dos;
        struct PeHeader pe;
        size_t scount;
        uint64_t soff, items;
        int rc;

        items = fread(&dos, 1, sizeof(dos), uki);
        if (items != sizeof(dos))
                return log_error_errno(SYNTHETIC_ERRNO(EIO), "DOS header read error");
        if (memcmp(dos.Magic, dos_file_magic, sizeof(dos_file_magic)) != 0)
                goto no_sections;

        rc = fseek(uki, le32toh(dos.ExeHeader), SEEK_SET);
        if (rc < 0)
                return log_error_errno(errno, "seek to PE header");
        items = fread(&pe, 1, sizeof(pe), uki);
        if (items != sizeof(pe))
                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE header read error");
        if (memcmp(pe.Magic, pe_file_magic, sizeof(pe_file_magic)) != 0)
                goto no_sections;

        soff = le32toh(dos.ExeHeader) + sizeof(pe) + le16toh(pe.FileHeader.SizeOfOptionalHeader);
        rc = fseek(uki, soff, SEEK_SET);
        if (rc < 0)
                return log_error_errno(errno, "seek to PE section headers");

        scount = le16toh(pe.FileHeader.NumberOfSections);
        if (scount > MAX_SECTIONS)
                goto no_sections;
        sections = new(struct PeSectionHeader, scount);
        if (!sections)
                return log_oom();
        items = fread(sections, sizeof(*sections), scount, uki);
        if (items != scount)
                return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section header read error");

        *ret = TAKE_PTR(sections);
        *ret_n = scount;
        return 0;

no_sections:
        *ret = NULL;
        *ret_n = 0;
        return 0;
}

static int find_pe_section(struct PeSectionHeader *sections, size_t scount,
                           const uint8_t *name, size_t namelen, size_t *ret) {
        for (size_t s = 0; s < scount; s++) {
                if (memcmp_nn(sections[s].Name, sizeof(sections[s].Name),
                              name, namelen) == 0) {
                        if (ret)
                                *ret = s;
                        return 1;
                }
        }
        return 0;
}

static bool is_uki(struct PeSectionHeader *sections, size_t scount) {
        return (find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), NULL) &&
                find_pe_section(sections, scount, name_linux, sizeof(name_linux), NULL) &&
                find_pe_section(sections, scount, name_initrd, sizeof(name_initrd), NULL));
}

int verb_kernel_identify(int argc, char *argv[], void *userdata) {
        _cleanup_fclose_ FILE *uki = NULL;
        _cleanup_free_ struct PeSectionHeader *sections = NULL;
        size_t scount;
        int rc;

        uki = fopen(argv[1], "re");
        if (!uki)
                return log_error_errno(errno, "Failed to open UKI file '%s': %m", argv[1]);

        rc = pe_sections(uki, &sections, &scount);
        if (rc < 0)
                return EXIT_FAILURE;

        if (sections) {
                if (is_uki(sections, scount)) {
                        puts("uki");
                        return EXIT_SUCCESS;
                }
                puts("pe");
                return EXIT_SUCCESS;
        }

        puts("unknown");
        return EXIT_SUCCESS;
}