summaryrefslogtreecommitdiff
path: root/src/keytrans/gnutls-helpers.c
blob: 8d8ec17ae31f23a308380d90daa31956877374d2 (plain)
  1. /* Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net> */
  2. /* Date: Fri, 04 Apr 2008 19:31:16 -0400 */
  3. /* License: GPL v3 or later */
  4. #include "gnutls-helpers.h"
  5. /* for htonl() */
  6. #include <arpa/inet.h>
  7. /* for setlocale() */
  8. #include <locale.h>
  9. /* for isalnum() */
  10. #include <ctype.h>
  11. /* for exit() */
  12. #include <unistd.h>
  13. #include <assert.h>
  14. /* higher levels allow more frivolous error messages through.
  15. this is set with the MONKEYSPHERE_DEBUG variable */
  16. static int loglevel = 0;
  17. void err(int level, const char* fmt, ...) {
  18. va_list ap;
  19. if (level > loglevel)
  20. return;
  21. va_start(ap, fmt);
  22. vfprintf(stderr, fmt, ap);
  23. va_end(ap);
  24. fflush(stderr);
  25. }
  26. void logfunc(int level, const char* string) {
  27. fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
  28. }
  29. void init_keyid(gnutls_openpgp_keyid_t keyid) {
  30. memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t));
  31. }
  32. void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
  33. {
  34. assert(sizeof(out) >= 2*sizeof(keyid));
  35. hex_print_data((char*)out, (const unsigned char*)keyid, sizeof(keyid));
  36. }
  37. /* you must have twice as many bytes in the out buffer as in the in buffer */
  38. void hex_print_data(char* out, const unsigned char* in, size_t incount)
  39. {
  40. static const char hex[16] = "0123456789ABCDEF";
  41. unsigned int inix = 0, outix = 0;
  42. while (inix < incount) {
  43. out[outix] = hex[(in[inix] >> 4) & 0x0f];
  44. out[outix + 1] = hex[in[inix] & 0x0f];
  45. inix++;
  46. outix += 2;
  47. }
  48. }
  49. unsigned char hex2bin(unsigned char x) {
  50. if ((x >= '0') && (x <= '9'))
  51. return x - '0';
  52. if ((x >= 'A') && (x <= 'F'))
  53. return 10 + x - 'A';
  54. if ((x >= 'a') && (x <= 'f'))
  55. return 10 + x - 'a';
  56. return 0xff;
  57. }
  58. void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
  59. unsigned int pkix = 0, outkix = 0;
  60. while (pkix < sizeof(printable_keyid)) {
  61. unsigned hi = hex2bin(in[pkix]);
  62. unsigned lo = hex2bin(in[pkix + 1]);
  63. if (hi == 0xff) {
  64. err(0, "character '%c' is not a hex char\n", in[pkix]);
  65. exit(1);
  66. }
  67. if (lo == 0xff) {
  68. err(0, "character '%c' is not a hex char\n", in[pkix + 1]);
  69. exit(1);
  70. }
  71. out[outkix] = lo | (hi << 4);
  72. pkix += 2;
  73. outkix++;
  74. }
  75. }
  76. unsigned int hexstring2bin(unsigned char* out, const char* in) {
  77. unsigned int pkix = 0, outkix = 0;
  78. int hi = 0; /* which nybble is it? */
  79. while (in[pkix]) {
  80. unsigned char z = hex2bin(in[pkix]);
  81. if (z != 0xff) {
  82. if (!hi) {
  83. if (out) out[outkix] = (z << 4);
  84. hi = 1;
  85. } else {
  86. if (out) out[outkix] |= z;
  87. hi = 0;
  88. outkix++;
  89. }
  90. pkix++;
  91. }
  92. }
  93. return outkix*8 + (hi ? 4 : 0);
  94. }
  95. int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
  96. printable_keyid p;
  97. int ret;
  98. ret = convert_string_to_printable_keyid(p, str);
  99. if (ret == 0)
  100. collapse_printable_keyid(out, p);
  101. return ret;
  102. }
  103. int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) {
  104. int arglen, x;
  105. arglen = 0;
  106. x = 0;
  107. while ((arglen <= sizeof(printable_keyid)) &&
  108. (str[x] != '\0')) {
  109. if (isxdigit(str[x])) {
  110. if (arglen == sizeof(printable_keyid)) {
  111. err(0, "There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str);
  112. return 1;
  113. }
  114. pkeyid[arglen] = str[x];
  115. arglen++;
  116. }
  117. x++;
  118. }
  119. if (arglen != sizeof(printable_keyid)) {
  120. err(0, "Keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid));
  121. return 1;
  122. }
  123. return 0;
  124. }
  125. int init_gnutls() {
  126. const char* version = NULL;
  127. const char* debug_string = NULL;
  128. int ret;
  129. if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
  130. loglevel = atoi(debug_string);
  131. }
  132. if (ret = gnutls_global_init(), ret) {
  133. err(0, "Failed to do gnutls_global_init() (error: %d)\n", ret);
  134. return 1;
  135. }
  136. version = gnutls_check_version(NULL);
  137. if (version)
  138. err(1, "gnutls version: %s\n", version);
  139. else {
  140. err(0, "no gnutls version found!\n");
  141. return 1;
  142. }
  143. gnutls_global_set_log_function(logfunc);
  144. gnutls_global_set_log_level(loglevel);
  145. err(1, "set log level to %d\n", loglevel);
  146. return 0;
  147. }
  148. void init_datum(gnutls_datum_t* d) {
  149. d->data = NULL;
  150. d->size = 0;
  151. }
  152. void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
  153. dest->data = gnutls_realloc(dest->data, src->size);
  154. dest->size = src->size;
  155. memcpy(dest->data, src->data, src->size);
  156. }
  157. int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
  158. if (a->size > b->size) {
  159. err(0,"a is larger\n");
  160. return 1;
  161. }
  162. if (a->size < b->size) {
  163. err(0,"b is larger\n");
  164. return -1;
  165. }
  166. return memcmp(a->data, b->data, a->size);
  167. }
  168. void free_datum(gnutls_datum_t* d) {
  169. gnutls_free(d->data);
  170. d->data = NULL;
  171. d->size = 0;
  172. }
  173. /* read the passed-in string, store in a single datum */
  174. int set_datum_string(gnutls_datum_t* d, const char* s) {
  175. unsigned int x = strlen(s)+1;
  176. unsigned char* c = NULL;
  177. c = gnutls_realloc(d->data, x);
  178. if (NULL == c)
  179. return -1;
  180. d->data = c;
  181. d->size = x;
  182. memcpy(d->data, s, x);
  183. return 0;
  184. }
  185. /* read the passed-in file descriptor until EOF, store in a single
  186. datum */
  187. int set_datum_fd(gnutls_datum_t* d, int fd) {
  188. unsigned int bufsize = 1024;
  189. unsigned int len = 0;
  190. FILE* f = fdopen(fd, "r");
  191. if (bufsize > d->size) {
  192. bufsize = 1024;
  193. d->data = gnutls_realloc(d->data, bufsize);
  194. if (d->data == NULL) {
  195. err(0,"out of memory!\n");
  196. return -1;
  197. }
  198. d->size = bufsize;
  199. } else {
  200. bufsize = d->size;
  201. }
  202. f = fdopen(fd, "r");
  203. if (NULL == f) {
  204. err(0,"could not fdopen FD %d\n", fd);
  205. }
  206. clearerr(f);
  207. while (!feof(f) && !ferror(f)) {
  208. if (len == bufsize) {
  209. /* allocate more space by doubling: */
  210. bufsize *= 2;
  211. d->data = gnutls_realloc(d->data, bufsize);
  212. if (d->data == NULL) {
  213. err(0,"out of memory!\n");
  214. return -1;
  215. };
  216. d->size = bufsize;
  217. }
  218. len += fread(d->data + len, 1, bufsize - len, f);
  219. /* err(0,"read %d bytes\n", len); */
  220. }
  221. if (ferror(f)) {
  222. err(0,"Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
  223. return -1;
  224. }
  225. /* touch up buffer size to match reality: */
  226. d->data = gnutls_realloc(d->data, len);
  227. d->size = len;
  228. return 0;
  229. }
  230. /* read the file indicated (by name) in the fname parameter. store
  231. its entire contents in a single datum. */
  232. int set_datum_file(gnutls_datum_t* d, const char* fname) {
  233. struct stat sbuf;
  234. unsigned char* c = NULL;
  235. FILE* file = NULL;
  236. size_t x = 0;
  237. if (0 != stat(fname, &sbuf)) {
  238. err(0,"failed to stat '%s'\n", fname);
  239. return -1;
  240. }
  241. c = gnutls_realloc(d->data, sbuf.st_size);
  242. if (NULL == c) {
  243. err(0,"failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
  244. return -1;
  245. }
  246. d->data = c;
  247. d->size = sbuf.st_size;
  248. file = fopen(fname, "r");
  249. if (NULL == file) {
  250. err(0,"failed to open '%s' for reading\n", fname);
  251. return -1;
  252. }
  253. x = fread(d->data, d->size, 1, file);
  254. if (x != 1) {
  255. err(0,"tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
  256. fclose(file);
  257. return -1;
  258. }
  259. fclose(file);
  260. return 0;
  261. }
  262. int write_datum_fd(int fd, const gnutls_datum_t* d) {
  263. if (d->size != write(fd, d->data, d->size)) {
  264. err(0,"failed to write body of datum.\n");
  265. return -1;
  266. }
  267. return 0;
  268. }
  269. int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
  270. uint32_t len;
  271. int looks_negative = (d->data[0] & 0x80);
  272. unsigned char zero = 0;
  273. /* if the first bit is 1, then the datum will appear negative in the
  274. MPI encoding style used by OpenSSH. In that case, we'll increase
  275. the length by one, and dump out one more byte */
  276. if (looks_negative) {
  277. len = htonl(d->size + 1);
  278. } else {
  279. len = htonl(d->size);
  280. }
  281. if (write(fd, &len, sizeof(len)) != sizeof(len)) {
  282. err(0,"failed to write size of datum.\n");
  283. return -2;
  284. }
  285. if (looks_negative) {
  286. if (write(fd, &zero, 1) != 1) {
  287. err(0,"failed to write padding byte for MPI.\n");
  288. return -2;
  289. }
  290. }
  291. return write_datum_fd(fd, d);
  292. }
  293. int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
  294. unsigned int i;
  295. int ret;
  296. for (i = 0; i < num; i++)
  297. if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
  298. return ret;
  299. return 0;
  300. }
  301. int datum_from_string(gnutls_datum_t* d, const char* str) {
  302. d->size = strlen(str);
  303. d->data = gnutls_realloc(d->data, d->size);
  304. if (d->data == 0)
  305. return ENOMEM;
  306. memcpy(d->data, str, d->size);
  307. return 0;
  308. }
  309. int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
  310. int p[2];
  311. int ret;
  312. if (pid == NULL) {
  313. err(0,"bad pointer passed to create_writing_pipe()\n");
  314. return -1;
  315. }
  316. if (ret = pipe(p), ret == -1) {
  317. err(0,"failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
  318. return -1;
  319. }
  320. *pid = fork();
  321. if (*pid == -1) {
  322. err(0,"Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
  323. return -1;
  324. }
  325. if (*pid == 0) { /* this is the child */
  326. close(p[1]); /* close unused write end */
  327. if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
  328. err(0,"Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
  329. exit(1);
  330. }
  331. execvp(path, argv);
  332. err(0,"exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
  333. /* close the open file descriptors */
  334. close(p[0]);
  335. close(0);
  336. exit(1);
  337. } else { /* this is the parent */
  338. close(p[0]); /* close unused read end */
  339. return p[1];
  340. }
  341. }
  342. int validate_ssh_host_userid(const char* userid) {
  343. char* oldlocale = setlocale(LC_ALL, "C");
  344. /* choke if userid does not match the expected format
  345. ("ssh://fully.qualified.domain.name") */
  346. if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
  347. err(0,"The user ID should start with ssh:// for a host key\n");
  348. goto fail;
  349. }
  350. /* so that isalnum will work properly */
  351. userid += strlen("ssh://");
  352. while (0 != (*userid)) {
  353. if (!isalnum(*userid)) {
  354. err(0,"label did not start with a letter or a digit! (%s)\n", userid);
  355. goto fail;
  356. }
  357. userid++;
  358. while (isalnum(*userid) || ('-' == (*userid)))
  359. userid++;
  360. if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
  361. check last char
  362. isalnum */
  363. if (!isalnum(*(userid - 1))) {
  364. err(0,"label did not end with a letter or a digit!\n");
  365. goto fail;
  366. }
  367. if ('.' == (*userid)) /* advance to the start of the next label */
  368. userid++;
  369. } else {
  370. err(0,"invalid character in domain name: %c\n", *userid);
  371. goto fail;
  372. }
  373. }
  374. /* ensure that the last character is valid: */
  375. if (!isalnum(*(userid - 1))) {
  376. err(0,"hostname did not end with a letter or a digit!\n");
  377. goto fail;
  378. }
  379. /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
  380. make sure that we've got an OK string? */
  381. return 0;
  382. fail:
  383. setlocale(LC_ALL, oldlocale);
  384. return 1;
  385. }
  386. /* http://tools.ietf.org/html/rfc4880#section-5.5.2 */
  387. size_t get_openpgp_mpi_size(gnutls_datum_t* d) {
  388. return 2 + d->size;
  389. }
  390. int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) {
  391. uint16_t x;
  392. x = d->size * 8;
  393. x = htons(x);
  394. write(fd, &x, sizeof(x));
  395. write(fd, d->data, d->size);
  396. return 0;
  397. }