Blame SOURCES/openssl-pkcs11-0.4.8-expose-check-fork.patch

61e0f4
From 45d6529dbe1b69f3a838d01a83f0688e91696377 Mon Sep 17 00:00:00 2001
61e0f4
From: =?UTF-8?q?Micha=C5=82=20Trojnara?= <Michal.Trojnara@stunnel.org>
61e0f4
Date: Wed, 29 Aug 2018 21:35:48 +0200
61e0f4
Subject: [PATCH 07/23] Expose check_fork internal API
61e0f4
61e0f4
---
61e0f4
 src/Makefile.am  |   2 +-
61e0f4
 src/atfork.c     |  93 -------------------
61e0f4
 src/libp11-int.h |   7 ++
61e0f4
 src/p11_atfork.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++
61e0f4
 src/p11_front.c  | 138 ----------------------------
61e0f4
 5 files changed, 239 insertions(+), 232 deletions(-)
61e0f4
 delete mode 100644 src/atfork.c
61e0f4
 create mode 100644 src/p11_atfork.c
61e0f4
61e0f4
diff --git a/src/Makefile.am b/src/Makefile.am
61e0f4
index 3cdbce1..2ca250e 100644
61e0f4
--- a/src/Makefile.am
61e0f4
+++ b/src/Makefile.am
61e0f4
@@ -14,7 +14,7 @@ SHARED_EXT=@SHARED_EXT@
61e0f4
 
61e0f4
 libp11_la_SOURCES = libpkcs11.c p11_attr.c p11_cert.c p11_err.c p11_ckr.c \
61e0f4
 	p11_key.c p11_load.c p11_misc.c p11_rsa.c p11_ec.c p11_pkey.c \
61e0f4
-	p11_slot.c p11_front.c atfork.c libp11.exports
61e0f4
+	p11_slot.c p11_front.c p11_atfork.c libp11.exports
61e0f4
 if WIN32
61e0f4
 libp11_la_SOURCES += libp11.rc
61e0f4
 else
61e0f4
diff --git a/src/atfork.c b/src/atfork.c
61e0f4
deleted file mode 100644
61e0f4
index 04691fb..0000000
61e0f4
--- a/src/atfork.c
61e0f4
+++ /dev/null
61e0f4
@@ -1,93 +0,0 @@
61e0f4
-/*
61e0f4
- * Copyright (C) 2010-2012 Free Software Foundation, Inc.
61e0f4
- * Copyright (C) 2014 Red Hat
61e0f4
- *
61e0f4
- * Author: Nikos Mavrogiannopoulos
61e0f4
- *
61e0f4
- * This is free software; you can redistribute it and/or
61e0f4
- * modify it under the terms of the GNU Lesser General Public License
61e0f4
- * as published by the Free Software Foundation; either version 2.1 of
61e0f4
- * the License, or (at your option) any later version.
61e0f4
- *
61e0f4
- * This library is distributed in the hope that it will be useful, but
61e0f4
- * WITHOUT ANY WARRANTY; without even the implied warranty of
61e0f4
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
61e0f4
- * Lesser General Public License for more details.
61e0f4
- *
61e0f4
- * You should have received a copy of the GNU Lesser General Public License
61e0f4
- * along with this program.  If not, see <http://www.gnu.org/licenses/>
61e0f4
- *
61e0f4
- */
61e0f4
-
61e0f4
-#include "libp11-int.h"
61e0f4
-#if defined(_WIN32) && !defined(__CYGWIN__)
61e0f4
-#include <winsock2.h>
61e0f4
-#else
61e0f4
-#include <sys/socket.h>
61e0f4
-#endif
61e0f4
-#include <errno.h>
61e0f4
-#include <sys/stat.h>
61e0f4
-#include <sys/types.h>
61e0f4
-#include <unistd.h>
61e0f4
-#include <atfork.h>
61e0f4
-
61e0f4
-#ifdef __sun
61e0f4
-# pragma fini(lib_deinit)
61e0f4
-# pragma init(lib_init)
61e0f4
-# define _CONSTRUCTOR
61e0f4
-# define _DESTRUCTOR
61e0f4
-#else
61e0f4
-# define _CONSTRUCTOR __attribute__((constructor))
61e0f4
-# define _DESTRUCTOR __attribute__((destructor))
61e0f4
-#endif
61e0f4
-
61e0f4
-unsigned int P11_forkid = 0;
61e0f4
-
61e0f4
-#ifndef _WIN32
61e0f4
-
61e0f4
-# ifdef HAVE_ATFORK
61e0f4
-static void fork_handler(void)
61e0f4
-{
61e0f4
-	P11_forkid++;
61e0f4
-}
61e0f4
-# endif
61e0f4
-
61e0f4
-# if defined(HAVE___REGISTER_ATFORK)
61e0f4
-extern int __register_atfork(void (*)(void), void(*)(void), void (*)(void), void *);
61e0f4
-extern void *__dso_handle;
61e0f4
-
61e0f4
-_CONSTRUCTOR
61e0f4
-int _P11_register_fork_handler(void)
61e0f4
-{
61e0f4
-	if (__register_atfork(0, 0, fork_handler, __dso_handle) != 0)
61e0f4
-		return -1;
61e0f4
-	return 0;
61e0f4
-}
61e0f4
-
61e0f4
-# else
61e0f4
-
61e0f4
-unsigned int _P11_get_forkid(void)
61e0f4
-{
61e0f4
-	return getpid();
61e0f4
-}
61e0f4
-
61e0f4
-int _P11_detect_fork(unsigned int forkid)
61e0f4
-{
61e0f4
-	if (getpid() == forkid)
61e0f4
-		return 0;
61e0f4
-	return 1;
61e0f4
-}
61e0f4
-
61e0f4
-/* we have to detect fork manually */
61e0f4
-_CONSTRUCTOR
61e0f4
-int _P11_register_fork_handler(void)
61e0f4
-{
61e0f4
-	P11_forkid = getpid();
61e0f4
-	return 0;
61e0f4
-}
61e0f4
-
61e0f4
-# endif
61e0f4
-
61e0f4
-#endif /* !_WIN32 */
61e0f4
-
61e0f4
-/* vim: set noexpandtab: */
61e0f4
diff --git a/src/libp11-int.h b/src/libp11-int.h
61e0f4
index b62a13e..411f2b0 100644
61e0f4
--- a/src/libp11-int.h
61e0f4
+++ b/src/libp11-int.h
61e0f4
@@ -323,6 +323,13 @@ extern int pkcs11_store_certificate(PKCS11_TOKEN * token, X509 * x509,
61e0f4
 extern int pkcs11_seed_random(PKCS11_SLOT *, const unsigned char *s, unsigned int s_len);
61e0f4
 extern int pkcs11_generate_random(PKCS11_SLOT *, unsigned char *r, unsigned int r_len);
61e0f4
 
61e0f4
+/* Reinitialize the module afer fork if needed */
61e0f4
+extern int check_fork(PKCS11_CTX *ctx);
61e0f4
+extern int check_slot_fork(PKCS11_SLOT *slot);
61e0f4
+extern int check_token_fork(PKCS11_TOKEN *token);
61e0f4
+extern int check_key_fork(PKCS11_KEY *key);
61e0f4
+extern int check_cert_fork(PKCS11_CERT *cert);
61e0f4
+
61e0f4
 /* Internal implementation of deprecated features */
61e0f4
 
61e0f4
 /* Generate and store a private key on the token */
61e0f4
diff --git a/src/p11_atfork.c b/src/p11_atfork.c
61e0f4
new file mode 100644
61e0f4
index 0000000..fce87c6
61e0f4
--- /dev/null
61e0f4
+++ b/src/p11_atfork.c
61e0f4
@@ -0,0 +1,231 @@
61e0f4
+/*
61e0f4
+ * Copyright (C) 2010-2012 Free Software Foundation, Inc.
61e0f4
+ * Copyright (C) 2014 Red Hat
61e0f4
+ *
61e0f4
+ * Author: Nikos Mavrogiannopoulos
61e0f4
+ *
61e0f4
+ * This is free software; you can redistribute it and/or
61e0f4
+ * modify it under the terms of the GNU Lesser General Public License
61e0f4
+ * as published by the Free Software Foundation; either version 2.1 of
61e0f4
+ * the License, or (at your option) any later version.
61e0f4
+ *
61e0f4
+ * This library is distributed in the hope that it will be useful, but
61e0f4
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
61e0f4
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
61e0f4
+ * Lesser General Public License for more details.
61e0f4
+ *
61e0f4
+ * You should have received a copy of the GNU Lesser General Public License
61e0f4
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
61e0f4
+ *
61e0f4
+ */
61e0f4
+
61e0f4
+#include "libp11-int.h"
61e0f4
+#if defined(_WIN32) && !defined(__CYGWIN__)
61e0f4
+#include <winsock2.h>
61e0f4
+#else
61e0f4
+#include <sys/socket.h>
61e0f4
+#endif
61e0f4
+#include <errno.h>
61e0f4
+#include <sys/stat.h>
61e0f4
+#include <sys/types.h>
61e0f4
+#include <unistd.h>
61e0f4
+#include <atfork.h>
61e0f4
+
61e0f4
+#ifdef __sun
61e0f4
+# pragma fini(lib_deinit)
61e0f4
+# pragma init(lib_init)
61e0f4
+# define _CONSTRUCTOR
61e0f4
+# define _DESTRUCTOR
61e0f4
+#else
61e0f4
+# define _CONSTRUCTOR __attribute__((constructor))
61e0f4
+# define _DESTRUCTOR __attribute__((destructor))
61e0f4
+#endif
61e0f4
+
61e0f4
+unsigned int P11_forkid = 0;
61e0f4
+
61e0f4
+#ifndef _WIN32
61e0f4
+
61e0f4
+# ifdef HAVE_ATFORK
61e0f4
+static void fork_handler(void)
61e0f4
+{
61e0f4
+	P11_forkid++;
61e0f4
+}
61e0f4
+# endif
61e0f4
+
61e0f4
+# if defined(HAVE___REGISTER_ATFORK)
61e0f4
+extern int __register_atfork(void (*)(void), void(*)(void), void (*)(void), void *);
61e0f4
+extern void *__dso_handle;
61e0f4
+
61e0f4
+_CONSTRUCTOR
61e0f4
+int _P11_register_fork_handler(void)
61e0f4
+{
61e0f4
+	if (__register_atfork(0, 0, fork_handler, __dso_handle) != 0)
61e0f4
+		return -1;
61e0f4
+	return 0;
61e0f4
+}
61e0f4
+
61e0f4
+# else
61e0f4
+
61e0f4
+unsigned int _P11_get_forkid(void)
61e0f4
+{
61e0f4
+	return getpid();
61e0f4
+}
61e0f4
+
61e0f4
+int _P11_detect_fork(unsigned int forkid)
61e0f4
+{
61e0f4
+	if (getpid() == forkid)
61e0f4
+		return 0;
61e0f4
+	return 1;
61e0f4
+}
61e0f4
+
61e0f4
+/* we have to detect fork manually */
61e0f4
+_CONSTRUCTOR
61e0f4
+int _P11_register_fork_handler(void)
61e0f4
+{
61e0f4
+	P11_forkid = getpid();
61e0f4
+	return 0;
61e0f4
+}
61e0f4
+
61e0f4
+# endif
61e0f4
+
61e0f4
+#endif /* !_WIN32 */
61e0f4
+
61e0f4
+/*
61e0f4
+ * PKCS#11 reinitialization after fork
61e0f4
+ * It wipes out the internal state of the PKCS#11 library
61e0f4
+ * Any libp11 references to this state are no longer valid
61e0f4
+ */
61e0f4
+static int check_fork_int(PKCS11_CTX *ctx)
61e0f4
+{
61e0f4
+	PKCS11_CTX_private *cpriv = PRIVCTX(ctx);
61e0f4
+
61e0f4
+	if (_P11_detect_fork(cpriv->forkid)) {
61e0f4
+		if (pkcs11_CTX_reload(ctx) < 0)
61e0f4
+			return -1;
61e0f4
+		cpriv->forkid = _P11_get_forkid();
61e0f4
+	}
61e0f4
+	return 0;
61e0f4
+}
61e0f4
+
61e0f4
+/*
61e0f4
+ * PKCS#11 reinitialization after fork
61e0f4
+ * Also relogins and reopens the session if needed
61e0f4
+ */
61e0f4
+static int check_slot_fork_int(PKCS11_SLOT *slot)
61e0f4
+{
61e0f4
+	PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
61e0f4
+	PKCS11_CTX *ctx = SLOT2CTX(slot);
61e0f4
+	PKCS11_CTX_private *cpriv = PRIVCTX(ctx);
61e0f4
+
61e0f4
+	if (check_fork_int(SLOT2CTX(slot)) < 0)
61e0f4
+		return -1;
61e0f4
+	if (spriv->forkid != cpriv->forkid) {
61e0f4
+		if (spriv->loggedIn) {
61e0f4
+			int saved = spriv->haveSession;
61e0f4
+			spriv->haveSession = 0;
61e0f4
+			spriv->loggedIn = 0;
61e0f4
+			if (pkcs11_relogin(slot) < 0)
61e0f4
+				return -1;
61e0f4
+			spriv->haveSession = saved;
61e0f4
+		}
61e0f4
+		if (spriv->haveSession) {
61e0f4
+			spriv->haveSession = 0;
61e0f4
+			if (pkcs11_reopen_session(slot) < 0)
61e0f4
+				return -1;
61e0f4
+		}
61e0f4
+		spriv->forkid = cpriv->forkid;
61e0f4
+	}
61e0f4
+	return 0;
61e0f4
+}
61e0f4
+
61e0f4
+/*
61e0f4
+ * PKCS#11 reinitialization after fork
61e0f4
+ * Also reloads the key
61e0f4
+ */
61e0f4
+static int check_key_fork_int(PKCS11_KEY *key)
61e0f4
+{
61e0f4
+	PKCS11_SLOT *slot = KEY2SLOT(key);
61e0f4
+	PKCS11_KEY_private *kpriv = PRIVKEY(key);
61e0f4
+	PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
61e0f4
+
61e0f4
+	if (check_slot_fork_int(slot) < 0)
61e0f4
+		return -1;
61e0f4
+	if (spriv->forkid != kpriv->forkid) {
61e0f4
+		pkcs11_reload_key(key);
61e0f4
+		kpriv->forkid = spriv->forkid;
61e0f4
+	}
61e0f4
+	return 0;
61e0f4
+}
61e0f4
+
61e0f4
+/*
61e0f4
+ * Locking interface to check_fork_int()
61e0f4
+ */
61e0f4
+int check_fork(PKCS11_CTX *ctx)
61e0f4
+{
61e0f4
+	PKCS11_CTX_private *cpriv;
61e0f4
+	int rv;
61e0f4
+
61e0f4
+	if (ctx == NULL)
61e0f4
+		return -1;
61e0f4
+	cpriv = PRIVCTX(ctx);
61e0f4
+	CRYPTO_THREAD_write_lock(cpriv->rwlock);
61e0f4
+	rv = check_fork_int(ctx);
61e0f4
+	CRYPTO_THREAD_unlock(cpriv->rwlock);
61e0f4
+	return rv;
61e0f4
+}
61e0f4
+
61e0f4
+/*
61e0f4
+ * Locking interface to check_slot_fork_int()
61e0f4
+ */
61e0f4
+int check_slot_fork(PKCS11_SLOT *slot)
61e0f4
+{
61e0f4
+	PKCS11_CTX_private *cpriv;
61e0f4
+	int rv;
61e0f4
+
61e0f4
+	if (slot == NULL)
61e0f4
+		return -1;
61e0f4
+	cpriv = PRIVCTX(SLOT2CTX(slot));
61e0f4
+	CRYPTO_THREAD_write_lock(cpriv->rwlock);
61e0f4
+	rv = check_slot_fork_int(slot);
61e0f4
+	CRYPTO_THREAD_unlock(cpriv->rwlock);
61e0f4
+	return rv;
61e0f4
+}
61e0f4
+
61e0f4
+/*
61e0f4
+ * Reinitialize token (just its slot)
61e0f4
+ */
61e0f4
+int check_token_fork(PKCS11_TOKEN *token)
61e0f4
+{
61e0f4
+	if (token == NULL)
61e0f4
+		return -1;
61e0f4
+	return check_slot_fork(TOKEN2SLOT(token));
61e0f4
+}
61e0f4
+
61e0f4
+/*
61e0f4
+ * Locking interface to check_key_fork_int()
61e0f4
+ */
61e0f4
+int check_key_fork(PKCS11_KEY *key)
61e0f4
+{
61e0f4
+	PKCS11_CTX_private *cpriv;
61e0f4
+	int rv;
61e0f4
+
61e0f4
+	if (key == NULL)
61e0f4
+		return -1;
61e0f4
+	cpriv = PRIVCTX(KEY2CTX(key));
61e0f4
+	CRYPTO_THREAD_write_lock(cpriv->rwlock);
61e0f4
+	rv = check_key_fork_int(key);
61e0f4
+	CRYPTO_THREAD_unlock(cpriv->rwlock);
61e0f4
+	return rv;
61e0f4
+}
61e0f4
+
61e0f4
+/*
61e0f4
+ * Reinitialize cert (just its token)
61e0f4
+ */
61e0f4
+int check_cert_fork(PKCS11_CERT *cert)
61e0f4
+{
61e0f4
+	if (cert == NULL)
61e0f4
+		return -1;
61e0f4
+	return check_token_fork(CERT2TOKEN(cert));
61e0f4
+}
61e0f4
+
61e0f4
+/* vim: set noexpandtab: */
61e0f4
diff --git a/src/p11_front.c b/src/p11_front.c
61e0f4
index 167a778..efdd4c0 100644
61e0f4
--- a/src/p11_front.c
61e0f4
+++ b/src/p11_front.c
61e0f4
@@ -25,144 +25,6 @@
61e0f4
  * PKCS11_get_ec_key_method
61e0f4
  */
61e0f4
 
61e0f4
-/*
61e0f4
- * PKCS#11 reinitialization after fork
61e0f4
- * It wipes out the internal state of the PKCS#11 library
61e0f4
- * Any libp11 references to this state are no longer valid
61e0f4
- */
61e0f4
-static int check_fork_int(PKCS11_CTX *ctx)
61e0f4
-{
61e0f4
-	PKCS11_CTX_private *cpriv = PRIVCTX(ctx);
61e0f4
-
61e0f4
-	if (_P11_detect_fork(cpriv->forkid)) {
61e0f4
-		if (pkcs11_CTX_reload(ctx) < 0)
61e0f4
-			return -1;
61e0f4
-		cpriv->forkid = _P11_get_forkid();
61e0f4
-	}
61e0f4
-	return 0;
61e0f4
-}
61e0f4
-
61e0f4
-/*
61e0f4
- * PKCS#11 reinitialization after fork
61e0f4
- * Also relogins and reopens the session if needed
61e0f4
- */
61e0f4
-static int check_slot_fork_int(PKCS11_SLOT *slot)
61e0f4
-{
61e0f4
-	PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
61e0f4
-	PKCS11_CTX *ctx = SLOT2CTX(slot);
61e0f4
-	PKCS11_CTX_private *cpriv = PRIVCTX(ctx);
61e0f4
-
61e0f4
-	if (check_fork_int(SLOT2CTX(slot)) < 0)
61e0f4
-		return -1;
61e0f4
-	if (spriv->forkid != cpriv->forkid) {
61e0f4
-		if (spriv->loggedIn) {
61e0f4
-			int saved = spriv->haveSession;
61e0f4
-			spriv->haveSession = 0;
61e0f4
-			spriv->loggedIn = 0;
61e0f4
-			if (pkcs11_relogin(slot) < 0)
61e0f4
-				return -1;
61e0f4
-			spriv->haveSession = saved;
61e0f4
-		}
61e0f4
-		if (spriv->haveSession) {
61e0f4
-			spriv->haveSession = 0;
61e0f4
-			if (pkcs11_reopen_session(slot) < 0)
61e0f4
-				return -1;
61e0f4
-		}
61e0f4
-		spriv->forkid = cpriv->forkid;
61e0f4
-	}
61e0f4
-	return 0;
61e0f4
-}
61e0f4
-
61e0f4
-/*
61e0f4
- * PKCS#11 reinitialization after fork
61e0f4
- * Also reloads the key
61e0f4
- */
61e0f4
-static int check_key_fork_int(PKCS11_KEY *key)
61e0f4
-{
61e0f4
-	PKCS11_SLOT *slot = KEY2SLOT(key);
61e0f4
-	PKCS11_KEY_private *kpriv = PRIVKEY(key);
61e0f4
-	PKCS11_SLOT_private *spriv = PRIVSLOT(slot);
61e0f4
-
61e0f4
-	if (check_slot_fork_int(slot) < 0)
61e0f4
-		return -1;
61e0f4
-	if (spriv->forkid != kpriv->forkid) {
61e0f4
-		pkcs11_reload_key(key);
61e0f4
-		kpriv->forkid = spriv->forkid;
61e0f4
-	}
61e0f4
-	return 0;
61e0f4
-}
61e0f4
-
61e0f4
-/*
61e0f4
- * Locking interface to check_fork_int()
61e0f4
- */
61e0f4
-static int check_fork(PKCS11_CTX *ctx)
61e0f4
-{
61e0f4
-	PKCS11_CTX_private *cpriv;
61e0f4
-	int rv;
61e0f4
-
61e0f4
-	if (ctx == NULL)
61e0f4
-		return -1;
61e0f4
-	cpriv = PRIVCTX(ctx);
61e0f4
-	CRYPTO_THREAD_write_lock(cpriv->rwlock);
61e0f4
-	rv = check_fork_int(ctx);
61e0f4
-	CRYPTO_THREAD_unlock(cpriv->rwlock);
61e0f4
-	return rv;
61e0f4
-}
61e0f4
-
61e0f4
-/*
61e0f4
- * Locking interface to check_slot_fork_int()
61e0f4
- */
61e0f4
-static int check_slot_fork(PKCS11_SLOT *slot)
61e0f4
-{
61e0f4
-	PKCS11_CTX_private *cpriv;
61e0f4
-	int rv;
61e0f4
-
61e0f4
-	if (slot == NULL)
61e0f4
-		return -1;
61e0f4
-	cpriv = PRIVCTX(SLOT2CTX(slot));
61e0f4
-	CRYPTO_THREAD_write_lock(cpriv->rwlock);
61e0f4
-	rv = check_slot_fork_int(slot);
61e0f4
-	CRYPTO_THREAD_unlock(cpriv->rwlock);
61e0f4
-	return rv;
61e0f4
-}
61e0f4
-
61e0f4
-/*
61e0f4
- * Reinitialize token (just its slot)
61e0f4
- */
61e0f4
-static int check_token_fork(PKCS11_TOKEN *token)
61e0f4
-{
61e0f4
-	if (token == NULL)
61e0f4
-		return -1;
61e0f4
-	return check_slot_fork(TOKEN2SLOT(token));
61e0f4
-}
61e0f4
-
61e0f4
-/*
61e0f4
- * Locking interface to check_key_fork_int()
61e0f4
- */
61e0f4
-static int check_key_fork(PKCS11_KEY *key)
61e0f4
-{
61e0f4
-	PKCS11_CTX_private *cpriv;
61e0f4
-	int rv;
61e0f4
-
61e0f4
-	if (key == NULL)
61e0f4
-		return -1;
61e0f4
-	cpriv = PRIVCTX(KEY2CTX(key));
61e0f4
-	CRYPTO_THREAD_write_lock(cpriv->rwlock);
61e0f4
-	rv = check_key_fork_int(key);
61e0f4
-	CRYPTO_THREAD_unlock(cpriv->rwlock);
61e0f4
-	return rv;
61e0f4
-}
61e0f4
-
61e0f4
-/*
61e0f4
- * Reinitialize cert (just its token)
61e0f4
- */
61e0f4
-static int check_cert_fork(PKCS11_CERT *cert)
61e0f4
-{
61e0f4
-	if (cert == NULL)
61e0f4
-		return -1;
61e0f4
-	return check_token_fork(CERT2TOKEN(cert));
61e0f4
-}
61e0f4
-
61e0f4
 /* External interface to the libp11 features */
61e0f4
 
61e0f4
 PKCS11_CTX *PKCS11_CTX_new(void)
61e0f4
-- 
61e0f4
2.17.1
61e0f4