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 unsigned 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 unsigned 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;
76 while (pkix < sizeof(printable_keyid)) {
77 unsigned hi = hex2bin(in[pkix]);
78 unsigned lo = hex2bin(in[pkix + 1]);
80 err(0, "character '%c' is not a hex char\n", in[pkix]);
84 err(0, "character '%c' is not a hex char\n", in[pkix + 1]);
87 out[outkix] = lo | (hi << 4);
94 unsigned int hexstring2bin(unsigned char* out, const char* in) {
95 unsigned int pkix = 0, outkix = 0;
96 int hi = 0; /* which nybble is it? */
99 unsigned char z = hex2bin(in[pkix]);
102 if (out) out[outkix] = (z << 4);
105 if (out) out[outkix] |= z;
112 return outkix*8 + (hi ? 4 : 0);
115 int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
119 ret = convert_string_to_printable_keyid(p, str);
121 collapse_printable_keyid(out, p);
124 int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) {
128 while ((arglen <= sizeof(printable_keyid)) &&
130 if (isxdigit(str[x])) {
131 if (arglen == sizeof(printable_keyid)) {
132 err(0, "There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str);
135 pkeyid[arglen] = str[x];
141 if (arglen != sizeof(printable_keyid)) {
142 err(0, "Keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid));
151 const char* version = NULL;
152 const char* debug_string = NULL;
155 if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
156 loglevel = atoi(debug_string);
159 if (ret = gnutls_global_init(), ret) {
160 err(0, "Failed to do gnutls_global_init() (error: %d)\n", ret);
164 version = gnutls_check_version(NULL);
167 err(1, "gnutls version: %s\n", version);
169 err(0, "no gnutls version found!\n");
173 gnutls_global_set_log_function(logfunc);
175 gnutls_global_set_log_level(loglevel);
176 err(1, "set log level to %d\n", loglevel);
181 void init_datum(gnutls_datum_t* d) {
185 void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
186 dest->data = gnutls_realloc(dest->data, src->size);
187 dest->size = src->size;
188 memcpy(dest->data, src->data, src->size);
190 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
191 if (a->size > b->size) {
192 err(0,"a is larger\n");
195 if (a->size < b->size) {
196 err(0,"b is larger\n");
199 return memcmp(a->data, b->data, a->size);
201 void free_datum(gnutls_datum_t* d) {
202 gnutls_free(d->data);
207 /* read the passed-in string, store in a single datum */
208 int set_datum_string(gnutls_datum_t* d, const char* s) {
209 unsigned int x = strlen(s)+1;
210 unsigned char* c = NULL;
212 c = gnutls_realloc(d->data, x);
217 memcpy(d->data, s, x);
221 /* read the passed-in file descriptor until EOF, store in a single
223 int set_datum_fd(gnutls_datum_t* d, int fd) {
224 unsigned int bufsize = 1024;
225 unsigned int len = 0;
227 FILE* f = fdopen(fd, "r");
228 if (bufsize > d->size) {
230 d->data = gnutls_realloc(d->data, bufsize);
231 if (d->data == NULL) {
232 err(0,"out of memory!\n");
241 err(0,"could not fdopen FD %d\n", fd);
244 while (!feof(f) && !ferror(f)) {
245 if (len == bufsize) {
246 /* allocate more space by doubling: */
248 d->data = gnutls_realloc(d->data, bufsize);
249 if (d->data == NULL) {
250 err(0,"out of memory!\n");
255 len += fread(d->data + len, 1, bufsize - len, f);
256 /* err(0,"read %d bytes\n", len); */
259 err(0,"Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
263 /* touch up buffer size to match reality: */
264 d->data = gnutls_realloc(d->data, len);
269 /* read the file indicated (by name) in the fname parameter. store
270 its entire contents in a single datum. */
271 int set_datum_file(gnutls_datum_t* d, const char* fname) {
273 unsigned char* c = NULL;
277 if (0 != stat(fname, &sbuf)) {
278 err(0,"failed to stat '%s'\n", fname);
282 c = gnutls_realloc(d->data, sbuf.st_size);
284 err(0,"failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
289 d->size = sbuf.st_size;
290 file = fopen(fname, "r");
292 err(0,"failed to open '%s' for reading\n", fname);
296 x = fread(d->data, d->size, 1, file);
298 err(0,"tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
306 int write_datum_fd(int fd, const gnutls_datum_t* d) {
307 if (d->size != write(fd, d->data, d->size)) {
308 err(0,"failed to write body of datum.\n");
315 int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
317 int looks_negative = (d->data[0] & 0x80);
318 unsigned char zero = 0;
320 /* if the first bit is 1, then the datum will appear negative in the
321 MPI encoding style used by OpenSSH. In that case, we'll increase
322 the length by one, and dump out one more byte */
324 if (looks_negative) {
325 len = htonl(d->size + 1);
327 len = htonl(d->size);
329 if (write(fd, &len, sizeof(len)) != sizeof(len)) {
330 err(0,"failed to write size of datum.\n");
333 if (looks_negative) {
334 if (write(fd, &zero, 1) != 1) {
335 err(0,"failed to write padding byte for MPI.\n");
339 return write_datum_fd(fd, d);
342 int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
346 for (i = 0; i < num; i++)
347 if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
354 int datum_from_string(gnutls_datum_t* d, const char* str) {
355 d->size = strlen(str);
356 d->data = gnutls_realloc(d->data, d->size);
359 memcpy(d->data, str, d->size);
364 int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
369 err(0,"bad pointer passed to create_writing_pipe()\n");
373 if (ret = pipe(p), ret == -1) {
374 err(0,"failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
380 err(0,"Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
383 if (*pid == 0) { /* this is the child */
384 close(p[1]); /* close unused write end */
386 if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
387 err(0,"Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
391 err(0,"exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
392 /* close the open file descriptors */
397 } else { /* this is the parent */
398 close(p[0]); /* close unused read end */
403 int validate_ssh_host_userid(const char* userid) {
404 char* oldlocale = setlocale(LC_ALL, "C");
406 /* choke if userid does not match the expected format
407 ("ssh://fully.qualified.domain.name") */
408 if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
409 err(0,"The user ID should start with ssh:// for a host key\n");
412 /* so that isalnum will work properly */
413 userid += strlen("ssh://");
414 while (0 != (*userid)) {
415 if (!isalnum(*userid)) {
416 err(0,"label did not start with a letter or a digit! (%s)\n", userid);
420 while (isalnum(*userid) || ('-' == (*userid)))
422 if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
425 if (!isalnum(*(userid - 1))) {
426 err(0,"label did not end with a letter or a digit!\n");
429 if ('.' == (*userid)) /* advance to the start of the next label */
432 err(0,"invalid character in domain name: %c\n", *userid);
436 /* ensure that the last character is valid: */
437 if (!isalnum(*(userid - 1))) {
438 err(0,"hostname did not end with a letter or a digit!\n");
441 /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
442 make sure that we've got an OK string? */
447 setlocale(LC_ALL, oldlocale);
451 /* http://tools.ietf.org/html/rfc4880#section-5.5.2 */
452 size_t get_openpgp_mpi_size(gnutls_datum_t* d) {
456 int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) {
462 write(fd, &x, sizeof(x));
463 write(fd, d->data, d->size);