1 /*
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
26 #ifdef _MSC_VER
27 #include <io.h>
28 #else
29 #include <unistd.h>
30 #endif
31
32 #include <crypto/zrtpDH.h>
33
34 #include <libzrtpcpp/ZIDCacheFile.h>
35
36
37 static ZIDCacheFile* instance;
38 static int errors = 0; // maybe we will use as member of ZIDCache later...
39
40
41 /**
42 * A poor man's factory.
43 *
44 * The build process must not allow two cache file implementation classes linked
45 * into the same library.
46 */
47
getZidCacheInstance()48 ZIDCache* getZidCacheInstance() {
49
50 if (instance == NULL) {
51 instance = new ZIDCacheFile();
52 }
53 return instance;
54 }
55
56
createZIDFile(char * name)57 void ZIDCacheFile::createZIDFile(char* name) {
58 zidFile = fopen(name, "wb+");
59 // New file, generate an associated random ZID and save
60 // it as first record
61 if (zidFile != NULL) {
62 randomZRTP(associatedZid, IDENTIFIER_LEN);
63
64 ZIDRecordFile rec;
65 rec.setZid(associatedZid);
66 rec.setOwnZIDRecord();
67 fseek(zidFile, 0L, SEEK_SET);
68 if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1)
69 ++errors;
70 fflush(zidFile);
71 }
72 }
73
74 /**
75 * Migrate old ZID file format to new one.
76 *
77 * If ZID file is old format:
78 * - close it, rename it, then re-open
79 * - create ZID file for new format
80 * - copy over contents and flags.
81 */
checkDoMigration(char * name)82 void ZIDCacheFile::checkDoMigration(char* name) {
83 FILE* fdOld;
84 unsigned char inb[2];
85 zidrecord1_t recOld;
86
87 fseek(zidFile, 0L, SEEK_SET);
88 if (fread(inb, 2, 1, zidFile) < 1) {
89 ++errors;
90 inb[0] = 0;
91 }
92
93 if (inb[0] > 0) { // if it's new format just return
94 return;
95 }
96 fclose(zidFile); // close old ZID file
97 zidFile = NULL;
98
99 // create save file name, rename and re-open
100 // if rename fails, just unlink old ZID file and create a brand new file
101 // just a little inconvenience for the user, need to verify new SAS
102 std::string fn = std::string(name) + std::string(".save");
103 if (rename(name, fn.c_str()) < 0) {
104 unlink(name);
105 createZIDFile(name);
106 return;
107 }
108 fdOld = fopen(fn.c_str(), "rb"); // reopen old format in read only mode
109
110 // Get first record from old file - is the own ZID
111 fseek(fdOld, 0L, SEEK_SET);
112 if (fread(&recOld, sizeof(zidrecord1_t), 1, fdOld) != 1) {
113 fclose(fdOld);
114 return;
115 }
116 if (recOld.ownZid != 1) {
117 fclose(fdOld);
118 return;
119 }
120 zidFile = fopen(name, "wb+"); // create new format file in binary r/w mode
121 if (zidFile == NULL) {
122 fclose(fdOld);
123 return;
124 }
125 // create ZIDRecord in new format, copy over own ZID and write the record
126 ZIDRecordFile rec;
127 rec.setZid(recOld.identifier);
128 rec.setOwnZIDRecord();
129 if (fwrite(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) < 1)
130 ++errors;
131
132 // now copy over all valid records from old ZID file format.
133 // Sequentially read old records, sequentially write new records
134 int numRead;
135 do {
136 numRead = fread(&recOld, sizeof(zidrecord1_t), 1, fdOld);
137 if (numRead == 0) { // all old records processed
138 break;
139 }
140 // skip own ZID record and invalid records
141 if (recOld.ownZid == 1 || recOld.recValid == 0) {
142 continue;
143 }
144 ZIDRecordFile rec2;
145 rec2.setZid(recOld.identifier);
146 rec2.setValid();
147 if (recOld.rs1Valid & SASVerified) {
148 rec2.setSasVerified();
149 }
150 rec2.setNewRs1(recOld.rs2Data); // TODO: check squenec
151 rec2.setNewRs1(recOld.rs1Data);
152 if (fwrite(rec2.getRecordData(), rec2.getRecordLength(), 1, zidFile) < 1)
153 ++errors;
154
155 } while (numRead == 1);
156 fclose(fdOld);
157 fflush(zidFile);
158 }
159
~ZIDCacheFile()160 ZIDCacheFile::~ZIDCacheFile() {
161 close();
162 }
163
open(char * name)164 int ZIDCacheFile::open(char* name) {
165
166 // check for an already active ZID file
167 if (zidFile != NULL) {
168 return 0;
169 }
170 if ((zidFile = fopen(name, "rb+")) == NULL) {
171 createZIDFile(name);
172 } else {
173 checkDoMigration(name);
174 if (zidFile != NULL) {
175 ZIDRecordFile rec;
176 fseek(zidFile, 0L, SEEK_SET);
177 if (fread(rec.getRecordData(), rec.getRecordLength(), 1, zidFile) != 1) {
178 fclose(zidFile);
179 zidFile = NULL;
180 return -1;
181 }
182 if (!rec.isOwnZIDRecord()) {
183 fclose(zidFile);
184 zidFile = NULL;
185 return -1;
186 }
187 memcpy(associatedZid, rec.getIdentifier(), IDENTIFIER_LEN);
188 }
189 }
190 return ((zidFile == NULL) ? -1 : 1);
191 }
192
close()193 void ZIDCacheFile::close() {
194
195 if (zidFile != NULL) {
196 fclose(zidFile);
197 zidFile = NULL;
198 }
199 }
200
getRecord(unsigned char * zid)201 ZIDRecord *ZIDCacheFile::getRecord(unsigned char *zid) {
202 unsigned long pos;
203 int numRead;
204 // ZIDRecordFile rec;
205 ZIDRecordFile *zidRecord = new ZIDRecordFile();
206
207 // set read pointer behind first record (
208 fseek(zidFile, zidRecord->getRecordLength(), SEEK_SET);
209
210 do {
211 pos = ftell(zidFile);
212 numRead = fread(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile);
213 if (numRead == 0) {
214 break;
215 }
216
217 // skip own ZID record and invalid records
218 if (zidRecord->isOwnZIDRecord() || !zidRecord->isValid()) {
219 continue;
220 }
221
222 } while (numRead == 1 &&
223 memcmp(zidRecord->getIdentifier(), zid, IDENTIFIER_LEN) != 0);
224
225 // If we reached end of file, then no record with the ZID
226 // found. We need to create a new ZID record.
227 if (numRead == 0) {
228 // create new record
229 delete(zidRecord);
230 zidRecord = new ZIDRecordFile();
231 zidRecord->setZid(zid);
232 zidRecord->setValid();
233 if (fwrite(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile) < 1)
234 ++errors;
235 }
236 // remember position of record in file for save operation
237 zidRecord->setPosition(pos);
238 return zidRecord;
239 }
240
saveRecord(ZIDRecord * zidRec)241 unsigned int ZIDCacheFile::saveRecord(ZIDRecord *zidRec) {
242 ZIDRecordFile *zidRecord = reinterpret_cast<ZIDRecordFile *>(zidRec);
243
244 fseek(zidFile, zidRecord->getPosition(), SEEK_SET);
245 if (fwrite(zidRecord->getRecordData(), zidRecord->getRecordLength(), 1, zidFile) < 1)
246 ++errors;
247 fflush(zidFile);
248 return 1;
249 }
250
getPeerName(const uint8_t * peerZid,std::string * name)251 int32_t ZIDCacheFile::getPeerName(const uint8_t *peerZid, std::string *name) {
252 return 0;
253 }
254
putPeerName(const uint8_t * peerZid,const std::string name)255 void ZIDCacheFile::putPeerName(const uint8_t *peerZid, const std::string name) {
256 return;
257 }
258