Skip to content

Commit 6321e79

Browse files
authored
Ignore invalid job.json files (#421)
* Fix issue #420 by ignoring job.json files that claim to have completely finished a certificate renewal, but have not produced the necessary result files.
1 parent 1412009 commit 6321e79

File tree

5 files changed

+74
-3
lines changed

5 files changed

+74
-3
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Fix issue #420 by ignoring job.json files that claim to have completely
2+
finished a certificate renewal, but have not produced the necessary
3+
result files.
4+
15
v2.6.9
26
----------------------------------------------------------------------------------------------------
37
* Pebble 2.9+ reports another error when terms of service agreement is not set.

src/md_status.c

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,14 +97,45 @@ static apr_status_t status_get_cert_json(md_json_t **pjson, const md_cert_t *cer
9797
return rv;
9898
}
9999

100+
static int md_job_json_seems_valid(md_json_t *json, md_store_t *store,
101+
md_store_group_t group, const char *name,
102+
apr_pool_t *p)
103+
{
104+
105+
if (!json) return FALSE;
106+
if ((group == MD_SG_STAGING) &&
107+
md_json_getb(json, MD_KEY_FINISHED, NULL) &&
108+
md_json_getb(json, MD_KEY_NOTIFIED_RENEWED, NULL)) {
109+
md_t *md;
110+
/* A finished job in the staging area needs to have produced results */
111+
if(!md_exists(store, group, name, p)) return FALSE;
112+
113+
if (APR_SUCCESS == md_load(store, MD_SG_DOMAINS, name, &md, p)) {
114+
int i;
115+
for (i = 0; i < md_cert_count(md); ++i) {
116+
md_pkey_spec_t *spec = md_pkeys_spec_get(md->pks, i);
117+
if(md_pubcert_load(store, group, name, spec, NULL, p) != APR_SUCCESS)
118+
return FALSE;
119+
}
120+
}
121+
}
122+
return TRUE;
123+
}
124+
100125
static apr_status_t job_loadj(md_json_t **pjson, md_store_group_t group, const char *name,
101126
struct md_reg_t *reg, int with_log, apr_pool_t *p)
102127
{
103128
apr_status_t rv;
104129

105130
md_store_t *store = md_reg_store_get(reg);
106131
rv = md_store_load_json(store, group, name, MD_FN_JOB, pjson, p);
107-
if (APR_SUCCESS == rv && !with_log) md_json_del(*pjson, MD_KEY_LOG, NULL);
132+
if (APR_SUCCESS == rv) {
133+
if (!md_job_json_seems_valid(*pjson, store, group, name, p)) {
134+
*pjson = NULL;
135+
return APR_ENOENT;
136+
}
137+
if(!with_log) md_json_del(*pjson, MD_KEY_LOG, NULL);
138+
}
108139
return rv;
109140
}
110141

@@ -384,7 +415,8 @@ apr_status_t md_job_load(md_job_t *job)
384415
apr_status_t rv;
385416

386417
rv = md_store_load_json(job->store, job->group, job->mdomain, MD_FN_JOB, &jprops, job->p);
387-
if (APR_SUCCESS == rv) {
418+
if ((APR_SUCCESS == rv) &&
419+
md_job_json_seems_valid(jprops, job->store, job->group, job->mdomain, job->p)) {
388420
md_job_from_json(job, jprops, job->p);
389421
}
390422
return rv;

src/md_store.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,12 @@ typedef struct {
176176
md_store_group_t group;
177177
} md_group_ctx;
178178

179+
int md_exists(md_store_t *store, md_store_group_t group,
180+
const char *name, apr_pool_t *p)
181+
{
182+
return (md_store_load_json(store, group, name, MD_FN_MD, NULL, p) == APR_SUCCESS);
183+
}
184+
179185
apr_status_t md_load(md_store_t *store, md_store_group_t group,
180186
const char *name, md_t **pmd, apr_pool_t *p)
181187
{

src/md_store.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ void md_store_unlock_global(md_store_t *store, apr_pool_t *p);
225225
/**************************************************************************************************/
226226
/* Storage handling utils */
227227

228-
apr_status_t md_load(md_store_t *store, md_store_group_t group,
228+
int md_exists(md_store_t *store, md_store_group_t group,
229+
const char *name, apr_pool_t *p);
230+
apr_status_t md_load(md_store_t *store, md_store_group_t group,
229231
const char *name, md_t **pmd, apr_pool_t *p);
230232
apr_status_t md_save(struct md_store_t *store, apr_pool_t *p, md_store_group_t group,
231233
md_t *md, int create);

test/modules/md/test_702_auto.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import os
23
import re
34
import time
@@ -856,3 +857,29 @@ def test_md_702_071(self, env):
856857
assert env.await_completion(domains)
857858
env.check_md_complete(domains[0])
858859

860+
# Verify issue #420. A lying job.json in staging prevents renewal by
861+
# reporting it has already been done.
862+
def test_md_702_080(self, env):
863+
domain = self.test_domain
864+
dns_list = [domain]
865+
conf = MDConf(env, admin="admin@" + domain)
866+
conf.add_md(dns_list)
867+
conf.add_vhost(dns_list)
868+
conf.install()
869+
fake_job = {
870+
"name": domain,
871+
"finished": True,
872+
"notified": True,
873+
"notified-renewed": True,
874+
"errors": 0,
875+
}
876+
staging_dir = os.path.join(env.store_dir, 'staging', domain)
877+
env.mkpath(staging_dir)
878+
staging_job = os.path.join(staging_dir, 'job.json')
879+
with open(staging_job, 'w') as fd:
880+
fd.write(json.JSONEncoder(indent="").encode(fake_job))
881+
assert env.apache_restart() == 0, f'{env.apachectl_stderr}'
882+
stat = env.get_md_status(domain)
883+
assert stat['renew'] is True
884+
assert env.await_completion([domain])
885+
assert os.path.exists(env.store_domain_file(domain, 'pubcert.pem'))

0 commit comments

Comments
 (0)