]> granicus.if.org Git - apache/blob - modules/md/mod_md_config.c
d48c10937259f9a9c5e86ea34e350d66a1fd3901
[apache] / modules / md / mod_md_config.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16  
17 #include <assert.h>
18
19 #include <apr_lib.h>
20 #include <apr_strings.h>
21
22 #include <httpd.h>
23 #include <http_core.h>
24 #include <http_config.h>
25 #include <http_log.h>
26 #include <http_vhost.h>
27
28 #include "md.h"
29 #include "md_crypt.h"
30 #include "md_util.h"
31 #include "mod_md_private.h"
32 #include "mod_md_config.h"
33
34 #define MD_CMD_MD             "MDomain"
35 #define MD_CMD_OLD_MD         "ManagedDomain"
36 #define MD_CMD_MD_SECTION     "<MDomainSet"
37 #define MD_CMD_MD_OLD_SECTION "<ManagedDomain"
38 #define MD_CMD_BASE_SERVER    "MDBaseServer"
39 #define MD_CMD_CA             "MDCertificateAuthority"
40 #define MD_CMD_CAAGREEMENT    "MDCertificateAgreement"
41 #define MD_CMD_CACHALLENGES   "MDCAChallenges"
42 #define MD_CMD_CAPROTO        "MDCertificateProtocol"
43 #define MD_CMD_DRIVEMODE      "MDDriveMode"
44 #define MD_CMD_MEMBER         "MDMember"
45 #define MD_CMD_MEMBERS        "MDMembers"
46 #define MD_CMD_MUSTSTAPLE     "MDMustStaple"
47 #define MD_CMD_NOTIFYCMD      "MDNotifyCmd"
48 #define MD_CMD_PORTMAP        "MDPortMap"
49 #define MD_CMD_PKEYS          "MDPrivateKeys"
50 #define MD_CMD_PROXY          "MDHttpProxy"
51 #define MD_CMD_RENEWWINDOW    "MDRenewWindow"
52 #define MD_CMD_REQUIREHTTPS   "MDRequireHttps"
53 #define MD_CMD_STOREDIR       "MDStoreDir"
54
55 #define DEF_VAL     (-1)
56
57 #ifndef MD_DEFAULT_BASE_DIR
58 #define MD_DEFAULT_BASE_DIR "md"
59 #endif
60
61 /* Default settings for the global conf */
62 static md_mod_conf_t defmc = {
63     NULL,
64 #if AP_MODULE_MAGIC_AT_LEAST(20180906, 2)
65     NULL, /* apply default state-dir-relative */
66 #else
67     MD_DEFAULT_BASE_DIR,
68 #endif
69     NULL,
70     NULL,
71     80,
72     443,
73     0,
74     0,
75     0,
76     MD_HSTS_MAX_AGE_DEFAULT,
77     NULL,
78     NULL,
79     NULL,
80 };
81
82 /* Default server specific setting */
83 static md_srv_conf_t defconf = {
84     "default",
85     NULL,
86     &defmc,
87
88     1,
89     MD_REQUIRE_OFF,
90     MD_DRIVE_AUTO,
91     0,
92     NULL, 
93     apr_time_from_sec(90 * MD_SECS_PER_DAY), /* If the cert lifetime were 90 days, renew */
94     apr_time_from_sec(30 * MD_SECS_PER_DAY), /* 30 days before. Adjust to actual lifetime */
95     MD_ACME_DEF_URL,
96     "ACME",
97     NULL,
98     NULL,
99     NULL,
100     NULL,
101 };
102
103 static md_mod_conf_t *mod_md_config;
104
105 static apr_status_t cleanup_mod_config(void *dummy)
106 {
107     (void)dummy;
108     mod_md_config = NULL;
109     return APR_SUCCESS;
110 }
111
112 static md_mod_conf_t *md_mod_conf_get(apr_pool_t *pool, int create)
113 {
114     if (mod_md_config) {
115         return mod_md_config; /* reused for lifetime of the pool */
116     }
117
118     if (create) {
119         mod_md_config = apr_pcalloc(pool, sizeof(*mod_md_config));
120         memcpy(mod_md_config, &defmc, sizeof(*mod_md_config));
121         mod_md_config->mds = apr_array_make(pool, 5, sizeof(const md_t *));
122         mod_md_config->unused_names = apr_array_make(pool, 5, sizeof(const md_t *));
123         
124         apr_pool_cleanup_register(pool, NULL, cleanup_mod_config, apr_pool_cleanup_null);
125     }
126     
127     return mod_md_config;
128 }
129
130 #define CONF_S_NAME(s)  (s && s->server_hostname? s->server_hostname : "default")
131
132 static void srv_conf_props_clear(md_srv_conf_t *sc)
133 {
134     sc->transitive = DEF_VAL;
135     sc->require_https = MD_REQUIRE_UNSET;
136     sc->drive_mode = DEF_VAL;
137     sc->must_staple = DEF_VAL;
138     sc->pkey_spec = NULL;
139     sc->renew_norm = DEF_VAL;
140     sc->renew_window = DEF_VAL;
141     sc->ca_url = NULL;
142     sc->ca_proto = NULL;
143     sc->ca_agreement = NULL;
144     sc->ca_challenges = NULL;
145 }
146
147 static void srv_conf_props_copy(md_srv_conf_t *to, const md_srv_conf_t *from)
148 {
149     to->transitive = from->transitive;
150     to->require_https = from->require_https;
151     to->drive_mode = from->drive_mode;
152     to->must_staple = from->must_staple;
153     to->pkey_spec = from->pkey_spec;
154     to->renew_norm = from->renew_norm;
155     to->renew_window = from->renew_window;
156     to->ca_url = from->ca_url;
157     to->ca_proto = from->ca_proto;
158     to->ca_agreement = from->ca_agreement;
159     to->ca_challenges = from->ca_challenges;
160 }
161
162 static void srv_conf_props_apply(md_t *md, const md_srv_conf_t *from, apr_pool_t *p)
163 {
164     if (from->require_https != MD_REQUIRE_UNSET) md->require_https = from->require_https;
165     if (from->transitive != DEF_VAL) md->transitive = from->transitive;
166     if (from->drive_mode != DEF_VAL) md->drive_mode = from->drive_mode;
167     if (from->must_staple != DEF_VAL) md->must_staple = from->must_staple;
168     if (from->pkey_spec) md->pkey_spec = from->pkey_spec;
169     if (from->renew_norm != DEF_VAL) md->renew_norm = from->renew_norm;
170     if (from->renew_window != DEF_VAL) md->renew_window = from->renew_window;
171
172     if (from->ca_url) md->ca_url = from->ca_url;
173     if (from->ca_proto) md->ca_proto = from->ca_proto;
174     if (from->ca_agreement) md->ca_agreement = from->ca_agreement;
175     if (from->ca_challenges) md->ca_challenges = apr_array_copy(p, from->ca_challenges);
176 }
177
178 void *md_config_create_svr(apr_pool_t *pool, server_rec *s)
179 {
180     md_srv_conf_t *conf = (md_srv_conf_t *)apr_pcalloc(pool, sizeof(md_srv_conf_t));
181
182     conf->name = apr_pstrcat(pool, "srv[", CONF_S_NAME(s), "]", NULL);
183     conf->s = s;
184     conf->mc = md_mod_conf_get(pool, 1);
185
186     srv_conf_props_clear(conf);
187     
188     return conf;
189 }
190
191 static void *md_config_merge(apr_pool_t *pool, void *basev, void *addv)
192 {
193     md_srv_conf_t *base = (md_srv_conf_t *)basev;
194     md_srv_conf_t *add = (md_srv_conf_t *)addv;
195     md_srv_conf_t *nsc;
196     char *name = apr_pstrcat(pool, "[", CONF_S_NAME(add->s), ", ", CONF_S_NAME(base->s), "]", NULL);
197     
198     nsc = (md_srv_conf_t *)apr_pcalloc(pool, sizeof(md_srv_conf_t));
199     nsc->name = name;
200     nsc->mc = add->mc? add->mc : base->mc;
201     nsc->assigned = add->assigned? add->assigned : base->assigned;
202
203     nsc->transitive = (add->transitive != DEF_VAL)? add->transitive : base->transitive;
204     nsc->require_https = (add->require_https != MD_REQUIRE_UNSET)? add->require_https : base->require_https;
205     nsc->drive_mode = (add->drive_mode != DEF_VAL)? add->drive_mode : base->drive_mode;
206     nsc->must_staple = (add->must_staple != DEF_VAL)? add->must_staple : base->must_staple;
207     nsc->pkey_spec = add->pkey_spec? add->pkey_spec : base->pkey_spec;
208     nsc->renew_window = (add->renew_norm != DEF_VAL)? add->renew_norm : base->renew_norm;
209     nsc->renew_window = (add->renew_window != DEF_VAL)? add->renew_window : base->renew_window;
210
211     nsc->ca_url = add->ca_url? add->ca_url : base->ca_url;
212     nsc->ca_proto = add->ca_proto? add->ca_proto : base->ca_proto;
213     nsc->ca_agreement = add->ca_agreement? add->ca_agreement : base->ca_agreement;
214     nsc->ca_challenges = (add->ca_challenges? apr_array_copy(pool, add->ca_challenges) 
215                     : (base->ca_challenges? apr_array_copy(pool, base->ca_challenges) : NULL));
216     nsc->current = NULL;
217     nsc->assigned = NULL;
218     
219     return nsc;
220 }
221
222 void *md_config_merge_svr(apr_pool_t *pool, void *basev, void *addv)
223 {
224     return md_config_merge(pool, basev, addv);
225 }
226
227 static int inside_section(cmd_parms *cmd, const char *section) {
228     ap_directive_t *d;
229     for (d = cmd->directive->parent; d; d = d->parent) {
230        if (!ap_cstr_casecmp(d->directive, section)) {
231            return 1;
232        }
233     }
234     return 0; 
235 }
236
237 static int inside_md_section(cmd_parms *cmd) {
238     return (inside_section(cmd, MD_CMD_MD_SECTION) || inside_section(cmd, MD_CMD_MD_OLD_SECTION));
239 }
240
241 static const char *md_section_check(cmd_parms *cmd) {
242     if (!inside_md_section(cmd)) {
243         return apr_pstrcat(cmd->pool, cmd->cmd->name, " is only valid inside a '",  
244                            MD_CMD_MD_SECTION, "' context, not here", NULL);
245     }
246     return NULL;
247 }
248
249 static void add_domain_name(apr_array_header_t *domains, const char *name, apr_pool_t *p)
250 {
251     if (md_array_str_index(domains, name, 0, 0) < 0) {
252         APR_ARRAY_PUSH(domains, char *) = md_util_str_tolower(apr_pstrdup(p, name));
253     }
254 }
255
256 static const char *set_transitive(int *ptransitive, const char *value)
257 {
258     if (!apr_strnatcasecmp("auto", value)) {
259         *ptransitive = 1;
260         return NULL;
261     }
262     else if (!apr_strnatcasecmp("manual", value)) {
263         *ptransitive = 0;
264         return NULL;
265     }
266     return "unknown value, use \"auto|manual\"";
267 }
268
269 static const char *md_config_sec_start(cmd_parms *cmd, void *mconfig, const char *arg)
270 {
271     md_srv_conf_t *sc;
272     md_srv_conf_t save;
273     const char *endp;
274     const char *err, *name;
275     apr_array_header_t *domains;
276     md_t *md;
277     int transitive = -1;
278     
279     (void)mconfig;
280     if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
281         return err;
282     }
283         
284     sc = md_config_get(cmd->server);
285     endp = ap_strrchr_c(arg, '>');
286     if (endp == NULL) {
287         return  MD_CMD_MD_SECTION "> directive missing closing '>'";
288     }
289
290     arg = apr_pstrndup(cmd->pool, arg, (apr_size_t)(endp-arg));
291     if (!arg || !*arg) {
292         return MD_CMD_MD_SECTION " > section must specify a unique domain name";
293     }
294
295     name = ap_getword_white(cmd->pool, &arg);
296     domains = apr_array_make(cmd->pool, 5, sizeof(const char *));
297     add_domain_name(domains, name, cmd->pool);
298     while (*arg != '\0') {
299         name = ap_getword_white(cmd->pool, &arg);
300         if (NULL != set_transitive(&transitive, name)) {
301             add_domain_name(domains, name, cmd->pool);
302         }
303     }
304
305     if (domains->nelts == 0) {
306         return "needs at least one domain name";
307     }
308     
309     md = md_create(cmd->pool, domains);
310     if (transitive >= 0) {
311         md->transitive = transitive;
312     }
313     
314     /* Save the current settings in this srv_conf and apply+restore at the
315      * end of this section */
316     memcpy(&save, sc, sizeof(save));
317     srv_conf_props_clear(sc);
318     sc->current = md;
319     
320     if (NULL == (err = ap_walk_config(cmd->directive->first_child, cmd, cmd->context))) {
321         srv_conf_props_apply(md, sc, cmd->pool);
322         APR_ARRAY_PUSH(sc->mc->mds, const md_t *) = md;
323     }
324     
325     sc->current = NULL;
326     srv_conf_props_copy(sc, &save);
327     
328     return err;
329 }
330
331 static const char *md_config_sec_add_members(cmd_parms *cmd, void *dc, 
332                                              int argc, char *const argv[])
333 {
334     md_srv_conf_t *sc = md_config_get(cmd->server);
335     const char *err;
336     int i;
337     
338     (void)dc;
339     if (NULL != (err = md_section_check(cmd))) {
340         if (argc == 1) {
341             /* only these values are allowed outside a section */
342             return set_transitive(&sc->transitive, argv[0]);
343         }
344         return err;
345     }
346     
347     assert(sc->current);
348     for (i = 0; i < argc; ++i) {
349         if (NULL != set_transitive(&sc->transitive, argv[i])) {
350             add_domain_name(sc->current->domains, argv[i], cmd->pool);
351         }
352     }
353     return NULL;
354 }
355
356 static const char *md_config_set_names(cmd_parms *cmd, void *dc, 
357                                        int argc, char *const argv[])
358 {
359     md_srv_conf_t *sc = md_config_get(cmd->server);
360     apr_array_header_t *domains = apr_array_make(cmd->pool, 5, sizeof(const char *));
361     const char *err;
362     md_t *md;
363     int i, transitive = -1;
364
365     (void)dc;
366     err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE);
367     if (err) {
368         return err;
369     }
370
371     for (i = 0; i < argc; ++i) {
372         if (NULL != set_transitive(&transitive, argv[i])) {
373             add_domain_name(domains, argv[i], cmd->pool);
374         }
375     }
376     
377     if (domains->nelts == 0) {
378         return "needs at least one domain name";
379     }
380     md = md_create(cmd->pool, domains);
381
382     if (transitive >= 0) {
383         md->transitive = transitive;
384     }
385     
386     if (cmd->config_file) {
387         md->defn_name = cmd->config_file->name;
388         md->defn_line_number = cmd->config_file->line_number;
389     }
390
391     APR_ARRAY_PUSH(sc->mc->mds, md_t *) = md;
392
393     return NULL;
394 }
395
396 static const char *md_config_set_ca(cmd_parms *cmd, void *dc, const char *value)
397 {
398     md_srv_conf_t *sc = md_config_get(cmd->server);
399     const char *err;
400
401     (void)dc;
402     if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
403         return err;
404     }
405     sc->ca_url = value;
406     return NULL;
407 }
408
409 static const char *md_config_set_ca_proto(cmd_parms *cmd, void *dc, const char *value)
410 {
411     md_srv_conf_t *config = md_config_get(cmd->server);
412     const char *err;
413
414     (void)dc;
415     if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
416         return err;
417     }
418     config->ca_proto = value;
419     return NULL;
420 }
421
422 static const char *md_config_set_agreement(cmd_parms *cmd, void *dc, const char *value)
423 {
424     md_srv_conf_t *config = md_config_get(cmd->server);
425     const char *err;
426
427     (void)dc;
428     if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
429         return err;
430     }
431     config->ca_agreement = value;
432     return NULL;
433 }
434
435 static const char *md_config_set_drive_mode(cmd_parms *cmd, void *dc, const char *value)
436 {
437     md_srv_conf_t *config = md_config_get(cmd->server);
438     const char *err;
439     md_drive_mode_t drive_mode;
440
441     (void)dc;
442     if (!apr_strnatcasecmp("auto", value) || !apr_strnatcasecmp("automatic", value)) {
443         drive_mode = MD_DRIVE_AUTO;
444     }
445     else if (!apr_strnatcasecmp("always", value)) {
446         drive_mode = MD_DRIVE_ALWAYS;
447     }
448     else if (!apr_strnatcasecmp("manual", value) || !apr_strnatcasecmp("stick", value)) {
449         drive_mode = MD_DRIVE_MANUAL;
450     }
451     else {
452         return apr_pstrcat(cmd->pool, "unknown MDDriveMode ", value, NULL);
453     }
454     
455     if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
456         return err;
457     }
458     config->drive_mode = drive_mode;
459     return NULL;
460 }
461
462 static const char *md_config_set_must_staple(cmd_parms *cmd, void *dc, const char *value)
463 {
464     md_srv_conf_t *config = md_config_get(cmd->server);
465     const char *err;
466
467     (void)dc;
468     if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
469         return err;
470     }
471
472     if (!apr_strnatcasecmp("off", value)) {
473         config->must_staple = 0;
474     }
475     else if (!apr_strnatcasecmp("on", value)) {
476         config->must_staple = 1;
477     }
478     else {
479         return apr_pstrcat(cmd->pool, "unknown '", value, 
480                            "', supported parameter values are 'on' and 'off'", NULL);
481     }
482     return NULL;
483 }
484
485 static const char *md_config_set_base_server(cmd_parms *cmd, void *dc, const char *value)
486 {
487     md_srv_conf_t *config = md_config_get(cmd->server);
488     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
489
490     (void)dc;
491     if (!err) {
492         if (!apr_strnatcasecmp("off", value)) {
493             config->mc->manage_base_server = 0;
494         }
495         else if (!apr_strnatcasecmp("on", value)) {
496             config->mc->manage_base_server = 1;
497         }
498         else {
499             err = apr_pstrcat(cmd->pool, "unknown '", value, 
500                               "', supported parameter values are 'on' and 'off'", NULL);
501         }
502     }
503     return err;
504 }
505
506 static const char *md_config_set_require_https(cmd_parms *cmd, void *dc, const char *value)
507 {
508     md_srv_conf_t *config = md_config_get(cmd->server);
509     const char *err;
510
511     (void)dc;
512     if (!inside_md_section(cmd) && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
513         return err;
514     }
515
516     if (!apr_strnatcasecmp("off", value)) {
517         config->require_https = MD_REQUIRE_OFF;
518     }
519     else if (!apr_strnatcasecmp(MD_KEY_TEMPORARY, value)) {
520         config->require_https = MD_REQUIRE_TEMPORARY;
521     }
522     else if (!apr_strnatcasecmp(MD_KEY_PERMANENT, value)) {
523         config->require_https = MD_REQUIRE_PERMANENT;
524     }
525     else {
526         return apr_pstrcat(cmd->pool, "unknown '", value, 
527                            "', supported parameter values are 'temporary' and 'permanent'", NULL);
528     }
529     return NULL;
530 }
531
532 static apr_status_t duration_parse(const char *value, apr_interval_time_t *ptimeout, 
533                                    const char *def_unit)
534 {
535     char *endp;
536     long funits = 1;
537     apr_status_t rv;
538     apr_int64_t n;
539     
540     n = apr_strtoi64(value, &endp, 10);
541     if (errno) {
542         return errno;
543     }
544     if (!endp || !*endp) {
545         if (strcmp(def_unit, "d") == 0) {
546             def_unit = "s";
547             funits = MD_SECS_PER_DAY;
548         }
549     }
550     else if (endp == value) {
551         return APR_EINVAL;
552     }
553     else if (*endp == 'd') {
554         *ptimeout = apr_time_from_sec(n * MD_SECS_PER_DAY);
555         return APR_SUCCESS;
556     }
557     else {
558         def_unit = endp;
559     }
560     rv = ap_timeout_parameter_parse(value, ptimeout, def_unit);
561     if (APR_SUCCESS == rv && funits > 1) {
562         *ptimeout *= funits;
563     }
564     return rv;
565 }
566
567 static apr_status_t percentage_parse(const char *value, int *ppercent)
568 {
569     char *endp;
570     apr_int64_t n;
571     
572     n = apr_strtoi64(value, &endp, 10);
573     if (errno) {
574         return errno;
575     }
576     if (*endp == '%') {
577         if (n < 0 || n >= 100) {
578             return APR_BADARG;
579         }
580         *ppercent = (int)n;
581         return APR_SUCCESS;
582     }
583     return APR_EINVAL;
584 }
585
586 static const char *md_config_set_renew_window(cmd_parms *cmd, void *dc, const char *value)
587 {
588     md_srv_conf_t *config = md_config_get(cmd->server);
589     const char *err;
590     apr_interval_time_t timeout;
591     int percent = 0;
592     
593     (void)dc;
594     if (!inside_md_section(cmd)
595         && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
596         return err;
597     }
598
599     /* Inspired by http_core.c */
600     if (duration_parse(value, &timeout, "d") == APR_SUCCESS) {
601         config->renew_norm = 0;
602         config->renew_window = timeout;
603         return NULL;
604     }
605     else {
606         switch (percentage_parse(value, &percent)) {
607             case APR_SUCCESS:
608                 config->renew_norm = apr_time_from_sec(100 * MD_SECS_PER_DAY);
609                 config->renew_window = apr_time_from_sec(percent * MD_SECS_PER_DAY);
610                 return NULL;
611             case APR_BADARG:
612                 return "MDRenewWindow as percent must be less than 100";
613         }
614     }
615     return "MDRenewWindow has unrecognized format";
616 }
617
618 static const char *md_config_set_proxy(cmd_parms *cmd, void *arg, const char *value)
619 {
620     md_srv_conf_t *sc = md_config_get(cmd->server);
621     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
622
623     if (err) {
624         return err;
625     }
626     md_util_abs_http_uri_check(cmd->pool, value, &err);
627     if (err) {
628         return err;
629     }
630     sc->mc->proxy_url = value;
631     (void)arg;
632     return NULL;
633 }
634
635 static const char *md_config_set_store_dir(cmd_parms *cmd, void *arg, const char *value)
636 {
637     md_srv_conf_t *sc = md_config_get(cmd->server);
638     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
639
640     if (err) {
641         return err;
642     }
643     sc->mc->base_dir = value;
644     (void)arg;
645     return NULL;
646 }
647
648 static const char *set_port_map(md_mod_conf_t *mc, const char *value)
649 {
650     int net_port, local_port;
651     char *endp;
652
653     net_port = (int)apr_strtoi64(value, &endp, 10);
654     if (errno) {
655         return "unable to parse first port number";
656     }
657     if (!endp || *endp != ':') {
658         return "no ':' after first port number";
659     }
660     ++endp;
661     if (*endp == '-') {
662         local_port = 0;
663     }
664     else {
665         local_port = (int)apr_strtoi64(endp, &endp, 10);
666         if (errno) {
667             return "unable to parse second port number";
668         }
669         if (local_port <= 0 || local_port > 65535) {
670             return "invalid number for port map, must be in ]0,65535]";
671         }
672     }
673     switch (net_port) {
674         case 80:
675             mc->local_80 = local_port;
676             break;
677         case 443:
678             mc->local_443 = local_port;
679             break;
680         default:
681             return "mapped port number must be 80 or 443";
682     }
683     return NULL;
684 }
685
686 static const char *md_config_set_port_map(cmd_parms *cmd, void *arg, 
687                                           const char *v1, const char *v2)
688 {
689     md_srv_conf_t *sc = md_config_get(cmd->server);
690     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
691
692     (void)arg;
693     if (!err) {
694         err = set_port_map(sc->mc, v1);
695     }
696     if (!err && v2) {
697         err = set_port_map(sc->mc, v2);
698     }
699     return err;
700 }
701
702 static const char *md_config_set_cha_tyes(cmd_parms *cmd, void *dc, 
703                                           int argc, char *const argv[])
704 {
705     md_srv_conf_t *config = md_config_get(cmd->server);
706     apr_array_header_t **pcha, *ca_challenges;
707     const char *err;
708     int i;
709
710     (void)dc;
711     if (!inside_md_section(cmd)
712         && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
713         return err;
714     }
715     pcha = &config->ca_challenges; 
716     
717     ca_challenges = *pcha;
718     if (!ca_challenges) {
719         *pcha = ca_challenges = apr_array_make(cmd->pool, 5, sizeof(const char *));
720     }
721     for (i = 0; i < argc; ++i) {
722         APR_ARRAY_PUSH(ca_challenges, const char *) = argv[i];
723     }
724     
725     return NULL;
726 }
727
728 static const char *md_config_set_pkeys(cmd_parms *cmd, void *dc, 
729                                        int argc, char *const argv[])
730 {
731     md_srv_conf_t *config = md_config_get(cmd->server);
732     const char *err, *ptype;
733     apr_int64_t bits;
734     
735     (void)dc;
736     if (!inside_md_section(cmd)
737         && (err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
738         return err;
739     }
740     if (argc <= 0) {
741         return "needs to specify the private key type";
742     }
743     
744     ptype = argv[0];
745     if (!apr_strnatcasecmp("Default", ptype)) {
746         if (argc > 1) {
747             return "type 'Default' takes no parameter";
748         }
749         if (!config->pkey_spec) {
750             config->pkey_spec = apr_pcalloc(cmd->pool, sizeof(*config->pkey_spec));
751         }
752         config->pkey_spec->type = MD_PKEY_TYPE_DEFAULT;
753         return NULL;
754     }
755     else if (!apr_strnatcasecmp("RSA", ptype)) {
756         if (argc == 1) {
757             bits = MD_PKEY_RSA_BITS_DEF;
758         }
759         else if (argc == 2) {
760             bits = (int)apr_atoi64(argv[1]);
761             if (bits < MD_PKEY_RSA_BITS_MIN || bits >= INT_MAX) {
762                 return apr_psprintf(cmd->pool, "must be %d or higher in order to be considered "
763                 "safe. Too large a value will slow down everything. Larger then 4096 probably does "
764                 "not make sense unless quantum cryptography really changes spin.", 
765                 MD_PKEY_RSA_BITS_MIN);
766             }
767         }
768         else {
769             return "key type 'RSA' has only one optional parameter, the number of bits";
770         }
771
772         if (!config->pkey_spec) {
773             config->pkey_spec = apr_pcalloc(cmd->pool, sizeof(*config->pkey_spec));
774         }
775         config->pkey_spec->type = MD_PKEY_TYPE_RSA;
776         config->pkey_spec->params.rsa.bits = (unsigned int)bits;
777         return NULL;
778     }
779     return apr_pstrcat(cmd->pool, "unsupported private key type \"", ptype, "\"", NULL);
780 }
781
782 static const char *md_config_set_notify_cmd(cmd_parms *cmd, void *mconfig, const char *arg)
783 {
784     md_srv_conf_t *sc = md_config_get(cmd->server);
785     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
786
787     if (err) {
788         return err;
789     }
790     sc->mc->notify_cmd = arg;
791     (void)mconfig;
792     return NULL;
793 }
794
795 static const char *md_config_set_names_old(cmd_parms *cmd, void *dc, 
796                                            int argc, char *const argv[])
797 {
798     ap_log_error( APLOG_MARK, APLOG_WARNING, 0, cmd->server,  
799                  "mod_md: directive 'ManagedDomain' is deprecated, replace with 'MDomain'.");
800     return md_config_set_names(cmd, dc, argc, argv);
801 }
802
803 static const char *md_config_sec_start_old(cmd_parms *cmd, void *mconfig, const char *arg)
804 {
805     ap_log_error( APLOG_MARK, APLOG_WARNING, 0, cmd->server,  
806                  "mod_md: directive '<ManagedDomain' is deprecated, replace with '<MDomainSet'.");
807     return md_config_sec_start(cmd, mconfig, arg);
808 }
809
810 const command_rec md_cmds[] = {
811     AP_INIT_TAKE1(     MD_CMD_CA, md_config_set_ca, NULL, RSRC_CONF, 
812                   "URL of CA issuing the certificates"),
813     AP_INIT_TAKE1(     MD_CMD_CAAGREEMENT, md_config_set_agreement, NULL, RSRC_CONF, 
814                   "URL of CA Terms-of-Service agreement you accept"),
815     AP_INIT_TAKE_ARGV( MD_CMD_CACHALLENGES, md_config_set_cha_tyes, NULL, RSRC_CONF, 
816                       "A list of challenge types to be used."),
817     AP_INIT_TAKE1(     MD_CMD_CAPROTO, md_config_set_ca_proto, NULL, RSRC_CONF, 
818                   "Protocol used to obtain/renew certificates"),
819     AP_INIT_TAKE1(     MD_CMD_DRIVEMODE, md_config_set_drive_mode, NULL, RSRC_CONF, 
820                   "method of obtaining certificates for the managed domain"),
821     AP_INIT_TAKE_ARGV( MD_CMD_MD, md_config_set_names, NULL, RSRC_CONF, 
822                       "A group of server names with one certificate"),
823     AP_INIT_RAW_ARGS(  MD_CMD_MD_SECTION, md_config_sec_start, NULL, RSRC_CONF, 
824                      "Container for a managed domain with common settings and certificate."),
825     AP_INIT_TAKE_ARGV( MD_CMD_MEMBER, md_config_sec_add_members, NULL, RSRC_CONF, 
826                       "Define domain name(s) part of the Managed Domain. Use 'auto' or "
827                       "'manual' to enable/disable auto adding names from virtual hosts."),
828     AP_INIT_TAKE_ARGV( MD_CMD_MEMBERS, md_config_sec_add_members, NULL, RSRC_CONF, 
829                       "Define domain name(s) part of the Managed Domain. Use 'auto' or "
830                       "'manual' to enable/disable auto adding names from virtual hosts."),
831     AP_INIT_TAKE1(     MD_CMD_MUSTSTAPLE, md_config_set_must_staple, NULL, RSRC_CONF, 
832                   "Enable/Disable the Must-Staple flag for new certificates."),
833     AP_INIT_TAKE12(    MD_CMD_PORTMAP, md_config_set_port_map, NULL, RSRC_CONF, 
834                   "Declare the mapped ports 80 and 443 on the local server. E.g. 80:8000 "
835                   "to indicate that the server port 8000 is reachable as port 80 from the "
836                   "internet. Use 80:- to indicate that port 80 is not reachable from "
837                   "the outside."),
838     AP_INIT_TAKE_ARGV( MD_CMD_PKEYS, md_config_set_pkeys, NULL, RSRC_CONF, 
839                   "set the type and parameters for private key generation"),
840     AP_INIT_TAKE1(     MD_CMD_PROXY, md_config_set_proxy, NULL, RSRC_CONF, 
841                   "URL of a HTTP(S) proxy to use for outgoing connections"),
842     AP_INIT_TAKE1(     MD_CMD_STOREDIR, md_config_set_store_dir, NULL, RSRC_CONF, 
843                   "the directory for file system storage of managed domain data."),
844     AP_INIT_TAKE1(     MD_CMD_RENEWWINDOW, md_config_set_renew_window, NULL, RSRC_CONF, 
845                   "Time length for renewal before certificate expires (defaults to days)"),
846     AP_INIT_TAKE1(     MD_CMD_REQUIREHTTPS, md_config_set_require_https, NULL, RSRC_CONF, 
847                   "Redirect non-secure requests to the https: equivalent."),
848     AP_INIT_RAW_ARGS(MD_CMD_NOTIFYCMD, md_config_set_notify_cmd, NULL, RSRC_CONF, 
849                   "set the command and optional arguments to run when signup/renew of domain is complete."),
850     AP_INIT_TAKE1(     MD_CMD_BASE_SERVER, md_config_set_base_server, NULL, RSRC_CONF, 
851                   "allow managing of base server outside virtual hosts."),
852
853 /* This will disappear soon */
854     AP_INIT_TAKE_ARGV( MD_CMD_OLD_MD, md_config_set_names_old, NULL, RSRC_CONF, 
855                       "Deprecated, replace with 'MDomain'."),
856     AP_INIT_RAW_ARGS(  MD_CMD_MD_OLD_SECTION, md_config_sec_start_old, NULL, RSRC_CONF, 
857                      "Deprecated, replace with '<MDomainSet'."),
858 /* */
859
860     AP_INIT_TAKE1(NULL, NULL, NULL, RSRC_CONF, NULL)
861 };
862
863 apr_status_t md_config_post_config(server_rec *s, apr_pool_t *p)
864 {
865     md_srv_conf_t *sc;
866     md_mod_conf_t *mc;
867
868     sc = md_config_get(s);
869     mc = sc->mc;
870
871     mc->hsts_header = NULL;
872     if (mc->hsts_max_age > 0) {
873         mc->hsts_header = apr_psprintf(p, "max-age=%d", mc->hsts_max_age);
874     }
875
876 #if AP_MODULE_MAGIC_AT_LEAST(20180906, 2)
877     if (mc->base_dir == NULL) {
878         mc->base_dir = ap_state_dir_relative(p, MD_DEFAULT_BASE_DIR);
879     }
880 #endif
881     
882     return APR_SUCCESS;
883 }
884
885 static md_srv_conf_t *config_get_int(server_rec *s, apr_pool_t *p)
886 {
887     md_srv_conf_t *sc = (md_srv_conf_t *)ap_get_module_config(s->module_config, &md_module);
888     ap_assert(sc);
889     if (sc->s != s && p) {
890         sc = md_config_merge(p, &defconf, sc);
891         sc->name = apr_pstrcat(p, CONF_S_NAME(s), sc->name, NULL);
892         sc->mc = md_mod_conf_get(p, 1);
893         ap_set_module_config(s->module_config, &md_module, sc);
894     }
895     return sc;
896 }
897
898 md_srv_conf_t *md_config_get(server_rec *s)
899 {
900     return config_get_int(s, NULL);
901 }
902
903 md_srv_conf_t *md_config_get_unique(server_rec *s, apr_pool_t *p)
904 {
905     assert(p);
906     return config_get_int(s, p);
907 }
908
909 md_srv_conf_t *md_config_cget(conn_rec *c)
910 {
911     return md_config_get(c->base_server);
912 }
913
914 const char *md_config_gets(const md_srv_conf_t *sc, md_config_var_t var)
915 {
916     switch (var) {
917         case MD_CONFIG_CA_URL:
918             return sc->ca_url? sc->ca_url : defconf.ca_url;
919         case MD_CONFIG_CA_PROTO:
920             return sc->ca_proto? sc->ca_proto : defconf.ca_proto;
921         case MD_CONFIG_BASE_DIR:
922             return sc->mc->base_dir;
923         case MD_CONFIG_PROXY:
924             return sc->mc->proxy_url;
925         case MD_CONFIG_CA_AGREEMENT:
926             return sc->ca_agreement? sc->ca_agreement : defconf.ca_agreement;
927         case MD_CONFIG_NOTIFY_CMD:
928             return sc->mc->notify_cmd;
929         default:
930             return NULL;
931     }
932 }
933
934 int md_config_geti(const md_srv_conf_t *sc, md_config_var_t var)
935 {
936     switch (var) {
937         case MD_CONFIG_DRIVE_MODE:
938             return (sc->drive_mode != DEF_VAL)? sc->drive_mode : defconf.drive_mode;
939         case MD_CONFIG_LOCAL_80:
940             return sc->mc->local_80;
941         case MD_CONFIG_LOCAL_443:
942             return sc->mc->local_443;
943         case MD_CONFIG_TRANSITIVE:
944             return (sc->transitive != DEF_VAL)? sc->transitive : defconf.transitive;
945         case MD_CONFIG_REQUIRE_HTTPS:
946             return (sc->require_https != MD_REQUIRE_UNSET)? sc->require_https : defconf.require_https;
947         case MD_CONFIG_MUST_STAPLE:
948             return (sc->must_staple != DEF_VAL)? sc->must_staple : defconf.must_staple;
949         default:
950             return 0;
951     }
952 }
953
954 apr_interval_time_t md_config_get_interval(const md_srv_conf_t *sc, md_config_var_t var)
955 {
956     switch (var) {
957         case MD_CONFIG_RENEW_NORM:
958             return (sc->renew_norm != DEF_VAL)? sc->renew_norm : defconf.renew_norm;
959         case MD_CONFIG_RENEW_WINDOW:
960             return (sc->renew_window != DEF_VAL)? sc->renew_window : defconf.renew_window;
961         default:
962             return 0;
963     }
964 }