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"
18 /* higher levels allow more frivolous error messages through.
19 this is set with the MONKEYSPHERE_DEBUG variable */
20 static int loglevel = 0;
22 void err(int level, const char* fmt, ...) {
27 vfprintf(stderr, fmt, ap);
32 void logfunc(int level, const char* string) {
33 fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
36 void init_keyid(gnutls_openpgp_keyid_t keyid) {
37 memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t));
42 void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
44 static const char hex[16] = "0123456789ABCDEF";
45 unsigned int kix = 0, outix = 0;
47 while (kix < sizeof(gnutls_openpgp_keyid_t)) {
48 out[outix] = hex[(keyid[kix] >> 4) & 0x0f];
49 out[outix + 1] = hex[keyid[kix] & 0x0f];
55 unsigned char hex2bin(unsigned char x) {
56 if ((x >= '0') && (x <= '9'))
58 if ((x >= 'A') && (x <= 'F'))
60 if ((x >= 'a') && (x <= 'f'))
65 void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
66 unsigned int pkix = 0, outkix = 0;
68 while (pkix < sizeof(printable_keyid)) {
69 unsigned hi = hex2bin(in[pkix]);
70 unsigned lo = hex2bin(in[pkix + 1]);
72 err(0, "character '%c' is not a hex char\n", in[pkix]);
76 err(0, "character '%c' is not a hex char\n", in[pkix + 1]);
79 out[outkix] = lo | (hi << 4);
86 int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
90 ret = convert_string_to_printable_keyid(p, str);
92 collapse_printable_keyid(out, p);
95 int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) {
99 while ((arglen <= sizeof(printable_keyid)) &&
101 if (isxdigit(str[x])) {
102 if (arglen == sizeof(printable_keyid)) {
103 err(0, "There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str);
106 pkeyid[arglen] = str[x];
112 if (arglen != sizeof(printable_keyid)) {
113 err(0, "Keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid));
122 const char* version = NULL;
123 const char* debug_string = NULL;
126 if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
127 loglevel = atoi(debug_string);
130 if (ret = gnutls_global_init(), ret) {
131 err(0, "Failed to do gnutls_global_init() (error: %d)\n", ret);
135 version = gnutls_check_version(NULL);
138 err(1, "gnutls version: %s\n", version);
140 err(0, "no gnutls version found!\n");
144 gnutls_global_set_log_function(logfunc);
146 gnutls_global_set_log_level(loglevel);
147 err(1, "set log level to %d\n", loglevel);
152 void init_datum(gnutls_datum_t* d) {
156 void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
157 dest->data = gnutls_realloc(dest->data, src->size);
158 dest->size = src->size;
159 memcpy(dest->data, src->data, src->size);
161 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
162 if (a->size > b->size) {
163 err(0,"a is larger\n");
166 if (a->size < b->size) {
167 err(0,"b is larger\n");
170 return memcmp(a->data, b->data, a->size);
172 void free_datum(gnutls_datum_t* d) {
173 gnutls_free(d->data);
178 /* read the passed-in string, store in a single datum */
179 int set_datum_string(gnutls_datum_t* d, const char* s) {
180 unsigned int x = strlen(s)+1;
181 unsigned char* c = NULL;
183 c = gnutls_realloc(d->data, x);
188 memcpy(d->data, s, x);
192 /* read the passed-in file descriptor until EOF, store in a single
194 int set_datum_fd(gnutls_datum_t* d, int fd) {
195 unsigned int bufsize = 1024;
196 unsigned int len = 0;
198 FILE* f = fdopen(fd, "r");
199 if (bufsize > d->size) {
201 d->data = gnutls_realloc(d->data, bufsize);
202 if (d->data == NULL) {
203 err(0,"out of memory!\n");
212 err(0,"could not fdopen FD %d\n", fd);
215 while (!feof(f) && !ferror(f)) {
216 if (len == bufsize) {
217 /* allocate more space by doubling: */
219 d->data = gnutls_realloc(d->data, bufsize);
220 if (d->data == NULL) {
221 err(0,"out of memory!\n");
226 len += fread(d->data + len, 1, bufsize - len, f);
227 /* err(0,"read %d bytes\n", len); */
230 err(0,"Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
234 /* touch up buffer size to match reality: */
235 d->data = gnutls_realloc(d->data, len);
240 /* read the file indicated (by name) in the fname parameter. store
241 its entire contents in a single datum. */
242 int set_datum_file(gnutls_datum_t* d, const char* fname) {
244 unsigned char* c = NULL;
248 if (0 != stat(fname, &sbuf)) {
249 err(0,"failed to stat '%s'\n", fname);
253 c = gnutls_realloc(d->data, sbuf.st_size);
255 err(0,"failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
260 d->size = sbuf.st_size;
261 file = fopen(fname, "r");
263 err(0,"failed to open '%s' for reading\n", fname);
267 x = fread(d->data, d->size, 1, file);
269 err(0,"tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
277 int write_datum_fd(int fd, const gnutls_datum_t* d) {
278 if (d->size != write(fd, d->data, d->size)) {
279 err(0,"failed to write body of datum.\n");
286 int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
288 int looks_negative = (d->data[0] & 0x80);
289 unsigned char zero = 0;
291 /* if the first bit is 1, then the datum will appear negative in the
292 MPI encoding style used by OpenSSH. In that case, we'll increase
293 the length by one, and dump out one more byte */
295 if (looks_negative) {
296 len = htonl(d->size + 1);
298 len = htonl(d->size);
300 if (write(fd, &len, sizeof(len)) != sizeof(len)) {
301 err(0,"failed to write size of datum.\n");
304 if (looks_negative) {
305 if (write(fd, &zero, 1) != 1) {
306 err(0,"failed to write padding byte for MPI.\n");
310 return write_datum_fd(fd, d);
313 int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
317 for (i = 0; i < num; i++)
318 if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
325 int datum_from_string(gnutls_datum_t* d, const char* str) {
326 d->size = strlen(str);
327 d->data = gnutls_realloc(d->data, d->size);
330 memcpy(d->data, str, d->size);
335 int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
340 err(0,"bad pointer passed to create_writing_pipe()\n");
344 if (ret = pipe(p), ret == -1) {
345 err(0,"failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
351 err(0,"Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
354 if (*pid == 0) { /* this is the child */
355 close(p[1]); /* close unused write end */
357 if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
358 err(0,"Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
362 err(0,"exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
363 /* close the open file descriptors */
368 } else { /* this is the parent */
369 close(p[0]); /* close unused read end */
374 int validate_ssh_host_userid(const char* userid) {
375 char* oldlocale = setlocale(LC_ALL, "C");
377 /* choke if userid does not match the expected format
378 ("ssh://fully.qualified.domain.name") */
379 if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
380 err(0,"The user ID should start with ssh:// for a host key\n");
383 /* so that isalnum will work properly */
384 userid += strlen("ssh://");
385 while (0 != (*userid)) {
386 if (!isalnum(*userid)) {
387 err(0,"label did not start with a letter or a digit! (%s)\n", userid);
391 while (isalnum(*userid) || ('-' == (*userid)))
393 if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
396 if (!isalnum(*(userid - 1))) {
397 err(0,"label did not end with a letter or a digit!\n");
400 if ('.' == (*userid)) /* advance to the start of the next label */
403 err(0,"invalid character in domain name: %c\n", *userid);
407 /* ensure that the last character is valid: */
408 if (!isalnum(*(userid - 1))) {
409 err(0,"hostname did not end with a letter or a digit!\n");
412 /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
413 make sure that we've got an OK string? */
418 setlocale(LC_ALL, oldlocale);
422 /* http://tools.ietf.org/html/rfc4880#section-5.5.2 */
423 size_t get_openpgp_mpi_size(gnutls_datum_t* d) {
427 int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) {
433 write(fd, &x, sizeof(x));
434 write(fd, d->data, d->size);