Skip to content

Commit 4e6562a

Browse files
committed
priv2
1 parent 5d18b70 commit 4e6562a

2 files changed

Lines changed: 129 additions & 43 deletions

File tree

lib/system/whois/whois.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ lws_whois_parse_final(struct lws_whois *w)
159159
}
160160
}
161161

162-
lwsl_notice("%s: Parsed final for %s (len: %lu, dnssec: '%s')\n", __func__, w->args.domain, (unsigned long)w->buf_len, res.dnssec);
162+
lwsl_notice("[INSTRUMENT] %s: Parsed final for %s (len: %lu, dnssec: '%s', res.expiry: %llu)\n", __func__, w->args.domain, (unsigned long)w->buf_len, res.dnssec, (unsigned long long)res.expiry_date);
163163

164164
if (w->args.cb)
165165
w->args.cb(w->args.opaque, &res);
@@ -188,6 +188,7 @@ callback_whois(struct lws *wsi, enum lws_callback_reasons reason, void *user,
188188
w->wsi = NULL;
189189

190190
if (w->state == 2) {
191+
lwsl_notice("[INSTRUMENT] %s: RAW_CLOSE in state 2, calling callback with NULL for %s\n", __func__, w->args.domain);
191192
if (w->args.cb)
192193
w->args.cb(w->args.opaque, NULL);
193194
lws_set_opaque_user_data(wsi, NULL);

plugins/protocol_lws_dht_dnssec_monitor/protocol_lws_dht_dnssec_monitor.c

Lines changed: 127 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ struct pss {
7272
int send_ext_ips;
7373
};
7474

75+
struct published_jws_info {
76+
lws_dll2_t list;
77+
char domain[128];
78+
time_t mtime;
79+
};
80+
7581
struct vhd {
7682
struct lws_context *context;
7783
struct lws_vhost *vhost;
@@ -103,6 +109,8 @@ struct vhd {
103109
char ext_ips[256];
104110
lws_dll2_owner_t completed_checks;
105111
lws_dll2_owner_t whois_queries;
112+
lws_dll2_owner_t published_jws;
113+
lws_sorted_usec_list_t sul_timer_scan;
106114
};
107115

108116
struct cert_check_result {
@@ -155,13 +163,22 @@ static void
155163
whois_cb(void *opaque, const struct lws_whois_results *res)
156164
{
157165
struct whois_query_info *wqi = (struct whois_query_info *)opaque;
158-
char buf[2048], ns_list[1024] = "";
159-
char *p_ns, *saveptr, *token;
160-
161-
if (!wqi)
162-
return;
166+
int n;
167+
char buf[2048];
168+
char ns_list[1024] = "";
169+
170+
lwsl_notice("[INSTRUMENT] %s: callback triggered for %s. res is %s\n", __func__, wqi->domain, res ? "NOT NULL" : "NULL");
163171

172+
char s_dnssec[256] = "", s_ds[1024] = "";
164173
if (res) {
174+
lws_strncpy(s_dnssec, res->dnssec, sizeof(s_dnssec));
175+
lws_strncpy(s_ds, res->ds_data, sizeof(s_ds));
176+
for (size_t i = 0; i < strlen(s_dnssec); i++)
177+
if (s_dnssec[i] < 32 || s_dnssec[i] == '"') s_dnssec[i] = ' ';
178+
for (size_t i = 0; i < strlen(s_ds); i++)
179+
if (s_ds[i] < 32 || s_ds[i] == '"') s_ds[i] = ' ';
180+
181+
char *p_ns, *token, *saveptr;
165182
/* Convert comma-separated nameservers to JSON array of strings */
166183
p_ns = strdup(res->nameservers);
167184
if (p_ns) {
@@ -176,53 +193,45 @@ whois_cb(void *opaque, const struct lws_whois_results *res)
176193
}
177194
free(p_ns);
178195
}
179-
}
180-
181-
char s_dnssec[256] = "", s_ds[1024] = "";
182-
if (res) {
183-
lws_strncpy(s_dnssec, res->dnssec, sizeof(s_dnssec));
184-
lws_strncpy(s_ds, res->ds_data, sizeof(s_ds));
185-
for (size_t i = 0; i < strlen(s_dnssec); i++)
186-
if (s_dnssec[i] < 32 || s_dnssec[i] == '"') s_dnssec[i] = ' ';
187-
for (size_t i = 0; i < strlen(s_ds); i++)
188-
if (s_ds[i] < 32 || s_ds[i] == '"') s_ds[i] = ' ';
196+
189197
for (size_t i = 0; i < strlen(ns_list); i++)
190198
if (ns_list[i] < 32 && ns_list[i] != '\0') ns_list[i] = ' ';
191-
}
192199

193-
int n;
194-
if (res) {
195200
n = lws_snprintf(buf, sizeof(buf),
196201
"{\n \"creation_date\": %llu,\n \"expiry_date\": %llu,\n \"updated_date\": %llu,\n"
197202
" \"nameservers\": [%s],\n"
198203
" \"dnssec\": \"%s\",\n \"ds_data\": \"%s\",\n \"last_query\": %llu\n}\n",
199204
(unsigned long long)res->creation_date, (unsigned long long)res->expiry_date,
200205
(unsigned long long)res->updated_date,
201206
ns_list, s_dnssec, s_ds, (unsigned long long)lws_now_secs());
207+
208+
lwsl_notice("[INSTRUMENT] whois_cb: formatted JSON for %s, size = %d\n", wqi->domain, n);
202209
} else {
203-
n = lws_snprintf(buf, sizeof(buf),
204-
"{\n \"creation_date\": 0,\n \"expiry_date\": 0,\n \"updated_date\": 0,\n"
205-
" \"nameservers\": [],\n"
206-
" \"dnssec\": \"\",\n \"ds_data\": \"\",\n \"last_query\": %llu\n}\n",
207-
(unsigned long long)lws_now_secs());
210+
lwsl_notice("[INSTRUMENT] whois_cb: res is NULL for %s, skipping UDS publish\n", wqi->domain);
211+
n = 0; /* Let it organically fail or retry without caching `{}` */
208212
}
209213

210214
if (n > 0) {
211-
char b64[4096] = {0}, jwt[1024] = {0}, uds_json[6144] = {0}, temp[2048] = {0};
215+
char b64[8192] = {0}, jwt[1024] = {0}, uds_json[10240] = {0}, temp[2048] = {0};
212216
size_t jwt_len = sizeof(jwt);
213-
lws_b64_encode_string(buf, n, b64, sizeof(b64));
217+
lws_b64_encode_string(buf, (int)strlen(buf), b64, sizeof(b64));
214218

215219
if (wqi->vhd->auth_jwk.kty == LWS_GENCRYPTO_KTY_OCT) {
220+
char jwt_payload[512];
221+
unsigned long now = (unsigned long)lws_now_secs();
222+
lws_snprintf(jwt_payload, sizeof(jwt_payload),
223+
"{\"iss\":\"acme-ipc\",\"aud\":\"dnssec-monitor\",\"nbf\":%lu,\"exp\":%lu}",
224+
now, now + 300);
225+
216226
if (lws_jwt_sign_compact(wqi->vhd->context, &wqi->vhd->auth_jwk, "HS256",
217-
jwt, &jwt_len, temp, sizeof(temp), "{\"iss\":\"dnssec-monitor\"}")) {
218-
lwsl_err("%s: failed to generate jwt for whois\n", __func__);
227+
jwt, &jwt_len, temp, sizeof(temp), jwt_payload)) {
228+
lwsl_err("[INSTRUMENT] %s: failed to generate jwt for whois\n", __func__);
219229
}
220230
}
221231

222232
int payload_n = lws_snprintf(uds_json, sizeof(uds_json),
223233
"{\"req\":\"update_whois\",\"domain\":\"%s\",\"jwt\":\"%s\",\"zone\":\"%s\"}\n",
224234
wqi->domain, jwt, b64);
225-
226235
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
227236
if (fd >= 0) {
228237
struct sockaddr_un sun;
@@ -231,19 +240,21 @@ whois_cb(void *opaque, const struct lws_whois_results *res)
231240
lws_strncpy(sun.sun_path, wqi->vhd->uds_path, sizeof(sun.sun_path));
232241
if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
233242
if (write(fd, uds_json, (size_t)payload_n) < 0) {
234-
lwsl_err("%s: Failed writing whois payload to UDS\n", __func__);
243+
lwsl_err("[INSTRUMENT] %s: Failed writing whois payload to UDS, errno: %d\n", __func__, errno);
235244
} else {
236-
lwsl_notice("%s: Tunneled WHOIS for %s to Root over UDS\n", __func__, wqi->domain);
245+
lwsl_notice("[INSTRUMENT] %s: Tunneled WHOIS for %s to Root over UDS (payload %d bytes)\n", __func__, wqi->domain, payload_n);
237246
}
238247
} else {
239-
lwsl_err("%s: Failed connecting to root UDS for whois pass-back\n", __func__);
248+
lwsl_err("[INSTRUMENT] %s: Failed connecting to root UDS at %s for whois pass-back, errno: %d\n", __func__, sun.sun_path, errno);
240249
}
241250
close(fd);
251+
} else {
252+
lwsl_err("[INSTRUMENT] %s: socket creation failed! errno: %d\n", __func__, errno);
242253
}
254+
255+
lws_dll2_remove(&wqi->list);
256+
free(wqi);
243257
}
244-
245-
lws_dll2_remove(&wqi->list);
246-
free(wqi);
247258
}
248259

249260
static int
@@ -524,17 +535,70 @@ dir_notify_cb(const char *path, int is_file, void *user)
524535
}
525536
#endif
526537

538+
static int
539+
scan_jws_publish_cb(const char *dirpath, void *user, struct lws_dir_entry *lde)
540+
{
541+
struct vhd *vhd = (struct vhd *)user;
542+
543+
if (lde->type != LDOT_DIR || lde->name[0] == '.')
544+
return 0;
545+
546+
if (vhd->ops && vhd->ops->publish_jws) {
547+
char jws_path[1024];
548+
struct stat st;
549+
550+
lws_snprintf(jws_path, sizeof(jws_path), "%s/domains/%s/%s.zone.signed.jws", vhd->base_dir, lde->name, lde->name);
551+
552+
if (stat(jws_path, &st) == 0) {
553+
/* Check if we already published this version */
554+
struct published_jws_info *p = NULL;
555+
lws_start_foreach_dll(struct lws_dll2 *, d, vhd->published_jws.head) {
556+
struct published_jws_info *tp = lws_container_of(d, struct published_jws_info, list);
557+
if (!strcmp(tp->domain, lde->name)) {
558+
p = tp;
559+
break;
560+
}
561+
} lws_end_foreach_dll(d);
562+
563+
if (!p || p->mtime != st.st_mtime) {
564+
if (!p) {
565+
p = malloc(sizeof(*p));
566+
if (!p) return 0;
567+
memset(p, 0, sizeof(*p));
568+
lws_strncpy(p->domain, lde->name, sizeof(p->domain));
569+
lws_dll2_add_tail(&p->list, &vhd->published_jws);
570+
}
571+
p->mtime = st.st_mtime;
572+
lwsl_notice("%s: Engaging parent monitor to execute DHT publication for %s\n", __func__, lde->name);
573+
vhd->ops->publish_jws(vhd->vhost, jws_path);
574+
}
575+
}
576+
}
577+
return 0;
578+
}
579+
527580
static void
528581
parent_dnssec_monitor_timer_cb(struct lws_sorted_usec_list *sul)
529582
{
530583
struct vhd *vhd = lws_container_of(sul, struct vhd, sul_timer);
531584
char scan_path[1024];
532585

533586
lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir);
534-
lws_dir(scan_path, vhd, scan_dir_cb);
587+
lws_dir(scan_path, vhd, scan_jws_publish_cb);
535588
lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, parent_dnssec_monitor_timer_cb, 5 * LWS_US_PER_SEC);
536589
}
537590

591+
static void
592+
root_dnssec_scan_timer_cb(struct lws_sorted_usec_list *sul)
593+
{
594+
struct vhd *vhd = lws_container_of(sul, struct vhd, sul_timer_scan);
595+
char scan_path[1024];
596+
597+
lws_snprintf(scan_path, sizeof(scan_path), "%s/domains", vhd->base_dir);
598+
lws_dir(scan_path, vhd, scan_dir_cb);
599+
lws_sul_schedule(vhd->context, 0, &vhd->sul_timer_scan, root_dnssec_scan_timer_cb, 5 * LWS_US_PER_SEC);
600+
}
601+
538602

539603

540604

@@ -650,6 +714,10 @@ monitor_req_cb(struct lejp_ctx *ctx, char reason)
650714
}
651715
}
652716

717+
if (reason == LEJPCB_FAILED) {
718+
lwsl_err("[INSTRUMENT] monitor_req_cb: LEJP JSON Parse FAILED at struct offset %d\n", (int)ctx->st[ctx->sp].s);
719+
}
720+
653721
return 0;
654722
}
655723

@@ -1153,22 +1221,31 @@ handle_req_update_whois(struct vhd *vhd, struct pss *root_pss, struct monitor_re
11531221
{
11541222
char *tx = (char *)&root_pss->tx[LWS_PRE];
11551223

1224+
lwsl_notice("[INSTRUMENT] handle_req_update_whois START for domain: '%s', zone_buf present: %d\n", a->domain, !!a->zone_buf);
1225+
11561226
if (a->domain[0] && a->zone_buf) {
11571227
char path[1024];
11581228
lws_snprintf(path, sizeof(path), "%s/domains/%s/whois.json", vhd->base_dir, a->domain);
11591229
int fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
11601230
if (fd >= 0) {
1161-
char decoded[4096];
1231+
char decoded[8192];
11621232
int n = lws_b64_decode_string(a->zone_buf, decoded, sizeof(decoded));
1233+
lwsl_notice("[INSTRUMENT] lws_b64_decode_string returned %d for %s\n", n, a->domain);
11631234
if (n > 0) {
11641235
if (write(fd, decoded, (size_t)n) < 0) {
1165-
lwsl_err("%s: Failed writing to %s\n", __func__, path);
1236+
lwsl_err("[INSTRUMENT] %s: Failed writing to %s (errno: %d)\n", __func__, path, errno);
11661237
} else {
1167-
lwsl_info("%s: Successfully synced WHOIS via UDS IPC for %s\n", __func__, a->domain);
1238+
lwsl_info("[INSTRUMENT] %s: Successfully synced WHOIS via UDS IPC for %s\n", __func__, a->domain);
11681239
}
1240+
} else {
1241+
lwsl_err("[INSTRUMENT] %s: Failed B64 decode on whois zone payload size=%d\n", __func__, (int)a->zone_len);
11691242
}
11701243
close(fd);
1244+
} else {
1245+
lwsl_err("[INSTRUMENT] %s: Failed to open %s for writing! errno: %d\n", __func__, path, errno);
11711246
}
1247+
} else {
1248+
lwsl_err("[INSTRUMENT] %s: Failed prerequisites. domain: '%s', zone_buf present: %d\n", __func__, a->domain, !!a->zone_buf);
11721249
}
11731250

11741251
/* Empty response is fine, IPC fire-and-forget */
@@ -1522,6 +1599,7 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason,
15221599
if (!vhd->dn)
15231600
lwsl_err("%s: Failed to attach lws_dir_notify to %s\n", __func__, scan_path);
15241601
#endif
1602+
lws_sul_schedule(cx, 0, &vhd->sul_timer_scan, root_dnssec_scan_timer_cb, 5 * LWS_US_PER_SEC);
15251603
} else {
15261604
lwsl_err("%s: Skipped scheduling timer on %s because vhd->ops is NULL!\n", __func__, lws_get_vhost_name(vhost));
15271605
/* It will organically retry when the next vhost runs PROTOCOL_INIT */
@@ -1717,7 +1795,17 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason,
17171795
global_root_vhd = vhd;
17181796
lwsl_notice("%s: Spawned root monitor process successfully and assigned global_root_vhd=%p (fallback active)\n", __func__, global_root_vhd);
17191797

1720-
/* Engage parent monitor to execute DHT publications off completed JWS child drops cleanly */
1798+
/*
1799+
* Privilege Separation Policy:
1800+
* - The "root daemon" drops its privileges to run as the `lwsws-priv` user.
1801+
* - Only the `lwsws-priv` daemon can write to the base dir (e.g., /var/dnssec)
1802+
* and read secrets like cert keys.
1803+
* - The less-privileged network-facing side (here) asks the daemon to handle
1804+
* write operations securely.
1805+
* - We keep the privileged daemon isolated from external network content.
1806+
* Therefore, this unprivileged side leverages a timer to securely scan for
1807+
* completed .jws drops and natively handles the DHT network publication.
1808+
*/
17211809
lws_sul_schedule(vhd->context, 0, &vhd->sul_timer, parent_dnssec_monitor_timer_cb, 1 * LWS_US_PER_SEC);
17221810
} else {
17231811
/* Already globally spawned! Just map the auth context */
@@ -2158,12 +2246,9 @@ callback_dht_dnssec_monitor(struct lws *wsi, enum lws_callback_reasons reason,
21582246
}
21592247
break;
21602248

2161-
2162-
21632249
default:
21642250
break;
21652251
}
2166-
21672252
return 0;
21682253
}
21692254

0 commit comments

Comments
 (0)