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