💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › c5d868bff133f435e52df7e35af35… captured on 2024-02-05 at 09:58:34. Gemini links have been rewritten to link to archived content
-=-=-=-=-=-=-
0 /*
1 * ISC License
2 * Copyright (c) 2023 RMF <rawmonk@firemail.cc>
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <time.h>
10 #include "macro.h"
11 #include "strlcpy.h"
12 #define KNOWN_HOSTS_INTERNAL
13 #include "known_hosts.h"
14 #include "storage.h"
15 #include "error.h"
16
17 #define FILENAME "known_hosts"
18 #ifdef __OpenBSD__
19 #define TIME_T "%lld"
20 #else
21 #define TIME_T "%ld"
22 #endif
23
24 #define HT_MOD (HT_SIZE - 1)
25
26 struct known_host *known_hosts[HT_SIZE] = {NULL};
27
28 const uint32_t fnv_prime = 0x01000193;
29 const uint32_t fnv_offset = 0x811C9DC5;
30 static uint32_t fnv1a(const uint8_t *data, size_t length) {
31 uint32_t hash = fnv_offset;
32 size_t i;
33 for (i = 0; i < length; i++)
34 hash = (hash ^ data[i]) * fnv_prime;
35 return hash;
36 }
37 #define FNV1A(X) (fnv1a((uint8_t*)X, strlen(X)) & HT_MOD)
38
39 int known_hosts_add(const char *host, const char *hash,
40 time_t start, time_t end) {
41 struct known_host **ptr;
42 int index = FNV1A(host);
43 for (ptr = &known_hosts[index]; *ptr; ptr = &((*ptr)->next)) ;
44 *ptr = calloc(1, sizeof(struct known_host));
45 if (!*ptr) return ERROR_MEMORY_FAILURE;
46
47 STRLCPY((*ptr)->host, host);
48 STRLCPY((*ptr)->hash, hash);
49 (*ptr)->start = start;
50 (*ptr)->end = end;
51 return 0;
52 }
53
54 int known_hosts_load(void) {
55
56 FILE *f;
57 int ch = !EOF;
58 int ret = 0;
59 ASSERT((HT_SIZE & HT_MOD) == 0);
60
61 f = storage_fopen(FILENAME, "r");
62 if (!f) return 0;
63
64 while (ch != EOF) {
65
66 char line[1024] = {0};
67 char *host, *hash, *start, *end;
68 size_t i;
69 struct known_host known_host;
70
71 for (i = 0; i < sizeof(line); i++) {
72 ch = getc(f);
73 if (ch == EOF || ch == '\n') break;
74 if (ch == '\t') ch = ' ';
75 if (i > 0 && ch == ' ' && line[i - 1] == ' ') i--;
76 line[i] = ch;
77 }
78 if (i == sizeof(line)) continue;
79 line[i] = '\0';
80
81 host = line;
82
83 hash = strchr(host, ' ');
84 if (!hash) continue;
85 *(hash++) = '\0';
86
87 start = strchr(hash, ' ');
88 if (!start) continue;
89 *(start++) = '\0';
90
91 end = strchr(start, ' ');
92 if (!end) continue;
93 *(end++) = '\0';
94
95 known_host.start = strtoll(start, NULL, 10);
96 if (!known_host.start) continue;
97 known_host.end = strtoll(end, NULL, 10);
98 if (!known_host.end) continue;
99
100 if ((ret = known_hosts_add(host, hash,
101 known_host.start, known_host.end))) {
102 return ret;
103 }
104 }
105
106 fclose(f);
107 return ret;
108 }
109
110 int known_hosts_write(const char *host, const char *hash,
111 time_t start, time_t end) {
112 FILE *f;
113 f = storage_fopen(FILENAME, "a");
114 if (!f) return ERROR_STORAGE_ACCESS;
115 fprintf(f, "%s %s "TIME_T" "TIME_T"\n", host, hash, start, end);
116 fclose(f);
117 return 0;
118 }
119
120 int known_hosts_rewrite(void) {
121 FILE *f;
122 int i;
123
124 f = storage_fopen(FILENAME, "w");
125 if (!f) return ERROR_STORAGE_ACCESS;
126
127 for (i = 0; i < HT_SIZE; i++) {
128 struct known_host *ptr;
129 for (ptr = known_hosts[i]; ptr; ptr = ptr->next) {
130 fprintf(f, "%s %s "TIME_T" "TIME_T"\n",
131 ptr->host, ptr->hash, ptr->start, ptr->end);
132 }
133 }
134 fclose(f);
135 return 0;
136 }
137
138 struct known_host *known_hosts_get(const char *host) {
139 int index = FNV1A(host);
140 struct known_host *ptr = known_hosts[index];
141 for (; ptr; ptr = ptr->next) {
142 if (!strcmp(ptr->host, host)) break;
143 }
144 return ptr;
145 }
146
147 int known_hosts_verify(const char *host, const char *hash,
148 time_t start, time_t end) {
149
150 time_t now = time(NULL);
151 int ret;
152 struct known_host *ptr = known_hosts_get(host);
153 if (!ptr) {
154 if ((ret = known_hosts_add(host, hash, start, end)))
155 return ret;
156 return known_hosts_write(host, hash, start, end);
157 }
158 if (ptr->end < now) {
159 if (start > now) return ERROR_CERTIFICATE_AHEAD;
160 /* old certificate expired, accept new certificate */
161 STRLCPY(ptr->hash, hash);
162 ptr->start = start;
163 ptr->end = end;
164 return known_hosts_rewrite();
165 }
166 if (strcmp(ptr->hash, hash))
167 return ERROR_CERTIFICATE_MISMATCH;
168 if (ptr->end < now)
169 return ERROR_CERTIFICATE_EXPIRED;
170 return 0;
171 }
172
173 int known_hosts_forget(const char *host) {
174 struct known_host *ptr, *prev = NULL;
175 int index = FNV1A(host);
176 for (ptr = known_hosts[index]; ptr; ptr = ptr->next) {
177 if (!strcmp(ptr->host, host)) break;
178 prev = ptr;
179 }
180 if (!ptr) return ERROR_INVALID_ARGUMENT;
181 if (prev) prev->next = ptr->next;
182 else known_hosts[index] = ptr->next;
183 free(ptr);
184 return known_hosts_rewrite();
185 }
186
187 int known_hosts_expired(const char *host) {
188 struct known_host *ptr = known_hosts_get(host);
189 if (!ptr) return -1;
190 return time(NULL) > ptr->end;
191 }
192
193 void known_hosts_free(void) {
194 int i;
195 for (i = 0; i < HT_SIZE; i++) {
196 struct known_host *ptr, *next = NULL;
197 for (ptr = known_hosts[i]; ptr; ptr = next) {
198 next = ptr->next;
199 free(ptr);
200 }
201 }
202 }
203