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 @@
+ &lt;/VirtualHost&gt;
+         </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">&lt;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 @@
+ &lt;/VirtualHost&gt;
+                 </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
++
++&lt;VirtualHost *:443&gt;
++    ServerName example.org
++    ServerAlias www.example.org
++    DocumentRoot htdocs/root
++
++    SSLEngine on
++&lt;/VirtualHost&gt;
++                </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>&lt;ManagedDomain</name>
+-        <description>Define a group of domains with common properties</description>
+-        <syntax>&lt;ManagedDomain dns-name&gt;...&lt;/ManagedDomain&gt;</syntax>
++    <directivesynopsis type="section" idtype="section">
++        <name>ManagedDomain</name>
++        <description>Container for directives applied to the same managed domains.</description>
++        <syntax>&lt;ManagedDomain <var>dns-name</var> [ <var>other-dns-name</var>... ]&gt;...&lt;/ManagedDomain&gt;</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
+-&lt;/ManagedDomain example.org&gt;
++&lt;/ManagedDomain&gt;
+                 </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">
++&lt;ManagedDomain xxx.yyy&gt;
++  MDRequireHttps temporary
++&lt;/ManagedDomain&gt;
++                </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, &notify_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, &notify_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 ...]