1 /* Author: Daniel Kahn Gillmor <dkg@fifthhorseman.net> */
2 /* Date: Fri, 04 Apr 2008 19:31:16 -0400 */
3 /* License: GPL v3 or later */
5 #include "gnutls-helpers.h"
20 /* higher levels allow more frivolous error messages through.
21 this is set with the MONKEYSPHERE_DEBUG variable */
22 static int loglevel = 0;
24 void err(int level, const char* fmt, ...) {
29 vfprintf(stderr, fmt, ap);
34 void logfunc(int level, const char* string) {
35 fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
38 void init_keyid(gnutls_openpgp_keyid_t keyid) {
39 memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t));
44 void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
46 assert(sizeof(out) >= 2*sizeof(keyid));
47 hex_print_data((char*)out, (const char*)keyid, sizeof(keyid));
50 /* you must have twice as many bytes in the out buffer as in the in buffer */
51 void hex_print_data(char* out, const char* in, size_t incount)
53 static const char hex[16] = "0123456789ABCDEF";
54 unsigned int inix = 0, outix = 0;
56 while (inix < incount) {
57 out[outix] = hex[(in[inix] >> 4) & 0x0f];
58 out[outix + 1] = hex[in[inix] & 0x0f];
64 unsigned char hex2bin(unsigned char x) {
65 if ((x >= '0') && (x <= '9'))
67 if ((x >= 'A') && (x <= 'F'))
69 if ((x >= 'a') && (x <= 'f'))
74 void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
75 unsigned int pkix = 0, outkix = 0;
77 while (pkix < sizeof(printable_keyid)) {
78 unsigned hi = hex2bin(in[pkix]);
79 unsigned lo = hex2bin(in[pkix + 1]);
81 err(0, "character '%c' is not a hex char\n", in[pkix]);
85 err(0, "character '%c' is not a hex char\n", in[pkix + 1]);
88 out[outkix] = lo | (hi << 4);
95 int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
99 ret = convert_string_to_printable_keyid(p, str);
101 collapse_printable_keyid(out, p);
104 int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) {
108 while ((arglen <= sizeof(printable_keyid)) &&
110 if (isxdigit(str[x])) {
111 if (arglen == sizeof(printable_keyid)) {
112 err(0, "There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str);
115 pkeyid[arglen] = str[x];
121 if (arglen != sizeof(printable_keyid)) {
122 err(0, "Keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid));
131 const char* version = NULL;
132 const char* debug_string = NULL;
135 if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
136 loglevel = atoi(debug_string);
139 if (ret = gnutls_global_init(), ret) {
140 err(0, "Failed to do gnutls_global_init() (error: %d)\n", ret);
144 version = gnutls_check_version(NULL);
147 err(1, "gnutls version: %s\n", version);
149 err(0, "no gnutls version found!\n");
153 gnutls_global_set_log_function(logfunc);
155 gnutls_global_set_log_level(loglevel);
156 err(1, "set log level to %d\n", loglevel);
161 void init_datum(gnutls_datum_t* d) {
165 void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
166 dest->data = gnutls_realloc(dest->data, src->size);
167 dest->size = src->size;
168 memcpy(dest->data, src->data, src->size);
170 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
171 if (a->size > b->size) {
172 err(0,"a is larger\n");
175 if (a->size < b->size) {
176 err(0,"b is larger\n");
179 return memcmp(a->data, b->data, a->size);
181 void free_datum(gnutls_datum_t* d) {
182 gnutls_free(d->data);
187 /* read the passed-in string, store in a single datum */
188 int set_datum_string(gnutls_datum_t* d, const char* s) {
189 unsigned int x = strlen(s)+1;
190 unsigned char* c = NULL;
192 c = gnutls_realloc(d->data, x);
197 memcpy(d->data, s, x);
201 /* read the passed-in file descriptor until EOF, store in a single
203 int set_datum_fd(gnutls_datum_t* d, int fd) {
204 unsigned int bufsize = 1024;
205 unsigned int len = 0;
207 FILE* f = fdopen(fd, "r");
208 if (bufsize > d->size) {
210 d->data = gnutls_realloc(d->data, bufsize);
211 if (d->data == NULL) {
212 err(0,"out of memory!\n");
221 err(0,"could not fdopen FD %d\n", fd);
224 while (!feof(f) && !ferror(f)) {
225 if (len == bufsize) {
226 /* allocate more space by doubling: */
228 d->data = gnutls_realloc(d->data, bufsize);
229 if (d->data == NULL) {
230 err(0,"out of memory!\n");
235 len += fread(d->data + len, 1, bufsize - len, f);
236 /* err(0,"read %d bytes\n", len); */
239 err(0,"Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
243 /* touch up buffer size to match reality: */
244 d->data = gnutls_realloc(d->data, len);
249 /* read the file indicated (by name) in the fname parameter. store
250 its entire contents in a single datum. */
251 int set_datum_file(gnutls_datum_t* d, const char* fname) {
253 unsigned char* c = NULL;
257 if (0 != stat(fname, &sbuf)) {
258 err(0,"failed to stat '%s'\n", fname);
262 c = gnutls_realloc(d->data, sbuf.st_size);
264 err(0,"failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
269 d->size = sbuf.st_size;
270 file = fopen(fname, "r");
272 err(0,"failed to open '%s' for reading\n", fname);
276 x = fread(d->data, d->size, 1, file);
278 err(0,"tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
286 int write_datum_fd(int fd, const gnutls_datum_t* d) {
287 if (d->size != write(fd, d->data, d->size)) {
288 err(0,"failed to write body of datum.\n");
295 int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
297 int looks_negative = (d->data[0] & 0x80);
298 unsigned char zero = 0;
300 /* if the first bit is 1, then the datum will appear negative in the
301 MPI encoding style used by OpenSSH. In that case, we'll increase
302 the length by one, and dump out one more byte */
304 if (looks_negative) {
305 len = htonl(d->size + 1);
307 len = htonl(d->size);
309 if (write(fd, &len, sizeof(len)) != sizeof(len)) {
310 err(0,"failed to write size of datum.\n");
313 if (looks_negative) {
314 if (write(fd, &zero, 1) != 1) {
315 err(0,"failed to write padding byte for MPI.\n");
319 return write_datum_fd(fd, d);
322 int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
326 for (i = 0; i < num; i++)
327 if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
334 int datum_from_string(gnutls_datum_t* d, const char* str) {
335 d->size = strlen(str);
336 d->data = gnutls_realloc(d->data, d->size);
339 memcpy(d->data, str, d->size);
344 int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
349 err(0,"bad pointer passed to create_writing_pipe()\n");
353 if (ret = pipe(p), ret == -1) {
354 err(0,"failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
360 err(0,"Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
363 if (*pid == 0) { /* this is the child */
364 close(p[1]); /* close unused write end */
366 if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
367 err(0,"Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
371 err(0,"exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
372 /* close the open file descriptors */
377 } else { /* this is the parent */
378 close(p[0]); /* close unused read end */
383 int validate_ssh_host_userid(const char* userid) {
384 char* oldlocale = setlocale(LC_ALL, "C");
386 /* choke if userid does not match the expected format
387 ("ssh://fully.qualified.domain.name") */
388 if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
389 err(0,"The user ID should start with ssh:// for a host key\n");
392 /* so that isalnum will work properly */
393 userid += strlen("ssh://");
394 while (0 != (*userid)) {
395 if (!isalnum(*userid)) {
396 err(0,"label did not start with a letter or a digit! (%s)\n", userid);
400 while (isalnum(*userid) || ('-' == (*userid)))
402 if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
405 if (!isalnum(*(userid - 1))) {
406 err(0,"label did not end with a letter or a digit!\n");
409 if ('.' == (*userid)) /* advance to the start of the next label */
412 err(0,"invalid character in domain name: %c\n", *userid);
416 /* ensure that the last character is valid: */
417 if (!isalnum(*(userid - 1))) {
418 err(0,"hostname did not end with a letter or a digit!\n");
421 /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
422 make sure that we've got an OK string? */
427 setlocale(LC_ALL, oldlocale);
431 /* http://tools.ietf.org/html/rfc4880#section-5.5.2 */
432 size_t get_openpgp_mpi_size(gnutls_datum_t* d) {
436 int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) {
442 write(fd, &x, sizeof(x));
443 write(fd, d->data, d->size);