diff --git a/apache2-load-private-keys-from-pkcs11.patch b/apache2-load-private-keys-from-pkcs11.patch deleted file mode 100644 index ca23c40..0000000 --- a/apache2-load-private-keys-from-pkcs11.patch +++ /dev/null @@ -1,697 +0,0 @@ -Index: httpd-2.4.41/modules/ssl/ssl_engine_config.c -=================================================================== ---- httpd-2.4.41.orig/modules/ssl/ssl_engine_config.c 2018-10-18 12:06:37.000000000 +0200 -+++ httpd-2.4.41/modules/ssl/ssl_engine_config.c 2020-02-26 11:10:32.727187775 +0100 -@@ -916,7 +916,9 @@ const char *ssl_cmd_SSLCertificateFile(c - SSLSrvConfigRec *sc = mySrvConfig(cmd->server); - const char *err; - -- if ((err = ssl_cmd_check_file(cmd, &arg))) { -+ /* Only check for non-ENGINE based certs. */ -+ if (!modssl_is_engine_id(arg) -+ && (err = ssl_cmd_check_file(cmd, &arg))) { - return err; - } - -@@ -932,7 +934,9 @@ const char *ssl_cmd_SSLCertificateKeyFil - SSLSrvConfigRec *sc = mySrvConfig(cmd->server); - const char *err; - -- if ((err = ssl_cmd_check_file(cmd, &arg))) { -+ /* Check keyfile exists for non-ENGINE keys. */ -+ if (!modssl_is_engine_id(arg) -+ && (err = ssl_cmd_check_file(cmd, &arg))) { - return err; - } - -Index: httpd-2.4.41/modules/ssl/ssl_engine_init.c -=================================================================== ---- httpd-2.4.41.orig/modules/ssl/ssl_engine_init.c 2019-08-06 14:16:14.000000000 +0200 -+++ httpd-2.4.41/modules/ssl/ssl_engine_init.c 2020-02-26 11:10:32.727187775 +0100 -@@ -1247,12 +1247,18 @@ static apr_status_t ssl_init_server_cert - (certfile = APR_ARRAY_IDX(mctx->pks->cert_files, i, - const char *)); - i++) { -+ EVP_PKEY *pkey; -+ const char *engine_certfile = NULL; -+ - key_id = apr_psprintf(ptemp, "%s:%d", vhost_id, i); - - ERR_clear_error(); - - /* first the certificate (public key) */ -- if (mctx->cert_chain) { -+ if (modssl_is_engine_id(certfile)) { -+ engine_certfile = certfile; -+ } -+ else if (mctx->cert_chain) { - if ((SSL_CTX_use_certificate_file(mctx->ssl_ctx, certfile, - SSL_FILETYPE_PEM) < 1)) { - ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(02561) -@@ -1281,12 +1287,46 @@ static apr_status_t ssl_init_server_cert - - ERR_clear_error(); - -- if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile, -- SSL_FILETYPE_PEM) < 1) && -- (ERR_GET_FUNC(ERR_peek_last_error()) -- != X509_F_X509_CHECK_PRIVATE_KEY)) { -+ if (modssl_is_engine_id(keyfile)) { -+ apr_status_t rv; -+ -+ cert = NULL; -+ -+ if ((rv = modssl_load_engine_keypair(s, ptemp, vhost_id, -+ engine_certfile, keyfile, -+ &cert, &pkey))) { -+ return rv; -+ } -+ -+ if (cert) { -+ if (SSL_CTX_use_certificate(mctx->ssl_ctx, cert) < 1) { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10137) -+ "Failed to configure engine certificate %s, check %s", -+ key_id, certfile); -+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); -+ return APR_EGENERAL; -+ } -+ -+ /* SSL_CTX now owns the cert. */ -+ X509_free(cert); -+ } -+ -+ if (SSL_CTX_use_PrivateKey(mctx->ssl_ctx, pkey) < 1) { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10130) -+ "Failed to configure private key %s from engine", -+ keyfile); -+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); -+ return APR_EGENERAL; -+ } -+ -+ /* SSL_CTX now owns the key */ -+ EVP_PKEY_free(pkey); -+ } -+ else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile, -+ SSL_FILETYPE_PEM) < 1) -+ && (ERR_GET_FUNC(ERR_peek_last_error()) -+ != X509_F_X509_CHECK_PRIVATE_KEY)) { - ssl_asn1_t *asn1; -- EVP_PKEY *pkey; - const unsigned char *ptr; - - ERR_clear_error(); -@@ -1372,8 +1412,9 @@ static apr_status_t ssl_init_server_cert - /* - * Try to read DH parameters from the (first) SSLCertificateFile - */ -- if ((certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *)) && -- (dhparams = ssl_dh_GetParamFromFile(certfile))) { -+ certfile = APR_ARRAY_IDX(mctx->pks->cert_files, 0, const char *); -+ if (certfile && !modssl_is_engine_id(certfile) -+ && (dhparams = ssl_dh_GetParamFromFile(certfile))) { - SSL_CTX_set_tmp_dh(mctx->ssl_ctx, dhparams); - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02540) - "Custom DH parameters (%d bits) for %s loaded from %s", -@@ -1385,10 +1426,10 @@ static apr_status_t ssl_init_server_cert - /* - * Similarly, try to read the ECDH curve name from SSLCertificateFile... - */ -- if ((certfile != NULL) && -- (ecparams = ssl_ec_GetParamFromFile(certfile)) && -- (nid = EC_GROUP_get_curve_name(ecparams)) && -- (eckey = EC_KEY_new_by_curve_name(nid))) { -+ if (certfile && !modssl_is_engine_id(certfile) -+ && (ecparams = ssl_ec_GetParamFromFile(certfile)) -+ && (nid = EC_GROUP_get_curve_name(ecparams)) -+ && (eckey = EC_KEY_new_by_curve_name(nid))) { - SSL_CTX_set_tmp_ecdh(mctx->ssl_ctx, eckey); - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(02541) - "ECDH curve %s for %s specified in %s", -Index: httpd-2.4.41/modules/ssl/ssl_engine_pphrase.c -=================================================================== ---- httpd-2.4.41.orig/modules/ssl/ssl_engine_pphrase.c 2018-12-11 15:14:40.000000000 +0100 -+++ httpd-2.4.41/modules/ssl/ssl_engine_pphrase.c 2020-02-26 11:10:32.727187775 +0100 -@@ -143,8 +143,6 @@ apr_status_t ssl_load_encrypted_pkey(ser - const char *key_id = asn1_table_vhost_key(mc, p, sc->vhost_id, idx); - EVP_PKEY *pPrivateKey = NULL; - ssl_asn1_t *asn1; -- unsigned char *ucp; -- long int length; - int nPassPhrase = (*pphrases)->nelts; - int nPassPhraseRetry = 0; - apr_time_t pkey_mtime = 0; -@@ -221,7 +219,7 @@ apr_status_t ssl_load_encrypted_pkey(ser - * is not empty. */ - ERR_clear_error(); - -- pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file, NULL, -+ pPrivateKey = modssl_read_privatekey(ppcb_arg.pkey_file, - ssl_pphrase_Handle_CB, &ppcb_arg); - /* If the private key was successfully read, nothing more to - do here. */ -@@ -351,19 +349,12 @@ apr_status_t ssl_load_encrypted_pkey(ser - nPassPhrase++; - } - -- /* -- * Insert private key into the global module configuration -- * (we convert it to a stand-alone DER byte sequence -- * because the SSL library uses static variables inside a -- * RSA structure which do not survive DSO reloads!) -- */ -- length = i2d_PrivateKey(pPrivateKey, NULL); -- ucp = ssl_asn1_table_set(mc->tPrivateKey, key_id, length); -- (void)i2d_PrivateKey(pPrivateKey, &ucp); /* 2nd arg increments */ -+ /* Cache the private key in the global module configuration so it -+ * can be used after subsequent reloads. */ -+ asn1 = ssl_asn1_table_set(mc->tPrivateKey, key_id, pPrivateKey); - - if (ppcb_arg.nPassPhraseDialogCur != 0) { - /* remember mtime of encrypted keys */ -- asn1 = ssl_asn1_table_get(mc->tPrivateKey, key_id); - asn1->source_mtime = pkey_mtime; - } - -@@ -614,3 +605,307 @@ int ssl_pphrase_Handle_CB(char *buf, int - */ - return (len); - } -+ -+ -+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) -+ -+/* OpenSSL UI implementation for passphrase entry; largely duplicated -+ * from ssl_pphrase_Handle_CB but adjusted for UI API. TODO: Might be -+ * worth trying to shift pphrase handling over to the UI API -+ * completely. */ -+static int passphrase_ui_open(UI *ui) -+{ -+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui); -+ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s); -+ -+ ppcb->nPassPhraseDialog++; -+ ppcb->nPassPhraseDialogCur++; -+ -+ /* -+ * Builtin or Pipe dialog -+ */ -+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN -+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { -+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { -+ if (!readtty) { -+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, -+ APLOGNO(10143) -+ "Init: Creating pass phrase dialog pipe child " -+ "'%s'", sc->server->pphrase_dialog_path); -+ if (ssl_pipe_child_create(ppcb->p, -+ sc->server->pphrase_dialog_path) -+ != APR_SUCCESS) { -+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, -+ APLOGNO(10144) -+ "Init: Failed to create pass phrase pipe '%s'", -+ sc->server->pphrase_dialog_path); -+ return 0; -+ } -+ } -+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10145) -+ "Init: Requesting pass phrase via piped dialog"); -+ } -+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */ -+#ifdef WIN32 -+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, ppcb->s, APLOGNO(10146) -+ "Init: Failed to create pass phrase pipe '%s'", -+ sc->server->pphrase_dialog_path); -+ return 0; -+#else -+ /* -+ * stderr has already been redirected to the error_log. -+ * rather than attempting to temporarily rehook it to the terminal, -+ * we print the prompt to stdout before EVP_read_pw_string turns -+ * off tty echo -+ */ -+ apr_file_open_stdout(&writetty, ppcb->p); -+ -+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10147) -+ "Init: Requesting pass phrase via builtin terminal " -+ "dialog"); -+#endif -+ } -+ -+ /* -+ * The first time display a header to inform the user about what -+ * program he actually speaks to, which module is responsible for -+ * this terminal dialog and why to the hell he has to enter -+ * something... -+ */ -+ if (ppcb->nPassPhraseDialog == 1) { -+ apr_file_printf(writetty, "%s mod_ssl (Pass Phrase Dialog)\n", -+ AP_SERVER_BASEVERSION); -+ apr_file_printf(writetty, -+ "A pass phrase is required to access the private key.\n"); -+ } -+ if (ppcb->bPassPhraseDialogOnce) { -+ ppcb->bPassPhraseDialogOnce = FALSE; -+ apr_file_printf(writetty, "\n"); -+ apr_file_printf(writetty, "Private key %s (%s)\n", -+ ppcb->key_id, ppcb->pkey_file); -+ } -+ } -+ -+ return 1; -+} -+ -+static int passphrase_ui_read(UI *ui, UI_STRING *uis) -+{ -+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui); -+ SSLSrvConfigRec *sc = mySrvConfig(ppcb->s); -+ const char *prompt; -+ int i; -+ int bufsize; -+ int len; -+ char *buf; -+ -+ prompt = UI_get0_output_string(uis); -+ if (prompt == NULL) { -+ prompt = "Enter pass phrase:"; -+ } -+ -+ /* -+ * Get the maximum expected size and allocate the buffer -+ */ -+ bufsize = UI_get_result_maxsize(uis); -+ buf = apr_pcalloc(ppcb->p, bufsize); -+ -+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN -+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { -+ /* -+ * Get the pass phrase through a callback. -+ * Empty input is not accepted. -+ */ -+ for (;;) { -+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { -+ i = pipe_get_passwd_cb(buf, bufsize, "", FALSE); -+ } -+ else { /* sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN */ -+ i = EVP_read_pw_string(buf, bufsize, "", FALSE); -+ } -+ if (i != 0) { -+ OPENSSL_cleanse(buf, bufsize); -+ return 0; -+ } -+ len = strlen(buf); -+ if (len < 1){ -+ apr_file_printf(writetty, "Apache:mod_ssl:Error: Pass phrase" -+ "empty (needs to be at least 1 character).\n"); -+ apr_file_puts(prompt, writetty); -+ } -+ else { -+ break; -+ } -+ } -+ } -+ /* -+ * Filter program -+ */ -+ else if (sc->server->pphrase_dialog_type == SSL_PPTYPE_FILTER) { -+ const char *cmd = sc->server->pphrase_dialog_path; -+ const char **argv = apr_palloc(ppcb->p, sizeof(char *) * 3); -+ char *result; -+ -+ ap_log_error(APLOG_MARK, APLOG_INFO, 0, ppcb->s, APLOGNO(10148) -+ "Init: Requesting pass phrase from dialog filter " -+ "program (%s)", cmd); -+ -+ argv[0] = cmd; -+ argv[1] = ppcb->key_id; -+ argv[2] = NULL; -+ -+ result = ssl_util_readfilter(ppcb->s, ppcb->p, cmd, argv); -+ apr_cpystrn(buf, result, bufsize); -+ len = strlen(buf); -+ } -+ -+ /* -+ * Ok, we now have the pass phrase, so give it back -+ */ -+ ppcb->cpPassPhraseCur = apr_pstrdup(ppcb->p, buf); -+ UI_set_result(ui, uis, buf); -+ -+ /* Clear sensitive data. */ -+ OPENSSL_cleanse(buf, bufsize); -+ return 1; -+} -+ -+static int passphrase_ui_write(UI *ui, UI_STRING *uis) -+{ -+ pphrase_cb_arg_t *ppcb = UI_get0_user_data(ui); -+ SSLSrvConfigRec *sc; -+ const char *prompt; -+ -+ sc = mySrvConfig(ppcb->s); -+ -+ if (sc->server->pphrase_dialog_type == SSL_PPTYPE_BUILTIN -+ || sc->server->pphrase_dialog_type == SSL_PPTYPE_PIPE) { -+ prompt = UI_get0_output_string(uis); -+ apr_file_puts(prompt, writetty); -+ } -+ -+ return 1; -+} -+ -+static int passphrase_ui_close(UI *ui) -+{ -+ /* -+ * Close the pipes if they were opened -+ */ -+ if (readtty) { -+ apr_file_close(readtty); -+ apr_file_close(writetty); -+ readtty = writetty = NULL; -+ } -+ return 1; -+} -+ -+static apr_status_t pp_ui_method_cleanup(void *uip) -+{ -+ UI_METHOD *uim = uip; -+ -+ UI_destroy_method(uim); -+ -+ return APR_SUCCESS; -+} -+ -+static UI_METHOD *get_passphrase_ui(apr_pool_t *p) -+{ -+ UI_METHOD *ui_method = UI_create_method("Passphrase UI"); -+ -+ UI_method_set_opener(ui_method, passphrase_ui_open); -+ UI_method_set_reader(ui_method, passphrase_ui_read); -+ UI_method_set_writer(ui_method, passphrase_ui_write); -+ UI_method_set_closer(ui_method, passphrase_ui_close); -+ -+ apr_pool_cleanup_register(p, ui_method, pp_ui_method_cleanup, -+ pp_ui_method_cleanup); -+ -+ return ui_method; -+} -+#endif -+ -+ -+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, -+ const char *vhostid, -+ const char *certid, const char *keyid, -+ X509 **pubkey, EVP_PKEY **privkey) -+{ -+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) -+ const char *c, *scheme; -+ ENGINE *e; -+ UI_METHOD *ui_method = get_passphrase_ui(p); -+ pphrase_cb_arg_t ppcb; -+ -+ memset(&ppcb, 0, sizeof ppcb); -+ ppcb.s = s; -+ ppcb.p = p; -+ ppcb.bPassPhraseDialogOnce = TRUE; -+ ppcb.key_id = vhostid; -+ ppcb.pkey_file = keyid; -+ -+ c = ap_strchr_c(keyid, ':'); -+ if (!c || c == keyid) { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10131) -+ "Init: Unrecognized private key identifier `%s'", -+ keyid); -+ return ssl_die(s); -+ } -+ -+ scheme = apr_pstrmemdup(p, keyid, c - keyid); -+ if (!(e = ENGINE_by_id(scheme))) { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10132) -+ "Init: Failed to load engine for private key %s", -+ keyid); -+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); -+ return ssl_die(s); -+ } -+ -+ if (!ENGINE_init(e)) { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10149) -+ "Init: Failed to initialize engine %s for private key %s", -+ scheme, keyid); -+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); -+ return ssl_die(s); -+ } -+ -+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, -+ "Init: Initialized engine %s for private key %s", -+ scheme, keyid); -+ -+ if (APLOGdebug(s)) { -+ ENGINE_ctrl_cmd_string(e, "VERBOSE", NULL, 0); -+ } -+ -+ if (certid) { -+ struct { -+ const char *cert_id; -+ X509 *cert; -+ } params = { certid, NULL }; -+ -+ if (!ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, ¶ms, NULL, 1)) { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10136) -+ "Init: Unable to get the certificate"); -+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); -+ return ssl_die(s); -+ } -+ -+ *pubkey = params.cert; -+ } -+ -+ *privkey = ENGINE_load_private_key(e, keyid, ui_method, &ppcb); -+ if (*privkey == NULL) { -+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO(10133) -+ "Init: Unable to get the private key"); -+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s); -+ return ssl_die(s); -+ } -+ -+ ENGINE_finish(e); -+ ENGINE_free(e); -+ -+ return APR_SUCCESS; -+#else -+ return APR_ENOTIMPL; -+#endif -+} -Index: httpd-2.4.41/modules/ssl/ssl_private.h -=================================================================== ---- httpd-2.4.41.orig/modules/ssl/ssl_private.h 2018-11-23 16:10:24.000000000 +0100 -+++ httpd-2.4.41/modules/ssl/ssl_private.h 2020-02-26 11:10:32.727187775 +0100 -@@ -1002,21 +1002,28 @@ BOOL ssl_util_vhost_matches(cons - apr_status_t ssl_load_encrypted_pkey(server_rec *, apr_pool_t *, int, - const char *, apr_array_header_t **); - -+/* Load public and/or private key from the configured ENGINE. Private -+ * key returned as *pkey. certid can be NULL, in which case *pubkey -+ * is not altered. Errors logged on failure. */ -+apr_status_t modssl_load_engine_keypair(server_rec *s, apr_pool_t *p, -+ const char *vhostid, -+ const char *certid, const char *keyid, -+ X509 **pubkey, EVP_PKEY **privkey); -+ - /** Diffie-Hellman Parameter Support */ - DH *ssl_dh_GetParamFromFile(const char *); - #ifdef HAVE_ECC - EC_GROUP *ssl_ec_GetParamFromFile(const char *); - #endif - --unsigned char *ssl_asn1_table_set(apr_hash_t *table, -- const char *key, -- long int length); -- --ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, -- const char *key); -- --void ssl_asn1_table_unset(apr_hash_t *table, -- const char *key); -+/* Store the EVP_PKEY key (serialized into DER) in the hash table with -+ * key, returning the ssl_asn1_t structure pointer. */ -+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key, -+ EVP_PKEY *pkey); -+/* Retrieve the ssl_asn1_t structure with given key from the hash. */ -+ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, const char *key); -+/* Remove and free the ssl_asn1_t structure with given key. */ -+void ssl_asn1_table_unset(apr_hash_t *table, const char *key); - - /** Mutex Support */ - int ssl_mutex_init(server_rec *, apr_pool_t *); -@@ -1109,6 +1116,10 @@ int modssl_request_is_tls(const request_ - int ssl_is_challenge(conn_rec *c, const char *servername, - X509 **pcert, EVP_PKEY **pkey); - -+/* Returns non-zero if the cert/key filename should be handled through -+ * the configured ENGINE. */ -+int modssl_is_engine_id(const char *name); -+ - #endif /* SSL_PRIVATE_H */ - /** @} */ - -Index: httpd-2.4.41/modules/ssl/ssl_util.c -=================================================================== ---- httpd-2.4.41.orig/modules/ssl/ssl_util.c 2018-11-23 16:10:24.000000000 +0100 -+++ httpd-2.4.41/modules/ssl/ssl_util.c 2020-02-26 11:10:32.727187775 +0100 -@@ -192,45 +192,37 @@ BOOL ssl_util_path_check(ssl_pathcheck_t - return TRUE; - } - --/* -- * certain key data needs to survive restarts, -- * which are stored in the user data table of s->process->pool. -- * to prevent "leaking" of this data, we use malloc/free -- * rather than apr_palloc and these wrappers to help make sure -- * we do not leak the malloc-ed data. -- */ --unsigned char *ssl_asn1_table_set(apr_hash_t *table, -- const char *key, -- long int length) -+/* Decrypted private keys are cached to survive restarts. The cached -+ * data must have lifetime of the process (hence malloc/free rather -+ * than pools), and uses raw DER since the EVP_PKEY structure -+ * internals may not survive across a module reload. */ -+ssl_asn1_t *ssl_asn1_table_set(apr_hash_t *table, const char *key, -+ EVP_PKEY *pkey) - { - apr_ssize_t klen = strlen(key); - ssl_asn1_t *asn1 = apr_hash_get(table, key, klen); -+ apr_size_t length = i2d_PrivateKey(pkey, NULL); -+ unsigned char *p; - -- /* -- * if a value for this key already exists, -- * reuse as much of the already malloc-ed data -- * as possible. -- */ -+ /* Re-use structure if cached previously. */ - if (asn1) { - if (asn1->nData != length) { -- free(asn1->cpData); /* XXX: realloc? */ -- asn1->cpData = NULL; -+ asn1->cpData = ap_realloc(asn1->cpData, length); - } - } - else { - asn1 = ap_malloc(sizeof(*asn1)); - asn1->source_mtime = 0; /* used as a note for encrypted private keys */ -- asn1->cpData = NULL; -- } -- -- asn1->nData = length; -- if (!asn1->cpData) { - asn1->cpData = ap_malloc(length); -+ -+ apr_hash_set(table, key, klen, asn1); - } - -- apr_hash_set(table, key, klen, asn1); -+ asn1->nData = length; -+ p = asn1->cpData; -+ i2d_PrivateKey(pkey, &p); /* increases p by length */ - -- return asn1->cpData; /* caller will assign a value to this */ -+ return asn1; - } - - ssl_asn1_t *ssl_asn1_table_get(apr_hash_t *table, -@@ -480,3 +472,13 @@ void ssl_util_thread_id_setup(apr_pool_t - } - - #endif /* #if APR_HAS_THREADS && MODSSL_USE_OPENSSL_PRE_1_1_API */ -+ -+int modssl_is_engine_id(const char *name) -+{ -+#if defined(HAVE_OPENSSL_ENGINE_H) && defined(HAVE_ENGINE_INIT) -+ /* ### Can handle any other special ENGINE key names here? */ -+ return strncmp(name, "pkcs11:", 7) == 0; -+#else -+ return 0; -+#endif -+} -Index: httpd-2.4.41/modules/ssl/ssl_util_ssl.h -=================================================================== ---- httpd-2.4.41.orig/modules/ssl/ssl_util_ssl.h 2018-03-09 08:55:27.000000000 +0100 -+++ httpd-2.4.41/modules/ssl/ssl_util_ssl.h 2020-02-26 11:10:32.727187775 +0100 -@@ -64,8 +64,11 @@ - void modssl_init_app_data2_idx(void); - void *modssl_get_app_data2(SSL *); - void modssl_set_app_data2(SSL *, void *); --EVP_PKEY *modssl_read_privatekey(const char *, EVP_PKEY **, pem_password_cb *, void *); --EVP_PKEY *modssl_read_encrypted_pkey(const char *, EVP_PKEY **, const char *, apr_size_t); -+ -+/* Read private key from filename in either PEM or raw base64(DER) -+ * format, using password entry callback cb and userdata. */ -+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *ud); -+ - int modssl_smart_shutdown(SSL *ssl); - BOOL modssl_X509_getBC(X509 *, int *, int *); - char *modssl_X509_NAME_ENTRY_to_string(apr_pool_t *p, X509_NAME_ENTRY *xsne, -Index: httpd-2.4.41/modules/ssl/ssl_util_ssl.c -=================================================================== ---- httpd-2.4.41.orig/modules/ssl/ssl_util_ssl.c 2018-03-09 08:55:27.000000000 +0100 -+++ httpd-2.4.41/modules/ssl/ssl_util_ssl.c 2020-02-26 11:10:32.727187775 +0100 -@@ -74,7 +74,7 @@ void modssl_set_app_data2(SSL *ssl, void - ** _________________________________________________________________ - */ - --EVP_PKEY *modssl_read_privatekey(const char* filename, EVP_PKEY **key, pem_password_cb *cb, void *s) -+EVP_PKEY *modssl_read_privatekey(const char *filename, pem_password_cb *cb, void *s) - { - EVP_PKEY *rc; - BIO *bioS; -@@ -83,7 +83,7 @@ EVP_PKEY *modssl_read_privatekey(const c - /* 1. try PEM (= DER+Base64+headers) */ - if ((bioS=BIO_new_file(filename, "r")) == NULL) - return NULL; -- rc = PEM_read_bio_PrivateKey(bioS, key, cb, s); -+ rc = PEM_read_bio_PrivateKey(bioS, NULL, cb, s); - BIO_free(bioS); - - if (rc == NULL) { -@@ -107,41 +107,9 @@ EVP_PKEY *modssl_read_privatekey(const c - BIO_free(bioS); - } - } -- if (rc != NULL && key != NULL) { -- if (*key != NULL) -- EVP_PKEY_free(*key); -- *key = rc; -- } - return rc; - } - --typedef struct { -- const char *pass; -- int pass_len; --} pass_ctx; -- --static int provide_pass(char *buf, int size, int rwflag, void *baton) --{ -- pass_ctx *ctx = baton; -- if (ctx->pass_len > 0) { -- if (ctx->pass_len < size) { -- size = (int)ctx->pass_len; -- } -- memcpy(buf, ctx->pass, size); -- } -- return ctx->pass_len; --} -- --EVP_PKEY *modssl_read_encrypted_pkey(const char *filename, EVP_PKEY **key, -- const char *pass, apr_size_t pass_len) --{ -- pass_ctx ctx; -- -- ctx.pass = pass; -- ctx.pass_len = pass_len; -- return modssl_read_privatekey(filename, key, provide_pass, &ctx); --} -- - /* _________________________________________________________________ - ** - ** Smart shutdown diff --git a/apache2.changes b/apache2.changes index 509bd36..56806d2 100644 --- a/apache2.changes +++ b/apache2.changes @@ -1,3 +1,123 @@ +------------------------------------------------------------------- +Thu Apr 2 08:56:48 UTC 2020 - pgajdos@suse.com + +- version update to 2.4.43 + *) mod_ssl: Fix memory leak of OCSP stapling response. [Yann Ylavic] + *) mod_proxy_http: Fix the forwarding of requests with content body when a + balancer member is unavailable; the retry on the next member was issued + with an empty body (regression introduced in 2.4.41). PR63891. + [Yann Ylavic] + *) mod_http2: Fixes issue where mod_unique_id would generate non-unique request + identifier under load, see . + [Michael Kaufmann, Stefan Eissing] + *) mod_proxy_hcheck: Allow healthcheck expressions to use %{Content-Type}. + PR64140. [Renier Velazco ] + *) mod_authz_groupfile: Drop AH01666 from loglevel "error" to "info". + PR64172. + *) mod_usertrack: Add CookieSameSite, CookieHTTPOnly, and CookieSecure + to allow customization of the usertrack cookie. PR64077. + [Prashant Keshvani , Eric Covener] + *) mod_proxy_ajp: Add "secret" parameter to proxy workers to implement legacy + AJP13 authentication. PR 53098. [Dmitry A. Bakshaev ] + *) mpm_event: avoid possible KeepAliveTimeout off by -100 ms. + [Eric Covener, Yann Ylavic] + *) Add a config layout for OpenWRT. [Graham Leggett] + *) Add support for cross compiling to apxs. If apxs is being executed from + somewhere other than its target location, add that prefix to includes and + library directories. Without this, apxs would fail to find config_vars.mk + and exit. [Graham Leggett] + *) mod_ssl: Disable client verification on ACME ALPN challenges. Fixes github + issue mod_md#172 (https://github.com/icing/mod_md/issues/172). + [Michael Kaufmann , Stefan Eissing] + *) mod_ssl: use OPENSSL_init_ssl() to initialise OpenSSL on versions 1.1+. + [Graham Leggett] + *) mod_ssl: Support use of private keys and certificates from an + OpenSSL ENGINE via PKCS#11 URIs in SSLCertificateFile/KeyFile. + [Anderson Sasaki , Joe Orton] + *) mod_md: + - Prefer MDContactEmail directive to ServerAdmin for registration. New directive + thanks to Timothe Litt (@tlhackque). + - protocol check for pre-configured "tls-alpn-01" challenge has been improved. It will now + check all matching virtual hosts for protocol support. Thanks to @mkauf. + - Corrected a check when OCSP stapling was configured for hosts + where the responsible MDomain is not clear, by Michal Karm Babacek (@Karm). + - Softening the restrictions where mod_md configuration directives may appear. This should + allow for use in and sections. If all possible variations lead to the configuration + you wanted in the first place, is another matter. + [Michael Kaufmann , Timothe Litt (@tlhackque), + Michal Karm Babacek (@Karm), Stefan Eissing (@icing)] + *) test: Added continuous testing with Travis CI. + This tests various scenarios on Ubuntu with the full test suite. + Architectures tested: amd64, s390x, ppc64le, arm64 + The tests pass successfully. + [Luca Toscano, Joe Orton, Mike Rumph, and others] + *) core: Be stricter in parsing of Transfer-Encoding headers. + [ZeddYu , Eric Covener] + *) mod_ssl: negotiate the TLS protocol version per name based vhost + configuration, when linked with OpenSSL-1.1.1 or later. The base vhost's + SSLProtocol (from the first vhost declared on the IP:port) is now only + relevant if no SSLProtocol is declared for the vhost or globally, + otherwise the vhost or global value apply. [Yann Ylavic] + *) mod_cgi, mod_cgid: Fix a memory leak in some error cases with large script + output. PR 64096. [Joe Orton] + *) config: Speed up graceful restarts by using pre-hashed command table. PR 64066. + [Giovanni Bechis , Jim Jagielski] + *) mod_systemd: New module providing integration with systemd. [Jan Kaluza] + *) mod_lua: Add r:headers_in_table, r:headers_out_table, r:err_headers_out_table, + r:notes_table, r:subprocess_env_table as read-only native table alternatives + that can be iterated over. [Eric Covener] + *) mod_http2: Fixed rare cases where a h2 worker could deadlock the main connection. + [Yann Ylavic, Stefan Eissing] + *) mod_lua: Accept nil assignments to the exposed tables (r.subprocess_env, + r.headers_out, etc) to remove the key from the table. PR63971. + [Eric Covener] + *) mod_http2: Fixed interaction with mod_reqtimeout. A loaded mod_http2 was disabling the + ssl handshake timeouts. Also, fixed a mistake of the last version that made `H2Direct` + always `on`, regardless of configuration. Found and reported by + and + . [Stefan Eissing] + *) mod_http2: Multiple field length violations in the same request no longer cause + several log entries to be written. [@mkauf] + *) mod_ssl: OCSP does not apply to proxy mode. PR 63679. + [Lubos Uhliarik , Yann Ylavic] + *) mod_proxy_html, mod_xml2enc: Fix build issues with macOS due to r1864469 + [Jim Jagielski] + *) mod_authn_socache: Increase the maximum length of strings that can be cached by + the module from 100 to 256. PR 62149 [] + *) mod_proxy: Fix crash by resolving pool concurrency problems. PR 63503 + [Ruediger Pluem, Eric Covener] + *) core: On Windows, fix a start-up crash if is used with a path that is not + valid (For example, testing for a file on a flash drive that is not mounted) + [Christophe Jaillet] + *) mod_deflate, mod_brotli: honor "Accept-Encoding: foo;q=0" as per RFC 7231; which + means 'foo' is "not acceptable". PR 58158 [Chistophe Jaillet] + *) mod_md v2.2.3: + - Configuring MDCAChallenges replaces any previous existing challenge configuration. It + had been additive before which was not the intended behaviour. [@mkauf] + - Fixing order of ACME challenges used when nothing else configured. Code now behaves as + documented for `MDCAChallenges`. Fixes #156. Thanks again to @mkauf for finding this. + - Fixing a potential, low memory null pointer dereference [thanks to @uhliarik]. + - Fixing an incompatibility with a change in libcurl v7.66.0 that added unwanted + "transfer-encoding" to POST requests. This failed in directy communication with + Let's Encrypt boulder server. Thanks to @mkauf for finding and fixing. [Stefan Eissing] + *) mod_md: Adding the several new features. + The module offers an implementation of OCSP Stapling that can replace fully or + for a limited set of domains the existing one from mod_ssl. OCSP handling + is part of mod_md's monitoring and message notifications. If can be used + for sites that do not have ACME certificates. + The url for a CTLog Monitor can be configured. It is used in the server-status + to link to the external status page of a certicate. + The MDMessageCmd is called with argument "installed" when a new certificate + has been activated on server restart/reload. This allows for processing of + the new certificate, for example to applications that require it in different + locations or formats. + [Stefan Eissing] + *) mod_proxy_balancer: Fix case-sensitive referer check related to CSRF/XSS + protection. PR 63688. [Armin Abfalterer ] +- deleted patches + - apache2-load-private-keys-from-pkcs11.patch (upstreamed) + - httpd-2.4.3-mod_systemd.patch (upstreamed) + ------------------------------------------------------------------- Wed Feb 26 10:33:47 UTC 2020 - pgajdos@suse.com diff --git a/apache2.spec b/apache2.spec index e1c9a24..314a707 100644 --- a/apache2.spec +++ b/apache2.spec @@ -65,7 +65,7 @@ %define build_http2 0 %endif Name: apache2 -Version: 2.4.41 +Version: 2.4.43 Release: 0 Summary: The Apache Web Server License: Apache-2.0 @@ -140,14 +140,10 @@ Patch67: httpd-2.2.0-apxs-a2enmod.dif Patch68: httpd-2.x.x-logresolve.patch Patch69: httpd-2.4.9-bnc690734.patch Patch70: httpd-implicit-pointer-decl.patch -# PATCH-FEATURE-UPSTREAM httpd-2.4.3-mod_systemd.patch crrodriguez@opensuse.org simple module provides systemd integration. -Patch109: httpd-2.4.3-mod_systemd.patch Patch111: httpd-visibility.patch # PATCH-FEATURE-UPSTREAM kstreitova@suse.com -- backport of HttpContentLengthHeadZero and HttpExpectStrict Patch115: httpd-2.4.x-fate317766-config-control-two-protocol-options.diff Patch116: deprecated-scripts-arch.patch -# https://svn.apache.org/viewvc?view=revision&revision=1874196 -Patch117: apache2-load-private-keys-from-pkcs11.patch BuildRequires: apache-rpm-macros-control BuildRequires: apr-util-devel #Since 2.4.7 the event MPM requires apr 1.5.0 or later. @@ -331,16 +327,11 @@ to administrators of web servers in general. %patch68 -p1 %patch69 %patch70 -p1 -# Systemd module enabling patch -%if 0%{?suse_version} >= 1210 -%patch109 -p1 -%endif %patch111 -p1 %patch115 -p1 %if 0%{?suse_version} == 1110 %patch116 -p1 %endif -%patch117 -p1 cat %{_sourcedir}/SUSE-NOTICE >> NOTICE # install READMEs a=$(basename %{SOURCE22}) diff --git a/httpd-2.4.3-mod_systemd.patch b/httpd-2.4.3-mod_systemd.patch deleted file mode 100644 index c030e43..0000000 --- a/httpd-2.4.3-mod_systemd.patch +++ /dev/null @@ -1,179 +0,0 @@ -Index: httpd-2.4.23/modules/arch/unix/config5.m4 -=================================================================== ---- httpd-2.4.23.orig/modules/arch/unix/config5.m4 2016-12-09 12:29:49.398598223 +0100 -+++ httpd-2.4.23/modules/arch/unix/config5.m4 2016-12-09 13:00:35.429783217 +0100 -@@ -18,6 +18,19 @@ APACHE_MODULE(privileges, Per-virtualhos - fi - ]) - -+ -+APACHE_MODULE(systemd, Systemd support, , , $unixd_mods_enabled, [ -+ PKG_CHECK_MODULES([SYSTEMD], -+ [libsystemd >= 209], -+ [have_systemd=yes], -+ [PKG_CHECK_MODULES([SYSTEMD], -+ [libsystemd-login >= 32, libsystemd-daemon >= 32, libsystemd-journal >= 32], -+ [have_systemd=yes], -+ [have_systemd=no])]) -+ APR_ADDTO(MOD_SYSTEMD_LDADD, [$SYSTEMD_LIBS]) -+]) -+ -+ - APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) - - APACHE_MODPATH_FINISH -Index: httpd-2.4.23/modules/arch/unix/mod_systemd.c -=================================================================== ---- /dev/null 1970-01-01 00:00:00.000000000 +0000 -+++ httpd-2.4.23/modules/arch/unix/mod_systemd.c 2016-12-09 12:30:21.935147438 +0100 -@@ -0,0 +1,138 @@ -+/* Licensed to the Apache Software Foundation (ASF) under one or more -+ * contributor license agreements. See the NOTICE file distributed with -+ * this work for additional information regarding copyright ownership. -+ * The ASF licenses this file to You under the Apache License, Version 2.0 -+ * (the "License"); you may not use this file except in compliance with -+ * the License. You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ * -+ */ -+ -+#include -+#include -+#include "ap_mpm.h" -+#include -+#include -+#include -+#include -+#include -+#include "unixd.h" -+#include "scoreboard.h" -+#include "mpm_common.h" -+ -+#include "systemd/sd-daemon.h" -+ -+#if APR_HAVE_UNISTD_H -+#include -+#endif -+ -+#define KBYTE 1024 -+ -+static pid_t pid; /* PID of the main httpd instance */ -+static int server_limit, thread_limit, threads_per_child, max_servers; -+static time_t last_update_time; -+static unsigned long last_update_access; -+static unsigned long last_update_kbytes; -+ -+static int systemd_pre_mpm(apr_pool_t *p, ap_scoreboard_e sb_type) -+{ -+ int rv; -+ last_update_time = time(0); -+ -+ ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit); -+ ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit); -+ ap_mpm_query(AP_MPMQ_MAX_THREADS, &threads_per_child); -+ /* work around buggy MPMs */ -+ if (threads_per_child == 0) -+ threads_per_child = 1; -+ ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_servers); -+ -+ pid = getpid(); -+ -+ rv = sd_notifyf(0, "READY=1\n" -+ "STATUS=Processing requests...\n" -+ "MAINPID=%lu", -+ (unsigned long) pid); -+ if (rv < 0) { -+ ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p, -+ "sd_notifyf returned an error %d", rv); -+ } -+ -+ return OK; -+} -+ -+static int systemd_monitor(apr_pool_t *p, server_rec *s) -+{ -+ int i, j, res, rv; -+ process_score *ps_record; -+ worker_score *ws_record; -+ unsigned long access = 0; -+ unsigned long bytes = 0; -+ unsigned long kbytes = 0; -+ char bps[5]; -+ time_t now = time(0); -+ time_t elapsed = now - last_update_time; -+ -+ for (i = 0; i < server_limit; ++i) { -+ ps_record = ap_get_scoreboard_process(i); -+ for (j = 0; j < thread_limit; ++j) { -+ ws_record = ap_get_scoreboard_worker_from_indexes(i, j); -+ if (ap_extended_status && !ps_record->quiescing && ps_record->pid) { -+ res = ws_record->status; -+ if (ws_record->access_count != 0 || -+ (res != SERVER_READY && res != SERVER_DEAD)) { -+ access += ws_record->access_count; -+ bytes += ws_record->bytes_served; -+ if (bytes >= KBYTE) { -+ kbytes += (bytes >> 10); -+ bytes = bytes & 0x3ff; -+ } -+ } -+ } -+ } -+ } -+ -+ apr_strfsize((unsigned long)(KBYTE *(float) (kbytes - last_update_kbytes) -+ / (float) elapsed), bps); -+ -+ rv = sd_notifyf(0, "READY=1\n" -+ "STATUS=Total requests: %lu; Current requests/sec: %.3g; " -+ "Current traffic: %sB/sec\n", access, -+ ((float)access - last_update_access) / (float) elapsed, bps); -+ if (rv < 0) { -+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(00000) -+ "sd_notifyf returned an error %d", rv); -+ } -+ -+ last_update_access = access; -+ last_update_kbytes = kbytes; -+ last_update_time = now; -+ -+ return DECLINED; -+} -+ -+static void systemd_register_hooks(apr_pool_t *p) -+{ -+ /* We know the PID in this hook ... */ -+ ap_hook_pre_mpm(systemd_pre_mpm, NULL, NULL, APR_HOOK_LAST); -+ /* Used to update httpd's status line using sd_notifyf */ -+ ap_hook_monitor(systemd_monitor, NULL, NULL, APR_HOOK_MIDDLE); -+} -+ -+module AP_MODULE_DECLARE_DATA systemd_module = -+{ -+ STANDARD20_MODULE_STUFF, -+ NULL, -+ NULL, -+ NULL, -+ NULL, -+ NULL, -+ systemd_register_hooks, -+}; -Index: httpd-2.4.23/configure.in -=================================================================== ---- httpd-2.4.23.orig/configure.in 2016-12-09 12:29:49.398598223 +0100 -+++ httpd-2.4.23/configure.in 2016-12-09 12:30:21.939147506 +0100 -@@ -77,6 +77,7 @@ dnl shared library support for these pac - dnl work on some platforms - - AC_CANONICAL_SYSTEM -+PKG_PROG_PKG_CONFIG - - orig_prefix="$prefix" - diff --git a/httpd-2.4.41.tar.bz2 b/httpd-2.4.41.tar.bz2 deleted file mode 100644 index 5ef8401..0000000 --- a/httpd-2.4.41.tar.bz2 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:133d48298fe5315ae9366a0ec66282fa4040efa5d566174481077ade7d18ea40 -size 7072373 diff --git a/httpd-2.4.41.tar.bz2.asc b/httpd-2.4.41.tar.bz2.asc deleted file mode 100644 index 3f99550..0000000 --- a/httpd-2.4.41.tar.bz2.asc +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQEzBAABCAAdFiEEueghOu+4Ya81pB8smV41IhrYTf8FAl1NdwkACgkQmV41IhrY -Tf/7cQf/YbevWNKn+DaPX+3pZ8BblqhMYzYZLDavSejV4dKzXoF0FA2IkMxq9Gh7 -y6cbLaycTOewkV2HZmtBhBLJRDlOLISKmkzXdq2qZJq9UR3aGFXnpZfRfaLBPExx -XX0H0con4n8qvcPTAfOSc6uT64HR9kZlAzPjr2I2nsZht3rDsXw+pvA0AhpfpqPS -7FSVMTXb7WUvMJauJdLetV2l8vY5P9LGDGMc7YPSfjGY3nrZJJv1DqCChF52SHph -6C0FPDjrewIIzUyJLKTkBH+dK6Wx1Lv4blBqOX+FK0Zj79zSwcNkZJDxoEdpYjyk -cPCTuAUKTV9e29TN1elZgP2ES75H4Q== -=vwtE ------END PGP SIGNATURE----- diff --git a/httpd-2.4.43.tar.bz2 b/httpd-2.4.43.tar.bz2 new file mode 100644 index 0000000..fcaa471 --- /dev/null +++ b/httpd-2.4.43.tar.bz2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a497652ab3fc81318cdc2a203090a999150d86461acff97c1065dc910fe10f43 +size 7155865 diff --git a/httpd-2.4.43.tar.bz2.asc b/httpd-2.4.43.tar.bz2.asc new file mode 100644 index 0000000..345ed61 --- /dev/null +++ b/httpd-2.4.43.tar.bz2.asc @@ -0,0 +1,11 @@ +-----BEGIN PGP SIGNATURE----- + +iQEzBAABCAAdFiEEueghOu+4Ya81pB8smV41IhrYTf8FAl58wHMACgkQmV41IhrY +Tf8I0AgArzcV/UmSu+Kbwb2MGtMQJaAKbuNT8tpL1OYYuINYDinmgNboPm9GHJdb +zd7fQIIBovlmuiC9s5cG0Fpm0O/pDUYs4jnxhnFTuSSMJcd/bDcRbb9Nh8lEfOkL +McHpyAIwQHQJJUueUlirGbNKdUj3INgLV/RvtP6ZsylHQQCf2nuUlel5ueJxtxtB +skI/lzcO/+XI+GH6/tGilHxVKHfFth47fWHsLj2tQzaLqK3aBHrCXK8m4HAmziOI +ijiZQw4s5Q3m/AC/5Lto0qYDKLtyCARQBpS7DW1BnJTH4OcvvYKWAUCtTDgH7N+f +qfHoOuKR2wOJjPRB2ats1VDOmP16hg== +=ZcVG +-----END PGP SIGNATURE-----