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