Sync with Fedora at same NVR

Resolves: #2084621
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
This commit is contained in:
Robbie Harwood 2022-10-27 15:12:09 +00:00
parent b853a6b348
commit 338e2944f5
9 changed files with 172 additions and 405 deletions

View File

@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Tue, 17 May 2022 11:23:28 -0400
Subject: [PATCH] Show usage instead of aborting on bad flags
Aborting here just confuses users and is sufficiently unexpected to
cause the filing of bugs.
Related: https://bugzilla.redhat.com/show_bug.cgi?id=2087066
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
(cherry picked from commit 82694cb1ce3b29c3705c25ae4cea3d07fe57b558)
---
src/mokutil.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/mokutil.c b/src/mokutil.c
index 5d725c9..e8228af 100644
--- a/src/mokutil.c
+++ b/src/mokutil.c
@@ -2087,10 +2087,9 @@ main (int argc, char *argv[])
goto out;
case 'h':
case '?':
+ default:
command |= HELP;
break;
- default:
- abort ();
}
}

View File

@ -1,199 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Renaud=20M=C3=A9trich?= <rmetrich@redhat.com>
Date: Fri, 3 Dec 2021 14:18:31 +0100
Subject: [PATCH] mokutil: enable setting fallback verbosity and noreboot mode
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Having mokutil handle FALLBACK_VERBOSE and FB_NO_REBOOT variables eases
fallback debugging.
Signed-off-by: Renaud Métrich <rmetrich@redhat.com>
(cherry picked from commit 57bc385827e7c0e0c86f30bbfa2d48ca9505537e)
---
src/mokutil.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
data/mokutil | 8 +++++++
man/mokutil.1 | 10 ++++++++
3 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/src/mokutil.c b/src/mokutil.c
index 787b85e..e1bd0e3 100644
--- a/src/mokutil.c
+++ b/src/mokutil.c
@@ -83,6 +83,8 @@
#define VERBOSITY (1 << 22)
#define TIMEOUT (1 << 23)
#define LIST_SBAT (1 << 24)
+#define FB_VERBOSITY (1 << 25)
+#define FB_NOREBOOT (1 << 26)
#define DEFAULT_CRYPT_METHOD SHA512_BASED
#define DEFAULT_SALT_SIZE SHA512_SALT_MAX
@@ -127,6 +129,8 @@ print_help ()
printf (" --import-hash <hash>\t\t\tImport a hash into MOK or MOKX\n");
printf (" --delete-hash <hash>\t\t\tDelete a hash in MOK or MOKX\n");
printf (" --set-verbosity <true/false>\t\tSet the verbosity bit for shim\n");
+ printf (" --set-fallback-verbosity <true/false>\t\tSet the verbosity bit for fallback\n");
+ printf (" --set-fallback-noreboot <true/false>\t\tPrevent fallback from automatically rebooting\n");
printf (" --pk\t\t\t\t\tList the keys in PK\n");
printf (" --kek\t\t\t\t\tList the keys in KEK\n");
printf (" --db\t\t\t\t\tList the keys in db\n");
@@ -1672,6 +1676,46 @@ set_verbosity (const uint8_t verbosity)
return 0;
}
+static int
+set_fallback_verbosity (const uint8_t verbosity)
+{
+ if (verbosity) {
+ uint32_t attributes = EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS;
+ if (efi_set_variable (efi_guid_shim, "FALLBACK_VERBOSE",
+ (uint8_t *)&verbosity, sizeof (verbosity),
+ attributes, S_IRUSR | S_IWUSR) < 0) {
+ fprintf (stderr, "Failed to set FALLBACK_VERBOSE\n");
+ return -1;
+ }
+ } else {
+ return test_and_delete_mok_var ("FALLBACK_VERBOSE");
+ }
+
+ return 0;
+}
+
+static int
+set_fallback_noreboot (const uint8_t noreboot)
+{
+ if (noreboot) {
+ uint32_t attributes = EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS;
+ if (efi_set_variable (efi_guid_shim, "FB_NO_REBOOT",
+ (uint8_t *)&noreboot, sizeof (noreboot),
+ attributes, S_IRUSR | S_IWUSR) < 0) {
+ fprintf (stderr, "Failed to set FB_NO_REBOOT\n");
+ return -1;
+ }
+ } else {
+ return test_and_delete_mok_var ("FB_NO_REBOOT");
+ }
+
+ return 0;
+}
+
static inline int
list_db (const DBName db_name)
{
@@ -1707,6 +1751,8 @@ main (int argc, char *argv[])
unsigned int command = 0;
int use_root_pw = 0;
uint8_t verbosity = 0;
+ uint8_t fb_verbosity = 0;
+ uint8_t fb_noreboot = 0;
DBName db_name = MOK_LIST_RT;
int ret = -1;
int sb_check;
@@ -1747,6 +1793,8 @@ main (int argc, char *argv[])
{"import-hash", required_argument, 0, 0 },
{"delete-hash", required_argument, 0, 0 },
{"set-verbosity", required_argument, 0, 0 },
+ {"set-fallback-verbosity", required_argument, 0, 0 },
+ {"set-fallback-noreboot", required_argument, 0, 0 },
{"pk", no_argument, 0, 0 },
{"kek", no_argument, 0, 0 },
{"db", no_argument, 0, 0 },
@@ -1815,6 +1863,22 @@ main (int argc, char *argv[])
verbosity = 0;
else
command |= HELP;
+ } else if (strcmp (option, "set-fallback-verbosity") == 0) {
+ command |= FB_VERBOSITY;
+ if (strcmp (optarg, "true") == 0)
+ fb_verbosity = 1;
+ else if (strcmp (optarg, "false") == 0)
+ fb_verbosity = 0;
+ else
+ command |= HELP;
+ } else if (strcmp (option, "set-fallback-noreboot") == 0) {
+ command |= FB_NOREBOOT;
+ if (strcmp (optarg, "true") == 0)
+ fb_noreboot = 1;
+ else if (strcmp (optarg, "false") == 0)
+ fb_noreboot = 0;
+ else
+ command |= HELP;
} else if (strcmp (option, "pk") == 0) {
if (db_name != MOK_LIST_RT) {
command |= HELP;
@@ -1978,7 +2042,8 @@ main (int argc, char *argv[])
command |= LIST_ENROLLED;
sb_check = !(command & HELP || command & TEST_KEY ||
- command & VERBOSITY || command & TIMEOUT);
+ command & VERBOSITY || command & TIMEOUT ||
+ command & FB_VERBOSITY || command & FB_NOREBOOT);
if (sb_check) {
/* Check whether the machine supports Secure Boot or not */
int rc;
@@ -2100,6 +2165,12 @@ main (int argc, char *argv[])
case VERBOSITY:
ret = set_verbosity (verbosity);
break;
+ case FB_VERBOSITY:
+ ret = set_fallback_verbosity (fb_verbosity);
+ break;
+ case FB_NOREBOOT:
+ ret = set_fallback_noreboot (fb_noreboot);
+ break;
case TIMEOUT:
ret = set_timeout (timeout);
break;
diff --git a/data/mokutil b/data/mokutil
index cf50606..b6ee859 100755
--- a/data/mokutil
+++ b/data/mokutil
@@ -24,6 +24,14 @@ _mokutil()
COMPREPLY=( $( compgen -W "true false") )
return 0
;;
+ --set-fallback-verbosity)
+ COMPREPLY=( $( compgen -W "true false") )
+ return 0
+ ;;
+ --set-fallback-noreboot)
+ COMPREPLY=( $( compgen -W "true false") )
+ return 0
+ ;;
--generate-hash|-g)
COMPREPLY=( $( compgen -o nospace -P= -W "") )
return 0
diff --git a/man/mokutil.1 b/man/mokutil.1
index 11804af..2ea081f 100644
--- a/man/mokutil.1
+++ b/man/mokutil.1
@@ -63,6 +63,10 @@ mokutil \- utility to manipulate machine owner keys
.br
\fBmokutil\fR [--set-verbosity (\fItrue\fR | \fIfalse\fR)]
.br
+\fBmokutil\fR [--set-fallback-verbosity (\fItrue\fR | \fIfalse\fR)]
+.br
+\fBmokutil\fR [--set-fallback-noreboot (\fItrue\fR | \fIfalse\fR)]
+.br
\fBmokutil\fR [--pk]
.br
\fBmokutil\fR [--kek]
@@ -158,6 +162,12 @@ this is not the password hash.
\fB--set-verbosity\fR
Set the SHIM_VERBOSE to make shim more or less verbose
.TP
+\fB--set-fallback-verbosity\fR
+Set the FALLBACK_VERBOSE to make fallback more or less verbose
+.TP
+\fB--set-fallback-noreboot\fR
+Set the FB_NO_REBOOT to prevent fallback from automatically rebooting the system
+.TP
\fB--pk\fR
List the keys in the public Platform Key (PK)
.TP

View File

@ -1,185 +0,0 @@
From 405b34cdd4b006f9d6496f201cb058eb8d3ac376 Mon Sep 17 00:00:00 2001
From: Jan Setje-Eilers <jan.setjeeilers@oracle.com>
Date: Thu, 21 Apr 2022 17:28:07 -0700
Subject: [PATCH] SBAT revocation update support
Control how shim will apply SBAT revocations:
mokutil --set-sbat-policy latest
applies the latest SBAT revocations
(default behavior)
mokutil --set-sbat-policy previous
applies previous SBAT revocations to
allow falling back to an older release
In both of the above cases shim will only apply SBAT revocations that
are newer than the ones currently installed.
mokutil --set-sbat-policy delete
resets SBAT revocations only if Secure
Boot is disabled. This setting does not
persist.
Signed-off-by: Jan Setje-Eilers <Jan.SetjeEilers@oracle.com>
---
src/mokutil.c | 47 +++++++++++++++++++++++++++++++++++++++++++----
man/mokutil.1 | 14 ++++++++++++--
2 files changed, 55 insertions(+), 6 deletions(-)
diff --git a/src/mokutil.c b/src/mokutil.c
index e1bd0e36e8a..4fb75cd9b3d 100644
--- a/src/mokutil.c
+++ b/src/mokutil.c
@@ -85,6 +85,7 @@
#define LIST_SBAT (1 << 24)
#define FB_VERBOSITY (1 << 25)
#define FB_NOREBOOT (1 << 26)
+#define SET_SBAT (1 << 27)
#define DEFAULT_CRYPT_METHOD SHA512_BASED
#define DEFAULT_SALT_SIZE SHA512_SALT_MAX
@@ -131,12 +132,13 @@ print_help ()
printf (" --set-verbosity <true/false>\t\tSet the verbosity bit for shim\n");
printf (" --set-fallback-verbosity <true/false>\t\tSet the verbosity bit for fallback\n");
printf (" --set-fallback-noreboot <true/false>\t\tPrevent fallback from automatically rebooting\n");
++ printf (" --set-sbat-policy <latest/previous/delete>\t\tApply Latest, Previous, or Blank SBAT revocations\n");
++ printf (" --list-sbat-revocations\t\t\t\tList the entries in SBAT\n");
printf (" --pk\t\t\t\t\tList the keys in PK\n");
printf (" --kek\t\t\t\t\tList the keys in KEK\n");
printf (" --db\t\t\t\t\tList the keys in db\n");
printf (" --dbx\t\t\t\t\tList the keys in dbx\n");
printf (" --timeout <-1,0..0x7fff>\t\tSet the timeout for MOK prompt\n");
- printf (" --sbat\t\t\t\tList the entries in SBAT\n");
printf ("\n");
printf ("Supplimentary Options:\n");
printf (" --hash-file <hash file>\t\tUse the specific password hash\n");
@@ -1737,6 +1739,26 @@ list_db (const DBName db_name)
return -1;
}
+static int
+manage_sbat (const uint8_t sbat_policy)
+{
+ if (sbat_policy) {
+ uint32_t attributes = EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS;
+ if (efi_set_variable (efi_guid_shim, "SbatPolicy",
+ (uint8_t *)&sbat_policy,
+ sizeof (sbat_policy),
+ attributes, S_IRUSR | S_IWUSR) < 0) {
+ fprintf (stderr, "Failed to set SbatPolicy\n");
+ return -1;
+ }
+ } else {
+ return test_and_delete_mok_var ("SbatPolicy");
+ }
+ return 0;
+}
+
int
main (int argc, char *argv[])
{
@@ -1753,6 +1775,7 @@ main (int argc, char *argv[])
uint8_t verbosity = 0;
uint8_t fb_verbosity = 0;
uint8_t fb_noreboot = 0;
+ uint8_t sbat_policy = 0;
DBName db_name = MOK_LIST_RT;
int ret = -1;
int sb_check;
@@ -1795,11 +1818,12 @@ main (int argc, char *argv[])
{"set-verbosity", required_argument, 0, 0 },
{"set-fallback-verbosity", required_argument, 0, 0 },
{"set-fallback-noreboot", required_argument, 0, 0 },
+ {"set-sbat-policy", required_argument, 0, 0 },
+ {"list-sbat-revocations", no_argument, 0, 0 },
{"pk", no_argument, 0, 0 },
{"kek", no_argument, 0, 0 },
{"db", no_argument, 0, 0 },
{"dbx", no_argument, 0, 0 },
- {"sbat", no_argument, 0, 0 },
{"timeout", required_argument, 0, 0 },
{"ca-check", no_argument, 0, 0 },
{"ignore-keyring", no_argument, 0, 0 },
@@ -1879,6 +1903,20 @@ main (int argc, char *argv[])
fb_noreboot = 0;
else
command |= HELP;
+ } else if (strcmp (option, "set-sbat-policy") == 0) {
+ command |= SET_SBAT;
+ if (strcmp (optarg, "latest") == 0)
+ sbat_policy = 1;
+ else if (strcmp (optarg, "previous") == 0)
+ sbat_policy = 2;
+ else if (strcmp (optarg, "delete") == 0)
+ sbat_policy = 3;
+ else
+ command |= HELP;
+ } else if (strcmp (option, "list-sbat-revocations") == 0) {
+ command |= LIST_SBAT;
+ } else if (strcmp (option, "sbat") == 0) {
+ command |= LIST_SBAT;
} else if (strcmp (option, "pk") == 0) {
if (db_name != MOK_LIST_RT) {
command |= HELP;
@@ -1903,8 +1941,6 @@ main (int argc, char *argv[])
} else {
db_name = DBX;
}
- } else if (strcmp (option, "sbat") == 0) {
- command |= LIST_SBAT;
} else if (strcmp (option, "timeout") == 0) {
command |= TIMEOUT;
timeout = strdup (optarg);
@@ -2177,6 +2213,9 @@ main (int argc, char *argv[])
case LIST_SBAT:
ret = print_var_content ("SbatLevelRT", efi_guid_shim);
break;
+ case SET_SBAT:
+ ret = manage_sbat(sbat_policy);
+ break;
default:
print_help ();
break;
diff --git a/man/mokutil.1 b/man/mokutil.1
index 2ea081fc932..4260a7e8467 100644
--- a/man/mokutil.1
+++ b/man/mokutil.1
@@ -75,7 +75,9 @@ mokutil \- utility to manipulate machine owner keys
.br
\fBmokutil\fR [--dbx]
.br
-\fBmokutil\fR [--sbat]
+\fBmokutil\fR [--list-sbat-revocations]
+.br
+\fBmokutil\fR [--set-sbat-policy (\fIlatest\fR | \fIprevious\fR | \fIdelete\fR)]
.br
\fBmokutil\fR [--timeout \fI-1,0..0x7fff\fR]
.br
@@ -180,9 +182,17 @@ List the keys in the secure boot signature store (db)
\fB--dbx\fR
List the keys in the secure boot blacklist signature store (dbx)
.TP
-\fB--sbat\fR
+\fB--list-sbat-revocations\fR
List the entries in the Secure Boot Advanced Targeting store (SBAT)
.TP
+\fB--set-sbat-policy (\fIlatest\fR | \fIprevious\fR | \fIdelete\fR)\fR
+Set the SbatPolicy UEFI Variable to have shim apply either the latest
+or the previous SBAT revocations. If UEFI Secure Boot is disabled, then
+delete will reset the SBAT revocations to an empty revocation list.
+While latest and previous are persistent configuration, delete will be
+cleared by shim on the next boot whether or not it succeeds. The default
+behavior is for shim to apply the previous revocations.
+.TP
\fB--timeout\fR
Set the timeout for MOK prompt
.TP
--
2.35.1

View File

@ -0,0 +1,26 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: gaoyusong <gaoyusong2@huawei.com>
Date: Mon, 30 May 2022 17:54:47 +0800
Subject: [PATCH] mokutil bugfix: del unused opt "-s"
The -s option can cause unexcepted result.
Signed-off-by: gaoyusong <gaoyusong2@huawei.com>
(cherry picked from commit 04791c29e198b18808bca519267e31c8d3786a08)
---
src/mokutil.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mokutil.c b/src/mokutil.c
index e8228af..6982ade 100644
--- a/src/mokutil.c
+++ b/src/mokutil.c
@@ -1851,7 +1851,7 @@ main (int argc, char *argv[])
};
int option_index = 0;
- c = getopt_long (argc, argv, "cd:f:g::hi:lmpst:xDNPXv",
+ c = getopt_long (argc, argv, "cd:f:g::hi:lmpt:xDNPXv",
long_options, &option_index);
if (c == -1)

View File

@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 2 Jun 2022 12:56:31 -0400
Subject: [PATCH] Fix leak of list in delete_data_from_req_var()
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
(cherry picked from commit d978c18f61b877afaab45a82d260b525423b8248)
---
src/util.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/util.c b/src/util.c
index 621869f..6cd0302 100644
--- a/src/util.c
+++ b/src/util.c
@@ -295,8 +295,10 @@ delete_data_from_req_var (const MokRequest req, const efi_guid_t *type,
}
/* the key or hash is not in this list */
- if (start == NULL)
- return 0;
+ if (start == NULL) {
+ ret = 0;
+ goto done;
+ }
/* all keys are removed */
if (total == 0) {

View File

@ -0,0 +1,70 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood@redhat.com>
Date: Thu, 2 Jun 2022 13:00:22 -0400
Subject: [PATCH] Fix leak of fd in mok_get_variable()
On success, it was never closed. Refactor the code to use a single
egress path so its closure is clear.
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
(cherry picked from commit e498f6460ff5aea6a7cd61a33087d03e88a2f52a)
---
src/util.c | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/src/util.c b/src/util.c
index 6cd0302..f7fc033 100644
--- a/src/util.c
+++ b/src/util.c
@@ -57,22 +57,21 @@ mok_get_variable(const char *name, uint8_t **datap, size_t *data_sizep)
return fd;
rc = fstat(fd, &sb);
- if (rc < 0) {
-err_close:
- close(fd);
- return rc;
- }
+ if (rc < 0)
+ goto done;
if (sb.st_size == 0) {
errno = ENOENT;
rc = -1;
- goto err_close;
+ goto done;
}
bufsz = sb.st_size;
buf = calloc(1, bufsz);
- if (!buf)
- goto err_close;
+ if (!buf) {
+ rc = -1;
+ goto done;
+ }
while (pos < bufsz) {
ssz = read(fd, &buf[pos], bufsz - pos);
@@ -82,15 +81,18 @@ err_close:
errno == EINTR)
continue;
free(buf);
- goto err_close;
+ rc = -1;
+ goto done;
}
pos += ssz;
}
*datap = buf;
*data_sizep = pos;
-
- return 0;
+ rc = 0;
+done:
+ close(fd);
+ return rc;
}
MokListNode*

View File

@ -1,2 +1,4 @@
Patch0001: 0001-mokutil-enable-setting-fallback-verbosity-and-norebo.patch
Patch0002: 0002-SBAT-revocation-update-support.patch
Patch0001: 0001-Show-usage-instead-of-aborting-on-bad-flags.patch
Patch0002: 0002-mokutil-bugfix-del-unused-opt-s.patch
Patch0003: 0003-Fix-leak-of-list-in-delete_data_from_req_var.patch
Patch0004: 0004-Fix-leak-of-fd-in-mok_get_variable.patch

View File

@ -1,13 +1,13 @@
Name: mokutil
Version: 0.5.0
Release: 3%{?dist}
Version: 0.6.0
Release: 4%{?dist}
Epoch: 2
Summary: Tool to manage UEFI Secure Boot MoK Keys
License: GPLv3+
URL: https://github.com/lcp/mokutil
Source0: https://github.com/lcp/mokutil/archive/%{version}.tar.gz
Source0: https://github.com/lcp/mokutil/archive/%{version}.tar.gz#/%{name}-%{version}.tar.gz
Source1: mokutil.patches
ExclusiveArch: %{ix86} x86_64 aarch64
ExclusiveArch: %{ix86} x86_64 aarch64 %{arm}
BuildRequires: autoconf
BuildRequires: automake
@ -29,27 +29,17 @@ mokutil provides a tool to manage keys for Secure Boot through the MoK
("Machine's Own Keys") mechanism.
%prep
%setup -q -n %{name}-%{version}
git init
git config user.email "%{name}-owner@fedoraproject.org"
git config user.name "Fedora Ninjas"
git add .
git commit -a -q -m "%{version} baseline."
git am %{patches} </dev/null
git config --unset user.email
git config --unset user.name
%autosetup -S git_am -b 0 -T
%build
./autogen.sh
%configure
make %{?_smp_mflags}
%{make_build}
%install
rm -rf %{buildroot}
make PREFIX=%{_prefix} LIBDIR=%{_libdir} DESTDIR=%{buildroot} install
%{make_install}
%files
%{!?_licensedir:%global license %%doc}
%license COPYING
%doc README
%{_bindir}/mokutil
@ -57,7 +47,11 @@ make PREFIX=%{_prefix} LIBDIR=%{_libdir} DESTDIR=%{buildroot} install
%{_datadir}/bash-completion/completions/mokutil
%changelog
* Wed Jun 01 2022 Peter Jones <pjones@redhat.com> - 0.4.0-9
* Thu Oct 27 2022 Robbie Harwood <rharwood@redhat.com> - 2:0.6.0-4
- Sync with Fedora at same NVR
- Resolves: #2084621
* Wed Jun 01 2022 Peter Jones <pjones@redhat.com> - 2:0.5.0-3
- Add support for "mokutil --list-sbat-revocations" and
"mokutil --set-sbat-policy"
Related: CVE-2022-28737

View File

@ -1 +1 @@
SHA512 (0.5.0.tar.gz) = 600c142fcc44e33efd307341b814018ef956668790b56d42a523140e81098746d14ae096fc6c93985b3c26bb414b8b6862f59312f2c4bd9d657a11e4becc6ea7
SHA512 (mokutil-0.6.0.tar.gz) = 11a9d172dba4fbb674e58e5d82cb1dc65a80cff844c0eaebd106b4d4608b24a8207e0cfabf36fe1eedb67f68a8a18db2136c7b62aa3230ac104615e8284dbd7d