Files
LithosAnanake/src/starkernel/capsule/capsule_wirebind.c
T
Robert Allan JamesandClaude Opus 5 ed86a759e1 §H.12 steps 7-9: thread real parent VMUuid through the birth call chain
Session.parent now comes from the actual birthing VM's own
stadium_vm_id, not a hardcoded vm_uuid_hera(). Added a VMUuid parent
parameter to capsule_birth_baby() and, one level up, to
capsule_console_birth()/capsule_runcap_birth() (neither had a VM* in
their own signature, but every caller did). Updated all 6 real call
sites: BIRTH, CAPSULE-BIRTH, CONNECT-ARTEMIS, CONNECT-HERMES,
RUNCAP-TEST, PAIR-TEST (mama_forth_words.c) and the console+user birth
pair in capsule_wirebind_try_attach() (capsule_wirebind.c). Two
functions had their vm parameter marked __attribute__((unused)), now
genuinely used -- attribute removed.

Steps 8 (Session.name from capsule name) and 9 (identity defaults to
installed=0) were already satisfied by step 5's existing
session_register() call and its identity-zeroing -- confirmed by
inspection, no further code needed.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 06:38:32 -04:00

179 lines
7.0 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 20232025 Robert A. James
All rights reserved.
Licensed under the StarForth License, Version 1.0
*/
#ifndef __STARKERNEL__
#error "capsule_wirebind.c is kernel-only"
#endif
#include "starkernel/capsule_wirebind.h"
#include "starkernel/capsule_birth.h"
#include "starkernel/capsule_runcap.h"
#include "starkernel/capsule_console.h"
#include "starkernel/vm_identity.h"
#include "starkernel/user_identity_seed.h"
#include "starkernel/console.h"
#include "blkio.h"
#include "block_subsystem.h" /* compute_crc64() */
#include "freestanding/stdio.h"
#include <string.h>
#include <stddef.h>
/* WIREBIND_CERT_MAX_DEVBLOCKS: a sane upper bound on how much cert
* content this reads, independent of whatever sig->cert_devblocks
* claims -- MINT (capsule_mint.c) only ever writes 1 devblock's worth
* (a real cert is ~150-250 bytes), so 4 devblocks (16KB) is already
* generous headroom, not a real constraint. */
#define WIREBIND_CERT_MAX_DEVBLOCKS 4u
/* Read exactly one devblock (4096 bytes) at devblock offset `devblock`
* -- same convention capsule_runcap.c/capsule_mint.c already use. */
static int read_devblock(struct blkio_dev *dev, uint32_t devblock, uint8_t *buf4096) {
uint32_t base = devblock * 4u;
for (uint32_t i = 0; i < 4u; i++) {
if (blkio_read((blkio_dev_t *)dev, base + i,
buf4096 + (size_t)i * BLKIO_FORTH_BLOCK_SIZE) != BLKIO_OK) {
return -1;
}
}
return 0;
}
int capsule_wirebind_verify_cert(struct blkio_dev *dev,
const homeblocks_sig_t *sig,
VM *mama_vm, VMIdentity *out) {
if (!dev || !sig || !mama_vm || !out) return -1;
/* Genesis-mode Zuse drives have no cert region -- capsule_zuse_boot_
* try_attach() owns that case, not this one. */
if (sig->cert_offset == 0 || sig->cert_devblocks == 0) return -1;
/* Nothing to verify a regular cert against until Zuse herself has
* authenticated this boot. */
if (!mama_vm->zuse_cert_installed) return -1;
/* Read the cert region into a local buffer. */
uint32_t n_devblocks = sig->cert_devblocks;
if (n_devblocks > WIREBIND_CERT_MAX_DEVBLOCKS) n_devblocks = WIREBIND_CERT_MAX_DEVBLOCKS;
uint8_t cert_der[WIREBIND_CERT_MAX_DEVBLOCKS * 4096];
for (uint32_t d = 0; d < n_devblocks; d++) {
if (read_devblock(dev, sig->cert_offset + d, cert_der + (size_t)d * 4096) != 0) {
return -1;
}
}
size_t cert_len = (size_t)n_devblocks * 4096;
memset(out, 0, sizeof(*out));
return vm_identity_from_cert(out, cert_der, cert_len,
mama_vm->zuse_cert_pubkey, sig->drive_uuid,
0 /* acl_caps: no bits assigned yet, §F.2 */);
}
void capsule_wirebind_try_attach(struct blkio_dev *dev,
const homeblocks_sig_t *sig,
VM *mama_vm) {
if (!dev || !sig || !mama_vm) return;
/* Genesis-mode Zuse drives have no cert region -- capsule_zuse_boot_
* try_attach() already owns that case. Check here too so the right
* "not applicable" case stays silent (matches capsule_wirebind_
* verify_cert()'s own -1, but avoids a misleading refusal message
* for a drive this function was never going to act on anyway). */
if (sig->cert_offset == 0 || sig->cert_devblocks == 0) return;
if (!mama_vm->zuse_cert_installed) return;
VMIdentity identity;
if (capsule_wirebind_verify_cert(dev, sig, mama_vm, &identity) != 0) {
console_println("WIREBIND: cert verification FAILED -- drive refused");
return;
}
/* Verified. Read the drive's own profile for its username. */
if (sig->identity_src_offset == 0 || sig->identity_src_devblocks < 1) {
console_println("WIREBIND: verified cert but no identity_src region -- refusing");
return;
}
user_identity_seed_t idrec;
if (read_devblock(dev, sig->identity_src_offset, (uint8_t *)&idrec) != 0) {
console_println("WIREBIND: verified cert but identity record unreadable -- refusing");
return;
}
/* Same magic -> version -> CRC-64 discipline as capsule_zuse_boot.c:
* the identity record is the same on-disk format, so a corrupt or
* format-mismatched record must be refused rather than trusted. */
if (idrec.magic != USER_IDENTITY_SEED_MAGIC ||
idrec.version != USER_IDENTITY_SEED_VERSION) {
console_println("WIREBIND: verified cert but identity record unreadable -- refusing");
return;
}
uint64_t want_crc = compute_crc64((const uint8_t *)&idrec,
offsetof(user_identity_seed_t, crc));
if (want_crc != idrec.crc) {
console_println("WIREBIND: verified cert but identity record corrupt -- refusing");
return;
}
char username[USER_IDENTITY_USERNAME_MAX];
memcpy(username, idrec.username, sizeof(username));
username[sizeof(username) - 1] = '\0'; /* defensive; MINT already NUL-terminates */
char user_vm_name[USER_IDENTITY_USERNAME_MAX + 8];
size_t ulen = strlen(username);
if (ulen == 0 || ulen + 6 > sizeof(user_vm_name)) {
console_println("WIREBIND: empty or oversized username -- refusing");
return;
}
memcpy(user_vm_name, username, ulen);
memcpy(user_vm_name + ulen, "~user", 6);
/* Idempotent: already bound this session (console VM already live
* under this username) -- nothing to do. Matches BIRTH's own
* idempotent convention. */
{
VMRegistryEntry existing;
if (capsule_vm_find_by_name(username, &existing) == 0 &&
existing.state == VM_STATE_LIVE) {
return;
}
}
VMUuid console_id, user_id;
void *console_ctx = (void *)0;
void *user_ctx = (void *)0;
if (capsule_console_birth(username, mama_vm->stadium_vm_id, &console_id, &console_ctx)
!= CAPSULE_RUN_OK) {
console_println("WIREBIND: console VM birth FAILED");
return;
}
if (capsule_runcap_birth(dev, sig, user_vm_name, mama_vm->stadium_vm_id, &user_id,
&user_ctx) != CAPSULE_RUN_OK) {
console_println("WIREBIND: user VM birth FAILED");
return;
}
/* Install the verified identity onto the user VM -- the one BINDSTEP
* (§F.9) will later re-verify against on every USE. */
if (user_ctx) {
((VM *)user_ctx)->identity = identity;
}
/* Register the pairing in the console's own routing table, index 3
* -- the fixed convention sk_repl_dispatch_line() (repl.c) uses. */
{
char reg_cmd[sizeof(user_vm_name) + 32];
int n = snprintf(reg_cmd, sizeof(reg_cmd), "S\" %s\" 3 VM-NAME-REG", user_vm_name);
if (n > 0 && (size_t)n < sizeof(reg_cmd)) {
vm_interpret((VM *)console_ctx, reg_cmd);
}
}
console_puts("WIREBIND: ");
console_puts(username);
console_println(" attached and ready -- USE it to begin");
}