Implement TLS-PSK.
authorLinus Nordberg <linus@nordu.net>
Tue, 31 Jan 2012 12:15:20 +0000 (13:15 +0100)
committerLinus Nordberg <linus@nordu.net>
Tue, 31 Jan 2012 12:15:20 +0000 (13:15 +0100)
lib/conf.c
lib/examples/client.conf
lib/include/radsec/radsec-impl.h
lib/include/radsec/radsec.h
lib/tls.c

index e813409..37fd9b4 100644 (file)
@@ -26,7 +26,8 @@
       #cacertpath = STRING
       certfile = STRING
       certkeyfile = STRING
-      psk = STRING             # Transport pre-shared key.
+      pskstr = STRING  # Transport pre-shared key, ASCII (UTF-8?) string form.
+      pskhexstr = STRING # Transport pre-shared key, hexadecimal string form.
       pskid = STRING
       pskex = "PSK"|"DHE_PSK"|"RSA_PSK"
   }
@@ -67,7 +68,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
       /*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/
       CFG_STR ("certfile", NULL, CFGF_NONE),
       CFG_STR ("certkeyfile", NULL, CFGF_NONE),
-      CFG_STR ("psk", NULL, CFGF_NONE),
+      CFG_STR ("pskstr", NULL, CFGF_NONE),
+      CFG_STR ("pskhexstr", NULL, CFGF_NONE),
       CFG_STR ("pskid", NULL, CFGF_NONE),
       CFG_STR ("pskex", "PSK", CFGF_NONE),
       CFG_SEC ("server", server_opts, CFGF_MULTI),
@@ -110,7 +112,7 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
     {
       struct rs_realm *r = NULL;
       const char *typestr;
-      char *psk;
+      char *pskstr = NULL, *pskhexstr = NULL;
 
       r = rs_calloc (ctx, 1, sizeof(*r));
       if (r == NULL)
@@ -154,8 +156,9 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
       r->certfile = cfg_getstr (cfg_realm, "certfile");
       r->certkeyfile = cfg_getstr (cfg_realm, "certkeyfile");
 
-      psk = cfg_getstr (cfg_realm, "psk");
-      if (psk)
+      pskstr = cfg_getstr (cfg_realm, "pskstr");
+      pskhexstr = cfg_getstr (cfg_realm, "pskhexstr");
+      if (pskstr || pskhexstr)
         {
           char *kex = cfg_getstr (cfg_realm, "pskex");
           rs_cred_type_t type = RS_CRED_NONE;
@@ -180,7 +183,19 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
                                            NULL);
               cred->type = type;
               cred->identity = cfg_getstr (cfg_realm, "pskid");
-              cred->secret = psk;
+              if (pskhexstr)
+                {
+                  cred->secret_encoding = RS_KEY_ENCODING_ASCII_HEX;
+                  cred->secret = pskhexstr;
+                  if (pskstr)
+                    ;      /* TODO: warn that we're ignoring pskstr */
+                }
+              else
+                {
+                  cred->secret_encoding = RS_KEY_ENCODING_UTF8;
+                  cred->secret = pskstr;
+                }
+
               r->transport_cred = cred;
             }
         }
index edd090e..cedd259 100644 (file)
@@ -1,4 +1,4 @@
-dictionary = "/usr/share/freeradius/dictionary"
+dictionary = "/home/linus/usr/moonshot/share/freeradius/dictionary"
 
 realm blocking-udp {
     type = "UDP"
@@ -18,8 +18,9 @@ realm blocking-tls {
     cacertfile = "tests/demoCA/newcerts/01.pem"
     certfile = "tests/demoCA/newcerts/02.pem"
     certkeyfile = "tests/demoCA/private/c2key.pem"
-    psk = "sikrit psk"
-    pskid = "allan"
+    #pskstr = "sikrit psk"
+    pskhexstr = "deadbeef4711"
+    pskid = "Client_identity"
     pskex = "PSK"
     server {
         hostname = "localhost"
index 01288d3..59cb8bf 100644 (file)
@@ -23,6 +23,12 @@ enum rs_cred_type {
 };
 typedef unsigned int rs_cred_type_t;
 
+enum rs_key_encoding {
+    RS_KEY_ENCODING_UTF8 = 1,
+    RS_KEY_ENCODING_ASCII_HEX = 2,
+};
+typedef unsigned int rs_key_encoding_t;
+
 #if defined (__cplusplus)
 extern "C" {
 #endif
@@ -31,6 +37,8 @@ struct rs_credentials {
     enum rs_cred_type type;
     char *identity;
     char *secret;
+    enum rs_key_encoding secret_encoding;
+    unsigned int secret_len;
 };
 
 struct rs_error {
index 2744cd2..abaa6e2 100644 (file)
@@ -34,6 +34,7 @@ enum rs_error_code {
     RSE_TIMEOUT_IO = 18,       /* I/O timeout.  */
     RSE_TIMEOUT = 19,          /* High level timeout.  */
     RSE_DISCO = 20,
+    RSE_CRED = 21,              /* Credentials.  */
 };
 
 enum rs_conn_type {
index 6fcf5a0..9427f78 100644 (file)
--- a/lib/tls.c
+++ b/lib/tls.c
@@ -8,6 +8,7 @@
 #include <assert.h>
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#include <openssl/bn.h>
 #include <radsec/radsec.h>
 #include <radsec/radsec-impl.h>
 
@@ -41,6 +42,72 @@ _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm)
   return c;
 }
 
+static unsigned int
+psk_client_cb (SSL *ssl,
+               const char *hint,
+               char *identity,
+               unsigned int max_identity_len,
+               unsigned char *psk,
+               unsigned int max_psk_len)
+{
+  struct rs_connection *conn = NULL;
+  struct rs_credentials *cred = NULL;
+
+  conn = SSL_get_ex_data (ssl, 0);
+  assert (conn != NULL);
+  cred = conn->active_peer->realm->transport_cred;
+  assert (cred != NULL);
+  /* NOTE: Ignoring identity hint from server.  */
+
+  if (strlen (cred->identity) + 1 > max_identity_len)
+    {
+      rs_err_conn_push (conn, RSE_CRED, "PSK identity longer than max %d",
+                        max_identity_len - 1);
+      return 0;
+    }
+  strcpy (identity, cred->identity);
+
+  switch (cred->secret_encoding)
+    {
+    case RS_KEY_ENCODING_UTF8:
+      cred->secret_len = strlen (cred->secret);
+      if (cred->secret_len > max_psk_len)
+        {
+          rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d",
+                            max_psk_len);
+          return 0;
+        }
+      memcpy (psk, cred->secret, cred->secret_len);
+      break;
+    case RS_KEY_ENCODING_ASCII_HEX:
+      {
+        BIGNUM *bn = NULL;
+
+        if (BN_hex2bn (&bn, cred->secret) == 0)
+          {
+            rs_err_conn_push (conn, RSE_CRED, "Unable to convert pskhexstr");
+            if (bn != NULL)
+              BN_clear_free (bn);
+            return 0;
+          }
+        if ((unsigned int) BN_num_bytes (bn) > max_psk_len)
+          {
+            rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d",
+                             max_psk_len);
+            BN_clear_free (bn);
+            return 0;
+          }
+        cred->secret_len = BN_bn2bin (bn, psk);
+        BN_clear_free (bn);
+      }
+      break;
+    default:
+      assert (!"unknown psk encoding");
+    }
+
+  return cred->secret_len;
+}
+
 int
 rs_tls_init (struct rs_connection *conn)
 {
@@ -73,6 +140,11 @@ rs_tls_init (struct rs_connection *conn)
       return -1;
     }
 
+  if (conn->active_peer->realm->transport_cred != NULL)
+    {
+      SSL_set_psk_client_callback (ssl, psk_client_cb);
+      SSL_set_ex_data (ssl, 0, conn);
+    }
   conn->tls_ctx = ssl_ctx;
   conn->tls_ssl = ssl;
   rs_free (ctx, tlsconf);