Conflicts:
version.m4
- Reset version.m4 to a more neutral version number
Signed-off-by: David Sommerseth <dazo@users.sourceforge.net>
| ... | ... |
@@ -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" |
| ... | ... |
@@ -1397,10 +1398,11 @@ get_console_input (const char *prompt, const bool echo, char *input, const int c |
| 1397 | 1397 |
*/ |
| 1398 | 1398 |
|
| 1399 | 1399 |
bool |
| 1400 |
-get_user_pass (struct user_pass *up, |
|
| 1401 |
- const char *auth_file, |
|
| 1402 |
- const char *prefix, |
|
| 1403 |
- const unsigned int flags) |
|
| 1400 |
+get_user_pass_cr (struct user_pass *up, |
|
| 1401 |
+ const char *auth_file, |
|
| 1402 |
+ const char *prefix, |
|
| 1403 |
+ const unsigned int flags, |
|
| 1404 |
+ const char *auth_challenge) |
|
| 1404 | 1405 |
{
|
| 1405 | 1406 |
struct gc_arena gc = gc_new (); |
| 1406 | 1407 |
|
| ... | ... |
@@ -1413,7 +1415,7 @@ get_user_pass (struct user_pass *up, |
| 1413 | 1413 |
|
| 1414 | 1414 |
#ifdef ENABLE_MANAGEMENT |
| 1415 | 1415 |
/* |
| 1416 |
- * Get username/password from standard input? |
|
| 1416 |
+ * Get username/password from management interface? |
|
| 1417 | 1417 |
*/ |
| 1418 | 1418 |
if (management |
| 1419 | 1419 |
&& ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT))) |
| ... | ... |
@@ -1453,22 +1455,47 @@ get_user_pass (struct user_pass *up, |
| 1453 | 1453 |
*/ |
| 1454 | 1454 |
else if (from_stdin) |
| 1455 | 1455 |
{
|
| 1456 |
- struct buffer user_prompt = alloc_buf_gc (128, &gc); |
|
| 1457 |
- struct buffer pass_prompt = alloc_buf_gc (128, &gc); |
|
| 1458 |
- |
|
| 1459 |
- buf_printf (&user_prompt, "Enter %s Username:", prefix); |
|
| 1460 |
- buf_printf (&pass_prompt, "Enter %s Password:", prefix); |
|
| 1461 |
- |
|
| 1462 |
- if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) |
|
| 1456 |
+#ifdef ENABLE_CLIENT_CR |
|
| 1457 |
+ if (auth_challenge) |
|
| 1463 | 1458 |
{
|
| 1464 |
- if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN)) |
|
| 1465 |
- msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix); |
|
| 1466 |
- if (strlen (up->username) == 0) |
|
| 1467 |
- msg (M_FATAL, "ERROR: %s username is empty", prefix); |
|
| 1459 |
+ struct auth_challenge_info *ac = get_auth_challenge (auth_challenge, &gc); |
|
| 1460 |
+ if (ac) |
|
| 1461 |
+ {
|
|
| 1462 |
+ char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc); |
|
| 1463 |
+ struct buffer packed_resp; |
|
| 1464 |
+ |
|
| 1465 |
+ buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN); |
|
| 1466 |
+ msg (M_INFO, "CHALLENGE: %s", ac->challenge_text); |
|
| 1467 |
+ if (!get_console_input ("Response:", BOOL_CAST(ac->flags&CR_ECHO), response, USER_PASS_LEN))
|
|
| 1468 |
+ msg (M_FATAL, "ERROR: could not read challenge response from stdin"); |
|
| 1469 |
+ strncpynt (up->username, ac->user, USER_PASS_LEN); |
|
| 1470 |
+ buf_printf (&packed_resp, "CRV1::%s::%s", ac->state_id, response); |
|
| 1471 |
+ } |
|
| 1472 |
+ else |
|
| 1473 |
+ {
|
|
| 1474 |
+ msg (M_FATAL, "ERROR: received malformed challenge request from server"); |
|
| 1475 |
+ } |
|
| 1468 | 1476 |
} |
| 1477 |
+ else |
|
| 1478 |
+#endif |
|
| 1479 |
+ {
|
|
| 1480 |
+ struct buffer user_prompt = alloc_buf_gc (128, &gc); |
|
| 1481 |
+ struct buffer pass_prompt = alloc_buf_gc (128, &gc); |
|
| 1469 | 1482 |
|
| 1470 |
- if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) |
|
| 1471 |
- msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix); |
|
| 1483 |
+ buf_printf (&user_prompt, "Enter %s Username:", prefix); |
|
| 1484 |
+ buf_printf (&pass_prompt, "Enter %s Password:", prefix); |
|
| 1485 |
+ |
|
| 1486 |
+ if (!(flags & GET_USER_PASS_PASSWORD_ONLY)) |
|
| 1487 |
+ {
|
|
| 1488 |
+ if (!get_console_input (BSTR (&user_prompt), true, up->username, USER_PASS_LEN)) |
|
| 1489 |
+ msg (M_FATAL, "ERROR: could not read %s username from stdin", prefix); |
|
| 1490 |
+ if (strlen (up->username) == 0) |
|
| 1491 |
+ msg (M_FATAL, "ERROR: %s username is empty", prefix); |
|
| 1492 |
+ } |
|
| 1493 |
+ |
|
| 1494 |
+ if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN)) |
|
| 1495 |
+ msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix); |
|
| 1496 |
+ } |
|
| 1472 | 1497 |
} |
| 1473 | 1498 |
else |
| 1474 | 1499 |
{
|
| ... | ... |
@@ -1532,6 +1559,101 @@ get_user_pass (struct user_pass *up, |
| 1532 | 1532 |
return true; |
| 1533 | 1533 |
} |
| 1534 | 1534 |
|
| 1535 |
+#ifdef ENABLE_CLIENT_CR |
|
| 1536 |
+ |
|
| 1537 |
+/* |
|
| 1538 |
+ * Parse a challenge message returned along with AUTH_FAILED. |
|
| 1539 |
+ * The message is formatted as such: |
|
| 1540 |
+ * |
|
| 1541 |
+ * CRV1:<flags>:<state_id>:<username_base64>:<challenge_text> |
|
| 1542 |
+ * |
|
| 1543 |
+ * flags: a series of optional, comma-separated flags: |
|
| 1544 |
+ * E : echo the response when the user types it |
|
| 1545 |
+ * R : a response is required |
|
| 1546 |
+ * |
|
| 1547 |
+ * state_id: an opaque string that should be returned to the server |
|
| 1548 |
+ * along with the response. |
|
| 1549 |
+ * |
|
| 1550 |
+ * username_base64 : the username formatted as base64 |
|
| 1551 |
+ * |
|
| 1552 |
+ * challenge_text : the challenge text to be shown to the user |
|
| 1553 |
+ * |
|
| 1554 |
+ * Example challenge: |
|
| 1555 |
+ * |
|
| 1556 |
+ * CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN |
|
| 1557 |
+ * |
|
| 1558 |
+ * After showing the challenge_text and getting a response from the user |
|
| 1559 |
+ * (if R flag is specified), the client should submit the following |
|
| 1560 |
+ * auth creds back to the OpenVPN server: |
|
| 1561 |
+ * |
|
| 1562 |
+ * Username: [username decoded from username_base64] |
|
| 1563 |
+ * Password: CRV1::<state_id>::<response_text> |
|
| 1564 |
+ * |
|
| 1565 |
+ * Where state_id is taken from the challenge request and response_text |
|
| 1566 |
+ * is what the user entered in response to the challenge_text. |
|
| 1567 |
+ * If the R flag is not present, response_text may be the empty |
|
| 1568 |
+ * string. |
|
| 1569 |
+ * |
|
| 1570 |
+ * Example response (suppose the user enters "8675309" for the token PIN): |
|
| 1571 |
+ * |
|
| 1572 |
+ * Username: cr1 ("Y3Ix" base64 decoded)
|
|
| 1573 |
+ * Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309 |
|
| 1574 |
+ */ |
|
| 1575 |
+struct auth_challenge_info * |
|
| 1576 |
+get_auth_challenge (const char *auth_challenge, struct gc_arena *gc) |
|
| 1577 |
+{
|
|
| 1578 |
+ if (auth_challenge) |
|
| 1579 |
+ {
|
|
| 1580 |
+ struct auth_challenge_info *ac; |
|
| 1581 |
+ const int len = strlen (auth_challenge); |
|
| 1582 |
+ char *work = (char *) gc_malloc (len+1, false, gc); |
|
| 1583 |
+ char *cp; |
|
| 1584 |
+ |
|
| 1585 |
+ struct buffer b; |
|
| 1586 |
+ buf_set_read (&b, (const uint8_t *)auth_challenge, len); |
|
| 1587 |
+ |
|
| 1588 |
+ ALLOC_OBJ_CLEAR_GC (ac, struct auth_challenge_info, gc); |
|
| 1589 |
+ |
|
| 1590 |
+ /* parse prefix */ |
|
| 1591 |
+ if (!buf_parse(&b, ':', work, len)) |
|
| 1592 |
+ return NULL; |
|
| 1593 |
+ if (strcmp(work, "CRV1")) |
|
| 1594 |
+ return NULL; |
|
| 1595 |
+ |
|
| 1596 |
+ /* parse flags */ |
|
| 1597 |
+ if (!buf_parse(&b, ':', work, len)) |
|
| 1598 |
+ return NULL; |
|
| 1599 |
+ for (cp = work; *cp != '\0'; ++cp) |
|
| 1600 |
+ {
|
|
| 1601 |
+ const char c = *cp; |
|
| 1602 |
+ if (c == 'E') |
|
| 1603 |
+ ac->flags |= CR_ECHO; |
|
| 1604 |
+ else if (c == 'R') |
|
| 1605 |
+ ac->flags |= CR_RESPONSE; |
|
| 1606 |
+ } |
|
| 1607 |
+ |
|
| 1608 |
+ /* parse state ID */ |
|
| 1609 |
+ if (!buf_parse(&b, ':', work, len)) |
|
| 1610 |
+ return NULL; |
|
| 1611 |
+ ac->state_id = string_alloc(work, gc); |
|
| 1612 |
+ |
|
| 1613 |
+ /* parse user name */ |
|
| 1614 |
+ if (!buf_parse(&b, ':', work, len)) |
|
| 1615 |
+ return NULL; |
|
| 1616 |
+ ac->user = (char *) gc_malloc (strlen(work)+1, true, gc); |
|
| 1617 |
+ base64_decode(work, (void*)ac->user); |
|
| 1618 |
+ |
|
| 1619 |
+ /* parse challenge text */ |
|
| 1620 |
+ ac->challenge_text = string_alloc(BSTR(&b), gc); |
|
| 1621 |
+ |
|
| 1622 |
+ return ac; |
|
| 1623 |
+ } |
|
| 1624 |
+ else |
|
| 1625 |
+ return NULL; |
|
| 1626 |
+} |
|
| 1627 |
+ |
|
| 1628 |
+#endif |
|
| 1629 |
+ |
|
| 1535 | 1630 |
#if AUTO_USERID |
| 1536 | 1631 |
|
| 1537 | 1632 |
static const char * |
| ... | ... |
@@ -261,6 +261,26 @@ struct user_pass |
| 261 | 261 |
char password[USER_PASS_LEN]; |
| 262 | 262 |
}; |
| 263 | 263 |
|
| 264 |
+#ifdef ENABLE_CLIENT_CR |
|
| 265 |
+/* |
|
| 266 |
+ * Challenge response info on client as pushed by server. |
|
| 267 |
+ */ |
|
| 268 |
+struct auth_challenge_info {
|
|
| 269 |
+# define CR_ECHO (1<<0) /* echo response when typed by user */ |
|
| 270 |
+# define CR_RESPONSE (1<<1) /* response needed */ |
|
| 271 |
+ unsigned int flags; |
|
| 272 |
+ |
|
| 273 |
+ const char *user; |
|
| 274 |
+ const char *state_id; |
|
| 275 |
+ const char *challenge_text; |
|
| 276 |
+}; |
|
| 277 |
+ |
|
| 278 |
+struct auth_challenge_info *get_auth_challenge (const char *auth_challenge, struct gc_arena *gc); |
|
| 279 |
+ |
|
| 280 |
+#else |
|
| 281 |
+struct auth_challenge_info {};
|
|
| 282 |
+#endif |
|
| 283 |
+ |
|
| 264 | 284 |
bool get_console_input (const char *prompt, const bool echo, char *input, const int capacity); |
| 265 | 285 |
|
| 266 | 286 |
/* |
| ... | ... |
@@ -274,10 +294,20 @@ bool get_console_input (const char *prompt, const bool echo, char *input, const |
| 274 | 274 |
#define GET_USER_PASS_NEED_STR (1<<5) |
| 275 | 275 |
#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6) |
| 276 | 276 |
|
| 277 |
-bool get_user_pass (struct user_pass *up, |
|
| 278 |
- const char *auth_file, |
|
| 279 |
- const char *prefix, |
|
| 280 |
- const unsigned int flags); |
|
| 277 |
+bool get_user_pass_cr (struct user_pass *up, |
|
| 278 |
+ const char *auth_file, |
|
| 279 |
+ const char *prefix, |
|
| 280 |
+ const unsigned int flags, |
|
| 281 |
+ const char *auth_challenge); |
|
| 282 |
+ |
|
| 283 |
+static inline bool |
|
| 284 |
+get_user_pass (struct user_pass *up, |
|
| 285 |
+ const char *auth_file, |
|
| 286 |
+ const char *prefix, |
|
| 287 |
+ const unsigned int flags) |
|
| 288 |
+{
|
|
| 289 |
+ return get_user_pass_cr (up, auth_file, prefix, flags, NULL); |
|
| 290 |
+} |
|
| 281 | 291 |
|
| 282 | 292 |
void fail_user_pass (const char *prefix, |
| 283 | 293 |
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 |
|
| ... | ... |
@@ -290,6 +290,10 @@ pem_password_callback (char *buf, int size, int rwflag, void *u) |
| 290 | 290 |
static bool auth_user_pass_enabled; /* GLOBAL */ |
| 291 | 291 |
static struct user_pass auth_user_pass; /* GLOBAL */ |
| 292 | 292 |
|
| 293 |
+#ifdef ENABLE_CLIENT_CR |
|
| 294 |
+static char *auth_challenge; /* GLOBAL */ |
|
| 295 |
+#endif |
|
| 296 |
+ |
|
| 293 | 297 |
void |
| 294 | 298 |
auth_user_pass_setup (const char *auth_file) |
| 295 | 299 |
{
|
| ... | ... |
@@ -298,6 +302,8 @@ auth_user_pass_setup (const char *auth_file) |
| 298 | 298 |
{
|
| 299 | 299 |
#if AUTO_USERID |
| 300 | 300 |
get_user_pass_auto_userid (&auth_user_pass, auth_file); |
| 301 |
+#elif defined(ENABLE_CLIENT_CR) |
|
| 302 |
+ get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge); |
|
| 301 | 303 |
#else |
| 302 | 304 |
get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE); |
| 303 | 305 |
#endif |
| ... | ... |
@@ -325,8 +331,29 @@ ssl_purge_auth (void) |
| 325 | 325 |
#endif |
| 326 | 326 |
purge_user_pass (&passbuf, true); |
| 327 | 327 |
purge_user_pass (&auth_user_pass, true); |
| 328 |
+#ifdef ENABLE_CLIENT_CR |
|
| 329 |
+ ssl_purge_auth_challenge(); |
|
| 330 |
+#endif |
|
| 331 |
+} |
|
| 332 |
+ |
|
| 333 |
+#ifdef ENABLE_CLIENT_CR |
|
| 334 |
+ |
|
| 335 |
+void |
|
| 336 |
+ssl_purge_auth_challenge (void) |
|
| 337 |
+{
|
|
| 338 |
+ free (auth_challenge); |
|
| 339 |
+ auth_challenge = NULL; |
|
| 328 | 340 |
} |
| 329 | 341 |
|
| 342 |
+void |
|
| 343 |
+ssl_put_auth_challenge (const char *cr_str) |
|
| 344 |
+{
|
|
| 345 |
+ ssl_purge_auth_challenge(); |
|
| 346 |
+ auth_challenge = string_alloc(cr_str, NULL); |
|
| 347 |
+} |
|
| 348 |
+ |
|
| 349 |
+#endif |
|
| 350 |
+ |
|
| 330 | 351 |
/* |
| 331 | 352 |
* OpenSSL callback to get a temporary RSA key, mostly |
| 332 | 353 |
* used for export ciphers. |
| ... | ... |
@@ -709,6 +709,17 @@ void auth_user_pass_setup (const char *auth_file); |
| 709 | 709 |
void ssl_set_auth_nocache (void); |
| 710 | 710 |
void ssl_purge_auth (void); |
| 711 | 711 |
|
| 712 |
+ |
|
| 713 |
+#ifdef ENABLE_CLIENT_CR |
|
| 714 |
+/* |
|
| 715 |
+ * ssl_get_auth_challenge will parse the server-pushed auth-failed |
|
| 716 |
+ * reason string and return a dynamically allocated |
|
| 717 |
+ * auth_challenge_info struct. |
|
| 718 |
+ */ |
|
| 719 |
+void ssl_purge_auth_challenge (void); |
|
| 720 |
+void ssl_put_auth_challenge (const char *cr_str); |
|
| 721 |
+#endif |
|
| 722 |
+ |
|
| 712 | 723 |
void tls_set_verify_command (const char *cmd); |
| 713 | 724 |
void tls_set_crl_verify (const char *crl); |
| 714 | 725 |
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 |