├── Make.version ├── .gitignore ├── src ├── .gitignore ├── dbxtool.service ├── esltree.h ├── x509.h ├── Makefile ├── dbxtool.1 ├── iter.h ├── esltree.c ├── esl.h ├── util.h ├── fix_coverity.h ├── error.h ├── iter.c └── dbxtool.c ├── sampledata ├── thinkpad.0 ├── thinkpad.1 ├── DBXUpdate.bin.0 └── DBXUpdate.bin.1 ├── data ├── DBXUpdate-20160809.x64.bin └── Makefile ├── README.md ├── Make.scan-build ├── Make.rules ├── Make.defaults ├── Make.coverity ├── Makefile ├── dbxtool.spec ├── .github └── workflows │ ├── push.yml │ └── pullrequest.yml └── COPYING /Make.version: -------------------------------------------------------------------------------- 1 | VERSION = 8 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.tar.* 2 | cov-int 3 | scan-results 4 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | *.so 4 | *.a 5 | .*.P 6 | dbxtool 7 | *.sw? 8 | core.* 9 | -------------------------------------------------------------------------------- /sampledata/thinkpad.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhboot/dbxtool/HEAD/sampledata/thinkpad.0 -------------------------------------------------------------------------------- /sampledata/thinkpad.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhboot/dbxtool/HEAD/sampledata/thinkpad.1 -------------------------------------------------------------------------------- /sampledata/DBXUpdate.bin.0: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhboot/dbxtool/HEAD/sampledata/DBXUpdate.bin.0 -------------------------------------------------------------------------------- /sampledata/DBXUpdate.bin.1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhboot/dbxtool/HEAD/sampledata/DBXUpdate.bin.1 -------------------------------------------------------------------------------- /data/DBXUpdate-20160809.x64.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rhboot/dbxtool/HEAD/data/DBXUpdate-20160809.x64.bin -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | dbxtool has been replaced by [fwupd](https://github.com/fwupd/fwupd); we 2 | recommend using that instead. 3 | 4 | dbxtool 5 | ======= 6 | 7 | tool for managing dbx updates installed on a machine. 8 | -------------------------------------------------------------------------------- /src/dbxtool.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Secure Boot DBX (blacklist) updater 3 | ConditionPathExists=/sys/firmware/efi/efivars 4 | ConditionKernelCommandLine=!rd.live.image 5 | ConditionKernelCommandLine=!boot=live 6 | 7 | [Install] 8 | WantedBy=multi-user.target 9 | 10 | [Service] 11 | RemainAfterExit=yes 12 | ExecStart=/usr/bin/dbxtool -a /usr/share/dbxtool/ -q 13 | -------------------------------------------------------------------------------- /Make.scan-build: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/Make.version 2 | include $(TOPDIR)/Make.rules 3 | include $(TOPDIR)/Make.defaults 4 | 5 | scan-clean : 6 | @if [[ -d scan-results ]]; then rm -rf scan-results && echo "removed 'scan-results'"; fi 7 | 8 | scan-build : clean 9 | scan-build -o scan-results make -j4 all 10 | 11 | clean : | scan-clean 12 | 13 | .PHONY : scan-build scan-clean 14 | -------------------------------------------------------------------------------- /data/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR = . 2 | TOPDIR = $(SRCDIR)/.. 3 | 4 | include $(TOPDIR)/Make.defaults 5 | 6 | all : 7 | 8 | deps : 9 | 10 | depclean : 11 | 12 | clean : 13 | 14 | install : 15 | $(INSTALL) -d -m 755 $(INSTALLROOT)$(PREFIX)/share/dbxtool/ 16 | $(INSTALL) -m 644 $$(ls -1 DBXUpdate*.bin | sort -n | tail -1) \ 17 | $(INSTALLROOT)$(PREFIX)/share/dbxtool/ 18 | 19 | .PHONY: all deps clean depclean install 20 | 21 | include $(TOPDIR)/Make.rules 22 | -------------------------------------------------------------------------------- /Make.rules: -------------------------------------------------------------------------------- 1 | 2 | include $(TOPDIR)/Make.version 3 | 4 | %.a : 5 | $(AR) -cvqs $@ $^ 6 | 7 | % : %.o 8 | $(CC) $(LINKFLAGS) -o $@ $^ $(foreach lib,$(LIBS),-l$(lib)) $(foreach pklib,$(PKLIBS), $(shell pkg-config --libs-only-l --libs-only-other $(pklib))) -lpthread 9 | 10 | %.so : 11 | $(CC) $(INCDIR) $(BUILDFLAGS) -Wl,-soname,$(SONAME) $(LINKFLAGS) $^ -o $@ 12 | 13 | %.o: %.c 14 | $(CC) $(INCDIR) $(BUILDFLAGS) $(CPPFLAGS) -c $< -o $@ 15 | 16 | .%.P : %.c 17 | $(CC) $(INCDIR) $(BUILDFLAGS) $(CPPFLAGS) -M -MM -MF $@ $^ 18 | 19 | %.S: %.c 20 | $(CC) $(INCDIR) $(BUILDFLAGS) $(CPPFLAGS) -S $< -o $@ 21 | 22 | %.E: %.c 23 | $(CC) $(INCDIR) $(BUILDFLAGS) $(CPPFLAGS) -E $< -o $@ 24 | 25 | %.c : %.h 26 | 27 | define get-config 28 | $(shell git config --local --get "$(NAME).$(1)") 29 | endef 30 | -------------------------------------------------------------------------------- /src/esltree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Red Hat, Inc. 3 | * All rights reserved. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; version 2 of the License. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * Author(s): Peter Jones 18 | */ 19 | #ifndef DBXTOOL_ESLTREE_H 20 | #define DBXTOOL_ESLTREE_H 1 21 | 22 | #include 23 | 24 | extern int esl_tree_create(void **rootp, uint8_t *dbx_buf, size_t dbx_len); 25 | extern void esl_tree_destroy(void **rootp); 26 | extern int esl_cmp(const void *l, const void *r); 27 | 28 | struct esl_tree_entry { 29 | efi_guid_t type; 30 | efi_guid_t owner; 31 | uint8_t *data; 32 | size_t datalen; 33 | }; 34 | 35 | #endif /* DBXTOOL_ESLTREE_H */ 36 | -------------------------------------------------------------------------------- /Make.defaults: -------------------------------------------------------------------------------- 1 | NAME = dbxtool 2 | COMMIT_ID ?= $(shell git log -1 --pretty=%H 2>/dev/null || echo master) 3 | INSTALLROOT:= $(DESTDIR) 4 | PREFIX := /usr 5 | 6 | INCDIR = -I$(TOPDIR)/include 7 | CFLAGS ?= -g3 -Og \ 8 | -Wp,-D_FORTIFY_SOURCE=2 \ 9 | -Wp,-D_GLIBCXX_ASSERTIONS \ 10 | -grecord-gcc-switches \ 11 | -fasynchronous-unwind-tables \ 12 | -fcf-protection \ 13 | -fexceptions \ 14 | -fstack-clash-protection \ 15 | -fstack-protector-strong \ 16 | -fno-strict-aliasing \ 17 | -fPIE \ 18 | --param=ssp-buffer-size=4 19 | 20 | BUILDFLAGS = $(CFLAGS) \ 21 | -std=gnu11 \ 22 | -Wp,-D_GNU_SOURCE \ 23 | -Wall -Wextra \ 24 | -Wno-unused-result \ 25 | -Wno-unused-function \ 26 | -Werror \ 27 | -Wno-error=cpp 28 | 29 | ASFLAGS = 30 | LDFLAGS ?= 31 | CCLDFLAGS ?= -fPIE 32 | LINKFLAGS = $(CCLDFLAGS) -Wl,-z,relro -Wl,--as-needed -Wl,-z,now 33 | INSTALL = install 34 | COMPILER ?= 35 | 36 | CC ?= $(COMPILER)gcc 37 | ifeq ($(origin CC),default) 38 | override CC = $(COMPILER)gcc 39 | endif 40 | AS ?= $(COMPILER)as 41 | LD ?= $(COMPILER)ld.bfd 42 | AR ?= $(COMPILER)ar 43 | RANLIB ?= $(COMPILER)ranlib 44 | OBJCOPY ?= $(COMPILER)objcopy 45 | -------------------------------------------------------------------------------- /Make.coverity: -------------------------------------------------------------------------------- 1 | include $(TOPDIR)/Make.version 2 | include $(TOPDIR)/Make.rules 3 | include $(TOPDIR)/Make.defaults 4 | 5 | COV_EMAIL=$(call get-config,coverity.email) 6 | COV_TOKEN=$(call get-config,coverity.token) 7 | COV_URL=$(call get-config,coverity.url) 8 | COV_FILE=$(NAME)-coverity-$(VERSION)-$(COMMIT_ID).tar.bz2 9 | 10 | cov-int : clean 11 | cov-build --dir cov-int make all 12 | 13 | cov-clean : 14 | @rm -vf $(NAME)-coverity-*.tar.* 15 | @if [[ -d cov-int ]]; then rm -rf cov-int && echo "removed 'cov-int'"; fi 16 | 17 | cov-file : | $(COV_FILE) 18 | 19 | $(COV_FILE) : cov-int 20 | tar caf $@ cov-int 21 | 22 | cov-upload : 23 | @if [[ -n "$(COV_URL)" ]] && \ 24 | [[ -n "$(COV_TOKEN)" ]] && \ 25 | [[ -n "$(COV_EMAIL)" ]] ; \ 26 | then \ 27 | echo curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ 28 | curl --form token=$(COV_TOKEN) --form email="$(COV_EMAIL)" --form file=@"$(COV_FILE)" --form version=$(VERSION).1 --form description="$(COMMIT_ID)" "$(COV_URL)" ; \ 29 | else \ 30 | echo Coverity output is in $(COV_FILE) ; \ 31 | fi 32 | 33 | coverity : cov-file cov-upload 34 | 35 | clean : | cov-clean 36 | 37 | .PHONY : coverity cov-upload cov-clean cov-file 38 | -------------------------------------------------------------------------------- /src/x509.h: -------------------------------------------------------------------------------- 1 | #ifndef DBXTOOL_X509_H 2 | #define DBXTOOL_X509_H 3 | 4 | #define SMALLEST_POSSIBLE_DER_SEQ 3 5 | 6 | static inline int32_t 7 | __attribute__((unused)) 8 | GetASN1SequenceSize(uint8_t *Location, uint32_t Size) 9 | { 10 | uint8_t I; 11 | uint8_t Octets; 12 | uint32_t DERLength = 0; 13 | 14 | if (Size < SMALLEST_POSSIBLE_DER_SEQ) 15 | return -1; 16 | 17 | // If it's not a CONSTRUCTED SEQUENCE it's not a certificate 18 | if (Location[0] != 0x30) 19 | return -1; 20 | 21 | if (!(Location[1] & 0x80)) { 22 | // Short form, which is too small to hold a certificate. 23 | return -1; 24 | } 25 | 26 | // Long form 27 | Octets = Location[1] & 0x7; 28 | 29 | // There is no chance our data is more than 3GB. 30 | if (Octets > 4 || (Octets == 4 && (Location[2] & 0x8))) 31 | return -1; 32 | 33 | // and if our size won't fit in the data it's wrong as well 34 | if (Size - 2 < Octets) 35 | return -1; 36 | 37 | for (I = 0; I < Octets; I++) { 38 | DERLength <<= 8; 39 | DERLength |= Location[I + 2]; 40 | } 41 | // and if DERLength is greater than what's left, it's bad too. 42 | if (Size - 2 - Octets < DERLength) 43 | return -1; 44 | 45 | // or else it's a reasonable certificate from a size point of view. 46 | return DERLength + 4; 47 | } 48 | 49 | #undef SMALLEST_POSSIBLE_DER_SEQ 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | SRCDIR = . 2 | TOPDIR = $(SRCDIR)/.. 3 | 4 | include $(TOPDIR)/Make.defaults 5 | 6 | PKLIBS = efivar 7 | LIBS = popt 8 | LDFLAGS = 9 | LINKFLAGS += $(foreach pklib,$(PKLIBS), $(shell pkg-config --libs-only-L $(pklib))) 10 | BUILDFLAGS += $(foreach pklib,$(PKLIBS), $(shell pkg-config --cflags $(pklib))) 11 | 12 | TARGETS = dbxtool 13 | 14 | all : $(TARGETS) 15 | 16 | dbxtool_SOURCES = dbxtool.c iter.c esltree.c 17 | dbxtool_OBJECTS = $(foreach source,$(dbxtool_SOURCES),$(patsubst %.c,%,$(source)).o) 18 | dbxtool_DEPS = $(foreach source,$(dbxtool_SOURCES),.$(patsubst %.c,%,$(source)).P) 19 | dbxtool : $(dbxtool_OBJECTS) $(STATIC_LIBS) 20 | 21 | DEPS = $(dbxtool_DEPS) 22 | 23 | deps : $(DEPS) 24 | 25 | depclean : 26 | @rm -fv .*.P 27 | 28 | -include $(DEPS) 29 | 30 | clean : depclean 31 | @rm -rfv *.o *.a *.so $(TARGETS) 32 | 33 | install : 34 | $(INSTALL) -d -m 755 $(INSTALLROOT)$(PREFIX)/bin/ 35 | $(INSTALL) -m 755 dbxtool $(INSTALLROOT)$(PREFIX)/bin/ 36 | $(INSTALL) -d -m 755 $(INSTALLROOT)$(PREFIX)/lib/systemd/system/ 37 | $(INSTALL) -m 644 dbxtool.service $(INSTALLROOT)$(PREFIX)/lib/systemd/system 38 | $(INSTALL) -d -m 755 $(INSTALLROOT)$(PREFIX)/share/man/man1/ 39 | $(INSTALL) -m 644 dbxtool.1 $(INSTALLROOT)$(PREFIX)/share/man/man1/ 40 | $(INSTALL) -d -m 755 $(INSTALLROOT)/etc/rpm/ 41 | 42 | .PHONY: all deps clean depclean install 43 | 44 | include $(TOPDIR)/Make.rules 45 | -------------------------------------------------------------------------------- /src/dbxtool.1: -------------------------------------------------------------------------------- 1 | .TH DBXTOOL 1 "Thu May 02 2014" 2 | .SH NAME 3 | dbxtool \- mechanism to distribute and apply UEFI Secure Boot DBX updates 4 | 5 | .SH SYNOPSIS 6 | \fBdbxtool\fR [--dbx=\fIdbxfile\fR | -d \fIdbxfile\fR ] [ --list | -l ] 7 | [ --verbose | -v ] [ --apply | -a ] 8 | [ \fIupdatedir\fR | \fIupdate0\fR [ ... \fIupdateN\fR ]] 9 | 10 | .SH DESCRIPTION 11 | \fBdbxtool\fR is a command line tool and oneshot systemd service for 12 | applying UEFI Secure Boot DBX updates. 13 | 14 | .SH OPTIONS 15 | .TP 16 | \fB-\-dbx\fR=\fIdbxfile\fR 17 | Use a specified file for dbx rather than the system variable. This option 18 | only works with \fB-\-list\fR. 19 | 20 | .TP 21 | \fB-\-export\fR 22 | Export the binary hashes and certificates from DBX entries into individual 23 | files. 24 | 25 | .TP 26 | \fB-\-prefix=\fIprefix\fR 27 | File prefix to use with \fB-\-export\fR. Defaults to "dbx". 28 | 29 | .TP 30 | \fB-\-list\fR 31 | List DBX entries in the system database, or in a copy of it or an update file. 32 | 33 | .TP 34 | \fB-\-verbose\fR 35 | Show a bunch of verbose output about what dbxtool is doing. 36 | 37 | .TP 38 | \fB-\-apply\fR 39 | Apply updates specified on the command line. Does not work with \fB-\-dbx\fR. 40 | This argument requires either an \fIupdatedir\fR argument or one or more 41 | \fIupdateN\fR arguments. 42 | 43 | .TP 44 | \fIupdatedir\fR 45 | Directory of dbx updates. Filenames must end in ".bin". This argument only 46 | works with \fB-\-apply\fR. 47 | 48 | .TP 49 | \fIupdate0\fR 50 | An individual dbx update. This argument only works with \fB-\-apply\fR. 51 | 52 | .SH AUTHORS 53 | .nf 54 | Peter Jones 55 | .fi 56 | -------------------------------------------------------------------------------- /src/iter.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Red Hat, Inc. 3 | * All rights reserved. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; version 2 of the License. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * Author(s): Peter Jones 18 | */ 19 | #ifndef DBXTOOL_ITER_H 20 | #define DBXTOOL_ITER_H 1 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "esl.h" 27 | 28 | typedef struct esd_iter esd_iter; 29 | 30 | extern int esd_iter_new(esd_iter **iter, uint8_t *buf, size_t len); 31 | extern int esd_iter_end(esd_iter *iter); 32 | extern int esd_iter_next(esd_iter *iter, efi_guid_t *type, 33 | efi_guid_t *owner, uint8_t **data, size_t *len); 34 | extern int esd_iter_get_line(esd_iter *iter); 35 | 36 | typedef struct esl_iter esl_iter; 37 | extern int esl_iter_new(esl_iter **iter, uint8_t *buf, size_t len); 38 | extern int esl_iter_end(esl_iter *iter); 39 | extern int esl_iter_next(esl_iter *iter, efi_guid_t *type, 40 | EFI_SIGNATURE_DATA **data, size_t *len); 41 | extern int esl_list_size(esl_iter *iter, size_t *sls); 42 | extern int esl_header_size(esl_iter *iter, size_t *slh); 43 | extern int esl_sig_size(esl_iter *iter, size_t *ss); 44 | extern int esl_get_type(esl_iter *iter, efi_guid_t *type); 45 | 46 | #endif /* DBXTOOL_ITER_H */ 47 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOPDIR = $(shell echo $$PWD) 2 | 3 | include $(TOPDIR)/Make.version 4 | include $(TOPDIR)/Make.rules 5 | include $(TOPDIR)/Make.defaults 6 | 7 | SUBDIRS := src data 8 | DOCDIR := /share/doc/ 9 | 10 | all : $(SUBDIRS) 11 | 12 | include $(TOPDIR)/Make.coverity 13 | include $(TOPDIR)/Make.scan-build 14 | 15 | $(SUBDIRS) : 16 | $(MAKE) -C $@ TOPDIR=$(TOPDIR) SRCDIR=$(TOPDIR)/$@/ 17 | 18 | clean : 19 | @for x in $(SUBDIRS) ; do $(MAKE) -C $${x} TOPDIR=$(TOPDIR) SRCDIR=$(TOPDIR)/$@/ $@ ; done 20 | 21 | install : 22 | @for x in $(SUBDIRS) ; do $(MAKE) -C $${x} TOPDIR=$(TOPDIR) SRCDIR=$(TOPDIR)/$@/ $@ ; done 23 | $(INSTALL) -d -m 755 $(INSTALLROOT)$(PREFIX)$(DOCDIR)/dbxtool/ 24 | $(INSTALL) -m 644 COPYING $(INSTALLROOT)$(PREFIX)$(DOCDIR)/dbxtool/ 25 | 26 | .PHONY: $(SUBDIRS) clean install 27 | 28 | GITTAG = $(VERSION) 29 | 30 | test-archive: 31 | @rm -rf /tmp/dbxtool-$(VERSION) /tmp/dbxtool-$(VERSION)-tmp 32 | @mkdir -p /tmp/dbxtool-$(VERSION)-tmp 33 | @git archive --format=tar $(shell git branch | awk '/^*/ { print $$2 }') | ( cd /tmp/dbxtool-$(VERSION)-tmp/ ; tar x ) 34 | @git diff | ( cd /tmp/dbxtool-$(VERSION)-tmp/ ; patch -s -p1 -b -z .gitdiff ) 35 | @mv /tmp/dbxtool-$(VERSION)-tmp/ /tmp/dbxtool-$(VERSION)/ 36 | @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/dbxtool-$(VERSION).tar.bz2 dbxtool-$(VERSION) 37 | @rm -rf /tmp/dbxtool-$(VERSION) 38 | @echo "The archive is in dbxtool-$(VERSION).tar.bz2" 39 | 40 | tag: 41 | git tag -s dbxtool-$(GITTAG) refs/heads/master 42 | 43 | archive: tag 44 | @rm -rf /tmp/dbxtool-$(VERSION) /tmp/dbxtool-$(VERSION)-tmp 45 | @mkdir -p /tmp/dbxtool-$(VERSION)-tmp 46 | @git archive --format=tar dbxtool-$(GITTAG) | ( cd /tmp/dbxtool-$(VERSION)-tmp/ ; tar x ) 47 | @mv /tmp/dbxtool-$(VERSION)-tmp/ /tmp/dbxtool-$(VERSION)/ 48 | @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/dbxtool-$(VERSION).tar.bz2 dbxtool-$(VERSION) 49 | @rm -rf /tmp/dbxtool-$(VERSION) 50 | @echo "The archive is in dbxtool-$(VERSION).tar.bz2" 51 | -------------------------------------------------------------------------------- /src/esltree.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Red Hat, Inc. 3 | * All rights reserved. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; version 2 of the License. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * Author(s): Peter Jones 18 | */ 19 | 20 | #include "fix_coverity.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include "esltree.h" 27 | #include "iter.h" 28 | #include "util.h" 29 | 30 | int 31 | esl_cmp(const void *l, const void *r) 32 | { 33 | const struct esl_tree_entry *le = l, *re = r; 34 | int ret; 35 | 36 | ret = efi_guid_cmp(&le->type, &re->type); 37 | if (ret != 0) 38 | return ret; 39 | 40 | ret = memcmp2(le->data, le->datalen, re->data, re->datalen); 41 | return ret; 42 | } 43 | 44 | int 45 | esl_tree_create(void **rootp, uint8_t *dbx_buf, size_t dbx_len) 46 | { 47 | esd_iter *iter = NULL; 48 | int rc; 49 | int ret = 0; 50 | 51 | if (dbx_len == 0) { 52 | esl_tree_destroy(rootp); 53 | return 0; 54 | } 55 | 56 | rc = esd_iter_new(&iter, dbx_buf, dbx_len); 57 | if (rc < 0) 58 | err(1, NULL); 59 | 60 | while (1) { 61 | struct esl_tree_entry *ehp; 62 | 63 | ehp = calloc(1, sizeof (*ehp)); 64 | if (!ehp) 65 | err(1, NULL); 66 | 67 | rc = esd_iter_next(iter, &ehp->type, &ehp->owner, 68 | &ehp->data, &ehp->datalen); 69 | if (rc < 0) 70 | err(1, NULL); 71 | if (rc == 0) { 72 | free(ehp); 73 | break; 74 | } 75 | 76 | tsearch(ehp, rootp, esl_cmp); 77 | } 78 | 79 | esd_iter_end(iter); 80 | return ret; 81 | } 82 | 83 | static void 84 | esl_tree_destroy_node(void *nodep) 85 | { 86 | struct esl_tree_entry *ehp = (struct esl_tree_entry *)nodep; 87 | 88 | free(ehp); 89 | } 90 | 91 | void 92 | esl_tree_destroy(void **rootp) 93 | { 94 | tdestroy(*rootp, esl_tree_destroy_node); 95 | *rootp = NULL; 96 | } 97 | -------------------------------------------------------------------------------- /dbxtool.spec: -------------------------------------------------------------------------------- 1 | Name: dbxtool 2 | Version: 8 3 | Release: 1%{?dist} 4 | Summary: Secure Boot DBX updater 5 | License: GPLv2 6 | URL: https://github.com/vathpela/dbxtool 7 | ExclusiveArch: i386 x86_64 aarch64 8 | BuildRequires: popt-devel git systemd 9 | BuildRequires: efivar-devel >= 26-1 10 | Requires: efivar >= 26-1 11 | Source0: https://github.com/vathpela/dbxtool/releases/download/dbxtool-%{version}/dbxtool-%{version}.tar.bz2 12 | 13 | %description 14 | This package contains DBX updates for UEFI Secure Boot. 15 | 16 | %prep 17 | %setup -q -n %{name}-%{version} 18 | git init 19 | git config user.email "%{name}-owner@fedoraproject.org" 20 | git config user.name "Fedora Ninjas" 21 | git add . 22 | git commit -a -q -m "%{version} baseline." 23 | git am %{patches} - 8-1 48 | - Update to version 8 49 | - Make a "make coverity" rule to scan the source 50 | Results at: https://scan.coverity.com/projects/rhboot-dbxtool 51 | - Don't try to apply anything if PK and KEK aren't enrolled 52 | - Add --force and --quiet for the PK/KEK checker, and use them in the 53 | systemd service. 54 | - Add a .syntastic_c_config for vim's Syntastic modules 55 | - Use tsearch()/tfind()/tdestroy() from libc instead of ccan htables 56 | - Don't open the dbx file with O_RDWR|O_CREAT, use O_RDONLY. 57 | - Lots of minor bug fixes gcc -Wextra and friends found. 58 | 59 | * Wed Aug 10 2016 Peter Jones - 7-1 60 | - Update to version 7 61 | - Add new dbxupdate.bin for CVE-2016-3320 and 62 | https://support.microsoft.com/en-us/kb/3179577 63 | 64 | * Wed Oct 08 2014 Peter Jones - 0.6-1 65 | - Update to 0.6 66 | - make "dbxtool -l" correctly show not-well-known guids. 67 | 68 | * Tue Oct 07 2014 Peter Jones - 0.5-1 69 | - Update to 0.5: 70 | - make applying to dbx when it doesn't exist work (lersek) 71 | - make displaying KEK work right 72 | 73 | * Wed Aug 20 2014 Peter Jones - 0.4-1 74 | - First packaging attempt. 75 | -------------------------------------------------------------------------------- /src/esl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Red Hat, Inc. 3 | * All rights reserved. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; version 2 of the License. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * Author(s): Peter Jones 18 | */ 19 | #ifndef ESL_H 20 | #define ESL_H 1 21 | 22 | #include 23 | 24 | typedef struct { 25 | efi_guid_t SignatureOwner; 26 | uint8_t SignatureData[0]; 27 | } EFI_SIGNATURE_DATA; 28 | 29 | typedef struct { 30 | efi_guid_t SignatureType; 31 | uint32_t SignatureListSize; 32 | uint32_t SignatureHeaderSize; 33 | uint32_t SignatureSize; 34 | } EFI_SIGNATURE_LIST; 35 | 36 | typedef struct { 37 | uint16_t Year; /* 1998-20xx */ 38 | uint8_t Month; /* 1-12 */ 39 | uint8_t Day; /* 1-31 */ 40 | uint8_t Hour; /* 0-23 */ 41 | uint8_t Minute; /* 0-59 */ 42 | uint8_t Second; /* 0-59 */ 43 | uint8_t pad1; 44 | uint32_t Nanosecond; /* 0 - 999999999 */ 45 | int16_t TimeZone; /* -1440 to 1440 or 0x7ff */ 46 | uint8_t Daylight; /* bitmask, see below */ 47 | uint8_t pad2; 48 | } __attribute__((aligned (1))) EFI_TIME; 49 | 50 | #define EFI_TIME_ADJUST_DAYLIGHT 0x01 51 | #define EFI_TIME_IN_DAYLIGHT 0x02 52 | 53 | #define EFI_UNSPECIFIED_TIMEZONE 0x07ff 54 | 55 | typedef struct _EFI_CERT_BLOCK_RSA_2048_SHA256 { 56 | efi_guid_t HashType; 57 | uint8_t PublicKey[256]; 58 | uint8_t Signature[256]; 59 | } __attribute__((aligned (1))) EFI_CERT_BLOCK_RSA_2048_SHA256; 60 | 61 | typedef struct { 62 | uint32_t dwLength; 63 | uint16_t wRevision; 64 | uint16_t wCertificateType; 65 | uint8_t bCertificate[0]; 66 | } __attribute__((aligned (1))) WIN_CERTIFICATE; 67 | 68 | #define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 69 | #define WIN_CERT_TYPE_EFI_PKCS115 0x0ef0 70 | #define WIN_CERT_TYPE_EFI_GUID 0x0ef1 71 | 72 | typedef struct { 73 | WIN_CERTIFICATE Hdr; 74 | efi_guid_t CertType; 75 | // uint8_t CertData[0]; 76 | } __attribute__((aligned (1))) WIN_CERTIFICATE_UEFI_GUID; 77 | 78 | typedef struct { 79 | uint64_t MonotonicCount; 80 | WIN_CERTIFICATE_UEFI_GUID AuthInfo; 81 | } __attribute__((aligned (1))) EFI_VARIABLE_AUTHENTICATION; 82 | 83 | typedef struct { 84 | EFI_TIME TimeStamp; 85 | WIN_CERTIFICATE_UEFI_GUID AuthInfo; 86 | } __attribute__((aligned (1))) EFI_VARIABLE_AUTHENTICATION_2; 87 | 88 | #endif /* ESL_H */ 89 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | name: push-builds 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | schedule: 8 | - cron: '0 3 * * *' 9 | 10 | jobs: 11 | push-f34-x64: 12 | runs-on: ubuntu-20.04 13 | container: vathpela/efi-ci:f34 14 | name: f34 build 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | - name: Do the build 19 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test clean all 20 | id: build 21 | - name: Install in /destdir 22 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test install 23 | id: install 24 | # - name: Archive production artifacts 25 | # uses: actions/upload-artifact@v2 26 | # with: 27 | # name: shim 28 | # path: | 29 | # /destdir 30 | push-f33-x64: 31 | runs-on: ubuntu-20.04 32 | container: vathpela/efi-ci:f33 33 | name: f33 build 34 | steps: 35 | - name: Checkout 36 | uses: actions/checkout@v2 37 | - name: Do the build 38 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test clean all 39 | id: build 40 | - name: Install in /destdir 41 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test install 42 | id: install 43 | # - name: Archive production artifacts 44 | # uses: actions/upload-artifact@v2 45 | # with: 46 | # name: shim 47 | # path: | 48 | # /destdir 49 | push-f32-x64: 50 | runs-on: ubuntu-20.04 51 | container: vathpela/efi-ci:f32 52 | name: f32 build 53 | steps: 54 | - name: Checkout 55 | uses: actions/checkout@v2 56 | - name: Do the build 57 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test clean all 58 | id: build 59 | - name: Install in /destdir 60 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test install 61 | id: install 62 | # - name: Archive production artifacts 63 | # uses: actions/upload-artifact@v2 64 | # with: 65 | # name: shim 66 | # path: | 67 | # /destdir 68 | push-f31-x64: 69 | runs-on: ubuntu-20.04 70 | container: vathpela/efi-ci:f31 71 | name: f31 build 72 | steps: 73 | - name: Checkout 74 | uses: actions/checkout@v2 75 | - name: Do the build 76 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test clean all 77 | id: build 78 | - name: Install in /destdir 79 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test install 80 | id: install 81 | # - name: Archive production artifacts 82 | # uses: actions/upload-artifact@v2 83 | # with: 84 | # name: shim 85 | # path: | 86 | # /destdir 87 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011-2014 Red Hat, Inc. 3 | * All rights reserved. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; version 2 of the License. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * Author(s): Peter Jones 18 | */ 19 | #ifndef DBXTOOL_UTIL_H 20 | #define DBXTOOL_UTIL_H 1 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #define save_errno(x) \ 31 | ({ \ 32 | typeof (errno) __saved_errno = errno; \ 33 | x; \ 34 | errno = __saved_errno; \ 35 | }) 36 | 37 | extern int verbose; 38 | #define vprintf(fmt, args...) ({ \ 39 | if (verbose) \ 40 | printf(fmt, ##args); \ 41 | }) 42 | 43 | static inline int 44 | __attribute__ ((unused)) 45 | read_file(int fd, char **bufp, size_t *lenptr) { 46 | int alloced = 0, size = 0, i = 0; 47 | char * buf = NULL; 48 | 49 | do { 50 | size += i; 51 | if ((size + 1024) > alloced) { 52 | alloced += 4096; 53 | buf = realloc(buf, alloced + 1); 54 | } 55 | } while ((i = read(fd, buf + size, 1024)) > 0); 56 | 57 | if (i < 0) { 58 | free(buf); 59 | return -1; 60 | } 61 | 62 | *bufp = buf; 63 | *lenptr = size; 64 | 65 | return 0; 66 | } 67 | 68 | static int 69 | __attribute__ ((unused)) 70 | timecmp(EFI_TIME *a, EFI_TIME *b) 71 | { 72 | if (a->Year != b->Year) 73 | return a->Year - b->Year; 74 | if (a->Month != b->Month) 75 | return a->Month - b->Month; 76 | if (a->Day != b->Day) 77 | return a->Day - b->Day; 78 | if (a->Hour != b->Hour) 79 | return a->Hour - b->Hour; 80 | if (a->Minute != b->Minute) 81 | return a->Minute - b->Minute; 82 | if (a->Second != b->Second) 83 | return a->Second - b->Second; 84 | return 0; 85 | } 86 | 87 | static void 88 | __attribute__ ((unused)) 89 | print_hex(uint8_t *data, size_t len) 90 | { 91 | char hex[] = "0123456789abcdef"; 92 | for (unsigned int i = 0; i < len; i++) 93 | printf("%c%c", hex[(data[i] & 0xf0) >> 4], 94 | hex[(data[i] & 0x0f) >> 0]); 95 | } 96 | 97 | static int 98 | __attribute__ ((unused)) 99 | memcmp2(const void *l, const ssize_t ll, const void *r, const ssize_t rl) 100 | { 101 | int ret; 102 | 103 | ret = memcmp(l, r, MIN(ll, rl)); 104 | if (ret != 0 || ll == rl) 105 | return ret; 106 | 107 | return ll < rl ? -1 : 1; 108 | } 109 | 110 | #endif /* DBXTOOL_UTIL_H */ 111 | -------------------------------------------------------------------------------- /src/fix_coverity.h: -------------------------------------------------------------------------------- 1 | /* 2 | * fix_coverity.h 3 | * Copyright 2017 Peter Jones 4 | * 5 | * Distributed under terms of the GPLv3 license. 6 | */ 7 | 8 | #ifndef FIX_COVERITY_H 9 | #define FIX_COVERITY_H 10 | 11 | #ifndef __COVERITY_GCC_VERSION_AT_LEAST 12 | #define __COVERITY_GCC_VERSION_AT_LEAST(x, y) 0 13 | #define FAKE__COVERITY_GCC_VERSION_AT_LEAST__ 14 | #endif /* __COVERITY_GCC_VERSION_AT_LEAST */ 15 | 16 | /* With gcc 7 on x86_64 (at least), coverity pretends to be GCC but 17 | * accidentally doesn't create all of the types GCC would. 18 | * 19 | * In glibc's headers, bits/floatn.h has: 20 | * 21 | * #if (defined __x86_64__ \ 22 | * ? __GNUC_PREREQ (4, 3) \ 23 | * : (defined __GNU__ ? __GNUC_PREREQ (4, 5) : __GNUC_PREREQ (4, 4))) 24 | * # define __HAVE_FLOAT128 1 25 | * #else 26 | * # define __HAVE_FLOAT128 0 27 | * #endif 28 | * 29 | * and stdlib.h has: 30 | * 31 | * #if __HAVE_FLOAT128 && __GLIBC_USE (IEC_60559_TYPES_EXT) 32 | * slash* Likewise for the '_Float128' format *slash 33 | * extern _Float128 strtof128 (const char *__restrict __nptr, 34 | * char **__restrict __endptr) 35 | * __THROW __nonnull ((1)); 36 | * #endif 37 | * 38 | * Which then causes cov-emit to lose its shit: 39 | * 40 | * "/usr/include/stdlib.h", line 133: error #20: identifier "_Float128" is 41 | * undefined 42 | * extern _Float128 strtof128 (const char *__restrict __nptr, 43 | * ^ 44 | * "/usr/include/stdlib.h", line 190: error #20: identifier "_Float128" is 45 | * undefined 46 | * _Float128 __f) 47 | * ^ 48 | * "/usr/include/stdlib.h", line 236: error #20: identifier "_Float128" is 49 | * undefined 50 | * extern _Float128 strtof128_l (const char *__restrict __nptr, 51 | * ^ 52 | * 53 | * And then you'll notice something like this later on: 54 | * [WARNING] Emitted 0 C/C++ compilation units (0%) successfully 55 | * 56 | * 0 C/C++ compilation units (0%) are ready for analysis 57 | * For more details, please look at: 58 | * /home/pjones/devel/github.com/dbxtool/master/cov-int/build-log.txt 59 | * 60 | * You would think that if you're writing something that pretends to be 61 | * gcc, and you've got a "build a configuration by running shit through gcc 62 | * and looking at the output" stage (which they do), you would run "gcc -da 63 | * -fdump-tree-all -c -o foo.o foo.c" on an empty file and snarf up all the 64 | * types defined in the foo.c.001t.tu output. Apparently, they do not. 65 | * 66 | * So if we're in that case, just define the type for the thing. 67 | */ 68 | #ifdef __x86_64__ 69 | #if __COVERITY_GCC_VERSION_AT_LEAST(7, 0) 70 | typedef float _Float128 __attribute__((__vector_size__(128))); 71 | #endif 72 | #endif 73 | 74 | #ifdef FAKE__COVERITY_GCC_VERSION_AT_LEAST__ 75 | #undef FAKE__COVERITY_GCC_VERSION_AT_LEAST 76 | #undef __COVERITY_GCC_VERSION_AT_LEAST 77 | #endif 78 | 79 | #endif /* !FIX_COVERITY_H */ 80 | // vim:fenc=utf-8:tw=75 81 | -------------------------------------------------------------------------------- /.github/workflows/pullrequest.yml: -------------------------------------------------------------------------------- 1 | name: pull-request-builds 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | pull-request-f34-x64: 10 | runs-on: ubuntu-20.04 11 | container: vathpela/efi-ci:f34 12 | name: f34 build 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v2 16 | with: 17 | # otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger) 18 | ref: ${{ github.event.pull_request.head.sha }} 19 | fetch-depth: 0 20 | - name: fetch-origin 21 | run: git fetch origin 22 | id: fetch-origin 23 | - name: dammit0 24 | run: git remote -v 25 | id: dammit0 26 | - name: dammit1 27 | run: ls .git/refs/heads/ 28 | id: dammit1 29 | - name: Do the build 30 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test clean all 31 | id: build 32 | - name: Install in /destdir 33 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test install 34 | id: install 35 | # - name: Archive production artifacts 36 | # uses: actions/upload-artifact@v2 37 | # with: 38 | # name: shim 39 | # path: | 40 | # /destdir 41 | pull-request-f33-x64: 42 | runs-on: ubuntu-20.04 43 | container: vathpela/efi-ci:f33 44 | name: f33 build 45 | steps: 46 | - name: Checkout 47 | uses: actions/checkout@v2 48 | with: 49 | # otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger) 50 | ref: ${{ github.event.pull_request.head.sha }} 51 | fetch-depth: 0 52 | - name: Do the build 53 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test clean all 54 | id: build 55 | - name: Install in /destdir 56 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test install 57 | id: install 58 | # - name: Archive production artifacts 59 | # uses: actions/upload-artifact@v2 60 | # with: 61 | # name: shim 62 | # path: | 63 | # /destdir 64 | pull-request-f32-x64: 65 | runs-on: ubuntu-20.04 66 | container: vathpela/efi-ci:f32 67 | name: f32 build 68 | steps: 69 | - name: Checkout 70 | uses: actions/checkout@v2 71 | with: 72 | # otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger) 73 | ref: ${{ github.event.pull_request.head.sha }} 74 | fetch-depth: 0 75 | - name: Do the build 76 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test clean all 77 | id: build 78 | - name: Install in /destdir 79 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test install 80 | id: install 81 | # - name: Archive production artifacts 82 | # uses: actions/upload-artifact@v2 83 | # with: 84 | # name: shim 85 | # path: | 86 | # /destdir 87 | pull-request-f31-x64: 88 | runs-on: ubuntu-20.04 89 | container: vathpela/efi-ci:f31 90 | name: f31 build 91 | steps: 92 | - name: Checkout 93 | uses: actions/checkout@v2 94 | with: 95 | # otherwise we are testing target branch instead of the PR branch (see pull_request_target trigger) 96 | ref: ${{ github.event.pull_request.head.sha }} 97 | fetch-depth: 0 98 | - name: Do the build 99 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test clean all 100 | id: build 101 | - name: Install in /destdir 102 | run: make PREFIX=/usr LIBDIR=/usr/lib64 DESTDIR=/destdir EFIDIR=test install 103 | id: install 104 | # - name: Archive production artifacts 105 | # uses: actions/upload-artifact@v2 106 | # with: 107 | # name: shim 108 | # path: | 109 | # /destdir 110 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | /* 2 | error.h - some error functions to work with efivars error logger. 3 | 4 | Copyright 2016 Red Hat, Inc. 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | */ 20 | #ifndef EFIBOOTMGR_ERROR_H__ 21 | #define EFIBOOTMGR_ERROR_H__ 1 22 | 23 | #include 24 | #include 25 | 26 | extern int verbose; 27 | 28 | static inline void 29 | __attribute__((__unused__)) 30 | error_reporter(void) 31 | { 32 | int rc = 1; 33 | int saved_errno = errno; 34 | 35 | for (int i = 0; rc > 0; i++) { 36 | char *filename = NULL; 37 | char *function = NULL; 38 | int line = 0; 39 | char *message = NULL; 40 | int error = 0; 41 | 42 | rc = efi_error_get(i, &filename, &function, &line, &message, 43 | &error); 44 | if (rc < 0) { 45 | fprintf(stderr, "error fetching trace value"); 46 | exit(1); 47 | } 48 | if (rc == 0) 49 | break; 50 | fprintf(stderr, " %s:%d %s(): %s: %s\n", 51 | filename, line, function, message, strerror(error)); 52 | } 53 | errno = saved_errno; 54 | } 55 | 56 | static inline void 57 | __attribute__((__unused__)) 58 | conditional_error_reporter(int show, int clear) 59 | { 60 | int saved_errno = errno; 61 | fflush(NULL); 62 | 63 | if (show) { 64 | fprintf(stderr, "error trace:\n"); 65 | error_reporter(); 66 | } 67 | if (clear) { 68 | errno = 0; 69 | efi_error_clear(); 70 | } 71 | errno = saved_errno; 72 | } 73 | 74 | static inline void 75 | __attribute__((__unused__)) 76 | cond_error(int test, int eval, const char *fmt, ...) 77 | { 78 | int saved_errno = errno; 79 | if (!test) 80 | return; 81 | fflush(NULL); 82 | 83 | va_list ap; 84 | va_start(ap, fmt); 85 | vfprintf(stderr, fmt, ap); 86 | errno = saved_errno; 87 | fprintf(stderr, ": %m\n"); 88 | conditional_error_reporter(verbose >= 1, 0); 89 | va_end(ap); 90 | exit(eval); 91 | } 92 | 93 | static inline void 94 | __attribute__((__unused__)) 95 | error(int eval, const char *fmt, ...) 96 | { 97 | int saved_errno = errno; 98 | fflush(NULL); 99 | 100 | va_list ap; 101 | va_start(ap, fmt); 102 | vfprintf(stderr, fmt, ap); 103 | errno = saved_errno; 104 | fprintf(stderr, ": %m\n"); 105 | conditional_error_reporter(verbose >= 1, 0); 106 | va_end(ap); 107 | exit(eval); 108 | } 109 | 110 | static inline void 111 | __attribute__((__unused__)) 112 | errorx(int eval, const char *fmt, ...) 113 | { 114 | fflush(NULL); 115 | 116 | va_list ap; 117 | va_start(ap, fmt); 118 | vfprintf(stderr, fmt, ap); 119 | fprintf(stderr, "\n"); 120 | conditional_error_reporter(verbose >= 1, 1); 121 | va_end(ap); 122 | exit(eval); 123 | } 124 | 125 | static inline void 126 | __attribute__((__unused__)) 127 | cond_warning(int test, const char *fmt, ...) 128 | { 129 | int saved_errno = errno; 130 | if (!test) 131 | return; 132 | 133 | va_list ap; 134 | va_start(ap, fmt); 135 | vprintf(fmt, ap); 136 | errno = saved_errno; 137 | printf(": %m\n"); 138 | conditional_error_reporter(verbose >= 1, 1); 139 | va_end(ap); 140 | } 141 | 142 | static inline void 143 | __attribute__((__unused__)) 144 | warning(const char *fmt, ...) 145 | { 146 | int saved_errno = errno; 147 | va_list ap; 148 | 149 | va_start(ap, fmt); 150 | vprintf(fmt, ap); 151 | errno = saved_errno; 152 | printf(": %m\n"); 153 | conditional_error_reporter(verbose >= 1, 1); 154 | va_end(ap); 155 | } 156 | 157 | static inline void 158 | __attribute__((__unused__)) 159 | warningx(const char *fmt, ...) 160 | { 161 | va_list ap; 162 | 163 | va_start(ap, fmt); 164 | vprintf(fmt, ap); 165 | printf("\n"); 166 | conditional_error_reporter(verbose >= 1, 1); 167 | va_end(ap); 168 | } 169 | #endif /* EFIBOOTMGR_ERROR_H__ */ 170 | -------------------------------------------------------------------------------- /src/iter.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Red Hat, Inc. 3 | * All rights reserved. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; version 2 of the License. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * Author(s): Peter Jones 18 | */ 19 | 20 | #include "fix_coverity.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "esl.h" 28 | #include "iter.h" 29 | #include "util.h" 30 | #include "x509.h" 31 | 32 | struct esd_iter { 33 | esl_iter *iter; 34 | int line; 35 | 36 | EFI_SIGNATURE_DATA *esd; 37 | size_t len; 38 | 39 | size_t nmemb; 40 | unsigned int i; 41 | }; 42 | 43 | int 44 | esd_iter_new(esd_iter **iter, uint8_t *buf, size_t len) 45 | { 46 | if (len < sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA)) { 47 | errno = EINVAL; 48 | return -1; 49 | } 50 | 51 | *iter = calloc(1, sizeof (esd_iter)); 52 | if (!*iter) 53 | err(1, NULL); 54 | 55 | int rc; 56 | 57 | rc = esl_iter_new(&(*iter)->iter, buf, len); 58 | if (rc < 0) { 59 | save_errno(free(*iter)); 60 | return -1; 61 | } 62 | 63 | (*iter)->i = -1; 64 | 65 | return 0; 66 | } 67 | 68 | int 69 | esd_iter_end(esd_iter *iter) 70 | { 71 | if (!iter) { 72 | errno = EINVAL; 73 | return -1; 74 | } 75 | if (iter->iter) 76 | esl_iter_end(iter->iter); 77 | free(iter); 78 | return 0; 79 | } 80 | 81 | int 82 | esd_iter_next(esd_iter *iter, efi_guid_t *type, efi_guid_t *owner, 83 | uint8_t **data, size_t *len) 84 | { 85 | int rc; 86 | size_t ss; 87 | 88 | if (!iter) 89 | return -EINVAL; 90 | 91 | if (iter->iter == NULL) 92 | return -EINVAL; 93 | 94 | iter->line += 1; 95 | 96 | iter->i += 1; 97 | if (iter->i == iter->nmemb) { 98 | vprintf("Getting next EFI_SIGNATURE_DATA\n"); 99 | iter->i = 0; 100 | rc = esl_iter_next(iter->iter, type, &iter->esd, &iter->len); 101 | if (rc < 1) 102 | return rc; 103 | 104 | if (!efi_guid_cmp(type, &efi_guid_x509_cert)) { 105 | int32_t asn1size; 106 | 107 | asn1size = GetASN1SequenceSize(iter->esd->SignatureData, 108 | iter->len - sizeof (iter->esd->SignatureOwner)); 109 | 110 | if (asn1size < 0) { 111 | warnx("iterator data claims to be an X.509 " 112 | "Cert but is not valid ASN.1 DER"); 113 | } else if ((uint32_t)asn1size != iter->len - 114 | sizeof (iter->esd->SignatureOwner)) { 115 | warnx("X.509 Cert ASN.1 size does not match " 116 | "SignatureList Size (%d vs %ld)", 117 | asn1size, iter->len - 118 | sizeof (iter->esd->SignatureOwner)); 119 | } 120 | } 121 | 122 | size_t sls, slh; 123 | rc = esl_list_size(iter->iter, &sls); 124 | if (rc < 0) 125 | return rc; 126 | 127 | rc = esl_header_size(iter->iter, &slh); 128 | if (rc < 0) 129 | return rc; 130 | 131 | rc = esl_sig_size(iter->iter, &ss); 132 | if (rc < 0) 133 | return rc; 134 | 135 | /* if we'd have leftover data, then this ESD is garbage. */ 136 | if ((sls - sizeof (EFI_SIGNATURE_LIST) - slh) % ss != 0) 137 | return -EINVAL; 138 | 139 | iter->nmemb = (sls - sizeof (EFI_SIGNATURE_LIST) - slh) / ss; 140 | } else { 141 | vprintf("Getting next esd element\n"); 142 | rc = esl_sig_size(iter->iter, &ss); 143 | if (rc < 0) 144 | return rc; 145 | 146 | iter->esd = (EFI_SIGNATURE_DATA *)((intptr_t)iter->esd + ss); 147 | } 148 | 149 | rc = esl_get_type(iter->iter, type); 150 | if (rc < 0) 151 | return rc; 152 | *owner = iter->esd->SignatureOwner; 153 | *data = iter->esd->SignatureData; 154 | *len = ss - sizeof (iter->esd->SignatureOwner); 155 | return 1; 156 | } 157 | 158 | int 159 | esd_iter_get_line(esd_iter *iter) 160 | { 161 | if (!iter) { 162 | errno = EINVAL; 163 | return -1; 164 | } 165 | 166 | return iter->line; 167 | } 168 | 169 | struct esl_iter { 170 | uint8_t *buf; 171 | size_t len; 172 | 173 | off_t offset; 174 | 175 | EFI_SIGNATURE_LIST *esl; 176 | }; 177 | 178 | int 179 | esl_iter_new(esl_iter **iter, uint8_t *buf, size_t len) 180 | { 181 | if (len < sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA)) { 182 | errno = EINVAL; 183 | return -1; 184 | } 185 | 186 | *iter = calloc(1, sizeof (esl_iter)); 187 | if (!*iter) 188 | err(1, NULL); 189 | 190 | (*iter)->buf = buf; 191 | (*iter)->len = len; 192 | 193 | return 0; 194 | } 195 | 196 | int 197 | esl_iter_end(esl_iter *iter) 198 | { 199 | if (!iter) { 200 | errno = EINVAL; 201 | return -1; 202 | } 203 | free(iter); 204 | return 0; 205 | } 206 | 207 | int 208 | esl_iter_next(esl_iter *iter, efi_guid_t *type, 209 | EFI_SIGNATURE_DATA **data, size_t *len) 210 | { 211 | if (!iter) 212 | return -EINVAL; 213 | if (iter->offset < 0) 214 | return -EINVAL; 215 | if ((uint32_t)iter->offset >= iter->len) 216 | return -EINVAL; 217 | 218 | if (!iter->esl) { 219 | vprintf("Getting next ESL buffer\n"); 220 | iter->esl = (EFI_SIGNATURE_LIST *)iter->buf; 221 | } else { 222 | vprintf("Getting next EFI_SIGNATURE_LIST\n"); 223 | efi_guid_t type; 224 | esl_get_type(iter, &type); 225 | if (iter->len - iter->offset < iter->esl->SignatureListSize) { 226 | warnx("EFI Signature List is malformed"); 227 | errx(1, "list has %zd bytes left, element is %"PRIu32" bytes", 228 | iter->len - iter->offset, 229 | iter->esl->SignatureListSize); 230 | } 231 | if (!efi_guid_cmp(&type, &efi_guid_x509_cert)) { 232 | int32_t asn1size; 233 | 234 | asn1size = GetASN1SequenceSize( 235 | ((uint8_t *)*data) + sizeof (efi_guid_t), 236 | *len - sizeof (efi_guid_t)); 237 | if (asn1size < 0) { 238 | warnx("iterator data claims to be an X.509 " 239 | "Cert but is not valid ASN.1 DER"); 240 | } else if ((uint32_t)asn1size != iter->esl->SignatureSize 241 | - sizeof (efi_guid_t)) { 242 | warnx("X.509 Cert ASN.1 size does not match " 243 | "SignatureList Size (%d vs %ld)", 244 | asn1size, iter->esl->SignatureSize - 245 | sizeof (efi_guid_t)); 246 | } 247 | 248 | } 249 | 250 | iter->offset += iter->esl->SignatureListSize; 251 | if ((uint32_t)iter->offset >= iter->len) 252 | return 0; 253 | iter->esl = (EFI_SIGNATURE_LIST *)((intptr_t)iter->buf 254 | + iter->offset); 255 | } 256 | 257 | EFI_SIGNATURE_LIST esl; 258 | memset(&esl, '\0', sizeof (esl)); 259 | /* if somehow we've gotten a buffer that's bigger than our 260 | * real list, this will be zeros, so we've hit the end. */ 261 | if (!memcmp(&esl, iter->esl, sizeof (esl))) 262 | return 0; 263 | 264 | /* if this list size is too big for our data, then it's malformed 265 | * data and we're done. */ 266 | if (iter->esl->SignatureListSize > iter->len - iter->offset) 267 | return -EINVAL; 268 | 269 | *type = iter->esl->SignatureType; 270 | *data = (EFI_SIGNATURE_DATA *)((intptr_t)iter->esl 271 | + sizeof (EFI_SIGNATURE_LIST) 272 | + iter->esl->SignatureHeaderSize); 273 | *len = iter->esl->SignatureListSize - sizeof (EFI_SIGNATURE_LIST); 274 | 275 | return 1; 276 | } 277 | 278 | int 279 | esl_list_size(esl_iter *iter, size_t *sls) 280 | { 281 | if (!iter || !iter->esl) { 282 | errno = EINVAL; 283 | return -1; 284 | } 285 | /* this has to be at least as large as its header to be valid */ 286 | if (iter->esl->SignatureListSize < sizeof (EFI_SIGNATURE_LIST)) { 287 | errno = EINVAL; 288 | return -1; 289 | } 290 | 291 | *sls = iter->esl->SignatureListSize; 292 | return 0; 293 | } 294 | 295 | int 296 | esl_header_size(esl_iter *iter, size_t *slh) 297 | { 298 | if (!iter || !iter->esl) { 299 | errno = EINVAL; 300 | return -1; 301 | } 302 | 303 | *slh = iter->esl->SignatureHeaderSize; 304 | return 0; 305 | } 306 | 307 | int 308 | esl_sig_size(esl_iter *iter, size_t *ss) 309 | { 310 | if (!iter || !iter->esl) { 311 | errno = EINVAL; 312 | return -1; 313 | } 314 | /* If signature size isn't positive, there's invalid data. */ 315 | if (iter->esl->SignatureSize < 1) { 316 | errno = EINVAL; 317 | return -1; 318 | } 319 | 320 | *ss = iter->esl->SignatureSize; 321 | return 0; 322 | } 323 | 324 | int 325 | esl_get_type(esl_iter *iter, efi_guid_t *type) 326 | { 327 | if (!iter || !iter->esl) { 328 | errno = EINVAL; 329 | return -1; 330 | } 331 | 332 | memcpy(type, &iter->esl->SignatureType, sizeof (*type)); 333 | return 0; 334 | } 335 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; version 2 of the License. 299 | 300 | This program is distributed in the hope that it will be useful, 301 | but WITHOUT ANY WARRANTY; without even the implied warranty of 302 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 303 | GNU General Public License for more details. 304 | 305 | You should have received a copy of the GNU General Public License along 306 | with this program; if not, write to the Free Software Foundation, Inc., 307 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 308 | 309 | Also add information on how to contact you by electronic and paper mail. 310 | 311 | If the program is interactive, make it output a short notice like this 312 | when it starts in an interactive mode: 313 | 314 | Gnomovision version 69, Copyright (C) year name of author 315 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 316 | This is free software, and you are welcome to redistribute it 317 | under certain conditions; type `show c' for details. 318 | 319 | The hypothetical commands `show w' and `show c' should show the appropriate 320 | parts of the General Public License. Of course, the commands you use may 321 | be called something other than `show w' and `show c'; they could even be 322 | mouse-clicks or menu items--whatever suits your program. 323 | 324 | You should also get your employer (if you work as a programmer) or your 325 | school, if any, to sign a "copyright disclaimer" for the program, if 326 | necessary. Here is a sample; alter the names: 327 | 328 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 329 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 330 | 331 | {signature of Ty Coon}, 1 April 1989 332 | Ty Coon, President of Vice 333 | 334 | This General Public License does not permit incorporating your program into 335 | proprietary programs. If your program is a subroutine library, you may 336 | consider it more useful to permit linking proprietary applications with the 337 | library. If this is what you want to do, use the GNU Lesser General 338 | Public License instead of this License. 339 | -------------------------------------------------------------------------------- /src/dbxtool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Red Hat, Inc. 3 | * All rights reserved. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; version 2 of the License. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | * 17 | * Author(s): Peter Jones 18 | */ 19 | 20 | #include "fix_coverity.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "error.h" 37 | #include "esl.h" 38 | #include "esltree.h" 39 | #include "iter.h" 40 | #include "util.h" 41 | 42 | #define ACTION_LIST 0x1 43 | #define ACTION_APPLY 0x2 44 | #define ACTION_EXPORT 0x4 45 | 46 | typedef enum { 47 | DUMP_PRINT, 48 | DUMP_EXPORT 49 | } dump_mode_t; 50 | 51 | int verbose = 0; 52 | 53 | typedef struct { 54 | char *dbx_file; 55 | int action; 56 | bool force; 57 | bool quiet; 58 | } dbxtool_ctx; 59 | 60 | struct db_update_file { 61 | const char *name; 62 | void *base; 63 | size_t len; 64 | }; 65 | 66 | static inline int 67 | print_time(FILE *f, EFI_TIME *t) 68 | { 69 | return fprintf(f, "%4d-%d-%d %d:%d:%d", 70 | t->Year, t->Month, t->Day, t->Hour, t->Minute, t->Second); 71 | } 72 | 73 | static inline int 74 | is_time_sane(EFI_TIME *t) 75 | { 76 | if (t->Second >= 60) 77 | return 0; 78 | if (t->Minute >= 60) 79 | return 0; 80 | if (t->Hour >= 24) 81 | return 0; 82 | int mlen = 0; 83 | switch (t->Month) { 84 | case 1: 85 | case 3: 86 | case 5: 87 | case 7: 88 | case 8: 89 | case 10: 90 | case 12: 91 | mlen = 31; 92 | break; 93 | case 2: 94 | mlen = 28; 95 | break; 96 | case 4: 97 | case 6: 98 | case 9: 99 | case 11: 100 | mlen = 30; 101 | break; 102 | default: 103 | return 0; 104 | } 105 | if (t->Day == 0 || t->Day > mlen) 106 | return 0; 107 | if (t->Year < 1998) 108 | return 0; 109 | return 1; 110 | } 111 | 112 | static const struct { 113 | const efi_guid_t * const guid; 114 | const char * const suffix; 115 | } suffixes[] = { 116 | { &efi_guid_x509_cert, ".cer" }, 117 | { &efi_guid_sha1, ".sha1" }, 118 | { &efi_guid_sha224, ".sha224" }, 119 | { &efi_guid_sha256, ".sha256" }, 120 | { &efi_guid_sha384, ".sha384" }, 121 | { &efi_guid_sha512, ".sha512" }, 122 | { &efi_guid_x509_sha256, ".tbs.sha256" }, 123 | { &efi_guid_x509_sha384, ".tbs.sha384" }, 124 | { &efi_guid_x509_sha512, ".tbs.sha512" }, 125 | { &efi_guid_empty, "" }, 126 | }; 127 | 128 | int 129 | dump_dbx(dump_mode_t mode, const char * const prefix, 130 | uint8_t *buf, size_t len) 131 | { 132 | int rc; 133 | int x = 0, i; 134 | char *outfile; 135 | FILE *out; 136 | int pfxdot; 137 | esd_iter *iter = NULL; 138 | 139 | if (len == 0) 140 | return 0; 141 | 142 | rc = esd_iter_new(&iter, buf, len); 143 | if (rc < 0) 144 | error(1, NULL); 145 | 146 | while (1) { 147 | efi_guid_t type; 148 | efi_guid_t owner; 149 | uint8_t *data; 150 | size_t datalen; 151 | 152 | rc = esd_iter_next(iter, &type, &owner, &data, &datalen); 153 | if (rc < 0) 154 | error(1, NULL); 155 | if (rc == 0) 156 | break; 157 | 158 | char *typestr = NULL; 159 | int rc = efi_guid_to_id_guid(&type, &typestr); 160 | if (rc < 0) 161 | error(1, "bad type guid"); 162 | 163 | char *ownerstr = NULL; 164 | rc = efi_guid_to_id_guid(&owner, &ownerstr); 165 | if (rc < 0) 166 | error(1, "bad owner guid"); 167 | 168 | printf("%4d: %s %s ", esd_iter_get_line(iter), 169 | ownerstr, typestr); 170 | switch (mode) { 171 | case DUMP_PRINT: 172 | print_hex(data, datalen); 173 | printf("\n"); 174 | break; 175 | case DUMP_EXPORT: 176 | pfxdot = strlen(prefix); 177 | if (pfxdot < 1 || prefix[pfxdot-1] != '.') 178 | pfxdot = 1; 179 | else 180 | pfxdot = 0; 181 | 182 | for (i = 0; !efi_guid_is_empty(suffixes[i].guid); i++) { 183 | if (!efi_guid_cmp(&type, suffixes[i].guid)) 184 | break; 185 | } 186 | 187 | rc = asprintf(&outfile, "%s%s%04d%s", 188 | prefix, pfxdot ? "." : "", x++, 189 | suffixes[i].suffix); 190 | if (rc < 1) 191 | error(1, "Couldn't allocate buffer"); 192 | 193 | out = fopen(outfile, "w"); 194 | if (!out) 195 | error(1, "Couldn't open \"%s\" for writing", 196 | outfile); 197 | 198 | printf("writing \"%s\"\n", outfile); 199 | free(outfile); 200 | fwrite(data, 1, datalen, out); 201 | fclose(out); 202 | break; 203 | } 204 | 205 | free(typestr); 206 | free(ownerstr); 207 | } 208 | 209 | esd_iter_end(iter); 210 | return 0; 211 | } 212 | 213 | typedef enum { 214 | ft_unknown, 215 | ft_dbx, 216 | ft_dbx_noattr, 217 | ft_append_timestamp, 218 | ft_append_monotonic 219 | } filetype; 220 | 221 | static filetype 222 | guess_file_type(uint8_t *buf, size_t buflen) 223 | { 224 | EFI_VARIABLE_AUTHENTICATION_2 va2; 225 | EFI_VARIABLE_AUTHENTICATION va; 226 | 227 | efi_guid_t guids[] = { 228 | efi_guid_pkcs7_cert, 229 | efi_guid_rsa2048_sha256_cert, 230 | efi_guid_empty 231 | }; 232 | 233 | efi_guid_t esl_guids[] = { 234 | efi_guid_sha256, 235 | efi_guid_rsa2048, 236 | efi_guid_rsa2048_sha256, 237 | efi_guid_sha1, 238 | efi_guid_x509_cert, 239 | efi_guid_sha224, 240 | efi_guid_sha384, 241 | efi_guid_sha512, 242 | efi_guid_x509_sha256, 243 | efi_guid_empty 244 | }; 245 | 246 | vprintf("Attempting to identify filetype: "); 247 | 248 | while (buflen >= sizeof (va2)) { 249 | char *guidname = NULL; 250 | 251 | memcpy(&va2, buf, sizeof(va2)); 252 | efi_guid_to_id_guid(&va2.AuthInfo.CertType, &guidname); 253 | vprintf("va2 guid is %s ", guidname); 254 | free(guidname); 255 | guidname = NULL; 256 | if (efi_guid_is_empty(&va2.AuthInfo.CertType)) { 257 | vprintf("cannot be va2 data\n"); 258 | break; 259 | } else { 260 | vprintf("\n"); 261 | } 262 | 263 | for (int i = 0; efi_guid_is_empty(&guids[i]) == 0; i++) { 264 | efi_guid_to_id_guid(&guids[i], &guidname); 265 | vprintf("guid table guid is %s\n", guidname); 266 | free(guidname); 267 | guidname = NULL; 268 | if (!efi_guid_cmp(&guids[i], &va2.AuthInfo.CertType)) { 269 | vprintf("ft_append_timestamp is " 270 | "%4d-%02d-%02d %d:%d:%d\n", 271 | va2.TimeStamp.Year, 272 | va2.TimeStamp.Month, 273 | va2.TimeStamp.Day, 274 | va2.TimeStamp.Hour, 275 | va2.TimeStamp.Minute, 276 | va2.TimeStamp.Second); 277 | return ft_append_timestamp; 278 | } 279 | } 280 | break; 281 | } 282 | 283 | while (buflen >= sizeof (va)) { 284 | char *guidname = NULL; 285 | 286 | memcpy(&va, buf, sizeof(va)); 287 | efi_guid_to_id_guid(&va.AuthInfo.CertType, &guidname); 288 | vprintf("va guid is %s ", guidname); 289 | free(guidname); 290 | guidname = NULL; 291 | if (efi_guid_is_empty(&va.AuthInfo.CertType)) { 292 | vprintf("cannot be va data\n"); 293 | break; 294 | } else { 295 | vprintf("\n"); 296 | } 297 | 298 | for (int i = 0; efi_guid_is_empty(&guids[i]) == 0; i++) { 299 | if (!efi_guid_cmp(&guids[i], &va.AuthInfo.CertType)) { 300 | vprintf("ft_append_monotonic\n"); 301 | return ft_append_monotonic; 302 | } 303 | } 304 | break; 305 | } 306 | 307 | /* if we get a file that's just dd-ed from sysfs, 308 | * it'll have some attribute bits at the beginning */ 309 | vprintf("buf[0] is 0x%02x\n", buf[0]); 310 | if ((buf[0] & 311 | ~(EFI_VARIABLE_NON_VOLATILE| 312 | EFI_VARIABLE_BOOTSERVICE_ACCESS| 313 | EFI_VARIABLE_RUNTIME_ACCESS| 314 | EFI_VARIABLE_HARDWARE_ERROR_RECORD| 315 | EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS| 316 | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS| 317 | EFI_VARIABLE_APPEND_WRITE)) == 0 && 318 | buf[1] == 0 && 319 | buf[2] == 0 && 320 | buf[3] == 0 && 321 | buflen > (4 + sizeof (EFI_SIGNATURE_LIST))) { 322 | EFI_SIGNATURE_LIST esl; 323 | memcpy(&esl, buf + 4, sizeof (EFI_SIGNATURE_LIST)); 324 | for (int i = 0; efi_guid_is_empty(&esl_guids[i]) == 0; i++) { 325 | if (!efi_guid_cmp(&esl_guids[i], &esl.SignatureType)) { 326 | vprintf("ft_dbx\n"); 327 | return ft_dbx; 328 | } 329 | } 330 | } 331 | 332 | EFI_SIGNATURE_LIST esl; 333 | memcpy(&esl, buf, sizeof (EFI_SIGNATURE_LIST)); 334 | for (int i = 0; efi_guid_is_empty(&esl_guids[i]) == 0; i++) { 335 | if (!efi_guid_cmp(&esl_guids[i], &esl.SignatureType)) { 336 | vprintf("ft_dbx_noattr\n"); 337 | return ft_dbx_noattr; 338 | } 339 | } 340 | 341 | vprintf("ft_unknown\n"); 342 | return ft_unknown; 343 | } 344 | 345 | static ssize_t 346 | get_cert_type_size(efi_guid_t *guid) 347 | { 348 | struct { 349 | efi_guid_t guid; 350 | ssize_t size; 351 | } sizes[] = { 352 | {efi_guid_sha256, 32 }, 353 | {efi_guid_empty, 0 } 354 | }; 355 | for (int i = 0; efi_guid_cmp(&sizes[i].guid, &efi_guid_empty); i++) { 356 | if (!efi_guid_cmp(&sizes[i].guid, guid)) 357 | return sizes[i].size; 358 | } 359 | errno = ENOENT; 360 | efi_error("could not determine cert type size"); 361 | return -1; 362 | } 363 | 364 | static int update_cmp(const void *p, const void *q) 365 | { 366 | struct db_update_file *piov = (struct db_update_file *)p; 367 | struct db_update_file *qiov = (struct db_update_file *)q; 368 | 369 | EFI_VARIABLE_AUTHENTICATION_2 *vap = 370 | (EFI_VARIABLE_AUTHENTICATION_2 *)piov->base; 371 | EFI_VARIABLE_AUTHENTICATION_2 *vaq = 372 | (EFI_VARIABLE_AUTHENTICATION_2 *)qiov->base; 373 | 374 | return timecmp(&vap->TimeStamp, &vaq->TimeStamp); 375 | } 376 | 377 | static inline void 378 | sort_updates(struct db_update_file *updates, size_t num_updates) 379 | { 380 | vprintf("Sorting updates list\n"); 381 | if (num_updates < 2) 382 | return; 383 | qsort(updates, num_updates, sizeof (struct db_update_file), 384 | update_cmp); 385 | } 386 | 387 | static void print_update_name(const void *base) 388 | { 389 | EFI_VARIABLE_AUTHENTICATION_2 *va = 390 | (EFI_VARIABLE_AUTHENTICATION_2 *)base; 391 | print_time(stdout, &va->TimeStamp); 392 | } 393 | 394 | static void apply_update(struct db_update_file *update, uint32_t attributes) 395 | { 396 | int rc = 0; 397 | rc = efi_append_variable(efi_guid_security, "dbx", 398 | update->base, update->len, attributes); 399 | if (rc < 0) { 400 | error(1, "Could not apply database update \"%s\": %m\n" 401 | "Cannot Continue.", update->name); 402 | } 403 | } 404 | 405 | static int 406 | is_update_applied(struct db_update_file *update, void **dbx) 407 | { 408 | /* this function is a bit of a lie. We can't actually tell if an 409 | * update itself is installed, because to do that we'd need the 410 | * timestamp as well as the data. We also don't know what the 411 | * most recent timestamp applied *is* (or any other time stamp), 412 | * so the only thing we actually know is if all the list entries 413 | * in the update are present, then applying it doesn't accomplish 414 | * anything. 415 | * 416 | * So we can use that as a proxy for the update itself being applied, 417 | * but that means we may try to process an update with a newer 418 | * date than the unknown previous date, which will then fail. 419 | */ 420 | int rc; 421 | int ret = 1; 422 | 423 | EFI_VARIABLE_AUTHENTICATION_2 *va = 424 | (EFI_VARIABLE_AUTHENTICATION_2 *) 425 | update->base; 426 | size_t esllen = update->len 427 | - sizeof (va->TimeStamp) 428 | - va->AuthInfo.Hdr.dwLength; 429 | uint8_t *eslbuf = (uint8_t *) 430 | ((intptr_t)&va->AuthInfo.Hdr.bCertificate 431 | + va->AuthInfo.Hdr.dwLength 432 | - sizeof (va->AuthInfo.Hdr)); 433 | 434 | esd_iter *esdi = NULL; 435 | rc = esd_iter_new(&esdi, eslbuf, esllen); 436 | if (rc < 0) 437 | error(1, "Couldn't iterate contents of update"); 438 | 439 | while (1) { 440 | struct esl_tree_entry ehe; 441 | struct esl_tree_entry *ehep; 442 | 443 | rc = esd_iter_next(esdi, &ehe.type, &ehe.owner, 444 | &ehe.data, &ehe.datalen); 445 | if (rc < 0) 446 | error(1, NULL); 447 | if (rc == 0) 448 | break; 449 | 450 | ehep = tfind(&ehe, dbx, esl_cmp); 451 | if (!ehep) { 452 | vprintf("Update entry is not applied.\n"); 453 | ret = 0; 454 | break; 455 | } else { 456 | vprintf("Update entry is already applied.\n"); 457 | } 458 | } 459 | esd_iter_end(esdi); 460 | 461 | return ret; 462 | } 463 | 464 | static int 465 | endswith(char *str, char *suffix) 466 | { 467 | if (!str || !suffix || !*str || !*suffix) 468 | return 0; 469 | 470 | size_t str_len = strlen(str); 471 | size_t suffix_len = strlen(suffix); 472 | if (str_len < suffix_len) 473 | return 0; 474 | 475 | return !strcmp(str + (str_len - suffix_len), suffix); 476 | } 477 | 478 | static void 479 | load_update_file(struct db_update_file *update_ret, const char *path, int infd) 480 | { 481 | int fd = infd >= 0 ? infd : open(path, O_RDONLY); 482 | struct db_update_file update; 483 | int rc; 484 | 485 | if (fd < 0) 486 | error(1, "1 Could not read file \"%s\"", path); 487 | 488 | update.name = path; 489 | rc = read_file(fd, (char **)&update.base, &update.len); 490 | if (rc < 0) 491 | error(1, "2 Could not read file \"%s\"", path); 492 | 493 | if (infd < 0) 494 | close(fd); 495 | 496 | filetype ft = guess_file_type(update.base, update.len); 497 | if (ft != ft_append_timestamp) 498 | errorx(1, "dbxtool only supports timestamped updates\n"); 499 | 500 | EFI_VARIABLE_AUTHENTICATION_2 *va = 501 | (EFI_VARIABLE_AUTHENTICATION_2 *)update.base; 502 | 503 | if (!is_time_sane(&va->TimeStamp)) { 504 | fprintf(stderr, 505 | "\"%s\" contains a time stamp that is invalid: ", 506 | path); 507 | print_time(stderr, &va->TimeStamp); 508 | fprintf(stderr, "\n"); 509 | exit(1); 510 | } 511 | 512 | memcpy(update_ret, &update, sizeof (update)); 513 | } 514 | 515 | static void 516 | get_apply_files_from_dir(const char *dirname, 517 | struct db_update_file **updates_ret, 518 | int *num_updates_ret) 519 | { 520 | int rc; 521 | DIR *dir; 522 | struct db_update_file *updates = NULL; 523 | int num_updates = 0; 524 | 525 | dir = opendir(dirname); 526 | if (!dir) 527 | error(1, "Couldn't open directory \"%s\"", dirname); 528 | 529 | int dfd = dirfd(dir); 530 | if (dfd < 0) 531 | error(1, "Couldn't get directory \"%s\"", dirname); 532 | while (1) { 533 | struct dirent *d; 534 | 535 | errno = 0; 536 | if (!(d = readdir(dir))) { 537 | if (errno) 538 | error(1, "Couldn't read directory \"%s\"", 539 | dirname); 540 | break; 541 | } 542 | 543 | if (!endswith(d->d_name, ".bin")) 544 | continue; 545 | 546 | struct stat sb; 547 | rc = fstatat(dfd, d->d_name, &sb, 0); 548 | if (rc < 0) 549 | error(1, "Could not stat \"%s\"", d->d_name); 550 | 551 | if (!S_ISREG(sb.st_mode)) { 552 | vprintf("Skipping non regular file \"%s\"", 553 | d->d_name); 554 | continue; 555 | } 556 | num_updates++; 557 | 558 | struct db_update_file *new_updates = realloc(updates, 559 | num_updates * sizeof (*updates)); 560 | if (!new_updates) 561 | error(1, "Could not process updates"); 562 | updates = new_updates; 563 | 564 | int fd = openat(dfd, d->d_name, O_RDONLY); 565 | if (fd < 0) 566 | error(1, "Couldn't open update \"%s\"", d->d_name); 567 | 568 | load_update_file(&new_updates[num_updates-1], d->d_name, fd); 569 | close(fd); 570 | } 571 | *updates_ret = updates; 572 | *num_updates_ret = num_updates; 573 | } 574 | 575 | static void 576 | check_pk_and_kek(bool force, bool quiet) 577 | { 578 | struct { 579 | char *name; 580 | efi_guid_t guid; 581 | } guids[] = { 582 | { "PK", efi_guid_global }, 583 | { "KEK", efi_guid_global }, 584 | { NULL, efi_guid_zero }, 585 | }; 586 | bool all_found = true; 587 | 588 | for (int i = 0; guids[i].name != NULL; i++) { 589 | int rc; 590 | size_t datasize = 0; 591 | errno = 0; 592 | rc = efi_get_variable_size(guids[i].guid, guids[i].name, 593 | &datasize); 594 | if (rc < 0 && errno == ENOENT) { 595 | if (!quiet) 596 | warnx("%s is not set on this system.", 597 | guids[i].name); 598 | all_found = false; 599 | } 600 | } 601 | if (!all_found) { 602 | if (quiet) { 603 | if (!force) 604 | exit(0); 605 | } else { 606 | if (!force) 607 | errx(1, "Not attempting to apply updates."); 608 | warnx("attempting to apply updates anyway."); 609 | } 610 | } 611 | } 612 | 613 | int 614 | main(int argc, char *argv[]) 615 | { 616 | int rc; 617 | uint32_t action = 0; 618 | 619 | const char **apply_files = NULL; 620 | char * const prefix_default = "dbx."; 621 | char *prefix = prefix_default; 622 | 623 | dbxtool_ctx ctx = { 0 }; 624 | poptContext optCon; 625 | int force = 0, quiet = 0; 626 | struct poptOption options[] = { 627 | {NULL, '\0', POPT_ARG_INTL_DOMAIN, "dbxtool", 0, NULL, NULL }, 628 | {"dbx", 'd', POPT_ARG_STRING, 629 | &ctx.dbx_file, 0, "specify dbx database file", 630 | ""}, 631 | {"list", 'l', POPT_ARG_VAL|POPT_ARGFLAG_OR, 632 | &action, ACTION_LIST, 633 | "list entries in dbx", NULL }, 634 | {"apply", 'a', POPT_ARG_VAL|POPT_ARGFLAG_OR, 635 | &action, ACTION_APPLY, 636 | "apply update files", NULL }, 637 | {"export", 'e', POPT_ARG_VAL|POPT_ARGFLAG_OR, 638 | &action, ACTION_EXPORT, 639 | "update binary hashes and certificates", NULL }, 640 | {"force", 'f', POPT_ARG_VAL, 641 | &force, 1, 642 | "apply updates even if PK and KEK are not set", 643 | NULL }, 644 | {"prefix", 'p', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, 645 | &prefix, 0, "prefix to use with --export", NULL }, 646 | {"quiet", 'q', POPT_ARG_VAL, 647 | &quiet, 1, 648 | "do not show warnings if PK and KEK are not set", 649 | NULL }, 650 | {"verbose", 'v', POPT_ARG_VAL, 651 | &verbose, 1, 652 | "be verbose about everything", NULL }, 653 | POPT_AUTOALIAS 654 | POPT_AUTOHELP 655 | POPT_TABLEEND 656 | }; 657 | int num_updates = 0; 658 | 659 | optCon = poptGetContext("dbxtool", argc, (const char **)argv, 660 | options, 0); 661 | 662 | rc = poptReadDefaultConfig(optCon, 0); 663 | if (rc < 0 && !(rc == POPT_ERROR_ERRNO && errno == ENOENT)) 664 | errorx(1, "poptReadDefaultConfig failed: %s", poptStrerror(rc)); 665 | 666 | while ((rc = poptGetNextOpt(optCon)) > 0) 667 | ; 668 | 669 | if (action & ACTION_APPLY) { 670 | if (rc >= 0) 671 | errorx(1, "--apply was specified with no files given"); 672 | 673 | apply_files = poptGetArgs(optCon); 674 | if (apply_files == NULL) 675 | errorx(1, "--apply was specified with no files given: " 676 | "\"%s\": %s", 677 | poptBadOption(optCon, 0), poptStrerror(rc)); 678 | for (int i = 0; apply_files[i] != NULL; i++, num_updates++) { 679 | poptGetArg(optCon); 680 | if (access(apply_files[i], R_OK)) 681 | error(1, "Could not open \"%s\"", 682 | apply_files[i]); 683 | } 684 | rc = 0; 685 | } 686 | 687 | if (rc < -1) 688 | errorx(1, "Invalid argument: \"%s\": %s", 689 | poptBadOption(optCon, 0), poptStrerror(rc)); 690 | 691 | if (poptPeekArg(optCon)) 692 | errorx(1, "Invalid argument: \"%s\"", 693 | poptPeekArg(optCon)); 694 | 695 | ctx.force = force; 696 | ctx.quiet = quiet; 697 | 698 | uint8_t *orig_dbx_buffer = NULL; 699 | uint8_t *dbx_buffer = NULL; 700 | size_t dbx_len = 0; 701 | uint32_t attributes = 0; 702 | if (ctx.dbx_file != NULL) { 703 | vprintf("Loading dbx from \"%s\"\n", ctx.dbx_file); 704 | int fd = open(ctx.dbx_file, O_RDONLY); 705 | if (fd < 0) 706 | error(1, "Could not open file \"%s\"", ctx.dbx_file); 707 | 708 | rc = read_file(fd, (char **)&dbx_buffer, &dbx_len); 709 | if (rc < 0) 710 | error(1, "4 Could not read file \"%s\"", ctx.dbx_file); 711 | 712 | close(fd); 713 | 714 | filetype ft = guess_file_type(dbx_buffer, dbx_len); 715 | switch (ft) { 716 | case ft_unknown: 717 | error(1, "Unknown file type"); 718 | break; 719 | case ft_dbx: 720 | vprintf("dbx file type is dbx\n"); 721 | attributes = dbx_buffer[0]; 722 | uint8_t *tmp = malloc(dbx_len - 4); 723 | if (!tmp) 724 | error(1, "%m"); 725 | 726 | memmove(tmp, dbx_buffer + 4, dbx_len - 4); 727 | free(dbx_buffer); 728 | dbx_buffer = tmp; 729 | dbx_len -= 4; 730 | break; 731 | case ft_dbx_noattr: 732 | vprintf("dbx file type is dbx_noattr\n"); 733 | attributes = 734 | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | 735 | EFI_VARIABLE_RUNTIME_ACCESS | 736 | EFI_VARIABLE_BOOTSERVICE_ACCESS | 737 | EFI_VARIABLE_NON_VOLATILE; 738 | break; 739 | case ft_append_timestamp: { 740 | vprintf("dbx file type is append_timesrtamp\n"); 741 | EFI_VARIABLE_AUTHENTICATION_2 *va = 742 | (void *)dbx_buffer; 743 | orig_dbx_buffer = dbx_buffer; 744 | off_t offset = va->AuthInfo.Hdr.dwLength 745 | + sizeof (efi_guid_t); 746 | dbx_buffer = (void *)((intptr_t)va + offset); 747 | dbx_len -= offset; 748 | break; 749 | } 750 | default: 751 | errno = EINVAL; 752 | error(1, "Sorry, can't handle this yet"); 753 | break; 754 | } 755 | } else { 756 | if (!efi_variables_supported()) 757 | errorx(1, "EFI variables are not supported on this " 758 | "machine, and no dbx file was specified"); 759 | 760 | rc = efi_get_variable(efi_guid_security, "dbx", &dbx_buffer, 761 | &dbx_len, &attributes); 762 | if (rc < 0) { 763 | /* a missing dbx variable is okay as long as "apply" 764 | * is among the requested actions */ 765 | if (errno != ENOENT || !(action & ACTION_APPLY)) { 766 | error(1, "Could not get dbx variable"); 767 | } 768 | 769 | assert(dbx_buffer == NULL); 770 | assert(dbx_len == 0); 771 | attributes = 772 | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | 773 | EFI_VARIABLE_RUNTIME_ACCESS | 774 | EFI_VARIABLE_BOOTSERVICE_ACCESS | 775 | EFI_VARIABLE_NON_VOLATILE; 776 | } 777 | } 778 | 779 | struct db_update_file *updates = NULL; 780 | if ((action & ACTION_APPLY) && num_updates != 0) { 781 | void *dbxtree = NULL; 782 | 783 | check_pk_and_kek(ctx.force, ctx.quiet); 784 | 785 | rc = esl_tree_create(&dbxtree, dbx_buffer, dbx_len); 786 | if (rc < 0) 787 | error(1, NULL); 788 | 789 | if (num_updates == 1) { 790 | struct stat sb; 791 | rc = stat(apply_files[0], &sb); 792 | if (rc < 0) 793 | error(1, "Couldn't access \"%s\"", 794 | apply_files[0]); 795 | 796 | const char *dirname = apply_files[0]; 797 | if (S_ISDIR(sb.st_mode)) { 798 | int new_num_updates = 0; 799 | get_apply_files_from_dir(dirname, 800 | &updates, 801 | &new_num_updates); 802 | num_updates = new_num_updates; 803 | if (num_updates == 0) 804 | warnx("Updates directory \"%s\" contains no updates.", 805 | dirname); 806 | } 807 | } 808 | if (updates == NULL && num_updates > 0) { 809 | updates = calloc(num_updates, 810 | sizeof (struct db_update_file)); 811 | if (updates == NULL) 812 | error(1, "Couldn't allocate buffers"); 813 | for (int i = 0; 814 | apply_files != NULL && apply_files[i] != NULL; 815 | i++) { 816 | vprintf("Loading update file \"%s\"\n", 817 | apply_files[i]); 818 | load_update_file(&updates[i], 819 | apply_files[i], -1); 820 | } 821 | } 822 | 823 | vprintf("Attempting to apply %d updates\n", num_updates); 824 | sort_updates(updates, num_updates); 825 | 826 | int first_unapplied = -1; 827 | int applied = 0; 828 | for (int i = 0; i < num_updates; i++) { 829 | vprintf("Checking if \"%s\" has been applied.\n", 830 | updates[i].name); 831 | rc = is_update_applied(&updates[i], &dbxtree); 832 | if (rc < 0) 833 | error(1, NULL); 834 | if (rc == 0) { 835 | if (applied == 1) { 836 | fprintf(stderr, 837 | "Update set contains applied " 838 | "updates older than unapplied " 839 | "updates.\n"); 840 | fprintf(stderr, 841 | "There's nothing reasonable to " 842 | "do here, aborting.\n"); 843 | exit(1); 844 | } 845 | vprintf("Update \"%s\" is not applied\n", 846 | updates[i].name); 847 | if (first_unapplied == -1) 848 | first_unapplied = i; 849 | continue; 850 | } 851 | vprintf("Update \"%s\" is already applied\n", 852 | updates[i].name); 853 | applied = 1; 854 | } 855 | if (first_unapplied != -1) { 856 | printf("Applying %d updates\n", 857 | num_updates - first_unapplied); 858 | 859 | for (int i = first_unapplied; i < num_updates; i++) { 860 | printf("Applying \"%s\" ", updates[i].name); 861 | print_update_name(updates[i].base); 862 | printf("\n"); 863 | apply_update(&updates[i], attributes); 864 | } 865 | } 866 | 867 | esl_tree_destroy(&dbxtree); 868 | } 869 | 870 | int ret = 0; 871 | if (action == 0) { 872 | fprintf(stderr, "No action specified\n"); 873 | ret = 1; 874 | goto end; 875 | } 876 | 877 | if (action & ACTION_LIST) { 878 | dump_dbx(DUMP_PRINT, NULL, dbx_buffer, dbx_len); 879 | action &= ~ACTION_LIST; 880 | } 881 | if (action & ACTION_EXPORT) { 882 | dump_dbx(DUMP_EXPORT, prefix, dbx_buffer, dbx_len); 883 | action &= ~ACTION_EXPORT; 884 | } 885 | 886 | end: 887 | if (updates) { 888 | for (int i = 0; i < num_updates; i++) 889 | free(updates[i].base); 890 | free(updates); 891 | } 892 | free(orig_dbx_buffer ? orig_dbx_buffer : dbx_buffer); 893 | free(ctx.dbx_file); 894 | 895 | poptFreeContext(optCon); 896 | 897 | return ret; 898 | } 899 | --------------------------------------------------------------------------------