You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2017/11/23 15:18:12 UTC
svn commit: r1816161 [2/3] - in /httpd/httpd/patches/2.4.x:
mod_md-r1816156_svn.diff mod_ssl_minimal_md.diff
Added: httpd/httpd/patches/2.4.x/mod_md-r1816156_svn.diff
URL: http://svn.apache.org/viewvc/httpd/httpd/patches/2.4.x/mod_md-r1816156_svn.diff?rev=1816161&view=auto
==============================================================================
--- httpd/httpd/patches/2.4.x/mod_md-r1816156_svn.diff (added)
+++ httpd/httpd/patches/2.4.x/mod_md-r1816156_svn.diff Thu Nov 23 15:18:12 2017
@@ -0,0 +1,7715 @@
+Index: Apache-apr2.dsw
+===================================================================
+--- Apache-apr2.dsw (revision 1816151)
++++ Apache-apr2.dsw (working copy)
+@@ -2086,6 +2086,24 @@
+
+ ###############################################################################
+
++Project: "mod_md"=.\modules\md\mod_mmd.dsp - Package Owner=<4>
++
++Package=<5>
++{{{
++}}}
++
++Package=<4>
++{{{
++ Begin Project Dependency
++ Project_Dep_Name libapr
++ End Project Dependency
++ Begin Project Dependency
++ Project_Dep_Name libhttpd
++ End Project Dependency
++}}}
++
++###############################################################################
++
+ Project: "mod_mime"=.\modules\http\mod_mime.dsp - Package Owner=<4>
+
+ Package=<5>
+Index: Apache.dsw
+===================================================================
+--- Apache.dsw (revision 1816151)
++++ Apache.dsw (working copy)
+@@ -2485,6 +2485,27 @@
+
+ ###############################################################################
+
++Project: "mod_md"=.\modules\md\mod_mmd.dsp - Package Owner=<4>
++
++Package=<5>
++{{{
++}}}
++
++Package=<4>
++{{{
++ Begin Project Dependency
++ Project_Dep_Name libapr
++ End Project Dependency
++ Begin Project Dependency
++ Project_Dep_Name libaprutil
++ End Project Dependency
++ Begin Project Dependency
++ Project_Dep_Name libhttpd
++ End Project Dependency
++}}}
++
++###############################################################################
++
+ Project: "mod_mime"=.\modules\http\mod_mime.dsp - Package Owner=<4>
+
+ Package=<5>
+Index: BuildBin.dsp
+===================================================================
+--- BuildBin.dsp (revision 1816151)
++++ BuildBin.dsp (working copy)
+@@ -39,7 +39,7 @@
+ # PROP Use_Debug_Libraries 0
+ # PROP Output_Dir ""
+ # PROP Intermediate_Dir ""
+-# PROP Cmd_Line "NMAKE /f makefile.win INSTDIR="\Apache2" LONG=Release _trydb _trylua _tryxml _tryssl _tryzlib _trynghttp2 _trybrotli _dummy"
++# PROP Cmd_Line "NMAKE /f makefile.win INSTDIR="\Apache2" LONG=Release _trydb _trylua _tryxml _tryssl _tryzlib _trynghttp2 _trymd _tryserf _dummy"
+ # PROP Rebuild_Opt ""
+ # PROP Target_File "\Apache2\bin\httpd.exe"
+ # PROP Bsc_Name ".\Browse\httpd.bsc"
+@@ -58,7 +58,7 @@
+ # PROP Use_Debug_Libraries 1
+ # PROP Output_Dir ""
+ # PROP Intermediate_Dir ""
+-# PROP Cmd_Line "NMAKE /f makefile.win INSTDIR="\Apache2" LONG=Debug _trydb _trylua _tryxml _tryssl _tryzlib _trynghttp2 _trybrotli _dummy"
++# PROP Cmd_Line "NMAKE /f makefile.win INSTDIR="\Apache2" LONG=Debug _trydb _trylua _tryxml _tryssl _tryzlib _trynghttp2 _trymd _tryserf _dummy"
+ # PROP Rebuild_Opt ""
+ # PROP Target_File "\Apache2\bin\httpd.exe"
+ # PROP Bsc_Name ".\Browse\httpd.bsc"
+Index: Makefile.win
+===================================================================
+--- Makefile.win (revision 1816151)
++++ Makefile.win (working copy)
+@@ -295,6 +295,30 @@
+
+ !ENDIF
+
++!IF EXIST("srclib\openssl") && EXIST("srclib\jansson") && EXIST("srclib\curl")
++
++_trymd:
++!IF $(USEMAK) == 1
++ cd modules\md
++ $(MAKE) $(MAKEOPT) $(SSLOPT) -f mod_md.mak CFG="mod_md - Win32 $(LONG)" RECURSE=0 $(CTARGET)
++ cd ..\..
++!ELSEIF $(USESLN) == 1
++ devenv $(TLP).sln /useenv $(CTARGET) $(LONG) /project mod_md
++!ELSE
++ @msdev $(TLP).dsw /USEENV /MAKE \
++ "mod_md - Win32 $(LONG)" /NORECURSE $(CTARGET)
++!ENDIF
++
++!ELSE
++# NOT EXIST("srclib\openssl") && EXIST("srclib\jansson") && EXIST("srclib\curl")
++
++_trymd:
++ @echo -----
++ @echo mod_md will not build unless OpenSSL, Jansson and Curl are built in placed
++ @echo in srclib. Example: srclib/openssl, srclib/jansson and srclib/curl
++
++!ENDIF
++
+ _trydb:
+ !IF $(USEMAK) == 1
+ cd srclib\$(UTILDIR)\dbd
+@@ -644,7 +668,12 @@
+ cd support
+ $(MAKE) $(MAKEOPT) $(SSLOPT) -f abs.mak CFG="abs - Win32 $(LONG)" RECURSE=0 $(CTARGET)
+ cd ..
++!IF EXIST("srclib\jansson") && EXIST("srclib\curl")
++ cd modules\md
++ $(MAKE) $(MAKEOPT) $(SSLOPT) -f mod_md.mak CFG="mod_md - Win32 $(LONG)" RECURSE=0 $(CTARGET)
++ cd ..\..
+ !ENDIF
++!ENDIF
+ cd support
+ $(MAKE) $(MAKEOPT) -f ab.mak CFG="ab - Win32 $(LONG)" RECURSE=0 $(CTARGET)
+ # $(MAKE) $(MAKEOPT) -f fcgistarter.mak CFG="fcgistarter - Win32 $(LONG)" RECURSE=0 $(CTARGET)
+@@ -903,6 +932,11 @@
+ copy modules\slotmem\$(LONG)\mod_slotmem_plain.$(src_so) "$(inst_so)" <.y
+ copy modules\slotmem\$(LONG)\mod_slotmem_shm.$(src_so) "$(inst_so)" <.y
+ !IF EXIST("srclib\openssl")
++!IF EXIST("srclib\jansson") && EXIST("srclib\curl")
++ copy modules\md\$(LONG)\mod_md.$(src_so) "$(inst_so)" <.y
++ -copy srclib\jansson\bin\jansson.$(src_dll) "$(inst_dll)" <.y
++ -copy srclib\curl\bin\libcurl.$(src_dll) "$(inst_dll)" <.y
++!ENDIF
+ # copy modules\session\$(LONG)\mod_session_crypto.$(src_so) "$(inst_so)" <.y
+ copy modules\ssl\$(LONG)\mod_ssl.$(src_so) "$(inst_so)" <.y
+ -copy srclib\openssl\$(SSLBIN)\$(SSLCRP)*.$(src_dll) "$(inst_dll)" <.y
+Index: build/installwinconf.awk
+===================================================================
+--- build/installwinconf.awk (revision 1816151)
++++ build/installwinconf.awk (working copy)
+@@ -157,6 +157,7 @@
+ print "#LoadModule log_forensic_module modules/mod_log_forensic.so" > dstfl;
+ print "#LoadModule lua_module modules/mod_lua.so" > dstfl;
+ print "#LoadModule macro_module modules/mod_macro.so" > dstfl;
++ print "#LoadModule md_module modules/mod_md.so" > dstfl;
+ print "LoadModule mime_module modules/mod_mime.so" > dstfl;
+ print "#LoadModule mime_magic_module modules/mod_mime_magic.so" > dstfl;
+ print "LoadModule negotiation_module modules/mod_negotiation.so" > dstfl;
+Index: docs/manual/mod/mod_md.xml
+===================================================================
+--- docs/manual/mod/mod_md.xml (revision 1804542)
++++ docs/manual/mod/mod_md.xml (working copy)
+@@ -1,7 +1,7 @@
+ <?xml version="1.0"?>
+ <!DOCTYPE modulesynopsis SYSTEM "../style/modulesynopsis.dtd">
+ <?xml-stylesheet type="text/xsl" href="../style/manual.en.xsl"?>
+-<!-- $LastChangedRevision: 1793934 $ -->
++<!-- $LastChangedRevision$ -->
+
+ <!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+@@ -30,14 +30,15 @@
+ <sourcefile>mod_md.c</sourcefile>
+ <identifier>md_module</identifier>
+ <compatibility>Available in version 2.5.0 and later</compatibility>
+-
+ <summary>
+ <p>
+ This module manages common properties of domains for one or more virtual hosts.
+- Specifically it can use the ACME protocol (<a href="https://datatracker.ietf.org/doc/draft-ietf-acme-acme/">RFC Draft</a>)
++ Specifically it can use the ACME protocol
++ (<a href="https://datatracker.ietf.org/doc/draft-ietf-acme-acme/">RFC Draft</a>)
+ to automate certificate provisioning. These will be configured for managed domains and
+ their virtual hosts automatically. This includes renewal of certificates before they
+- expire.</p>
++ expire. The most famous Certificate Authority currently implementing the ACME protocol
++ is <a href="https://letsencrypt.org/">Let's Encrypt</a>.</p>
+
+ <note type="warning"><title>Warning</title>
+ <p>This module is experimental. Its behaviors, directives, and
+@@ -61,10 +62,12 @@
+ </VirtualHost>
+ </highlight>
+ <p>
+- This setup will, on server start, contact <a href="https://letsencrypt.org/">Let's Encrypt</a>
++ This setup will, on server start, contact
++ <a href="https://letsencrypt.org/">Let's Encrypt</a>
+ to request a certificate for the domain. If Let's Encrypt can verify the ownership
+ of the domain, the module will retrieve the certificate and its chain, store it
+- in the local file system and provide it, on next restart, to mod_ssl.
++ in the local file system (see <directive module="mod_md">MDStoreDir</directive>)
++ and provide it, on next restart, to mod_ssl.
+ </p><p>
+ This happens while the server is already running. All other hosts will continue
+ to work as before. While a certificate is not available, requests for the managed
+@@ -74,11 +77,10 @@
+
+ </summary>
+
+-
+ <directivesynopsis>
+ <name>ManagedDomain</name>
+- <description>Define list of domain names that belong to one group</description>
+- <syntax>ManagedDomain dns-name [ other-dns-name... ]</syntax>
++ <description>Define list of domain names that belong to one group.</description>
++ <syntax>ManagedDomain <var>dns-name</var> [ <var>other-dns-name</var>... ]</syntax>
+ <contextlist>
+ <context>server config</context>
+ </contextlist>
+@@ -86,15 +88,17 @@
+ <usage>
+ <p>
+ All the names in the list are managed as one Managed Domain (MD).
+- mod_md will request one certificate that is valid for all these names. This
++ mod_md will request one single certificate that is valid for all these names. This
+ directive uses the global settings (see other MD directives below). If you
+ need specific settings for one MD, use
+- the <directive module="mod_md" type="section"><ManagedDomain</directive>.
++ the <directive module="mod_md" type="section">ManagedDomain</directive>.
+ </p><p>
+- There are 2 additional settings that are necessary for a Managed Domain: ServerAdmin
+- and MDCertificateAgreement. The mail address of
+- <directive module="mod_core">ServerAdmin</directive> is used to register
+- at the CA (Let's Encrypt by default). The CA may use it to notify you about
++ There are 2 additional settings that are necessary for a Managed Domain:
++ <directive module="core">ServerAdmin</directive>
++ and <directive module="mod_md">MDCertificateAgreement</directive>.
++ The mail address of <directive module="core">ServerAdmin</directive>
++ is used to register at the CA (Let's Encrypt by default).
++ The CA may use it to notify you about
+ changes in its service or status of your certificates.
+ </p><p>
+ The second setting, <directive module="mod_md">MDCertificateAgreement</directive>,
+@@ -123,13 +127,40 @@
+ </VirtualHost>
+ </highlight>
+ </example>
++ <p>
++ There are two special names that you may use in this directive: 'manual'
++ and 'auto'. This determines if a Managed Domain shall have exactly the
++ name list as is configured ('manual') or offer more convenience. With 'auto'
++ all names of a virtual host are added to a MD.
++ </p>
++ <example><title>Example</title>
++ <highlight language="config">
++ManagedDomain example.org
++
++<VirtualHost *:443>
++ ServerName example.org
++ ServerAlias www.example.org
++ DocumentRoot htdocs/root
++
++ SSLEngine on
++</VirtualHost>
++ </highlight>
++ </example>
++ <p>
++ In this example, the domain 'www.example.org' is automatically added to
++ the MD 'example.org'. And when you add more ServerAlias names to this
++ virtual host, they will be added as well.
++ </p><p>
++ If this is too much automagic for you, define 'manual' mode.
++ <module>mod_md</module> will then complain if the names do not match.
++ </p>
+ </usage>
+ </directivesynopsis>
+
+- <directivesynopsis type="section">
+- <name><ManagedDomain</name>
+- <description>Define a group of domains with common properties</description>
+- <syntax><ManagedDomain dns-name>...</ManagedDomain></syntax>
++ <directivesynopsis type="section" idtype="section">
++ <name>ManagedDomain</name>
++ <description>Container for directives applied to the same managed domains.</description>
++ <syntax><ManagedDomain <var>dns-name</var> [ <var>other-dns-name</var>... ]>...</ManagedDomain></syntax>
+ <contextlist>
+ <context>server config</context>
+ </contextlist>
+@@ -147,7 +178,7 @@
+ MDDriveMode manual
+ MDCertificateAuthority https://someotherca.com/ACME
+ MDCertificateAgreement https://someotherca.com/terms/v_1.02.pdf
+-</ManagedDomain example.org>
++</ManagedDomain>
+ </highlight>
+ </example>
+ </usage>
+@@ -159,7 +190,6 @@
+ <syntax>MDCertificateAgreement url-of-terms-of-service</syntax>
+ <contextlist>
+ <context>server config</context>
+- <context>managed domain</context>
+ </contextlist>
+ <usage>
+ <p>When you use <module>mod_md</module> to obtain a certificate, you become a customer of the CA (e.g. Let's Encrypt). That means you need to read and agree to their Terms of Service,
+@@ -179,12 +209,11 @@
+
+ <directivesynopsis>
+ <name>MDCertificateAuthority</name>
+- <description>The URL of the ACME CA service</description>
++ <description>The URL of the ACME Certificate Authority service.</description>
+ <syntax>MDCertificateAuthority url</syntax>
+ <default>MDCertificateAuthority https://acme-v01.api.letsencrypt.org/directory</default>
+ <contextlist>
+ <context>server config</context>
+- <context>managed domain</context>
+ </contextlist>
+ <usage>
+ <p>
+@@ -191,14 +220,14 @@
+ The URL where the CA offers its service.
+ </p><p>
+ Let's Encrypt offers, right now, two such URLs. One for the real certificates and
+- one for testing (their staging area, athttps://acme-staging.api.letsencrypt.org/directory).
++ one for testing (their staging area, at https://acme-staging.api.letsencrypt.org/directory).
+ In order to have <module>mod_md</module> use this testing service, configure your
+ server like this:
+ </p>
+ <example><title>LE Staging Setup</title>
+ <highlight language="config">
+- MDCertificateAuthority https://acme-staging.api.letsencrypt.org/directory
+- MDCertificateAgreement https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf
++MDCertificateAuthority https://acme-staging.api.letsencrypt.org/directory
++MDCertificateAgreement https://letsencrypt.org/documents/LE-SA-v1.1.1-August-1-2016.pdf
+ </highlight>
+ </example>
+ </usage>
+@@ -206,12 +235,11 @@
+
+ <directivesynopsis>
+ <name>MDCertificateProtocol</name>
+- <description>The protocol to use with the CA</description>
++ <description>The protocol to use with the Certificate Authority.</description>
+ <syntax>MDCertificateProtocol protocol</syntax>
+ <default>MDCertificateProtocol ACME</default>
+ <contextlist>
+ <context>server config</context>
+- <context>managed domain</context>
+ </contextlist>
+ <usage>
+ <p>Specifies the protocol to use. Currently, only <code>ACME</code> is supported.</p>
+@@ -220,23 +248,22 @@
+
+ <directivesynopsis>
+ <name>MDDriveMode</name>
+- <description>Controls when <module>mod_md</module> will try to obtain/renew certificates.</description>
+- <syntax>MDDriveMode always|auto|manual</syntax>
+- <default>MDDriveMode auto</default>
++ <description>Control when it is allowed to obtain/renew certificates.</description>
++ <syntax>MDDriveMode always|auto|manual</syntax>
++ <default>MDDriveMode auto</default>
+ <contextlist>
+ <context>server config</context>
+- <context>managed domain</context>
+ </contextlist>
+ <usage>
+ <p>In 'auto' mode, <module>mod_md</module> will <em>drive</em> a Managed Domain's
+- properties (e.g. certicate management) whenever necessary. When a MD is not used
++ properties (e.g. certificate management) whenever necessary. When a MD is not used
+ in any virtual host, the module will do nothing. When a certificate is missing, it
+ will try to get one. When a certificate expires soon (see
+- <directive module="mod_md" type="section">MDRenewWindow</directive>), it will
++ <directive module="mod_md">MDRenewWindow</directive>), it will
+ renew it.
+ </p><p>
+- In 'manual' mode, it is your duty to do all this. The module will provide existing
+- ceriticate to mod_ssl, if available. But it will not contact the CA for signup/renewal.
++ In 'manual' mode, it is your duty to do all this. The module will provide the existing
++ certificate to mod_ssl, if available. But it will not contact the CA for signup/renewal.
+ This can be useful in clustered setups where you want just one node to perform
+ the driving.
+ </p><p>
+@@ -247,17 +274,30 @@
+ </directivesynopsis>
+
+ <directivesynopsis>
++ <name>MDHttpProxy</name>
++ <description>Define a proxy for outgoing connections.</description>
++ <syntax>MDHttpProxy url</syntax>
++ <contextlist>
++ <context>server config</context>
++ </contextlist>
++ <usage>
++ <p>Use a http proxy to connect to the MDCertificateAuthority. Define this
++ if your webserver can only reach the internet with a forward proxy.
++ </p>
++ </usage>
++ </directivesynopsis>
++
++ <directivesynopsis>
+ <name>MDMember</name>
+- <description>Additional hostname for the managed domain</description>
++ <description>Additional hostname for the managed domain.</description>
+ <syntax>MDMember hostname</syntax>
+ <contextlist>
+ <context>server config</context>
+- <context>managed domain</context>
+ </contextlist>
+ <usage>
+ <p>
+ Instead of listing all dns names on the same line, you may use
+- <directive module="mod_md" type="section">MDMember</directive> to add such names
++ <directive module="mod_md">MDMember</directive> to add such names
+ to a managed domain.
+ </p>
+ <example><title>Example</title>
+@@ -269,17 +309,67 @@
+ </highlight>
+ </example>
+ <p>
+- If you use it in the global context, outside a specific MD, you can only
+- specify one value, 'auto' or 'manual' as the default for all other MDs. See
+- <directive module="mod_md" type="section">ManagedDomain</directive> for a
+- description of these special values.
++ If you use it in the global context, outside a specific MD, you can only
++ specify one value, 'auto' or 'manual' as the default for all other MDs. See
++ <directive module="mod_md" type="section">ManagedDomain</directive> for a
++ description of these special values.
+ </p>
+ </usage>
+ </directivesynopsis>
+
+ <directivesynopsis>
++ <name>MDMembers</name>
++ <description>Control if the alias domain names are automatically added.</description>
++ <syntax>MDMembers auto|manual</syntax>
++ <default>MDDriveMode auto</default>
++ <contextlist>
++ <context>server config</context>
++ </contextlist>
++ <usage>
++ <p>Defines if the <directive module="core">ServerName</directive> and
++ <directive module="core">ServerAlias</directive> values of a VirtualHost
++ are automatically added to the members of a Managed Domain or not.
++ </p>
++ </usage>
++ </directivesynopsis>
++
++ <directivesynopsis>
++ <name>MDMustStaple</name>
++ <description>Control if new certificates carry the OCSP Must Staple flag.</description>
++ <syntax>MDMustStaple on|off</syntax>
++ <default>MDMustStaple off</default>
++ <contextlist>
++ <context>server config</context>
++ </contextlist>
++ <usage>
++ <p>Defines if newly requested certificate should have the OCSP Must Staple flag
++ set or not. If a certificate has this flag, the server is required to send a
++ OCSP stapling response to every client. This only works if you configure
++ mod_ssl to generate this (see <directive module="mod_ssl" >SSLUseStapling</directive>
++ and friends).
++ </p>
++ </usage>
++ </directivesynopsis>
++
++ <directivesynopsis>
++ <name>MDNotifyCmd</name>
++ <description>Run a program when Managed Domain are ready.</description>
++ <syntax>MDNotifyCmd path</syntax>
++ <contextlist>
++ <context>server config</context>
++ </contextlist>
++ <usage>
++ <p>The configured executable is run when Managed Domains have signed up or
++ renewed their certificates. It is given the names of the processed MDs as
++ arguments. It should return status code 0 to indicate that it has
++ run successfully.
++ </p>
++ </usage>
++ </directivesynopsis>
++
++ <directivesynopsis>
+ <name>MDPortMap</name>
+- <description></description>
++ <description>Map external to internal ports for domain ownership verification.</description>
+ <syntax>MDPortMap map1 [ map2 ]</syntax>
+ <default>MDPortMap 80:80 443:443</default>
+ <contextlist>
+@@ -314,32 +404,145 @@
+ </p>
+ </usage>
+ </directivesynopsis>
+-
++
+ <directivesynopsis>
++ <name>MDPrivateKeys</name>
++ <description>Set type and size of the private keys generated.</description>
++ <syntax>MDPrivateKeys type [ params... ]</syntax>
++ <default>MDPrivateKeys RSA 2048</default>
++ <contextlist>
++ <context>server config</context>
++ </contextlist>
++ <usage>
++ <p>
++ Defines what kind of private keys are generated for a managed domain and with
++ what parameters. The only supported type right now is 'RSA' and the only parameter
++ it takes is the number of bits used for the key.
++ </p><p>
++ The current (2017) recommendation is at least 2048 bits and a smaller number is
++ not accepted here. Higher numbers offer longer security, but are computationally more
++ expensive, e.g. increase the load on your server. That might or might not be an
++ issue for you.
++ </p><p>
++ Other key types will be defined in the future.
++ </p>
++ <example><title>Example</title>
++ <highlight language="config">
++MDPrivateKeys RSA 3072
++ </highlight>
++ </example>
++ <p>
++ Please note that this setting only has an effect on new keys. Any existing
++ private key you have remains unaffected. Also, this only affects private keys
++ generated for certificates. ACME account keys are unaffected by this.
++ </p>
++ </usage>
++ </directivesynopsis>
++
++ <directivesynopsis>
+ <name>MDRenewWindow</name>
+- <description></description>
++ <description>Control when a certificate will be renewed.</description>
+ <syntax>MDRenewWindow duration</syntax>
+- <default>MDRenewWindow 14d</default>
++ <default>MDRenewWindow 33%</default>
+ <contextlist>
+ <context>server config</context>
+- <context>managed domain</context>
+ </contextlist>
+ <usage>
+ <p>
+- Tells mod_md when to renew a certificate. The default means 14 days before a
+- certificate actually expires. If you configure this too short, a CA might
+- not be reachable in time and your server will show an invalid certificate. If
+- you do it too long, the CA might think you are a bother and block your requests.
+- Let's Encrypt has a certificate expiration of 90 days. So, if you configure the
+- renew window to 89 days, <module>mod_md</module> will renew the certificate
+- every day and Let's Encrypt will block you.
++ If the validity of the certificate falls below duration, mod_md will get a
++ new signed certificate.
++ </p><p>
++ Normally, certificates are valid for around 90 days and mod_md will renew
++ them the earliest 33% of their complete lifetime before they expire (so for
++ 90 days validity, 30 days before it expires). If you think this is not what
++ you need, you can specify either the exact time, as in:
+ </p>
++ <example><title>Example</title>
++ <highlight language="config">
++# 21 days before expiry
++MDRenewWindow 21d
++# 30 seconds (might be close)
++MDRenewWindow 30s
++# 10% of the cert lifetime
++MDRenewWindow 10%
++ </highlight>
++ </example>
++ <p>When in auto drive mode, the module will check every 12 hours at least
++ what the status of the managed domains is and if it needs to do something.
++ On errors, for example when the CA is unreachable, it will initially retry
++ after some seconds. Should that continue to fail, it will back off to a
++ maximum interval of hourly checks.
++ </p>
+ </usage>
+ </directivesynopsis>
+
+ <directivesynopsis>
++ <name>MDRequireHttps</name>
++ <description>Redirects http: traffic to https: for Managed Domains.</description>
++ <syntax>MDRequireHttps off|temporary|permanent</syntax>
++ <default>MDRequireHttps off</default>
++ <contextlist>
++ <context>server config</context>
++ </contextlist>
++ <usage>
++ <p>This is a convenience directive to ease http: to https: migration of
++ your Managed Domains. With:
++ </p>
++ <example><title>Example</title>
++ <highlight language="config">
++MDRequireHttps temporary
++ </highlight>
++ </example>
++ <p>you announce that you want all traffic via http: URLs to be redirected
++ to the https: ones, for now. This is safe and you can remove this again at
++ any time.
++ </p><p>
++ <strong>The following has consequences: </strong>if you want client to <strong>no longer</strong> use the
++ http: URLs, configure:
++ </p>
++ <example><title>Permanent (for at least half a year!)</title>
++ <highlight language="config">
++MDRequireHttps permanent
++ </highlight>
++ </example>
++ <p>This does two things:
++ </p>
++ <ol>
++ <li>All request to the <code>http:</code> resources are redirected to the
++ same url with the <code>https:</code> scheme using the <code>301</code>
++ status code. This tells clients that this is intended to be forever and
++ the should update any links they have accordingly.
++ </li>
++ <li>All answers to <code>https:</code> requests will carry the header
++ <code>Strict-Transport-Security</code> with a life time of half a year.
++ This tells the browser that it <strong>never</strong> (for half a year) shall use <code>http:</code>
++ when talking to this domain name. Browsers will, after having seen this, refuse
++ to contact your unencrypted site. This prevents malicious middleware to
++ downgrade connections and listen/manipulate the traffic. Which is good. But
++ you cannot simply take it back again.
++ </li>
++ </ol>
++ <p>You can achieve the same with mod_alias and some Redirect configuration,
++ basically. If you do it yourself, please make sure to exclude the paths
++ /.well-known/* from your redirection, otherwise mod_md might have trouble
++ signing on new certificates.
++ </p>
++ <p>If you set this globally, it applies to all managed domains. If you want
++ it for a specific domain only, use:
++ </p>
++ <example><title>Example</title>
++ <highlight language="config">
++<ManagedDomain xxx.yyy>
++ MDRequireHttps temporary
++</ManagedDomain>
++ </highlight>
++ </example>
++ </usage>
++ </directivesynopsis>
++
++ <directivesynopsis>
+ <name>MDStoreDir</name>
+- <description></description>
++ <description>Path on the local file system to store the Managed Domains data.</description>
+ <syntax>MDStoreDir path</syntax>
+ <default>MDStoreDir md</default>
+ <contextlist>
+@@ -358,15 +561,14 @@
+ </p>
+ </usage>
+ </directivesynopsis>
+-
++
+ <directivesynopsis>
+ <name>MDCAChallenges</name>
+- <description></description>
++ <description>Type of ACME challenge used to prove domain ownership.</description>
+ <syntax>MDCAChallenges name [ name ... ]</syntax>
+ <default>MDCAChallenges tls-sni-01 http-01</default>
+ <contextlist>
+ <context>server config</context>
+- <context>managed domain</context>
+ </contextlist>
+ <usage>
+ <p>
+@@ -378,5 +580,5 @@
+ </p>
+ </usage>
+ </directivesynopsis>
+-
++
+ </modulesynopsis>
+
+Property changes on: docs/manual/mod/mod_md.xml
+___________________________________________________________________
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+\ No newline at end of property
+Added: svn:keywords
+## -0,0 +1 ##
++LastChangedRevision
+\ No newline at end of property
+Index: docs/manual/mod/mod_md.xml.meta
+===================================================================
+Index: docs/manual/mod/mod_md.xml.meta
+===================================================================
+--- docs/manual/mod/mod_md.xml.meta (revision 1804542)
++++ docs/manual/mod/mod_md.xml.meta (working copy)
+
+Property changes on: docs/manual/mod/mod_md.xml.meta
+___________________________________________________________________
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+\ No newline at end of property
+Index: docs/manual/sections.xml
+===================================================================
+--- docs/manual/sections.xml (revision 1816151)
++++ docs/manual/sections.xml (working copy)
+@@ -51,6 +51,7 @@
+ <directive type="section" module="mod_version">IfVersion</directive>
+ <directive type="section" module="core">Location</directive>
+ <directive type="section" module="core">LocationMatch</directive>
++<directive type="section" module="md">MangedDomain</directive>
+ <directive type="section" module="mod_proxy">Proxy</directive>
+ <directive type="section" module="mod_proxy">ProxyMatch</directive>
+ <directive type="section" module="core">VirtualHost</directive>
+Index: docs/manual
+===================================================================
+--- docs/manual (revision 1816151)
++++ docs/manual (working copy)
+
+Property changes on: docs/manual
+___________________________________________________________________
+Modified: svn:mergeinfo
+## -0,0 +0,1 ##
+ Merged /httpd/httpd/trunk/docs/manual:r1804542,1804545,1804671,1804759,1804787,1804975,1805180,1805192,1805194,1805256,1805294,1805373,1806939,1807228,1807347,1807577,1807593,1807774,1807777,1808005,1808092,1808100,1808241-1808243,1808249,1808444,1809719,1809888,1810723,1811082,1811812,1812193,1812517-1812518,1812999,1813642,1814720,1814939,1815005,1815078,1815264
+Index: modules/md/Makefile
+===================================================================
+Index: modules/md/Makefile.in
+===================================================================
+Index: modules/md/config2.m4
+===================================================================
+--- modules/md/config2.m4 (revision 1804530)
++++ modules/md/config2.m4 (working copy)
+@@ -244,11 +244,29 @@
+
+ dnl # list of module object files
+ md_objs="dnl
++md_acme.lo dnl
++md_acme_acct.lo dnl
++md_acme_authz.lo dnl
++md_acme_drive.lo dnl
++md_core.lo dnl
++md_curl.lo dnl
++md_crypt.lo dnl
++md_http.lo dnl
++md_json.lo dnl
++md_jws.lo dnl
++md_log.lo dnl
++md_reg.lo dnl
++md_store.lo dnl
++md_store_fs.lo dnl
++md_util.lo dnl
+ mod_md.lo dnl
+ mod_md_config.lo dnl
+ mod_md_os.lo dnl
+ "
+
++# Ensure that other modules can pick up mod_md.h
++APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
++
+ dnl # hook module into the Autoconf mechanism (--enable-md)
+ APACHE_MODULE(md, [Managed Domain handling], $md_objs, , most, [
+ APACHE_CHECK_OPENSSL
+@@ -268,16 +286,8 @@
+ AC_MSG_WARN([libcurl not found])
+ enable_md=no
+ fi
+-
+- APR_ADDTO(MOD_MD_LDADD, [ "libmd.la" ])
+- APR_ADDTO(A2MD_LDADD, [ "libmd.la" ])
+ ])
+
+-# Ensure that other modules can pick up mod_md.h
+-APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current])
+-
+-
+-
+ dnl # end of module specific part
+ APACHE_MODPATH_FINISH
+
+Index: modules/md/md.h
+===================================================================
+--- modules/md/md.h (revision 1804530)
++++ modules/md/md.h (working copy)
+@@ -24,9 +24,18 @@
+ struct md_cert_t;
+ struct md_pkey_t;
+ struct md_store_t;
++struct md_srv_conf_t;
++struct md_pkey_spec_t;
+
+ #define MD_TLSSNI01_DNS_SUFFIX ".acme.invalid"
+
++#define MD_PKEY_RSA_BITS_MIN 2048
++#define MD_PKEY_RSA_BITS_DEF 2048
++
++/* Minimum age for the HSTS header (RFC 6797), considered appropriate by Mozilla Security */
++#define MD_HSTS_HEADER "Strict-Transport-Security"
++#define MD_HSTS_MAX_AGE_DEFAULT 15768000
++
+ typedef enum {
+ MD_S_UNKNOWN, /* MD has not been analysed yet */
+ MD_S_INCOMPLETE, /* MD is missing necessary information, cannot go live */
+@@ -33,9 +42,17 @@
+ MD_S_COMPLETE, /* MD has all necessary information, can go live */
+ MD_S_EXPIRED, /* MD is complete, but credentials have expired */
+ MD_S_ERROR, /* MD data is flawed, unable to be processed as is */
++ MD_S_MISSING, /* MD is missing config information, cannot proceed */
+ } md_state_t;
+
+ typedef enum {
++ MD_REQUIRE_UNSET = -1,
++ MD_REQUIRE_OFF,
++ MD_REQUIRE_TEMPORARY,
++ MD_REQUIRE_PERMANENT,
++} md_require_t;
++
++typedef enum {
+ MD_SV_TEXT,
+ MD_SV_JSON,
+ MD_SV_CERT,
+@@ -64,14 +81,17 @@
+ typedef struct md_t md_t;
+ struct md_t {
+ const char *name; /* unique name of this MD */
+- md_state_t state; /* state of this MD */
+- apr_time_t expires; /* When the credentials for this domain expire. 0 if unknown */
+- apr_interval_time_t renew_window;/* time before expiration that starts renewal */
+-
+ struct apr_array_header_t *domains; /* all DNS names this MD includes */
++ struct apr_array_header_t *contacts; /* list of contact uris, e.g. mailto:xxx */
++
+ int transitive; /* != 0 iff VirtualHost names/aliases are auto-added */
+- md_drive_mode_t drive_mode; /* mode of obtaining credentials */
++ md_require_t require_https; /* Iff https: is required for this MD */
++
++ int drive_mode; /* mode of obtaining credentials */
++ struct md_pkey_spec_t *pkey_spec;/* specification for generating new private keys */
+ int must_staple; /* certificates should set the OCSP Must Staple extension */
++ apr_interval_time_t renew_norm; /* if > 0, normalized cert lifetime */
++ apr_interval_time_t renew_window;/* time before expiration that starts renewal */
+
+ const char *ca_url; /* url of CA certificate service */
+ const char *ca_proto; /* protocol used vs CA (e.g. ACME) */
+@@ -78,10 +98,13 @@
+ const char *ca_account; /* account used at CA */
+ const char *ca_agreement; /* accepted agreement uri between CA and user */
+ struct apr_array_header_t *ca_challenges; /* challenge types configured for this MD */
+- struct apr_array_header_t *contacts; /* list of contact uris, e.g. mailto:xxx */
+
++ md_state_t state; /* state of this MD */
++ apr_time_t valid_from; /* When the credentials start to be valid. 0 if unknown */
++ apr_time_t expires; /* When the credentials expire. 0 if unknown */
+ const char *cert_url; /* url where cert has been created, remember during drive */
+-
++
++ const struct md_srv_conf_t *sc; /* server config where it was defined or NULL */
+ const char *defn_name; /* config file this MD was defined */
+ unsigned defn_line_number; /* line number of definition */
+ };
+@@ -88,6 +111,7 @@
+
+ #define MD_KEY_ACCOUNT "account"
+ #define MD_KEY_AGREEMENT "agreement"
++#define MD_KEY_BITS "bits"
+ #define MD_KEY_CA "ca"
+ #define MD_KEY_CA_URL "ca-url"
+ #define MD_KEY_CERT "cert"
+@@ -100,6 +124,7 @@
+ #define MD_KEY_DOMAIN "domain"
+ #define MD_KEY_DOMAINS "domains"
+ #define MD_KEY_DRIVE_MODE "drive-mode"
++#define MD_KEY_ERRORS "errors"
+ #define MD_KEY_EXPIRES "expires"
+ #define MD_KEY_HTTP "http"
+ #define MD_KEY_HTTPS "https"
+@@ -108,28 +133,41 @@
+ #define MD_KEY_KEY "key"
+ #define MD_KEY_KEYAUTHZ "keyAuthorization"
+ #define MD_KEY_LOCATION "location"
++#define MD_KEY_MUST_STAPLE "must-staple"
+ #define MD_KEY_NAME "name"
++#define MD_KEY_PERMANENT "permanent"
++#define MD_KEY_PKEY "privkey"
++#define MD_KEY_PROCESSED "processed"
+ #define MD_KEY_PROTO "proto"
+ #define MD_KEY_REGISTRATION "registration"
++#define MD_KEY_RENEW "renew"
+ #define MD_KEY_RENEW_WINDOW "renew-window"
++#define MD_KEY_REQUIRE_HTTPS "require-https"
+ #define MD_KEY_RESOURCE "resource"
+ #define MD_KEY_STATE "state"
+ #define MD_KEY_STATUS "status"
+ #define MD_KEY_STORE "store"
++#define MD_KEY_TEMPORARY "temporary"
+ #define MD_KEY_TOKEN "token"
+ #define MD_KEY_TRANSITIVE "transitive"
+ #define MD_KEY_TYPE "type"
+ #define MD_KEY_URL "url"
+ #define MD_KEY_URI "uri"
++#define MD_KEY_VALID_FROM "validFrom"
+ #define MD_KEY_VALUE "value"
+ #define MD_KEY_VERSION "version"
+
+ #define MD_FN_MD "md.json"
+-#define MD_FN_PKEY "pkey.pem"
++#define MD_FN_JOB "job.json"
++#define MD_FN_PRIVKEY "privkey.pem"
++#define MD_FN_PUBCERT "pubcert.pem"
+ #define MD_FN_CERT "cert.pem"
+ #define MD_FN_CHAIN "chain.pem"
+ #define MD_FN_HTTPD_JSON "httpd.json"
+
++#define MD_FN_FALLBACK_PKEY "fallback-privkey.pem"
++#define MD_FN_FALLBACK_CERT "fallback-cert.pem"
++
+ /* Check if a string member of a new MD (n) has
+ * a value and if it differs from the old MD o
+ */
+@@ -136,9 +174,6 @@
+ #define MD_VAL_UPDATE(n,o,s) ((n)->s != (o)->s)
+ #define MD_SVAL_UPDATE(n,o,s) ((n)->s && (!(o)->s || strcmp((n)->s, (o)->s)))
+
+-#define MD_SECS_PER_HOUR (60*60)
+-#define MD_SECS_PER_DAY (24*MD_SECS_PER_HOUR)
+-
+ /**
+ * Determine if the Managed Domain contains a specific domain name.
+ */
+@@ -199,7 +234,7 @@
+ /**
+ * Create a managed domain, given a list of domain names.
+ */
+-const char *md_create(md_t **pmd, apr_pool_t *p, struct apr_array_header_t *domains);
++md_t *md_create(apr_pool_t *p, struct apr_array_header_t *domains);
+
+ /**
+ * Deep copy an md record into another pool.
+@@ -211,6 +246,11 @@
+ */
+ md_t *md_copy(apr_pool_t *p, const md_t *src);
+
++/**
++ * Create a merged md with the settings of add overlaying the ones from base.
++ */
++md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base);
++
+ /**
+ * Convert the managed domain into a JSON representation and vice versa.
+ *
+@@ -219,14 +259,19 @@
+ struct md_json_t *md_to_json (const md_t *md, apr_pool_t *p);
+ md_t *md_from_json(struct md_json_t *json, apr_pool_t *p);
+
++/**
++ * Determine if MD should renew its cert (if it has one)
++ */
++int md_should_renew(const md_t *md);
++
+ /**************************************************************************************************/
+ /* domain credentials */
+
+ typedef struct md_creds_t md_creds_t;
+ struct md_creds_t {
++ struct md_pkey_t *privkey;
++ struct apr_array_header_t *pubcert; /* complete md_cert* chain */
+ struct md_cert_t *cert;
+- struct md_pkey_t *pkey;
+- struct apr_array_header_t *chain; /* list of md_cert* */
+ int expired;
+ };
+
+Index: modules/md/md_acme.c
+===================================================================
+--- modules/md/md_acme.c (revision 1804530)
++++ modules/md/md_acme.c (working copy)
+@@ -67,7 +67,7 @@
+ };
+
+ static apr_status_t problem_status_get(const char *type) {
+- int i;
++ size_t i;
+
+ if (strstr(type, "urn:ietf:params:") == type) {
+ type += strlen("urn:ietf:params:");
+@@ -90,7 +90,8 @@
+ return md_crypt_init(p);
+ }
+
+-apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url)
++apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
++ const char *proxy_url)
+ {
+ md_acme_t *acme;
+ const char *err = NULL;
+@@ -111,9 +112,9 @@
+ acme = apr_pcalloc(p, sizeof(*acme));
+ acme->url = url;
+ acme->p = p;
+- acme->user_agent = apr_psprintf(p, "%s mod_md/%s (Something, like certbot)",
++ acme->user_agent = apr_psprintf(p, "%s mod_md/%s",
+ base_product, MOD_MD_VERSION);
+- acme->pkey_bits = 4096;
++ acme->proxy_url = proxy_url? apr_pstrdup(p, proxy_url) : NULL;
+ acme->max_retries = 3;
+
+ if (APR_SUCCESS != (rv = apr_uri_parse(p, url, &uri_parsed))) {
+@@ -135,7 +136,7 @@
+
+ assert(acme->url);
+ if (!acme->http && APR_SUCCESS != (rv = md_http_create(&acme->http, acme->p,
+- acme->user_agent))) {
++ acme->user_agent, acme->proxy_url))) {
+ return rv;
+ }
+ md_http_set_response_limit(acme->http, 1024*1024);
+@@ -260,9 +261,15 @@
+ ptype = md_json_gets(problem, "type", NULL);
+ pdetail = md_json_gets(problem, "detail", NULL);
+ req->rv = problem_status_get(ptype);
+-
+- md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, req->rv, req->p,
+- "acme problem %s: %s", ptype, pdetail);
++
++ if (APR_STATUS_IS_EAGAIN(req->rv)) {
++ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, req->rv, req->p,
++ "acme reports %s: %s", ptype, pdetail);
++ }
++ else {
++ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, req->rv, req->p,
++ "acme problem %s: %s", ptype, pdetail);
++ }
+ return req->rv;
+ }
+ }
+@@ -277,7 +284,7 @@
+ return APR_ENOENT;
+ default:
+ md_log_perror(MD_LOG_MARK, MD_LOG_WARNING, 0, req->p,
+- "acme problem unknonw: http status %d", res->status);
++ "acme problem unknown: http status %d", res->status);
+ return APR_EGENERAL;
+ }
+ }
+@@ -485,7 +492,10 @@
+ md_json_t *jbody, void *baton)
+ {
+ json_ctx *ctx = baton;
+-
++
++ (void)acme;
++ (void)p;
++ (void)headers;
+ ctx->json = md_json_clone(ctx->pool, jbody);
+ return APR_SUCCESS;
+ }
+Index: modules/md/md_acme.h
+===================================================================
+--- modules/md/md_acme.h (revision 1804530)
++++ modules/md/md_acme.h (working copy)
+@@ -50,6 +50,7 @@
+ const char *sname; /* short name for the service, not necessarily unique */
+ apr_pool_t *p;
+ const char *user_agent;
++ const char *proxy_url;
+ struct md_acme_acct_t *acct;
+ struct md_pkey_t *acct_key;
+
+@@ -62,7 +63,6 @@
+
+ const char *nonce;
+ int max_retries;
+- unsigned int pkey_bits;
+ };
+
+ /**
+@@ -72,14 +72,16 @@
+
+ /**
+ * Create a new ACME server instance. If path is not NULL, will use that directory
+- * for persisting information. Will load any inforation persisted in earlier session.
++ * for persisting information. Will load any information persisted in earlier session.
+ * url needs only be specified for instances where this has never been persisted before.
+ *
+ * @param pacme will hold the ACME server instance on success
+ * @param p pool to used
+ * @param url url of the server, optional if known at path
++ * @param proxy_url optional url of a HTTP(S) proxy to use
+ */
+-apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url);
++apr_status_t md_acme_create(md_acme_t **pacme, apr_pool_t *p, const char *url,
++ const char *proxy_url);
+
+ /**
+ * Contact the ACME server and retrieve its directory information.
+@@ -111,7 +113,7 @@
+ * Get the local name of the account currently used by the acme instance.
+ * Will be NULL if no account has been setup successfully.
+ */
+-const char *md_acme_get_acct(md_acme_t *acme, apr_pool_t *p);
++const char *md_acme_get_acct_id(md_acme_t *acme);
+
+ /**
+ * Agree to the given Terms-of-Service url for the current account.
+@@ -123,11 +125,15 @@
+ * given in the agreement url.
+ * If the known agreement is equal to this, nothing is done.
+ * If it differs, the account is re-validated in the hope that the server
+- * accounces the Tos URL it wants. If this is equal to the agreement specified,
++ * announces the Tos URL it wants. If this is equal to the agreement specified,
+ * the server is notified of this. If the server requires a ToS that the account
+ * thinks it has already given, it is resend.
++ *
++ * If an agreement is required, different from the current one, APR_INCOMPLETE is
++ * returned and the agreement url is returned in the parameter.
+ */
+-apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p, const char *agreement);
++apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p,
++ const char *agreement, const char **prequired);
+
+ /**
+ * Get the ToS agreement for current account.
+@@ -170,7 +176,7 @@
+ /* request handling */
+
+ /**
+- * Request callback on a successfull HTTP response (status 2xx).
++ * Request callback on a successful HTTP response (status 2xx).
+ */
+ typedef apr_status_t md_acme_req_res_cb(md_acme_t *acme,
+ const struct md_http_response_t *res, void *baton);
+@@ -187,7 +193,7 @@
+ typedef apr_status_t md_acme_req_init_cb(md_acme_req_t *req, void *baton);
+
+ /**
+- * Request callback on a successfull response (HTTP response code 2xx) and content
++ * Request callback on a successful response (HTTP response code 2xx) and content
+ * type matching application/.*json.
+ */
+ typedef apr_status_t md_acme_req_json_cb(md_acme_t *acme, apr_pool_t *p,
+@@ -204,7 +210,7 @@
+ struct md_json_t *req_json; /* JSON to be POSTed in request body */
+
+ apr_table_t *resp_hdrs; /* HTTP response headers */
+- struct md_json_t *resp_json; /* JSON response body recevied */
++ struct md_json_t *resp_json; /* JSON response body received */
+
+ apr_status_t rv; /* status of request */
+
+Index: modules/md/md_acme_acct.c
+===================================================================
+--- modules/md/md_acme_acct.c (revision 1804530)
++++ modules/md/md_acme_acct.c (working copy)
+@@ -58,10 +58,6 @@
+ }
+
+
+-static void md_acme_acct_free(md_acme_acct_t *acct)
+-{
+-}
+-
+ static const char *mk_acct_id(apr_pool_t *p, md_acme_t *acme, int i)
+ {
+ return apr_psprintf(p, "ACME-%s-%04d", acme->sname, i);
+@@ -225,20 +221,25 @@
+ md_store_vtype_t vtype, void *value, apr_pool_t *ptemp)
+ {
+ find_ctx *ctx = baton;
+- md_json_t *json = value;
+ int disabled;
+ const char *ca_url, *id;
+
+- id = md_json_gets(json, MD_KEY_ID, NULL);
+- disabled = md_json_getb(json, MD_KEY_DISABLED, NULL);
+- ca_url = md_json_gets(json, MD_KEY_CA_URL, NULL);
+-
+- if (!disabled && ca_url && !strcmp(ctx->acme->url, ca_url)) {
+- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ctx->p,
+- "found account %s for %s: %s, disabled=%d, ca-url=%s",
+- name, ctx->acme->url, id, disabled, ca_url);
+- ctx->id = id;
+- return 0;
++ (void)aspect;
++ (void)ptemp;
++ if (MD_SV_JSON == vtype) {
++ md_json_t *json = value;
++
++ id = md_json_gets(json, MD_KEY_ID, NULL);
++ disabled = md_json_getb(json, MD_KEY_DISABLED, NULL);
++ ca_url = md_json_gets(json, MD_KEY_CA_URL, NULL);
++
++ if (!disabled && ca_url && !strcmp(ctx->acme->url, ca_url)) {
++ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, ctx->p,
++ "found account %s for %s: %s, disabled=%d, ca-url=%s",
++ name, ctx->acme->url, id, disabled, ca_url);
++ ctx->id = id;
++ return 0;
++ }
+ }
+ return 1;
+ }
+@@ -327,6 +328,7 @@
+ apr_status_t rv;
+ md_pkey_t *pkey;
+ const char *err = NULL, *uri;
++ md_pkey_spec_t spec;
+ int i;
+
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "create new account");
+@@ -347,7 +349,10 @@
+ }
+ }
+
+- if (APR_SUCCESS == (rv = md_pkey_gen_rsa(&pkey, acme->p, acme->pkey_bits))
++ spec.type = MD_PKEY_TYPE_RSA;
++ spec.params.rsa.bits = MD_ACME_ACCT_PKEY_BITS;
++
++ if (APR_SUCCESS == (rv = md_pkey_gen(&pkey, acme->p, &spec))
+ && APR_SUCCESS == (rv = acct_make(&acme->acct, p, acme->url, NULL, contacts))) {
+ acct_ctx_t ctx;
+
+@@ -367,7 +372,6 @@
+
+ out:
+ if (APR_SUCCESS != rv && acme->acct) {
+- md_acme_acct_free(acme->acct);
+ acme->acct = NULL;
+ }
+ return rv;
+@@ -380,6 +384,7 @@
+ {
+ md_json_t *jpayload;
+
++ (void)baton;
+ jpayload = md_json_create(req->p);
+ md_json_sets("reg", jpayload, MD_KEY_RESOURCE, NULL);
+
+@@ -394,6 +399,8 @@
+ const char *body_str;
+ const char *tos_required;
+
++ (void)p;
++ (void)baton;
+ apr_array_clear(acct->contacts);
+ md_json_getsa(acct->contacts, body, MD_KEY_CONTACT, NULL);
+ acct->registration = md_json_clone(acme->p, body);
+@@ -489,7 +496,7 @@
+ return rv;
+ }
+
+-const char *md_acme_get_acct(md_acme_t *acme, apr_pool_t *p)
++const char *md_acme_get_acct_id(md_acme_t *acme)
+ {
+ return acme->acct? acme->acct->id : NULL;
+ }
+@@ -549,6 +556,7 @@
+ {
+ md_json_t *jpayload;
+
++ (void)baton;
+ jpayload = md_json_create(req->p);
+ md_json_sets("reg", jpayload, MD_KEY_RESOURCE, NULL);
+ md_json_setb(1, jpayload, "delete", NULL);
+@@ -561,7 +569,9 @@
+ {
+ md_store_t *store = baton;
+ apr_status_t rv = APR_SUCCESS;
+-
++
++ (void)hdrs;
++ (void)body;
+ md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, p, "deleted account %s", acme->acct->url);
+ if (store) {
+ rv = md_acme_unstore_acct(store, p, acme->acct->id);
+@@ -575,6 +585,7 @@
+ {
+ md_acme_acct_t *acct = acme->acct;
+
++ (void)p;
+ if (!acct) {
+ return APR_EINVAL;
+ }
+@@ -610,15 +621,23 @@
+
+ static int agreement_required(md_acme_acct_t *acct)
+ {
+- return (!acct->agreement
+- || (acct->tos_required && strcmp(acct->tos_required, acct->agreement)));
++ /* We used to really check if the account agreement and the one
++ * indicated as valid are the very same:
++ * return (!acct->agreement
++ * || (acct->tos_required && strcmp(acct->tos_required, acct->agreement)));
++ * However, LE is happy if the account has agreed to a ToS in the past and
++ * does not required a renewed acceptance.
++ */
++ return !acct->agreement;
+ }
+
+-apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p, const char *agreement)
++apr_status_t md_acme_check_agreement(md_acme_t *acme, apr_pool_t *p,
++ const char *agreement, const char **prequired)
+ {
+ apr_status_t rv = APR_SUCCESS;
+
+ /* Check if (correct) Terms-of-Service for account were accepted */
++ *prequired = NULL;
+ if (agreement_required(acme->acct)) {
+ const char *tos = acme->acct->tos_required;
+ if (!tos) {
+@@ -642,10 +661,8 @@
+ rv = md_acme_agree(acme, p, tos);
+ }
+ else {
+- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, acme->p,
+- "need to accept terms-of-service <%s> for account %s",
+- tos, acme->acct->id);
+- rv = APR_EACCES;
++ *prequired = apr_pstrdup(p, tos);
++ rv = APR_INCOMPLETE;
+ }
+ }
+ return rv;
+Index: modules/md/md_acme_acct.h
+===================================================================
+--- modules/md/md_acme_acct.h (revision 1804530)
++++ modules/md/md_acme_acct.h (working copy)
+@@ -41,4 +41,8 @@
+ #define MD_FN_ACCOUNT "account.json"
+ #define MD_FN_ACCT_KEY "account.pem"
+
++/* ACME account private keys are always RSA and have that many bits. Since accounts
++ * are expected to live long, better err on the safe side. */
++#define MD_ACME_ACCT_PKEY_BITS 3072
++
+ #endif /* md_acme_acct_h */
+Index: modules/md/md_acme_authz.c
+===================================================================
+--- modules/md/md_acme_authz.c (revision 1804530)
++++ modules/md/md_acme_authz.c (working copy)
+@@ -45,7 +45,7 @@
+ return authz;
+ }
+
+-md_acme_authz_set_t *md_acme_authz_set_create(apr_pool_t *p, md_acme_t *acme)
++md_acme_authz_set_t *md_acme_authz_set_create(apr_pool_t *p)
+ {
+ md_acme_authz_set_t *authz_set;
+
+@@ -91,10 +91,10 @@
+ for (i = 0; i < set->authzs->nelts; ++i) {
+ authz = APR_ARRAY_IDX(set->authzs, i, md_acme_authz_t *);
+ if (!apr_strnatcasecmp(domain, authz->domain)) {
+- int n = i +1;
++ int n = i + 1;
+ if (n < set->authzs->nelts) {
+ void **elems = (void **)set->authzs->elts;
+- memmove(elems + i, elems + n, set->authzs->nelts - n);
++ memmove(elems + i, elems + n, (size_t)(set->authzs->nelts - n));
+ }
+ --set->authzs->nelts;
+ return APR_SUCCESS;
+@@ -152,6 +152,8 @@
+ const char *location = apr_table_get(hdrs, "location");
+ apr_status_t rv = APR_SUCCESS;
+
++ (void)acme;
++ (void)p;
+ if (location) {
+ ctx->authz = md_acme_authz_create(ctx->p);
+ ctx->authz->domain = apr_pstrdup(ctx->p, ctx->domain);
+@@ -172,6 +174,7 @@
+ apr_status_t rv;
+ authz_req_ctx ctx;
+
++ (void)store;
+ authz_req_ctx_init(&ctx, acme, domain, NULL, p);
+
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, acme->p, "create new authz");
+@@ -182,7 +185,7 @@
+ }
+
+ /**************************************************************************************************/
+-/* Update an exiosting authorization */
++/* Update an existing authorization */
+
+ apr_status_t md_acme_authz_update(md_acme_authz_t *authz, md_acme_t *acme,
+ md_store_t *store, apr_pool_t *p)
+@@ -191,6 +194,7 @@
+ const char *s;
+ apr_status_t rv;
+
++ (void)store;
+ assert(acme);
+ assert(acme->http);
+ assert(authz);
+@@ -261,6 +265,10 @@
+ {
+ authz_req_ctx *ctx = baton;
+
++ (void)acme;
++ (void)p;
++ (void)hdrs;
++ (void)body;
+ md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, ctx->p, "updated authz %s", ctx->authz->location);
+ return APR_SUCCESS;
+ }
+@@ -271,6 +279,7 @@
+ const char *thumb64, *key_authz;
+ apr_status_t rv;
+
++ (void)authz;
+ assert(cha);
+ assert(cha->token);
+
+@@ -292,12 +301,14 @@
+ }
+
+ static apr_status_t cha_http_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t *authz,
+- md_acme_t *acme, md_store_t *store, apr_pool_t *p)
++ md_acme_t *acme, md_store_t *store,
++ md_pkey_spec_t *key_spec, apr_pool_t *p)
+ {
+ const char *data;
+ apr_status_t rv;
+ int notify_server;
+
++ (void)key_spec;
+ if (APR_SUCCESS != (rv = setup_key_authz(cha, authz, acme, p, ¬ify_server))) {
+ goto out;
+ }
+@@ -347,7 +358,8 @@
+ }
+
+ static apr_status_t cha_tls_sni_01_setup(md_acme_authz_cha_t *cha, md_acme_authz_t *authz,
+- md_acme_t *acme, md_store_t *store, apr_pool_t *p)
++ md_acme_t *acme, md_store_t *store,
++ md_pkey_spec_t *key_spec, apr_pool_t *p)
+ {
+ md_cert_t *cha_cert;
+ md_pkey_t *cha_key;
+@@ -354,6 +366,7 @@
+ const char *cha_dns;
+ apr_status_t rv;
+ int notify_server;
++ apr_array_header_t *domains;
+
+ if ( APR_SUCCESS != (rv = setup_key_authz(cha, authz, acme, p, ¬ify_server))
+ || APR_SUCCESS != (rv = setup_cha_dns(&cha_dns, cha, p))) {
+@@ -365,14 +378,16 @@
+ if ((APR_SUCCESS == rv && !md_cert_covers_domain(cha_cert, cha_dns))
+ || APR_STATUS_IS_ENOENT(rv)) {
+
+- if (APR_SUCCESS != (rv = md_pkey_gen_rsa(&cha_key, p, acme->pkey_bits))) {
+- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: create tls-sni-01 challgenge key",
++ if (APR_SUCCESS != (rv = md_pkey_gen(&cha_key, p, key_spec))) {
++ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: create tls-sni-01 challenge key",
+ authz->domain);
+ goto out;
+ }
+
+ /* setup a certificate containing the challenge dns */
+- rv = md_cert_self_sign(&cha_cert, authz->domain, cha_dns, cha_key,
++ domains = apr_array_make(p, 5, sizeof(const char*));
++ APR_ARRAY_PUSH(domains, const char*) = cha_dns;
++ rv = md_cert_self_sign(&cha_cert, authz->domain, domains, cha_key,
+ apr_time_from_sec(7 * MD_SECS_PER_DAY), p);
+
+ if (APR_SUCCESS != rv) {
+@@ -405,7 +420,8 @@
+ }
+
+ typedef apr_status_t cha_starter(md_acme_authz_cha_t *cha, md_acme_authz_t *authz,
+- md_acme_t *acme, md_store_t *store, apr_pool_t *p);
++ md_acme_t *acme, md_store_t *store,
++ md_pkey_spec_t *key_spec, apr_pool_t *p);
+
+ typedef struct {
+ const char *name;
+@@ -428,9 +444,10 @@
+ static apr_status_t collect_offered(void *baton, size_t index, md_json_t *json)
+ {
+ cha_find_ctx *ctx = baton;
++ const char *ctype;
+
+- const char *ctype = md_json_gets(json, MD_KEY_TYPE, NULL);
+- if (ctype) {
++ (void)index;
++ if ((ctype = md_json_gets(json, MD_KEY_TYPE, NULL))) {
+ APR_ARRAY_PUSH(ctx->offered, const char*) = apr_pstrdup(ctx->p, ctype);
+ }
+ return 1;
+@@ -449,7 +466,8 @@
+ }
+
+ apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, md_acme_t *acme, md_store_t *store,
+- apr_array_header_t *challenges, apr_pool_t *p)
++ apr_array_header_t *challenges,
++ md_pkey_spec_t *key_spec, apr_pool_t *p)
+ {
+ apr_status_t rv;
+ int i;
+@@ -483,9 +501,9 @@
+ return rv;
+ }
+
+- for (i = 0; i < CHA_TYPES_LEN; ++i) {
++ for (i = 0; i < (int)CHA_TYPES_LEN; ++i) {
+ if (!apr_strnatcasecmp(CHA_TYPES[i].name, fctx.accepted->type)) {
+- return CHA_TYPES[i].start(fctx.accepted, authz, acme, store, p);
++ return CHA_TYPES[i].start(fctx.accepted, authz, acme, store, key_spec, p);
+ }
+ }
+
+@@ -508,6 +526,7 @@
+ {
+ md_json_t *jpayload;
+
++ (void)baton;
+ jpayload = md_json_create(req->p);
+ md_json_sets("deactivated", jpayload, MD_KEY_STATUS, NULL);
+
+@@ -519,6 +538,9 @@
+ {
+ authz_req_ctx *ctx = baton;
+
++ (void)p;
++ (void)body;
++ (void)hdrs;
+ md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, ctx->p, "deleted authz %s", ctx->authz->location);
+ acme->acct = NULL;
+ return APR_SUCCESS;
+@@ -529,6 +551,7 @@
+ {
+ authz_req_ctx ctx;
+
++ (void)store;
+ ctx.p = p;
+ ctx.authz = authz;
+
+@@ -560,7 +583,7 @@
+ authz->domain = md_json_dups(p, json, MD_KEY_DOMAIN, NULL);
+ authz->location = md_json_dups(p, json, MD_KEY_LOCATION, NULL);
+ authz->dir = md_json_dups(p, json, MD_KEY_DIR, NULL);
+- authz->state = (int)md_json_getl(json, MD_KEY_STATE, NULL);
++ authz->state = (md_acme_authz_state_t)md_json_getl(json, MD_KEY_STATE, NULL);
+ return authz;
+ }
+ return NULL;
+@@ -574,11 +597,13 @@
+
+ static apr_status_t authz_to_json(void *value, md_json_t *json, apr_pool_t *p, void *baton)
+ {
++ (void)baton;
+ return md_json_setj(md_acme_authz_to_json(value, p), json, NULL);
+ }
+
+ static apr_status_t authz_from_json(void **pvalue, md_json_t *json, apr_pool_t *p, void *baton)
+ {
++ (void)baton;
+ *pvalue = md_acme_authz_from_json(json, p);
+ return (*pvalue)? APR_SUCCESS : APR_EINVAL;
+ }
+@@ -595,7 +620,7 @@
+
+ md_acme_authz_set_t *md_acme_authz_set_from_json(md_json_t *json, apr_pool_t *p)
+ {
+- md_acme_authz_set_t *set = md_acme_authz_set_create(p, NULL);
++ md_acme_authz_set_t *set = md_acme_authz_set_create(p);
+ if (set) {
+ md_json_geta(set->authzs, authz_from_json, NULL, json, MD_KEY_AUTHZS, NULL);
+ return set;
+@@ -630,8 +655,9 @@
+ md_acme_authz_set_t *set;
+ const char *md_name;
+ int create;
+-
+- group = va_arg(ap, int);
++
++ (void)p;
++ group = (md_store_group_t)va_arg(ap, int);
+ md_name = va_arg(ap, const char *);
+ set = va_arg(ap, md_acme_authz_set_t *);
+ create = va_arg(ap, int);
+@@ -657,7 +683,7 @@
+ const char *md_name;
+ int i;
+
+- group = va_arg(ap, int);
++ group = (md_store_group_t)va_arg(ap, int);
+ md_name = va_arg(ap, const char *);
+
+ if (APR_SUCCESS == md_acme_authz_set_load(store, group, md_name, &authz_set, p)) {
+Index: modules/md/md_acme_authz.h
+===================================================================
+--- modules/md/md_acme_authz.h (revision 1804530)
++++ modules/md/md_acme_authz.h (working copy)
+@@ -21,6 +21,7 @@
+ struct md_acme_acct_t;
+ struct md_json_t;
+ struct md_store_t;
++struct md_pkey_spec_t;
+
+ typedef struct md_acme_challenge_t md_acme_challenge_t;
+
+@@ -67,8 +68,8 @@
+ struct md_store_t *store, apr_pool_t *p);
+
+ apr_status_t md_acme_authz_respond(md_acme_authz_t *authz, struct md_acme_t *acme,
+- struct md_store_t *store,
+- apr_array_header_t *challenges, apr_pool_t *p);
++ struct md_store_t *store, apr_array_header_t *challenges,
++ struct md_pkey_spec_t *key_spec, apr_pool_t *p);
+ apr_status_t md_acme_authz_del(md_acme_authz_t *authz, struct md_acme_t *acme,
+ struct md_store_t *store, apr_pool_t *p);
+
+@@ -81,7 +82,7 @@
+ struct apr_array_header_t *authzs;
+ };
+
+-md_acme_authz_set_t *md_acme_authz_set_create(apr_pool_t *p, struct md_acme_t *acme);
++md_acme_authz_set_t *md_acme_authz_set_create(apr_pool_t *p);
+ md_acme_authz_t *md_acme_authz_set_get(md_acme_authz_set_t *set, const char *domain);
+ apr_status_t md_acme_authz_set_add(md_acme_authz_set_t *set, md_acme_authz_t *authz);
+ apr_status_t md_acme_authz_set_remove(md_acme_authz_set_t *set, const char *domain);
+Index: modules/md/md_acme_drive.c
+===================================================================
+--- modules/md/md_acme_drive.c (revision 1804530)
++++ modules/md/md_acme_drive.c (working copy)
+@@ -42,9 +42,11 @@
+ const char *phase;
+ int complete;
+
+- md_pkey_t *pkey;
+- md_cert_t *cert;
+- apr_array_header_t *chain;
++ md_pkey_t *privkey; /* the new private key */
++ apr_array_header_t *pubcert; /* the new certificate + chain certs */
++
++ md_cert_t *cert; /* the new certificate */
++ apr_array_header_t *chain; /* the chain certificates */
+
+ md_acme_t *acme;
+ md_t *md;
+@@ -73,7 +75,7 @@
+
+ ad->phase = "setup acme";
+ if (!ad->acme
+- && APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, md->ca_url))) {
++ && APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, md->ca_url, d->proxy_url))) {
+ goto out;
+ }
+
+@@ -105,7 +107,7 @@
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: looking at existing accounts",
+ d->proto->protocol);
+ if (APR_SUCCESS == md_acme_find_acct(ad->acme, d->store, d->p)) {
+- md->ca_account = md_acme_get_acct(ad->acme, d->p);
++ md->ca_account = md_acme_get_acct_id(ad->acme);
+ update = 1;
+ }
+ }
+@@ -122,8 +124,8 @@
+ goto out;
+ }
+
+- if (APR_SUCCESS == (rv = md_acme_create_acct(ad->acme, d->p,
+- md->contacts, md->ca_agreement))
++ if (APR_SUCCESS == (rv = md_acme_create_acct(ad->acme, d->p, md->contacts,
++ md->ca_agreement))
+ && APR_SUCCESS == (rv = md_acme_acct_save_staged(ad->acme, d->store, md, d->p))) {
+ md->ca_account = MD_ACME_ACCT_STAGED;
+ update = 1;
+@@ -134,7 +136,7 @@
+ if (APR_SUCCESS == rv) {
+ const char *agreement = md_acme_get_agreement(ad->acme);
+ /* Persist the account chosen at the md so we use the same on future runs */
+- if (agreement && (!md->ca_agreement || strcmp(agreement, md->ca_agreement))) {
++ if (agreement && !md->ca_agreement) {
+ md->ca_agreement = agreement;
+ update = 1;
+ }
+@@ -174,7 +176,7 @@
+ */
+ rv = md_acme_authz_set_load(d->store, MD_SG_STAGING, md->name, &ad->authz_set, d->p);
+ if (!ad->authz_set || APR_STATUS_IS_ENOENT(rv)) {
+- ad->authz_set = md_acme_authz_set_create(d->p, ad->acme);
++ ad->authz_set = md_acme_authz_set_create(d->p);
+ rv = APR_SUCCESS;
+ }
+ else if (APR_SUCCESS != rv) {
+@@ -267,11 +269,13 @@
+ switch (authz->state) {
+ case MD_ACME_AUTHZ_S_VALID:
+ break;
++
+ case MD_ACME_AUTHZ_S_PENDING:
+-
+- rv = md_acme_authz_respond(authz, ad->acme, d->store, ad->ca_challenges, d->p);
++ rv = md_acme_authz_respond(authz, ad->acme, d->store, ad->ca_challenges,
++ d->md->pkey_spec, d->p);
+ changed = 1;
+ break;
++
+ default:
+ rv = APR_EINVAL;
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
+@@ -288,7 +292,7 @@
+ return rv;
+ }
+
+-static apr_status_t check_challenges(void *baton, int attemmpt)
++static apr_status_t check_challenges(void *baton, int attempt)
+ {
+ md_proto_driver_t *d = baton;
+ md_acme_driver_t *ad = d->baton;
+@@ -298,8 +302,8 @@
+
+ for (i = 0; i < ad->authz_set->authzs->nelts && APR_SUCCESS == rv; ++i) {
+ authz = APR_ARRAY_IDX(ad->authz_set->authzs, i, md_acme_authz_t*);
+- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: check AUTHZ for %s",
+- ad->md->name, authz->domain);
++ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: check AUTHZ for %s(%d. attempt)",
++ ad->md->name, authz->domain, attempt);
+ if (APR_SUCCESS == (rv = md_acme_authz_update(authz, ad->acme, d->store, d->p))) {
+ switch (authz->state) {
+ case MD_ACME_AUTHZ_S_VALID:
+@@ -362,7 +366,7 @@
+ md_acme_driver_t *ad = d->baton;
+ apr_status_t rv = APR_SUCCESS;
+
+-
++ (void)acme;
+ if (APR_SUCCESS == (rv = read_http_cert(&ad->cert, d->p, res))) {
+ rv = md_store_save(d->store, d->p, MD_SG_STAGING, ad->md->name, MD_FN_CERT,
+ MD_SV_CERT, ad->cert, 0);
+@@ -376,6 +380,7 @@
+ md_proto_driver_t *d = baton;
+ md_acme_driver_t *ad = d->baton;
+
++ (void)attempt;
+ return md_acme_GET(ad->acme, ad->md->cert_url, NULL, NULL, on_got_cert, d);
+ }
+
+@@ -422,6 +427,7 @@
+ md_acme_driver_t *ad = d->baton;
+ apr_status_t rv = APR_SUCCESS;
+
++ (void)acme;
+ ad->md->cert_url = apr_table_get(res->headers, "location");
+ if (!ad->md->cert_url) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, APR_EINVAL, d->p,
+@@ -464,22 +470,22 @@
+ static apr_status_t ad_setup_certificate(md_proto_driver_t *d)
+ {
+ md_acme_driver_t *ad = d->baton;
+- md_pkey_t *pkey;
++ md_pkey_t *privkey;
+ apr_status_t rv;
+
+- ad->phase = "setup cert pkey";
++ ad->phase = "setup cert privkey";
+
+- rv = md_pkey_load(d->store, MD_SG_STAGING, ad->md->name, &pkey, d->p);
++ rv = md_pkey_load(d->store, MD_SG_STAGING, ad->md->name, &privkey, d->p);
+ if (APR_STATUS_IS_ENOENT(rv)) {
+- if (APR_SUCCESS == (rv = md_pkey_gen_rsa(&pkey, d->p, ad->acme->pkey_bits))) {
+- rv = md_pkey_save(d->store, d->p, MD_SG_STAGING, ad->md->name, pkey, 1);
++ if (APR_SUCCESS == (rv = md_pkey_gen(&privkey, d->p, d->md->pkey_spec))) {
++ rv = md_pkey_save(d->store, d->p, MD_SG_STAGING, ad->md->name, privkey, 1);
+ }
+- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: generate pkey", ad->md->name);
++ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: generate privkey", ad->md->name);
+ }
+
+ if (APR_SUCCESS == rv) {
+ ad->phase = "setup csr";
+- rv = md_cert_req_create(&ad->csr_der_64, ad->md, pkey, d->p);
++ rv = md_cert_req_create(&ad->csr_der_64, ad->md, privkey, d->p);
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, d->p, "%s: create CSR", ad->md->name);
+ }
+
+@@ -507,6 +513,7 @@
+ md_cert_t *cert;
+ const char *ct;
+
++ (void)acme;
+ ct = apr_table_get(res->headers, "Content-Type");
+ if (ct && !strcmp("application/x-pkcs7-mime", ct)) {
+ /* root cert most likely, end it here */
+@@ -527,7 +534,7 @@
+ md_cert_t *cert;
+ const char *url, *last_url = NULL;
+ apr_status_t rv = APR_SUCCESS;
+-
++
+ while (APR_SUCCESS == rv && ad->chain->nelts < 10) {
+ int nelts = ad->chain->nelts;
+ if (ad->chain && nelts > 0) {
+@@ -565,7 +572,7 @@
+ }
+ }
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, rv, d->p,
+- "got chain with %d certs", ad->chain->nelts);
++ "got chain with %d certs (%d. attempt)", ad->chain->nelts, attempt);
+ return rv;
+ }
+
+@@ -613,8 +620,8 @@
+ }
+ else {
+ /* free to chose. Add all we support and see what we get offered */
++ APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_HTTP01;
+ APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_TLSSNI01;
+- APR_ARRAY_PUSH(ad->ca_challenges, const char*) = MD_AUTHZ_TYPE_HTTP01;
+ }
+
+ if (!d->can_http && !d->can_https) {
+@@ -625,6 +632,21 @@
+ return APR_EGENERAL;
+ }
+
++ if (!d->can_http) {
++ ad->ca_challenges = md_array_str_remove(d->p, ad->ca_challenges, MD_AUTHZ_TYPE_HTTP01, 0);
++ }
++ if (!d->can_https) {
++ ad->ca_challenges = md_array_str_remove(d->p, ad->ca_challenges, MD_AUTHZ_TYPE_TLSSNI01, 0);
++ }
++
++ if (apr_is_empty_array(ad->ca_challenges)) {
++ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, 0, d->p, "%s: specific CA challenge methods "
++ "have been configured, but the server is unable to use any of those. "
++ "For 'http-01' it needs to be reachable on port 80, for 'tls-sni-01'"
++ " port 443 is needed.", d->md->name);
++ return APR_EGENERAL;
++ }
++
+ md_log_perror(MD_LOG_MARK, MD_LOG_TRACE1, 0, d->p, "%s: init driver", d->md->name);
+
+ return rv;
+@@ -651,7 +673,7 @@
+ rv = md_load(d->store, MD_SG_STAGING, d->md->name, &ad->md, d->p);
+ if (APR_SUCCESS == rv) {
+ /* So, we have a copy in staging, but is it a recent or an old one? */
+- if (!md_is_newer(d->store, MD_SG_STAGING, MD_SG_DOMAINS, d->md->name, d->p)) {
++ if (md_is_newer(d->store, MD_SG_DOMAINS, MD_SG_STAGING, d->md->name, d->p)) {
+ reset_staging = 1;
+ }
+ }
+@@ -674,6 +696,12 @@
+ ad->md = NULL;
+ }
+
++ if (ad->md && ad->md->state == MD_S_MISSING) {
++ /* There is config information missing. It makes no sense to drive this MD further */
++ rv = APR_INCOMPLETE;
++ goto out;
++ }
++
+ if (ad->md) {
+ /* staging in progress. look for new ACME account information collected there */
+ rv = md_reg_creds_get(&ad->ncreds, d->reg, MD_SG_STAGING, d->md, d->p);
+@@ -684,7 +712,7 @@
+ }
+
+ /* Find out where we're at with this managed domain */
+- if (ad->ncreds && ad->ncreds->pkey && ad->ncreds->cert && ad->ncreds->chain) {
++ if (ad->ncreds && ad->ncreds->privkey && ad->ncreds->pubcert) {
+ /* There is a full set staged, to be loaded */
+ md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p, "%s: all data staged", d->md->name);
+ renew = 0;
+@@ -691,7 +719,7 @@
+ }
+
+ if (renew) {
+- if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, d->md->ca_url))
++ if (APR_SUCCESS != (rv = md_acme_create(&ad->acme, d->p, d->md->ca_url, d->proxy_url))
+ || APR_SUCCESS != (rv = md_acme_setup(ad->acme))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p, "%s: setup ACME(%s)",
+ d->md->name, d->md->ca_url);
+@@ -724,11 +752,32 @@
+ * requests for new authorizations are denied. ToS may change during the
+ * lifetime of an account */
+ if (APR_SUCCESS == rv) {
++ const char *required;
++
+ ad->phase = "check agreement";
+ md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
+ "%s: check Terms-of-Service agreement", d->md->name);
+
+- rv = md_acme_check_agreement(ad->acme, d->p, ad->md->ca_agreement);
++ rv = md_acme_check_agreement(ad->acme, d->p, ad->md->ca_agreement, &required);
++
++ if (APR_STATUS_IS_INCOMPLETE(rv) && required) {
++ /* The CA wants the user to agree to Terms-of-Services. Until the user
++ * has reconfigured and restarted the server, this MD cannot be
++ * driven further */
++ ad->md->state = MD_S_MISSING;
++ md_save(d->store, d->p, MD_SG_STAGING, ad->md, 0);
++
++ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, d->p,
++ "%s: the CA requires you to accept the terms-of-service "
++ "as specified in <%s>. "
++ "Please read the document that you find at that URL and, "
++ "if you agree to the conditions, configure "
++ "\"MDCertificateAgreement url\" "
++ "with exactly that URL in your Apache. "
++ "Then (graceful) restart the server to activate.",
++ ad->md->name, required);
++ goto out;
++ }
+ }
+
+ /* If we know a cert's location, try to get it. Previous download might
+@@ -780,11 +829,47 @@
+ }
+
+ if (APR_SUCCESS == rv && !ad->chain) {
++ /* have we created this already? */
++ md_chain_load(d->store, MD_SG_STAGING, ad->md->name, &ad->chain, d->p);
++ }
++ if (APR_SUCCESS == rv && !ad->chain) {
+ ad->phase = "install chain";
+ md_log_perror(MD_LOG_MARK, MD_LOG_INFO, 0, d->p,
+ "%s: retrieving certificate chain", d->md->name);
+ rv = ad_chain_install(d);
+ }
++
++ if (APR_SUCCESS == rv && !ad->pubcert) {
++ /* have we created this already? */
++ md_pubcert_load(d->store, MD_SG_STAGING, ad->md->name, &ad->pubcert, d->p);
++ }
++ if (APR_SUCCESS == rv && !ad->pubcert) {
++ /* combine cert + chain into the pubcert */
++ ad->pubcert = apr_array_make(d->p, ad->chain->nelts + 1, sizeof(md_cert_t*));
++ APR_ARRAY_PUSH(ad->pubcert, md_cert_t *) = ad->cert;
++ apr_array_cat(ad->pubcert, ad->chain);
++ rv = md_pubcert_save(d->store, d->p, MD_SG_STAGING, ad->md->name, ad->pubcert, 0);
++ }
++
++ if (APR_SUCCESS == rv && ad->cert) {
++ apr_time_t now = apr_time_now();
++ apr_interval_time_t max_delay, delay_activation;
++
++ /* determine when this cert should be activated */
++ d->stage_valid_from = md_cert_get_not_before(ad->cert);
++ if (d->md->state == MD_S_COMPLETE && d->md->expires > now) {
++ /**
++ * The MD is complete and un-expired. This is a renewal run.
++ * Give activation 24 hours leeway (if we have that time) to
++ * accomodate for clients with somewhat weird clocks.
++ */
++ delay_activation = apr_time_from_sec(MD_SECS_PER_DAY);
++ if (delay_activation > (max_delay = d->md->expires - now)) {
++ delay_activation = max_delay;
++ }
++ d->stage_valid_from += delay_activation;
++ }
++ }
+ }
+ out:
+ return rv;
+@@ -809,13 +894,12 @@
+ /* ACME preload */
+
+ static apr_status_t acme_preload(md_store_t *store, md_store_group_t load_group,
+- const char *name, apr_pool_t *p)
++ const char *name, const char *proxy_url, apr_pool_t *p)
+ {
+ apr_status_t rv;
+- md_pkey_t *pkey, *acct_key;
++ md_pkey_t *privkey, *acct_key;
+ md_t *md;
+- md_cert_t *cert;
+- apr_array_header_t *chain;
++ apr_array_header_t *pubcert;
+ struct md_acme_acct_t *acct;
+
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, 0, p, "%s: preload start", name);
+@@ -832,18 +916,14 @@
+ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading md json", name);
+ return rv;
+ }
+- if (APR_SUCCESS != (rv = md_cert_load(store, MD_SG_STAGING, name, &cert, p))) {
+- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading certificate", name);
++ if (APR_SUCCESS != (rv = md_pkey_load(store, MD_SG_STAGING, name, &privkey, p))) {
++ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading staging private key", name);
+ return rv;
+ }
+- if (APR_SUCCESS != (rv = md_chain_load(store, MD_SG_STAGING, name, &chain, p))) {
+- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading cert chain", name);
++ if (APR_SUCCESS != (rv = md_pubcert_load(store, MD_SG_STAGING, name, &pubcert, p))) {
++ md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading pubcert", name);
+ return rv;
+ }
+- if (APR_SUCCESS != (rv = md_pkey_load(store, MD_SG_STAGING, name, &pkey, p))) {
+- md_log_perror(MD_LOG_MARK, MD_LOG_DEBUG, rv, p, "%s: loading staging private key", name);
+- return rv;
+- }
+
+ /* See if staging holds a new or modified account data */
+ rv = md_acme_acct_load(&acct, &acct_key, store, MD_SG_STAGING, name, p);
+@@ -870,7 +950,7 @@
+ if (acct) {
+ md_acme_t *acme;
+
+- if (APR_SUCCESS != (rv = md_acme_create(&acme, p, md->ca_url))
++ if (APR_SUCCESS != (rv = md_acme_create(&acme, p, md->ca_url, proxy_url))
+ || APR_SUCCESS != (rv = md_acme_acct_save(store, p, acme, acct, acct_key))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: error saving acct", name);
+ return rv;
+@@ -884,16 +964,12 @@
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving md json", name);
+ return rv;
+ }
+- if (APR_SUCCESS != (rv = md_cert_save(store, p, load_group, name, cert, 1))) {
+- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving certificate", name);
+- return rv;
+- }
+- if (APR_SUCCESS != (rv = md_chain_save(store, p, load_group, name, chain, 1))) {
++ if (APR_SUCCESS != (rv = md_pubcert_save(store, p, load_group, name, pubcert, 1))) {
+ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving cert chain", name);
+ return rv;
+ }
+- if (APR_SUCCESS != (rv = md_pkey_save(store, p, load_group, name, pkey, 1))) {
+- md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving domain private key", name);
++ if (APR_SUCCESS != (rv = md_pkey_save(store, p, load_group, name, privkey, 1))) {
++ md_log_perror(MD_LOG_MARK, MD_LOG_ERR, rv, p, "%s: saving private key", name);
+ return rv;
+ }
+
+@@ -906,7 +982,7 @@
+ apr_status_t rv;
+
+ ad->phase = "ACME preload";
+- if (APR_SUCCESS == (rv = acme_preload(d->store, group, d->md->name, d->p))) {
++ if (APR_SUCCESS == (rv = acme_preload(d->store, group, d->md->name, d->proxy_url, d->p))) {
+ ad->phase = "preload done";
+ }
+
+@@ -921,6 +997,7 @@
+
+ apr_status_t md_acme_protos_add(apr_hash_t *protos, apr_pool_t *p)
+ {
++ (void)p;
+ apr_hash_set(protos, MD_PROTO_ACME, sizeof(MD_PROTO_ACME)-1, &ACME_PROTO);
+ return APR_SUCCESS;
+ }
+Index: modules/md/md_core.c
+===================================================================
+--- modules/md/md_core.c (revision 1804530)
++++ modules/md/md_core.c (working copy)
+@@ -24,6 +24,7 @@
+
+ #include "md_json.h"
+ #include "md.h"
++#include "md_crypt.h"
+ #include "md_log.h"
+ #include "md_store.h"
+ #include "md_util.h"
+@@ -84,6 +85,8 @@
+ md->domains = apr_array_make(p, 5, sizeof(const char *));
+ md->contacts = apr_array_make(p, 5, sizeof(const char *));
+ md->drive_mode = MD_DRIVE_DEFAULT;
++ md->require_https = MD_REQUIRE_UNSET;
++ md->must_staple = -1;
+ md->transitive = -1;
+ md->defn_name = "unknown";
+ md->defn_line_number = 0;
+@@ -189,26 +192,45 @@
+ return NULL;
+ }
+
+-const char *md_create(md_t **pmd, apr_pool_t *p, apr_array_header_t *domains)
++md_t *md_create(apr_pool_t *p, apr_array_header_t *domains)
+ {
+ md_t *md;
+
+- if (domains->nelts <= 0) {
+- return "needs at least one domain name";
+- }
+-
+ md = md_create_empty(p);
+- if (!md) {
+- return "not enough memory";
+- }
+-
+ md->domains = md_array_str_compact(p, domains, 0);
+ md->name = APR_ARRAY_IDX(md->domains, 0, const char *);
+
+- *pmd = md;
+- return NULL;
++ return md;
+ }
+
++int md_should_renew(const md_t *md)
++{
++ apr_time_t now = apr_time_now();
++
++ if (md->expires <= now) {
++ return 1;
++ }
++ else if (md->expires > 0) {
++ double renew_win, life;
++ apr_interval_time_t left;
++
++ renew_win = (double)md->renew_window;
++ if (md->renew_norm > 0
++ && md->renew_norm > renew_win
++ && md->expires > md->valid_from) {
++ /* Calc renewal days as fraction of cert lifetime - if known */
++ life = (double)(md->expires - md->valid_from);
++ renew_win = life * renew_win / (double)md->renew_norm;
++ }
++
++ left = md->expires - now;
++ if (left <= renew_win) {
++ return 1;
++ }
++ }
++ return 0;
++}
++
+ /**************************************************************************************************/
+ /* lifetime */
+
+@@ -236,8 +258,12 @@
+ if (md) {
+ md->state = src->state;
+ md->name = apr_pstrdup(p, src->name);
++ md->require_https = src->require_https;
++ md->must_staple = src->must_staple;
+ md->drive_mode = src->drive_mode;
+ md->domains = md_array_str_compact(p, src->domains, 0);
++ md->pkey_spec = src->pkey_spec;
++ md->renew_norm = src->renew_norm;
+ md->renew_window = src->renew_window;
+ md->contacts = md_array_str_clone(p, src->contacts);
+ if (src->ca_url) md->ca_url = apr_pstrdup(p, src->ca_url);
+@@ -254,6 +280,29 @@
+ return md;
+ }
+
++md_t *md_merge(apr_pool_t *p, const md_t *add, const md_t *base)
++{
++ md_t *n = apr_pcalloc(p, sizeof(*n));
++
++ n->ca_url = add->ca_url? add->ca_url : base->ca_url;
++ n->ca_proto = add->ca_proto? add->ca_proto : base->ca_proto;
++ n->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement;
++ n->require_https = (add->require_https != MD_REQUIRE_UNSET)? add->require_https : base->require_https;
++ n->must_staple = (add->must_staple >= 0)? add->must_staple : base->must_staple;
++ n->drive_mode = (add->drive_mode != MD_DRIVE_DEFAULT)? add->drive_mode : base->drive_mode;
++ n->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec;
++ n->renew_norm = (add->renew_norm > 0)? add->renew_norm : base->renew_norm;
++ n->renew_window = (add->renew_window > 0)? add->renew_window : base->renew_window;
++ n->transitive = (add->transitive >= 0)? add->transitive : base->transitive;
++ if (add->ca_challenges) {
++ n->ca_challenges = apr_array_copy(p, add->ca_challenges);
++ }
++ else if (base->ca_challenges) {
++ n->ca_challenges = apr_array_copy(p, base->ca_challenges);
++ }
++ return n;
++}
++
+ /**************************************************************************************************/
+ /* format conversion */
+
+@@ -273,6 +322,9 @@
+ if (md->cert_url) {
+ md_json_sets(md->cert_url, json, MD_KEY_CERT, MD_KEY_URL, NULL);
+ }
++ if (md->pkey_spec) {
++ md_json_setj(md_pkey_spec_to_json(md->pkey_spec, p), json, MD_KEY_PKEY, NULL);
++ }
+ md_json_setl(md->state, json, MD_KEY_STATE, NULL);
+ md_json_setl(md->drive_mode, json, MD_KEY_DRIVE_MODE, NULL);
+ if (md->expires > 0) {
+@@ -280,12 +332,35 @@
+ apr_rfc822_date(ts, md->expires);
+ md_json_sets(ts, json, MD_KEY_CERT, MD_KEY_EXPIRES, NULL);
+ }
[... 5544 lines stripped ...]