/* create a temporary filename in directory */ const char * create_temp_file (const char *directory, const char *prefix, struct gc_arena *gc) { static unsigned int counter; struct buffer fname = alloc_buf_gc (256, gc); int fd; const char *retfname = NULL; unsigned int attempts = 0; do { uint8_t rndbytes[16]; const char *rndstr; ++attempts; ++counter; prng_bytes (rndbytes, sizeof rndbytes); rndstr = format_hex_ex (rndbytes, sizeof rndbytes, 40, 0, NULL, gc); buf_printf (&fname, PACKAGE "_%s_%s.tmp", prefix, rndstr); retfname = gen_path (directory, BSTR (&fname), gc); if (!retfname) { msg (M_FATAL, "Failed to create temporary filename and path"); return NULL; } /* Atomically create the file. Errors out if the file already exists. */ fd = platform_open (retfname, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); if (fd != -1) { close (fd); return retfname; } else if (fd == -1 && errno != EEXIST) { /* Something else went wrong, no need to retry. */ struct gc_arena gcerr = gc_new (); msg (M_FATAL, "Could not create temporary file '%s': %s", retfname, strerror_ts (errno, &gcerr)); gc_free (&gcerr); return NULL; } } while (attempts < 6); msg (M_FATAL, "Failed to create temporary file after %i attempts", attempts); return NULL; }
int read_passphrase_hash (const char *passphrase_file, const md_kt_t *digest, uint8_t *output, int len) { unsigned int outlen = 0; md_ctx_t md; ASSERT (len >= md_kt_size(digest)); memset (output, 0, len); md_ctx_init(&md, digest); /* read passphrase file */ { const int min_passphrase_size = 8; uint8_t buf[64]; int total_size = 0; int fd = platform_open (passphrase_file, O_RDONLY, 0); if (fd == -1) msg (M_ERR, "Cannot open passphrase file: '%s'", passphrase_file); for (;;) { int size = read (fd, buf, sizeof (buf)); if (size == 0) break; if (size == -1) msg (M_ERR, "Read error on passphrase file: '%s'", passphrase_file); md_ctx_update(&md, buf, size); total_size += size; } close (fd); warn_if_group_others_accessible (passphrase_file); if (total_size < min_passphrase_size) msg (M_FATAL, "Passphrase file '%s' is too small (must have at least %d characters)", passphrase_file, min_passphrase_size); } md_ctx_final(&md, output); md_ctx_cleanup(&md); return md_kt_size(digest); }
/* * Record IP/port of client in filesystem, so that server receiving * the proxy can determine true client origin. */ static void journal_add(const char *journal_dir, struct proxy_connection *pc, struct proxy_connection *cp) { struct gc_arena gc = gc_new(); struct openvpn_sockaddr from, to; socklen_t slen, dlen; int fnlen; char *jfn; int fd; slen = sizeof(from.addr.sa); dlen = sizeof(to.addr.sa); if (!getpeername(pc->sd, (struct sockaddr *) &from.addr.sa, &slen) && !getsockname(cp->sd, (struct sockaddr *) &to.addr.sa, &dlen)) { const char *f = print_openvpn_sockaddr(&from, &gc); const char *t = print_openvpn_sockaddr(&to, &gc); fnlen = strlen(journal_dir) + strlen(t) + 2; jfn = (char *) malloc(fnlen); check_malloc_return(jfn); openvpn_snprintf(jfn, fnlen, "%s/%s", journal_dir, t); dmsg(D_PS_PROXY_DEBUG, "PORT SHARE PROXY: client origin %s -> %s", jfn, f); fd = platform_open(jfn, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP); if (fd != -1) { if (write(fd, f, strlen(f)) != strlen(f)) { msg(M_WARN, "PORT SHARE: writing to journal file (%s) failed", jfn); } close(fd); cp->jfn = jfn; } else { msg(M_WARN|M_ERRNO, "PORT SHARE: unable to write journal file in %s", jfn); free(jfn); } } gc_free(&gc); }
void read_key_file (struct key2 *key2, const char *file, const unsigned int flags) { struct gc_arena gc = gc_new (); struct buffer in; int fd, size; uint8_t hex_byte[3] = {0, 0, 0}; const char *error_filename = file; /* parse info */ const unsigned char *cp; int hb_index = 0; int line_num = 1; int line_index = 0; int match = 0; /* output */ uint8_t* out = (uint8_t*) &key2->keys; const int keylen = sizeof (key2->keys); int count = 0; /* parse states */ # define PARSE_INITIAL 0 # define PARSE_HEAD 1 # define PARSE_DATA 2 # define PARSE_DATA_COMPLETE 3 # define PARSE_FOOT 4 # define PARSE_FINISHED 5 int state = PARSE_INITIAL; /* constants */ const int hlen = strlen (static_key_head); const int flen = strlen (static_key_foot); const int onekeylen = sizeof (key2->keys[0]); CLEAR (*key2); /* * Key can be provided as a filename in 'file' or if RKF_INLINE * is set, the actual key data itself in ascii form. */ if (flags & RKF_INLINE) /* 'file' is a string containing ascii representation of key */ { size = strlen (file) + 1; buf_set_read (&in, (const uint8_t *)file, size); error_filename = INLINE_FILE_TAG; } else /* 'file' is a filename which refers to a file containing the ascii key */ { in = alloc_buf_gc (2048, &gc); fd = platform_open (file, O_RDONLY, 0); if (fd == -1) msg (M_ERR, "Cannot open file key file '%s'", file); size = read (fd, in.data, in.capacity); if (size < 0) msg (M_FATAL, "Read error on key file ('%s')", file); if (size == in.capacity) msg (M_FATAL, "Key file ('%s') can be a maximum of %d bytes", file, (int)in.capacity); close (fd); } cp = (unsigned char *)in.data; while (size > 0) { const unsigned char c = *cp; #if 0 msg (M_INFO, "char='%c'[%d] s=%d ln=%d li=%d m=%d c=%d", c, (int)c, state, line_num, line_index, match, count); #endif if (c == '\n') { line_index = match = 0; ++line_num; } else { /* first char of new line */ if (!line_index) { /* first char of line after header line? */ if (state == PARSE_HEAD) state = PARSE_DATA; /* first char of footer */ if ((state == PARSE_DATA || state == PARSE_DATA_COMPLETE) && c == '-') state = PARSE_FOOT; } /* compare read chars with header line */ if (state == PARSE_INITIAL) { if (line_index < hlen && c == static_key_head[line_index]) { if (++match == hlen) state = PARSE_HEAD; } } /* compare read chars with footer line */ if (state == PARSE_FOOT) { if (line_index < flen && c == static_key_foot[line_index]) { if (++match == flen) state = PARSE_FINISHED; } } /* reading key */ if (state == PARSE_DATA) { if (isxdigit(c)) { ASSERT (hb_index >= 0 && hb_index < 2); hex_byte[hb_index++] = c; if (hb_index == 2) { unsigned int u; ASSERT(sscanf((const char *)hex_byte, "%x", &u) == 1); *out++ = u; hb_index = 0; if (++count == keylen) state = PARSE_DATA_COMPLETE; } } else if (isspace(c)) ; else { msg (M_FATAL, (isprint (c) ? printable_char_fmt : unprintable_char_fmt), c, line_num, error_filename, count, onekeylen, keylen); } } ++line_index; } ++cp; --size; } /* * Normally we will read either 1 or 2 keys from file. */ key2->n = count / onekeylen; ASSERT (key2->n >= 0 && key2->n <= (int) SIZE (key2->keys)); if (flags & RKF_MUST_SUCCEED) { if (!key2->n) msg (M_FATAL, "Insufficient key material or header text not found in file '%s' (%d/%d/%d bytes found/min/max)", error_filename, count, onekeylen, keylen); if (state != PARSE_FINISHED) msg (M_FATAL, "Footer text not found in file '%s' (%d/%d/%d bytes found/min/max)", error_filename, count, onekeylen, keylen); } /* zero file read buffer if not an inline file */ if (!(flags & RKF_INLINE)) ovpn_buf_clear (&in); if (key2->n) warn_if_group_others_accessible (error_filename); #if 0 /* DEBUGGING */ { int i; printf ("KEY READ, n=%d\n", key2->n); for (i = 0; i < (int) SIZE (key2->keys); ++i) { /* format key as ascii */ const char *fmt = format_hex_ex ((const uint8_t*)&key2->keys[i], sizeof (key2->keys[i]), 0, 16, "\n", &gc); printf ("[%d]\n%s\n\n", i, fmt); } } #endif /* pop our garbage collection level */ gc_free (&gc); }
/* * Write key to file, return number of random bits * written. */ int write_key_file (const int nkeys, const char *filename) { struct gc_arena gc = gc_new (); int fd, i; int nbits = 0; /* must be large enough to hold full key file */ struct buffer out = alloc_buf_gc (2048, &gc); struct buffer nbits_head_text = alloc_buf_gc (128, &gc); /* how to format the ascii file representation of key */ const int bytes_per_line = 16; /* open key file */ fd = platform_open (filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); if (fd == -1) msg (M_ERR, "Cannot open shared secret file '%s' for write", filename); buf_printf (&out, "%s\n", static_key_head); for (i = 0; i < nkeys; ++i) { struct key key; char* fmt; /* generate random bits */ generate_key_random (&key, NULL); /* format key as ascii */ fmt = format_hex_ex ((const uint8_t*)&key, sizeof (key), 0, bytes_per_line, "\n", &gc); /* increment random bits counter */ nbits += sizeof (key) * 8; /* write to holding buffer */ buf_printf (&out, "%s\n", fmt); /* zero memory which held key component (will be freed by GC) */ memset (fmt, 0, strlen(fmt)); CLEAR (key); } buf_printf (&out, "%s\n", static_key_foot); /* write number of bits */ buf_printf (&nbits_head_text, "#\n# %d bit OpenVPN static key\n#\n", nbits); buf_write_string_file (&nbits_head_text, filename, fd); /* write key file, now formatted in out, to file */ buf_write_string_file (&out, filename, fd); if (close (fd)) msg (M_ERR, "Close error on shared secret file %s", filename); /* zero memory which held file content (memory will be freed by GC) */ ovpn_buf_clear (&out); /* pop our garbage collection level */ gc_free (&gc); return nbits; }
struct status_output * status_open(const char *filename, const int refresh_freq, const int msglevel, const struct virtual_output *vout, const unsigned int flags) { struct status_output *so = NULL; if (filename || msglevel >= 0 || vout) { ALLOC_OBJ_CLEAR(so, struct status_output); so->flags = flags; so->msglevel = msglevel; so->vout = vout; so->fd = -1; buf_reset(&so->read_buf); event_timeout_clear(&so->et); if (filename) { switch (so->flags) { case STATUS_OUTPUT_WRITE: so->fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); break; case STATUS_OUTPUT_READ: so->fd = platform_open(filename, O_RDONLY, S_IRUSR | S_IWUSR); break; case STATUS_OUTPUT_READ|STATUS_OUTPUT_WRITE: so->fd = platform_open(filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); break; default: ASSERT(0); } if (so->fd >= 0) { so->filename = string_alloc(filename, NULL); set_cloexec(so->fd); /* allocate read buffer */ if (so->flags & STATUS_OUTPUT_READ) { so->read_buf = alloc_buf(512); } } else { msg(M_WARN, "Note: cannot open %s for %s", filename, print_status_mode(so->flags)); so->errors = true; } } else { so->flags = STATUS_OUTPUT_WRITE; } if ((so->flags & STATUS_OUTPUT_WRITE) && refresh_freq > 0) { event_timeout_init(&so->et, refresh_freq, 0); } } return so; }