summaryrefslogtreecommitdiff
path: root/src/ZIDFile.cpp
diff options
context:
space:
mode:
authorMark Purcell <msp@debian.org>2013-07-09 15:55:55 +0100
committerMark Purcell <msp@debian.org>2013-07-09 15:55:55 +0100
commit669109e369a1be69ff7c4108eb545eff4c5c26d9 (patch)
tree73c117a2e7dd22a7a6ee315101f6357ab43386ec /src/ZIDFile.cpp
libzrtpcpp (2.3.4-1) unstable; urgency=medium
* New upstream release - Fixes "CVE-2013-2221 CVE-2013-2222 CVE-2013-2223" (Closes: #714650) # imported from the archive
Diffstat (limited to 'src/ZIDFile.cpp')
-rw-r--r--src/ZIDFile.cpp430
1 files changed, 430 insertions, 0 deletions
diff --git a/src/ZIDFile.cpp b/src/ZIDFile.cpp
new file mode 100644
index 0000000..69b43eb
--- /dev/null
+++ b/src/ZIDFile.cpp
@@ -0,0 +1,430 @@
+/*
+ Copyright (C) 2006-2008 Werner Dittmann
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Authors: Werner Dittmann <Werner.Dittmann@t-online.de>
+ */
+// #define UNIT_TEST
+
+#include <string>
+#include <time.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <libzrtpcpp/ZIDFile.h>
+
+
+static ZIDFile* instance;
+static int errors = 0; // maybe we will use as member of ZIDFile later...
+
+void ZIDFile::createZIDFile(char* name) {
+ zidFile = fopen(name, "wb+");
+ // New file, generate an associated random ZID and save
+ // it as first record
+ if (zidFile != NULL) {
+ unsigned int* ip;
+ ip = (unsigned int*) associatedZid;
+ srand(time(NULL));
+ *ip++ = rand();
+ *ip++ = rand();
+ *ip = rand();
+
+ ZIDRecord rec(associatedZid);
+ rec.setOwnZIDRecord();
+ fseek(zidFile, 0L, SEEK_SET);
+ if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1)
+ ++errors;
+ fflush(zidFile);
+ }
+}
+
+/**
+ * Migrate old ZID file format to new one.
+ *
+ * If ZID file is old format:
+ * - close it, rename it, then re-open
+ * - create ZID file for new format
+ * - copy over contents and flags.
+ */
+void ZIDFile::checkDoMigration(char* name) {
+ FILE* fdOld;
+ unsigned char inb[2];
+ zidrecord1_t recOld;
+
+ fseek(zidFile, 0L, SEEK_SET);
+ if (fread(inb, 2, 1, zidFile) < 1) {
+ ++errors;
+ inb[0] = 0;
+ }
+
+ if (inb[0] > 0) { // if it's new format just return
+ return;
+ }
+ fclose(zidFile); // close old ZID file
+ zidFile = NULL;
+
+ // create save file name, rename and re-open
+ // if rename fails, just unlink old ZID file and create a brand new file
+ // just a little inconvenience for the user, need to verify new SAS
+ std::string fn = std::string(name) + std::string(".save");
+ if (rename(name, fn.c_str()) < 0) {
+ unlink(name);
+ createZIDFile(name);
+ return;
+ }
+ fdOld = fopen(fn.c_str(), "rb"); // reopen old format in read only mode
+
+ // Get first record from old file - is the own ZID
+ fseek(fdOld, 0L, SEEK_SET);
+ if (fread(&recOld, sizeof(zidrecord1_t), 1, fdOld) != 1) {
+ fclose(fdOld);
+ return;
+ }
+ if (recOld.ownZid != 1) {
+ fclose(fdOld);
+ return;
+ }
+ zidFile = fopen(name, "wb+"); // create new format file in binary r/w mode
+ if (zidFile == NULL) {
+ return;
+ }
+ // create ZIDRecord in new format, copy over own ZID and write the record
+ ZIDRecord rec(recOld.identifier);
+ rec.setOwnZIDRecord();
+ if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1)
+ ++errors;
+
+ // now copy over all valid records from old ZID file format.
+ // Sequentially read old records, sequentially write new records
+ int numRead;
+ do {
+ numRead = fread(&recOld, sizeof(zidrecord1_t), 1, fdOld);
+ if (numRead == 0) { // all old records processed
+ break;
+ }
+ // skip own ZID record and invalid records
+ if (recOld.ownZid == 1 || recOld.recValid == 0) {
+ continue;
+ }
+ ZIDRecord rec2(recOld.identifier);
+ rec2.setValid();
+ if (recOld.rs1Valid & SASVerified) {
+ rec2.setSasVerified();
+ }
+ rec2.setNewRs1(recOld.rs2Data);
+ rec2.setNewRs1(recOld.rs1Data);
+ if (fwrite(rec2.getRecordData(), rec2.getRecordLength(), 1, zidFile) < 1)
+ ++errors;
+
+ } while (numRead == 1);
+ fflush(zidFile);
+}
+
+ZIDFile::~ZIDFile() {
+ close();
+}
+
+ZIDFile* ZIDFile::getInstance() {
+
+ if (instance == NULL) {
+ instance = new ZIDFile();
+ }
+ return instance;
+}
+
+int ZIDFile::open(char* name) {
+
+ // check for an already active ZID file
+ if (zidFile != NULL) {
+ return 0;
+ }
+ if ((zidFile = fopen(name, "rb+")) == NULL) {
+ createZIDFile(name);
+ } else {
+ checkDoMigration(name);
+ if (zidFile != NULL) {
+ ZIDRecord rec;
+ fseek(zidFile, 0L, SEEK_SET);
+ if (fread(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) != 1) {
+ fclose(zidFile);
+ zidFile = NULL;
+ return -1;
+ }
+ if (!rec.isOwnZIDRecord()) {
+ fclose(zidFile);
+ zidFile = NULL;
+ return -1;
+ }
+ memcpy(associatedZid, rec.getIdentifier(), IDENTIFIER_LEN);
+ }
+ }
+ return ((zidFile == NULL) ? -1 : 1);
+}
+
+void ZIDFile::close() {
+
+ if (zidFile != NULL) {
+ fclose(zidFile);
+ zidFile = NULL;
+ }
+}
+
+unsigned int ZIDFile::getRecord(ZIDRecord* zidRecord) {
+ unsigned long pos;
+ ZIDRecord rec;
+ int numRead;
+
+ // set read pointer behind first record (
+ fseek(zidFile, rec.getRecordLength(), SEEK_SET);
+
+ do {
+ pos = ftell(zidFile);
+ numRead = fread(rec.getRecordData(), rec.getRecordLength(), 1, zidFile);
+ if (numRead == 0) {
+ break;
+ }
+
+ // skip own ZID record and invalid records
+ if (rec.isOwnZIDRecord() || !rec.isValid()) {
+ continue;
+ }
+
+ } while (numRead == 1 &&
+ memcmp(zidRecord->getIdentifier(), rec.getIdentifier(), IDENTIFIER_LEN) != 0);
+
+ // If we reached end of file, then no record with the ZID
+ // found. We need to create a new ZID record.
+ if (numRead == 0) {
+ // create new record
+ ZIDRecord rec1(zidRecord->getIdentifier());
+ rec1.setValid();
+ if (fwrite(rec1.getRecordData(), rec1.getRecordLength(), 1, zidFile) < 1)
+ ++errors;
+ memcpy(zidRecord->getRecordData(), rec1.getRecordData(), rec1.getRecordLength());
+ } else {
+ // Copy the read data into caller's the record storage
+ memcpy(zidRecord->getRecordData(), rec.getRecordData(), rec.getRecordLength());
+ }
+
+ // remember position of record in file for save operation
+ zidRecord->setPosition(pos);
+ return 1;
+}
+
+unsigned int ZIDFile::saveRecord(ZIDRecord *zidRecord) {
+
+ fseek(zidFile, zidRecord->getPosition(), SEEK_SET);
+ if (fwrite(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile) < 1)
+ ++errors;
+ fflush(zidFile);
+ return 1;
+}
+
+
+#ifdef UNIT_TEST
+
+#include <iostream>
+#include <unistd.h>
+using namespace std;
+
+static void hexdump(const char* title, const unsigned char *s, int l) {
+ int n=0;
+
+ if (s == NULL) return;
+
+ fprintf(stderr, "%s",title);
+ for (; n < l ; ++n) {
+ if ((n%16) == 0)
+ fprintf(stderr, "\n%04x",n);
+ fprintf(stderr, " %02x",s[n]);
+ }
+ fprintf(stderr, "\n");
+}
+
+int main(int argc, char *argv[]) {
+
+ unsigned char myId[IDENTIFIER_LEN];
+ ZIDFile *zid = ZIDFile::getInstance();
+
+ unlink("testzid2");
+ zid->open("testzid2");
+ hexdump("My ZID: ", zid->getZid(), IDENTIFIER_LEN);
+ memcpy(myId, zid->getZid(), IDENTIFIER_LEN);
+ zid->close();
+
+ zid->open("testzid2");
+ if (memcmp(myId, zid->getZid(), IDENTIFIER_LEN) != 0) {
+ cerr << "Wrong ZID in testfile" << endl;
+ return 1;
+ }
+
+ // Create a new ZID record for peer ZID "123456789012"
+ ZIDRecord zr3((unsigned char*) "123456789012");
+ zid->getRecord(&zr3);
+ if (!zr3.isValid()) {
+ cerr << "New ZID record '123456789012' not set to valid" << endl;
+ return 1;
+ }
+ zid->saveRecord(&zr3);
+
+ // create a second record with peer ZID "210987654321"
+ ZIDRecord zr4((unsigned char*) "210987654321");
+ zid->getRecord(&zr4);
+ if (!zr4.isValid()) {
+ cerr << "New ZID record '210987654321' not set to valid" << endl;
+ return 1;
+ }
+ zid->saveRecord(&zr4);
+
+ // now set a first RS1 with default expiration interval, check
+ // if set correctly, valid flag and expiration interval
+ zr3.setNewRs1((unsigned char*) "11122233344455566677788899900012");
+ if (memcmp(zr3.getRs1(), "11122233344455566677788899900012", RS_LENGTH) != 0) {
+ cerr << "RS1 was not set (111...012)" << endl;
+ return 1;
+ }
+ if (!zr3.isRs1Valid()) {
+ cerr << "RS1 was not set to valid state (111...012)" << endl;
+ return 1;
+ }
+ if (!zr3.isRs1NotExpired()) {
+ cerr << "RS1 expired (111...012)" << endl;
+ return 1;
+ }
+ if (zr3.isRs2Valid()) {
+ cerr << "RS2 was set to valid state (111...012)" << endl;
+ return 1;
+ }
+ zid->saveRecord(&zr3);
+
+ // create a second RS1, RS2 will become the first RS1, check
+ // if set correctly, valid flag and expiration interval for both
+ // RS1 and RS2
+ zr3.setNewRs1((unsigned char*) "00099988877766655544433322211121");
+ if (memcmp(zr3.getRs1(), "00099988877766655544433322211121", RS_LENGTH) != 0) {
+ cerr << "RS1 was not set (000...121)" << endl;
+ return 1;
+ }
+ if (!zr3.isRs1Valid()) {
+ cerr << "RS1 was not set to valid state (000...121)" << endl;
+ return 1;
+ }
+ if (!zr3.isRs1NotExpired()) {
+ cerr << "RS1 expired (000...121)" << endl;
+ return 1;
+ }
+ if (memcmp(zr3.getRs2(), "11122233344455566677788899900012", RS_LENGTH) != 0) {
+ cerr << "RS2 was not set (111...012)" << endl;
+ return 1;
+ }
+ if (!zr3.isRs2Valid()) {
+ cerr << "RS2 was not set to valid state (111...012)" << endl;
+ return 1;
+ }
+ if (!zr3.isRs2NotExpired()) {
+ cerr << "RS2 expired (111...012)" << endl;
+ return 1;
+ }
+ zid->saveRecord(&zr3);
+
+ zid->close();
+
+ // Reopen, check if first record is still valid, RSx vaild and
+ // not expired. Then manipulate 2nd record.
+ zid->open("testzid2");
+
+ ZIDRecord zr3a((unsigned char*) "123456789012");
+ zid->getRecord(&zr3a);
+ if (!zr3a.isValid()) {
+ cerr << "Re-read ZID record '123456789012' not set to valid" << endl;
+ return 1;
+ }
+ if (memcmp(zr3a.getRs1(), "00099988877766655544433322211121", RS_LENGTH) != 0) {
+ cerr << "re-read RS1 was not set (000...121)" << endl;
+ return 1;
+ }
+ if (!zr3a.isRs1Valid()) {
+ cerr << "Re-read RS1 was not set to valid state (000...121)" << endl;
+ return 1;
+ }
+ if (!zr3a.isRs1NotExpired()) {
+ cerr << "re-read RS1 expired (000...121)" << endl;
+ return 1;
+ }
+ if (memcmp(zr3a.getRs2(), "11122233344455566677788899900012", RS_LENGTH) != 0) {
+ cerr << "re-read RS2 was not set (111...012)" << endl;
+ return 1;
+ }
+ if (!zr3a.isRs2Valid()) {
+ cerr << "Re-read RS2 was not set to valid state (111...012)" << endl;
+ return 1;
+ }
+ if (!zr3a.isRs2NotExpired()) {
+ cerr << "Re-read RS2 expired (111...012)" << endl;
+ return 1;
+ }
+
+ ZIDRecord zr5((unsigned char*) "210987654321");
+ zid->getRecord(&zr5);
+
+
+ // set new RS1 with expire interval of 5 second, then check immediatly
+ zr5.setNewRs1((unsigned char*) "aaa22233344455566677788899900012", 5);
+ if (!zr5.isValid()) {
+ cerr << "Re-read ZID record '210987654321' not set to valid" << endl;
+ return 1;
+ }
+ if (memcmp(zr5.getRs1(), "aaa22233344455566677788899900012", RS_LENGTH) != 0) {
+ cerr << "RS1 (2) was not set (aaa...012)" << endl;
+ return 1;
+ }
+ if (!zr5.isRs1Valid()) {
+ cerr << "RS1 (2) was not set to valid state (aaa...012)" << endl;
+ return 1;
+ }
+ if (!zr5.isRs1NotExpired()) {
+ cerr << "RS1 (2) expired (aaa...012)" << endl;
+ return 1;
+ }
+
+ // wait for 6 second, now the expire check shall fail
+ sleep(6);
+ if (zr5.isRs1NotExpired()) {
+ cerr << "RS1 (2) is not expired after defined interval (aaa...012)" << endl;
+ return 1;
+ }
+
+ zr5.setNewRs1((unsigned char*) "bbb99988877766655544433322211121", 256);
+ zid->saveRecord(&zr5);
+
+ zid->close();
+
+ // Test migration
+ zid->open("testzidOld");
+ zid->close();
+
+}
+
+#endif
+
+/** EMACS **
+ * Local variables:
+ * mode: c++
+ * c-default-style: ellemtel
+ * c-basic-offset: 4
+ * End:
+ */