p≡p JSON adapter
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

581 lines
17 KiB

  1. #define _EXPORT_PEP_ENGINE_DLL
  2. #include "platform.h"
  3. #include "pEp_internal.h"
  4. // #include "pgp_gpg.h"
  5. #include <limits.h>
  6. #include "wrappers.h"
  7. #define _GPGERR(X) ((X) & 0xffffL)
  8. static void *gpgme;
  9. static struct gpg_s gpg;
  10. struct _str_ptr_and_bit {
  11. const char* key;
  12. int bit;
  13. };
  14. typedef struct _str_ptr_and_bit str_ptr_and_bit;
  15. int strptrcmp(const void* a, const void* b) {
  16. return (int)((((str_ptr_and_bit*)(a))->key) - (((str_ptr_and_bit*)(b))->key));
  17. }
  18. bool quickfix_config(stringlist_t* keys, const char* config_file_path) {
  19. static char buf[MAX_LINELENGTH];
  20. size_t num_keys = stringlist_length(keys);
  21. // This function does:
  22. // 1. find a non-existent backup name numbered from 0 to 99 (otherwise fails)
  23. // 2. read the original file and meanwhile write the backup copy
  24. // 3. write the new config file to a temporary file in the same directory
  25. // 4. rename the temp file replacing the original config file
  26. // 5. on Windows remove the left-overs
  27. /* Find a suitable backup file name, without trashing previous ones */
  28. char* backup_file_path = NULL;
  29. size_t backup_file_path_baselen = strlen(config_file_path);
  30. FILE *backup_file = 0;
  31. int ret;
  32. int found = 0;
  33. int i;
  34. char* temp_config_file_path = NULL;
  35. char* s = NULL;
  36. stringlist_t* _k;
  37. stringlist_t* lines = new_stringlist(NULL);
  38. FILE *f;
  39. FILE *temp_config_file;
  40. stringlist_t* cur_string;
  41. bool status = false;
  42. #ifdef WIN32
  43. WIN32_FIND_DATA FindFileData;
  44. HANDLE handle;
  45. const char* line_end = "\r\n";
  46. #else
  47. const char* line_end = "\n";
  48. #endif
  49. // If we bork it up somehow, we don't go beyond 100 tries...
  50. for (int nr = 0; nr < 99; nr++) {
  51. backup_file_path = (char*)calloc(backup_file_path_baselen + 12, 1); // .99.pep.old\0
  52. ret = snprintf(backup_file_path, backup_file_path_baselen + 12,
  53. "%s.%d.pep.bkp", config_file_path, nr);
  54. assert(ret >= 0); // snprintf(2)
  55. if (ret < 0) {
  56. goto quickfix_error; // frees backup_file_path
  57. }
  58. #ifdef WIN32
  59. // The fopen(.., "x") is not documented on Windows (fopen_s actually respects it, but...).
  60. // So we make an extra check for the existence of the file. This introduces a possible
  61. // race-condition, but it has little effect even if we incur into it.
  62. handle = FindFirstFile(backup_file_path, &FindFileData);
  63. if (handle != INVALID_HANDLE_VALUE) {
  64. FindClose(handle);
  65. free(backup_file_path);
  66. backup_file_path = NULL;
  67. continue;
  68. }
  69. FindClose(handle);
  70. backup_file = Fopen(backup_file_path, "wb");
  71. #else
  72. backup_file = Fopen(backup_file_path, "wbx"); // the 'x' is important
  73. #endif
  74. if (backup_file <= 0) {
  75. free(backup_file_path);
  76. backup_file_path = NULL;
  77. continue;
  78. }
  79. break;
  80. }
  81. if (!backup_file_path)
  82. goto quickfix_error;
  83. if (backup_file <= 0)
  84. goto quickfix_error;
  85. // Open original file, parse it, and meanwhile write a backup copy
  86. f = Fopen(config_file_path, "rb");
  87. if (f == NULL)
  88. goto quickfix_error;
  89. ret = Fprintf(backup_file, "# Backup created by pEp.%s"
  90. "# If GnuPG and pEp work smoothly this file may safely be removed.%s%s",
  91. line_end, line_end, line_end);
  92. // Go through every line in the file
  93. str_ptr_and_bit *found_keys = NULL;
  94. while ((s = Fgets(buf, MAX_LINELENGTH, f))) {
  95. // pointers to the keys found in this string
  96. found_keys = (str_ptr_and_bit*)(calloc(num_keys, sizeof(str_ptr_and_bit)));
  97. int num_found_keys = 0;
  98. ret = Fprintf(backup_file, "%s", s);
  99. assert(ret >= 0);
  100. if (ret < 0) {
  101. free(found_keys);
  102. found_keys = NULL;
  103. goto quickfix_error;
  104. }
  105. char* rest;
  106. char* line_token = strtok_r(s, "\r\n", &rest);
  107. if (!line_token)
  108. line_token = s;
  109. if (*line_token == '\n' || *line_token == '\r')
  110. line_token = "";
  111. if (*line_token == '#' || *line_token == '\0') {
  112. stringlist_add(lines, strdup(line_token));
  113. continue;
  114. }
  115. bool only_key_on_line = false;
  116. for (_k = keys, i = 1; _k; _k = _k->next, i<<=1) {
  117. char* keypos = strstr(line_token, _k->value);
  118. if (!keypos)
  119. continue;
  120. size_t keystr_len = strlen(_k->value);
  121. char* nextpos = keypos + keystr_len;
  122. bool notkey = false;
  123. if (keypos != line_token) {
  124. char prevchar = *(keypos - 1);
  125. switch (prevchar) {
  126. case '-':
  127. case ':':
  128. case '/':
  129. case '\\':
  130. notkey = true;
  131. break;
  132. default:
  133. break;
  134. }
  135. }
  136. if (*nextpos && !notkey) {
  137. char nextchar = *nextpos;
  138. switch (nextchar) {
  139. case '-':
  140. case ':':
  141. case '/':
  142. case '\\':
  143. notkey = true;
  144. break;
  145. default:
  146. break;
  147. }
  148. }
  149. else if (line_token == keypos) {
  150. only_key_on_line = true;
  151. if (!(found & i)) {
  152. found |= i;
  153. stringlist_add(lines, strdup(line_token));
  154. num_found_keys++;
  155. }
  156. break;
  157. }
  158. if (!notkey) {
  159. // Ok, it's not just the key with a null terminator. So...
  160. // add a pointer to the key to the list from this string
  161. found_keys[num_found_keys].key = keypos;
  162. found_keys[num_found_keys].bit = i;
  163. num_found_keys++;
  164. }
  165. // Check to see if there are more annoying occurences of this
  166. // key in the string
  167. for (keypos = strstr(nextpos, _k->value);
  168. keypos; keypos = strstr(nextpos, _k->value)) {
  169. notkey = false;
  170. nextpos = keypos + keystr_len;
  171. char prevchar = *(keypos - 1);
  172. switch (prevchar) {
  173. case '-':
  174. case ':':
  175. case '/':
  176. case '\\':
  177. notkey = true;
  178. break;
  179. default:
  180. break;
  181. }
  182. if (!notkey) {
  183. char nextchar = *nextpos;
  184. switch (nextchar) {
  185. case '-':
  186. case ':':
  187. case '/':
  188. case '\\':
  189. notkey = true;
  190. break;
  191. default:
  192. break;
  193. }
  194. }
  195. if (notkey)
  196. continue;
  197. if (num_found_keys >= num_keys)
  198. found_keys = (str_ptr_and_bit*)realloc(found_keys, (num_found_keys + 1) * sizeof(str_ptr_and_bit));
  199. found_keys[num_found_keys].key = keypos;
  200. found_keys[num_found_keys].bit = i;
  201. num_found_keys++;
  202. }
  203. }
  204. if (!only_key_on_line) {
  205. if (num_found_keys == 0)
  206. stringlist_add(lines, strdup(line_token));
  207. else if (num_found_keys == 1 && (line_token == found_keys[0].key)) {
  208. if (!(found & found_keys[0].bit)) {
  209. stringlist_add(lines, strdup(line_token));
  210. found |= found_keys[0].bit;
  211. }
  212. }
  213. else {
  214. qsort(found_keys, num_found_keys, sizeof(str_ptr_and_bit), strptrcmp);
  215. int j;
  216. const char* curr_start = line_token;
  217. const char* next_start = NULL;
  218. for (j = 0; j < num_found_keys; j++, curr_start = next_start) {
  219. next_start = found_keys[j].key;
  220. if (curr_start == next_start)
  221. continue;
  222. size_t copy_len = next_start - curr_start;
  223. const char* movable_end = next_start - 1;
  224. while (copy_len > 0 && (*movable_end == ' ' || *movable_end == '\t' || *movable_end == '\0')) {
  225. movable_end--;
  226. copy_len--;
  227. }
  228. if (copy_len > 0) {
  229. if (j == 0 || !(found & found_keys[j - 1].bit)) {
  230. // if j is 0 here, the first thing in the string wasn't a key, or we'd have continued.
  231. // otherwise, regardless of the value of j, we check that the "last" key (j-1) isn't already
  232. // found and dealt with.
  233. // Having passed that, we copy.
  234. stringlist_add(lines, strndup(curr_start, copy_len));
  235. if (j > 0)
  236. found |= found_keys[j-1].bit;
  237. }
  238. }
  239. }
  240. if (!(found & found_keys[num_found_keys - 1].bit)) {
  241. stringlist_add(lines, strdup(found_keys[num_found_keys - 1].key));
  242. found |= found_keys[num_found_keys - 1].bit;
  243. }
  244. }
  245. }
  246. free(found_keys);
  247. found_keys = NULL;
  248. } // End of file
  249. // Now do the failsafe writing dance
  250. ret = Fclose(f);
  251. assert(ret == 0);
  252. if (ret != 0)
  253. goto quickfix_error;
  254. ret = Fclose(backup_file);
  255. assert(ret == 0);
  256. if (ret != 0)
  257. goto quickfix_error;
  258. // 2. Write the new config file to a temporary file in the same directory
  259. assert(backup_file_path_baselen != NULL);
  260. temp_config_file_path = (char*)calloc(backup_file_path_baselen + 8, 1); // .XXXXXX\0
  261. ret = snprintf(temp_config_file_path, backup_file_path_baselen + 8, "%s.XXXXXX", config_file_path);
  262. assert(ret >= 0);
  263. if (ret < 0)
  264. goto quickfix_error;
  265. int temp_config_filedesc = Mkstemp(temp_config_file_path);
  266. assert(temp_config_filedesc != -1);
  267. if (temp_config_filedesc == -1)
  268. goto quickfix_error;
  269. temp_config_file = Fdopen(temp_config_filedesc, "wb"); // no "b" in fdopen() is documentend, use freopen()
  270. assert(temp_config_file != NULL);
  271. if (temp_config_file == NULL)
  272. goto quickfix_error;
  273. // temp_config_file = Freopen(config_file_path, "wb", temp_config_file);
  274. // assert(temp_config_file != NULL);
  275. // if (temp_config_file == NULL)
  276. // goto quickfix_error;
  277. ret = Fprintf(temp_config_file, "# File re-created by pEp%s"
  278. "# See backup in '%s'%s%s", line_end,
  279. backup_file_path,
  280. line_end, line_end);
  281. assert(ret >= 0);
  282. if (ret < 0)
  283. goto quickfix_error;
  284. for (cur_string = lines; cur_string; cur_string = cur_string->next) {
  285. assert(cur_string->value != NULL);
  286. ret = Fprintf(temp_config_file, "%s%s", cur_string->value, line_end);
  287. assert(ret >= 0);
  288. if (ret < 0)
  289. goto quickfix_error;
  290. }
  291. ret = Fclose(temp_config_file);
  292. assert(ret == 0);
  293. if (ret != 0)
  294. goto quickfix_error;
  295. #ifdef WIN32
  296. ret = !(0 == ReplaceFile(config_file_path, temp_config_file_path, NULL, 0, NULL, NULL));
  297. assert(ret == 0);
  298. if (ret != 0)
  299. goto quickfix_error;
  300. ret = unlink(temp_config_file_path);
  301. #else
  302. ret = rename(temp_config_file_path, config_file_path);
  303. // ret = 0;
  304. #endif
  305. assert(ret == 0);
  306. if (ret != 0)
  307. goto quickfix_error;
  308. free(temp_config_file_path);
  309. temp_config_file_path = NULL;
  310. status = true;
  311. free(backup_file_path);
  312. goto quickfix_success;
  313. quickfix_error:
  314. assert(status == false);
  315. free(backup_file_path);
  316. quickfix_success:
  317. // assert(found_keys == NULL);
  318. if (found_keys) {
  319. free(found_keys);
  320. found_keys = NULL;
  321. }
  322. if (temp_config_file_path)
  323. free(temp_config_file_path);
  324. free_stringlist(lines);
  325. return status;
  326. }
  327. static bool ensure_config_values(stringlist_t *keys, stringlist_t *values, const char* config_file_path)
  328. {
  329. static char buf[MAX_LINELENGTH];
  330. int r;
  331. stringlist_t *_k;
  332. stringlist_t *_v;
  333. unsigned int i;
  334. unsigned int found = 0;
  335. bool eof_nl = 0;
  336. char * rest;
  337. char * token;
  338. char * s;
  339. const char* line_end;
  340. #ifdef WIN32
  341. line_end = "\r\n";
  342. #else
  343. line_end = "\n";
  344. #endif
  345. FILE *f = Fopen(config_file_path, "rb");
  346. if (f == NULL && errno == ENOMEM)
  347. return false;
  348. if (f != NULL) {
  349. int length = stringlist_length(keys);
  350. // make sure we 1) have the same number of keys and values
  351. // and 2) we don't have more key/value pairs than
  352. // the size of the bitfield used to hold the indices
  353. // of key/value pairs matching keys in the config file.
  354. assert(length <= sizeof(unsigned int) * CHAR_BIT);
  355. assert(length == stringlist_length(values));
  356. if (!(length == stringlist_length(values) &&
  357. length <= sizeof(unsigned int) * CHAR_BIT)) {
  358. Fclose(f);
  359. return false;
  360. }
  361. while ((s = Fgets(buf, MAX_LINELENGTH, f))) {
  362. token = strtok_r(s, " \t\r\n", &rest);
  363. for (_k = keys, _v = values, i = 1;
  364. _k != NULL;
  365. _k = _k->next, _v = _v->next, i <<= 1) {
  366. if (((found & i) != i) && token &&
  367. (strncmp(token, _k->value, strlen(_k->value)) == 0)) {
  368. found |= i;
  369. break;
  370. }
  371. }
  372. if (feof(f)) {
  373. eof_nl = 1;
  374. break;
  375. }
  376. }
  377. if (!s && ferror(f))
  378. return false;
  379. f = Freopen(config_file_path, "ab", f);
  380. }
  381. else {
  382. #ifdef WIN32
  383. f = Fopen(config_file_path, "wb");
  384. #else
  385. f = Fopen(config_file_path, "wbx");
  386. #endif
  387. }
  388. assert(f);
  389. if (f == NULL)
  390. return false;
  391. if (eof_nl)
  392. r = Fprintf(f, line_end);
  393. for (i = 1, _k = keys, _v = values; _k != NULL; _k = _k->next,
  394. _v = _v->next, i <<= 1) {
  395. if ((found & i) == 0) {
  396. r = Fprintf(f, "%s %s%s", _k->value, _v->value, line_end);
  397. assert(r >= 0);
  398. if (r < 0)
  399. return false;
  400. }
  401. }
  402. r = Fclose(f);
  403. assert(r == 0);
  404. if (r != 0)
  405. return false;
  406. return true;
  407. }
  408. int main(int argc, char** argv)
  409. {
  410. PEP_STATUS status = PEP_STATUS_OK;
  411. bool bResult;
  412. #if defined(WIN32) || defined(NDEBUG)
  413. printf("gpg_conf %s\n", gpg_conf());
  414. printf("gpg_agent_conf %s\n", gpg_agent_conf());
  415. #else
  416. printf("gpg_conf %s\n", gpg_conf(false));
  417. printf("gpg_agent_conf %s\n", gpg_agent_conf(false));
  418. #endif
  419. stringlist_t *conf_keys = new_stringlist("keyserver");
  420. stringlist_t *conf_values = new_stringlist("hkp://keys.gnupg.net");
  421. stringlist_add(conf_keys, "cert-digest-algo");
  422. stringlist_add(conf_values, "SHA256");
  423. stringlist_add(conf_keys, "no-emit-version");
  424. stringlist_add(conf_values, "");
  425. stringlist_add(conf_keys, "no-comments");
  426. stringlist_add(conf_values, "");
  427. stringlist_add(conf_keys, "personal-cipher-preferences");
  428. stringlist_add(conf_values, "AES AES256 AES192 CAST5");
  429. stringlist_add(conf_keys, "personal-digest-preferences");
  430. stringlist_add(conf_values, "SHA256 SHA512 SHA384 SHA224");
  431. stringlist_add(conf_keys, "ignore-time-conflict");
  432. stringlist_add(conf_values, "");
  433. stringlist_add(conf_keys, "allow-freeform-uid");
  434. stringlist_add(conf_values, "");
  435. bResult = true;
  436. if (1)
  437. #if defined(WIN32) || defined(NDEBUG)
  438. bResult = quickfix_config(conf_keys, gpg_conf());
  439. if (bResult)
  440. bResult = ensure_config_values(conf_keys, conf_values, gpg_conf());
  441. #else
  442. bResult = quickfix_config(conf_keys, gpg_conf(false));
  443. if (bResult)
  444. bResult = ensure_config_values(conf_keys, conf_values, gpg_conf(false));
  445. #endif
  446. status = PEP_STATUS_OK;
  447. free_stringlist(conf_keys);
  448. free_stringlist(conf_values);
  449. assert(bResult);
  450. if (!bResult) {
  451. status = PEP_INIT_NO_GPG_HOME;
  452. goto pep_error;
  453. }
  454. conf_keys = new_stringlist("default-cache-ttl");
  455. conf_values = new_stringlist("300");
  456. stringlist_add(conf_keys, "max-cache-ttl");
  457. stringlist_add(conf_values, "1200");
  458. if (1)
  459. #if defined(WIN32) || defined(NDEBUG)
  460. bResult = quickfix_config(conf_keys, gpg_agent_conf());
  461. if (bResult)
  462. bResult = ensure_config_values(conf_keys, conf_values, gpg_agent_conf());
  463. #else
  464. bResult = quickfix_config(conf_keys, gpg_agent_conf(false));
  465. if (bResult)
  466. bResult = ensure_config_values(conf_keys, conf_values, gpg_agent_conf(false));
  467. #endif
  468. free_stringlist(conf_keys);
  469. free_stringlist(conf_values);
  470. return 0;
  471. pep_error:
  472. return 1;
  473. }