summaryrefslogtreecommitdiffstats
path: root/misc.c
diff options
context:
space:
mode:
Diffstat (limited to 'misc.c')
-rw-r--r--misc.c158
1 files changed, 140 insertions, 18 deletions
diff --git a/misc.c b/misc.c
index 507bcc2..f5a3b89 100644
--- a/misc.c
+++ b/misc.c
@@ -26,6 +26,7 @@
#include "buffer.h"
#include "misc.h"
+#include "base64.h"
#include "tun.h"
#include "error.h"
#include "thread.h"
@@ -1363,10 +1364,11 @@ get_console_input (const char *prompt, const bool echo, char *input, const int c
*/
bool
-get_user_pass (struct user_pass *up,
- const char *auth_file,
- const char *prefix,
- const unsigned int flags)
+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 ();
@@ -1379,7 +1381,7 @@ get_user_pass (struct user_pass *up,
#ifdef ENABLE_MANAGEMENT
/*
- * Get username/password from standard input?
+ * Get username/password from management interface?
*/
if (management
&& ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT)))
@@ -1419,22 +1421,47 @@ get_user_pass (struct user_pass *up,
*/
else if (from_stdin)
{
- struct buffer user_prompt = alloc_buf_gc (128, &gc);
- struct buffer pass_prompt = alloc_buf_gc (128, &gc);
-
- buf_printf (&user_prompt, "Enter %s Username:", prefix);
- buf_printf (&pass_prompt, "Enter %s Password:", prefix);
-
- if (!(flags & GET_USER_PASS_PASSWORD_ONLY))
+#ifdef ENABLE_CLIENT_CR
+ if (auth_challenge)
{
- if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN))
- msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix);
- if (strlen (up->username) == 0)
- msg (M_FATAL, "ERROR: %s username is empty", prefix);
+ 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;
+
+ buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN);
+ msg (M_INFO, "CHALLENGE: %s", ac->challenge_text);
+ if (!get_console_input ("Response:", BOOL_CAST(ac->flags&CR_ECHO), response, USER_PASS_LEN))
+ 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
+ {
+ struct buffer user_prompt = alloc_buf_gc (128, &gc);
+ struct buffer pass_prompt = alloc_buf_gc (128, &gc);
- if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
- msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix);
+ buf_printf (&user_prompt, "Enter %s Username:", prefix);
+ buf_printf (&pass_prompt, "Enter %s Password:", prefix);
+
+ if (!(flags & GET_USER_PASS_PASSWORD_ONLY))
+ {
+ if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN))
+ msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix);
+ if (strlen (up->username) == 0)
+ msg (M_FATAL, "ERROR: %s username is empty", prefix);
+ }
+
+ if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
+ msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix);
+ }
}
else
{
@@ -1498,6 +1525,101 @@ get_user_pass (struct user_pass *up,
return true;
}
+#ifdef ENABLE_CLIENT_CR
+
+/*
+ * Parse a challenge message returned along with AUTH_FAILED.
+ * The message is formatted as such:
+ *
+ * CRV1:<flags>:<state_id>:<username_base64>:<challenge_text>
+ *
+ * flags: a series of optional, comma-separated flags:
+ * E : echo the response when the user types it
+ * R : a response is required
+ *
+ * state_id: an opaque string that should be returned to the server
+ * along with the response.
+ *
+ * username_base64 : the username formatted as base64
+ *
+ * challenge_text : the challenge text to be shown to the user
+ *
+ * Example challenge:
+ *
+ * CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN
+ *
+ * After showing the challenge_text and getting a response from the user
+ * (if R flag is specified), the client should submit the following
+ * auth creds back to the OpenVPN server:
+ *
+ * Username: [username decoded from username_base64]
+ * Password: CRV1::<state_id>::<response_text>
+ *
+ * Where state_id is taken from the challenge request and response_text
+ * is what the user entered in response to the challenge_text.
+ * If the R flag is not present, response_text may be the empty
+ * string.
+ *
+ * Example response (suppose the user enters "8675309" for the token PIN):
+ *
+ * Username: cr1 ("Y3Ix" base64 decoded)
+ * Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309
+ */
+struct auth_challenge_info *
+get_auth_challenge (const char *auth_challenge, struct gc_arena *gc)
+{
+ if (auth_challenge)
+ {
+ struct auth_challenge_info *ac;
+ const int len = strlen (auth_challenge);
+ char *work = (char *) gc_malloc (len+1, false, gc);
+ char *cp;
+
+ struct buffer b;
+ buf_set_read (&b, (const uint8_t *)auth_challenge, len);
+
+ ALLOC_OBJ_CLEAR_GC (ac, struct auth_challenge_info, gc);
+
+ /* parse prefix */
+ if (!buf_parse(&b, ':', work, len))
+ return NULL;
+ if (strcmp(work, "CRV1"))
+ return NULL;
+
+ /* parse flags */
+ if (!buf_parse(&b, ':', work, len))
+ return NULL;
+ for (cp = work; *cp != '\0'; ++cp)
+ {
+ const char c = *cp;
+ if (c == 'E')
+ ac->flags |= CR_ECHO;
+ else if (c == 'R')
+ ac->flags |= CR_RESPONSE;
+ }
+
+ /* parse state ID */
+ if (!buf_parse(&b, ':', work, len))
+ return NULL;
+ ac->state_id = string_alloc(work, gc);
+
+ /* parse user name */
+ if (!buf_parse(&b, ':', work, len))
+ return NULL;
+ ac->user = (char *) gc_malloc (strlen(work)+1, true, gc);
+ base64_decode(work, (void*)ac->user);
+
+ /* parse challenge text */
+ ac->challenge_text = string_alloc(BSTR(&b), gc);
+
+ return ac;
+ }
+ else
+ return NULL;
+}
+
+#endif
+
#if AUTO_USERID
static const char *