123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- #define _POSIX_C_SOURCE 200112L
- #include <assert.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <string.h>
- #include <time.h>
- #include <pthread.h>
- #ifndef _WIN32
- #include <signal.h>
- #endif
- #include "types.h"
- #include "yaml.h"
- #include "ioutil.h"
- #include "base32.h"
- #include "base64.h"
- #include "common.h"
- #define LINEFEED_LEN (sizeof(char))
- #define NULLTERM_LEN (sizeof(char))
- static const char keys_field_generated[] = "---";
- static const char keys_field_hostname[] = "hostname: ";
- static const char keys_field_publickey[] = "hs_ed25519_public_key: ";
- static const char keys_field_secretkey[] = "hs_ed25519_secret_key: ";
- static const char keys_field_time[] = "time: ";
- #define KEYS_FIELD_GENERATED_LEN (sizeof(keys_field_generated) - NULLTERM_LEN)
- #define KEYS_FIELD_HOSTNAME_LEN (sizeof(keys_field_hostname) - NULLTERM_LEN)
- #define KEYS_FIELD_PUBLICKEY_LEN (sizeof(keys_field_publickey) - NULLTERM_LEN)
- #define KEYS_FIELD_SECRETKEY_LEN (sizeof(keys_field_secretkey) - NULLTERM_LEN)
- #define KEYS_FIELD_TIME_LEN (sizeof(keys_field_time) - NULLTERM_LEN)
- #define B64_PUBKEY_LEN (BASE64_TO_LEN(FORMATTED_PUBLIC_LEN))
- #define B64_SECKEY_LEN (BASE64_TO_LEN(FORMATTED_SECRET_LEN))
- #define B64_RAW_PUBKEY_LEN (BASE64_TO_LEN(PUBLIC_LEN))
- #define B64_RAW_SECKEY_LEN (BASE64_TO_LEN(SECRET_LEN))
- #define TIME_LEN 21 // strlen("2018-07-04 21:31:20 Z")
- #define KEYS_LEN ( \
- KEYS_FIELD_GENERATED_LEN + LINEFEED_LEN + \
- KEYS_FIELD_HOSTNAME_LEN + ONION_LEN + LINEFEED_LEN + \
- KEYS_FIELD_PUBLICKEY_LEN + B64_PUBKEY_LEN + LINEFEED_LEN + \
- KEYS_FIELD_SECRETKEY_LEN + B64_SECKEY_LEN + LINEFEED_LEN + \
- KEYS_FIELD_TIME_LEN + TIME_LEN + LINEFEED_LEN \
- )
- #define RAW_KEYS_LEN (KEYS_LEN \
- - B64_PUBKEY_LEN + B64_RAW_PUBKEY_LEN \
- - B64_SECKEY_LEN + B64_RAW_SECKEY_LEN)
- static pthread_mutex_t tminfo_mutex;
- void yamlout_init(void)
- {
- pthread_mutex_init(&tminfo_mutex,0);
- }
- void yamlout_clean(void)
- {
- pthread_mutex_destroy(&tminfo_mutex);
- }
- #define BUF_APPEND(buf,offset,src,srclen) \
- do { \
- memcpy(&buf[offset],(src),(srclen)); \
- offset += (srclen); \
- } while (0)
- #define BUF_APPEND_CSTR(buf,offset,src) BUF_APPEND(buf,offset,src,strlen(src))
- #define BUF_APPEND_CHAR(buf,offset,c) buf[offset++] = (c)
- void yamlout_writekeys(
- const char *hostname,const u8 *publickey,const u8 *secretkey,int rawkeys)
- {
- char keysbuf[KEYS_LEN];
- char pubkeybuf[B64_PUBKEY_LEN + NULLTERM_LEN];
- char seckeybuf[B64_SECKEY_LEN + NULLTERM_LEN];
- char timebuf[TIME_LEN + NULLTERM_LEN];
- size_t offset = 0;
- BUF_APPEND(keysbuf,offset,keys_field_generated,KEYS_FIELD_GENERATED_LEN);
- BUF_APPEND_CHAR(keysbuf,offset,'\n');
- BUF_APPEND(keysbuf,offset,keys_field_hostname,KEYS_FIELD_HOSTNAME_LEN);
- BUF_APPEND(keysbuf,offset,hostname,ONION_LEN);
- BUF_APPEND_CHAR(keysbuf,offset,'\n');
- BUF_APPEND(keysbuf,offset,keys_field_publickey,KEYS_FIELD_PUBLICKEY_LEN);
- if (!rawkeys)
- base64_to(pubkeybuf,publickey,FORMATTED_PUBLIC_LEN);
- else
- base64_to(pubkeybuf,&publickey[PKPREFIX_SIZE],PUBLIC_LEN);
- BUF_APPEND_CSTR(keysbuf,offset,pubkeybuf);
- BUF_APPEND_CHAR(keysbuf,offset,'\n');
- BUF_APPEND(keysbuf,offset,keys_field_secretkey,KEYS_FIELD_SECRETKEY_LEN);
- if (!rawkeys)
- base64_to(seckeybuf,secretkey,FORMATTED_SECRET_LEN);
- else
- base64_to(seckeybuf,&secretkey[SKPREFIX_SIZE],SECRET_LEN);
- BUF_APPEND_CSTR(keysbuf,offset,seckeybuf);
- BUF_APPEND_CHAR(keysbuf,offset,'\n');
- BUF_APPEND(keysbuf,offset,keys_field_time,KEYS_FIELD_TIME_LEN);
- time_t currtime;
- time(&currtime);
- struct tm *tm_info;
- pthread_mutex_lock(&tminfo_mutex);
- tm_info = gmtime(&currtime);
- strftime(timebuf,sizeof(timebuf),"%Y-%m-%d %H:%M:%S Z",tm_info);
- pthread_mutex_unlock(&tminfo_mutex);
- BUF_APPEND(keysbuf,offset,timebuf,TIME_LEN);
- BUF_APPEND_CHAR(keysbuf,offset,'\n');
- assert(offset == (!rawkeys ? KEYS_LEN : RAW_KEYS_LEN));
- pthread_mutex_lock(&fout_mutex);
- fwrite(keysbuf,offset,1,fout);
- fflush(fout);
- pthread_mutex_unlock(&fout_mutex);
- }
- #undef BUF_APPEND_CHAR
- #undef BUF_APPEND_CSTR
- #undef BUF_APPEND
- // pseudo YAML parser
- int yamlin_parseandcreate(
- FILE *fin,char *sname,const char *onehostname,int rawkeys)
- {
- char line[256];
- size_t len,cnt;
- u8 pubbuf[BASE64_DATA_ALIGN(FORMATTED_PUBLIC_LEN)];
- u8 secbuf[BASE64_DATA_ALIGN(FORMATTED_SECRET_LEN)];
- int hashost = 0,haspub = 0,hassec = 0,skipthis = 0;
- enum keytype { HOST, PUB, SEC } keyt;
- if (rawkeys) {
- memcpy(pubbuf,pkprefix,PKPREFIX_SIZE);
- memcpy(secbuf,skprefix,SKPREFIX_SIZE);
- }
- while (!feof(fin) && !ferror(fin)) {
- if (!fgets(line,sizeof(line),fin))
- break;
- len = strlen(line);
- // trim whitespace from the end
- while (len != 0 && (line[len-1] == ' ' || line[len-1] == '\n' || line[len-1] == '\r'))
- line[--len] = '\0';
- // skip empty lines
- if (len == 0)
- continue;
- if (len >= 3 && line[0] == '-' && line[1] == '-' && line[2] == '-') {
- // end of document / start of new document indicator
- if (!skipthis && (hashost || haspub || hassec)) {
- fprintf(stderr,"ERROR: incomplete record\n");
- return 1;
- }
- hashost = haspub = hassec = skipthis = 0;
- continue;
- }
- if (skipthis)
- continue;
- char *start = line;
- // trim whitespace
- while (len != 0 && *start == ' ') {
- ++start;
- --len;
- }
- // find ':'
- char *p = start;
- for (;*p != '\0';++p) {
- if (*p == ':') {
- *p++ = '\0';
- goto foundkey;
- }
- }
- // not `key: value`
- fprintf(stderr,"ERROR: invalid syntax\n");
- return 1; // XXX could continue too there but eh
- foundkey:
- if (!strcmp(start,"hostname"))
- keyt = HOST;
- else if (!strcmp(start,"hs_ed25519_public_key"))
- keyt = PUB;
- else if (!strcmp(start,"hs_ed25519_secret_key"))
- keyt = SEC;
- else
- continue; // uninterested
- // skip WS
- while (*p == ' ')
- ++p;
- if (*p == '!') {
- // skip ! tag
- while (*p != '\0' && *p != ' ')
- ++p;
- // skip WS
- while (*p == ' ')
- ++p;
- }
- len = strlen(p);
- switch (keyt) {
- case HOST:
- if (len != ONION_LEN ||
- base32_valid(p,&cnt) ||
- cnt != BASE32_TO_LEN(PUBONION_LEN) ||
- strcmp(&p[cnt],".onion") != 0)
- {
- fprintf(stderr,"ERROR: invalid hostname syntax\n");
- return 1;
- }
- if (!onehostname || !strcmp(onehostname,p)) {
- memcpy(&sname[direndpos],p,len + 1);
- hashost = 1;
- } else
- skipthis = 1;
- break;
- case PUB:
- if (!rawkeys
- ? (len != B64_PUBKEY_LEN || !base64_valid(p,0) ||
- base64_from(pubbuf,p,len) != FORMATTED_PUBLIC_LEN)
- : (len != B64_RAW_PUBKEY_LEN || !base64_valid(p,0) ||
- base64_from(&pubbuf[PKPREFIX_SIZE],p,len) != PUBLIC_LEN))
- {
- fprintf(stderr,"ERROR: invalid pubkey syntax\n");
- return 1;
- }
- haspub = 1;
- break;
- case SEC:
- if (!rawkeys
- ? (len != B64_SECKEY_LEN || !base64_valid(p,0) ||
- base64_from(secbuf,p,len) != FORMATTED_SECRET_LEN)
- : (len != B64_RAW_SECKEY_LEN || !base64_valid(p,0) ||
- base64_from(&secbuf[SKPREFIX_SIZE],p,len) != SECRET_LEN))
- {
- fprintf(stderr,"ERROR: invalid seckey syntax\n");
- return 1;
- }
- hassec = 1;
- break;
- }
- if (hashost && haspub && hassec) {
- #ifndef _WIN32
- sigset_t nset,oset;
- sigemptyset(&nset);
- sigaddset(&nset,SIGINT);
- sigaddset(&nset,SIGTERM);
- sigprocmask(SIG_BLOCK,&nset,&oset);
- #endif
- if (createdir(sname,1) != 0) {
- fprintf(stderr,"ERROR: could not create directory \"%s\" for key output\n",sname);
- return 1;
- }
- strcpy(&sname[onionendpos],"/hs_ed25519_secret_key");
- writetofile(sname,secbuf,FORMATTED_SECRET_LEN,1);
- strcpy(&sname[onionendpos],"/hs_ed25519_public_key");
- writetofile(sname,pubbuf,FORMATTED_PUBLIC_LEN,0);
- strcpy(&sname[onionendpos],"/hostname");
- FILE *hfile = fopen(sname,"w");
- sname[onionendpos] = '\n';
- if (hfile) {
- fwrite(&sname[direndpos],ONION_LEN + 1,1,hfile);
- fclose(hfile);
- }
- if (fout) {
- fwrite(&sname[printstartpos],printlen,1,fout);
- fflush(fout);
- }
- #ifndef _WIN32
- sigprocmask(SIG_SETMASK,&oset,0);
- #endif
- if (onehostname)
- return 0; // finished
- // skip rest of lines until we hit start of new doc indicator
- skipthis = 1;
- }
- }
- if (!feof(fin)) {
- fprintf(stderr,"error while reading input\n");
- return 1;
- }
- if (onehostname) {
- fprintf(stderr,"hostname wasn't found in input\n");
- return 1;
- }
- return 0;
- }
|