yaml.c 8.1 KB

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