/* * Run execve() inside a fork(), duping stdout. Designed to replicate the semantics of popen() but * in a safer way that doesn't require the invocation of a shell or the risks * assocated with formatting and parsing a command line. */ int openvpn_popen(const struct argv *a, const struct env_set *es) { struct gc_arena gc = gc_new(); int ret = -1; static bool warn_shown = false; if (a && a->argv[0]) { #if defined(ENABLE_FEATURE_EXECVE) if (script_security() >= SSEC_BUILT_IN) { const char *cmd = a->argv[0]; char *const *argv = a->argv; char *const *envp = (char *const *)make_env_array(es, true, &gc); pid_t pid; int pipe_stdout[2]; if (pipe(pipe_stdout) == 0) { pid = fork(); if (pid == (pid_t)0) /* child side */ { close(pipe_stdout[0]); /* Close read end */ dup2(pipe_stdout[1],1); execve(cmd, argv, envp); exit(127); } else if (pid > (pid_t)0) /* parent side */ { int status = 0; close(pipe_stdout[1]); /* Close write end */ waitpid(pid, &status, 0); ret = pipe_stdout[0]; } else /* fork failed */ { close(pipe_stdout[0]); close(pipe_stdout[1]); msg(M_ERR, "openvpn_popen: unable to fork %s", cmd); } } else { msg(M_WARN, "openvpn_popen: unable to create stdout pipe for %s", cmd); ret = -1; } } else if (!warn_shown && (script_security() < SSEC_SCRIPTS)) { msg(M_WARN, SCRIPT_SECURITY_WARNING); warn_shown = true; } #else /* if defined(ENABLE_FEATURE_EXECVE) */ msg(M_WARN, "openvpn_popen: execve function not available"); #endif /* if defined(ENABLE_FEATURE_EXECVE) */ } else { msg(M_FATAL, "openvpn_popen: called with empty argv"); } gc_free(&gc); return ret; }
struct multi_instance * multi_get_create_instance_udp (struct multi_context *m, bool *floated) { struct gc_arena gc = gc_new (); struct mroute_addr real; struct multi_instance *mi = NULL; struct hash *hash = m->hash; if (mroute_extract_openvpn_sockaddr (&real, &m->top.c2.from.dest, true)) { struct hash_element *he; const uint32_t hv = hash_value (hash, &real); struct hash_bucket *bucket = hash_bucket (hash, hv); uint8_t* ptr = BPTR(&m->top.c2.buf); uint8_t op = ptr[0] >> P_OPCODE_SHIFT; uint32_t peer_id; int i; /* make sure buffer has enough length to read opcode (1 byte) and peer-id (3 bytes) */ if (op == P_DATA_V2 && m->top.c2.buf.len >= (1 + 3)) { peer_id = ntohl(*(uint32_t*)ptr) & 0xFFFFFF; if ((peer_id < m->max_clients) && (m->instances[peer_id])) { mi = m->instances[peer_id]; *floated = !link_socket_actual_match(&mi->context.c2.from, &m->top.c2.from); if (*floated) { /* reset prefix, since here we are not sure peer is the one it claims to be */ ungenerate_prefix(mi); msg (D_MULTI_ERRORS, "Untrusted peer %" PRIu32 " wants to float to %s", peer_id, mroute_addr_print (&real, &gc)); } } } else { he = hash_lookup_fast (hash, bucket, &real, hv); if (he) { mi = (struct multi_instance *) he->value; } } if (!mi) { if (!m->top.c2.tls_auth_standalone || tls_pre_decrypt_lite (m->top.c2.tls_auth_standalone, &m->top.c2.from, &m->top.c2.buf)) { if (frequency_limit_event_allowed (m->new_connection_limiter)) { mi = multi_create_instance (m, &real); if (mi) { hash_add_fast (hash, bucket, &mi->real, hv, mi); mi->did_real_hash = true; for (i = 0; i < m->max_clients; ++i) { if (!m->instances[i]) { mi->context.c2.tls_multi->peer_id = i; m->instances[i] = mi; break; } } /* should not really end up here, since multi_create_instance returns null * if amount of clients exceeds max_clients */ ASSERT(i < m->max_clients); } } else { msg (D_MULTI_ERRORS, "MULTI: Connection from %s would exceed new connection frequency limit as controlled by --connect-freq", mroute_addr_print (&real, &gc)); } } } #ifdef ENABLE_DEBUG if (check_debug_level (D_MULTI_DEBUG)) { const char *status = mi ? "[ok]" : "[failed]"; dmsg (D_MULTI_DEBUG, "GET INST BY REAL: %s %s", mroute_addr_print (&real, &gc), status); } #endif } gc_free (&gc); ASSERT (!(mi && mi->halt)); return mi; }
/* * Attempt to simulate fork/execve on Windows */ int openvpn_execve(const struct argv *a, const struct env_set *es, const unsigned int flags) { int ret = -1; static bool exec_warn = false; if (a && a->argv[0]) { if (openvpn_execve_allowed(flags)) { struct gc_arena gc = gc_new(); STARTUPINFOW start_info; PROCESS_INFORMATION proc_info; char *env = env_block(es); WCHAR *cl = wide_cmd_line(a, &gc); WCHAR *cmd = wide_string(a->argv[0], &gc); /* this allows console programs to run, and is ignored otherwise */ DWORD proc_flags = CREATE_NO_WINDOW; CLEAR(start_info); CLEAR(proc_info); /* fill in STARTUPINFO struct */ GetStartupInfoW(&start_info); start_info.cb = sizeof(start_info); start_info.dwFlags = STARTF_USESHOWWINDOW; start_info.wShowWindow = SW_HIDE; if (CreateProcessW(cmd, cl, NULL, NULL, FALSE, proc_flags, env, NULL, &start_info, &proc_info)) { DWORD exit_status = 0; CloseHandle(proc_info.hThread); WaitForSingleObject(proc_info.hProcess, INFINITE); if (GetExitCodeProcess(proc_info.hProcess, &exit_status)) { ret = (int)exit_status; } else { msg(M_WARN|M_ERRNO, "openvpn_execve: GetExitCodeProcess %S failed", cmd); } CloseHandle(proc_info.hProcess); } else { msg(M_WARN|M_ERRNO, "openvpn_execve: CreateProcess %S failed", cmd); } free(env); gc_free(&gc); } else if (!exec_warn && (script_security < SSEC_SCRIPTS)) { msg(M_WARN, SCRIPT_SECURITY_WARNING); exec_warn = true; } } else { msg(M_WARN, "openvpn_execve: called with empty argv"); } return ret; }
void list_test(void) { openvpn_thread_init(); { struct gc_arena gc = gc_new(); struct hash *hash = hash_init(10000, get_random(), word_hash_function, word_compare_function); struct hash *nhash = hash_init(256, get_random(), word_hash_function, word_compare_function); printf("hash_init n_buckets=%d mask=0x%08x\n", hash->n_buckets, hash->mask); /* parse words from stdin */ while (true) { char buf[256]; char wordbuf[256]; int wbi; int bi; char c; if (!fgets(buf, sizeof(buf), stdin)) { break; } bi = wbi = 0; do { c = buf[bi++]; if (isalnum(c) || c == '_') { ASSERT(wbi < (int) sizeof(wordbuf)); wordbuf[wbi++] = c; } else { if (wbi) { struct word *w; ASSERT(wbi < (int) sizeof(wordbuf)); wordbuf[wbi++] = '\0'; /* word is parsed from stdin */ /* does it already exist in table? */ w = (struct word *) hash_lookup(hash, wordbuf); if (w) { /* yes, increment count */ ++w->n; } else { /* no, make a new object */ ALLOC_OBJ_GC(w, struct word, &gc); w->word = string_alloc(wordbuf, &gc); w->n = 1; ASSERT(hash_add(hash, w->word, w, false)); ASSERT(hash_add(nhash, w->word, (void *) ((random() & 0x0F) + 1), false)); } } wbi = 0; } } while (c); } #if 1 /* remove some words from the table */ { rmhash(hash, "true"); rmhash(hash, "false"); } #endif /* output contents of hash table */ { int base; int inc = 0; int count = 0; for (base = 0; base < hash_n_buckets(hash); base += inc) { struct hash_iterator hi; struct hash_element *he; inc = (get_random() % 3) + 1; hash_iterator_init_range(hash, &hi, true, base, base + inc); while ((he = hash_iterator_next(&hi))) { struct word *w = (struct word *) he->value; printf("%6d '%s'\n", w->n, w->word); ++count; } hash_iterator_free(&hi); } ASSERT(count == hash_n_elements(hash)); } #if 1 /* test hash_remove_by_value function */ { int i; for (i = 1; i <= 16; ++i) { printf("[%d] ***********************************\n", i); print_nhash(nhash); hash_remove_by_value(nhash, (void *) i, true); } printf("FINAL **************************\n"); print_nhash(nhash); } #endif hash_free(hash); hash_free(nhash); gc_free(&gc); } openvpn_thread_cleanup(); }
bool get_user_pass_cr(struct user_pass *up, const char *auth_file, const char *prefix, const unsigned int flags, const char *auth_challenge) { struct gc_arena gc = gc_new(); if (!up->defined) { bool from_authfile = (auth_file && !streq(auth_file, "stdin")); bool username_from_stdin = false; bool password_from_stdin = false; bool response_from_stdin = true; if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED) { msg(M_WARN, "Note: previous '%s' credentials failed", prefix); } #ifdef ENABLE_MANAGEMENT /* * Get username/password from management interface? */ if (management && (!from_authfile && (flags & GET_USER_PASS_MANAGEMENT)) && management_query_user_pass_enabled(management)) { const char *sc = NULL; response_from_stdin = false; if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED) { management_auth_failure(management, prefix, "previous auth credentials failed"); } #ifdef ENABLE_CLIENT_CR if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE)) { sc = auth_challenge; } #endif if (!management_query_user_pass(management, up, prefix, flags, sc)) { if ((flags & GET_USER_PASS_NOFATAL) != 0) { return false; } else { msg(M_FATAL, "ERROR: could not read %s username/password/ok/string from management interface", prefix); } } } else #endif /* ifdef ENABLE_MANAGEMENT */ /* * Get NEED_OK confirmation from the console */ if (flags & GET_USER_PASS_NEED_OK) { struct buffer user_prompt = alloc_buf_gc(128, &gc); buf_printf(&user_prompt, "NEED-OK|%s|%s:", prefix, up->username); if (!query_user_SINGLE(BSTR(&user_prompt), BLEN(&user_prompt), up->password, USER_PASS_LEN, false)) { msg(M_FATAL, "ERROR: could not read %s ok-confirmation from stdin", prefix); } if (!strlen(up->password)) { strcpy(up->password, "ok"); } } else if (flags & GET_USER_PASS_INLINE_CREDS) { struct buffer buf; buf_set_read(&buf, (uint8_t *) auth_file, strlen(auth_file) + 1); if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) { buf_parse(&buf, '\n', up->username, USER_PASS_LEN); } buf_parse(&buf, '\n', up->password, USER_PASS_LEN); } /* * Read from auth file unless this is a dynamic challenge request. */ else if (from_authfile && !(flags & GET_USER_PASS_DYNAMIC_CHALLENGE)) { /* * Try to get username/password from a file. */ FILE *fp; char password_buf[USER_PASS_LEN] = { '\0' }; fp = platform_fopen(auth_file, "r"); if (!fp) { msg(M_ERR, "Error opening '%s' auth file: %s", prefix, auth_file); } if ((flags & GET_USER_PASS_PASSWORD_ONLY) == 0) { /* Read username first */ if (fgets(up->username, USER_PASS_LEN, fp) == NULL) { msg(M_FATAL, "Error reading username from %s authfile: %s", prefix, auth_file); } } chomp(up->username); if (fgets(password_buf, USER_PASS_LEN, fp) != NULL) { chomp(password_buf); } if (flags & GET_USER_PASS_PASSWORD_ONLY && !password_buf[0]) { msg(M_FATAL, "Error reading password from %s authfile: %s", prefix, auth_file); } if (password_buf[0]) { strncpy(up->password, password_buf, USER_PASS_LEN); } else { password_from_stdin = 1; } fclose(fp); if (!(flags & GET_USER_PASS_PASSWORD_ONLY) && strlen(up->username) == 0) { msg(M_FATAL, "ERROR: username from %s authfile '%s' is empty", prefix, auth_file); } } else { username_from_stdin = true; password_from_stdin = true; } /* * Get username/password from standard input? */ if (username_from_stdin || password_from_stdin || response_from_stdin) { #ifdef ENABLE_CLIENT_CR if (auth_challenge && (flags & GET_USER_PASS_DYNAMIC_CHALLENGE) && response_from_stdin) { struct auth_challenge_info *ac = get_auth_challenge(auth_challenge, &gc); if (ac) { char *response = (char *) gc_malloc(USER_PASS_LEN, false, &gc); struct buffer packed_resp, challenge; challenge = alloc_buf_gc(14+strlen(ac->challenge_text), &gc); buf_printf(&challenge, "CHALLENGE: %s", ac->challenge_text); buf_set_write(&packed_resp, (uint8_t *)up->password, USER_PASS_LEN); if (!query_user_SINGLE(BSTR(&challenge), BLEN(&challenge), response, USER_PASS_LEN, BOOL_CAST(ac->flags&CR_ECHO))) { msg(M_FATAL, "ERROR: could not read challenge response from stdin"); } strncpynt(up->username, ac->user, USER_PASS_LEN); buf_printf(&packed_resp, "CRV1::%s::%s", ac->state_id, response); } else { msg(M_FATAL, "ERROR: received malformed challenge request from server"); } } else #endif /* ifdef ENABLE_CLIENT_CR */ { struct buffer user_prompt = alloc_buf_gc(128, &gc); struct buffer pass_prompt = alloc_buf_gc(128, &gc); query_user_clear(); buf_printf(&user_prompt, "Enter %s Username:"******"Enter %s Password:"******"ERROR: Failed retrieving username or password"); } if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) { if (strlen(up->username) == 0) { msg(M_FATAL, "ERROR: %s username is empty", prefix); } } #ifdef ENABLE_CLIENT_CR if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE) && response_from_stdin) { char *response = (char *) gc_malloc(USER_PASS_LEN, false, &gc); struct buffer packed_resp, challenge; char *pw64 = NULL, *resp64 = NULL; challenge = alloc_buf_gc(14+strlen(auth_challenge), &gc); buf_printf(&challenge, "CHALLENGE: %s", auth_challenge); if (!query_user_SINGLE(BSTR(&challenge), BLEN(&challenge), response, USER_PASS_LEN, BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO))) { msg(M_FATAL, "ERROR: could not retrieve static challenge response"); } if (openvpn_base64_encode(up->password, strlen(up->password), &pw64) == -1 || openvpn_base64_encode(response, strlen(response), &resp64) == -1) { msg(M_FATAL, "ERROR: could not base64-encode password/static_response"); } buf_set_write(&packed_resp, (uint8_t *)up->password, USER_PASS_LEN); buf_printf(&packed_resp, "SCRV1:%s:%s", pw64, resp64); string_clear(pw64); free(pw64); string_clear(resp64); free(resp64); } #endif /* ifdef ENABLE_CLIENT_CR */ } } string_mod(up->username, CC_PRINT, CC_CRLF, 0); string_mod(up->password, CC_PRINT, CC_CRLF, 0); up->defined = true; } #if 0 msg(M_INFO, "GET_USER_PASS %s u='%s' p='%s'", prefix, up->username, up->password); #endif gc_free(&gc); return true; }
bool send_push_reply (struct context *c) { struct gc_arena gc = gc_new (); struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); struct push_entry *e = c->options.push_list.head; bool multi_push = false; static char cmd[] = "PUSH_REPLY"; const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */ const int safe_cap = BCAP (&buf) - extra; bool push_sent = false; char foxconn_log[512]; msg( M_INFO, "send_push_reply(): safe_cap=%d", safe_cap ); sprintf(foxconn_log, "[OpenVPN, connection successfully]IP address:%s",inet_ntoa (c->c2.to_link_addr->dest.addr.in4.sin_addr)); ambitWriteLog(foxconn_log, sizeof(foxconn_log));//allenwen add openvpn log buf_printf (&buf, "%s", cmd); if ( c->c2.push_ifconfig_ipv6_defined ) { /* IPv6 is put into buffer first, could be lengthy */ buf_printf( &buf, ",ifconfig-ipv6 %s/%d %s", print_in6_addr( c->c2.push_ifconfig_ipv6_local, 0, &gc), c->c2.push_ifconfig_ipv6_netbits, print_in6_addr( c->c2.push_ifconfig_ipv6_remote, 0, &gc) ); if (BLEN (&buf) >= safe_cap) { msg (M_WARN, "--push ifconfig-ipv6 option is too long"); goto fail; } } while (e) { if (e->enable) { const int l = strlen (e->option); if (BLEN (&buf) + l >= safe_cap) { buf_printf (&buf, ",push-continuation 2"); { const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); if (!status) goto fail; push_sent = true; multi_push = true; buf_reset_len (&buf); buf_printf (&buf, "%s", cmd); } } if (BLEN (&buf) + l >= safe_cap) { msg (M_WARN, "--push option is too long"); goto fail; } buf_printf (&buf, ",%s", e->option); } e = e->next; } if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local && c->c2.push_ifconfig_remote_netmask) { in_addr_t ifconfig_local = c->c2.push_ifconfig_local; #ifdef ENABLE_CLIENT_NAT if (c->c2.push_ifconfig_local_alias) ifconfig_local = c->c2.push_ifconfig_local_alias; #endif buf_printf (&buf, ",ifconfig %s %s", print_in_addr_t (ifconfig_local, 0, &gc), print_in_addr_t (c->c2.push_ifconfig_remote_netmask, 0, &gc)); } if (multi_push) buf_printf (&buf, ",push-continuation 1"); if (BLEN (&buf) > sizeof(cmd)-1) { const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); if (!status) goto fail; push_sent = true; } /* If nothing have been pushed, send an empty push, * as the client is expecting a response */ if (!push_sent) { bool status = false; buf_reset_len (&buf); buf_printf (&buf, "%s", cmd); status = send_control_channel_string (c, BSTR(&buf), D_PUSH); if (!status) goto fail; } gc_free (&gc); return true; fail: gc_free (&gc); return false; }
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 ENABLE_INLINE_FILES 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 */ #endif { in = alloc_buf_gc (2048, &gc); fd = openvpn_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 ENABLE_INLINE_FILES if (!(flags & RKF_INLINE)) #endif 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); }
void process_outgoing_tun (struct context *c) { struct gc_arena gc = gc_new (); /* * Set up for write() call to TUN/TAP * device. */ if (c->c2.to_tun.len <= 0) return; perf_push (PERF_PROC_OUT_TUN); /* * The --mssfix option requires * us to examine the IPv4 header. */ process_ipv4_header (c, PIPV4_MSSFIX|PIPV4_EXTRACT_DHCP_ROUTER|PIPV4_OUTGOING, &c->c2.to_tun); if (c->c2.to_tun.len <= MAX_RW_SIZE_TUN (&c->c2.frame)) { /* * Write to TUN/TAP device. */ int size; #ifdef LOG_RW if (c->c2.log_rw) fprintf (stderr, "w"); #endif dmsg (D_TUN_RW, "TUN WRITE [%d]", BLEN (&c->c2.to_tun)); #ifdef PACKET_TRUNCATION_CHECK ipv4_packet_size_verify (BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun), TUNNEL_TYPE (c->c1.tuntap), "WRITE_TUN", &c->c2.n_trunc_tun_write); #endif #ifdef TUN_PASS_BUFFER size = write_tun_buffered (c->c1.tuntap, &c->c2.to_tun); #else size = write_tun (c->c1.tuntap, BPTR (&c->c2.to_tun), BLEN (&c->c2.to_tun)); #endif if (size > 0) c->c2.tun_write_bytes += size; check_status (size, "write to TUN/TAP", NULL, c->c1.tuntap); /* check written packet size */ if (size > 0) { /* Did we write a different size packet than we intended? */ if (size != BLEN (&c->c2.to_tun)) msg (D_LINK_ERRORS, "TUN/TAP packet was destructively fragmented on write to %s (tried=%d,actual=%d)", c->c1.tuntap->actual_name, BLEN (&c->c2.to_tun), size); /* indicate activity regarding --inactive parameter */ register_activity (c, size); } } else { /* * This should never happen, probably indicates some kind * of MTU mismatch. */ msg (D_LINK_ERRORS, "tun packet too large on write (tried=%d,max=%d)", c->c2.to_tun.len, MAX_RW_SIZE_TUN (&c->c2.frame)); } buf_reset (&c->c2.to_tun); perf_pop (); gc_free (&gc); }
static void plugin_init_item(struct plugin *p, const struct plugin_option *o) { struct gc_arena gc = gc_new(); bool rel = false; p->so_pathname = o->so_pathname; p->plugin_type_mask = plugin_supported_types(); #ifndef _WIN32 p->handle = NULL; /* If the plug-in filename is not an absolute path, * or beginning with '.', it should use the PLUGIN_LIBDIR * as the base directory for loading the plug-in. * * This means the following scenarios are loaded from these places: * --plugin fancyplug.so -> $PLUGIN_LIBDIR/fancyplug.so * --plugin my/fancyplug.so -> $PLUGIN_LIBDIR/my/fancyplug.so * --plugin ./fancyplug.so -> $CWD/fancyplug.so * --plugin /usr/lib/my/fancyplug.so -> /usr/lib/my/fancyplug.so * * Please note that $CWD means the directory OpenVPN is either started from * or the directory OpenVPN have changed into using --cd before --plugin * was parsed. * */ if (!platform_absolute_pathname(p->so_pathname) && p->so_pathname[0] != '.') { char full[PATH_MAX]; openvpn_snprintf(full, sizeof(full), "%s/%s", PLUGIN_LIBDIR, p->so_pathname); p->handle = dlopen(full, RTLD_NOW); } else { rel = !platform_absolute_pathname(p->so_pathname); p->handle = dlopen(p->so_pathname, RTLD_NOW); } if (!p->handle) { msg(M_ERR, "PLUGIN_INIT: could not load plugin shared object %s: %s", p->so_pathname, dlerror()); } #define PLUGIN_SYM(var, name, flags) libdl_resolve_symbol(p->handle, (void *)&p->var, name, p->so_pathname, flags) #else /* ifndef _WIN32 */ rel = !platform_absolute_pathname(p->so_pathname); p->module = LoadLibraryW(wide_string(p->so_pathname, &gc)); if (!p->module) { msg(M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname); } #define PLUGIN_SYM(var, name, flags) dll_resolve_symbol(p->module, (void *)&p->var, name, p->so_pathname, flags) #endif /* ifndef _WIN32 */ PLUGIN_SYM(open1, "openvpn_plugin_open_v1", 0); PLUGIN_SYM(open2, "openvpn_plugin_open_v2", 0); PLUGIN_SYM(open3, "openvpn_plugin_open_v3", 0); PLUGIN_SYM(func1, "openvpn_plugin_func_v1", 0); PLUGIN_SYM(func2, "openvpn_plugin_func_v2", 0); PLUGIN_SYM(func3, "openvpn_plugin_func_v3", 0); PLUGIN_SYM(close, "openvpn_plugin_close_v1", PLUGIN_SYMBOL_REQUIRED); PLUGIN_SYM(abort, "openvpn_plugin_abort_v1", 0); PLUGIN_SYM(client_constructor, "openvpn_plugin_client_constructor_v1", 0); PLUGIN_SYM(client_destructor, "openvpn_plugin_client_destructor_v1", 0); PLUGIN_SYM(min_version_required, "openvpn_plugin_min_version_required_v1", 0); PLUGIN_SYM(initialization_point, "openvpn_plugin_select_initialization_point_v1", 0); if (!p->open1 && !p->open2 && !p->open3) { msg(M_FATAL, "PLUGIN: symbol openvpn_plugin_open_vX is undefined in plugin: %s", p->so_pathname); } if (!p->func1 && !p->func2 && !p->func3) { msg(M_FATAL, "PLUGIN: symbol openvpn_plugin_func_vX is undefined in plugin: %s", p->so_pathname); } /* * Verify that we are sufficiently up-to-date to handle the plugin */ if (p->min_version_required) { const int plugin_needs_version = (*p->min_version_required)(); if (plugin_needs_version > OPENVPN_PLUGIN_VERSION) { msg(M_FATAL, "PLUGIN_INIT: plugin needs interface version %d, but this version of OpenVPN only supports version %d: %s", plugin_needs_version, OPENVPN_PLUGIN_VERSION, p->so_pathname); } } if (p->initialization_point) { p->requested_initialization_point = (*p->initialization_point)(); } else { p->requested_initialization_point = OPENVPN_PLUGIN_INIT_PRE_DAEMON; } if (rel) { msg(M_WARN, "WARNING: plugin '%s' specified by a relative pathname -- using an absolute pathname would be more secure", p->so_pathname); } p->initialized = true; 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 = openvpn_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) */ buf_clear (&out); /* pop our garbage collection level */ gc_free (&gc); return nbits; }
/** * Drops UDP packets which OS decided to route via tun. * * On Windows and OS X when netwotk adapter is disabled or * disconnected, platform starts to use tun as external interface. * When packet is sent to tun, it comes to openvpn, encapsulated * and sent to routing table, which sends it again to tun. */ static void drop_if_recursive_routing (struct context *c, struct buffer *buf) { bool drop = false; struct openvpn_sockaddr tun_sa; int ip_hdr_offset = 0; if (c->c2.to_link_addr == NULL) /* no remote addr known */ return; tun_sa = c->c2.to_link_addr->dest; int proto_ver = get_tun_ip_ver (TUNNEL_TYPE (c->c1.tuntap), &c->c2.buf, &ip_hdr_offset); if (proto_ver == 4) { const struct openvpn_iphdr *pip; /* make sure we got whole IP header */ if (BLEN (buf) < ((int) sizeof (struct openvpn_iphdr) + ip_hdr_offset)) return; /* skip ipv4 packets for ipv6 tun */ if (tun_sa.addr.sa.sa_family != AF_INET) return; pip = (struct openvpn_iphdr *) (BPTR (buf) + ip_hdr_offset); /* drop packets with same dest addr as gateway */ if (tun_sa.addr.in4.sin_addr.s_addr == pip->daddr) drop = true; } else if (proto_ver == 6) { const struct openvpn_ipv6hdr *pip6; /* make sure we got whole IPv6 header */ if (BLEN (buf) < ((int) sizeof (struct openvpn_ipv6hdr) + ip_hdr_offset)) return; /* skip ipv6 packets for ipv4 tun */ if (tun_sa.addr.sa.sa_family != AF_INET6) return; /* drop packets with same dest addr as gateway */ pip6 = (struct openvpn_ipv6hdr *) (BPTR (buf) + ip_hdr_offset); if (IN6_ARE_ADDR_EQUAL(&tun_sa.addr.in6.sin6_addr, &pip6->daddr)) drop = true; } if (drop) { struct gc_arena gc = gc_new (); c->c2.buf.len = 0; msg(D_LOW, "Recursive routing detected, drop tun packet to %s", print_link_socket_actual(c->c2.to_link_addr, &gc)); gc_free (&gc); } }
unsigned int mroute_extract_addr_ip(struct mroute_addr *src, struct mroute_addr *dest, const struct buffer *buf) { unsigned int ret = 0; if (BLEN(buf) >= 1) { switch (OPENVPN_IPH_GET_VER(*BPTR(buf))) { case 4: if (BLEN(buf) >= (int) sizeof(struct openvpn_iphdr)) { const struct openvpn_iphdr *ip = (const struct openvpn_iphdr *) BPTR(buf); mroute_get_in_addr_t(src, ip->saddr, 0); mroute_get_in_addr_t(dest, ip->daddr, 0); /* multicast packet? */ if (mroute_is_mcast(ip->daddr)) { ret |= MROUTE_EXTRACT_MCAST; } /* IGMP message? */ if (ip->protocol == OPENVPN_IPPROTO_IGMP) { ret |= MROUTE_EXTRACT_IGMP; } ret |= MROUTE_EXTRACT_SUCCEEDED; } break; case 6: if (BLEN(buf) >= (int) sizeof(struct openvpn_ipv6hdr)) { const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *) BPTR(buf); #if 0 /* very basic debug */ struct gc_arena gc = gc_new(); msg( M_INFO, "IPv6 packet! src=%s, dst=%s", print_in6_addr( ipv6->saddr, 0, &gc ), print_in6_addr( ipv6->daddr, 0, &gc )); gc_free(&gc); #endif mroute_get_in6_addr(src, ipv6->saddr, 0); mroute_get_in6_addr(dest, ipv6->daddr, 0); if (mroute_is_mcast_ipv6(ipv6->daddr)) { ret |= MROUTE_EXTRACT_MCAST; } ret |= MROUTE_EXTRACT_SUCCEEDED; } break; default: msg(M_WARN, "IP packet with unknown IP version=%d seen", OPENVPN_IPH_GET_VER(*BPTR(buf))); } } return ret; }
void redirect_stdout_stderr (const char *file, bool append) { #if defined(WIN32) if (!std_redir) { struct gc_arena gc = gc_new (); HANDLE log_handle; int log_fd; SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = NULL; log_handle = CreateFileW (wide_string (file, &gc), GENERIC_WRITE, FILE_SHARE_READ, &saAttr, append ? OPEN_ALWAYS : CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); gc_free (&gc); if (log_handle == INVALID_HANDLE_VALUE) { msg (M_WARN|M_ERRNO, "Warning: cannot open --log file: %s", file); return; } /* append to logfile? */ if (append) { if (SetFilePointer (log_handle, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER) msg (M_ERR, "Error: cannot seek to end of --log file: %s", file); } /* save original stderr for password prompts */ orig_stderr = GetStdHandle (STD_ERROR_HANDLE); #if 0 /* seems not be necessary with stdout/stderr redirection below*/ /* set up for redirection */ if (!SetStdHandle (STD_OUTPUT_HANDLE, log_handle) || !SetStdHandle (STD_ERROR_HANDLE, log_handle)) msg (M_ERR, "Error: cannot redirect stdout/stderr to --log file: %s", file); #endif /* direct stdout/stderr to point to log_handle */ log_fd = _open_osfhandle ((intptr_t)log_handle, _O_TEXT); if (log_fd == -1) msg (M_ERR, "Error: --log redirect failed due to _open_osfhandle failure"); /* open log_handle as FILE stream */ ASSERT (msgfp == NULL); msgfp = _fdopen (log_fd, "wt"); if (msgfp == NULL) msg (M_ERR, "Error: --log redirect failed due to _fdopen"); /* redirect C-library stdout/stderr to log file */ if (_dup2 (log_fd, 1) == -1 || _dup2 (log_fd, 2) == -1) msg (M_WARN, "Error: --log redirect of stdout/stderr failed"); std_redir = true; } #elif defined(HAVE_DUP2) if (!std_redir) { int out = open (file, O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC), S_IRUSR | S_IWUSR); if (out < 0) { msg (M_WARN|M_ERRNO, "Warning: Error redirecting stdout/stderr to --log file: %s", file); return; } if (dup2 (out, 1) == -1) msg (M_ERR, "--log file redirection error on stdout"); if (dup2 (out, 2) == -1) msg (M_ERR, "--log file redirection error on stderr"); if (out > 2) close (out); std_redir = true; } #else msg (M_WARN, "WARNING: The --log option is not supported on this OS because it lacks the dup2 function"); #endif }
struct multi_instance * multi_get_create_instance_udp (struct multi_context *m) { struct gc_arena gc = gc_new (); struct mroute_addr real; struct multi_instance *mi = NULL; struct hash *hash = m->hash; if (mroute_extract_sockaddr_in (&real, &m->top.c2.from, true)) { struct hash_element *he; const uint32_t hv = hash_value (hash, &real); struct hash_bucket *bucket = hash_bucket (hash, hv); hash_bucket_lock (bucket); he = hash_lookup_fast (hash, bucket, &real, hv); if (he) { mi = (struct multi_instance *) he->value; } else { if (!m->top.c2.tls_auth_standalone || tls_pre_decrypt_lite (m->top.c2.tls_auth_standalone, &m->top.c2.from, &m->top.c2.buf)) { if (frequency_limit_event_allowed (m->new_connection_limiter)) { mi = multi_create_instance (m, &real); if (mi) { hash_add_fast (hash, bucket, &mi->real, hv, mi); mi->did_real_hash = true; } } else { msg (D_MULTI_ERRORS, "MULTI: Connection from %s would exceed new connection frequency limit as controlled by --connect-freq", mroute_addr_print (&real, &gc)); } } } hash_bucket_unlock (bucket); #ifdef ENABLE_DEBUG if (check_debug_level (D_MULTI_DEBUG)) { const char *status; if (he && mi) status = "[succeeded]"; else if (!he && mi) status = "[created]"; else status = "[failed]"; dmsg (D_MULTI_DEBUG, "GET INST BY REAL: %s %s", mroute_addr_print (&real, &gc), status); } #endif } gc_free (&gc); ASSERT (!(mi && mi->halt)); return mi; }
void process_outgoing_link (struct context *c) { struct gc_arena gc = gc_new (); int error_code = 0; perf_push (PERF_PROC_OUT_LINK); if (c->c2.to_link.len > 0 && c->c2.to_link.len <= EXPANDED_SIZE (&c->c2.frame)) { /* * Setup for call to send/sendto which will send * packet to remote over the TCP/UDP port. */ int size = 0; ASSERT (link_socket_actual_defined (c->c2.to_link_addr)); #ifdef ENABLE_DEBUG /* In gremlin-test mode, we may choose to drop this packet */ if (!c->options.gremlin || ask_gremlin (c->options.gremlin)) #endif { /* * Let the traffic shaper know how many bytes * we wrote. */ #ifdef ENABLE_FEATURE_SHAPER if (c->options.shaper) shaper_wrote_bytes (&c->c2.shaper, BLEN (&c->c2.to_link) + datagram_overhead (c->options.ce.proto)); #endif /* * Let the pinger know that we sent a packet. */ if (c->options.ping_send_timeout) event_timeout_reset (&c->c2.ping_send_interval); #if PASSTOS_CAPABILITY /* Set TOS */ link_socket_set_tos (c->c2.link_socket); #endif /* Log packet send */ #ifdef LOG_RW if (c->c2.log_rw) fprintf (stderr, "W"); #endif msg (D_LINK_RW, "%s WRITE [%d] to %s: %s", proto2ascii (c->c2.link_socket->info.proto, c->c2.link_socket->info.af, true), BLEN (&c->c2.to_link), print_link_socket_actual (c->c2.to_link_addr, &gc), PROTO_DUMP (&c->c2.to_link, &gc)); /* Packet send complexified by possible Socks5 usage */ { struct link_socket_actual *to_addr = c->c2.to_link_addr; int size_delta = 0; /* If Socks5 over UDP, prepend header */ socks_preprocess_outgoing_link (c, &to_addr, &size_delta); /* Send packet */ size = link_socket_write (c->c2.link_socket, &c->c2.to_link, to_addr); /* Undo effect of prepend */ link_socket_write_post_size_adjust (&size, size_delta, &c->c2.to_link); } if (size > 0) { c->c2.max_send_size_local = max_int (size, c->c2.max_send_size_local); c->c2.link_write_bytes += size; link_write_bytes_global += size; #ifdef ENABLE_MEMSTATS if (mmap_stats) mmap_stats->link_write_bytes = link_write_bytes_global; #endif #ifdef ENABLE_MANAGEMENT if (management) { management_bytes_out (management, size); #ifdef MANAGEMENT_DEF_AUTH management_bytes_server (management, &c->c2.link_read_bytes, &c->c2.link_write_bytes, &c->c2.mda_context); #endif } #endif } } /* Check return status */ error_code = openvpn_errno(); check_status (size, "write", c->c2.link_socket, NULL); if (size > 0) { /* Did we write a different size packet than we intended? */ if (size != BLEN (&c->c2.to_link)) msg (D_LINK_ERRORS, "TCP/UDP packet was truncated/expanded on write to %s (tried=%d,actual=%d)", print_link_socket_actual (c->c2.to_link_addr, &gc), BLEN (&c->c2.to_link), size); } /* if not a ping/control message, indicate activity regarding --inactive parameter */ if (c->c2.buf.len > 0 ) register_activity (c, size); #ifdef ENABLE_CRYPTO /* for unreachable network and "connecting" state switch to the next host */ if (size < 0 && ENETUNREACH == error_code && c->c2.tls_multi && !tls_initial_packet_received (c->c2.tls_multi) && c->options.mode == MODE_POINT_TO_POINT) { msg (M_INFO, "Network unreachable, restarting"); register_signal (c, SIGUSR1, "network-unreachable"); } #endif } else { if (c->c2.to_link.len > 0) msg (D_LINK_ERRORS, "TCP/UDP packet too large on write to %s (tried=%d,max=%d)", print_link_socket_actual (c->c2.to_link_addr, &gc), c->c2.to_link.len, EXPANDED_SIZE (&c->c2.frame)); } buf_reset (&c->c2.to_link); perf_pop (); gc_free (&gc); }
void process_outgoing_link (struct context *c) { struct gc_arena gc = gc_new (); perf_push (PERF_PROC_OUT_LINK); if (c->c2.to_link.len > 0 && c->c2.to_link.len <= EXPANDED_SIZE (&c->c2.frame)) { /* * Setup for call to send/sendto which will send * packet to remote over the TCP/UDP port. */ int size = 0; ASSERT (link_socket_actual_defined (c->c2.to_link_addr)); #ifdef ENABLE_DEBUG /* In gremlin-test mode, we may choose to drop this packet */ if (!c->options.gremlin || ask_gremlin (c->options.gremlin)) #endif { /* * Let the traffic shaper know how many bytes * we wrote. */ #ifdef HAVE_GETTIMEOFDAY if (c->options.shaper) shaper_wrote_bytes (&c->c2.shaper, BLEN (&c->c2.to_link) + datagram_overhead (c->options.ce.proto)); #endif /* * Let the pinger know that we sent a packet. */ if (c->options.ping_send_timeout) event_timeout_reset (&c->c2.ping_send_interval); #if PASSTOS_CAPABILITY /* Set TOS */ link_socket_set_tos (c->c2.link_socket); #endif /* Log packet send */ #ifdef LOG_RW if (c->c2.log_rw) fprintf (stderr, "W"); #endif msg (D_LINK_RW, "%s WRITE [%d] to %s: %s", proto2ascii (c->c2.link_socket->info.proto, true), BLEN (&c->c2.to_link), print_link_socket_actual (c->c2.to_link_addr, &gc), PROTO_DUMP (&c->c2.to_link, &gc)); /* Packet send complexified by possible Socks5 usage */ { struct link_socket_actual *to_addr = c->c2.to_link_addr; #ifdef ENABLE_SOCKS int size_delta = 0; #endif #ifdef ENABLE_SOCKS /* If Socks5 over UDP, prepend header */ socks_preprocess_outgoing_link (c, &to_addr, &size_delta); #endif /* Send packet */ size = link_socket_write (c->c2.link_socket, &c->c2.to_link, to_addr); #ifdef ENABLE_SOCKS /* Undo effect of prepend */ link_socket_write_post_size_adjust (&size, size_delta, &c->c2.to_link); #endif } if (size > 0) { c->c2.max_send_size_local = max_int (size, c->c2.max_send_size_local); c->c2.link_write_bytes += size; link_write_bytes_global += size; #ifdef ENABLE_MANAGEMENT if (management) { management_bytes_out (management, size); #ifdef MANAGEMENT_DEF_AUTH management_bytes_server (management, &c->c2.link_read_bytes, &c->c2.link_write_bytes, &c->c2.mda_context); #endif } #endif } } /* Check return status */ check_status (size, "write", c->c2.link_socket, NULL); if (size > 0) { /* Did we write a different size packet than we intended? */ if (size != BLEN (&c->c2.to_link)) msg (D_LINK_ERRORS, "TCP/UDP packet was truncated/expanded on write to %s (tried=%d,actual=%d)", print_link_socket_actual (c->c2.to_link_addr, &gc), BLEN (&c->c2.to_link), size); } /* indicate activity regarding --inactive parameter */ register_activity (c, size); } else { if (c->c2.to_link.len > 0) msg (D_LINK_ERRORS, "TCP/UDP packet too large on write to %s (tried=%d,max=%d)", print_link_socket_actual (c->c2.to_link_addr, &gc), c->c2.to_link.len, EXPANDED_SIZE (&c->c2.frame)); } buf_reset (&c->c2.to_link); perf_pop (); gc_free (&gc); }
static void plugin_open_item (struct plugin *p, const struct plugin_option *o, struct openvpn_plugin_string_list **retlist, const char **envp, const int init_point) { ASSERT (p->initialized); /* clear return list */ if (retlist) *retlist = NULL; if (!p->plugin_handle && init_point == p->requested_initialization_point) { struct gc_arena gc = gc_new (); dmsg (D_PLUGIN_DEBUG, "PLUGIN_INIT: PRE"); plugin_show_args_env (D_PLUGIN_DEBUG, o->argv, envp); /* * Call the plugin initialization */ if (p->open3) { struct openvpn_plugin_args_open_in args = { p->plugin_type_mask, (const char ** const) o->argv, (const char ** const) envp }; struct openvpn_plugin_args_open_return retargs; CLEAR(retargs); if ((*p->open3)(OPENVPN_PLUGINv3_STRUCTVER, &args, &retargs) == OPENVPN_PLUGIN_FUNC_SUCCESS) { p->plugin_type_mask = retargs.type_mask; p->plugin_handle = retargs.handle; retlist = retargs.return_list; } else { p->plugin_handle = NULL; } } else if (p->open2) p->plugin_handle = (*p->open2)(&p->plugin_type_mask, o->argv, envp, retlist); else if (p->open1) p->plugin_handle = (*p->open1)(&p->plugin_type_mask, o->argv, envp); else ASSERT (0); msg (D_PLUGIN, "PLUGIN_INIT: POST %s '%s' intercepted=%s %s", p->so_pathname, print_argv (o->argv, &gc, PA_BRACKET), plugin_mask_string (p->plugin_type_mask, &gc), (retlist && *retlist) ? "[RETLIST]" : ""); if ((p->plugin_type_mask | plugin_supported_types()) != plugin_supported_types()) msg (M_FATAL, "PLUGIN_INIT: plugin %s expressed interest in unsupported plugin types: [want=0x%08x, have=0x%08x]", p->so_pathname, p->plugin_type_mask, plugin_supported_types()); if (p->plugin_handle == NULL) msg (M_FATAL, "PLUGIN_INIT: plugin initialization function failed: %s", p->so_pathname); gc_free (&gc); } }
void process_incoming_link (struct context *c) { struct gc_arena gc = gc_new (); bool decrypt_status; struct link_socket_info *lsi = get_link_socket_info (c); const uint8_t *orig_buf = c->c2.buf.data; perf_push (PERF_PROC_IN_LINK); if (c->c2.buf.len > 0) { c->c2.link_read_bytes += c->c2.buf.len; link_read_bytes_global += c->c2.buf.len; c->c2.original_recv_size = c->c2.buf.len; #ifdef ENABLE_MANAGEMENT if (management) { management_bytes_in (management, c->c2.buf.len); #ifdef MANAGEMENT_DEF_AUTH management_bytes_server (management, &c->c2.link_read_bytes, &c->c2.link_write_bytes, &c->c2.mda_context); #endif } #endif } else c->c2.original_recv_size = 0; #ifdef ENABLE_DEBUG /* take action to corrupt packet if we are in gremlin test mode */ if (c->options.gremlin) { if (!ask_gremlin (c->options.gremlin)) c->c2.buf.len = 0; corrupt_gremlin (&c->c2.buf, c->options.gremlin); } #endif /* log incoming packet */ #ifdef LOG_RW if (c->c2.log_rw) fprintf (stderr, "R"); #endif msg (D_LINK_RW, "%s READ [%d] from %s: %s", proto2ascii (lsi->proto, true), BLEN (&c->c2.buf), print_link_socket_actual (&c->c2.from, &gc), PROTO_DUMP (&c->c2.buf, &gc)); /* * Good, non-zero length packet received. * Commence multi-stage processing of packet, * such as authenticate, decrypt, decompress. * If any stage fails, it sets buf.len to 0 or -1, * telling downstream stages to ignore the packet. */ if (c->c2.buf.len > 0) { if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from)) link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from); #ifdef USE_CRYPTO #ifdef USE_SSL if (c->c2.tls_multi) { /* * If tls_pre_decrypt returns true, it means the incoming * packet was a good TLS control channel packet. If so, TLS code * will deal with the packet and set buf.len to 0 so downstream * stages ignore it. * * If the packet is a data channel packet, tls_pre_decrypt * will load crypto_options with the correct encryption key * and return false. */ /*tls_mutex_lock (c->c2.tls_multi);*/ if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options)) { interval_action (&c->c2.tmp_int); /* reset packet received timer if TLS packet */ if (c->options.ping_rec_timeout) event_timeout_reset (&c->c2.ping_rec_interval); } } #if P2MP_SERVER /* * Drop non-TLS packet if client-connect script/plugin has not * yet succeeded. */ if (c->c2.context_auth != CAS_SUCCEEDED) c->c2.buf.len = 0; #endif #endif /* USE_SSL */ /* authenticate and decrypt the incoming packet */ decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, &c->c2.crypto_options, &c->c2.frame); #ifdef USE_SSL if (c->c2.tls_multi) { /*tls_mutex_unlock (c->c2.tls_multi);*/ } #endif if (!decrypt_status && link_socket_connection_oriented (c->c2.link_socket)) { /* decryption errors are fatal in TCP mode */ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- decryption error in TCP mode */ c->sig->signal_text = "decryption-error"; msg (D_STREAM_ERRORS, "Fatal decryption error (process_incoming_link), restarting"); goto done; } #endif /* USE_CRYPTO */ #ifdef ENABLE_FRAGMENT if (c->c2.fragment) fragment_incoming (c->c2.fragment, &c->c2.buf, &c->c2.frame_fragment); #endif #ifdef USE_LZO /* decompress the incoming packet */ if (lzo_defined (&c->c2.lzo_compwork)) lzo_decompress (&c->c2.buf, c->c2.buffers->lzo_decompress_buf, &c->c2.lzo_compwork, &c->c2.frame); #endif #ifdef PACKET_TRUNCATION_CHECK /* if (c->c2.buf.len > 1) --c->c2.buf.len; */ ipv4_packet_size_verify (BPTR (&c->c2.buf), BLEN (&c->c2.buf), TUNNEL_TYPE (c->c1.tuntap), "POST_DECRYPT", &c->c2.n_trunc_post_decrypt); #endif /* * Set our "official" outgoing address, since * if buf.len is non-zero, we know the packet * authenticated. In TLS mode we do nothing * because TLS mode takes care of source address * authentication. * * Also, update the persisted version of our packet-id. */ if (!TLS_MODE (c)) link_socket_set_outgoing_addr (&c->c2.buf, lsi, &c->c2.from, NULL, c->c2.es); /* reset packet received timer */ if (c->options.ping_rec_timeout && c->c2.buf.len > 0) event_timeout_reset (&c->c2.ping_rec_interval); /* increment authenticated receive byte count */ if (c->c2.buf.len > 0) { c->c2.link_read_bytes_auth += c->c2.buf.len; c->c2.max_recv_size_local = max_int (c->c2.original_recv_size, c->c2.max_recv_size_local); } /* Did we just receive an openvpn ping packet? */ if (is_ping_msg (&c->c2.buf)) { dmsg (D_PING, "RECEIVED PING PACKET"); c->c2.buf.len = 0; /* drop packet */ } #ifdef ENABLE_OCC /* Did we just receive an OCC packet? */ if (is_occ_msg (&c->c2.buf)) process_received_occ_msg (c); #endif buffer_turnover (orig_buf, &c->c2.to_tun, &c->c2.buf, &c->c2.buffers->read_link_buf); /* to_tun defined + unopened tuntap can cause deadlock */ if (!tuntap_defined (c->c1.tuntap)) c->c2.to_tun.len = 0; } else { buf_reset (&c->c2.to_tun); } done: perf_pop (); gc_free (&gc); }
static int plugin_call_item (const struct plugin *p, void *per_client_context, const int type, const struct argv *av, struct openvpn_plugin_string_list **retlist, const char **envp #ifdef ENABLE_SSL , int certdepth, openvpn_x509_cert_t *current_cert #endif ) { int status = OPENVPN_PLUGIN_FUNC_SUCCESS; /* clear return list */ if (retlist) *retlist = NULL; if (p->plugin_handle && (p->plugin_type_mask & OPENVPN_PLUGIN_MASK (type))) { struct gc_arena gc = gc_new (); struct argv a = argv_insert_head (av, p->so_pathname); dmsg (D_PLUGIN_DEBUG, "PLUGIN_CALL: PRE type=%s", plugin_type_name (type)); plugin_show_args_env (D_PLUGIN_DEBUG, (const char **)a.argv, envp); /* * Call the plugin work function */ if (p->func3) { struct openvpn_plugin_args_func_in args = { type, (const char ** const) a.argv, (const char ** const) envp, p->plugin_handle, per_client_context, #ifdef ENABLE_SSL (current_cert ? certdepth : -1), current_cert #else -1, NULL #endif }; struct openvpn_plugin_args_func_return retargs; CLEAR(retargs); status = (*p->func3)(OPENVPN_PLUGINv3_STRUCTVER, &args, &retargs); retlist = retargs.return_list; } else if (p->func2) status = (*p->func2)(p->plugin_handle, type, (const char **)a.argv, envp, per_client_context, retlist); else if (p->func1) status = (*p->func1)(p->plugin_handle, type, (const char **)a.argv, envp); else ASSERT (0); msg (D_PLUGIN, "PLUGIN_CALL: POST %s/%s status=%d", p->so_pathname, plugin_type_name (type), status); if (status == OPENVPN_PLUGIN_FUNC_ERROR) msg (M_WARN, "PLUGIN_CALL: plugin function %s failed with status %d: %s", plugin_type_name (type), status, p->so_pathname); argv_reset (&a); gc_free (&gc); } return status; }
bool establish_http_proxy_passthru (struct http_proxy_info *p, socket_descriptor_t sd, /* already open to proxy */ const char *host, /* openvpn server remote */ const int port, /* openvpn server port */ struct buffer *lookahead, volatile int *signal_received) { struct gc_arena gc = gc_new (); char buf[256]; char buf2[128]; char get[80]; int status; int nparms; bool ret = false; /* get user/pass if not previously given or if --auto-proxy is being used */ if (p->auth_method == HTTP_AUTH_BASIC || p->auth_method == HTTP_AUTH_NTLM) get_user_pass_http (p, false); /* format HTTP CONNECT message */ openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", host, port, p->options.http_version); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); /* send HTTP CONNECT message to proxy */ if (!send_line_crlf (sd, buf)) goto error; /* send User-Agent string if provided */ if (p->options.user_agent) { openvpn_snprintf (buf, sizeof(buf), "User-Agent: %s", p->options.user_agent); if (!send_line_crlf (sd, buf)) goto error; } /* auth specified? */ switch (p->auth_method) { case HTTP_AUTH_NONE: break; case HTTP_AUTH_BASIC: openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Basic %s", username_password_as_base64 (p, &gc)); msg (D_PROXY, "Attempting Basic Proxy-Authorization"); dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf); openvpn_sleep (1); if (!send_line_crlf (sd, buf)) goto error; break; #if NTLM case HTTP_AUTH_NTLM: case HTTP_AUTH_NTLM2: /* keep-alive connection */ openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive"); if (!send_line_crlf (sd, buf)) goto error; openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s", ntlm_phase_1 (p, &gc)); msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 1"); dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf); openvpn_sleep (1); if (!send_line_crlf (sd, buf)) goto error; break; #endif default: ASSERT (0); } /* send empty CR, LF */ openvpn_sleep (1); if (!send_crlf (sd)) goto error; /* receive reply from proxy */ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) goto error; /* remove trailing CR, LF */ chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); /* parse return string */ nparms = sscanf (buf, "%*s %d", &status); /* check for a "407 Proxy Authentication Required" response */ if (nparms >= 1 && status == 407) { msg (D_PROXY, "Proxy requires authentication"); /* check for NTLM */ if (p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) { #if NTLM /* look for the phase 2 response */ while (true) { if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) goto error; chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); openvpn_snprintf (get, sizeof get, "%%*s NTLM %%%ds", (int) sizeof (buf2) - 1); nparms = sscanf (buf, get, buf2); buf2[127] = 0; /* we only need the beginning - ensure it's null terminated. */ /* check for "Proxy-Authenticate: NTLM TlRM..." */ if (nparms == 1) { /* parse buf2 */ msg (D_PROXY, "auth string: '%s'", buf2); break; } } /* if we are here then auth string was got */ msg (D_PROXY, "Received NTLM Proxy-Authorization phase 2 response"); /* receive and discard everything else */ while (recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received)) ; /* now send the phase 3 reply */ /* format HTTP CONNECT message */ openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", host, port, p->options.http_version); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); /* send HTTP CONNECT message to proxy */ if (!send_line_crlf (sd, buf)) goto error; /* keep-alive connection */ openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive"); if (!send_line_crlf (sd, buf)) goto error; /* send HOST etc, */ openvpn_sleep (1); openvpn_snprintf (buf, sizeof(buf), "Host: %s", host); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); if (!send_line_crlf (sd, buf)) goto error; msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 3"); { const char *np3 = ntlm_phase_3 (p, buf2, &gc); if (!np3) { msg (D_PROXY, "NTLM Proxy-Authorization phase 3 failed: received corrupted data from proxy server"); goto error; } openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s", np3); } msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); openvpn_sleep (1); if (!send_line_crlf (sd, buf)) goto error; /* ok so far... */ /* send empty CR, LF */ openvpn_sleep (1); if (!send_crlf (sd)) goto error; /* receive reply from proxy */ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) goto error; /* remove trailing CR, LF */ chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); /* parse return string */ nparms = sscanf (buf, "%*s %d", &status); #else ASSERT (0); /* No NTLM support */ #endif } else if (p->auth_method == HTTP_AUTH_NONE && p->options.auth_retry) { /* * Proxy needs authentication, but we don't have a user/pass. * Now we will change p->auth_method and return true so that * our caller knows to call us again on a newly opened socket. * JYFIXME: This code needs to check proxy error output and set * JYFIXME: p->auth_method = HTTP_AUTH_NTLM if necessary. */ p->auth_method = HTTP_AUTH_BASIC; ret = true; goto done; } else goto error; } /* check return code, success = 200 */ if (nparms < 1 || status != 200) { msg (D_LINK_ERRORS, "HTTP proxy returned bad status"); #if 0 /* DEBUGGING -- show a multi-line HTTP error response */ while (true) { if (!recv_line (sd, buf, sizeof (buf), p->options.timeout, true, NULL, signal_received)) goto error; chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); } #endif goto error; } /* receive line from proxy and discard */ if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received)) goto error; /* * Toss out any extraneous chars, but don't throw away the * start of the OpenVPN data stream (put it in lookahead). */ while (recv_line (sd, NULL, 0, 2, false, lookahead, signal_received)) ; #if 0 if (lookahead && BLEN (lookahead)) msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0)); #endif done: gc_free (&gc); return ret; error: /* on error, should we exit or restart? */ if (!*signal_received) *signal_received = (p->options.retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- HTTP proxy error */ gc_free (&gc); return ret; }
int plugin_call_ssl (const struct plugin_list *pl, const int type, const struct argv *av, struct plugin_return *pr, struct env_set *es #ifdef ENABLE_SSL , int certdepth, openvpn_x509_cert_t *current_cert #endif ) { if (pr) plugin_return_init (pr); if (plugin_defined (pl, type)) { struct gc_arena gc = gc_new (); int i; const char **envp; const int n = plugin_n (pl); bool success = false; bool error = false; bool deferred = false; setenv_del (es, "script_type"); envp = make_env_array (es, false, &gc); for (i = 0; i < n; ++i) { const int status = plugin_call_item (&pl->common->plugins[i], pl->per_client.per_client_context[i], type, av, pr ? &pr->list[i] : NULL, envp #ifdef ENABLE_SSL ,certdepth, current_cert #endif ); switch (status) { case OPENVPN_PLUGIN_FUNC_SUCCESS: success = true; break; case OPENVPN_PLUGIN_FUNC_DEFERRED: deferred = true; break; default: error = true; break; } } if (pr) pr->n = i; gc_free (&gc); if (type == OPENVPN_PLUGIN_ENABLE_PF && success) return OPENVPN_PLUGIN_FUNC_SUCCESS; else if (error) return OPENVPN_PLUGIN_FUNC_ERROR; else if (deferred) return OPENVPN_PLUGIN_FUNC_DEFERRED; } return OPENVPN_PLUGIN_FUNC_SUCCESS; }
/* * Process server, server-bridge, and client helper * directives after the parameters themselves have been * parsed and placed in struct options. */ void helper_client_server (struct options *o) { struct gc_arena gc = gc_new (); #if P2MP #if P2MP_SERVER /* * Get tun/tap/null device type */ const int dev = dev_type_enum (o->dev, o->dev_type); const int topology = o->topology; /* * * HELPER DIRECTIVE for IPv6 * * server-ipv6 2001:db8::/64 * * EXPANDS TO: * * tun-ipv6 * push "tun-ipv6" * ifconfig-ipv6 2001:db8::1 2001:db8::2 * if !nopool: * ifconfig-ipv6-pool 2001:db8::1:0/64 * */ if ( o->server_ipv6_defined ) { if ( ! o->server_defined ) { msg (M_USAGE, "--server-ipv6 must be used together with --server"); } if ( o->server_flags & SF_NOPOOL ) { msg( M_USAGE, "--server-ipv6 is incompatible with 'nopool' option" ); } if ( o->ifconfig_ipv6_pool_defined ) { msg( M_USAGE, "--server-ipv6 already defines an ifconfig-ipv6-pool, so you can't also specify --ifconfig-pool explicitly"); } /* local ifconfig is "base address + 1" and "+2" */ o->ifconfig_ipv6_local = print_in6_addr( add_in6_addr( o->server_network_ipv6, 1), 0, &o->gc ); o->ifconfig_ipv6_remote = print_in6_addr( add_in6_addr( o->server_network_ipv6, 2), 0, &o->gc ); o->ifconfig_ipv6_netbits = o->server_netbits_ipv6; /* pool starts at "base address + 0x1000" - leave enough room */ ASSERT( o->server_netbits_ipv6 <= 112 ); /* want 16 bits */ o->ifconfig_ipv6_pool_defined = true; o->ifconfig_ipv6_pool_base = add_in6_addr( o->server_network_ipv6, 0x1000 ); o->ifconfig_ipv6_pool_netbits = o->server_netbits_ipv6; o->tun_ipv6 = true; push_option( o, "tun-ipv6", M_USAGE ); } /* * * HELPER DIRECTIVE: * * server 10.8.0.0 255.255.255.0 * * EXPANDS TO: * * mode server * tls-server * push "topology [topology]" * * if tun AND (topology == net30 OR topology == p2p): * ifconfig 10.8.0.1 10.8.0.2 * if !nopool: * ifconfig-pool 10.8.0.4 10.8.0.251 * route 10.8.0.0 255.255.255.0 * if client-to-client: * push "route 10.8.0.0 255.255.255.0" * else if topology == net30: * push "route 10.8.0.1" * * if tap OR (tun AND topology == subnet): * ifconfig 10.8.0.1 255.255.255.0 * if !nopool: * ifconfig-pool 10.8.0.2 10.8.0.254 255.255.255.0 * push "route-gateway 10.8.0.1" */ if (o->server_defined) { int netbits = -2; bool status = false; if (o->client) msg (M_USAGE, "--server and --client cannot be used together"); if (o->server_bridge_defined || o->server_bridge_proxy_dhcp) msg (M_USAGE, "--server and --server-bridge cannot be used together"); if (o->shared_secret_file) msg (M_USAGE, "--server and --secret cannot be used together (you must use SSL/TLS keys)"); if (!(o->server_flags & SF_NOPOOL) && o->ifconfig_pool_defined) msg (M_USAGE, "--server already defines an ifconfig-pool, so you can't also specify --ifconfig-pool explicitly"); if (!(dev == DEV_TYPE_TAP || dev == DEV_TYPE_TUN)) msg (M_USAGE, "--server directive only makes sense with --dev tun or --dev tap"); status = netmask_to_netbits (o->server_network, o->server_netmask, &netbits); if (!status) msg (M_USAGE, "--server directive network/netmask combination is invalid"); if (netbits < 0) msg (M_USAGE, "--server directive netmask is invalid"); if (netbits < IFCONFIG_POOL_MIN_NETBITS) msg (M_USAGE, "--server directive netmask allows for too many host addresses (subnet must be %s or higher)", print_netmask (IFCONFIG_POOL_MIN_NETBITS, &gc)); if (dev == DEV_TYPE_TUN) { int pool_end_reserve = 4; if (netbits > 29) msg (M_USAGE, "--server directive when used with --dev tun must define a subnet of %s or lower", print_netmask (29, &gc)); if (netbits == 29) pool_end_reserve = 0; o->mode = MODE_SERVER; o->tls_server = true; if (topology == TOP_NET30 || topology == TOP_P2P) { o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc); o->ifconfig_remote_netmask = print_in_addr_t (o->server_network + 2, 0, &o->gc); if (!(o->server_flags & SF_NOPOOL)) { o->ifconfig_pool_defined = true; o->ifconfig_pool_start = o->server_network + 4; o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - pool_end_reserve; ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end); } helper_add_route (o->server_network, o->server_netmask, o); if (o->enable_c2c) push_option (o, print_opt_route (o->server_network, o->server_netmask, &o->gc), M_USAGE); else if (topology == TOP_NET30) push_option (o, print_opt_route (o->server_network + 1, 0, &o->gc), M_USAGE); } else if (topology == TOP_SUBNET) { o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc); o->ifconfig_remote_netmask = print_in_addr_t (o->server_netmask, 0, &o->gc); if (!(o->server_flags & SF_NOPOOL)) { o->ifconfig_pool_defined = true; o->ifconfig_pool_start = o->server_network + 2; o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 2; ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end); } o->ifconfig_pool_netmask = o->server_netmask; push_option (o, print_opt_route_gateway (o->server_network + 1, &o->gc), M_USAGE); } else ASSERT (0); push_option (o, print_opt_topology (topology, &o->gc), M_USAGE); } else if (dev == DEV_TYPE_TAP) { if (netbits > 30) msg (M_USAGE, "--server directive when used with --dev tap must define a subnet of %s or lower", print_netmask (30, &gc)); o->mode = MODE_SERVER; o->tls_server = true; o->ifconfig_local = print_in_addr_t (o->server_network + 1, 0, &o->gc); o->ifconfig_remote_netmask = print_in_addr_t (o->server_netmask, 0, &o->gc); if (!(o->server_flags & SF_NOPOOL)) { o->ifconfig_pool_defined = true; o->ifconfig_pool_start = o->server_network + 2; o->ifconfig_pool_end = (o->server_network | ~o->server_netmask) - 1; ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end); } o->ifconfig_pool_netmask = o->server_netmask; push_option (o, print_opt_route_gateway (o->server_network + 1, &o->gc), M_USAGE); } else { ASSERT (0); } /* set push-ifconfig-constraint directive */ if ((dev == DEV_TYPE_TAP || topology == TOP_SUBNET)) { o->push_ifconfig_constraint_defined = true; o->push_ifconfig_constraint_network = o->server_network; o->push_ifconfig_constraint_netmask = o->server_netmask; } } /* * HELPER DIRECTIVE: * * server-bridge 10.8.0.4 255.255.255.0 10.8.0.128 10.8.0.254 * * EXPANDS TO: * * mode server * tls-server * * ifconfig-pool 10.8.0.128 10.8.0.254 255.255.255.0 * push "route-gateway 10.8.0.4" * * OR * * server-bridge * * EXPANDS TO: * * mode server * tls-server * * if !nogw: * push "route-gateway dhcp" */ else if (o->server_bridge_defined | o->server_bridge_proxy_dhcp) { if (o->client) msg (M_USAGE, "--server-bridge and --client cannot be used together"); if (!(o->server_flags & SF_NOPOOL) && o->ifconfig_pool_defined) msg (M_USAGE, "--server-bridge already defines an ifconfig-pool, so you can't also specify --ifconfig-pool explicitly"); if (o->shared_secret_file) msg (M_USAGE, "--server-bridge and --secret cannot be used together (you must use SSL/TLS keys)"); if (dev != DEV_TYPE_TAP) msg (M_USAGE, "--server-bridge directive only makes sense with --dev tap"); if (o->server_bridge_defined) { verify_common_subnet ("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_start, o->server_bridge_netmask); verify_common_subnet ("--server-bridge", o->server_bridge_pool_start, o->server_bridge_pool_end, o->server_bridge_netmask); verify_common_subnet ("--server-bridge", o->server_bridge_ip, o->server_bridge_pool_end, o->server_bridge_netmask); } o->mode = MODE_SERVER; o->tls_server = true; if (o->server_bridge_defined) { o->ifconfig_pool_defined = true; o->ifconfig_pool_start = o->server_bridge_pool_start; o->ifconfig_pool_end = o->server_bridge_pool_end; ifconfig_pool_verify_range (M_USAGE, o->ifconfig_pool_start, o->ifconfig_pool_end); o->ifconfig_pool_netmask = o->server_bridge_netmask; push_option (o, print_opt_route_gateway (o->server_bridge_ip, &o->gc), M_USAGE); } else if (o->server_bridge_proxy_dhcp && !(o->server_flags & SF_NO_PUSH_ROUTE_GATEWAY)) { push_option (o, print_opt_route_gateway_dhcp (&o->gc), M_USAGE); } } else #endif /* P2MP_SERVER */ /* * HELPER DIRECTIVE: * * client * * EXPANDS TO: * * pull * tls-client */ if (o->client) { if (o->key_method != 2) msg (M_USAGE, "--client requires --key-method 2"); o->pull = true; o->tls_client = true; } #endif /* P2MP */ gc_free (&gc); }
bool establish_http_proxy_passthru (struct http_proxy_info *p, socket_descriptor_t sd, /* already open to proxy */ const char *host, /* openvpn server remote */ const int port, /* openvpn server port */ struct buffer *lookahead, volatile int *signal_received) { struct gc_arena gc = gc_new (); char buf[512]; char buf2[128]; char get[80]; int status; int nparms; bool ret = false; bool processed = false; /* get user/pass if not previously given */ if (p->auth_method == HTTP_AUTH_BASIC || p->auth_method == HTTP_AUTH_DIGEST || p->auth_method == HTTP_AUTH_NTLM) get_user_pass_http (p, false); /* are we being called again after getting the digest server nonce in the previous transaction? */ if (p->auth_method == HTTP_AUTH_DIGEST && p->proxy_authenticate) { nparms = 1; status = 407; } else { /* format HTTP CONNECT message */ openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", host, port, p->options.http_version); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); /* send HTTP CONNECT message to proxy */ if (!send_line_crlf (sd, buf)) goto error; openvpn_snprintf(buf, sizeof(buf), "Host: %s", host); if (!send_line_crlf(sd, buf)) goto error; /* send User-Agent string if provided */ if (p->options.user_agent) { openvpn_snprintf (buf, sizeof(buf), "User-Agent: %s", p->options.user_agent); if (!send_line_crlf (sd, buf)) goto error; } /* auth specified? */ switch (p->auth_method) { case HTTP_AUTH_NONE: break; case HTTP_AUTH_BASIC: openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Basic %s", username_password_as_base64 (p, &gc)); msg (D_PROXY, "Attempting Basic Proxy-Authorization"); dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf); if (!send_line_crlf (sd, buf)) goto error; break; #if NTLM case HTTP_AUTH_NTLM: case HTTP_AUTH_NTLM2: /* keep-alive connection */ openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive"); if (!send_line_crlf (sd, buf)) goto error; openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s", ntlm_phase_1 (p, &gc)); msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 1"); dmsg (D_SHOW_KEYS, "Send to HTTP proxy: '%s'", buf); if (!send_line_crlf (sd, buf)) goto error; break; #endif default: ASSERT (0); } /* send empty CR, LF */ if (!send_crlf (sd)) goto error; /* receive reply from proxy */ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) goto error; /* remove trailing CR, LF */ chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); /* parse return string */ nparms = sscanf (buf, "%*s %d", &status); } /* check for a "407 Proxy Authentication Required" response */ while (nparms >= 1 && status == 407) { msg (D_PROXY, "Proxy requires authentication"); if (p->auth_method == HTTP_AUTH_BASIC && !processed) { processed = true; } else if ((p->auth_method == HTTP_AUTH_NTLM || p->auth_method == HTTP_AUTH_NTLM2) && !processed) /* check for NTLM */ { #if NTLM /* look for the phase 2 response */ while (true) { if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) goto error; chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); openvpn_snprintf (get, sizeof get, "%%*s NTLM %%%ds", (int) sizeof (buf2) - 1); nparms = sscanf (buf, get, buf2); buf2[127] = 0; /* we only need the beginning - ensure it's null terminated. */ /* check for "Proxy-Authenticate: NTLM TlRM..." */ if (nparms == 1) { /* parse buf2 */ msg (D_PROXY, "auth string: '%s'", buf2); break; } } /* if we are here then auth string was got */ msg (D_PROXY, "Received NTLM Proxy-Authorization phase 2 response"); /* receive and discard everything else */ while (recv_line (sd, NULL, 0, 2, true, NULL, signal_received)) ; /* now send the phase 3 reply */ /* format HTTP CONNECT message */ openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", host, port, p->options.http_version); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); /* send HTTP CONNECT message to proxy */ if (!send_line_crlf (sd, buf)) goto error; /* keep-alive connection */ openvpn_snprintf (buf, sizeof(buf), "Proxy-Connection: Keep-Alive"); if (!send_line_crlf (sd, buf)) goto error; /* send HOST etc, */ openvpn_snprintf (buf, sizeof(buf), "Host: %s", host); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); if (!send_line_crlf (sd, buf)) goto error; msg (D_PROXY, "Attempting NTLM Proxy-Authorization phase 3"); { const char *np3 = ntlm_phase_3 (p, buf2, &gc); if (!np3) { msg (D_PROXY, "NTLM Proxy-Authorization phase 3 failed: received corrupted data from proxy server"); goto error; } openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: NTLM %s", np3); } msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); if (!send_line_crlf (sd, buf)) goto error; /* ok so far... */ /* send empty CR, LF */ if (!send_crlf (sd)) goto error; /* receive reply from proxy */ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) goto error; /* remove trailing CR, LF */ chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); /* parse return string */ nparms = sscanf (buf, "%*s %d", &status); processed = true; #endif } #if PROXY_DIGEST_AUTH else if (p->auth_method == HTTP_AUTH_DIGEST && !processed) { char *pa = p->proxy_authenticate; const int method = p->auth_method; ASSERT(pa); if (method == HTTP_AUTH_DIGEST) { const char *http_method = "CONNECT"; const char *nonce_count = "00000001"; const char *qop = "auth"; const char *username = p->up.username; const char *password = p->up.password; char *opaque_kv = ""; char uri[128]; uint8_t cnonce_raw[8]; uint8_t *cnonce; HASHHEX session_key; HASHHEX response; const char *realm = get_pa_var("realm", pa, &gc); const char *nonce = get_pa_var("nonce", pa, &gc); const char *algor = get_pa_var("algorithm", pa, &gc); const char *opaque = get_pa_var("opaque", pa, &gc); /* generate a client nonce */ ASSERT(rand_bytes(cnonce_raw, sizeof(cnonce_raw))); cnonce = make_base64_string2(cnonce_raw, sizeof(cnonce_raw), &gc); /* build the digest response */ openvpn_snprintf (uri, sizeof(uri), "%s:%d", host, port); if (opaque) { const int len = strlen(opaque)+16; opaque_kv = gc_malloc(len, false, &gc); openvpn_snprintf (opaque_kv, len, ", opaque=\"%s\"", opaque); } DigestCalcHA1(algor, username, realm, password, nonce, (char *)cnonce, session_key); DigestCalcResponse(session_key, nonce, nonce_count, (char *)cnonce, qop, http_method, uri, NULL, response); /* format HTTP CONNECT message */ openvpn_snprintf (buf, sizeof(buf), "%s %s HTTP/%s", http_method, uri, p->options.http_version); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); /* send HTTP CONNECT message to proxy */ if (!send_line_crlf (sd, buf)) goto error; /* send HOST etc, */ openvpn_snprintf (buf, sizeof(buf), "Host: %s", host); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); if (!send_line_crlf (sd, buf)) goto error; /* send digest response */ openvpn_snprintf (buf, sizeof(buf), "Proxy-Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", qop=%s, nc=%s, cnonce=\"%s\", response=\"%s\"%s", username, realm, nonce, uri, qop, nonce_count, cnonce, response, opaque_kv ); msg (D_PROXY, "Send to HTTP proxy: '%s'", buf); if (!send_line_crlf (sd, buf)) goto error; if (!send_crlf (sd)) goto error; /* receive reply from proxy */ if (!recv_line (sd, buf, sizeof(buf), p->options.timeout, true, NULL, signal_received)) goto error; /* remove trailing CR, LF */ chomp (buf); msg (D_PROXY, "HTTP proxy returned: '%s'", buf); /* parse return string */ nparms = sscanf (buf, "%*s %d", &status); processed = true; } else { msg (D_PROXY, "HTTP proxy: digest method not supported"); goto error; } } #endif else if (p->options.auth_retry) { /* figure out what kind of authentication the proxy needs */ char *pa = NULL; const int method = get_proxy_authenticate(sd, p->options.timeout, &pa, NULL, signal_received); if (method != HTTP_AUTH_NONE) { if (pa) msg (D_PROXY, "HTTP proxy authenticate '%s'", pa); if (p->options.auth_retry == PAR_NCT && method == HTTP_AUTH_BASIC) { msg (D_PROXY, "HTTP proxy: support for basic auth and other cleartext proxy auth methods is disabled"); goto error; } p->auth_method = method; store_proxy_authenticate(p, pa); ret = true; goto done; } else { msg (D_PROXY, "HTTP proxy: do not recognize the authentication method required by proxy"); free (pa); goto error; } } else { if (!processed) msg (D_PROXY, "HTTP proxy: no support for proxy authentication method"); goto error; } /* clear state */ if (p->options.auth_retry) clear_user_pass_http(); store_proxy_authenticate(p, NULL); } /* check return code, success = 200 */ if (nparms < 1 || status != 200) { msg (D_LINK_ERRORS, "HTTP proxy returned bad status"); #if 0 /* DEBUGGING -- show a multi-line HTTP error response */ dump_residual(sd, p->options.timeout, signal_received); #endif goto error; } /* SUCCESS */ /* receive line from proxy and discard */ if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received)) goto error; /* * Toss out any extraneous chars, but don't throw away the * start of the OpenVPN data stream (put it in lookahead). */ while (recv_line (sd, NULL, 0, 2, false, lookahead, signal_received)) ; /* reset queried_creds so that we don't think that the next creds request is due to an auth error */ p->queried_creds = false; #if 0 if (lookahead && BLEN (lookahead)) msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0)); #endif done: gc_free (&gc); return ret; error: /* on error, should we exit or restart? */ if (!*signal_received) *signal_received = (p->options.retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- HTTP proxy error */ gc_free (&gc); return ret; }
bool send_push_reply (struct context *c) { struct gc_arena gc = gc_new (); struct buffer buf = alloc_buf_gc (PUSH_BUNDLE_SIZE, &gc); struct push_entry *e = c->options.push_list.head; bool multi_push = false; static char cmd[] = "PUSH_REPLY"; const int extra = 64; /* extra space for possible trailing ifconfig and push-continuation */ const int safe_cap = BCAP (&buf) - extra; bool push_sent = false; buf_printf (&buf, "%s", cmd); while (e) { if (e->enable) { const int l = strlen (e->option); if (BLEN (&buf) + l >= safe_cap) { buf_printf (&buf, ",push-continuation 2"); { const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); if (!status) goto fail; push_sent = true; multi_push = true; buf_reset_len (&buf); buf_printf (&buf, "%s", cmd); } } if (BLEN (&buf) + l >= safe_cap) { msg (M_WARN, "--push option is too long"); goto fail; } buf_printf (&buf, ",%s", e->option); } e = e->next; } 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 (multi_push) buf_printf (&buf, ",push-continuation 1"); if (BLEN (&buf) > sizeof(cmd)-1) { const bool status = send_control_channel_string (c, BSTR (&buf), D_PUSH); if (!status) goto fail; push_sent = true; } /* If nothing have been pushed, send an empty push, * as the client is expecting a response */ if (!push_sent) { bool status = false; buf_reset_len (&buf); buf_printf (&buf, "%s", cmd); status = send_control_channel_string (c, BSTR(&buf), D_PUSH); if (!status) goto fail; } gc_free (&gc); return true; fail: gc_free (&gc); return false; }
bool process_incoming_link_part1 (struct context *c, struct link_socket_info *lsi, bool floated) { struct gc_arena gc = gc_new (); bool decrypt_status = false; if (c->c2.buf.len > 0) { c->c2.link_read_bytes += c->c2.buf.len; link_read_bytes_global += c->c2.buf.len; #ifdef ENABLE_MEMSTATS if (mmap_stats) mmap_stats->link_read_bytes = link_read_bytes_global; #endif c->c2.original_recv_size = c->c2.buf.len; #ifdef ENABLE_MANAGEMENT if (management) { management_bytes_in (management, c->c2.buf.len); #ifdef MANAGEMENT_DEF_AUTH management_bytes_server (management, &c->c2.link_read_bytes, &c->c2.link_write_bytes, &c->c2.mda_context); #endif } #endif } else c->c2.original_recv_size = 0; #ifdef ENABLE_DEBUG /* take action to corrupt packet if we are in gremlin test mode */ if (c->options.gremlin) { if (!ask_gremlin (c->options.gremlin)) c->c2.buf.len = 0; corrupt_gremlin (&c->c2.buf, c->options.gremlin); } #endif /* log incoming packet */ #ifdef LOG_RW if (c->c2.log_rw && c->c2.buf.len > 0) fprintf (stderr, "R"); #endif msg (D_LINK_RW, "%s READ [%d] from %s: %s", proto2ascii (lsi->proto, lsi->af, true), BLEN (&c->c2.buf), print_link_socket_actual (&c->c2.from, &gc), PROTO_DUMP (&c->c2.buf, &gc)); /* * Good, non-zero length packet received. * Commence multi-stage processing of packet, * such as authenticate, decrypt, decompress. * If any stage fails, it sets buf.len to 0 or -1, * telling downstream stages to ignore the packet. */ if (c->c2.buf.len > 0) { if (!link_socket_verify_incoming_addr (&c->c2.buf, lsi, &c->c2.from)) link_socket_bad_incoming_addr (&c->c2.buf, lsi, &c->c2.from); #ifdef ENABLE_CRYPTO if (c->c2.tls_multi) { /* * If tls_pre_decrypt returns true, it means the incoming * packet was a good TLS control channel packet. If so, TLS code * will deal with the packet and set buf.len to 0 so downstream * stages ignore it. * * If the packet is a data channel packet, tls_pre_decrypt * will load crypto_options with the correct encryption key * and return false. */ if (tls_pre_decrypt (c->c2.tls_multi, &c->c2.from, &c->c2.buf, &c->c2.crypto_options, floated)) { interval_action (&c->c2.tmp_int); /* reset packet received timer if TLS packet */ if (c->options.ping_rec_timeout) event_timeout_reset (&c->c2.ping_rec_interval); } } #if P2MP_SERVER /* * Drop non-TLS packet if client-connect script/plugin has not * yet succeeded. */ if (c->c2.context_auth != CAS_SUCCEEDED) c->c2.buf.len = 0; #endif /* authenticate and decrypt the incoming packet */ decrypt_status = openvpn_decrypt (&c->c2.buf, c->c2.buffers->decrypt_buf, &c->c2.crypto_options, &c->c2.frame); if (!decrypt_status && link_socket_connection_oriented (c->c2.link_socket)) { /* decryption errors are fatal in TCP mode */ register_signal (c, SIGUSR1, "decryption-error"); /* SOFT-SIGUSR1 -- decryption error in TCP mode */ msg (D_STREAM_ERRORS, "Fatal decryption error (process_incoming_link), restarting"); } #else /* ENABLE_CRYPTO */ decrypt_status = true; #endif /* ENABLE_CRYPTO */ } else { buf_reset (&c->c2.to_tun); } gc_free (&gc); return decrypt_status; }
static void plugin_init_item (struct plugin *p, const struct plugin_option *o) { struct gc_arena gc = gc_new (); bool rel = false; p->so_pathname = o->so_pathname; p->plugin_type_mask = plugin_supported_types (); #if defined(USE_LIBDL) p->handle = NULL; #if defined(PLUGIN_LIBDIR) if (!absolute_pathname (p->so_pathname)) { char full[PATH_MAX]; openvpn_snprintf (full, sizeof(full), "%s/%s", PLUGIN_LIBDIR, p->so_pathname); p->handle = dlopen (full, RTLD_NOW); #if defined(ENABLE_PLUGIN_SEARCH) if (!p->handle) { rel = true; p->handle = dlopen (p->so_pathname, RTLD_NOW); } #endif } else #endif { rel = !absolute_pathname (p->so_pathname); p->handle = dlopen (p->so_pathname, RTLD_NOW); } if (!p->handle) msg (M_ERR, "PLUGIN_INIT: could not load plugin shared object %s: %s", p->so_pathname, dlerror()); # define PLUGIN_SYM(var, name, flags) libdl_resolve_symbol (p->handle, (void*)&p->var, name, p->so_pathname, flags) #elif defined(USE_LOAD_LIBRARY) rel = !absolute_pathname (p->so_pathname); p->module = LoadLibrary (p->so_pathname); if (!p->module) msg (M_ERR, "PLUGIN_INIT: could not load plugin DLL: %s", p->so_pathname); # define PLUGIN_SYM(var, name, flags) dll_resolve_symbol (p->module, (void*)&p->var, name, p->so_pathname, flags) #endif PLUGIN_SYM (open1, "openvpn_plugin_open_v1", 0); PLUGIN_SYM (open2, "openvpn_plugin_open_v2", 0); PLUGIN_SYM (func1, "openvpn_plugin_func_v1", 0); PLUGIN_SYM (func2, "openvpn_plugin_func_v2", 0); PLUGIN_SYM (close, "openvpn_plugin_close_v1", PLUGIN_SYMBOL_REQUIRED); PLUGIN_SYM (abort, "openvpn_plugin_abort_v1", 0); PLUGIN_SYM (client_constructor, "openvpn_plugin_client_constructor_v1", 0); PLUGIN_SYM (client_destructor, "openvpn_plugin_client_destructor_v1", 0); PLUGIN_SYM (min_version_required, "openvpn_plugin_min_version_required_v1", 0); PLUGIN_SYM (initialization_point, "openvpn_plugin_select_initialization_point_v1", 0); if (!p->open1 && !p->open2) msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_open_vX is undefined in plugin: %s", p->so_pathname); if (!p->func1 && !p->func2) msg (M_FATAL, "PLUGIN: symbol openvpn_plugin_func_vX is undefined in plugin: %s", p->so_pathname); /* * Verify that we are sufficiently up-to-date to handle the plugin */ if (p->min_version_required) { const int plugin_needs_version = (*p->min_version_required)(); if (plugin_needs_version > OPENVPN_PLUGIN_VERSION) msg (M_FATAL, "PLUGIN_INIT: plugin needs interface version %d, but this version of OpenVPN only supports version %d: %s", plugin_needs_version, OPENVPN_PLUGIN_VERSION, p->so_pathname); } if (p->initialization_point) p->requested_initialization_point = (*p->initialization_point)(); else p->requested_initialization_point = OPENVPN_PLUGIN_INIT_PRE_DAEMON; if (rel) msg (M_WARN, "WARNING: plugin '%s' specified by a relative pathname -- using an absolute pathname would be more secure", p->so_pathname); p->initialized = true; gc_free (&gc); }
void ifconfig_pool_test(in_addr_t start, in_addr_t end) { struct gc_arena gc = gc_new(); struct ifconfig_pool *p = ifconfig_pool_init(IFCONFIG_POOL_30NET, start, end); /*struct ifconfig_pool *p = ifconfig_pool_init (IFCONFIG_POOL_INDIV, start, end);*/ ifconfig_pool_handle array[256]; int i; CLEAR(array); msg(M_INFO | M_NOPREFIX, "************ 1"); for (i = 0; i < (int) SIZE(array); ++i) { char *cn; ifconfig_pool_handle h; in_addr_t local, remote; char buf[256]; openvpn_snprintf(buf, sizeof(buf), "common-name-%d", i); #ifdef DUP_CN cn = NULL; #else cn = buf; #endif h = ifconfig_pool_acquire(p, &local, &remote, NULL, cn); if (h < 0) { break; } msg(M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 1: l=%s r=%s cn=%s", print_in_addr_t(local, 0, &gc), print_in_addr_t(remote, 0, &gc), cn); array[i] = h; } msg(M_INFO | M_NOPREFIX, "************* 2"); for (i = (int) SIZE(array) / 16; i < (int) SIZE(array) / 8; ++i) { msg(M_INFO, "Attempt to release %d cn=%s", array[i], p->list[i].common_name); if (!ifconfig_pool_release(p, array[i])) { break; } msg(M_INFO, "Succeeded"); } CLEAR(array); msg(M_INFO | M_NOPREFIX, "**************** 3"); for (i = 0; i < (int) SIZE(array); ++i) { char *cn; ifconfig_pool_handle h; in_addr_t local, remote; char buf[256]; snprintf(buf, sizeof(buf), "common-name-%d", i+24); #ifdef DUP_CN cn = NULL; #else cn = buf; #endif h = ifconfig_pool_acquire(p, &local, &remote, NULL, cn); if (h < 0) { break; } msg(M_INFO | M_NOPREFIX, "IFCONFIG_POOL TEST pass 3: l=%s r=%s cn=%s", print_in_addr_t(local, 0, &gc), print_in_addr_t(remote, 0, &gc), cn); array[i] = h; } ifconfig_pool_free(p); gc_free(&gc); }