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 static int loglevel = 0;
20 void err(int level, const char* fmt, ...) {
25 vfprintf(stderr, fmt, ap);
30 void logfunc(int level, const char* string) {
31 fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
34 void init_keyid(gnutls_openpgp_keyid_t keyid) {
35 memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t));
40 void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
42 static const char hex[16] = "0123456789ABCDEF";
43 unsigned int kix = 0, outix = 0;
45 while (kix < sizeof(gnutls_openpgp_keyid_t)) {
46 out[outix] = hex[(keyid[kix] >> 4) & 0x0f];
47 out[outix + 1] = hex[keyid[kix] & 0x0f];
53 unsigned char hex2bin(unsigned char x) {
54 if ((x >= '0') && (x <= '9'))
56 if ((x >= 'A') && (x <= 'F'))
58 if ((x >= 'a') && (x <= 'f'))
63 void collapse_printable_keyid(gnutls_openpgp_keyid_t out, printable_keyid in) {
64 unsigned int pkix = 0, outkix = 0;
66 while (pkix < sizeof(printable_keyid)) {
67 unsigned hi = hex2bin(in[pkix]);
68 unsigned lo = hex2bin(in[pkix + 1]);
70 err(0, "character '%c' is not a hex char\n", in[pkix]);
74 err(0, "character '%c' is not a hex char\n", in[pkix + 1]);
77 out[outkix] = lo | (hi << 4);
84 int convert_string_to_keyid(gnutls_openpgp_keyid_t out, const char* str) {
88 ret = convert_string_to_printable_keyid(p, str);
90 collapse_printable_keyid(out, p);
93 int convert_string_to_printable_keyid(printable_keyid pkeyid, const char* str) {
97 while ((arglen <= sizeof(printable_keyid)) &&
99 if (isxdigit(str[x])) {
100 if (arglen == sizeof(printable_keyid)) {
101 err(0, "There are more than %d hex digits in the keyid '%s'\n", sizeof(printable_keyid), str);
104 pkeyid[arglen] = str[x];
110 if (arglen != sizeof(printable_keyid)) {
111 err(0, "Keyid '%s' is not %d hex digits in length\n", str, sizeof(printable_keyid));
120 const char* version = NULL;
121 const char* debug_string = NULL;
124 if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
125 loglevel = atoi(debug_string);
128 if (ret = gnutls_global_init(), ret) {
129 err(0, "Failed to do gnutls_global_init() (error: %d)\n", ret);
133 version = gnutls_check_version(NULL);
136 err(1, "gnutls version: %s\n", version);
138 err(0, "no gnutls version found!\n");
142 gnutls_global_set_log_function(logfunc);
144 gnutls_global_set_log_level(loglevel);
145 err(1, "set log level to %d\n", loglevel);
150 void init_datum(gnutls_datum_t* d) {
154 void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
155 dest->data = gnutls_realloc(dest->data, src->size);
156 dest->size = src->size;
157 memcpy(dest->data, src->data, src->size);
159 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
160 if (a->size > b->size) {
161 err(0,"a is larger\n");
164 if (a->size < b->size) {
165 err(0,"b is larger\n");
168 return memcmp(a->data, b->data, a->size);
170 void free_datum(gnutls_datum_t* d) {
171 gnutls_free(d->data);
176 /* read the passed-in string, store in a single datum */
177 int set_datum_string(gnutls_datum_t* d, const char* s) {
178 unsigned int x = strlen(s)+1;
179 unsigned char* c = NULL;
181 c = gnutls_realloc(d->data, x);
186 memcpy(d->data, s, x);
190 /* read the passed-in file descriptor until EOF, store in a single
192 int set_datum_fd(gnutls_datum_t* d, int fd) {
193 unsigned int bufsize = 1024;
194 unsigned int len = 0;
196 FILE* f = fdopen(fd, "r");
197 if (bufsize > d->size) {
199 d->data = gnutls_realloc(d->data, bufsize);
200 if (d->data == NULL) {
201 err(0,"out of memory!\n");
210 err(0,"could not fdopen FD %d\n", fd);
213 while (!feof(f) && !ferror(f)) {
214 if (len == bufsize) {
215 /* allocate more space by doubling: */
217 d->data = gnutls_realloc(d->data, bufsize);
218 if (d->data == NULL) {
219 err(0,"out of memory!\n");
224 len += fread(d->data + len, 1, bufsize - len, f);
225 /* err(0,"read %d bytes\n", len); */
228 err(0,"Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
232 /* touch up buffer size to match reality: */
233 d->data = gnutls_realloc(d->data, len);
238 /* read the file indicated (by name) in the fname parameter. store
239 its entire contents in a single datum. */
240 int set_datum_file(gnutls_datum_t* d, const char* fname) {
242 unsigned char* c = NULL;
246 if (0 != stat(fname, &sbuf)) {
247 err(0,"failed to stat '%s'\n", fname);
251 c = gnutls_realloc(d->data, sbuf.st_size);
253 err(0,"failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
258 d->size = sbuf.st_size;
259 file = fopen(fname, "r");
261 err(0,"failed to open '%s' for reading\n", fname);
265 x = fread(d->data, d->size, 1, file);
267 err(0,"tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
275 int write_datum_fd(int fd, const gnutls_datum_t* d) {
276 if (d->size != write(fd, d->data, d->size)) {
277 err(0,"failed to write body of datum.\n");
284 int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
286 int looks_negative = (d->data[0] & 0x80);
287 unsigned char zero = 0;
289 /* if the first bit is 1, then the datum will appear negative in the
290 MPI encoding style used by OpenSSH. In that case, we'll increase
291 the length by one, and dump out one more byte */
293 if (looks_negative) {
294 len = htonl(d->size + 1);
296 len = htonl(d->size);
298 if (write(fd, &len, sizeof(len)) != sizeof(len)) {
299 err(0,"failed to write size of datum.\n");
302 if (looks_negative) {
303 if (write(fd, &zero, 1) != 1) {
304 err(0,"failed to write padding byte for MPI.\n");
308 return write_datum_fd(fd, d);
311 int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
315 for (i = 0; i < num; i++)
316 if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
323 int datum_from_string(gnutls_datum_t* d, const char* str) {
324 d->size = strlen(str);
325 d->data = gnutls_realloc(d->data, d->size);
328 memcpy(d->data, str, d->size);
333 int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
338 err(0,"bad pointer passed to create_writing_pipe()\n");
342 if (ret = pipe(p), ret == -1) {
343 err(0,"failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
349 err(0,"Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
352 if (*pid == 0) { /* this is the child */
353 close(p[1]); /* close unused write end */
355 if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
356 err(0,"Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
360 err(0,"exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
361 /* close the open file descriptors */
366 } else { /* this is the parent */
367 close(p[0]); /* close unused read end */
372 int validate_ssh_host_userid(const char* userid) {
373 char* oldlocale = setlocale(LC_ALL, "C");
375 /* choke if userid does not match the expected format
376 ("ssh://fully.qualified.domain.name") */
377 if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
378 err(0,"The user ID should start with ssh:// for a host key\n");
381 /* so that isalnum will work properly */
382 userid += strlen("ssh://");
383 while (0 != (*userid)) {
384 if (!isalnum(*userid)) {
385 err(0,"label did not start with a letter or a digit! (%s)\n", userid);
389 while (isalnum(*userid) || ('-' == (*userid)))
391 if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
394 if (!isalnum(*(userid - 1))) {
395 err(0,"label did not end with a letter or a digit!\n");
398 if ('.' == (*userid)) /* advance to the start of the next label */
401 err(0,"invalid character in domain name: %c\n", *userid);
405 /* ensure that the last character is valid: */
406 if (!isalnum(*(userid - 1))) {
407 err(0,"hostname did not end with a letter or a digit!\n");
410 /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
411 make sure that we've got an OK string? */
416 setlocale(LC_ALL, oldlocale);
420 /* http://tools.ietf.org/html/rfc4880#section-5.5.2 */
421 size_t get_openpgp_mpi_size(gnutls_datum_t* d) {
425 int write_openpgp_mpi_to_fd(int fd, gnutls_datum_t* d) {
431 write(fd, &x, sizeof(x));
432 write(fd, d->data, d->size);