oficina / home Public

Clone
git clone https://amadan.net/oficina/home

Plain git — no account needed to clone.

Download

Download this file

1//go:build linux
2
3package home
4
5import (
6 "bytes"
7 "context"
8 "crypto/sha256"
9 "database/sql"
10 "embed"
11 "encoding/hex"
12 "encoding/json"
13 "strings"
14
15 "amadan.net/oficina/home/internal/operator"
16)
17
18// Admission is deliberately unmounted until verified platform enrollment exists.
19// These manifests commit exact bytes; diagnostic observations cannot select one.
20//
21//go:embed admission_profiles/*.json
22var admissionProfiles embed.FS
23
24type schemaCommitmentMigration struct {
25 ID string `json:"id"`
26 Checksum string `json:"checksum"`
27}
28type schemaCommitmentManifest struct {
29 Version int `json:"version"`
30 ID string `json:"id"`
31 Role string `json:"role"`
32 ValidationPolicy int `json:"validation_policy"`
33 Migrations []schemaCommitmentMigration `json:"migrations"`
34 Objects []schemaObject `json:"objects"`
35 EmptyTables []string `json:"empty_tables"`
36}
37
38type schemaCommitment struct {
39 manifest schemaCommitmentManifest
40 digest string
41}
42
43func decodeSchemaCommitment(raw []byte) (schemaCommitmentManifest, error) {
44 var m schemaCommitmentManifest
45 if len(raw) == 0 || len(raw) > 1<<20 {
46 return m, operator.ErrRefused
47 }
48 d := json.NewDecoder(bytes.NewReader(raw))
49 d.DisallowUnknownFields()
50 if d.Decode(&m) != nil {
51 return m, operator.ErrRefused
52 }
53 canonical, err := json.MarshalIndent(m, "", " ")
54 if err != nil || !bytes.Equal(append(canonical, '\n'), raw) || m.Version != 1 || m.ValidationPolicy != 1 ||
55 len(m.Migrations) == 0 || len(m.Migrations) > 1000 || len(m.Objects) == 0 || len(m.Objects) > 1000 || m.EmptyTables == nil {
56 return m, operator.ErrRefused
57 }
58 switch m.ID {
59 case "credential-current-v1":
60 if m.Role != "credential" || len(m.EmptyTables) != 0 {
61 return m, operator.ErrRefused
62 }
63 case "directory-current-v1":
64 if m.Role != "directory" || len(m.EmptyTables) != 0 {
65 return m, operator.ErrRefused
66 }
67 case "directory-historical-empty-v1":
68 if m.Role != "directory" || strings.Join(m.EmptyTables, ",") != "passkey_challenges,passkey_credentials,passkey_pending,passkey_recovery_codes,people,signin_links" {
69 return m, operator.ErrRefused
70 }
71 default:
72 return m, operator.ErrRefused
73 }
74 for i, migration := range m.Migrations {
75 sum, err := hex.DecodeString(migration.Checksum)
76 if migration.ID == "" || len(migration.ID) > 256 || err != nil || len(sum) != 32 || hex.EncodeToString(sum) != migration.Checksum ||
77 (i > 0 && m.Migrations[i-1].ID >= migration.ID) {
78 return m, operator.ErrRefused
79 }
80 }
81 tables := map[string]bool{}
82 for i, obj := range m.Objects {
83 if obj.Type != "table" && obj.Type != "index" && obj.Type != "trigger" && obj.Type != "view" {
84 return m, operator.ErrRefused
85 }
86 if obj.Name == "" || obj.Table == "" || len(obj.Name) > 256 || len(obj.Table) > 256 || len(obj.SQL) > 65536 {
87 return m, operator.ErrRefused
88 }
89 if i > 0 {
90 prior := m.Objects[i-1]
91 if prior.Type > obj.Type || (prior.Type == obj.Type && prior.Name >= obj.Name) {
92 return m, operator.ErrRefused
93 }
94 }
95 if obj.Type == "table" {
96 tables[obj.Name] = true
97 }
98 }
99 for _, table := range m.EmptyTables {
100 if !tables[table] {
101 return m, operator.ErrRefused
102 }
103 }
104 return m, nil
105}
106
107func loadSchemaCommitment(role, id, digest string) (schemaCommitment, error) {
108 var result schemaCommitment
109 switch id {
110 case "credential-current-v1", "directory-current-v1", "directory-historical-empty-v1":
111 default:
112 return result, operator.ErrRefused
113 }
114 raw, err := admissionProfiles.ReadFile("admission_profiles/" + id + ".json")
115 if err != nil {
116 return result, operator.ErrUnavailable
117 }
118 sum := sha256.Sum256(raw)
119 if hex.EncodeToString(sum[:]) != digest {
120 return result, operator.ErrRefused
121 }
122 manifest, err := decodeSchemaCommitment(raw)
123 if err != nil || manifest.Role != role || manifest.ID != id {
124 return result, operator.ErrRefused
125 }
126 return schemaCommitment{manifest: manifest, digest: digest}, nil
127}
128
129// Caller must hold the same transaction through schema checking and any later
130// operation. Stage A supplies no transaction, database opener or production caller.
131func checkSchemaCommitment(ctx context.Context, conn *sql.Conn, selected schemaCommitment) error {
132 if ctx == nil || conn == nil {
133 return operator.ErrRefused
134 }
135 pinned, err := loadSchemaCommitment(selected.manifest.Role, selected.manifest.ID, selected.digest)
136 if err != nil {
137 return err
138 }
139 m := pinned.manifest
140 expected := map[string]string{}
141 for _, migration := range m.Migrations {
142 expected[migration.ID] = migration.Checksum
143 }
144 rows, err := conn.QueryContext(ctx, `SELECT
145 CASE WHEN typeof(id)='text' AND length(CAST(id AS BLOB))<=256 THEN id END,
146 CASE WHEN typeof(checksum)='text' AND length(CAST(checksum AS BLOB))=64 THEN checksum END
147 FROM rastrillo_migrations LIMIT 1001`)
148 if err != nil {
149 return operator.ErrRefused
150 }
151 matched, count := true, 0
152 for rows.Next() {
153 count++
154 var id, checksum sql.NullString
155 if rows.Scan(&id, &checksum) != nil {
156 rows.Close()
157 return operator.ErrRefused
158 }
159 if !id.Valid || !checksum.Valid || count > 1000 || expected[id.String] != checksum.String {
160 matched = false
161 }
162 delete(expected, id.String)
163 }
164 scanErr, closeErr := rows.Err(), rows.Close()
165 if scanErr != nil || closeErr != nil || ctx.Err() != nil || !matched || len(expected) != 0 {
166 return operator.ErrRefused
167 }
168 objects := map[string]schemaObject{}
169 for _, obj := range m.Objects {
170 objects[obj.Type+"/"+obj.Name] = obj
171 }
172 rows, err = conn.QueryContext(ctx, `SELECT
173 CASE WHEN typeof(type)='text' AND length(CAST(type AS BLOB))<=16 THEN type END,
174 CASE WHEN typeof(name)='text' AND length(CAST(name AS BLOB))<=256 THEN name END,
175 CASE WHEN typeof(tbl_name)='text' AND length(CAST(tbl_name AS BLOB))<=256 THEN tbl_name END,
176 CASE WHEN typeof(coalesce(sql,''))='text' AND length(CAST(coalesce(sql,'') AS BLOB))<=65536 THEN coalesce(sql,'') END
177 FROM sqlite_schema LIMIT 1001`)
178 if err != nil {
179 return operator.ErrRefused
180 }
181 count = 0
182 for rows.Next() {
183 count++
184 var kind, name, table, statement sql.NullString
185 if rows.Scan(&kind, &name, &table, &statement) != nil {
186 rows.Close()
187 return operator.ErrRefused
188 }
189 obj := schemaObject{kind.String, name.String, table.String, statement.String}
190 key := kind.String + "/" + name.String
191 prior, exists := objects[key]
192 if !kind.Valid || !name.Valid || !table.Valid || !statement.Valid || count > 1000 || !exists || prior != obj {
193 matched = false
194 }
195 delete(objects, key)
196 }
197 scanErr, closeErr = rows.Err(), rows.Close()
198 if scanErr != nil || closeErr != nil || ctx.Err() != nil || !matched || len(objects) != 0 {
199 return operator.ErrRefused
200 }
201 total := 0
202 for _, obj := range m.Objects {
203 if obj.Type != "table" {
204 continue
205 }
206 var count int
207 query := `SELECT count(*) FROM (SELECT 1 FROM "` + strings.ReplaceAll(obj.Name, `"`, `""`) + `" LIMIT 100001)`
208 if conn.QueryRowContext(ctx, query).Scan(&count) != nil {
209 return operator.ErrRefused
210 }
211 total += count
212 if total > 100000 {
213 return operator.ErrUnavailable
214 }
215 }
216 for _, table := range m.EmptyTables {
217 var occupied bool
218 if conn.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM "`+table+`")`).Scan(&occupied) != nil || occupied {
219 return operator.ErrRefused
220 }
221 }
222 rows, err = conn.QueryContext(ctx, "PRAGMA foreign_key_check")
223 if err != nil {
224 return operator.ErrRefused
225 }
226 bad := rows.Next()
227 scanErr, closeErr = rows.Err(), rows.Close()
228 if bad || scanErr != nil || closeErr != nil {
229 return operator.ErrRefused
230 }
231 var n int
232 if conn.QueryRowContext(ctx, "SELECT count(*) FROM (SELECT 1 FROM _litestream_lock LIMIT 1)").Scan(&n) != nil || n != 0 {
233 return operator.ErrRefused
234 }
235 if conn.QueryRowContext(ctx, "SELECT count(*) FROM (SELECT 1 FROM _litestream_seq LIMIT 2)").Scan(&n) != nil || n > 1 {
236 return operator.ErrRefused
237 }
238 if n == 1 {
239 var valid int
240 if conn.QueryRowContext(ctx, "SELECT typeof(id)='integer' AND id=1 AND typeof(seq)='integer' AND seq>=1 FROM _litestream_seq").Scan(&valid) != nil || valid != 1 {
241 return operator.ErrRefused
242 }
243 }
244 if ctx.Err() != nil {
245 return operator.ErrRefused
246 }
247 return nil
248}
249