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 void err(const char* fmt, ...) {
21 vfprintf(stderr, fmt, ap);
26 void logfunc(int level, const char* string) {
27 fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
30 void init_keyid(gnutls_openpgp_keyid_t keyid) {
31 memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t));
36 void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
38 static const char hex[16] = "0123456789ABCDEF";
39 unsigned int kix = 0, outix = 0;
41 while (kix < sizeof(gnutls_openpgp_keyid_t)) {
42 out[outix] = hex[(keyid[kix] >> 4) & 0x0f];
43 out[outix + 1] = hex[keyid[kix] & 0x0f];
51 const char* version = NULL;
52 const char* debug_string = NULL;
55 if (ret = gnutls_global_init(), ret) {
56 err("Failed to do gnutls_global_init() (error: %d)\n", ret);
60 version = gnutls_check_version(NULL);
63 err("gnutls version: %s\n", version);
65 err("no version found!\n");
69 if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
70 loglevel = atoi(debug_string);
71 gnutls_global_set_log_function(logfunc);
73 gnutls_global_set_log_level(loglevel);
74 err("set log level to %d\n", loglevel);
79 void init_datum(gnutls_datum_t* d) {
83 void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
84 dest->data = gnutls_realloc(dest->data, src->size);
85 dest->size = src->size;
86 memcpy(dest->data, src->data, src->size);
88 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
89 if (a->size > b->size) {
93 if (a->size < b->size) {
97 return memcmp(a->data, b->data, a->size);
99 void free_datum(gnutls_datum_t* d) {
100 gnutls_free(d->data);
105 /* read the passed-in string, store in a single datum */
106 int set_datum_string(gnutls_datum_t* d, const char* s) {
107 unsigned int x = strlen(s)+1;
108 unsigned char* c = NULL;
110 c = gnutls_realloc(d->data, x);
115 memcpy(d->data, s, x);
119 /* read the passed-in file descriptor until EOF, store in a single
121 int set_datum_fd(gnutls_datum_t* d, int fd) {
122 unsigned int bufsize = 1024;
123 unsigned int len = 0;
125 FILE* f = fdopen(fd, "r");
126 if (bufsize > d->size) {
128 d->data = gnutls_realloc(d->data, bufsize);
129 if (d->data == NULL) {
130 err("out of memory!\n");
139 err("could not fdopen FD %d\n", fd);
142 while (!feof(f) && !ferror(f)) {
143 if (len == bufsize) {
144 /* allocate more space by doubling: */
146 d->data = gnutls_realloc(d->data, bufsize);
147 if (d->data == NULL) {
148 err("out of memory!\n");
153 len += fread(d->data + len, 1, bufsize - len, f);
154 /* err("read %d bytes\n", len); */
157 err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
161 /* touch up buffer size to match reality: */
162 d->data = gnutls_realloc(d->data, len);
167 /* read the file indicated (by name) in the fname parameter. store
168 its entire contents in a single datum. */
169 int set_datum_file(gnutls_datum_t* d, const char* fname) {
171 unsigned char* c = NULL;
175 if (0 != stat(fname, &sbuf)) {
176 err("failed to stat '%s'\n", fname);
180 c = gnutls_realloc(d->data, sbuf.st_size);
182 err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
187 d->size = sbuf.st_size;
188 file = fopen(fname, "r");
190 err("failed to open '%s' for reading\n", fname);
194 x = fread(d->data, d->size, 1, file);
196 err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
204 int write_datum_fd(int fd, const gnutls_datum_t* d) {
205 if (d->size != write(fd, d->data, d->size)) {
206 err("failed to write body of datum.\n");
213 int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
215 int looks_negative = (d->data[0] & 0x80);
216 unsigned char zero = 0;
218 /* if the first bit is 1, then the datum will appear negative in the
219 MPI encoding style used by OpenSSH. In that case, we'll increase
220 the length by one, and dump out one more byte */
222 if (looks_negative) {
223 len = htonl(d->size + 1);
225 len = htonl(d->size);
227 if (write(fd, &len, sizeof(len)) != sizeof(len)) {
228 err("failed to write size of datum.\n");
231 if (looks_negative) {
232 if (write(fd, &zero, 1) != 1) {
233 err("failed to write padding byte for MPI.\n");
237 return write_datum_fd(fd, d);
240 int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
244 for (i = 0; i < num; i++)
245 if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
252 int datum_from_string(gnutls_datum_t* d, const char* str) {
253 d->size = strlen(str);
254 d->data = gnutls_realloc(d->data, d->size);
257 memcpy(d->data, str, d->size);
262 int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
267 err("bad pointer passed to create_writing_pipe()\n");
271 if (ret = pipe(p), ret == -1) {
272 err("failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
278 err("Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
281 if (*pid == 0) { /* this is the child */
282 close(p[1]); /* close unused write end */
284 if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
285 err("Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
289 err("exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
290 /* close the open file descriptors */
295 } else { /* this is the parent */
296 close(p[0]); /* close unused read end */
301 int validate_ssh_host_userid(const char* userid) {
302 char* oldlocale = setlocale(LC_ALL, "C");
304 /* choke if userid does not match the expected format
305 ("ssh://fully.qualified.domain.name") */
306 if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
307 err("The user ID should start with ssh:// for a host key\n");
310 /* so that isalnum will work properly */
311 userid += strlen("ssh://");
312 while (0 != (*userid)) {
313 if (!isalnum(*userid)) {
314 err("label did not start with a letter or a digit! (%s)\n", userid);
318 while (isalnum(*userid) || ('-' == (*userid)))
320 if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
323 if (!isalnum(*(userid - 1))) {
324 err("label did not end with a letter or a digit!\n");
327 if ('.' == (*userid)) /* advance to the start of the next label */
330 err("invalid character in domain name: %c\n", *userid);
334 /* ensure that the last character is valid: */
335 if (!isalnum(*(userid - 1))) {
336 err("hostname did not end with a letter or a digit!\n");
339 /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
340 make sure that we've got an OK string? */
345 setlocale(LC_ALL, oldlocale);