blob: 5975fc2c5f74e6de035991d3010d7b66788f6bcd [file] [log] [blame]
Alexandre Lision7fd5d3d2013-12-04 13:06:40 -05001/*
2 Copyright (C) 2006-2013 Werner Dittmann
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16*/
17
18/*
19 * Authors: Werner Dittmann <Werner.Dittmann@t-online.de>
20 */
21// #define UNIT_TEST
22
23#include <string>
24#include <stdlib.h>
25#include <unistd.h>
26
27#include <crypto/zrtpDH.h>
28
29#include <libzrtpcpp/ZIDCacheFile.h>
30
31
32static ZIDCacheFile* instance;
33static int errors = 0; // maybe we will use as member of ZIDCache later...
34
35
36/**
37 * A poor man's factory.
38 *
39 * The build process must not allow two cache file implementation classes linked
40 * into the same library.
41 */
42
43ZIDCache* getZidCacheInstance() {
44
45 if (instance == NULL) {
46 instance = new ZIDCacheFile();
47 }
48 return instance;
49}
50
51
52void ZIDCacheFile::createZIDFile(char* name) {
53 zidFile = fopen(name, "wb+");
54 // New file, generate an associated random ZID and save
55 // it as first record
56 if (zidFile != NULL) {
57 randomZRTP(associatedZid, IDENTIFIER_LEN);
58
59 ZIDRecordFile rec;
60 rec.setZid(associatedZid);
61 rec.setOwnZIDRecord();
62 fseek(zidFile, 0L, SEEK_SET);
63 if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1)
64 ++errors;
65 fflush(zidFile);
66 }
67}
68
69/**
70 * Migrate old ZID file format to new one.
71 *
72 * If ZID file is old format:
73 * - close it, rename it, then re-open
74 * - create ZID file for new format
75 * - copy over contents and flags.
76 */
77void ZIDCacheFile::checkDoMigration(char* name) {
78 FILE* fdOld;
79 unsigned char inb[2];
80 zidrecord1_t recOld;
81
82 fseek(zidFile, 0L, SEEK_SET);
83 if (fread(inb, 2, 1, zidFile) < 1) {
84 ++errors;
85 inb[0] = 0;
86 }
87
88 if (inb[0] > 0) { // if it's new format just return
89 return;
90 }
91 fclose(zidFile); // close old ZID file
92 zidFile = NULL;
93
94 // create save file name, rename and re-open
95 // if rename fails, just unlink old ZID file and create a brand new file
96 // just a little inconvenience for the user, need to verify new SAS
97 std::string fn = std::string(name) + std::string(".save");
98 if (rename(name, fn.c_str()) < 0) {
99 unlink(name);
100 createZIDFile(name);
101 return;
102 }
103 fdOld = fopen(fn.c_str(), "rb"); // reopen old format in read only mode
104
105 // Get first record from old file - is the own ZID
106 fseek(fdOld, 0L, SEEK_SET);
107 if (fread(&recOld, sizeof(zidrecord1_t), 1, fdOld) != 1) {
108 fclose(fdOld);
109 return;
110 }
111 if (recOld.ownZid != 1) {
112 fclose(fdOld);
113 return;
114 }
115 zidFile = fopen(name, "wb+"); // create new format file in binary r/w mode
116 if (zidFile == NULL) {
117 return;
118 }
119 // create ZIDRecord in new format, copy over own ZID and write the record
120 ZIDRecordFile rec;
121 rec.setZid(recOld.identifier);
122 rec.setOwnZIDRecord();
123 if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1)
124 ++errors;
125
126 // now copy over all valid records from old ZID file format.
127 // Sequentially read old records, sequentially write new records
128 int numRead;
129 do {
130 numRead = fread(&recOld, sizeof(zidrecord1_t), 1, fdOld);
131 if (numRead == 0) { // all old records processed
132 break;
133 }
134 // skip own ZID record and invalid records
135 if (recOld.ownZid == 1 || recOld.recValid == 0) {
136 continue;
137 }
138 ZIDRecordFile rec2;
139 rec2.setZid(recOld.identifier);
140 rec2.setValid();
141 if (recOld.rs1Valid & SASVerified) {
142 rec2.setSasVerified();
143 }
144 rec2.setNewRs1(recOld.rs2Data); // TODO: check squenec
145 rec2.setNewRs1(recOld.rs1Data);
146 if (fwrite(rec2.getRecordData(), rec2.getRecordLength(), 1, zidFile) < 1)
147 ++errors;
148
149 } while (numRead == 1);
150 fflush(zidFile);
151}
152
153ZIDCacheFile::~ZIDCacheFile() {
154 close();
155}
156
157int ZIDCacheFile::open(char* name) {
158
159 // check for an already active ZID file
160 if (zidFile != NULL) {
161 return 0;
162 }
163 if ((zidFile = fopen(name, "rb+")) == NULL) {
164 createZIDFile(name);
165 } else {
166 checkDoMigration(name);
167 if (zidFile != NULL) {
168 ZIDRecordFile rec;
169 fseek(zidFile, 0L, SEEK_SET);
170 if (fread(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) != 1) {
171 fclose(zidFile);
172 zidFile = NULL;
173 return -1;
174 }
175 if (!rec.isOwnZIDRecord()) {
176 fclose(zidFile);
177 zidFile = NULL;
178 return -1;
179 }
180 memcpy(associatedZid, rec.getIdentifier(), IDENTIFIER_LEN);
181 }
182 }
183 return ((zidFile == NULL) ? -1 : 1);
184}
185
186void ZIDCacheFile::close() {
187
188 if (zidFile != NULL) {
189 fclose(zidFile);
190 zidFile = NULL;
191 }
192}
193
194ZIDRecord *ZIDCacheFile::getRecord(unsigned char *zid) {
195 unsigned long pos;
196 int numRead;
197 // ZIDRecordFile rec;
198 ZIDRecordFile *zidRecord = new ZIDRecordFile();
199
200 // set read pointer behind first record (
201 fseek(zidFile, zidRecord->getRecordLength(), SEEK_SET);
202
203 do {
204 pos = ftell(zidFile);
205 numRead = fread(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile);
206 if (numRead == 0) {
207 break;
208 }
209
210 // skip own ZID record and invalid records
211 if (zidRecord->isOwnZIDRecord() || !zidRecord->isValid()) {
212 continue;
213 }
214
215 } while (numRead == 1 &&
216 memcmp(zidRecord->getIdentifier(), zid, IDENTIFIER_LEN) != 0);
217
218 // If we reached end of file, then no record with the ZID
219 // found. We need to create a new ZID record.
220 if (numRead == 0) {
221 // create new record
222 // ZIDRecordFile rec1;
223 zidRecord->setZid(zid);
224 zidRecord->setValid();
225 if (fwrite(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile) < 1)
226 ++errors;
227 }
228 // remember position of record in file for save operation
229 zidRecord->setPosition(pos);
230 return zidRecord;
231}
232
233unsigned int ZIDCacheFile::saveRecord(ZIDRecord *zidRec) {
234 ZIDRecordFile *zidRecord = reinterpret_cast<ZIDRecordFile *>(zidRec);
235
236 fseek(zidFile, zidRecord->getPosition(), SEEK_SET);
237 if (fwrite(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile) < 1)
238 ++errors;
239 fflush(zidFile);
240 return 1;
241}
242
243int32_t ZIDCacheFile::getPeerName(const uint8_t *peerZid, std::string *name) {
244 return 0;
245}
246
247void ZIDCacheFile::putPeerName(const uint8_t *peerZid, const std::string name) {
248 return;
249}