| ... | ... |
@@ -34,7 +34,7 @@ |
| 34 | 34 |
#ifndef _BASE64_H_ |
| 35 | 35 |
#define _BASE64_H_ |
| 36 | 36 |
|
| 37 |
-#ifdef ENABLE_HTTP_PROXY |
|
| 37 |
+#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR) |
|
| 38 | 38 |
|
| 39 | 39 |
int base64_encode(const void *data, int size, char **str); |
| 40 | 40 |
int base64_decode(const char *str, void *data); |
| ... | ... |
@@ -26,6 +26,7 @@ |
| 26 | 26 |
|
| 27 | 27 |
#include "buffer.h" |
| 28 | 28 |
#include "misc.h" |
| 29 |
+#include "base64.h" |
|
| 29 | 30 |
#include "tun.h" |
| 30 | 31 |
#include "error.h" |
| 31 | 32 |
#include "thread.h" |
| ... | ... |
@@ -1394,10 +1395,11 @@ get_console_input (const char *prompt, const bool echo, char *input, const int c |
| 1394 | 1394 |
*/ |
| 1395 | 1395 |
|
| 1396 | 1396 |
bool |
| 1397 |
-get_user_pass (struct user_pass *up, |
|
| 1398 |
- const char *auth_file, |
|
| 1399 |
- const char *prefix, |
|
| 1400 |
- const unsigned int flags) |
|
| 1397 |
+get_user_pass_cr (struct user_pass *up, |
|
| 1398 |
+ const char *auth_file, |
|
| 1399 |
+ const char *prefix, |
|
| 1400 |
+ const unsigned int flags, |
|
| 1401 |
+ const char *auth_challenge) |
|
| 1401 | 1402 |
{
|
| 1402 | 1403 |
struct gc_arena gc = gc_new (); |
| 1403 | 1404 |
|
| ... | ... |
@@ -1410,7 +1412,7 @@ get_user_pass (struct user_pass *up, |
| 1410 | 1410 |
|
| 1411 | 1411 |
#ifdef ENABLE_MANAGEMENT |
| 1412 | 1412 |
/* |
| 1413 |
- * Get username/password from standard input? |
|
| 1413 |
+ * Get username/password from management interface? |
|
| 1414 | 1414 |
*/ |
| 1415 | 1415 |
if (management |
| 1416 | 1416 |
&& ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT))) |
| ... | ... |
@@ -1450,22 +1452,47 @@ get_user_pass (struct user_pass *up, |
| 1450 | 1450 |
*/ |
| 1451 | 1451 |
else if (from_stdin) |
| 1452 | 1452 |
{
|
| 1453 |
- struct buffer user_prompt = alloc_buf_gc (128, &gc); |
|
| 1454 |
- struct buffer pass_prompt = alloc_buf_gc (128, &gc); |
|
| 1455 |
- |
|
| 1456 |
- buf_printf (&user_prompt, "Enter %s Username:", prefix); |
|
| 1457 |
- buf_printf (&pass_prompt, "Enter %s Password:", prefix); |
|
| 1458 |
- |
|
| 1459 |
- if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) |
|
| 1453 |
+#ifdef ENABLE_CLIENT_CR |
|
| 1454 |
+ if (auth_challenge) |
|
| 1460 | 1455 |
{
|
| 1461 |
- if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN)) |
|
| 1462 |
- msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix); |
|
| 1463 |
- if (strlen (up->username) == 0) |
|
| 1464 |
- msg (M_FATAL, "ERROR: %s username is empty", prefix); |
|
| 1456 |
+ struct auth_challenge_info *ac = get_auth_challenge (auth_challenge, &gc); |
|
| 1457 |
+ if (ac) |
|
| 1458 |
+ {
|
|
| 1459 |
+ char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc); |
|
| 1460 |
+ struct buffer packed_resp; |
|
| 1461 |
+ |
|
| 1462 |
+ buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN); |
|
| 1463 |
+ msg (M_INFO, "CHALLENGE: %s", ac->challenge_text); |
|
| 1464 |
+ if (!get_console_input ("Response:", BOOL_CAST(ac->flags&CR_ECHO), response, USER_PASS_LEN))
|
|
| 1465 |
+ msg (M_FATAL, "ERROR: could not read challenge response from stdin"); |
|
| 1466 |
+ strncpynt (up->username, ac->user, USER_PASS_LEN); |
|
| 1467 |
+ buf_printf (&packed_resp, "CRV1::%s::%s", ac->state_id, response); |
|
| 1468 |
+ } |
|
| 1469 |
+ else |
|
| 1470 |
+ {
|
|
| 1471 |
+ msg (M_FATAL, "ERROR: received malformed challenge request from server"); |
|
| 1472 |
+ } |
|
| 1465 | 1473 |
} |
| 1474 |
+ else |
|
| 1475 |
+#endif |
|
| 1476 |
+ {
|
|
| 1477 |
+ struct buffer user_prompt = alloc_buf_gc (128, &gc); |
|
| 1478 |
+ struct buffer pass_prompt = alloc_buf_gc (128, &gc); |
|
| 1466 | 1479 |
|
| 1467 |
- if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) |
|
| 1468 |
- msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix); |
|
| 1480 |
+ buf_printf (&user_prompt, "Enter %s Username:", prefix); |
|
| 1481 |
+ buf_printf (&pass_prompt, "Enter %s Password:", prefix); |
|
| 1482 |
+ |
|
| 1483 |
+ if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) |
|
| 1484 |
+ {
|
|
| 1485 |
+ if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN)) |
|
| 1486 |
+ msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix); |
|
| 1487 |
+ if (strlen (up->username) == 0) |
|
| 1488 |
+ msg (M_FATAL, "ERROR: %s username is empty", prefix); |
|
| 1489 |
+ } |
|
| 1490 |
+ |
|
| 1491 |
+ if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) |
|
| 1492 |
+ msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix); |
|
| 1493 |
+ } |
|
| 1469 | 1494 |
} |
| 1470 | 1495 |
else |
| 1471 | 1496 |
{
|
| ... | ... |
@@ -1529,6 +1556,101 @@ get_user_pass (struct user_pass *up, |
| 1529 | 1529 |
return true; |
| 1530 | 1530 |
} |
| 1531 | 1531 |
|
| 1532 |
+#ifdef ENABLE_CLIENT_CR |
|
| 1533 |
+ |
|
| 1534 |
+/* |
|
| 1535 |
+ * Parse a challenge message returned along with AUTH_FAILED. |
|
| 1536 |
+ * The message is formatted as such: |
|
| 1537 |
+ * |
|
| 1538 |
+ * CRV1:<flags>:<state_id>:<username_base64>:<challenge_text> |
|
| 1539 |
+ * |
|
| 1540 |
+ * flags: a series of optional, comma-separated flags: |
|
| 1541 |
+ * E : echo the response when the user types it |
|
| 1542 |
+ * R : a response is required |
|
| 1543 |
+ * |
|
| 1544 |
+ * state_id: an opaque string that should be returned to the server |
|
| 1545 |
+ * along with the response. |
|
| 1546 |
+ * |
|
| 1547 |
+ * username_base64 : the username formatted as base64 |
|
| 1548 |
+ * |
|
| 1549 |
+ * challenge_text : the challenge text to be shown to the user |
|
| 1550 |
+ * |
|
| 1551 |
+ * Example challenge: |
|
| 1552 |
+ * |
|
| 1553 |
+ * CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN |
|
| 1554 |
+ * |
|
| 1555 |
+ * After showing the challenge_text and getting a response from the user |
|
| 1556 |
+ * (if R flag is specified), the client should submit the following |
|
| 1557 |
+ * auth creds back to the OpenVPN server: |
|
| 1558 |
+ * |
|
| 1559 |
+ * Username: [username decoded from username_base64] |
|
| 1560 |
+ * Password: CRV1::<state_id>::<response_text> |
|
| 1561 |
+ * |
|
| 1562 |
+ * Where state_id is taken from the challenge request and response_text |
|
| 1563 |
+ * is what the user entered in response to the challenge_text. |
|
| 1564 |
+ * If the R flag is not present, response_text may be the empty |
|
| 1565 |
+ * string. |
|
| 1566 |
+ * |
|
| 1567 |
+ * Example response (suppose the user enters "8675309" for the token PIN): |
|
| 1568 |
+ * |
|
| 1569 |
+ * Username: cr1 ("Y3Ix" base64 decoded)
|
|
| 1570 |
+ * Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309 |
|
| 1571 |
+ */ |
|
| 1572 |
+struct auth_challenge_info * |
|
| 1573 |
+get_auth_challenge (const char *auth_challenge, struct gc_arena *gc) |
|
| 1574 |
+{
|
|
| 1575 |
+ if (auth_challenge) |
|
| 1576 |
+ {
|
|
| 1577 |
+ struct auth_challenge_info *ac; |
|
| 1578 |
+ const int len = strlen (auth_challenge); |
|
| 1579 |
+ char *work = (char *) gc_malloc (len+1, false, gc); |
|
| 1580 |
+ char *cp; |
|
| 1581 |
+ |
|
| 1582 |
+ struct buffer b; |
|
| 1583 |
+ buf_set_read (&b, (const uint8_t *)auth_challenge, len); |
|
| 1584 |
+ |
|
| 1585 |
+ ALLOC_OBJ_CLEAR_GC (ac, struct auth_challenge_info, gc); |
|
| 1586 |
+ |
|
| 1587 |
+ /* parse prefix */ |
|
| 1588 |
+ if (!buf_parse(&b, ':', work, len)) |
|
| 1589 |
+ return NULL; |
|
| 1590 |
+ if (strcmp(work, "CRV1")) |
|
| 1591 |
+ return NULL; |
|
| 1592 |
+ |
|
| 1593 |
+ /* parse flags */ |
|
| 1594 |
+ if (!buf_parse(&b, ':', work, len)) |
|
| 1595 |
+ return NULL; |
|
| 1596 |
+ for (cp = work; *cp != '\0'; ++cp) |
|
| 1597 |
+ {
|
|
| 1598 |
+ const char c = *cp; |
|
| 1599 |
+ if (c == 'E') |
|
| 1600 |
+ ac->flags |= CR_ECHO; |
|
| 1601 |
+ else if (c == 'R') |
|
| 1602 |
+ ac->flags |= CR_RESPONSE; |
|
| 1603 |
+ } |
|
| 1604 |
+ |
|
| 1605 |
+ /* parse state ID */ |
|
| 1606 |
+ if (!buf_parse(&b, ':', work, len)) |
|
| 1607 |
+ return NULL; |
|
| 1608 |
+ ac->state_id = string_alloc(work, gc); |
|
| 1609 |
+ |
|
| 1610 |
+ /* parse user name */ |
|
| 1611 |
+ if (!buf_parse(&b, ':', work, len)) |
|
| 1612 |
+ return NULL; |
|
| 1613 |
+ ac->user = (char *) gc_malloc (strlen(work)+1, true, gc); |
|
| 1614 |
+ base64_decode(work, (void*)ac->user); |
|
| 1615 |
+ |
|
| 1616 |
+ /* parse challenge text */ |
|
| 1617 |
+ ac->challenge_text = string_alloc(BSTR(&b), gc); |
|
| 1618 |
+ |
|
| 1619 |
+ return ac; |
|
| 1620 |
+ } |
|
| 1621 |
+ else |
|
| 1622 |
+ return NULL; |
|
| 1623 |
+} |
|
| 1624 |
+ |
|
| 1625 |
+#endif |
|
| 1626 |
+ |
|
| 1532 | 1627 |
#if AUTO_USERID |
| 1533 | 1628 |
|
| 1534 | 1629 |
static const char * |
| ... | ... |
@@ -252,6 +252,26 @@ struct user_pass |
| 252 | 252 |
char password[USER_PASS_LEN]; |
| 253 | 253 |
}; |
| 254 | 254 |
|
| 255 |
+#ifdef ENABLE_CLIENT_CR |
|
| 256 |
+/* |
|
| 257 |
+ * Challenge response info on client as pushed by server. |
|
| 258 |
+ */ |
|
| 259 |
+struct auth_challenge_info {
|
|
| 260 |
+# define CR_ECHO (1<<0) /* echo response when typed by user */ |
|
| 261 |
+# define CR_RESPONSE (1<<1) /* response needed */ |
|
| 262 |
+ unsigned int flags; |
|
| 263 |
+ |
|
| 264 |
+ const char *user; |
|
| 265 |
+ const char *state_id; |
|
| 266 |
+ const char *challenge_text; |
|
| 267 |
+}; |
|
| 268 |
+ |
|
| 269 |
+struct auth_challenge_info *get_auth_challenge (const char *auth_challenge, struct gc_arena *gc); |
|
| 270 |
+ |
|
| 271 |
+#else |
|
| 272 |
+struct auth_challenge_info {};
|
|
| 273 |
+#endif |
|
| 274 |
+ |
|
| 255 | 275 |
bool get_console_input (const char *prompt, const bool echo, char *input, const int capacity); |
| 256 | 276 |
|
| 257 | 277 |
/* |
| ... | ... |
@@ -265,10 +285,20 @@ bool get_console_input (const char *prompt, const bool echo, char *input, const |
| 265 | 265 |
#define GET_USER_PASS_NEED_STR (1<<5) |
| 266 | 266 |
#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6) |
| 267 | 267 |
|
| 268 |
-bool get_user_pass (struct user_pass *up, |
|
| 269 |
- const char *auth_file, |
|
| 270 |
- const char *prefix, |
|
| 271 |
- const unsigned int flags); |
|
| 268 |
+bool get_user_pass_cr (struct user_pass *up, |
|
| 269 |
+ const char *auth_file, |
|
| 270 |
+ const char *prefix, |
|
| 271 |
+ const unsigned int flags, |
|
| 272 |
+ const char *auth_challenge); |
|
| 273 |
+ |
|
| 274 |
+static inline bool |
|
| 275 |
+get_user_pass (struct user_pass *up, |
|
| 276 |
+ const char *auth_file, |
|
| 277 |
+ const char *prefix, |
|
| 278 |
+ const unsigned int flags) |
|
| 279 |
+{
|
|
| 280 |
+ return get_user_pass_cr (up, auth_file, prefix, flags, NULL); |
|
| 281 |
+} |
|
| 272 | 282 |
|
| 273 | 283 |
void fail_user_pass (const char *prefix, |
| 274 | 284 |
const unsigned int flags, |
| ... | ... |
@@ -68,8 +68,18 @@ receive_auth_failed (struct context *c, const struct buffer *buffer) |
| 68 | 68 |
if (buf_string_compare_advance (&buf, "AUTH_FAILED,") && BLEN (&buf)) |
| 69 | 69 |
reason = BSTR (&buf); |
| 70 | 70 |
management_auth_failure (management, UP_TYPE_AUTH, reason); |
| 71 |
- } |
|
| 71 |
+ } else |
|
| 72 | 72 |
#endif |
| 73 |
+ {
|
|
| 74 |
+#ifdef ENABLE_CLIENT_CR |
|
| 75 |
+ struct buffer buf = *buffer; |
|
| 76 |
+ if (buf_string_match_head_str (&buf, "AUTH_FAILED,CRV1:") && BLEN (&buf)) |
|
| 77 |
+ {
|
|
| 78 |
+ buf_advance (&buf, 12); /* Length of "AUTH_FAILED," substring */ |
|
| 79 |
+ ssl_put_auth_challenge (BSTR (&buf)); |
|
| 80 |
+ } |
|
| 81 |
+#endif |
|
| 82 |
+ } |
|
| 73 | 83 |
} |
| 74 | 84 |
} |
| 75 | 85 |
|
| ... | ... |
@@ -286,6 +286,10 @@ pem_password_callback (char *buf, int size, int rwflag, void *u) |
| 286 | 286 |
static bool auth_user_pass_enabled; /* GLOBAL */ |
| 287 | 287 |
static struct user_pass auth_user_pass; /* GLOBAL */ |
| 288 | 288 |
|
| 289 |
+#ifdef ENABLE_CLIENT_CR |
|
| 290 |
+static char *auth_challenge; /* GLOBAL */ |
|
| 291 |
+#endif |
|
| 292 |
+ |
|
| 289 | 293 |
void |
| 290 | 294 |
auth_user_pass_setup (const char *auth_file) |
| 291 | 295 |
{
|
| ... | ... |
@@ -294,6 +298,8 @@ auth_user_pass_setup (const char *auth_file) |
| 294 | 294 |
{
|
| 295 | 295 |
#if AUTO_USERID |
| 296 | 296 |
get_user_pass_auto_userid (&auth_user_pass, auth_file); |
| 297 |
+#elif defined(ENABLE_CLIENT_CR) |
|
| 298 |
+ get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge); |
|
| 297 | 299 |
#else |
| 298 | 300 |
get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE); |
| 299 | 301 |
#endif |
| ... | ... |
@@ -321,8 +327,29 @@ ssl_purge_auth (void) |
| 321 | 321 |
#endif |
| 322 | 322 |
purge_user_pass (&passbuf, true); |
| 323 | 323 |
purge_user_pass (&auth_user_pass, true); |
| 324 |
+#ifdef ENABLE_CLIENT_CR |
|
| 325 |
+ ssl_purge_auth_challenge(); |
|
| 326 |
+#endif |
|
| 327 |
+} |
|
| 328 |
+ |
|
| 329 |
+#ifdef ENABLE_CLIENT_CR |
|
| 330 |
+ |
|
| 331 |
+void |
|
| 332 |
+ssl_purge_auth_challenge (void) |
|
| 333 |
+{
|
|
| 334 |
+ free (auth_challenge); |
|
| 335 |
+ auth_challenge = NULL; |
|
| 324 | 336 |
} |
| 325 | 337 |
|
| 338 |
+void |
|
| 339 |
+ssl_put_auth_challenge (const char *cr_str) |
|
| 340 |
+{
|
|
| 341 |
+ ssl_purge_auth_challenge(); |
|
| 342 |
+ auth_challenge = string_alloc(cr_str, NULL); |
|
| 343 |
+} |
|
| 344 |
+ |
|
| 345 |
+#endif |
|
| 346 |
+ |
|
| 326 | 347 |
/* |
| 327 | 348 |
* OpenSSL callback to get a temporary RSA key, mostly |
| 328 | 349 |
* used for export ciphers. |
| ... | ... |
@@ -705,6 +705,17 @@ void auth_user_pass_setup (const char *auth_file); |
| 705 | 705 |
void ssl_set_auth_nocache (void); |
| 706 | 706 |
void ssl_purge_auth (void); |
| 707 | 707 |
|
| 708 |
+ |
|
| 709 |
+#ifdef ENABLE_CLIENT_CR |
|
| 710 |
+/* |
|
| 711 |
+ * ssl_get_auth_challenge will parse the server-pushed auth-failed |
|
| 712 |
+ * reason string and return a dynamically allocated |
|
| 713 |
+ * auth_challenge_info struct. |
|
| 714 |
+ */ |
|
| 715 |
+void ssl_purge_auth_challenge (void); |
|
| 716 |
+void ssl_put_auth_challenge (const char *cr_str); |
|
| 717 |
+#endif |
|
| 718 |
+ |
|
| 708 | 719 |
void tls_set_verify_command (const char *cmd); |
| 709 | 720 |
void tls_set_crl_verify (const char *crl); |
| 710 | 721 |
void tls_set_verify_x509name (const char *x509name); |
| ... | ... |
@@ -665,6 +665,11 @@ socket_defined (const socket_descriptor_t sd) |
| 665 | 665 |
#endif |
| 666 | 666 |
|
| 667 | 667 |
/* |
| 668 |
+ * Do we support challenge/response authentication, as a console-based client? |
|
| 669 |
+ */ |
|
| 670 |
+#define ENABLE_CLIENT_CR |
|
| 671 |
+ |
|
| 672 |
+/* |
|
| 668 | 673 |
* Do we support pushing peer info? |
| 669 | 674 |
*/ |
| 670 | 675 |
#define ENABLE_PUSH_PEER_INFO |