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);
25 void logfunc(int level, const char* string) {
26 fprintf(stderr, "GnuTLS Logging (%d): %s\n", level, string);
29 void init_keyid(gnutls_openpgp_keyid_t keyid) {
30 memset(keyid, 'x', sizeof(gnutls_openpgp_keyid_t));
35 void make_keyid_printable(printable_keyid out, gnutls_openpgp_keyid_t keyid)
37 static const char hex[16] = "0123456789ABCDEF";
38 unsigned int kix = 0, outix = 0;
40 while (kix < sizeof(gnutls_openpgp_keyid_t)) {
41 out[outix] = hex[(keyid[kix] >> 4) & 0x0f];
42 out[outix + 1] = hex[keyid[kix] & 0x0f];
50 const char* version = NULL;
51 const char* debug_string = NULL;
54 if (ret = gnutls_global_init(), ret) {
55 err("Failed to do gnutls_global_init() (error: %d)\n", ret);
59 version = gnutls_check_version(NULL);
62 err("gnutls version: %s\n", version);
64 err("no version found!\n");
68 if (debug_string = getenv("MONKEYSPHERE_DEBUG"), debug_string) {
69 loglevel = atoi(debug_string);
70 gnutls_global_set_log_function(logfunc);
72 gnutls_global_set_log_level(loglevel);
73 err("set log level to %d\n", loglevel);
78 void init_datum(gnutls_datum_t* d) {
82 void copy_datum(gnutls_datum_t* dest, const gnutls_datum_t* src) {
83 dest->data = gnutls_realloc(dest->data, src->size);
84 dest->size = src->size;
85 memcpy(dest->data, src->data, src->size);
87 int compare_data(const gnutls_datum_t* a, const gnutls_datum_t* b) {
88 if (a->size > b->size) {
92 if (a->size < b->size) {
96 return memcmp(a->data, b->data, a->size);
98 void free_datum(gnutls_datum_t* d) {
104 /* read the passed-in string, store in a single datum */
105 int set_datum_string(gnutls_datum_t* d, const char* s) {
106 unsigned int x = strlen(s)+1;
107 unsigned char* c = NULL;
109 c = gnutls_realloc(d->data, x);
114 memcpy(d->data, s, x);
118 /* read the passed-in file descriptor until EOF, store in a single
120 int set_datum_fd(gnutls_datum_t* d, int fd) {
121 unsigned int bufsize = 1024;
122 unsigned int len = 0;
124 FILE* f = fdopen(fd, "r");
125 if (bufsize > d->size) {
127 d->data = gnutls_realloc(d->data, bufsize);
128 if (d->data == NULL) {
129 err("out of memory!\n");
138 err("could not fdopen FD %d\n", fd);
141 while (!feof(f) && !ferror(f)) {
142 if (len == bufsize) {
143 /* allocate more space by doubling: */
145 d->data = gnutls_realloc(d->data, bufsize);
146 if (d->data == NULL) {
147 err("out of memory!\n");
152 len += fread(d->data + len, 1, bufsize - len, f);
153 /* err("read %d bytes\n", len); */
156 err("Error reading from fd %d (error: %d) (error: %d '%s')\n", fd, ferror(f), errno, strerror(errno));
160 /* touch up buffer size to match reality: */
161 d->data = gnutls_realloc(d->data, len);
166 /* read the file indicated (by name) in the fname parameter. store
167 its entire contents in a single datum. */
168 int set_datum_file(gnutls_datum_t* d, const char* fname) {
170 unsigned char* c = NULL;
174 if (0 != stat(fname, &sbuf)) {
175 err("failed to stat '%s'\n", fname);
179 c = gnutls_realloc(d->data, sbuf.st_size);
181 err("failed to allocate %d bytes for '%s'\n", sbuf.st_size, fname);
186 d->size = sbuf.st_size;
187 file = fopen(fname, "r");
189 err("failed to open '%s' for reading\n", fname);
193 x = fread(d->data, d->size, 1, file);
195 err("tried to read %d bytes, read %d instead from '%s'\n", d->size, x, fname);
203 int write_datum_fd(int fd, const gnutls_datum_t* d) {
204 if (d->size != write(fd, d->data, d->size)) {
205 err("failed to write body of datum.\n");
212 int write_datum_fd_with_length(int fd, const gnutls_datum_t* d) {
214 int looks_negative = (d->data[0] & 0x80);
215 unsigned char zero = 0;
217 /* if the first bit is 1, then the datum will appear negative in the
218 MPI encoding style used by OpenSSH. In that case, we'll increase
219 the length by one, and dump out one more byte */
221 if (looks_negative) {
222 len = htonl(d->size + 1);
224 len = htonl(d->size);
226 if (write(fd, &len, sizeof(len)) != sizeof(len)) {
227 err("failed to write size of datum.\n");
230 if (looks_negative) {
231 if (write(fd, &zero, 1) != 1) {
232 err("failed to write padding byte for MPI.\n");
236 return write_datum_fd(fd, d);
239 int write_data_fd_with_length(int fd, const gnutls_datum_t** d, unsigned int num) {
243 for (i = 0; i < num; i++)
244 if (ret = write_datum_fd_with_length(fd, d[i]), ret != 0)
251 int datum_from_string(gnutls_datum_t* d, const char* str) {
252 d->size = strlen(str);
253 d->data = gnutls_realloc(d->data, d->size);
256 memcpy(d->data, str, d->size);
261 int create_writing_pipe(pid_t* pid, const char* path, char* const argv[]) {
266 err("bad pointer passed to create_writing_pipe()\n");
270 if (ret = pipe(p), ret == -1) {
271 err("failed to create a pipe (error: %d \"%s\")\n", errno, strerror(errno));
277 err("Failed to fork (error: %d \"%s\")\n", errno, strerror(errno));
280 if (*pid == 0) { /* this is the child */
281 close(p[1]); /* close unused write end */
283 if (0 != dup2(p[0], 0)) { /* map the reading end into stdin */
284 err("Failed to transfer reading file descriptor to stdin (error: %d \"%s\")\n", errno, strerror(errno));
288 err("exec %s failed (error: %d \"%s\")\n", path, errno, strerror(errno));
290 } else { /* this is the parent */
291 close(p[0]); /* close unused read end */
296 int validate_ssh_host_userid(const char* userid) {
297 char* oldlocale = setlocale(LC_ALL, "C");
299 /* choke if userid does not match the expected format
300 ("ssh://fully.qualified.domain.name") */
301 if (strncmp("ssh://", userid, strlen("ssh://")) != 0) {
302 err("The user ID should start with ssh:// for a host key\n");
305 /* so that isalnum will work properly */
306 userid += strlen("ssh://");
307 while (0 != (*userid)) {
308 if (!isalnum(*userid)) {
309 err("label did not start with a letter or a digit! (%s)\n", userid);
313 while (isalnum(*userid) || ('-' == (*userid)))
315 if (('.' == (*userid)) || (0 == (*userid))) { /* clean end of label:
318 if (!isalnum(*(userid - 1))) {
319 err("label did not end with a letter or a digit!\n");
322 if ('.' == (*userid)) /* advance to the start of the next label */
325 err("invalid character in domain name: %c\n", *userid);
329 /* ensure that the last character is valid: */
330 if (!isalnum(*(userid - 1))) {
331 err("hostname did not end with a letter or a digit!\n");
334 /* FIXME: fqdn's can be unicode now, thanks to RFC 3490 -- how do we
335 make sure that we've got an OK string? */
340 setlocale(LC_ALL, oldlocale);