[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 <=
|
|