Complete.Org: Mailing Lists: Archives: freeciv-dev: October 2005:
[Freeciv-Dev] (PR#14350) RSA based authentication
Home

[Freeciv-Dev] (PR#14350) RSA based authentication

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: [Freeciv-Dev] (PR#14350) RSA based authentication
From: "Mateusz Stefek" <mstefek@xxxxxxxxx>
Date: Sun, 16 Oct 2005 01:23:51 -0700
Reply-to: bugs@xxxxxxxxxxx

<URL: http://bugs.freeciv.org/Ticket/Display.html?id=14350 >

This patch encrypts passwords sent to the server using RSA algorithm and
openSSL library.

The patch misses a feature of reading a key from external file.
Currently the key is regenerated every time the server is run.
--
mateusz
Index: client/packhand_gen.c
===================================================================
--- client/packhand_gen.c       (wersja 11144)
+++ client/packhand_gen.c       (kopia robocza)
@@ -45,9 +45,7 @@
     return TRUE;
 
   case PACKET_AUTHENTICATION_REQ:
-    handle_authentication_req(
-      ((struct packet_authentication_req *)packet)->type,
-      ((struct packet_authentication_req *)packet)->message);
+    handle_authentication_req(packet);
     return TRUE;
 
   case PACKET_SERVER_SHUTDOWN:
Index: client/packhand_gen.h
===================================================================
--- client/packhand_gen.h       (wersja 11144)
+++ client/packhand_gen.h       (kopia robocza)
@@ -20,7 +20,8 @@
 void handle_freeze_hint(void);
 void handle_thaw_hint(void);
 void handle_server_join_reply(bool you_can_join, char *message, char 
*capability, char *challenge_file, int conn_id);
-void handle_authentication_req(enum authentication_type type, char *message);
+struct packet_authentication_req;
+void handle_authentication_req(struct packet_authentication_req *packet);
 void handle_server_shutdown(void);
 void handle_game_state(int value);
 struct packet_endgame_report;
Index: client/gui-gtk-2.0/pages.c
===================================================================
--- client/gui-gtk-2.0/pages.c  (wersja 11144)
+++ client/gui-gtk-2.0/pages.c  (kopia robocza)
@@ -520,11 +520,12 @@
  configure the dialog depending on what type of authentication request the
  server is making.
 **************************************************************************/
-void handle_authentication_req(enum authentication_type type, char *message)
+void handle_authentication_req(struct packet_authentication_req *req)
 {
-  append_network_statusbar(message, TRUE);
+  append_network_statusbar(req->message, TRUE);
 
-  switch (type) {
+  unserialize_RSA_key(req);
+  switch (req->type) {
   case AUTH_NEWUSER_FIRST:
   case AUTH_NEWUSER_RETRY:
     set_connection_state(NEW_PASSWORD_TYPE);
@@ -533,10 +534,7 @@
     /* if we magically have a password already present in 'password'
      * then, use that and skip the password entry dialog */
     if (password[0] != '\0') {
-      struct packet_authentication_reply reply;
-
-      sz_strlcpy(reply.password, password);
-      send_packet_authentication_reply(&aconnection, &reply);
+      send_authentication_reply(password);
       return;
     } else {
       set_connection_state(ENTER_PASSWORD_TYPE);
@@ -558,8 +556,7 @@
 static void connect_callback(GtkWidget *w, gpointer data)
 {
   char errbuf [512];
-  struct packet_authentication_reply reply;
-
+  
   switch (connection_status) {
   case LOGIN_TYPE:
     sz_strlcpy(user_name, gtk_entry_get_text(GTK_ENTRY(network_login)));
@@ -576,13 +573,14 @@
     break; 
   case NEW_PASSWORD_TYPE:
     if (w != network_password) {
+      char password2[MAX_LEN_PASSWORD];
       sz_strlcpy(password,
          gtk_entry_get_text(GTK_ENTRY(network_password)));
-      sz_strlcpy(reply.password,
+      sz_strlcpy(password2,
          gtk_entry_get_text(GTK_ENTRY(network_confirm_password)));
-      if (strncmp(reply.password, password, MAX_LEN_NAME) == 0) {
+      if (strncmp(password2, password, MAX_LEN_NAME) == 0) {
        password[0] = '\0';
-       send_packet_authentication_reply(&aconnection, &reply);
+       send_authentication_reply(password2);
 
        set_connection_state(WAITING_TYPE);
       } else { 
@@ -594,9 +592,10 @@
     }
     break;
   case ENTER_PASSWORD_TYPE:
-    sz_strlcpy(reply.password,
+    sz_strlcpy(password,
        gtk_entry_get_text(GTK_ENTRY(network_password)));
-    send_packet_authentication_reply(&aconnection, &reply);
+    send_authentication_reply(password);
+    password[0] = '\0';
 
     set_connection_state(WAITING_TYPE);
     break;
Index: client/connectdlg_common.c
===================================================================
--- client/connectdlg_common.c  (wersja 11144)
+++ client/connectdlg_common.c  (kopia robocza)
@@ -20,6 +20,7 @@
 #include <signal.h>             /* SIGTERM and kill */
 #include <string.h>
 #include <time.h>
+#include <openssl/rsa.h>
 
 #ifdef WIN32_NATIVE
 #include <windows.h>
@@ -543,3 +544,48 @@
   freelog(LOG_DEBUG, "Executing '%s'", buf);
   send_chat(buf);
 }
+
+static RSA* server_public_key;
+static int nonce;
+/***************************************************************************
+  Unserialize server public key and nonce from 
+  authentication request into server_public_key and nonce variables.
+***************************************************************************/
+void unserialize_RSA_key(struct packet_authentication_req* req)
+{
+  if (server_public_key != NULL) {
+    RSA_free(server_public_key);
+  }
+
+  server_public_key = RSA_new();
+  server_public_key->n =
+    BN_bin2bn(req->public_modulus, req->public_modulus_length, NULL);
+
+  server_public_key->e =
+    BN_bin2bn(req->public_exponent, req->public_exponent_length, NULL);
+
+  nonce = req->nonce;
+}
+
+/***************************************************************************
+  Send encrypted password concatenated with '|' and nonce
+***************************************************************************/
+void send_authentication_reply(const char* password)
+{
+  struct packet_authentication_reply reply;
+  char buf[MAX_LEN_PASSWORD + 12];
+  
+  my_snprintf(buf, sizeof(buf), "%s|%d", password, nonce);
+  reply.ciphertext_length = 
+    RSA_public_encrypt(strlen(buf) + 1, buf, reply.ciphertext,
+                       server_public_key,
+                       RSA_PKCS1_OAEP_PADDING);
+  if (reply.ciphertext_length == -1) {
+    freelog(LOG_ERROR, "RSA_public_encrypt_error");
+    return;
+  }
+  RSA_free(server_public_key);
+  server_public_key = NULL;
+
+  send_packet_authentication_reply(&aconnection, &reply);
+}
Index: client/connectdlg_common.h
===================================================================
--- client/connectdlg_common.h  (wersja 11144)
+++ client/connectdlg_common.h  (kopia robocza)
@@ -47,4 +47,7 @@
 
 extern const char *skill_level_names[NUM_SKILL_LEVELS];
 
+void unserialize_RSA_key(struct packet_authentication_req* req);
+void send_authentication_reply(const char* password);
+
 #endif  /* FC__CONNECTDLG_COMMON_H */ 
Index: server/srv_main.c
===================================================================
--- server/srv_main.c   (wersja 11144)
+++ server/srv_main.c   (kopia robocza)
@@ -951,7 +951,9 @@
   if (type == PACKET_AUTHENTICATION_REPLY) {
     return handle_authentication_reply(pconn,
                                ((struct packet_authentication_reply *)
-                                packet)->password);
+                                packet)->ciphertext,
+                               ((struct packet_authentication_reply *)
+                                packet)->ciphertext_length);
   }
 
   if (type == PACKET_CONN_PONG) {
Index: server/connecthand.c
===================================================================
--- server/connecthand.c        (wersja 11144)
+++ server/connecthand.c        (kopia robocza)
@@ -16,6 +16,10 @@
 #endif
 
 #include <string.h>
+#include <stdlib.h>
+#include <openssl/rsa.h>
+#include <openssl/bn.h>
+#include <openssl/engine.h>
 
 #include "capability.h"
 #include "capstr.h"
@@ -36,6 +40,7 @@
 #include "maphand.h"
 #include "meta.h"
 #include "plrhand.h"
+#include "rand.h"
 #include "ruleset.h"
 #include "sernet.h"
 #include "srv_main.h"
@@ -220,6 +225,40 @@
   flush_connection_send_buffer_all(pconn);
 }
 
+/****************************************************************************
+ Generate random number for authentication and store it in pconn and req.
+****************************************************************************/
+static void generate_nonce(struct packet_authentication_req* req,
+                           struct connection *pconn)
+{
+  unsigned int nonce;
+  RAND_bytes((unsigned char*)&nonce, sizeof(nonce));
+  nonce %= 500000000;
+  req->nonce = nonce;
+  pconn->server.nonce = nonce;
+}
+
+/* Server's private key */
+static RSA* private_key;
+
+/***************************************************************************
+  Serialize server's private key into packet_authentication_req structure.
+***************************************************************************/
+static void serialize_public_key(struct packet_authentication_req* packet)
+{
+  if (private_key == NULL) {
+    ERR_load_crypto_strings();
+    int t = time(NULL);
+    RAND_add(&t, sizeof(t), 1.0);
+    private_key = RSA_generate_key(1024, 17, NULL, NULL);
+  }
+
+  packet->public_exponent_length = BN_num_bytes(private_key->e);
+  BN_bn2bin(private_key->e, packet->public_exponent);
+  packet->public_modulus_length = BN_num_bytes(private_key->n);
+  BN_bn2bin(private_key->n, packet->public_modulus);
+}
+
 /**************************************************************************
  Returns FALSE if the clients gets rejected and the connection should be
  closed. Returns TRUE if the client get accepted.
@@ -228,7 +267,12 @@
                           struct packet_server_join_req *req)
 {
   char msg[MAX_LEN_MSG];
+  struct packet_authentication_req auth_req;
   
+  auth_req.public_modulus_length = 0;
+  auth_req.public_exponent_length = 0;
+  auth_req.nonce = 0;
+  
   freelog(LOG_NORMAL, _("Connection request from %s from %s"),
           req->username, pconn->addr);
   
@@ -318,7 +362,6 @@
       /* we are not a guest, we need an extra check as to whether a 
        * connection can be established: the client must authenticate itself */
       char tmpname[MAX_LEN_NAME] = "\0";
-      char buffer[MAX_LEN_MSG];
 
       sz_strlcpy(pconn->username, req->username);
 
@@ -343,17 +386,28 @@
         }
         break;
       case USER_DB_SUCCESS:
+      
         /* we found a user */
-        my_snprintf(buffer, sizeof(buffer), _("Enter password for %s:"),
-                    pconn->username);
-        dsend_packet_authentication_req(pconn, AUTH_LOGIN_FIRST, buffer);
+        my_snprintf(auth_req.message, sizeof(auth_req.message),
+                   _("Enter password for %s:"),
+                   pconn->username);
+       generate_nonce(&auth_req, pconn);
+       serialize_public_key(&auth_req);
+
+       auth_req.type = AUTH_LOGIN_FIRST;
+        send_packet_authentication_req(pconn, &auth_req);
         pconn->server.status = AS_REQUESTING_OLD_PASS;
         break;
       case USER_DB_NOT_FOUND:
+        auth_req.type = AUTH_NEWUSER_FIRST;
+
+       generate_nonce(&auth_req, pconn);
+       serialize_public_key(&auth_req);
         /* we couldn't find the user, he is new */
         if (srvarg.auth_allow_newusers) {
-          sz_strlcpy(buffer, _("Enter a new password (and remember it)."));
-          dsend_packet_authentication_req(pconn, AUTH_NEWUSER_FIRST, buffer);
+          sz_strlcpy(auth_req.message,
+                    _("Enter a new password (and remember it)."));
+          send_packet_authentication_req(pconn, &auth_req);
           pconn->server.status = AS_REQUESTING_NEW_PASS;
         } else {
           reject_new_connection(_("This server allows only preregistered "
@@ -388,7 +442,8 @@
     close_connection(pconn);
   } else {
     struct packet_authentication_req request;
-
+    generate_nonce(&request, pconn);
+    serialize_public_key(&request);
     pconn->server.status = AS_REQUESTING_OLD_PASS;
     request.type = AUTH_LOGIN_RETRY;
     sz_strlcpy(request.message,
@@ -400,26 +455,64 @@
 /**************************************************************************
   Receives a password from a client and verifies it.
 **************************************************************************/
-bool handle_authentication_reply(struct connection *pconn, char *password)
+bool handle_authentication_reply(struct connection *pconn,
+                                 unsigned char* ciphertext,
+                                int ciphertext_length)
 {
-  char msg[MAX_LEN_MSG];
+  char buf[1024];
+  struct packet_authentication_req auth_req;
+  int decrypted_size;
+  int nonce;
+  char* p;
 
+  auth_req.public_modulus_length = 0;
+  auth_req.public_exponent_length = 0;
+  auth_req.nonce = 0;
+  
+  decrypted_size = RSA_private_decrypt(ciphertext_length,
+                                       ciphertext,
+                                      (unsigned char*)buf,
+                                      private_key,
+                                      RSA_PKCS1_OAEP_PADDING);
+  if (decrypted_size == -1) {
+    reject_new_connection(_("Error while decrypting password %s"), pconn);
+    return FALSE;
+  }
+
+  buf[decrypted_size] = '\0';
+ 
+  p = memchr(buf, '|', decrypted_size);
+  if (p == NULL) {
+    reject_new_connection(_("Invalid password format"), pconn);
+    return FALSE;
+  }
+  sscanf(p + 1, "%d", &nonce);
+  if (nonce != pconn->server.nonce) {
+    reject_new_connection(_("Nonce doesn't match"), pconn);
+    return FALSE;
+  }
+  
+  *p = '\0';
+  
   if (pconn->server.status == AS_REQUESTING_NEW_PASS) {
 
     /* check if the new password is acceptable */
-    if (!is_good_password(password, msg)) {
+    if (!is_good_password(buf, auth_req.message)) {
       if (pconn->server.authentication_tries++ >= MAX_AUTHENTICATION_TRIES) {
         reject_new_connection(_("Sorry, too many wrong tries..."), pconn);
        return FALSE;
       } else {
-        dsend_packet_authentication_req(pconn, AUTH_NEWUSER_RETRY,msg);
+        auth_req.type = AUTH_NEWUSER_RETRY;
+       generate_nonce(&auth_req, pconn);
+       serialize_public_key(&auth_req);
+        send_packet_authentication_req(pconn, &auth_req);
        return TRUE;
       }
     }
 
     /* the new password is good, create a database entry for
      * this user; we establish the connection in handle_db_lookup */
-    sz_strlcpy(pconn->server.password, password);
+    sz_strlcpy(pconn->server.password, buf);
 
     switch(user_db_save(pconn)) {
     case USER_DB_SUCCESS:
@@ -437,7 +530,7 @@
 
     establish_new_connection(pconn);
   } else if (pconn->server.status == AS_REQUESTING_OLD_PASS) { 
-    if (userdb_check_password(pconn, password, strlen(password)) == 1) {
+    if (userdb_check_password(pconn, buf, strlen(buf)) == 1) {
       pconn->server.status = AS_ESTABLISHED;
       establish_new_connection(pconn);
     } else {
Index: server/connecthand.h
===================================================================
--- server/connecthand.h        (wersja 11144)
+++ server/connecthand.h        (kopia robocza)
@@ -36,6 +36,8 @@
 bool attach_connection_to_player(struct connection *pconn, 
                                  struct player *pplayer);
 bool unattach_connection_from_player(struct connection *pconn);
-bool handle_authentication_reply(struct connection *pc, char *password);
+bool handle_authentication_reply(struct connection *pc,
+                                 unsigned char* ciphertext,
+                                int ciphertext_length);
 
 #endif /* FC__CONNECTHAND_H */
Index: configure.ac
===================================================================
--- configure.ac        (wersja 11144)
+++ configure.ac        (kopia robocza)
@@ -345,6 +345,10 @@
   AC_MSG_ERROR([You need the gzip program for compilation.])
 fi
 
+AC_CHECK_LIB(crypto, RSA_new, , AC_MSG_ERROR([Could not find crypto 
library.]), )
+AC_CHECK_HEADER(openssl/rsa.h, , AC_MSG_ERROR([rsa.h not found. 
+You may need to install a openssl \"development\" package.]))
+
 dnl Check and compile ftwl
 if test "$ftwl" = x11 ; then
      FTWL_CFLAGS=`freetype-config --cflags`
Index: common/connection.h
===================================================================
--- common/connection.h (wersja 11144)
+++ common/connection.h (kopia robocza)
@@ -194,6 +194,9 @@
 
     /* used to follow where the connection is in the authentication process */
     enum auth_status status;
+    /* A number which must be concatenated with a password. This prevents 
+     * reply attacks. */
+    int nonce;
     char password[MAX_LEN_PASSWORD];
 
     /* for reverse lookup and blacklisting in db */
Index: common/packets.def
===================================================================
--- common/packets.def  (wersja 11144)
+++ common/packets.def  (kopia robocza)
@@ -280,13 +280,19 @@
   CONNECTION conn_id;
 end
 
-PACKET_AUTHENTICATION_REQ=6;sc,handle-per-conn,dsend
+PACKET_AUTHENTICATION_REQ=6;sc,handle-per-conn, handle-via-packet
   AUTH_TYPE type;
+  UINT16 public_modulus_length;
+  MEMORY public_modulus[256:public_modulus_length];
+  UINT16 public_exponent_length;
+  MEMORY public_exponent[256:public_exponent_length];
+  UINT32 nonce;
   STRING message[MAX_LEN_MSG]; /* explain to the client if there's a problem */
 end
 
 PACKET_AUTHENTICATION_REPLY=7;cs,no-handle
-  STRING password[MAX_LEN_PASSWORD];
+  UINT16 ciphertext_length;
+  MEMORY ciphertext[256:ciphertext_length];
 end
 
 

[Prev in Thread] Current Thread [Next in Thread]
  • [Freeciv-Dev] (PR#14350) RSA based authentication, Mateusz Stefek <=