summaryrefslogtreecommitdiff
path: root/src/gpg2ssh/gnutls-helpers.c
blob: 6eae29e0aa3a32352c62212587a33118d5ca973f (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. int loglevel = 0;
  12. void err(const char* fmt, ...) {
  13. va_list ap;
  14. va_start(ap, fmt);
  15. vfprintf(stderr, fmt, ap);
  16. va_end(ap);
  17. fflush(stderr);
  18. }
  19. void logfunc(int level, const char* string) {
  20. fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
  21. }
  22. void init_keyid(gnutls_openpgp_keyid_t keyid) {
  23. memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t));
  24. }
  25. void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
  26. {
  27. static const char hex[16] = "0123456789ABCDEF";
  28. unsigned int kix = 0, outix = 0;
  29. while (kix < sizeof(gnutls_openpgp_keyid_t)) {
  30. out[outix] = hex[(keyid[kix] >> 4) & 0x0f];
  31. out[outix + 1] = hex[keyid[kix] & 0x0f];
  32. kix++;
  33. outix += 2;
  34. }
  35. }
  36. int init_gnutls() {
  37. const char* version = NULL;
  38. const char* debug_string = NULL;
  39. int ret;
  40. if (ret = gnutls_global_init(), ret) {
  41. err("Failed to do gnutls_global_init() (error: %d)\n", ret);
  42. return 1;
  43. }
  44. version = gnutls_check_version(NULL);
  45. if (version)
  46. err("gnutls version: %s\n", version);
  47. else {
  48. err("no version found!\n");
  49. return 1;
  50. }
  51. if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
  52. loglevel = atoi(debug_string);
  53. gnutls_global_set_log_function(logfunc);
  54. gnutls_global_set_log_level(loglevel);
  55. err("set log level to %d\n", loglevel);
  56. }
  57. return 0;
  58. }
  59. void init_datum(gnutls_datum_t* d) {
  60. d->data = NULL;
  61. d->size = 0;
  62. }
  63. void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
  64. dest->data = gnutls_realloc(dest->data, src->size);
  65. dest->size = src->size;
  66. memcpy(dest->data, src->data, src->size);
  67. }
  68. int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
  69. if (a->size > b->size) {
  70. err("a is larger\n");
  71. return 1;
  72. }
  73. if (a->size < b->size) {
  74. err("b is larger\n");
  75. return -1;
  76. }
  77. return memcmp(a->data, b->data, a->size);
  78. }
  79. void free_datum(gnutls_datum_t* d) {
  80. gnutls_free(d->data);
  81. d->data = NULL;
  82. d->size = 0;
  83. }
  84. /* read the passed-in string, store in a single datum */
  85. int set_datum_string(gnutls_datum_t* d, const char* s) {
  86. unsigned int x = strlen(s)+1;
  87. unsigned char* c = NULL;
  88. c = gnutls_realloc(d->data, x);
  89. if (NULL == c)
  90. return -1;
  91. d->data = c;
  92. d->size = x;
  93. memcpy(d->data, s, x);
  94. return 0;
  95. }
  96. /* read the passed-in file descriptor until EOF, store in a single
  97. datum */
  98. int set_datum_fd(gnutls_datum_t* d, int fd) {
  99. unsigned int bufsize = 1024;
  100. unsigned int len = 0;
  101. FILE* f = fdopen(fd, "r");
  102. if (bufsize > d->size) {
  103. bufsize = 1024;
  104. d->data = gnutls_realloc(d->data, bufsize);
  105. if (d->data == NULL) {
  106. err("out of memory!\n");
  107. return -1;
  108. }
  109. d->size = bufsize;
  110. } else {
  111. bufsize = d->size;
  112. }
  113. f = fdopen(fd, "r");
  114. if (NULL == f) {
  115. err("could not fdopen FD %d\n", fd);
  116. }
  117. clearerr(f);
  118. while (!feof(f) && !ferror(f)) {
  119. if (len == bufsize) {
  120. /* allocate more space by doubling: */
  121. bufsize *= 2;
  122. d->data = gnutls_realloc(d->data, bufsize);
  123. if (d->data == NULL) {
  124. err("out of memory!\n");
  125. return -1;
  126. };
  127. d->size = bufsize;
  128. }
  129. len += fread(d->data + len, 1, bufsize - len, f);
  130. /* err("read %d bytes\n", len); */
  131. }
  132. if (ferror(f)) {
  133. err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
  134. return -1;
  135. }
  136. /* touch up buffer size to match reality: */
  137. d->data = gnutls_realloc(d->data, len);
  138. d->size = len;
  139. return 0;
  140. }
  141. /* read the file indicated (by name) in the fname parameter. store
  142. its entire contents in a single datum. */
  143. int set_datum_file(gnutls_datum_t* d, const char* fname) {
  144. struct stat sbuf;
  145. unsigned char* c = NULL;
  146. FILE* file = NULL;
  147. size_t x = 0;
  148. if (0 != stat(fname, &sbuf)) {
  149. err("failed to stat '%s'\n", fname);
  150. return -1;
  151. }
  152. c = gnutls_realloc(d->data, sbuf.st_size);
  153. if (NULL == c) {
  154. err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
  155. return -1;
  156. }
  157. d->data = c;
  158. d->size = sbuf.st_size;
  159. file = fopen(fname, "r");
  160. if (NULL == file) {
  161. err("failed to open '%s' for reading\n", fname);
  162. return -1;
  163. }
  164. x = fread(d->data, d->size, 1, file);
  165. if (x != 1) {
  166. err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
  167. fclose(file);
  168. return -1;
  169. }
  170. fclose(file);
  171. return 0;
  172. }
  173. int write_datum_fd(int fd, const gnutls_datum_t* d) {
  174. if (d->size != write(fd, d->data, d->size)) {
  175. err("failed to write body of datum.\n");
  176. return -1;
  177. }
  178. return 0;
  179. }
  180. int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
  181. uint32_t len;
  182. int looks_negative = (d->data[0] & 0x80);
  183. unsigned char zero = 0;
  184. /* if the first bit is 1, then the datum will appear negative in the
  185. MPI encoding style used by OpenSSH. In that case, we'll increase
  186. the length by one, and dump out one more byte */
  187. if (looks_negative) {
  188. len = htonl(d->size + 1);
  189. } else {
  190. len = htonl(d->size);
  191. }
  192. if (write(fd, &len, sizeof(len)) != sizeof(len)) {
  193. err("failed to write size of datum.\n");
  194. return -2;
  195. }
  196. if (looks_negative) {
  197. if (write(fd, &zero, 1) != 1) {
  198. err("failed to write padding byte for MPI.\n");
  199. return -2;
  200. }
  201. }
  202. return write_datum_fd(fd, d);
  203. }
  204. int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
  205. unsigned int i;
  206. int ret;
  207. for (i = 0; i < num; i++)
  208. if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
  209. return ret;
  210. return 0;
  211. }
  212. int datum_from_string(gnutls_datum_t* d, const char* str) {
  213. d->size = strlen(str);
  214. d->data = gnutls_realloc(d->data, d->size);
  215. if (d->data == 0)
  216. return ENOMEM;
  217. memcpy(d->data, str, d->size);
  218. return 0;
  219. }
  220. int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
  221. int p[2];
  222. int ret;
  223. if (pid == NULL) {
  224. err("bad pointer passed to create_writing_pipe()\n");
  225. return -1;
  226. }
  227. if (ret = pipe(p), ret == -1) {
  228. err("failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
  229. return -1;
  230. }
  231. *pid = fork();
  232. if (*pid == -1) {
  233. err("Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
  234. return -1;
  235. }
  236. if (*pid == 0) { /* this is the child */
  237. close(p[1]); /* close unused write end */
  238. if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
  239. err("Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
  240. exit(1);
  241. }
  242. execv(path, argv);
  243. err("exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
  244. /* close the open file descriptors */
  245. close(p[0]);
  246. close(0);
  247. exit(1);
  248. } else { /* this is the parent */
  249. close(p[0]); /* close unused read end */
  250. return p[1];
  251. }
  252. }
  253. int validate_ssh_host_userid(const char* userid) {
  254. char* oldlocale = setlocale(LC_ALL, "C");
  255. /* choke if userid does not match the expected format
  256. ("ssh://fully.qualified.domain.name") */
  257. if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
  258. err("The user ID should start with ssh:// for a host key\n");
  259. goto fail;
  260. }
  261. /* so that isalnum will work properly */
  262. userid += strlen("ssh://");
  263. while (0 != (*userid)) {
  264. if (!isalnum(*userid)) {
  265. err("label did not start with a letter or a digit! (%s)\n", userid);
  266. goto fail;
  267. }
  268. userid++;
  269. while (isalnum(*userid) || ('-' == (*userid)))
  270. userid++;
  271. if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
  272. check last char
  273. isalnum */
  274. if (!isalnum(*(userid - 1))) {
  275. err("label did not end with a letter or a digit!\n");
  276. goto fail;
  277. }
  278. if ('.' == (*userid)) /* advance to the start of the next label */
  279. userid++;
  280. } else {
  281. err("invalid character in domain name: %c\n", *userid);
  282. goto fail;
  283. }
  284. }
  285. /* ensure that the last character is valid: */
  286. if (!isalnum(*(userid - 1))) {
  287. err("hostname did not end with a letter or a digit!\n");
  288. goto fail;
  289. }
  290. /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
  291. make sure that we've got an OK string? */
  292. return 0;
  293. fail:
  294. setlocale(LC_ALL, oldlocale);
  295. return 1;
  296. }
  297. /* http://tools.ietf.org/html/rfc4880#section-5.5.2 */
  298. size_t get_openpgp_mpi_size(gnutls_datum_t* d) {
  299. return 2 + d->size;
  300. }
  301. int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) {
  302. uint16_t x;
  303. x = d->size * 8;
  304. x = htons(x);
  305. write(fd, &x, sizeof(x));
  306. write(fd, d->data, d->size);
  307. return 0;
  308. }