Browse code

Merge branch 'svn-BETA21' into bugfix2.1

David Sommerseth authored on 2010/11/13 08:46:13
Showing 9 changed files
... ...
@@ -33,7 +33,7 @@
33 33
 
34 34
 #include "syshead.h"
35 35
 
36
-#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11)
36
+#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_PKCS11) || defined(ENABLE_CLIENT_CR)
37 37
 
38 38
 #include "base64.h"
39 39
 
... ...
@@ -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
... ...
@@ -1,5 +1,5 @@
1 1
 dnl define the OpenVPN version
2
-define(PRODUCT_VERSION,[2.1.3a])
2
+define(PRODUCT_VERSION,[2.1.3b])
3 3
 dnl define the TAP version
4 4
 define(PRODUCT_TAP_ID,[tap0901])
5 5
 define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])