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"
21 void err(const char* fmt, ...) {
24 vfprintf(stderr, fmt, ap);
29 void logfunc(int level, const char* string) {
30 fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
33 void init_keyid(gnutls_openpgp_keyid_t keyid) {
34 memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t));
39 void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
41 static const char hex[16] = "0123456789ABCDEF";
42 unsigned int kix = 0, outix = 0;
44 while (kix < sizeof(gnutls_openpgp_keyid_t)) {
45 out[outix] = hex[(keyid[kix] >> 4) & 0x0f];
46 out[outix + 1] = hex[keyid[kix] & 0x0f];
52 unsigned char hex2bin(unsigned char x) {
53 if ((x >= '0') && (x <= '9'))
55 if ((x >= 'A') && (x <= 'F'))
57 if ((x >= 'a') && (x <= 'f'))
62 void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
63 unsigned int pkix = 0, outkix = 0;
65 while (pkix < sizeof(printable_keyid)) {
66 unsigned hi = hex2bin(in[pkix]);
67 unsigned lo = hex2bin(in[pkix + 1]);
69 err("character '%c' is not a hex char\n", in[pkix]);
73 err("character '%c' is not a hex char\n", in[pkix + 1]);
76 out[outkix] = lo | (hi << 4);
83 int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
87 ret = convert_string_to_printable_keyid(p, str);
89 collapse_printable_keyid(out, p);
92 int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) {
96 while ((arglen <= sizeof(printable_keyid)) &&
98 if (isxdigit(str[x])) {
99 if (arglen == sizeof(printable_keyid)) {
100 err("There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str);
103 pkeyid[arglen] = str[x];
109 if (arglen != sizeof(printable_keyid)) {
110 err("keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid));
119 const char* version = NULL;
120 const char* debug_string = NULL;
123 if (ret = gnutls_global_init(), ret) {
124 err("Failed to do gnutls_global_init() (error: %d)\n", ret);
128 version = gnutls_check_version(NULL);
131 err("gnutls version: %s\n", version);
133 err("no version found!\n");
137 if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
138 loglevel = atoi(debug_string);
139 gnutls_global_set_log_function(logfunc);
141 gnutls_global_set_log_level(loglevel);
142 err("set log level to %d\n", loglevel);
147 void init_datum(gnutls_datum_t* d) {
151 void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
152 dest->data = gnutls_realloc(dest->data, src->size);
153 dest->size = src->size;
154 memcpy(dest->data, src->data, src->size);
156 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
157 if (a->size > b->size) {
158 err("a is larger\n");
161 if (a->size < b->size) {
162 err("b is larger\n");
165 return memcmp(a->data, b->data, a->size);
167 void free_datum(gnutls_datum_t* d) {
168 gnutls_free(d->data);
173 /* read the passed-in string, store in a single datum */
174 int set_datum_string(gnutls_datum_t* d, const char* s) {
175 unsigned int x = strlen(s)+1;
176 unsigned char* c = NULL;
178 c = gnutls_realloc(d->data, x);
183 memcpy(d->data, s, x);
187 /* read the passed-in file descriptor until EOF, store in a single
189 int set_datum_fd(gnutls_datum_t* d, int fd) {
190 unsigned int bufsize = 1024;
191 unsigned int len = 0;
193 FILE* f = fdopen(fd, "r");
194 if (bufsize > d->size) {
196 d->data = gnutls_realloc(d->data, bufsize);
197 if (d->data == NULL) {
198 err("out of memory!\n");
207 err("could not fdopen FD %d\n", fd);
210 while (!feof(f) && !ferror(f)) {
211 if (len == bufsize) {
212 /* allocate more space by doubling: */
214 d->data = gnutls_realloc(d->data, bufsize);
215 if (d->data == NULL) {
216 err("out of memory!\n");
221 len += fread(d->data + len, 1, bufsize - len, f);
222 /* err("read %d bytes\n", len); */
225 err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
229 /* touch up buffer size to match reality: */
230 d->data = gnutls_realloc(d->data, len);
235 /* read the file indicated (by name) in the fname parameter. store
236 its entire contents in a single datum. */
237 int set_datum_file(gnutls_datum_t* d, const char* fname) {
239 unsigned char* c = NULL;
243 if (0 != stat(fname, &sbuf)) {
244 err("failed to stat '%s'\n", fname);
248 c = gnutls_realloc(d->data, sbuf.st_size);
250 err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
255 d->size = sbuf.st_size;
256 file = fopen(fname, "r");
258 err("failed to open '%s' for reading\n", fname);
262 x = fread(d->data, d->size, 1, file);
264 err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
272 int write_datum_fd(int fd, const gnutls_datum_t* d) {
273 if (d->size != write(fd, d->data, d->size)) {
274 err("failed to write body of datum.\n");
281 int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
283 int looks_negative = (d->data[0] & 0x80);
284 unsigned char zero = 0;
286 /* if the first bit is 1, then the datum will appear negative in the
287 MPI encoding style used by OpenSSH. In that case, we'll increase
288 the length by one, and dump out one more byte */
290 if (looks_negative) {
291 len = htonl(d->size + 1);
293 len = htonl(d->size);
295 if (write(fd, &len, sizeof(len)) != sizeof(len)) {
296 err("failed to write size of datum.\n");
299 if (looks_negative) {
300 if (write(fd, &zero, 1) != 1) {
301 err("failed to write padding byte for MPI.\n");
305 return write_datum_fd(fd, d);
308 int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
312 for (i = 0; i < num; i++)
313 if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
320 int datum_from_string(gnutls_datum_t* d, const char* str) {
321 d->size = strlen(str);
322 d->data = gnutls_realloc(d->data, d->size);
325 memcpy(d->data, str, d->size);
330 int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
335 err("bad pointer passed to create_writing_pipe()\n");
339 if (ret = pipe(p), ret == -1) {
340 err("failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
346 err("Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
349 if (*pid == 0) { /* this is the child */
350 close(p[1]); /* close unused write end */
352 if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
353 err("Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
357 err("exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
358 /* close the open file descriptors */
363 } else { /* this is the parent */
364 close(p[0]); /* close unused read end */
369 int validate_ssh_host_userid(const char* userid) {
370 char* oldlocale = setlocale(LC_ALL, "C");
372 /* choke if userid does not match the expected format
373 ("ssh://fully.qualified.domain.name") */
374 if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
375 err("The user ID should start with ssh:// for a host key\n");
378 /* so that isalnum will work properly */
379 userid += strlen("ssh://");
380 while (0 != (*userid)) {
381 if (!isalnum(*userid)) {
382 err("label did not start with a letter or a digit! (%s)\n", userid);
386 while (isalnum(*userid) || ('-' == (*userid)))
388 if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
391 if (!isalnum(*(userid - 1))) {
392 err("label did not end with a letter or a digit!\n");
395 if ('.' == (*userid)) /* advance to the start of the next label */
398 err("invalid character in domain name: %c\n", *userid);
402 /* ensure that the last character is valid: */
403 if (!isalnum(*(userid - 1))) {
404 err("hostname did not end with a letter or a digit!\n");
407 /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
408 make sure that we've got an OK string? */
413 setlocale(LC_ALL, oldlocale);
417 /* http://tools.ietf.org/html/rfc4880#section-5.5.2 */
418 size_t get_openpgp_mpi_size(gnutls_datum_t* d) {
422 int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) {
428 write(fd, &x, sizeof(x));
429 write(fd, d->data, d->size);