diff options
| author | Anton <anton@picapica> | 2019-02-14 21:53:06 +0400 |
|---|---|---|
| committer | Anton <anton@picapica> | 2019-02-14 21:53:06 +0400 |
| commit | c1856913ef78ceffc4766cd53686c86d0c314e3f (patch) | |
| tree | 6fe61431c6d8aab6c001551bd813e359f4219694 | |
| parent | 130f4611430a348bd482ba5c8755a6e12e0f8eda (diff) | |
| download | pica-pica-c1856913ef78ceffc4766cd53686c86d0c314e3f.tar.gz pica-pica-c1856913ef78ceffc4766cd53686c86d0c314e3f.zip | |
pica-client: sending and receiving MULTILOGIN message
| -rw-r--r-- | PICA_client.c | 241 | ||||
| -rw-r--r-- | PICA_client.h | 6 | ||||
| -rw-r--r-- | PICA_proto.h | 5 | ||||
| -rw-r--r-- | pica-client/skynet.cpp | 15 | ||||
| -rw-r--r-- | pica-client/skynet.h | 3 |
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] = ×tamp; + 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, ×tamp, 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); |
