[Openvpn-devel] management: add multi-line base64 input for passwords
Commit Message
From: Luca Boccassi <luca.boccassi@gmail.com>
Allow management clients to send long passwords via the
usual multi-line base64 encoded protocol.
A client sends a 'password <type>' line, followed by
as many lines (each up to 1024 bytes) as needed, in base64
encoded format, terminated by 'END'.
This is useful when a password is a JIT-generated use-once
token.
Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
---
As suggested by:
https://sourceforge.net/p/openvpn/mailman/openvpn-devel/thread/CAKuzo_gRotwfVONQSn-yj1otvUNocAeUZqX8YjkRhP_L9jfb7A%40mail.gmail.com/#msg59309940
I have kept the same keyword 'password'. Currently sending
'password <type>' without a third field results in an hard error,
so I hope this is ok, but can of course use a different token if
preferred/needed.
doc/management-notes.txt | 14 ++++++++
src/openvpn/manage.c | 76 ++++++++++++++++++++++++++++++++++++++--
src/openvpn/manage.h | 1 +
3 files changed, 89 insertions(+), 2 deletions(-)
Comments
On Sat, Mar 21, 2026 at 9:47 PM <luca.boccassi@gmail.com> wrote:
> From: Luca Boccassi <luca.boccassi@gmail.com>
>
> Allow management clients to send long passwords via the
> usual multi-line base64 encoded protocol.
>
> A client sends a 'password <type>' line, followed by
> as many lines (each up to 1024 bytes) as needed, in base64
> encoded format, terminated by 'END'.
>
> This is useful when a password is a JIT-generated use-once
> token.
>
> Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
>
I think this is a step in the right direction: allows arbitrary long
passwords to be passed via the management interface bringing it in line
with stdin/console input. No other intrusive changes like option parsing.
Until now, the largish USER_PASS_LEN = 4096 was mostly a waste of space as
it was often not possible to use it even locally within the client because
of the 255 byte restriction in the management interface.
I have only skimmed through the patch, but reusing "password" with no
argument could break existing UI clients, hypothetically. If any UI client
is currently using an empty password to quit the dialog, it will get into a
stalemate. I guess a new keyword or an extra token after 'Auth' to
indicate multi-line input would be required.
Selva
P.S.
Next version could go into gerrit for a more thorough review.
On Wed, 25 Mar 2026 at 21:54, Selva Nair <selva.nair@gmail.com> wrote:
> On Sat, Mar 21, 2026 at 9:47 PM <luca.boccassi@gmail.com> wrote:
>>
>> From: Luca Boccassi <luca.boccassi@gmail.com>
>>
>> Allow management clients to send long passwords via the
>> usual multi-line base64 encoded protocol.
>>
>> A client sends a 'password <type>' line, followed by
>> as many lines (each up to 1024 bytes) as needed, in base64
>> encoded format, terminated by 'END'.
>>
>> This is useful when a password is a JIT-generated use-once
>> token.
>>
>> Signed-off-by: Luca Boccassi <luca.boccassi@gmail.com>
>
>
> I think this is a step in the right direction: allows arbitrary long passwords to be passed via the management interface bringing it in line with stdin/console input. No other intrusive changes like option parsing. Until now, the largish USER_PASS_LEN = 4096 was mostly a waste of space as it was often not possible to use it even locally within the client because of the 255 byte restriction in the management interface.
>
> I have only skimmed through the patch, but reusing "password" with no argument could break existing UI clients, hypothetically. If any UI client is currently using an empty password to quit the dialog, it will get into a stalemate. I guess a new keyword or an extra token after 'Auth' to indicate multi-line input would be required.
>
> Selva
> P.S.
> Next version could go into gerrit for a more thorough review.
Ok, changed to add a new "password-base64" (I don't think we want a
new third token for "password", as that might be, well, a password?),
and pushed to gerrit:
https://gerrit.openvpn.net/c/openvpn/+/1593
Hi,
On Wed, Mar 25, 2026 at 05:54:01PM -0400, Selva Nair wrote:
> I have only skimmed through the patch, but reusing "password" with no
> argument could break existing UI clients, hypothetically. If any UI client
> is currently using an empty password to quit the dialog, it will get into a
> stalemate. I guess a new keyword or an extra token after 'Auth' to
> indicate multi-line input would be required.
Bump the mgmt version, query it?
gert
@@ -313,6 +313,20 @@ COMMAND -- password and username
The escaping rules are the same as for the config file.
See the "Command Parsing" section below for more info.
+ If the password is too long to fit in a single command line
+ (longer than 256 bytes), the management interface client should
+ use the multi-line base64 format instead:
+
+ password "Auth"
+ [BASE64_PASSWORD_LINE]
+ ...
+ END
+
+ In this format, the password is base64-encoded and split across
+ multiple lines, followed by END. Each line can be at most 1024
+ bytes. This is the same format used by pk-sig and certificate
+ commands.
+
The PASSWORD real-time message type can also be used to
indicate password or other types of authentication failure:
@@ -31,6 +31,7 @@
#include "error.h"
#include "fdmisc.h"
#include "options.h"
+#include "base64.h"
#include "sig.h"
#include "event.h"
#include "otime.h"
@@ -107,6 +108,8 @@ man_help(void)
msg(M_CLIENT, " where action is reply string.");
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, "password type : Enter password for a queried OpenVPN password");
+ msg(M_CLIENT, " base64-encoded on subsequent lines followed by END.");
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 i|all [j]: Get remote entry at index = i to to j-1 or all.");
@@ -1012,6 +1015,41 @@ in_extra_reset(struct man_connection *mc, const int mode)
}
}
+/**
+ * Enter multi-line base64 mode for receiving a password that exceeds the
+ * single-line parameter size limit. The management client sends:
+ *
+ * password TYPE
+ * <base64-encoded password line 1>
+ * <base64-encoded password line 2>
+ * ...
+ * END
+ *
+ * @param man The management interface struct
+ * @param type The type of password being entered (e.g. "Auth", "TLS-Auth", etc)
+ */
+static void
+man_query_password_base64(struct management *man, const char *type)
+{
+ const bool needed = ((man->connection.up_query_mode == UP_QUERY_PASS
+ || man->connection.up_query_mode == UP_QUERY_USER_PASS)
+ && man->connection.up_query_type);
+ if (!needed)
+ {
+ msg(M_CLIENT, "ERROR: no password is currently needed at this time");
+ return;
+ }
+ if (!man->connection.up_query_type || !streq(man->connection.up_query_type, type))
+ {
+ msg(M_CLIENT, "ERROR: password of type '%s' entered, but we need one of type '%s'",
+ type, man->connection.up_query_type);
+ return;
+ }
+ struct man_connection *mc = &man->connection;
+ mc->in_extra_cmd = IEC_PASSWORD;
+ in_extra_reset(mc, IER_NEW);
+}
+
static void
in_extra_dispatch(struct management *man)
{
@@ -1045,6 +1083,33 @@ in_extra_dispatch(struct management *man)
man->connection.ext_cert_input = man->connection.in_extra;
man->connection.in_extra = NULL;
return;
+
+ case IEC_PASSWORD:
+ {
+ char decoded[USER_PASS_LEN];
+ CLEAR(decoded);
+
+ buffer_list_aggregate(man->connection.in_extra,
+ OPENVPN_BASE64_LENGTH(USER_PASS_LEN));
+ struct buffer *buf = buffer_list_peek(man->connection.in_extra);
+
+ if (buf && BLEN(buf) > 0)
+ {
+ int len = openvpn_base64_decode(BSTR(buf), decoded,
+ USER_PASS_LEN - 1);
+ if (len < 0)
+ {
+ msg(M_CLIENT, "ERROR: could not base64-decode password");
+ break;
+ }
+ decoded[len] = '\0';
+ }
+
+ man_query_password(man, man->connection.up_query_type,
+ decoded);
+ secure_memzero(decoded, sizeof(decoded));
+ break;
+ }
}
in_extra_reset(&man->connection, IER_RESET);
}
@@ -1591,9 +1656,16 @@ man_dispatch_command(struct management *man, struct status_output *so, const cha
}
else if (streq(p[0], "password"))
{
- if (man_need(man, p, 2, 0))
+ if (man_need(man, p, 1, MN_AT_LEAST))
{
- man_query_password(man, p[1], p[2]);
+ if (p[2])
+ {
+ man_query_password(man, p[1], p[2]);
+ }
+ else
+ {
+ man_query_password_base64(man, p[1]);
+ }
}
}
else if (streq(p[0], "forget-passwords"))
@@ -296,6 +296,7 @@ struct man_connection
#define IEC_RSA_SIGN 3
#define IEC_CERTIFICATE 4
#define IEC_PK_SIGN 5
+#define IEC_PASSWORD 6
int in_extra_cmd;
struct buffer_list *in_extra;
unsigned long in_extra_cid;