crypt/file.cc

Go to the documentation of this file.
00001 // $Id: file.cc,v 1.8 2008-02-18 20:27:18 rafi Exp $
00002 //
00003 // YAPET -- Yet Another Password Encryption Tool
00004 // Copyright (C) 2008  Rafael Ostertag
00005 //
00006 // This program is free software: you can redistribute it and/or modify
00007 // it under the terms of the GNU General Public License as published by
00008 // the Free Software Foundation, either version 3 of the License, or
00009 // (at your option) any later version.
00010 //
00011 // This program is distributed in the hope that it will be useful,
00012 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014 // GNU General Public License for more details.
00015 //
00016 // You should have received a copy of the GNU General Public License
00017 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018 //
00019 
00020 #include "crypt.h"
00021 #include "record.h"
00022 #include "structs.h"
00023 #include "file.h"
00024 
00025 #ifdef TIME_WITH_SYS_TIME
00026 # include <sys/time.h>
00027 # include <time.h>
00028 #else
00029 # ifdef HAVE_SYS_TIME_H
00030 #  include <sys/time.h>
00031 # else
00032 #  include <time.h>
00033 # endif
00034 #endif // TIME_WITH_SYS_TIME
00035 
00036 #ifdef HAVE_INTTYPES_H
00037 # include <inttypes.h>
00038 #endif
00039 
00040 #ifdef HAVE_SYS_TYPES_H
00041 # include <sys/types.h>
00042 #endif
00043 
00044 #ifdef HAVE_SYS_STAT_H
00045 # include <sys/stat.h>
00046 #endif
00047 
00048 #ifdef HAVE_FCNTL_H
00049 # include <fcntl.h>
00050 #endif
00051 
00052 #ifdef HAVE_STRING_H
00053 # include <string.h>
00054 #endif
00055 
00056 #ifdef HAVE_ERRNO_H
00057 # include <errno.h>
00058 #endif
00059 
00060 #ifdef HAVE_UNISTD_H
00061 # include <unistd.h>
00062 #endif
00063 
00064 #ifdef HAVE_ALLOCA_H
00065 # include <alloca.h>
00066 #elif defined __GNUC__
00067 # define alloca __builtin_alloca
00068 #elif defined _AIX
00069 # define alloca __alloca
00070 #elif defined _MSC_VER
00071 # include <malloc.h>
00072 # define alloca _alloca
00073 #else
00074 # include <stddef.h>
00075 #endif
00076 
00077 using namespace YAPET;
00078 
00088 const char CONTROL_STR[] = "ABCDEFGHIJKLMNOPQRSTUVW";
00089 
00098 const char RECOG_STR[] = "YAPET1.0";
00099 
00105 void
00106 File::openCreate() throw(YAPETException) {
00107     fd = ::open(filename.c_str(),
00108         O_RDWR | O_CREAT | O_TRUNC | O_APPEND, S_IRUSR | S_IWUSR);
00109     if (fd == -1)
00110     throw YAPETException(strerror(errno));
00111 }
00112 
00117 void
00118 File::openNoCreate() throw(YAPETException) {
00119     fd = ::open(filename.c_str(), O_RDWR | O_APPEND);
00120     if (fd == -1)
00121     throw YAPETException(strerror(errno));
00122 }
00123 
00130 time_t
00131 File::lastModified() const throw(YAPETException){
00132     struct stat st_buf;
00133     int retval = fstat(fd, &st_buf);
00134     if (retval == -1)
00135     throw YAPETException(strerror(errno));
00136 
00137     return st_buf.st_mtime;
00138 }
00139 
00147 void
00148 File::seekCurr(off_t offset) const throw(YAPETException) {
00149     off_t pos = lseek(fd, offset, SEEK_CUR);
00150     if ( ((off_t)-1) == pos)
00151     throw YAPETException(strerror(errno));
00152 }
00153 
00159 void
00160 File::seekAbs(off_t offset) const throw(YAPETException) {
00161     off_t pos = lseek(fd, offset, SEEK_SET);
00162     if ( ((off_t)-1) == pos)
00163     throw YAPETException(strerror(errno));
00164 
00165     if (pos != offset)
00166     throw YAPETException("Error seeking within file: " + filename);
00167 }
00168 
00173 void
00174 File::preparePWSave() throw(YAPETException) {
00175     BDBuffer* curr_header = readHeader();
00176 
00177     ::close(fd);
00178     try {
00179     openCreate();
00180     } catch (YAPETException& ex) {
00181     if (curr_header != NULL)
00182         delete curr_header;
00183     throw;
00184     }
00185 
00186     mtime = lastModified();
00187     writeHeader(*curr_header);
00188     delete curr_header;
00189 }
00190 
00196 void
00197 File::seekDataSection() const throw(YAPETException) {
00198     seekAbs(strlen(RECOG_STR));
00199     uint32_t len;
00200     int retval = ::read(fd, &len, sizeof(uint32_t));
00201     if (retval == -1)
00202     throw YAPETException(strerror(errno));
00203 
00204     if ( ((size_t)retval) != sizeof(uint32_t))
00205     throw YAPETException("Unable to seek to data section");
00206 
00207     len = uint32_from_disk(len);
00208 
00209     seekCurr(len);
00210 }
00211 
00212 #ifndef WORDS_BIGENDIAN
00213 
00221 uint32_t
00222 File::uint32_to_disk(uint32_t i) const {
00223     ENDIAN endian;
00224     endian.abcd = i;
00225 
00226     uint8_t tmp = endian.dword.b.b;
00227     endian.dword.b.b = endian.dword.a.a;
00228     endian.dword.a.a = tmp;
00229 
00230     tmp = endian.dword.b.a;
00231     endian.dword.b.a = endian.dword.a.b;
00232     endian.dword.a.b = tmp;
00233 
00234     return endian.abcd;
00235 }
00236 
00245 uint32_t
00246 File::uint32_from_disk(uint32_t i) const {
00247     return uint32_to_disk(i);
00248 }
00249 #endif // WORDS_BIGENDIAN
00250 
00268 BDBuffer*
00269 File::read() const throw(YAPETException) {
00270     uint32_t len;
00271     int retval = ::read(fd, &len, sizeof(uint32_t));
00272     if (retval == -1)
00273     throw YAPETException(strerror(errno));
00274 
00275     if (retval == 0)
00276     return NULL;
00277 
00278     if ( ((size_t)retval) < sizeof(uint32_t) )
00279     throw YAPETException("Short read on file: " + filename);
00280 
00281     // Convert len to the endianess of the architecture
00282     len = uint32_from_disk(len);
00283 
00284     BDBuffer* buf = new BDBuffer(len);
00285     retval = ::read(fd, *buf, len);
00286     if (retval == -1)
00287     throw YAPETException(strerror(errno));
00288 
00289     if (retval == 0) {
00290     delete buf;
00291     return NULL;
00292     }
00293 
00294     if (((uint32_t)retval) < len) {
00295     delete buf;
00296     throw YAPETException("Short read on file: " + filename);
00297     }
00298 
00299     return buf;
00300 }
00301 
00324 void
00325 File::write(const BDBuffer& buff, bool forceappend, bool forcewrite)
00326     throw(YAPETException, YAPETRetryException) {
00327     if ( (mtime != lastModified()) && !forcewrite)
00328     throw YAPETRetryException("File has been modified");
00329 
00330     if (forceappend) {
00331     off_t pos = lseek(fd, 0, SEEK_END);
00332     if ( ((off_t)-1) == pos)
00333         throw YAPETException(strerror(errno));
00334     }
00335     uint32_t s = buff.size();
00336 
00337     // Convert s to the on-disk structure
00338     s = uint32_to_disk(s);
00339 
00340     int retval = ::write(fd, &s, sizeof(uint32_t));
00341     if (retval == -1)
00342     throw YAPETException(strerror(errno));
00343     if (retval != sizeof(uint32_t) )
00344     throw YAPETException("Short write on file: " + filename);
00345 
00346     retval = ::write(fd, buff, buff.size());
00347     if (retval == -1)
00348     throw YAPETException(strerror(errno));
00349 
00350     if (((size_t)retval) < buff.size())
00351     throw YAPETException("Short write on file: " + filename);
00352 
00353     mtime = lastModified();
00354 }
00355 
00361 bool
00362 File::isempty() const throw(YAPETException){
00363     struct stat st_buf;
00364     int retval = fstat(fd, &st_buf);
00365     if (retval == -1)
00366     throw YAPETException(strerror(errno));
00367 
00368     if (st_buf.st_size == 0)
00369     return true;
00370 
00371     return false;
00372 }
00373 
00380 void
00381 File::initFile(const Key& key) throw(YAPETException) {
00382     Crypt crypt(key);
00383 
00384     Record<FileHeader> header;
00385     FileHeader* ptr = header;
00386     ptr->version = 1;
00387     memcpy(ptr->control, CONTROL_STR, HEADER_CONTROL_SIZE);
00388     ptr->pwset = uint32_to_disk(time(NULL));
00389 
00390     mtime = lastModified();
00391 
00392     writeHeader(header, key);
00393 
00394     // Sanity checks
00395     BDBuffer* buff = readHeader();
00396     if (buff == NULL)
00397     throw YAPETException("EOF encountered while reading header");
00398 
00399     Record<FileHeader>* dec_hdr = crypt.decrypt<FileHeader>(*buff);
00400 
00401     FileHeader* ptr_dec_hdr = *dec_hdr;
00402 
00403     int retval = memcmp(ptr_dec_hdr->control, ptr->control, HEADER_CONTROL_SIZE);
00404     if (retval != 0)
00405     throw YAPETException("Sanity check for control field failed");
00406 
00407     delete buff;
00408     delete dec_hdr;
00409 }
00410 
00419 void
00420 File::writeHeader(const Record<FileHeader>& header, const Key& key)
00421     throw(YAPETException) {
00422 
00423     Crypt crypt(key);
00424     BDBuffer* buff = NULL;
00425     try {
00426     buff = crypt.encrypt(header);
00427     writeHeader(*buff);
00428     } catch (YAPETException& ex) {
00429     if (buff != NULL)
00430         delete buff;
00431     throw;
00432     } catch (...) {
00433     if (buff != NULL)
00434         delete buff;
00435 
00436     throw YAPETException("Unknown exception catched");
00437     }
00438 
00439     delete buff;
00440 }
00441 
00449 void
00450 File::writeHeader(const BDBuffer& enc_header) throw(YAPETException) {
00451     seekAbs(0);
00452 
00453     // Write the recognition string
00454     ssize_t retval = ::write(fd, RECOG_STR, strlen(RECOG_STR));
00455     if (retval == -1)
00456     throw YAPETException(strerror(errno));
00457 
00458     if (((size_t)retval) != strlen(RECOG_STR) )
00459     throw YAPETException("Short write on file " + filename);
00460 
00461     mtime = lastModified();
00462 
00463     write(enc_header);
00464 }
00465 
00478 BDBuffer*
00479 File::readHeader() const throw(YAPETException) {
00480     seekAbs(0);
00481 
00482     char* buff = (char*) alloca(strlen(RECOG_STR));
00483     if (buff == NULL)
00484     throw YAPETException("Memory exhausted");
00485 
00486     int retval = ::read(fd, buff, strlen(RECOG_STR));
00487     if (retval == -1)
00488     throw YAPETException(strerror(errno));
00489 
00490     if (((size_t)retval) != strlen(RECOG_STR) )
00491     throw YAPETException("File type not recognized");
00492 
00493     retval = memcmp(RECOG_STR, buff, strlen(RECOG_STR));
00494     if (retval != 0)
00495     throw YAPETException("File type not recognized");
00496 
00497     return read();
00498 }
00499 
00511 void
00512 File::validateKey(const Key& key)
00513     throw(YAPETException,YAPETInvalidPasswordException) {
00514 
00515     Crypt crypt(key);
00516     BDBuffer* enc_header = NULL;
00517     Record<FileHeader>* dec_header = NULL;
00518     FileHeader* ptr_dec_header = NULL;
00519 
00520     try {
00521     enc_header = readHeader();
00522     dec_header = crypt.decrypt<FileHeader>(*enc_header);
00523     ptr_dec_header = *dec_header;
00524     } catch (YAPETEncryptionException& ex) {
00525     if (enc_header != NULL) delete enc_header;
00526     if (dec_header != NULL) delete dec_header;
00527     throw YAPETInvalidPasswordException();
00528     } catch (YAPETException& ex) {
00529     if (enc_header != NULL) delete enc_header;
00530     if (dec_header != NULL) delete dec_header;
00531     throw;
00532     }
00533 
00534     int retval = memcmp(ptr_dec_header->control,
00535             CONTROL_STR,
00536             HEADER_CONTROL_SIZE);
00537     delete enc_header;
00538     delete dec_header;
00539     if (retval != 0)
00540     throw YAPETInvalidPasswordException();
00541 }
00542 
00565 File::File(const std::string& fn, const Key& key, bool create)
00566     throw(YAPETException) : filename(fn) {
00567     if (create)
00568     openCreate();
00569     else
00570     openNoCreate();
00571 
00572     if (isempty()) {
00573     initFile(key);
00574     } else {
00575     validateKey(key);
00576     }
00577 }
00578 
00582 File::File(const File& f) throw(YAPETException) {
00583     fd = dup(f.fd);
00584     if (fd == -1)
00585     throw YAPETException(strerror(errno));
00586 
00587     filename = f.filename;
00588     mtime = f.mtime;
00589 }
00590 
00594 File::~File() {
00595     close(fd);
00596 }
00597 
00605 void
00606 File::save(std::list<PartDec>& records) throw(YAPETException) {
00607     preparePWSave();
00608     std::list<PartDec>::iterator it = records.begin();
00609     while (it != records.end() ) {
00610     write( it->getEncRecord());
00611     it++;
00612     }
00613 }
00614 
00628 std::list<PartDec>
00629 File::read(const Key& key) const throw(YAPETException) {
00630     seekDataSection();
00631 
00632     BDBuffer* buff = NULL;
00633     std::list<PartDec> retval;
00634 
00635     try {
00636     buff = read();
00637     while (buff != NULL) {
00638         retval.push_back(PartDec(*buff, key));
00639         delete buff;
00640         buff = read();
00641     }
00642     } catch (YAPETException& ex) {
00643     if (buff != NULL)
00644         delete buff;
00645     throw;
00646     }
00647 
00648     return retval;
00649 }
00650 
00665 void
00666 File::setNewKey(const Key& oldkey,
00667         const Key& newkey) throw (YAPETException) {
00668     close(fd);
00669     std::string backupfilename(filename + ".bak");
00670     int retval = rename(filename.c_str(), backupfilename.c_str());
00671     if (retval == -1) {
00672     // Reopen the old file
00673     openNoCreate();
00674     throw YAPETException(strerror(errno));
00675     }
00676 
00677 
00678     File* oldfile = NULL;
00679     try {
00680     // Reopen the old (backup) file
00681     oldfile = new File(backupfilename, oldkey, false);
00682     // Initialize the (this) file with the new key
00683     openCreate();
00684     initFile(newkey);
00685 
00686     // Retrieve the records encrypted with the old key
00687     std::list<PartDec> entries = oldfile->read(oldkey);
00688     std::list<PartDec>::iterator it = entries.begin();
00689     Crypt oldcrypt(oldkey);
00690     Crypt newcrypt(newkey);
00691     while (it != entries.end() ) {
00692         Record<PasswordRecord>* dec_rec_ptr = NULL;
00693         BDBuffer* new_enc_rec = NULL;
00694         try {
00695         // Decrypt with the old key
00696         const BDBuffer old_enc_rec = (*it).getEncRecord();
00697         dec_rec_ptr =
00698             oldcrypt.decrypt<PasswordRecord>(old_enc_rec);
00699         new_enc_rec =
00700             newcrypt.encrypt(*dec_rec_ptr);
00701         write(*new_enc_rec);
00702         delete dec_rec_ptr;
00703         delete new_enc_rec;
00704         } catch (YAPETException& ex) {
00705         if (dec_rec_ptr != NULL)
00706             delete dec_rec_ptr;
00707         if (new_enc_rec != NULL)
00708             delete new_enc_rec;
00709         throw;
00710         }
00711         it++;
00712     }
00713     } catch (YAPETException& ex) {
00714     if (oldfile != NULL)
00715         delete oldfile;
00716     throw;
00717     }
00718     delete oldfile;
00719 }
00720 
00729 time_t
00730 File::getMasterPWSet(const Key& key) const
00731     throw(YAPETException,YAPETInvalidPasswordException) {
00732     Crypt crypt(key);
00733     BDBuffer* enc_header = NULL;
00734     Record<FileHeader>* dec_header = NULL;
00735     FileHeader* ptr_dec_header = NULL;
00736 
00737     try {
00738     enc_header = readHeader();
00739     dec_header = crypt.decrypt<FileHeader>(*enc_header);
00740     ptr_dec_header = *dec_header;
00741     } catch (YAPETEncryptionException& ex) {
00742     if (enc_header != NULL) delete enc_header;
00743     if (dec_header != NULL) delete dec_header;
00744     throw YAPETInvalidPasswordException();
00745     } catch (YAPETException& ex) {
00746     if (enc_header != NULL) delete enc_header;
00747     if (dec_header != NULL) delete dec_header;
00748     throw;
00749     }
00750 
00751     time_t t = uint32_from_disk(ptr_dec_header->pwset);
00752     delete enc_header;
00753     delete dec_header;
00754 
00755     return t;
00756 }
00757 
00758 
00759 const File&
00760 File::operator=(const File& f) throw(YAPETException) {
00761     if (this == &f) return *this;
00762 
00763     close(fd);
00764 
00765     fd = dup(f.fd);
00766     if (fd == -1)
00767     throw YAPETException(strerror(errno));
00768 
00769     filename = f.filename;
00770 
00771     return *this;
00772 }

Generated on Wed Feb 27 16:15:41 2008 for YAPET by  doxygen 1.5.4