void test_crypto (const struct crypto_options *co, struct frame* frame) { int i, j; struct gc_arena gc = gc_new (); struct buffer src = alloc_buf_gc (TUN_MTU_SIZE (frame), &gc); struct buffer work = alloc_buf_gc (BUF_SIZE (frame), &gc); struct buffer encrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc); struct buffer decrypt_workspace = alloc_buf_gc (BUF_SIZE (frame), &gc); struct buffer buf = clear_buf(); /* init work */ ASSERT (buf_init (&work, FRAME_HEADROOM (frame))); msg (M_INFO, "Entering " PACKAGE_NAME " crypto self-test mode."); for (i = 1; i <= TUN_MTU_SIZE (frame); ++i) { update_time (); msg (M_INFO, "TESTING ENCRYPT/DECRYPT of packet length=%d", i); /* * Load src with random data. */ ASSERT (buf_init (&src, 0)); ASSERT (i <= src.capacity); src.len = i; ASSERT (rand_bytes (BPTR (&src), BLEN (&src))); /* copy source to input buf */ buf = work; memcpy (buf_write_alloc (&buf, BLEN (&src)), BPTR (&src), BLEN (&src)); /* encrypt */ openvpn_encrypt (&buf, encrypt_workspace, co, frame); /* decrypt */ openvpn_decrypt (&buf, decrypt_workspace, co, frame); /* compare */ if (buf.len != src.len) msg (M_FATAL, "SELF TEST FAILED, src.len=%d buf.len=%d", src.len, buf.len); for (j = 0; j < i; ++j) { const uint8_t in = *(BPTR (&src) + j); const uint8_t out = *(BPTR (&buf) + j); if (in != out) msg (M_FATAL, "SELF TEST FAILED, pos=%d in=%d out=%d", j, in, out); } } msg (M_INFO, PACKAGE_NAME " crypto self-test mode SUCCEEDED."); gc_free (&gc); }
static void mroute_helper_regenerate (struct mroute_helper *mh) { int i, j = 0; for (i = MR_HELPER_NET_LEN - 1; i >= 0; --i) { if (mh->net_len_refcount[i] > 0) mh->net_len[j++] = (uint8_t) i; } mh->n_net_len = j; #ifdef ENABLE_DEBUG if (check_debug_level (D_MULTI_DEBUG)) { struct gc_arena gc = gc_new (); struct buffer out = alloc_buf_gc (256, &gc); buf_printf (&out, "MROUTE CIDR netlen:"); for (i = 0; i < mh->n_net_len; ++i) { buf_printf (&out, " /%d", mh->net_len[i]); } dmsg (D_MULTI_DEBUG, "%s", BSTR (&out)); gc_free (&gc); } #endif }
/* print a reliable ACK record coming off the wire */ const char * reliable_ack_print (struct buffer *buf, bool verbose, struct gc_arena *gc) { int i; uint8_t n_ack; struct session_id sid_ack; packet_id_type pid; struct buffer out = alloc_buf_gc (256, gc); buf_printf (&out, "["); if (!buf_read (buf, &n_ack, sizeof (n_ack))) goto done; for (i = 0; i < n_ack; ++i) { if (!buf_read (buf, &pid, sizeof (pid))) goto done; pid = ntohpid (pid); buf_printf (&out, " " packet_id_format, (packet_id_print_type)pid); } if (n_ack) { if (!session_id_read (&sid_ack, buf)) goto done; if (verbose) buf_printf (&out, " sid=%s", session_id_print (&sid_ack, gc)); } done: buf_printf (&out, " ]"); return BSTR (&out); }
/* * Initialise the given ctr_drbg context, using a personalisation string and an * entropy gathering function. */ ctr_drbg_context * rand_ctx_get() { static entropy_context ec = {0}; static ctr_drbg_context cd_ctx = {0}; static bool rand_initialised = false; if (!rand_initialised) { struct gc_arena gc = gc_new(); struct buffer pers_string = alloc_buf_gc(100, &gc); /* * Personalisation string, should be as unique as possible (see NIST * 800-90 section 8.7.1). We have very little information at this stage. * Include Program Name, memory address of the context and PID. */ buf_printf(&pers_string, "OpenVPN %0u %p %s", platform_getpid(), &cd_ctx, time_string(0, 0, 0, &gc)); /* Initialise PolarSSL RNG, and built-in entropy sources */ entropy_init(&ec); if (0 != ctr_drbg_init(&cd_ctx, entropy_func, &ec, BPTR(&pers_string), BLEN(&pers_string))) msg (M_FATAL, "Failed to initialize random generator"); gc_free(&gc); rand_initialised = true; } return &cd_ctx; }
const char * time_string (time_t t, int usec, bool show_usec, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (64, gc); struct timeval tv; if (t) { tv.tv_sec = t; tv.tv_usec = usec; } else { gettimeofday (&tv, NULL); } t = tv.tv_sec; buf_printf (&out, "%s", ctime(&t)); buf_rmtail (&out, '\n'); if (show_usec && tv.tv_usec) buf_printf (&out, " us=%d", (int)tv.tv_usec); return BSTR (&out); }
/* * Print an error message based on the status code returned by system(). */ const char * system_error_message (int stat, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (256, gc); #ifdef WIN32 if (stat == -1) buf_printf (&out, "external program did not execute -- "); buf_printf (&out, "returned error code %d", stat); #else if (stat == -1) buf_printf (&out, "external program fork failed"); else if (!WIFEXITED (stat)) buf_printf (&out, "external program did not exit normally"); else { const int cmd_ret = WEXITSTATUS (stat); if (!cmd_ret) buf_printf (&out, "external program exited normally"); else if (cmd_ret == 127) buf_printf (&out, "could not execute external program"); else buf_printf (&out, "external program exited with error status: %d", cmd_ret); } #endif return (const char *)out.data; }
static const char * print_str_int (const char *str, const int i, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (128, gc); buf_printf (&out, "%s %d", str, i); return BSTR (&out); }
/* * Handle incoming configuration * messages on the control channel. */ void check_incoming_control_channel_dowork (struct context *c) { const int len = tls_test_payload_len (c->c2.tls_multi); if (len) { struct gc_arena gc = gc_new (); struct buffer buf = alloc_buf_gc (len, &gc); if (tls_rec_payload (c->c2.tls_multi, &buf)) { /* force null termination of message */ buf_null_terminate (&buf); /* enforce character class restrictions */ string_mod (BSTR (&buf), CC_PRINT, CC_CRLF, 0); if (buf_string_match_head_str (&buf, "AUTH_FAILED")) receive_auth_failed (c, &buf); else if (buf_string_match_head_str (&buf, "PUSH_")) incoming_push_message (c, &buf); else if (buf_string_match_head_str (&buf, "RESTART")) server_pushed_signal (c, &buf, true, 7); else if (buf_string_match_head_str (&buf, "HALT")) server_pushed_signal (c, &buf, false, 4); else msg (D_PUSH_ERRORS, "WARNING: Received unknown control message: %s", BSTR (&buf)); } else { msg (D_PUSH_ERRORS, "WARNING: Receive control message failed"); } gc_free (&gc); } }
bool crypto_pem_encode(const char *name, struct buffer *dst, const struct buffer *src, struct gc_arena *gc) { bool ret = false; BIO *bio = BIO_new(BIO_s_mem()); if (!bio || !PEM_write_bio(bio, name, "", BPTR(src), BLEN(src))) { ret = false; goto cleanup; } BUF_MEM *bptr; BIO_get_mem_ptr(bio, &bptr); *dst = alloc_buf_gc(bptr->length, gc); ASSERT(buf_write(dst, bptr->data, bptr->length)); ret = true; cleanup: if (!BIO_free(bio)) { ret = false;; } return ret; }
/* * Put a directory and filename together. */ const char * gen_path (const char *directory, const char *filename, struct gc_arena *gc) { #if WIN32 const int CC_PATH_RESERVED = CC_LESS_THAN|CC_GREATER_THAN|CC_COLON| CC_DOUBLE_QUOTE|CC_SLASH|CC_BACKSLASH|CC_PIPE|CC_QUESTION_MARK|CC_ASTERISK; #else const int CC_PATH_RESERVED = CC_SLASH; #endif const char *safe_filename = string_mod_const (filename, CC_PRINT, CC_PATH_RESERVED, '_', gc); if (safe_filename && strcmp (safe_filename, ".") && strcmp (safe_filename, "..") #ifdef WIN32 && win_safe_filename (safe_filename) #endif ) { const size_t outsize = strlen(safe_filename) + (directory ? strlen (directory) : 0) + 16; struct buffer out = alloc_buf_gc (outsize, gc); char dirsep[2]; dirsep[0] = OS_SPECIFIC_DIRSEP; dirsep[1] = '\0'; if (directory) buf_printf (&out, "%s%s", directory, dirsep); buf_printf (&out, "%s", safe_filename); return BSTR (&out); } else return NULL; }
static const char * print_opt_route_gateway_dhcp (struct gc_arena *gc) { struct buffer out = alloc_buf_gc (32, gc); buf_printf (&out, "route-gateway dhcp"); return BSTR (&out); }
bool send_push_reply (struct context *c) { struct gc_arena gc = gc_new (); struct buffer buf = alloc_buf_gc (MAX_PUSH_LIST_LEN + 256, &gc); bool ret = false; buf_printf (&buf, "PUSH_REPLY"); if (c->options.push_list && strlen (c->options.push_list->options)) buf_printf (&buf, ",%s", c->options.push_list->options); if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask) buf_printf (&buf, ",ifconfig %s %s", print_in_addr_t (c->c2.push_ifconfig_local, 0, &gc), print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc)); if (strlen (BSTR (&buf)) < MAX_PUSH_LIST_LEN) ret = send_control_channel_string (c, BSTR (&buf), D_PUSH); else msg (M_WARN, "Maximum length of --push buffer (%d) has been exceeded", MAX_PUSH_LIST_LEN); gc_free (&gc); return ret; }
/* * Send auth failed message from server to client. */ void send_auth_failed(struct context *c, const char *client_reason) { struct gc_arena gc = gc_new(); static const char auth_failed[] = "AUTH_FAILED"; size_t len; schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM); len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed); if (len > PUSH_BUNDLE_SIZE) { len = PUSH_BUNDLE_SIZE; } { struct buffer buf = alloc_buf_gc(len, &gc); buf_printf(&buf, auth_failed); if (client_reason) { buf_printf(&buf, ",%s", client_reason); } send_control_channel_string(c, BSTR(&buf), D_PUSH); } gc_free(&gc); }
const char * mroute_addr_print_ex (const struct mroute_addr *ma, const unsigned int flags, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (64, gc); if (ma) { struct mroute_addr maddr = *ma; switch (maddr.type & MR_ADDR_MASK) { case MR_ADDR_ETHER: buf_printf (&out, "%s", format_hex_ex (ma->addr, 6, 0, 1, ":", gc)); break; case MR_ADDR_IPV4: { struct buffer buf; in_addr_t addr; int port; bool status; buf_set_read (&buf, maddr.addr, maddr.len); addr = buf_read_u32 (&buf, &status); if (status) { if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP)) buf_printf (&out, "ARP/"); buf_printf (&out, "%s", print_in_addr_t (addr, (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc)); if (maddr.type & MR_WITH_NETBITS) { if (flags & MAPF_SUBNET) { const in_addr_t netmask = netbits_to_netmask (maddr.netbits); buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc)); } else buf_printf (&out, "/%d", maddr.netbits); } } if (maddr.type & MR_WITH_PORT) { port = buf_read_u16 (&buf); if (port >= 0) buf_printf (&out, ":%d", port); } } break; case MR_ADDR_IPV6: buf_printf (&out, "IPV6"); break; default: buf_printf (&out, "UNKNOWN"); break; } return BSTR (&out); } else return "[NULL]"; }
static const char * print_opt_route_gateway (const in_addr_t route_gateway, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (128, gc); ASSERT (route_gateway); buf_printf (&out, "route-gateway %s", print_in_addr_t (route_gateway, 0, gc)); return BSTR (&out); }
alloc_buf (size_t size) #endif { #ifdef DMALLOC return alloc_buf_gc_debug (size, NULL, file, line); #else return alloc_buf_gc (size, NULL); #endif }
static const char * username_password_as_base64 (const struct http_proxy_info *p, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (strlen (p->up.username) + strlen (p->up.password) + 2, gc); ASSERT (strlen (p->up.username) > 0); buf_printf (&out, "%s:%s", p->up.username, p->up.password); return (const char *)make_base64_string ((const uint8_t*)BSTR (&out), gc); }
static const char * print_opt_topology (const int topology, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (128, gc); buf_printf (&out, "topology %s", print_topology (topology)); return BSTR (&out); }
/* * Return a numerical string describing a struct timeval. */ const char * tv_string (const struct timeval *tv, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (64, gc); buf_printf (&out, "[%d/%d]", (int) tv->tv_sec, (int )tv->tv_usec); return BSTR (&out); }
static bool tls_crypt_v2_wrap_client_key(struct buffer *wkc, const struct key2 *src_key, const struct buffer *src_metadata, struct key_ctx *server_key, struct gc_arena *gc) { cipher_ctx_t *cipher_ctx = server_key->cipher; struct buffer work = alloc_buf_gc(TLS_CRYPT_V2_MAX_WKC_LEN + cipher_ctx_block_size(cipher_ctx), gc); /* Calculate auth tag and synthetic IV */ uint8_t *tag = buf_write_alloc(&work, TLS_CRYPT_TAG_SIZE); if (!tag) { msg(M_WARN, "ERROR: could not write tag"); return false; } uint16_t net_len = htons(sizeof(src_key->keys) + BLEN(src_metadata) + TLS_CRYPT_V2_TAG_SIZE + sizeof(uint16_t)); hmac_ctx_t *hmac_ctx = server_key->hmac; hmac_ctx_reset(hmac_ctx); hmac_ctx_update(hmac_ctx, (void *)&net_len, sizeof(net_len)); hmac_ctx_update(hmac_ctx, (void *)src_key->keys, sizeof(src_key->keys)); hmac_ctx_update(hmac_ctx, BPTR(src_metadata), BLEN(src_metadata)); hmac_ctx_final(hmac_ctx, tag); dmsg(D_CRYPTO_DEBUG, "TLS-CRYPT WRAP TAG: %s", format_hex(tag, TLS_CRYPT_TAG_SIZE, 0, gc)); /* Use the 128 most significant bits of the tag as IV */ ASSERT(cipher_ctx_reset(cipher_ctx, tag)); /* Overflow check (OpenSSL requires an extra block in the dst buffer) */ if (buf_forward_capacity(&work) < (sizeof(src_key->keys) + BLEN(src_metadata) + sizeof(net_len) + cipher_ctx_block_size(cipher_ctx))) { msg(M_WARN, "ERROR: could not crypt: insufficient space in dst"); return false; } /* Encrypt */ int outlen = 0; ASSERT(cipher_ctx_update(cipher_ctx, BEND(&work), &outlen, (void *)src_key->keys, sizeof(src_key->keys))); ASSERT(buf_inc_len(&work, outlen)); ASSERT(cipher_ctx_update(cipher_ctx, BEND(&work), &outlen, BPTR(src_metadata), BLEN(src_metadata))); ASSERT(buf_inc_len(&work, outlen)); ASSERT(cipher_ctx_final(cipher_ctx, BEND(&work), &outlen)); ASSERT(buf_inc_len(&work, outlen)); ASSERT(buf_write(&work, &net_len, sizeof(net_len))); return buf_copy(wkc, &work); }
static const char * print_netmask (int netbits, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (128, gc); const in_addr_t netmask = netbits_to_netmask (netbits); buf_printf (&out, "%s (/%d)", print_in_addr_t (netmask, 0, gc), netbits); return BSTR (&out); }
/* * Setenv functions that append an integer index to the name */ static const char * setenv_format_indexed_name (const char *name, const int i, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (strlen (name) + 16, gc); if (i >= 0) buf_printf (&out, "%s_%d", name, i); else buf_printf (&out, "%s", name); return BSTR (&out); }
void window_title_generate (const char *title) { struct gc_arena gc = gc_new (); struct buffer out = alloc_buf_gc (256, &gc); if (!title) title = ""; buf_printf (&out, "[%s] " PACKAGE_NAME " " VERSION " F4:EXIT F1:USR1 F2:USR2 F3:HUP", title); SetConsoleTitle (BSTR (&out)); gc_free (&gc); }
void ifconfig_pool_read(struct ifconfig_pool_persist *persist, struct ifconfig_pool *pool) { const int buf_size = 128; update_time(); if (persist && persist->file && pool) { struct gc_arena gc = gc_new(); struct buffer in = alloc_buf_gc(256, &gc); char *cn_buf; char *ip_buf; int line = 0; ALLOC_ARRAY_CLEAR_GC(cn_buf, char, buf_size, &gc); ALLOC_ARRAY_CLEAR_GC(ip_buf, char, buf_size, &gc); while (true) { ASSERT(buf_init(&in, 0)); if (!status_read(persist->file, &in)) { break; } ++line; if (BLEN(&in)) { int c = *BSTR(&in); if (c == '#' || c == ';') { continue; } msg( M_INFO, "ifconfig_pool_read(), in='%s', TODO: IPv6", BSTR(&in) ); if (buf_parse(&in, ',', cn_buf, buf_size) && buf_parse(&in, ',', ip_buf, buf_size)) { bool succeeded; const in_addr_t addr = getaddr(GETADDR_HOST_ORDER, ip_buf, 0, &succeeded, NULL); if (succeeded) { msg( M_INFO, "succeeded -> ifconfig_pool_set()"); ifconfig_pool_set(pool, cn_buf, addr, persist->fixed); } } } } ifconfig_pool_msg(pool, D_IFCONFIG_POOL); gc_free(&gc); } }
const char * strerror_ts (int errnum, struct gc_arena *gc) { #ifdef HAVE_STRERROR struct buffer out = alloc_buf_gc (256, gc); buf_printf (&out, "%s", openvpn_strerror (errnum, gc)); return BSTR (&out); #else return "[error string unavailable]"; #endif }
static char * construct_name_value (const char *name, const char *value, struct gc_arena *gc) { struct buffer out; ASSERT (name); if (!value) value = ""; out = alloc_buf_gc (strlen (name) + strlen (value) + 2, gc); buf_printf (&out, "%s=%s", name, value); return BSTR (&out); }
const char * wait_status_string (struct context *c, struct gc_arena *gc) { struct buffer out = alloc_buf_gc (64, gc); buf_printf (&out, "I/O WAIT %s|%s|%s|%s %s", tun_stat (c->c1.tuntap, EVENT_READ, gc), tun_stat (c->c1.tuntap, EVENT_WRITE, gc), socket_stat (c->c2.link_socket, EVENT_READ, gc), socket_stat (c->c2.link_socket, EVENT_WRITE, gc), tv_string (&c->c2.timeval, gc)); return BSTR (&out); }
/* * Configure PATH. On Windows, sometimes PATH is not set correctly * by default. */ static void configure_win_path (void) { static bool done = false; /* GLOBAL */ if (!done) { FILE *fp; fp = fopen ("c:\\windows\\system32\\route.exe", "rb"); if (fp) { const int bufsiz = 4096; struct gc_arena gc = gc_new (); struct buffer oldpath = alloc_buf_gc (bufsiz, &gc); struct buffer newpath = alloc_buf_gc (bufsiz, &gc); const char* delim = ";"; DWORD status; fclose (fp); status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), (DWORD)BCAP(&oldpath)); #if 0 status = 0; #endif if (!status) { *BPTR(&oldpath) = '\0'; delim = ""; } buf_printf (&newpath, "C:\\WINDOWS\\System32;C:\\WINDOWS;C:\\WINDOWS\\System32\\Wbem%s%s", delim, BSTR(&oldpath)); SetEnvironmentVariable ("PATH", BSTR(&newpath)); #if 0 status = GetEnvironmentVariable ("PATH", BPTR(&oldpath), (DWORD)BCAP(&oldpath)); if (status > 0) printf ("PATH: %s\n", BSTR(&oldpath)); #endif gc_free (&gc); done = true; } } }
/* 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; }
static WCHAR * wide_cmd_line(const struct argv *a, struct gc_arena *gc) { size_t nchars = 1; size_t maxlen = 0; size_t i; struct buffer buf; char *work = NULL; if (!a) { return NULL; } for (i = 0; i < a->argc; ++i) { const char *arg = a->argv[i]; const size_t len = strlen(arg); nchars += len + 3; if (len > maxlen) { maxlen = len; } } work = gc_malloc(maxlen + 1, false, gc); check_malloc_return(work); buf = alloc_buf_gc(nchars, gc); for (i = 0; i < a->argc; ++i) { const char *arg = a->argv[i]; strcpy(work, arg); string_mod(work, CC_PRINT, CC_DOUBLE_QUOTE|CC_CRLF, '_'); if (i) { buf_printf(&buf, " "); } if (string_class(work, CC_ANY, CC_SPACE)) { buf_printf(&buf, "%s", work); } else { buf_printf(&buf, "\"%s\"", work); } } return wide_string(BSTR(&buf), gc); }