From patchwork Wed Aug 25 04:01:21 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [Openvpn-devel,1/2] Add remote-count and remote-entry query via management X-Patchwork-Submitter: Selva Nair X-Patchwork-Id: 1919 Message-Id: <20210825040122.14244-1-selva.nair@gmail.com> To: openvpn-devel@lists.sourceforge.net Date: Wed, 25 Aug 2021 00:01:21 -0400 From: selva.nair@gmail.com List-Id: From: Selva Nair Selecting the remote host via the management iterface (management-query-remote) provides a restrictive user experience as there is no easy way to tabulate all available remote entries and show a list to the user to choose from. Fix that. Two new commands for querying the management interface are added: (i) remote-entry-count : returns the number of remotes specified in the config file. Example result: >REMOTE-ENTRY-COUNT:10 (ii) remote-entry-get index : returns the remote entry at index in the form index,host,port,protocol. Example result for index = 2: >REMOTE-ENTRY:2,ovpn.example.com,1194,udp See also management-notes.txt Signed-off-by: Selva Nair --- Changes.rst | 5 ++++ doc/management-notes.txt | 22 +++++++++++++++++ src/openvpn/init.c | 36 ++++++++++++++++++++++++++++ src/openvpn/manage.c | 52 ++++++++++++++++++++++++++++++++++++++++ src/openvpn/manage.h | 3 ++- 5 files changed, 117 insertions(+), 1 deletion(-) diff --git a/Changes.rst b/Changes.rst index 0323a7f7..e5ac8098 100644 --- a/Changes.rst +++ b/Changes.rst @@ -4,6 +4,11 @@ Overview of changes in 2.6 New features ------------ +New management commands to enumerate and list remote entries + Use ``remote-entry-count`` and ``remote-entry-get index`` + commands from the management interface to get the number of + remote entries and the entry at index respectively. + Keying Material Exporters (RFC 5705) based key generation As part of the cipher negotiation OpenVPN will automatically prefer the RFC5705 based key material generation to the current custom diff --git a/doc/management-notes.txt b/doc/management-notes.txt index 84e3d04b..f7a0fe1f 100644 --- a/doc/management-notes.txt +++ b/doc/management-notes.txt @@ -897,6 +897,28 @@ the 10.0.0.0/8 netblock is allowed: 10.10.0.1. Also, the client may not interact with external IP addresses using an "unknown" protocol (i.e. one that is not IPv4 or ARP). +COMMAND -- remote-entry-count (OpenVPN 2.6+ management version > 3) +------------------------------------------------------------------- + +Retrieve available number of remote host/port entries + +Example: + + remote-entry-count + >REMOTE-ENTRY-COUNT:5 + +COMMAND -- remote-entry-get (OpenVPN 2.6+ management version > 3) +------------------------------------------------------------------ + +Retrieve remote entry (host, port and protocol) by index. + +Example: + + remote-entgry-get 1 + REMOTE-ENTRY:1,vpn.example.com,1194,udp + +The protocol could be tcp-client or udp on client. + COMMAND -- remote (OpenVPN AS 2.1.5/OpenVPN 2.3 or higher) -------------------------------------------- diff --git a/src/openvpn/init.c b/src/openvpn/init.c index 386aee23..3c98a408 100644 --- a/src/openvpn/init.c +++ b/src/openvpn/init.c @@ -330,6 +330,41 @@ management_callback_send_cc_message(void *arg, return status; } +static bool +management_callback_remote_entry(void *arg, unsigned *count, char **remote) +{ + assert(arg); + assert(count); + + struct context *c = (struct context *) arg; + struct connection_list *l = c->options.connection_list; + bool ret = true; + + if (!remote) /* query is for the count of entries */ + { + *count = l->len; + } + else if (*count < l->len) /* the query is for entry with index = count */ + { + struct connection_entry *ce = l->array[*count]; + const char *proto = proto2ascii(ce->proto, ce->af, false); + + /* space for output including 2 commas and a nul */ + int len = strlen(ce->remote) + strlen(ce->remote_port) + strlen(proto) + 2 + 1; + char *out = malloc(len); + check_malloc_return(out); + + openvpn_snprintf(out, len, "%s,%s,%s", ce->remote, ce->remote_port, proto); + *remote = out; + } + else + { + ret = false; + msg(M_WARN, "Invalid arguments in management query for remote entry: count = %u", *count); + } + return ret; +} + static bool management_callback_remote_cmd(void *arg, const char **p) { @@ -3944,6 +3979,7 @@ init_management_callback_p2p(struct context *c) #ifdef TARGET_ANDROID cb.network_change = management_callback_network_change; #endif + cb.remote_entry = management_callback_remote_entry; management_set_callback(management, &cb); } #endif diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index f86c87f2..c2eb699f 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -94,6 +94,8 @@ man_help(void) msg(M_CLIENT, "net : (Windows only) Show network info and routing table."); msg(M_CLIENT, "password type p : Enter password p for a queried OpenVPN password."); msg(M_CLIENT, "remote type [host port] : Override remote directive, type=ACCEPT|MOD|SKIP."); + msg(M_CLIENT, "remote-entry-count : Get number of available remote entries."); + msg(M_CLIENT, "remote-entry-get index : Get remote entry at index."); msg(M_CLIENT, "proxy type [host port flags] : Enter dynamic proxy server info."); msg(M_CLIENT, "pid : Show process ID of the current OpenVPN process."); #ifdef ENABLE_PKCS11 @@ -829,6 +831,45 @@ man_pkcs11_id_get(struct management *man, const int index) #endif /* ifdef ENABLE_PKCS11 */ +static void +man_remote_entry_count(struct management *man) +{ + unsigned count = 0; + if (man->persist.callback.remote_entry) + { + (*man->persist.callback.remote_entry)(man->persist.callback.arg, &count, NULL); + msg(M_CLIENT, ">REMOTE-ENTRY-COUNT:%u", count); + } + else + { + msg(M_CLIENT, "ERROR: The remote-entry-count command is not supported by the current daemon mode"); + } +} + +static void +man_remote_entry_get(struct management *man, unsigned index) +{ + char *remote = NULL; + + if (man->persist.callback.remote_entry) + { + const bool status = (*man->persist.callback.remote_entry)(man->persist.callback.arg, &index, &remote); + if (status) + { + msg(M_CLIENT, ">REMOTE-ENTRY:%u,%s", index, remote); + } + else + { + msg(M_CLIENT, ">REMOTE-ENTRY:%u", index); + } + } + else + { + msg(M_CLIENT, "ERROR: The remote-entry command is not supported by the current daemon mode"); + } + free(remote); +} + static void man_hold(struct management *man, const char *cmd) { @@ -1601,6 +1642,17 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha } } #endif + else if (streq(p[0], "remote-entry-count")) + { + man_remote_entry_count(man); + } + else if (streq(p[0], "remote-entry-get")) + { + if (man_need(man, p, 1, 0)) + { + man_remote_entry_get(man, atoi(p[1])); + } + } else if (streq(p[0], "proxy")) { if (man_need(man, p, 1, MN_AT_LEAST)) diff --git a/src/openvpn/manage.h b/src/openvpn/manage.h index 6d6f2fb1..b7fcb86c 100644 --- a/src/openvpn/manage.h +++ b/src/openvpn/manage.h @@ -31,7 +31,7 @@ #include "socket.h" #include "mroute.h" -#define MANAGEMENT_VERSION 3 +#define MANAGEMENT_VERSION 4 #define MANAGEMENT_N_PASSWORD_RETRIES 3 #define MANAGEMENT_LOG_HISTORY_INITIAL_SIZE 100 #define MANAGEMENT_ECHO_BUFFER_SIZE 100 @@ -186,6 +186,7 @@ struct management_callback #ifdef TARGET_ANDROID int (*network_change)(void *arg, bool samenetwork); #endif + bool (*remote_entry) (void *arg, unsigned *count, char **remote); }; /* From patchwork Wed Aug 25 04:01:22 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [Openvpn-devel,2/2] Permit unlimited connection entries and remotes X-Patchwork-Submitter: Selva Nair X-Patchwork-Id: 1920 Message-Id: <20210825040122.14244-2-selva.nair@gmail.com> To: openvpn-devel@lists.sourceforge.net Date: Wed, 25 Aug 2021 00:01:22 -0400 From: selva.nair@gmail.com List-Id: From: Selva Nair Currently we allow a max of 64 connection entries and remotes. A larger number would allow users with 100's of independent config files for different end points of same provider to consolidate them to connection entries. Signed-off-by: Selva Nair --- Changes.rst | 2 ++ src/openvpn/options.c | 34 ++++++++++++++++++++++++++++------ src/openvpn/options.h | 6 ++++-- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/Changes.rst b/Changes.rst index e5ac8098..6e3c535e 100644 --- a/Changes.rst +++ b/Changes.rst @@ -4,6 +4,8 @@ Overview of changes in 2.6 New features ------------ +Support unlimited number of connection entries and remote entries + New management commands to enumerate and list remote entries Use ``remote-entry-count`` and ``remote-entry-get index`` commands from the management interface to get the number of diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 0e398c0f..5d0aa8af 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -889,6 +889,14 @@ init_options(struct options *o, const bool init_gc) void uninit_options(struct options *o) { + if (o->connection_list) { + free(o->connection_list->array); + CLEAR(*o->connection_list); + } + if (o->remote_list) { + free(o->remote_list->array); + CLEAR(*o->remote_list); + } if (o->gc_owned) { gc_free(&o->gc); @@ -1947,10 +1955,17 @@ alloc_connection_entry(struct options *options, const int msglevel) struct connection_list *l = alloc_connection_list_if_undef(options); struct connection_entry *e; - if (l->len >= CONNECTION_LIST_SIZE) + if (l->len == l->capacity) { - msg(msglevel, "Maximum number of 'connection' options (%d) exceeded", CONNECTION_LIST_SIZE); - return NULL; + int capacity = l->capacity + CONNECTION_LIST_SIZE; + struct connection_entry **ce = realloc(l->array, capacity*sizeof(struct connection_entry *)); + if (ce == NULL) + { + msg(msglevel, "Unable to process more connection options: out of memory. Number of entries = %d", l->len); + return NULL; + } + l->array = ce; + l->capacity = capacity; } ALLOC_OBJ_GC(e, struct connection_entry, &options->gc); l->array[l->len++] = e; @@ -1973,10 +1988,17 @@ alloc_remote_entry(struct options *options, const int msglevel) struct remote_list *l = alloc_remote_list_if_undef(options); struct remote_entry *e; - if (l->len >= CONNECTION_LIST_SIZE) + if (l->len == l->capacity) { - msg(msglevel, "Maximum number of 'remote' options (%d) exceeded", CONNECTION_LIST_SIZE); - return NULL; + int capacity = l->capacity + CONNECTION_LIST_SIZE; + struct remote_entry **re = realloc(l->array, capacity*sizeof(struct remote_entry *)); + if (re == NULL) + { + msg(msglevel, "Unable to process more remote options: out of memory. Number of entries = %d", l->len); + return NULL; + } + l->array = re; + l->capacity = capacity; } ALLOC_OBJ_GC(e, struct remote_entry, &options->gc); l->array[l->len++] = e; diff --git a/src/openvpn/options.h b/src/openvpn/options.h index b0e40cb7..98977d41 100644 --- a/src/openvpn/options.h +++ b/src/openvpn/options.h @@ -168,15 +168,17 @@ struct remote_entry struct connection_list { + int capacity; int len; int current; - struct connection_entry *array[CONNECTION_LIST_SIZE]; + struct connection_entry **array; }; struct remote_list { + int capacity; int len; - struct remote_entry *array[CONNECTION_LIST_SIZE]; + struct remote_entry **array; }; enum vlan_acceptable_frames From patchwork Wed Aug 25 21:02:32 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [Openvpn-devel,v2,3/3] Use a template for "unsupported management commands" error X-Patchwork-Submitter: Selva Nair X-Patchwork-Id: 1921 Message-Id: <20210825210232.22509-3-selva.nair@gmail.com> To: openvpn-devel@lists.sourceforge.net Date: Wed, 25 Aug 2021 17:02:32 -0400 From: selva.nair@gmail.com List-Id: From: Selva Nair The message "ERROR: The 'foo' commmand is not supported by current daemon mode" is repeatedly used in manage.c. Move it to a function for uniformity in messaging. Signed-off-by: Selva Nair --- src/openvpn/manage.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/openvpn/manage.c b/src/openvpn/manage.c index 214ea4be..7eff2dba 100644 --- a/src/openvpn/manage.c +++ b/src/openvpn/manage.c @@ -447,6 +447,12 @@ man_signal(struct management *man, const char *name) } } +static void +man_command_unsupported(const char *command_name) +{ + msg(M_CLIENT, "ERROR: The '%s' command is not supported by the current daemon mode", command_name); +} + static void man_status(struct management *man, const int version, struct status_output *so) { @@ -456,7 +462,7 @@ man_status(struct management *man, const int version, struct status_output *so) } else { - msg(M_CLIENT, "ERROR: The 'status' command is not supported by the current daemon mode"); + man_command_unsupported("status"); } } @@ -572,7 +578,7 @@ man_kill(struct management *man, const char *victim) } else { - msg(M_CLIENT, "ERROR: The 'kill' command is not supported by the current daemon mode"); + man_command_unsupported("kill"); } gc_free(&gc); @@ -777,7 +783,7 @@ man_net(struct management *man) } else { - msg(M_CLIENT, "ERROR: The 'net' command is not supported by the current daemon mode"); + man_command_unsupported("net"); } } @@ -799,7 +805,7 @@ man_send_cc_message(struct management *man, const char *message, const char *par } else { - msg(M_CLIENT, "ERROR: This command is not supported by the current daemon mode"); + man_command_unsupported("cr-repsonse"); } } #ifdef ENABLE_PKCS11 @@ -842,7 +848,7 @@ man_remote_entry_count(struct management *man) } else { - msg(M_CLIENT, "ERROR: The remote-entry-count command is not supported by the current daemon mode"); + man_command_unsupported("remote-entry-count"); } } @@ -865,7 +871,7 @@ man_remote_entry_get(struct management *man, unsigned int index) } else { - msg(M_CLIENT, "ERROR: The remote-entry command is not supported by the current daemon mode"); + man_command_unsupported("remote-entry-get"); } free(remote); @@ -956,7 +962,7 @@ in_extra_dispatch(struct management *man) } else { - msg(M_CLIENT, "ERROR: The client-auth command is not supported by the current daemon mode"); + man_command_unsupported("client-auth"); } break; @@ -980,7 +986,7 @@ in_extra_dispatch(struct management *man) } else { - msg(M_CLIENT, "ERROR: The client-pf command is not supported by the current daemon mode"); + man_command_unsupported("client-pf"); } break; @@ -1065,7 +1071,7 @@ man_client_pending_auth(struct management *man, const char *cid_str, } else { - msg(M_CLIENT, "ERROR: The client-pending-auth command is not supported by the current daemon mode"); + man_command_unsupported("client-pending-auth"); } } } @@ -1116,7 +1122,7 @@ man_client_deny(struct management *man, const char *cid_str, const char *kid_str } else { - msg(M_CLIENT, "ERROR: The client-deny command is not supported by the current daemon mode"); + man_command_unsupported("client-deny"); } } } @@ -1141,7 +1147,7 @@ man_client_kill(struct management *man, const char *cid_str, const char *kill_ms } else { - msg(M_CLIENT, "ERROR: The client-kill command is not supported by the current daemon mode"); + man_command_unsupported("client-kill"); } } } @@ -1156,7 +1162,7 @@ man_client_n_clients(struct management *man) } else { - msg(M_CLIENT, "ERROR: The nclients command is not supported by the current daemon mode"); + man_command_unsupported("nclients"); } } @@ -1280,7 +1286,7 @@ man_proxy(struct management *man, const char **p) } else { - msg(M_CLIENT, "ERROR: The proxy command is not supported by the current daemon mode"); + man_command_unsupported("proxy"); } } @@ -1301,7 +1307,7 @@ man_remote(struct management *man, const char **p) } else { - msg(M_CLIENT, "ERROR: The remote command is not supported by the current daemon mode"); + man_command_unsupported("remote"); } }