summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton <anton@picapica>2019-02-14 21:53:06 +0400
committerAnton <anton@picapica>2019-02-14 21:53:06 +0400
commitc1856913ef78ceffc4766cd53686c86d0c314e3f (patch)
tree6fe61431c6d8aab6c001551bd813e359f4219694
parent130f4611430a348bd482ba5c8755a6e12e0f8eda (diff)
downloadpica-pica-c1856913ef78ceffc4766cd53686c86d0c314e3f.tar.gz
pica-pica-c1856913ef78ceffc4766cd53686c86d0c314e3f.zip
pica-client: sending and receiving MULTILOGIN message
-rw-r--r--PICA_client.c241
-rw-r--r--PICA_client.h6
-rw-r--r--PICA_proto.h5
-rw-r--r--pica-client/skynet.cpp15
-rw-r--r--pica-client/skynet.h3
5 files changed, 265 insertions, 5 deletions
diff --git a/PICA_client.c b/PICA_client.c
index 9f0a5e8..8903072 100644
--- a/PICA_client.c
+++ b/PICA_client.c
@@ -20,8 +20,10 @@
#include "PICA_proto.h"
#include "PICA_msgproc.h"
#include "PICA_common.h"
+#include "PICA_signverify.h"
#include <openssl/dh.h>
#include <string.h>
+#include <time.h>
#define PICA_DEBUG
@@ -74,6 +76,7 @@ static unsigned int procmsg_C2CCONNREQ(unsigned char*, unsigned int, void*);
static unsigned int procmsg_PICA_PROTO_DIRECTC2C_ADDRLIST(unsigned char*, unsigned int, void*);
static unsigned int procmsg_PICA_PROTO_DIRECTC2C_FAILED(unsigned char*, unsigned int, void*);
static unsigned int procmsg_PICA_PROTO_DIRECTC2C_SWITCH(unsigned char*, unsigned int, void*);
+static unsigned int procmsg_MULTILOGIN(unsigned char*, unsigned int, void*);
static struct PICA_proto_msg* c2n_writebuf_push(struct PICA_c2n *ci, unsigned int msgid, unsigned int size);
@@ -99,7 +102,8 @@ const struct PICA_msginfo c2n_messages[] =
{PICA_PROTO_CLNODELIST, PICA_MSG_VAR_SIZE, PICA_MSG_VARSIZE_INT16, procmsg_CLNODELIST},
{PICA_PROTO_PINGREQ, PICA_MSG_FIXED_SIZE, PICA_PROTO_PINGREQ_SIZE, procmsg_PINGREQ},
{PICA_PROTO_INITRESPOK, PICA_MSG_FIXED_SIZE, PICA_PROTO_INITRESPOK_SIZE, procmsg_INITRESP},
- {PICA_PROTO_VERDIFFER, PICA_MSG_FIXED_SIZE, PICA_PROTO_VERDIFFER_SIZE, procmsg_INITRESP}
+ {PICA_PROTO_VERDIFFER, PICA_MSG_FIXED_SIZE, PICA_PROTO_VERDIFFER_SIZE, procmsg_INITRESP},
+ {PICA_PROTO_MULTILOGIN, PICA_MSG_VAR_SIZE, PICA_MSG_VARSIZE_INT16, procmsg_MULTILOGIN}
};
const struct PICA_msginfo c2c_messages[] =
@@ -1416,6 +1420,10 @@ int PICA_open_acc(const char *cert_file,
if (!a)
return PICA_ERRNOMEM;
+ a->pkey_file = pkey_file;
+ a->cert_file = cert_file;
+ a->password_cb = password_cb;
+
a->ctx = SSL_CTX_new(TLSv1_2_method());//(2)
if (!a->ctx)
@@ -1554,7 +1562,6 @@ static int c2n_stage3_sendreq(struct PICA_c2n *c2n)
{
mp->tail[0] = PICA_PROTO_VER_HIGH;
mp->tail[1] = PICA_PROTO_VER_LOW;
- mp->tail[2] = c2n->multilogin_policy;
}
else
return PICA_ERRNOMEM;
@@ -1615,6 +1622,233 @@ static int c2n_stage5_starttls(struct PICA_c2n *c2n)
return PICA_OK;
}
+static int do_signature(struct PICA_acc *acc, void **datapointers, int *datalengths, unsigned char **sig, int *siglen)
+{
+ int ret = PICA_ERRNOMEM;
+ EVP_MD_CTX *mdctx = NULL;
+ EVP_PKEY *pkey = NULL;
+ RSA *rsa = NULL;
+ FILE *f;
+
+ mdctx = EVP_MD_CTX_create();
+
+ if (!mdctx)
+ return PICA_ERRNOMEM;
+
+ pkey = EVP_PKEY_new();
+
+ if (!pkey)
+ goto sig_exit1;
+
+ f = fopen(acc->pkey_file, "r");
+
+ if (!f)
+ {
+ ret = PICA_ERRFILEOPEN;
+ goto sig_exit2;
+ }
+
+ ret = PICA_ERRSSL;
+
+ rsa = PEM_read_RSAPrivateKey(f, NULL, acc->password_cb, acc->id);
+ fclose(f);
+
+ if (!rsa)
+ goto sig_exit2;
+
+ ret = EVP_PKEY_set1_RSA(pkey, rsa);
+
+ if (ret != 1)
+ goto sig_exit3;
+
+ ret = EVP_DigestSignInit(mdctx, NULL, EVP_sha224(), NULL, pkey);
+
+ if (ret != 1)
+ goto sig_exit3;
+
+ while(*datapointers)
+ {
+ ret = EVP_DigestSignUpdate(mdctx, *datapointers, *datalengths);
+
+ if (ret != 1)
+ {
+ goto sig_exit3;
+ break;
+ }
+
+ datapointers++;
+ datalengths++;
+ }
+
+ ret = EVP_DigestSignFinal(mdctx, NULL, siglen);
+
+ if (ret != 1)
+ goto sig_exit3;
+
+ *sig = (unsigned char*) malloc(*siglen);
+
+ if (!*sig)
+ {
+ ret = PICA_ERRNOMEM;
+ goto sig_exit3;
+ }
+
+ ret = EVP_DigestSignFinal(mdctx, *sig, siglen);
+
+ if (ret == 1)
+ ret = PICA_OK;
+ else
+ free(*sig);
+
+sig_exit3:
+ RSA_free(rsa);
+sig_exit2:
+ EVP_PKEY_free(pkey);
+sig_exit1:
+ EVP_MD_CTX_destroy(mdctx);
+
+ return ret;
+}
+
+static unsigned int procmsg_MULTILOGIN(unsigned char *buf, unsigned int nb, void *p)
+{
+ struct PICA_c2n *c2n = (struct PICA_c2n *) p;
+ void *sigdatas[4];
+ int sigdatalengths[4];
+ uint64_t timestamp;
+ FILE *f;
+ X509 *x;
+ int ret;
+ uint16_t payload_len = *(uint16_t*)(buf + 2);
+ int siglen;
+
+ if (payload_len <= sizeof timestamp + PICA_PROTO_NODELIST_ITEM_IPV4_SIZE)
+ return 0;
+
+ timestamp = *(uint64_t*)(buf + 4);
+ if (timestamp <= c2n->multilogin_last_timestamp)
+ return 0;
+
+ f = fopen(c2n->acc->cert_file, "r");
+
+ if (!f)
+ return 0;
+
+ x = PEM_read_X509(f, 0, 0, 0);
+ fclose(f);
+
+ if (!x)
+ return 0;
+
+ sigdatas[0] = c2n->acc->id;
+ sigdatalengths[0] = PICA_ID_SIZE;
+
+ sigdatas[1] = buf + 4;
+ sigdatalengths[1] = sizeof(uint64_t);
+
+ sigdatas[2] = buf + 4 + sizeof(uint64_t);
+
+ switch (buf[4 + sizeof(uint64_t)])
+ {
+ case PICA_PROTO_NEWNODE_IPV4:
+ sigdatalengths[2] = PICA_PROTO_NODELIST_ITEM_IPV4_SIZE;
+ break;
+
+ default: // IPv6 !!
+ goto multilogin_err;
+ break;
+ }
+
+ sigdatas[3] = NULL;
+ sigdatalengths[3] = 0;
+
+ siglen = payload_len - sizeof(uint64_t) - sigdatalengths[2];
+
+ if (siglen <= 0)
+ goto multilogin_err;
+
+ ret = PICA_signverify(x->cert_info->key->pkey, sigdatas, sigdatalengths, buf + 4 + sizeof(uint64_t) + sigdatalengths[2], siglen);
+
+ if (ret != PICA_OK)
+ goto multilogin_err;
+
+ {
+ char ipaddr_string[INET6_ADDRSTRLEN];
+
+ unsigned char *ipv4_pos = buf + 4 + sizeof(uint64_t) + 1;
+ unsigned char *port_pos = buf + 4 + sizeof(uint64_t) + 1 + 4;
+
+ callbacks.multilogin_cb(timestamp, sigdatas[2],
+ inet_ntop(AF_INET, ipv4_pos, ipaddr_string, INET6_ADDRSTRLEN),
+ ntohs(*(uint16_t*)(port_pos)));
+ }
+
+ ret = 1;
+
+multilogin_err:
+ X509_free(x);
+ return ret;
+}
+
+static int c2n_optstage7_multilogin(struct PICA_c2n *c2n)
+{
+ struct PICA_proto_msg *mp;
+ void *sigdatas[4];
+ int sigdatalengths[4];
+ uint64_t timestamp;
+ unsigned char node_addr[PICA_PROTO_NODELIST_ITEM_IPV4_SIZE];
+ int siglen;
+ unsigned char *sig;
+ int ret;
+
+ sigdatas[0] = c2n->acc->id;
+ sigdatalengths[0] = PICA_ID_SIZE;
+
+ timestamp = (uint64_t) time(NULL);
+
+ sigdatas[1] = &timestamp;
+ sigdatalengths[1] = sizeof timestamp;
+
+ //nodeaddr buf, IPv4 for now
+ // TODO IPv6, DNS
+ node_addr[0] = PICA_PROTO_NEWNODE_IPV4;
+ memcpy(node_addr + 1, c2n->srv_addr.sin_addr.s_addr, 4);
+ memcpy(node_addr + 5, c2n->srv_addr.sin_port, 2);
+
+ sigdatas[2] = node_addr;
+ sigdatalengths[2] = PICA_PROTO_NODELIST_ITEM_IPV4_SIZE;
+
+ sigdatas[3] = NULL;
+ sigdatalengths[3] = 0;
+
+ ret = do_signature(c2n->acc, sigdatas, sigdatalengths, &sig, &siglen);
+
+ if (ret != PICA_OK)
+ return ret;
+
+ if (siglen > PICA_PROTO_MULTILOGIN_MAXSIGSIZE)
+ {
+ free(sig);
+ return PICA_ERRMSGSIZE;
+ }
+
+ mp = c2n_writebuf_push(c2n, PICA_PROTO_MULTILOGIN, 2 + 2 + sizeof timestamp + sigdatalengths[2] + siglen);
+
+ if (mp)
+ {
+ *((uint16_t*)mp->tail) = sizeof timestamp + sigdatalengths[2] + siglen;
+ memcpy(mp->tail + 2, &timestamp, sizeof timestamp);
+ memcpy(mp->tail + 2 + sizeof timestamp, sigdatas[2], sigdatalengths[2]);
+ memcpy(mp->tail + 2 + sizeof timestamp + sigdatalengths[2], sig, siglen);
+ ret = PICA_OK;
+ }
+ else
+ ret = PICA_ERRNOMEM;
+
+ free(sig);
+ return ret;
+}
+
static int c2n_stage6_nodelistrequest(struct PICA_c2n *c2n)
{
PICA_TRACEFUNC
@@ -1633,6 +1867,9 @@ static int c2n_stage6_nodelistrequest(struct PICA_c2n *c2n)
callbacks.c2n_established_cb(c2n);
+ if (c2n->multilogin_policy != PICA_MULTILOGIN_PROHIBIT)
+ return c2n_optstage7_multilogin(c2n);
+
return PICA_OK;
}
diff --git a/PICA_client.h b/PICA_client.h
index 2112284..2a83643 100644
--- a/PICA_client.h
+++ b/PICA_client.h
@@ -146,6 +146,9 @@ struct PICA_acc
unsigned char id[PICA_ID_SIZE];
SSL_CTX* ctx;
SSL_CTX* anon_ctx;
+ const char *pkey_file;
+ const char *cert_file;
+ int (*password_cb)(char *buf, int size, int rwflag, void *userdata);
};
enum PICA_c2n_state
@@ -177,6 +180,7 @@ struct PICA_c2n
enum PICA_c2n_state state;
enum PICA_directc2c_config directc2c_config;
int multilogin_policy;
+ uint64_t multilogin_last_timestamp;
struct PICA_listener *directc2c_listener;
unsigned char *read_buf;
@@ -379,6 +383,8 @@ struct PICA_client_callbacks
void (*c2n_closed_cb)(struct PICA_c2n *c2n, int error);
void (*listener_error_cb)(struct PICA_listener *lst, int errorcode);
+
+ void (*multilogin_cb)(time_t timestamp, void *addr_bin, const char *addr_str, uint16_t port);
};
diff --git a/PICA_proto.h b/PICA_proto.h
index b218c19..cb6e673 100644
--- a/PICA_proto.h
+++ b/PICA_proto.h
@@ -56,6 +56,8 @@
#define PICA_PROTO_DIRECTC2C_FAILED 0x0C
#define PICA_PROTO_DIRECTC2C_SWITCH 0x0D
+#define PICA_PROTO_MULTILOGIN 0x0E
+
#define PICA_PROTO_MSGUTF8 0x30
#define PICA_PROTO_MSGOK 0x40
#define PICA_PROTO_SENDFILEREQUEST 0x50
@@ -92,7 +94,7 @@
//size
-#define PICA_PROTO_INITREQ_SIZE 5
+#define PICA_PROTO_INITREQ_SIZE 4
#define PICA_PROTO_INITRESPOK_SIZE 4
#define PICA_PROTO_VERDIFFER_SIZE 4
@@ -141,6 +143,7 @@
#define PICA_PROTO_N2NMSG_MAXDATASIZE 65476
#define PICA_PROTO_C2CMSG_MAXDATASIZE 65532
#define PICA_PROTO_C2CMSG_MAXFILENAMESIZE 255
+#define PICA_PROTO_MULTILOGIN_MAXSIGSIZE 65265
#define PICA_MULTILOGIN_PROHIBIT 0
diff --git a/pica-client/skynet.cpp b/pica-client/skynet.cpp
index e445963..cdf63c5 100644
--- a/pica-client/skynet.cpp
+++ b/pica-client/skynet.cpp
@@ -55,7 +55,8 @@ SkyNet::SkyNet()
c2n_established_cb,
c2n_failed_cb,
c2n_closed_cb,
- listener_error_cb
+ listener_error_cb,
+ multilogin_cb
};
PICA_client_init(&cbs);
@@ -185,7 +186,12 @@ void SkyNet::nodelink_failed(PICA_c2n *c2n, int error)
emit LostSelfAwareness();
}
-
+void SkyNet::multilogin_event(time_t timestamp, const char *addr, uint16_t port)
+{
+ emit StatusMsg(QString("%3 Detected new login of your account at node %1:%2")
+ .arg(addr).arg(port).arg(QDateTime::fromTime_t(timestamp).toString("yyyy-MM-dd hh:mm:ss")),
+ true);
+}
void SkyNet::node_status_changed(Nodes::NodeRecord nr, bool alive)
{
@@ -1005,3 +1011,8 @@ void SkyNet::listener_error_cb(struct PICA_listener *lst, int errorcode)
{
}
+
+void SkyNet::multilogin_cb(time_t timestamp, void *addr_bin, const char *addr_str, uint16_t port)
+{
+ skynet->multilogin_event(timestamp, addr_str, port);
+}
diff --git a/pica-client/skynet.h b/pica-client/skynet.h
index 4010ec6..f748e95 100644
--- a/pica-client/skynet.h
+++ b/pica-client/skynet.h
@@ -161,10 +161,13 @@ private:
static void listener_error_cb(struct PICA_listener *lst, int errorcode);
+ static void multilogin_cb(time_t timestamp, void *addr_bin, const char *addr_str, uint16_t port);
+
private slots:
void nodelink_activated(PICA_c2n *c2n);
void nodelink_closed(PICA_c2n *c2n, int error);
void nodelink_failed(PICA_c2n *c2n, int error);
+ void multilogin_event(time_t timestamp, const char *addr, uint16_t port);
void node_status_changed(Nodes::NodeRecord nr, bool alive);
void verify_peer_cert(QByteArray peer_id, QString cert_pem, bool *verified);