some tweaks
[mkp224o.git] / yaml.c
blobc8778a6dfca1efb8280721638a18264e40a7080c
1 #define _POSIX_C_SOURCE 200112L
3 #include <assert.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <time.h>
9 #include <pthread.h>
11 #ifndef _WIN32
12 #include <signal.h>
13 #endif
15 #include "types.h"
16 #include "yaml.h"
17 #include "ioutil.h"
18 #include "base32.h"
19 #include "base64.h"
20 #include "common.h"
22 #define LINEFEED_LEN (sizeof(char))
23 #define NULLTERM_LEN (sizeof(char))
25 static const char keys_field_generated[] = "---";
26 static const char keys_field_hostname[] = "hostname: ";
27 static const char keys_field_publickey[] = "hs_ed25519_public_key: ";
28 static const char keys_field_secretkey[] = "hs_ed25519_secret_key: ";
29 static const char keys_field_time[] = "time: ";
31 #define KEYS_FIELD_GENERATED_LEN (sizeof(keys_field_generated) - NULLTERM_LEN)
32 #define KEYS_FIELD_HOSTNAME_LEN (sizeof(keys_field_hostname) - NULLTERM_LEN)
33 #define KEYS_FIELD_PUBLICKEY_LEN (sizeof(keys_field_publickey) - NULLTERM_LEN)
34 #define KEYS_FIELD_SECRETKEY_LEN (sizeof(keys_field_secretkey) - NULLTERM_LEN)
35 #define KEYS_FIELD_TIME_LEN (sizeof(keys_field_time) - NULLTERM_LEN)
37 #define B64_PUBKEY_LEN (BASE64_TO_LEN(FORMATTED_PUBLIC_LEN))
38 #define B64_SECKEY_LEN (BASE64_TO_LEN(FORMATTED_SECRET_LEN))
39 #define B64_RAW_PUBKEY_LEN (BASE64_TO_LEN(PUBLIC_LEN))
40 #define B64_RAW_SECKEY_LEN (BASE64_TO_LEN(SECRET_LEN))
41 #define TIME_LEN 21 // strlen("2018-07-04 21:31:20 Z")
43 #define KEYS_LEN ( \
44 KEYS_FIELD_GENERATED_LEN + LINEFEED_LEN + \
45 KEYS_FIELD_HOSTNAME_LEN + ONION_LEN + LINEFEED_LEN + \
46 KEYS_FIELD_PUBLICKEY_LEN + B64_PUBKEY_LEN + LINEFEED_LEN + \
47 KEYS_FIELD_SECRETKEY_LEN + B64_SECKEY_LEN + LINEFEED_LEN + \
48 KEYS_FIELD_TIME_LEN + TIME_LEN + LINEFEED_LEN \
50 #define RAW_KEYS_LEN (KEYS_LEN \
51 - B64_PUBKEY_LEN + B64_RAW_PUBKEY_LEN \
52 - B64_SECKEY_LEN + B64_RAW_SECKEY_LEN)
54 static pthread_mutex_t tminfo_mutex;
56 void yamlout_init(void)
58 pthread_mutex_init(&tminfo_mutex,0);
61 void yamlout_clean(void)
63 pthread_mutex_destroy(&tminfo_mutex);
66 #define BUF_APPEND(buf,offset,src,srclen) \
67 do { \
68 memcpy(&buf[offset],(src),(srclen)); \
69 offset += (srclen); \
70 } while (0)
71 #define BUF_APPEND_CSTR(buf,offset,src) BUF_APPEND(buf,offset,src,strlen(src))
72 #define BUF_APPEND_CHAR(buf,offset,c) buf[offset++] = (c)
74 void yamlout_writekeys(
75 const char *hostname,const u8 *publickey,const u8 *secretkey,int rawkeys)
77 char keysbuf[KEYS_LEN];
78 char pubkeybuf[B64_PUBKEY_LEN + NULLTERM_LEN];
79 char seckeybuf[B64_SECKEY_LEN + NULLTERM_LEN];
80 char timebuf[TIME_LEN + NULLTERM_LEN];
81 size_t offset = 0;
84 BUF_APPEND(keysbuf,offset,keys_field_generated,KEYS_FIELD_GENERATED_LEN);
85 BUF_APPEND_CHAR(keysbuf,offset,'\n');
88 BUF_APPEND(keysbuf,offset,keys_field_hostname,KEYS_FIELD_HOSTNAME_LEN);
89 BUF_APPEND(keysbuf,offset,hostname,ONION_LEN);
90 BUF_APPEND_CHAR(keysbuf,offset,'\n');
93 BUF_APPEND(keysbuf,offset,keys_field_publickey,KEYS_FIELD_PUBLICKEY_LEN);
95 if (!rawkeys)
96 base64_to(pubkeybuf,publickey,FORMATTED_PUBLIC_LEN);
97 else
98 base64_to(pubkeybuf,&publickey[PKPREFIX_SIZE],PUBLIC_LEN);
100 BUF_APPEND_CSTR(keysbuf,offset,pubkeybuf);
101 BUF_APPEND_CHAR(keysbuf,offset,'\n');
104 BUF_APPEND(keysbuf,offset,keys_field_secretkey,KEYS_FIELD_SECRETKEY_LEN);
106 if (!rawkeys)
107 base64_to(seckeybuf,secretkey,FORMATTED_SECRET_LEN);
108 else
109 base64_to(seckeybuf,&secretkey[SKPREFIX_SIZE],SECRET_LEN);
111 BUF_APPEND_CSTR(keysbuf,offset,seckeybuf);
112 BUF_APPEND_CHAR(keysbuf,offset,'\n');
115 BUF_APPEND(keysbuf,offset,keys_field_time,KEYS_FIELD_TIME_LEN);
117 time_t currtime;
118 time(&currtime);
119 struct tm *tm_info;
121 pthread_mutex_lock(&tminfo_mutex);
122 tm_info = gmtime(&currtime);
123 strftime(timebuf,sizeof(timebuf),"%Y-%m-%d %H:%M:%S Z",tm_info);
124 pthread_mutex_unlock(&tminfo_mutex);
126 BUF_APPEND(keysbuf,offset,timebuf,TIME_LEN);
127 BUF_APPEND_CHAR(keysbuf,offset,'\n');
130 assert(offset == (!rawkeys ? KEYS_LEN : RAW_KEYS_LEN));
133 pthread_mutex_lock(&fout_mutex);
134 fwrite(keysbuf,offset,1,fout);
135 fflush(fout);
136 pthread_mutex_unlock(&fout_mutex);
139 #undef BUF_APPEND_CHAR
140 #undef BUF_APPEND_CSTR
141 #undef BUF_APPEND
143 // pseudo YAML parser
144 int yamlin_parseandcreate(
145 FILE *fin,char *sname,const char *onehostname,int rawkeys)
147 char line[256];
148 size_t len,cnt;
149 u8 pubbuf[BASE64_DATA_ALIGN(FORMATTED_PUBLIC_LEN)];
150 u8 secbuf[BASE64_DATA_ALIGN(FORMATTED_SECRET_LEN)];
151 int hashost = 0,haspub = 0,hassec = 0,skipthis = 0;
152 enum keytype { HOST, PUB, SEC } keyt;
154 if (rawkeys) {
155 memcpy(pubbuf,pkprefix,PKPREFIX_SIZE);
156 memcpy(secbuf,skprefix,SKPREFIX_SIZE);
159 while (!feof(fin) && !ferror(fin)) {
160 if (!fgets(line,sizeof(line),fin))
161 break;
163 len = strlen(line);
165 // trim whitespace from the end
166 while (len != 0 && (line[len-1] == ' ' || line[len-1] == '\n' || line[len-1] == '\r'))
167 line[--len] = '\0';
169 // skip empty lines
170 if (len == 0)
171 continue;
173 if (len >= 3 && line[0] == '-' && line[1] == '-' && line[2] == '-') {
174 // end of document / start of new document indicator
175 if (!skipthis && (hashost || haspub || hassec)) {
176 fprintf(stderr,"ERROR: incomplete record\n");
177 return 1;
179 hashost = haspub = hassec = skipthis = 0;
180 continue;
183 if (skipthis)
184 continue;
186 char *start = line;
187 // trim whitespace
188 while (len != 0 && *start == ' ') {
189 ++start;
190 --len;
192 // find ':'
193 char *p = start;
194 for (;*p != '\0';++p) {
195 if (*p == ':') {
196 *p++ = '\0';
197 goto foundkey;
200 // not `key: value`
201 fprintf(stderr,"ERROR: invalid syntax\n");
202 return 1; // XXX could continue too there but eh
204 foundkey:
206 if (!strcmp(start,"hostname"))
207 keyt = HOST;
208 else if (!strcmp(start,"hs_ed25519_public_key"))
209 keyt = PUB;
210 else if (!strcmp(start,"hs_ed25519_secret_key"))
211 keyt = SEC;
212 else
213 continue; // uninterested
215 // skip WS
216 while (*p == ' ')
217 ++p;
218 if (*p == '!') {
219 // skip ! tag
220 while (*p != '\0' && *p != ' ')
221 ++p;
222 // skip WS
223 while (*p == ' ')
224 ++p;
226 len = strlen(p);
227 switch (keyt) {
228 case HOST:
229 if (len != ONION_LEN ||
230 base32_valid(p,&cnt) ||
231 cnt != BASE32_TO_LEN(PUBONION_LEN) ||
232 strcmp(&p[cnt],".onion") != 0)
234 fprintf(stderr,"ERROR: invalid hostname syntax\n");
235 return 1;
237 if (!onehostname || !strcmp(onehostname,p)) {
238 memcpy(&sname[direndpos],p,len + 1);
239 hashost = 1;
240 } else
241 skipthis = 1;
242 break;
243 case PUB:
244 if (!rawkeys
245 ? (len != B64_PUBKEY_LEN || !base64_valid(p,0) ||
246 base64_from(pubbuf,p,len) != FORMATTED_PUBLIC_LEN)
247 : (len != B64_RAW_PUBKEY_LEN || !base64_valid(p,0) ||
248 base64_from(&pubbuf[PKPREFIX_SIZE],p,len) != PUBLIC_LEN))
250 fprintf(stderr,"ERROR: invalid pubkey syntax\n");
251 return 1;
253 haspub = 1;
254 break;
255 case SEC:
256 if (!rawkeys
257 ? (len != B64_SECKEY_LEN || !base64_valid(p,0) ||
258 base64_from(secbuf,p,len) != FORMATTED_SECRET_LEN)
259 : (len != B64_RAW_SECKEY_LEN || !base64_valid(p,0) ||
260 base64_from(&secbuf[SKPREFIX_SIZE],p,len) != SECRET_LEN))
262 fprintf(stderr,"ERROR: invalid seckey syntax\n");
263 return 1;
265 hassec = 1;
266 break;
268 if (hashost && haspub && hassec) {
269 #ifndef _WIN32
270 sigset_t nset,oset;
271 sigemptyset(&nset);
272 sigaddset(&nset,SIGINT);
273 sigaddset(&nset,SIGTERM);
274 sigprocmask(SIG_BLOCK,&nset,&oset);
275 #endif
276 if (createdir(sname,1) != 0) {
277 fprintf(stderr,"ERROR: could not create directory for key output\n");
278 return 1;
281 strcpy(&sname[onionendpos],"/hs_ed25519_secret_key");
282 writetofile(sname,secbuf,FORMATTED_SECRET_LEN,1);
284 strcpy(&sname[onionendpos],"/hs_ed25519_public_key");
285 writetofile(sname,pubbuf,FORMATTED_PUBLIC_LEN,0);
287 strcpy(&sname[onionendpos],"/hostname");
288 FILE *hfile = fopen(sname,"w");
289 sname[onionendpos] = '\n';
290 if (hfile) {
291 fwrite(&sname[direndpos],ONION_LEN + 1,1,hfile);
292 fclose(hfile);
294 if (fout) {
295 fwrite(&sname[printstartpos],printlen,1,fout);
296 fflush(fout);
298 #ifndef _WIN32
299 sigprocmask(SIG_SETMASK,&oset,0);
300 #endif
301 if (onehostname)
302 return 0; // finished
303 // skip rest of lines until we hit start of new doc indicator
304 skipthis = 1;
308 if (!feof(fin)) {
309 fprintf(stderr,"error while reading input\n");
310 return 1;
313 if (onehostname) {
314 fprintf(stderr,"hostname wasn't found in input\n");
315 return 1;
318 return 0;