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 #include <stdlib.h>
19 
20 #include <apr_lib.h>
21 #include <apr_strings.h>
22 #include <apr_uri.h>
23 #include <apr_tables.h>
24 #include <apr_time.h>
25 #include <apr_date.h>
26 
27 #include "md_json.h"
28 #include "md.h"
29 #include "md_crypt.h"
30 #include "md_log.h"
31 #include "md_store.h"
32 #include "md_util.h"
33 
34 
md_contains(const md_t * md,const char * domain,int case_sensitive)35 int md_contains(const md_t *md, const char *domain, int case_sensitive)
36 {
37     if (md_array_str_index(md->domains, domain, 0, case_sensitive) >= 0) {
38         return 1;
39     }
40     return md_dns_domains_match(md->domains, domain);
41 }
42 
md_common_name(const md_t * md1,const md_t * md2)43 const char *md_common_name(const md_t *md1, const md_t *md2)
44 {
45     int i;
46 
47     if (md1 == NULL || md1->domains == NULL
48         || md2 == NULL || md2->domains == NULL) {
49         return NULL;
50     }
51 
52     for (i = 0; i < md1->domains->nelts; ++i) {
53         const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
54         if (md_contains(md2, name1, 0)) {
55             return name1;
56         }
57     }
58     return NULL;
59 }
60 
md_domains_overlap(const md_t * md1,const md_t * md2)61 int md_domains_overlap(const md_t *md1, const md_t *md2)
62 {
63     return md_common_name(md1, md2) != NULL;
64 }
65 
md_common_name_count(const md_t * md1,const md_t * md2)66 apr_size_t md_common_name_count(const md_t *md1, const md_t *md2)
67 {
68     int i;
69     apr_size_t hits;
70 
71     if (md1 == NULL || md1->domains == NULL
72         || md2 == NULL || md2->domains == NULL) {
73         return 0;
74     }
75 
76     hits = 0;
77     for (i = 0; i < md1->domains->nelts; ++i) {
78         const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
79         if (md_contains(md2, name1, 0)) {
80             ++hits;
81         }
82     }
83     return hits;
84 }
85 
md_is_covered_by_alt_names(const md_t * md,const struct apr_array_header_t * alt_names)86 int md_is_covered_by_alt_names(const md_t *md, const struct apr_array_header_t* alt_names)
87 {
88     const char *name;
89     int i;
90 
91     if (alt_names) {
92         for (i = 0; i < md->domains->nelts; ++i) {
93             name = APR_ARRAY_IDX(md->domains, i, const char *);
94             if (!md_dns_domains_match(alt_names, name)) {
95                 return 0;
96             }
97         }
98         return 1;
99     }
100     return 0;
101 }
102 
md_create_empty(apr_pool_t * p)103 md_t *md_create_empty(apr_pool_t *p)
104 {
105     md_t *md = apr_pcalloc(p, sizeof(*md));
106     if (md) {
107         md->domains = apr_array_make(p, 5, sizeof(const char *));
108         md->contacts = apr_array_make(p, 5, sizeof(const char *));
109         md->renew_mode = MD_RENEW_DEFAULT;
110         md->require_https = MD_REQUIRE_UNSET;
111         md->must_staple = -1;
112         md->transitive = -1;
113         md->acme_tls_1_domains = apr_array_make(p, 5, sizeof(const char *));
114         md->stapling = -1;
115         md->defn_name = "unknown";
116         md->defn_line_number = 0;
117     }
118     return md;
119 }
120 
md_equal_domains(const md_t * md1,const md_t * md2,int case_sensitive)121 int md_equal_domains(const md_t *md1, const md_t *md2, int case_sensitive)
122 {
123     int i;
124     if (md1->domains->nelts == md2->domains->nelts) {
125         for (i = 0; i < md1->domains->nelts; ++i) {
126             const char *name1 = APR_ARRAY_IDX(md1->domains, i, const char*);
127             if (!md_contains(md2, name1, case_sensitive)) {
128                 return 0;
129             }
130         }
131         return 1;
132     }
133     return 0;
134 }
135 
md_contains_domains(const md_t * md1,const md_t * md2)136 int md_contains_domains(const md_t *md1, const md_t *md2)
137 {
138     int i;
139     if (md1->domains->nelts >= md2->domains->nelts) {
140         for (i = 0; i < md2->domains->nelts; ++i) {
141             const char *name2 = APR_ARRAY_IDX(md2->domains, i, const char*);
142             if (!md_contains(md1, name2, 0)) {
143                 return 0;
144             }
145         }
146         return 1;
147     }
148     return 0;
149 }
150 
md_get_by_name(struct apr_array_header_t * mds,const char * name)151 md_t *md_get_by_name(struct apr_array_header_t *mds, const char *name)
152 {
153     int i;
154     for (i = 0; i < mds->nelts; ++i) {
155         md_t *md = APR_ARRAY_IDX(mds, i, md_t *);
156         if (!strcmp(name, md->name)) {
157             return md;
158         }
159     }
160     return NULL;
161 }
162 
md_get_by_domain(struct apr_array_header_t * mds,const char * domain)163 md_t *md_get_by_domain(struct apr_array_header_t *mds, const char *domain)
164 {
165     int i;
166     for (i = 0; i < mds->nelts; ++i) {
167         md_t *md = APR_ARRAY_IDX(mds, i, md_t *);
168         if (md_contains(md, domain, 0)) {
169             return md;
170         }
171     }
172     return NULL;
173 }
174 
md_get_by_dns_overlap(struct apr_array_header_t * mds,const md_t * md)175 md_t *md_get_by_dns_overlap(struct apr_array_header_t *mds, const md_t *md)
176 {
177     int i;
178     for (i = 0; i < mds->nelts; ++i) {
179         md_t *o = APR_ARRAY_IDX(mds, i, md_t *);
180         if (strcmp(o->name, md->name) && md_common_name(o, md)) {
181             return o;
182         }
183     }
184     return NULL;
185 }
186 
md_cert_count(const md_t * md)187 int md_cert_count(const md_t *md)
188 {
189     /* cert are defined as a list of static files or a list of private key specs */
190     if (md->cert_files && md->cert_files->nelts) {
191         return md->cert_files->nelts;
192     }
193     return md_pkeys_spec_count(md->pks);
194 }
195 
md_create(apr_pool_t * p,apr_array_header_t * domains)196 md_t *md_create(apr_pool_t *p, apr_array_header_t *domains)
197 {
198     md_t *md;
199 
200     md = md_create_empty(p);
201     md->domains = md_array_str_compact(p, domains, 0);
202     md->name = APR_ARRAY_IDX(md->domains, 0, const char *);
203 
204     return md;
205 }
206 
207 /**************************************************************************************************/
208 /* lifetime */
209 
md_copy(apr_pool_t * p,const md_t * src)210 md_t *md_copy(apr_pool_t *p, const md_t *src)
211 {
212     md_t *md;
213 
214     md = apr_pcalloc(p, sizeof(*md));
215     if (md) {
216         memcpy(md, src, sizeof(*md));
217         md->domains = apr_array_copy(p, src->domains);
218         md->contacts = apr_array_copy(p, src->contacts);
219         if (src->ca_challenges) {
220             md->ca_challenges = apr_array_copy(p, src->ca_challenges);
221         }
222         md->acme_tls_1_domains = apr_array_copy(p, src->acme_tls_1_domains);
223         md->pks = md_pkeys_spec_clone(p, src->pks);
224     }
225     return md;
226 }
227 
md_clone(apr_pool_t * p,const md_t * src)228 md_t *md_clone(apr_pool_t *p, const md_t *src)
229 {
230     md_t *md;
231 
232     md = apr_pcalloc(p, sizeof(*md));
233     if (md) {
234         md->state = src->state;
235         md->name = apr_pstrdup(p, src->name);
236         md->require_https = src->require_https;
237         md->must_staple = src->must_staple;
238         md->renew_mode = src->renew_mode;
239         md->domains = md_array_str_compact(p, src->domains, 0);
240         md->pks = md_pkeys_spec_clone(p, src->pks);
241         md->renew_window = src->renew_window;
242         md->warn_window = src->warn_window;
243         md->contacts = md_array_str_clone(p, src->contacts);
244         if (src->ca_url) md->ca_url = apr_pstrdup(p, src->ca_url);
245         if (src->ca_proto) md->ca_proto = apr_pstrdup(p, src->ca_proto);
246         if (src->ca_account) md->ca_account = apr_pstrdup(p, src->ca_account);
247         if (src->ca_agreement) md->ca_agreement = apr_pstrdup(p, src->ca_agreement);
248         if (src->defn_name) md->defn_name = apr_pstrdup(p, src->defn_name);
249         md->defn_line_number = src->defn_line_number;
250         if (src->ca_challenges) {
251             md->ca_challenges = md_array_str_clone(p, src->ca_challenges);
252         }
253         md->acme_tls_1_domains = md_array_str_compact(p, src->acme_tls_1_domains, 0);
254         md->stapling = src->stapling;
255         if (src->cert_files) md->cert_files = md_array_str_clone(p, src->cert_files);
256         if (src->pkey_files) md->pkey_files = md_array_str_clone(p, src->pkey_files);
257     }
258     return md;
259 }
260 
261 /**************************************************************************************************/
262 /* format conversion */
263 
md_to_json(const md_t * md,apr_pool_t * p)264 md_json_t *md_to_json(const md_t *md, apr_pool_t *p)
265 {
266     md_json_t *json = md_json_create(p);
267     if (json) {
268         apr_array_header_t *domains = md_array_str_compact(p, md->domains, 0);
269         md_json_sets(md->name, json, MD_KEY_NAME, NULL);
270         md_json_setsa(domains, json, MD_KEY_DOMAINS, NULL);
271         md_json_setsa(md->contacts, json, MD_KEY_CONTACTS, NULL);
272         md_json_setl(md->transitive, json, MD_KEY_TRANSITIVE, NULL);
273         md_json_sets(md->ca_account, json, MD_KEY_CA, MD_KEY_ACCOUNT, NULL);
274         md_json_sets(md->ca_proto, json, MD_KEY_CA, MD_KEY_PROTO, NULL);
275         md_json_sets(md->ca_url, json, MD_KEY_CA, MD_KEY_URL, NULL);
276         md_json_sets(md->ca_agreement, json, MD_KEY_CA, MD_KEY_AGREEMENT, NULL);
277         if (!md_pkeys_spec_is_empty(md->pks)) {
278             md_json_setj(md_pkeys_spec_to_json(md->pks, p), json, MD_KEY_PKEY, NULL);
279         }
280         md_json_setl(md->state, json, MD_KEY_STATE, NULL);
281         if (md->state_descr)
282             md_json_sets(md->state_descr, json, MD_KEY_STATE_DESCR, NULL);
283         md_json_setl(md->renew_mode, json, MD_KEY_RENEW_MODE, NULL);
284         if (md->renew_window)
285             md_json_sets(md_timeslice_format(md->renew_window, p), json, MD_KEY_RENEW_WINDOW, NULL);
286         if (md->warn_window)
287             md_json_sets(md_timeslice_format(md->warn_window, p), json, MD_KEY_WARN_WINDOW, NULL);
288         if (md->ca_challenges && md->ca_challenges->nelts > 0) {
289             apr_array_header_t *na;
290             na = md_array_str_compact(p, md->ca_challenges, 0);
291             md_json_setsa(na, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
292         }
293         switch (md->require_https) {
294             case MD_REQUIRE_TEMPORARY:
295                 md_json_sets(MD_KEY_TEMPORARY, json, MD_KEY_REQUIRE_HTTPS, NULL);
296                 break;
297             case MD_REQUIRE_PERMANENT:
298                 md_json_sets(MD_KEY_PERMANENT, json, MD_KEY_REQUIRE_HTTPS, NULL);
299                 break;
300             default:
301                 break;
302         }
303         md_json_setb(md->must_staple > 0, json, MD_KEY_MUST_STAPLE, NULL);
304         md_json_setsa(md->acme_tls_1_domains, json, MD_KEY_PROTO, MD_KEY_ACME_TLS_1, NULL);
305         if (md->cert_files) md_json_setsa(md->cert_files, json, MD_KEY_CERT_FILES, NULL);
306         if (md->pkey_files) md_json_setsa(md->pkey_files, json, MD_KEY_PKEY_FILES, NULL);
307         md_json_setb(md->stapling > 0, json, MD_KEY_STAPLING, NULL);
308         if (md->ca_eab_kid && strcmp("none", md->ca_eab_kid)) {
309             md_json_sets(md->ca_eab_kid, json, MD_KEY_EAB, MD_KEY_KID, NULL);
310             if (md->ca_eab_hmac) md_json_sets(md->ca_eab_hmac, json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
311         }
312         return json;
313     }
314     return NULL;
315 }
316 
md_from_json(md_json_t * json,apr_pool_t * p)317 md_t *md_from_json(md_json_t *json, apr_pool_t *p)
318 {
319     const char *s;
320     md_t *md = md_create_empty(p);
321     if (md) {
322         md->name = md_json_dups(p, json, MD_KEY_NAME, NULL);
323         md_json_dupsa(md->domains, p, json, MD_KEY_DOMAINS, NULL);
324         md_json_dupsa(md->contacts, p, json, MD_KEY_CONTACTS, NULL);
325         md->ca_account = md_json_dups(p, json, MD_KEY_CA, MD_KEY_ACCOUNT, NULL);
326         md->ca_proto = md_json_dups(p, json, MD_KEY_CA, MD_KEY_PROTO, NULL);
327         md->ca_url = md_json_dups(p, json, MD_KEY_CA, MD_KEY_URL, NULL);
328         md->ca_agreement = md_json_dups(p, json, MD_KEY_CA, MD_KEY_AGREEMENT, NULL);
329         if (md_json_has_key(json, MD_KEY_PKEY, NULL)) {
330             md->pks = md_pkeys_spec_from_json(md_json_getj(json, MD_KEY_PKEY, NULL), p);
331         }
332         md->state = (md_state_t)md_json_getl(json, MD_KEY_STATE, NULL);
333         md->state_descr = md_json_dups(p, json, MD_KEY_STATE_DESCR, NULL);
334         if (MD_S_EXPIRED_DEPRECATED == md->state) md->state = MD_S_COMPLETE;
335         md->renew_mode = (int)md_json_getl(json, MD_KEY_RENEW_MODE, NULL);
336         md->domains = md_array_str_compact(p, md->domains, 0);
337         md->transitive = (int)md_json_getl(json, MD_KEY_TRANSITIVE, NULL);
338         s = md_json_gets(json, MD_KEY_RENEW_WINDOW, NULL);
339         md_timeslice_parse(&md->renew_window, p, s, MD_TIME_LIFE_NORM);
340         s = md_json_gets(json, MD_KEY_WARN_WINDOW, NULL);
341         md_timeslice_parse(&md->warn_window, p, s, MD_TIME_LIFE_NORM);
342         if (md_json_has_key(json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL)) {
343             md->ca_challenges = apr_array_make(p, 5, sizeof(const char*));
344             md_json_dupsa(md->ca_challenges, p, json, MD_KEY_CA, MD_KEY_CHALLENGES, NULL);
345         }
346         md->require_https = MD_REQUIRE_OFF;
347         s = md_json_gets(json, MD_KEY_REQUIRE_HTTPS, NULL);
348         if (s && !strcmp(MD_KEY_TEMPORARY, s)) {
349             md->require_https = MD_REQUIRE_TEMPORARY;
350         }
351         else if (s && !strcmp(MD_KEY_PERMANENT, s)) {
352             md->require_https = MD_REQUIRE_PERMANENT;
353         }
354         md->must_staple = (int)md_json_getb(json, MD_KEY_MUST_STAPLE, NULL);
355         md_json_dupsa(md->acme_tls_1_domains, p, json, MD_KEY_PROTO, MD_KEY_ACME_TLS_1, NULL);
356 
357         if (md_json_has_key(json, MD_KEY_CERT_FILES, NULL)) {
358             md->cert_files = apr_array_make(p, 3, sizeof(char*));
359             md->pkey_files = apr_array_make(p, 3, sizeof(char*));
360             md_json_dupsa(md->cert_files, p, json, MD_KEY_CERT_FILES, NULL);
361             md_json_dupsa(md->pkey_files, p, json, MD_KEY_PKEY_FILES, NULL);
362         }
363         md->stapling = (int)md_json_getb(json, MD_KEY_STAPLING, NULL);
364 
365         if (md_json_has_key(json, MD_KEY_EAB, NULL)) {
366             md->ca_eab_kid = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_KID, NULL);
367             md->ca_eab_hmac = md_json_dups(p, json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
368         }
369         return md;
370     }
371     return NULL;
372 }
373 
md_to_public_json(const md_t * md,apr_pool_t * p)374 md_json_t *md_to_public_json(const md_t *md, apr_pool_t *p)
375 {
376     md_json_t *json = md_to_json(md, p);
377     if (md_json_has_key(json, MD_KEY_EAB, MD_KEY_HMAC, NULL)) {
378         md_json_sets("***", json, MD_KEY_EAB, MD_KEY_HMAC, NULL);
379     }
380     return json;
381 }
382 
383 typedef struct {
384     const char *name;
385     const char *url;
386 } md_ca_t;
387 
388 #define LE_ACMEv2_PROD      "https://acme-v02.api.letsencrypt.org/directory"
389 #define LE_ACMEv2_STAGING   "https://acme-staging-v02.api.letsencrypt.org/directory"
390 #define BUYPASS_ACME        "https://api.buypass.com/acme/directory"
391 #define BUYPASS_ACME_TEST   "https://api.test4.buypass.no/acme/directory"
392 
393 static md_ca_t KNOWN_CAs[] = {
394     { "LetsEncrypt", LE_ACMEv2_PROD },
395     { "LetsEncrypt-Test", LE_ACMEv2_STAGING },
396     { "Buypass", BUYPASS_ACME },
397     { "Buypass-Test", BUYPASS_ACME_TEST },
398 };
399 
md_get_ca_name_from_url(apr_pool_t * p,const char * url)400 const char *md_get_ca_name_from_url(apr_pool_t *p, const char *url)
401 {
402     apr_uri_t uri_parsed;
403     unsigned int i;
404 
405     for (i = 0; i < sizeof(KNOWN_CAs)/sizeof(KNOWN_CAs[0]); ++i) {
406         if (!apr_strnatcasecmp(KNOWN_CAs[i].url, url)) {
407             return KNOWN_CAs[i].name;
408         }
409     }
410     if (APR_SUCCESS == apr_uri_parse(p, url, &uri_parsed)) {
411         return uri_parsed.hostname;
412     }
413     return apr_pstrdup(p, url);
414 }
415 
md_get_ca_url_from_name(const char ** purl,apr_pool_t * p,const char * name)416 apr_status_t md_get_ca_url_from_name(const char **purl, apr_pool_t *p, const char *name)
417 {
418     const char *err;
419     unsigned int i;
420     apr_status_t rv = APR_SUCCESS;
421 
422     *purl = NULL;
423     for (i = 0; i < sizeof(KNOWN_CAs)/sizeof(KNOWN_CAs[0]); ++i) {
424         if (!apr_strnatcasecmp(KNOWN_CAs[i].name, name)) {
425             *purl = KNOWN_CAs[i].url;
426             goto leave;
427         }
428     }
429     *purl = name;
430     rv = md_util_abs_http_uri_check(p, name, &err);
431     if (APR_SUCCESS != rv) {
432         apr_array_header_t *names;
433 
434         names = apr_array_make(p, 10, sizeof(const char*));
435         for (i = 0; i < sizeof(KNOWN_CAs)/sizeof(KNOWN_CAs[0]); ++i) {
436             APR_ARRAY_PUSH(names, const char *) = KNOWN_CAs[i].name;
437         }
438         *purl = apr_psprintf(p,
439             "The CA name '%s' is not known and it is not a URL either (%s). "
440             "Known CA names are: %s.",
441             name, err, apr_array_pstrcat(p, names, ' '));
442     }
443 leave:
444     return rv;
445 }
446