cleanup
[mkp224o.git] / yaml.c
blob6a16965b760c0b80ec0b4d8e85a413dc327f0ba0
1 #ifdef __linux__
2 #define _POSIX_C_SOURCE 200112L
3 #endif
5 #include <assert.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <string.h>
10 #include <time.h>
11 #include <pthread.h>
13 #ifndef _WIN32
14 #include <signal.h>
15 #endif
17 #include "types.h"
18 #include "yaml.h"
19 #include "ioutil.h"
20 #include "base32.h"
21 #include "base64.h"
22 #include "common.h"
24 #define LINEFEED_LEN (sizeof(char))
25 #define NULLTERM_LEN (sizeof(char))
26 #define PATH_SEPARATOR_LEN (sizeof(char))
28 static const char keys_field_generated[] = "---";
29 static const char keys_field_hostname[] = "hostname: ";
30 static const char keys_field_publickey[] = "hs_ed25519_public_key: ";
31 static const char keys_field_secretkey[] = "hs_ed25519_secret_key: ";
32 static const char keys_field_time[] = "time: ";
34 #define KEYS_FIELD_GENERATED_LEN (sizeof(keys_field_generated) - NULLTERM_LEN)
35 #define KEYS_FIELD_HOSTNAME_LEN (sizeof(keys_field_hostname) - NULLTERM_LEN)
36 #define KEYS_FIELD_PUBLICKEY_LEN (sizeof(keys_field_publickey) - NULLTERM_LEN)
37 #define KEYS_FIELD_SECRETKEY_LEN (sizeof(keys_field_secretkey) - NULLTERM_LEN)
38 #define KEYS_FIELD_TIME_LEN (sizeof(keys_field_time) - NULLTERM_LEN)
40 #define B64_PUBKEY_LEN (BASE64_TO_LEN(FORMATTED_PUBLIC_LEN))
41 #define B64_SECKEY_LEN (BASE64_TO_LEN(FORMATTED_SECRET_LEN))
42 #define TIME_LEN (21 * sizeof(char)) // strlen("2018-07-04 21:31:20 Z")
44 #define KEYS_LEN ( \
45 KEYS_FIELD_GENERATED_LEN + LINEFEED_LEN + \
46 KEYS_FIELD_HOSTNAME_LEN + ONION_LEN + LINEFEED_LEN + \
47 KEYS_FIELD_PUBLICKEY_LEN + B64_PUBKEY_LEN + LINEFEED_LEN + \
48 KEYS_FIELD_SECRETKEY_LEN + B64_SECKEY_LEN + LINEFEED_LEN + \
49 KEYS_FIELD_TIME_LEN + TIME_LEN + LINEFEED_LEN \
52 static pthread_mutex_t tminfo_mutex;
54 void yamlout_init()
56 pthread_mutex_init(&tminfo_mutex,0);
59 void yamlout_clean()
61 pthread_mutex_destroy(&tminfo_mutex);
64 #define BUF_APPEND(buf,offset,src,srclen) \
65 do { \
66 memcpy(&buf[offset],(src),(srclen)); \
67 offset += (srclen); \
68 } while (0)
69 #define BUF_APPEND_CSTR(buf,offset,src) BUF_APPEND(buf,offset,src,strlen(src))
70 #define BUF_APPEND_CHAR(buf,offset,c) buf[offset++] = (c)
72 void yamlout_writekeys(const char *hostname,const u8 *formated_public,const u8 *formated_secret)
74 char keysbuf[KEYS_LEN];
75 char pubkeybuf[B64_PUBKEY_LEN + NULLTERM_LEN];
76 char seckeybuf[B64_SECKEY_LEN + NULLTERM_LEN];
77 char timebuf[TIME_LEN + NULLTERM_LEN];
78 size_t offset = 0;
80 BUF_APPEND(keysbuf,offset,keys_field_generated,KEYS_FIELD_GENERATED_LEN);
81 BUF_APPEND_CHAR(keysbuf,offset,'\n');
83 BUF_APPEND(keysbuf,offset,keys_field_hostname,KEYS_FIELD_HOSTNAME_LEN);
84 BUF_APPEND(keysbuf,offset,hostname,ONION_LEN);
85 BUF_APPEND_CHAR(keysbuf,offset,'\n');
87 BUF_APPEND(keysbuf,offset,keys_field_publickey,KEYS_FIELD_PUBLICKEY_LEN);
88 base64_to(pubkeybuf,formated_public,FORMATTED_PUBLIC_LEN);
89 BUF_APPEND(keysbuf,offset,pubkeybuf,B64_PUBKEY_LEN);
90 BUF_APPEND_CHAR(keysbuf,offset,'\n');
92 BUF_APPEND(keysbuf,offset,keys_field_secretkey,KEYS_FIELD_SECRETKEY_LEN);
93 base64_to(seckeybuf,formated_secret,FORMATTED_SECRET_LEN);
94 BUF_APPEND(keysbuf,offset,seckeybuf,B64_SECKEY_LEN);
95 BUF_APPEND_CHAR(keysbuf,offset,'\n');
97 BUF_APPEND(keysbuf,offset,keys_field_time,KEYS_FIELD_TIME_LEN);
99 time_t currtime;
100 time(&currtime);
101 struct tm *tm_info;
103 pthread_mutex_lock(&tminfo_mutex);
104 tm_info = gmtime(&currtime);
105 strftime(timebuf,sizeof(timebuf),"%Y-%m-%d %H:%M:%S Z",tm_info);
106 pthread_mutex_unlock(&tminfo_mutex);
108 BUF_APPEND(keysbuf,offset,timebuf,TIME_LEN);
109 BUF_APPEND_CHAR(keysbuf,offset,'\n');
111 assert(offset == KEYS_LEN);
113 pthread_mutex_lock(&fout_mutex);
114 fwrite(keysbuf,sizeof(keysbuf),1,fout);
115 fflush(fout);
116 pthread_mutex_unlock(&fout_mutex);
119 #undef BUF_APPEND_CHAR
120 #undef BUF_APPEND_CSTR
121 #undef BUF_APPEND
123 // pseudo YAML parser
124 int yamlin_parseandcreate(FILE *fin,char *sname,const char *hostname)
126 char line[256];
127 size_t len,cnt;
128 u8 pubbuf[BASE64_DATA_ALIGN(FORMATTED_PUBLIC_LEN)];
129 u8 secbuf[BASE64_DATA_ALIGN(FORMATTED_SECRET_LEN)];
130 int hashost = 0,haspub = 0,hassec = 0,skipthis = 0;
131 enum keytype { HOST, PUB, SEC } keyt;
133 while (!feof(fin) && !ferror(fin)) {
134 if (!fgets(line,sizeof(line),fin))
135 break;
137 len = strlen(line);
139 // trim whitespace from the end
140 while (len != 0 && (line[len-1] == ' ' || line[len-1] == '\n' || line[len-1] == '\r'))
141 line[--len] = '\0';
143 // skip empty lines
144 if (len == 0)
145 continue;
147 if (len >= 3 && line[0] == '-' && line[1] == '-' && line[2] == '-') {
148 // end of document indicator
149 if (!skipthis && (hashost || haspub || hassec)) {
150 fprintf(stderr,"ERROR: incomplete record\n");
151 return 1;
153 hashost = haspub = hassec = skipthis = 0;
154 continue;
157 if (skipthis)
158 continue;
160 char *start = line;
161 // trim whitespace
162 while (len != 0 && *start == ' ') {
163 ++start;
164 --len;
166 // find ':'
167 char *p = start;
168 for (;*p != '\0';++p) {
169 if (*p == ':') {
170 *p++ = '\0';
171 goto foundkey;
174 // not `key: value`
175 fprintf(stderr,"ERROR: invalid syntax\n");
176 return 1; // XXX could continue too there but eh
178 foundkey:
180 if (!strcmp(start,"hostname"))
181 keyt = HOST;
182 else if (!strcmp(start,"hs_ed25519_public_key"))
183 keyt = PUB;
184 else if (!strcmp(start,"hs_ed25519_secret_key"))
185 keyt = SEC;
186 else
187 continue; // uninterested
189 // skip WS
190 while (*p == ' ')
191 ++p;
192 if (*p == '!') {
193 // skip ! tag
194 while (*p != '\0' && *p != ' ')
195 ++p;
196 // skip WS
197 while (*p == ' ')
198 ++p;
200 len = strlen(p);
201 switch (keyt) {
202 case HOST:
203 if (len != ONION_LEN ||
204 base32_valid(p,&cnt) ||
205 cnt != BASE32_TO_LEN(PUBONION_LEN) ||
206 strcmp(&p[cnt],".onion") != 0)
208 fprintf(stderr,"ERROR: invalid hostname syntax\n");
209 return 1;
211 if (!hostname || !strcmp(hostname,p)) {
212 memcpy(&sname[direndpos],p,len + 1);
213 hashost = 1;
214 } else
215 skipthis = 1;
216 break;
217 case PUB:
218 if (len != B64_PUBKEY_LEN || !base64_valid(p,0) ||
219 base64_from(pubbuf,p,len) != FORMATTED_PUBLIC_LEN)
221 fprintf(stderr,"ERROR: invalid pubkey syntax\n");
222 return 1;
224 haspub = 1;
225 break;
226 case SEC:
227 if (len != B64_SECKEY_LEN || !base64_valid(p,0) ||
228 base64_from(secbuf,p,len) != FORMATTED_SECRET_LEN)
230 fprintf(stderr,"ERROR: invalid seckey syntax\n");
231 return 1;
233 hassec = 1;
234 break;
236 if (hashost && haspub && hassec) {
237 #ifndef _WIN32
238 sigset_t nset,oset;
239 sigemptyset(&nset);
240 sigaddset(&nset,SIGINT);
241 sigaddset(&nset,SIGTERM);
242 sigprocmask(SIG_BLOCK,&nset,&oset);
243 #endif
244 if (createdir(sname,1) != 0) {
245 fprintf(stderr,"ERROR: could not create directory for key output\n");
246 return 1;
249 strcpy(&sname[onionendpos],"/hs_ed25519_secret_key");
250 writetofile(sname,secbuf,FORMATTED_SECRET_LEN,1);
252 strcpy(&sname[onionendpos],"/hs_ed25519_public_key");
253 writetofile(sname,pubbuf,FORMATTED_PUBLIC_LEN,0);
255 strcpy(&sname[onionendpos],"/hostname");
256 FILE *hfile = fopen(sname,"w");
257 sname[onionendpos] = '\n';
258 if (hfile) {
259 fwrite(&sname[direndpos],ONION_LEN + 1,1,hfile);
260 fclose(hfile);
262 if (fout) {
263 fwrite(&sname[printstartpos],printlen,1,fout);
264 fflush(fout);
266 #ifndef _WIN32
267 sigprocmask(SIG_SETMASK,&oset,0);
268 #endif
269 if (hostname)
270 return 0; // finished
271 skipthis = 1;
275 if (!feof(fin)) {
276 fprintf(stderr,"error while reading input\n");
277 return 1;
280 if (hostname) {
281 fprintf(stderr,"hostname wasn't found in input\n");
282 return 1;
285 return 0;