diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b124cb8 --- /dev/null +++ b/.clang-format @@ -0,0 +1,52 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +ConstructorInitializerIndentWidth: 4 +AlignEscapedNewlinesLeft: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AlwaysBreakTemplateDeclarations: true +AlwaysBreakBeforeMultilineStrings: true +BreakBeforeBinaryOperators: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BinPackParameters: true +ColumnLimit: 80 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +DerivePointerBinding: true +ExperimentalAutoDetectBinPacking: false +IndentCaseLabels: true +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: false +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakString: 1000 +PenaltyBreakFirstLessLess: 120 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerBindsToType: true +SpacesBeforeTrailingComments: 2 +Cpp11BracedListStyle: true +Standard: Auto +IndentWidth: 2 +TabWidth: 8 +UseTab: Never +BreakBeforeBraces: Attach +IndentFunctionDeclarationAfterType: true +SpacesInParentheses: false +SpacesInAngles: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: true +SpaceBeforeAssignmentOperators: true +ContinuationIndentWidth: 4 +CommentPragmas: '^ IWYU pragma:' +SpaceBeforeParens: ControlStatements +... + diff --git a/encfs/BlockFileIO.cpp b/encfs/BlockFileIO.cpp index 18744c0..c8cd078 100644 --- a/encfs/BlockFileIO.cpp +++ b/encfs/BlockFileIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -27,406 +27,347 @@ #include "i18n.h" -template -inline Type min( Type A, Type B ) -{ - return (B < A) ? B : A; +template +inline Type min(Type A, Type B) { + return (B < A) ? B : A; } -static void clearCache( IORequest &req, int blockSize ) -{ - memset( req.data, 0, blockSize ); - req.dataLen = 0; +static void clearCache(IORequest &req, int blockSize) { + memset(req.data, 0, blockSize); + req.dataLen = 0; } -BlockFileIO::BlockFileIO( int blockSize, const FSConfigPtr &cfg ) - : _blockSize( blockSize ) - , _allowHoles( cfg->config->allowHoles ) -{ - rAssert( _blockSize > 1 ); - _cache.data = new unsigned char [ _blockSize ]; +BlockFileIO::BlockFileIO(int blockSize, const FSConfigPtr &cfg) + : _blockSize(blockSize), _allowHoles(cfg->config->allowHoles) { + rAssert(_blockSize > 1); + _cache.data = new unsigned char[_blockSize]; } -BlockFileIO::~BlockFileIO() -{ - clearCache( _cache, _blockSize ); - delete[] _cache.data; +BlockFileIO::~BlockFileIO() { + clearCache(_cache, _blockSize); + delete[] _cache.data; } -ssize_t BlockFileIO::cacheReadOneBlock( const IORequest &req ) const -{ - // we can satisfy the request even if _cache.dataLen is too short, because - // we always request a full block during reads.. - if((req.offset == _cache.offset) && (_cache.dataLen != 0)) - { - // satisfy request from cache - int len = req.dataLen; - if(_cache.dataLen < len) - len = _cache.dataLen; - memcpy( req.data, _cache.data, len ); - return len; - } else - { - if(_cache.dataLen > 0) - clearCache( _cache, _blockSize ); +ssize_t BlockFileIO::cacheReadOneBlock(const IORequest &req) const { + // we can satisfy the request even if _cache.dataLen is too short, because + // we always request a full block during reads.. + if ((req.offset == _cache.offset) && (_cache.dataLen != 0)) { + // satisfy request from cache + int len = req.dataLen; + if (_cache.dataLen < len) len = _cache.dataLen; + memcpy(req.data, _cache.data, len); + return len; + } else { + if (_cache.dataLen > 0) clearCache(_cache, _blockSize); - // cache results of read -- issue reads for full blocks - IORequest tmp; - tmp.offset = req.offset; - tmp.data = _cache.data; - tmp.dataLen = _blockSize; - ssize_t result = readOneBlock( tmp ); - if(result > 0) - { - _cache.offset = req.offset; - _cache.dataLen = result; // the amount we really have - if(result > req.dataLen) - result = req.dataLen; // only as much as requested - memcpy( req.data, _cache.data, result ); - } - return result; + // cache results of read -- issue reads for full blocks + IORequest tmp; + tmp.offset = req.offset; + tmp.data = _cache.data; + tmp.dataLen = _blockSize; + ssize_t result = readOneBlock(tmp); + if (result > 0) { + _cache.offset = req.offset; + _cache.dataLen = result; // the amount we really have + if (result > req.dataLen) + result = req.dataLen; // only as much as requested + memcpy(req.data, _cache.data, result); } -} - -bool BlockFileIO::cacheWriteOneBlock( const IORequest &req ) -{ - // cache results of write (before pass-thru, because it may be modified - // in-place) - memcpy( _cache.data, req.data, req.dataLen ); - _cache.offset = req.offset; - _cache.dataLen = req.dataLen; - bool ok = writeOneBlock( req ); - if(!ok) - clearCache( _cache, _blockSize ); - return ok; -} - -ssize_t BlockFileIO::read( const IORequest &req ) const -{ - rAssert( _blockSize != 0 ); - - int partialOffset = req.offset % _blockSize; - off_t blockNum = req.offset / _blockSize; - ssize_t result = 0; - - if(partialOffset == 0 && req.dataLen <= _blockSize) - { - // read completely within a single block -- can be handled as-is by - // readOneBloc(). - return cacheReadOneBlock( req ); - } else - { - size_t size = req.dataLen; - - // if the request is larger then a block, then request each block - // individually - MemBlock mb; // in case we need to allocate a temporary block.. - IORequest blockReq; // for requests we may need to make - blockReq.dataLen = _blockSize; - blockReq.data = NULL; - - unsigned char *out = req.data; - while( size ) - { - blockReq.offset = blockNum * _blockSize; - - // if we're reading a full block, then read directly into the - // result buffer instead of using a temporary - if(partialOffset == 0 && size >= (size_t)_blockSize) - blockReq.data = out; - else - { - if(!mb.data) - mb = MemoryPool::allocate( _blockSize ); - blockReq.data = mb.data; - } - - ssize_t readSize = cacheReadOneBlock( blockReq ); - if(unlikely(readSize <= partialOffset)) - break; // didn't get enough bytes - - int cpySize = min( (size_t)(readSize - partialOffset), size ); - rAssert(cpySize <= readSize); - - // if we read to a temporary buffer, then move the data - if(blockReq.data != out) - memcpy( out, blockReq.data + partialOffset, cpySize ); - - result += cpySize; - size -= cpySize; - out += cpySize; - ++blockNum; - partialOffset = 0; - - if(unlikely(readSize < _blockSize)) - break; - } - - if(mb.data) - MemoryPool::release( mb ); - } - return result; + } } -bool BlockFileIO::write( const IORequest &req ) -{ - rAssert( _blockSize != 0 ); +bool BlockFileIO::cacheWriteOneBlock(const IORequest &req) { + // cache results of write (before pass-thru, because it may be modified + // in-place) + memcpy(_cache.data, req.data, req.dataLen); + _cache.offset = req.offset; + _cache.dataLen = req.dataLen; + bool ok = writeOneBlock(req); + if (!ok) clearCache(_cache, _blockSize); + return ok; +} - off_t fileSize = getSize(); +ssize_t BlockFileIO::read(const IORequest &req) const { + rAssert(_blockSize != 0); - // where write request begins - off_t blockNum = req.offset / _blockSize; - int partialOffset = req.offset % _blockSize; + int partialOffset = req.offset % _blockSize; + off_t blockNum = req.offset / _blockSize; + ssize_t result = 0; - // last block of file (for testing write overlaps with file boundary) - off_t lastFileBlock = fileSize / _blockSize; - ssize_t lastBlockSize = fileSize % _blockSize; - - off_t lastNonEmptyBlock = lastFileBlock; - if(lastBlockSize == 0) - --lastNonEmptyBlock; - - if( req.offset > fileSize ) - { - // extend file first to fill hole with 0's.. - const bool forceWrite = false; - padFile( fileSize, req.offset, forceWrite ); - } - - // check against edge cases where we can just let the base class handle the - // request as-is.. - if(partialOffset == 0 && req.dataLen <= _blockSize) - { - // if writing a full block.. pretty safe.. - if( req.dataLen == _blockSize ) - return cacheWriteOneBlock( req ); - - // if writing a partial block, but at least as much as what is - // already there.. - if(blockNum == lastFileBlock && req.dataLen >= lastBlockSize) - return cacheWriteOneBlock( req ); - } - - // have to merge data with existing block(s).. - MemBlock mb; - - IORequest blockReq; - blockReq.data = NULL; - blockReq.dataLen = _blockSize; - - bool ok = true; + if (partialOffset == 0 && req.dataLen <= _blockSize) { + // read completely within a single block -- can be handled as-is by + // readOneBloc(). + return cacheReadOneBlock(req); + } else { size_t size = req.dataLen; - unsigned char *inPtr = req.data; - while( size ) - { - blockReq.offset = blockNum * _blockSize; - int toCopy = min((size_t)(_blockSize - partialOffset), size); - // if writing an entire block, or writing a partial block that requires - // no merging with existing data.. - if( (toCopy == _blockSize) - ||(partialOffset == 0 && blockReq.offset + toCopy >= fileSize)) - { - // write directly from buffer - blockReq.data = inPtr; - blockReq.dataLen = toCopy; - } else - { - // need a temporary buffer, since we have to either merge or pad - // the data. - if(!mb.data) - mb = MemoryPool::allocate( _blockSize ); - memset( mb.data, 0, _blockSize ); - blockReq.data = mb.data; + // if the request is larger then a block, then request each block + // individually + MemBlock mb; // in case we need to allocate a temporary block.. + IORequest blockReq; // for requests we may need to make + blockReq.dataLen = _blockSize; + blockReq.data = NULL; - if(blockNum > lastNonEmptyBlock) - { - // just pad.. - blockReq.dataLen = toCopy + partialOffset; - } else - { - // have to merge with existing block data.. - blockReq.dataLen = _blockSize; - blockReq.dataLen = cacheReadOneBlock( blockReq ); + unsigned char *out = req.data; + while (size) { + blockReq.offset = blockNum * _blockSize; - // extend data if necessary.. - if( partialOffset + toCopy > blockReq.dataLen ) - blockReq.dataLen = partialOffset + toCopy; - } - // merge in the data to be written.. - memcpy( blockReq.data + partialOffset, inPtr, toCopy ); - } + // if we're reading a full block, then read directly into the + // result buffer instead of using a temporary + if (partialOffset == 0 && size >= (size_t)_blockSize) + blockReq.data = out; + else { + if (!mb.data) mb = MemoryPool::allocate(_blockSize); + blockReq.data = mb.data; + } - // Finally, write the damn thing! - if(!cacheWriteOneBlock( blockReq )) - { - ok = false; - break; - } + ssize_t readSize = cacheReadOneBlock(blockReq); + if (unlikely(readSize <= partialOffset)) + break; // didn't get enough bytes - // prepare to start all over with the next block.. - size -= toCopy; - inPtr += toCopy; - ++blockNum; - partialOffset = 0; + int cpySize = min((size_t)(readSize - partialOffset), size); + rAssert(cpySize <= readSize); + + // if we read to a temporary buffer, then move the data + if (blockReq.data != out) + memcpy(out, blockReq.data + partialOffset, cpySize); + + result += cpySize; + size -= cpySize; + out += cpySize; + ++blockNum; + partialOffset = 0; + + if (unlikely(readSize < _blockSize)) break; } - if(mb.data) - MemoryPool::release( mb ); + if (mb.data) MemoryPool::release(mb); + } - return ok; + return result; } -int BlockFileIO::blockSize() const -{ - return _blockSize; +bool BlockFileIO::write(const IORequest &req) { + rAssert(_blockSize != 0); + + off_t fileSize = getSize(); + + // where write request begins + off_t blockNum = req.offset / _blockSize; + int partialOffset = req.offset % _blockSize; + + // last block of file (for testing write overlaps with file boundary) + off_t lastFileBlock = fileSize / _blockSize; + ssize_t lastBlockSize = fileSize % _blockSize; + + off_t lastNonEmptyBlock = lastFileBlock; + if (lastBlockSize == 0) --lastNonEmptyBlock; + + if (req.offset > fileSize) { + // extend file first to fill hole with 0's.. + const bool forceWrite = false; + padFile(fileSize, req.offset, forceWrite); + } + + // check against edge cases where we can just let the base class handle the + // request as-is.. + if (partialOffset == 0 && req.dataLen <= _blockSize) { + // if writing a full block.. pretty safe.. + if (req.dataLen == _blockSize) return cacheWriteOneBlock(req); + + // if writing a partial block, but at least as much as what is + // already there.. + if (blockNum == lastFileBlock && req.dataLen >= lastBlockSize) + return cacheWriteOneBlock(req); + } + + // have to merge data with existing block(s).. + MemBlock mb; + + IORequest blockReq; + blockReq.data = NULL; + blockReq.dataLen = _blockSize; + + bool ok = true; + size_t size = req.dataLen; + unsigned char *inPtr = req.data; + while (size) { + blockReq.offset = blockNum * _blockSize; + int toCopy = min((size_t)(_blockSize - partialOffset), size); + + // if writing an entire block, or writing a partial block that requires + // no merging with existing data.. + if ((toCopy == _blockSize) || + (partialOffset == 0 && blockReq.offset + toCopy >= fileSize)) { + // write directly from buffer + blockReq.data = inPtr; + blockReq.dataLen = toCopy; + } else { + // need a temporary buffer, since we have to either merge or pad + // the data. + if (!mb.data) mb = MemoryPool::allocate(_blockSize); + memset(mb.data, 0, _blockSize); + blockReq.data = mb.data; + + if (blockNum > lastNonEmptyBlock) { + // just pad.. + blockReq.dataLen = toCopy + partialOffset; + } else { + // have to merge with existing block data.. + blockReq.dataLen = _blockSize; + blockReq.dataLen = cacheReadOneBlock(blockReq); + + // extend data if necessary.. + if (partialOffset + toCopy > blockReq.dataLen) + blockReq.dataLen = partialOffset + toCopy; + } + // merge in the data to be written.. + memcpy(blockReq.data + partialOffset, inPtr, toCopy); + } + + // Finally, write the damn thing! + if (!cacheWriteOneBlock(blockReq)) { + ok = false; + break; + } + + // prepare to start all over with the next block.. + size -= toCopy; + inPtr += toCopy; + ++blockNum; + partialOffset = 0; + } + + if (mb.data) MemoryPool::release(mb); + + return ok; } -void BlockFileIO::padFile( off_t oldSize, off_t newSize, bool forceWrite ) -{ - off_t oldLastBlock = oldSize / _blockSize; - off_t newLastBlock = newSize / _blockSize; - int newBlockSize = newSize % _blockSize; +int BlockFileIO::blockSize() const { return _blockSize; } + +void BlockFileIO::padFile(off_t oldSize, off_t newSize, bool forceWrite) { + off_t oldLastBlock = oldSize / _blockSize; + off_t newLastBlock = newSize / _blockSize; + int newBlockSize = newSize % _blockSize; + + IORequest req; + MemBlock mb; + + if (oldLastBlock == newLastBlock) { + // when the real write occurs, it will have to read in the existing + // data and pad it anyway, so we won't do it here (unless we're + // forced). + if (forceWrite) { + mb = MemoryPool::allocate(_blockSize); + req.data = mb.data; + + req.offset = oldLastBlock * _blockSize; + req.dataLen = oldSize % _blockSize; + int outSize = newSize % _blockSize; // outSize > req.dataLen + + if (outSize) { + memset(mb.data, 0, outSize); + cacheReadOneBlock(req); + req.dataLen = outSize; + cacheWriteOneBlock(req); + } + } else + rDebug("optimization: not padding last block"); + } else { + mb = MemoryPool::allocate(_blockSize); + req.data = mb.data; + + // 1. extend the first block to full length + // 2. write the middle empty blocks + // 3. write the last block + + req.offset = oldLastBlock * _blockSize; + req.dataLen = oldSize % _blockSize; + + // 1. req.dataLen == 0, iff oldSize was already a multiple of blocksize + if (req.dataLen != 0) { + rDebug("padding block %" PRIi64, oldLastBlock); + memset(mb.data, 0, _blockSize); + cacheReadOneBlock(req); + req.dataLen = _blockSize; // expand to full block size + cacheWriteOneBlock(req); + ++oldLastBlock; + } + + // 2, pad zero blocks unless holes are allowed + if (!_allowHoles) { + for (; oldLastBlock != newLastBlock; ++oldLastBlock) { + rDebug("padding block %" PRIi64, oldLastBlock); + req.offset = oldLastBlock * _blockSize; + req.dataLen = _blockSize; + memset(mb.data, 0, req.dataLen); + cacheWriteOneBlock(req); + } + } + + // 3. only necessary if write is forced and block is non 0 length + if (forceWrite && newBlockSize) { + req.offset = newLastBlock * _blockSize; + req.dataLen = newBlockSize; + memset(mb.data, 0, req.dataLen); + cacheWriteOneBlock(req); + } + } + + if (mb.data) MemoryPool::release(mb); +} + +int BlockFileIO::truncateBase(off_t size, FileIO *base) { + int partialBlock = size % _blockSize; + int res = 0; + + off_t oldSize = getSize(); + + if (size > oldSize) { + // truncate can be used to extend a file as well. truncate man page + // states that it will pad with 0's. + // do the truncate so that the underlying filesystem can allocate + // the space, and then we'll fill it in padFile.. + if (base) base->truncate(size); + + const bool forceWrite = true; + padFile(oldSize, size, forceWrite); + } else if (size == oldSize) { + // the easiest case, but least likely.... + } else if (partialBlock) { + // partial block after truncate. Need to read in the block being + // truncated before the truncate. Then write it back out afterwards, + // since the encoding will change.. + off_t blockNum = size / _blockSize; + MemBlock mb = MemoryPool::allocate(_blockSize); IORequest req; - MemBlock mb; + req.offset = blockNum * _blockSize; + req.dataLen = _blockSize; + req.data = mb.data; - if(oldLastBlock == newLastBlock) - { - // when the real write occurs, it will have to read in the existing - // data and pad it anyway, so we won't do it here (unless we're - // forced). - if( forceWrite ) - { - mb = MemoryPool::allocate( _blockSize ); - req.data = mb.data; + ssize_t rdSz = cacheReadOneBlock(req); - req.offset = oldLastBlock * _blockSize; - req.dataLen = oldSize % _blockSize; - int outSize = newSize % _blockSize; // outSize > req.dataLen + // do the truncate + if (base) res = base->truncate(size); - if(outSize) - { - memset( mb.data, 0, outSize ); - cacheReadOneBlock( req ); - req.dataLen = outSize; - cacheWriteOneBlock( req ); - } - } else - rDebug("optimization: not padding last block"); - } else - { - mb = MemoryPool::allocate( _blockSize ); - req.data = mb.data; + // write back out partial block + req.dataLen = partialBlock; + bool wrRes = cacheWriteOneBlock(req); - // 1. extend the first block to full length - // 2. write the middle empty blocks - // 3. write the last block - - req.offset = oldLastBlock * _blockSize; - req.dataLen = oldSize % _blockSize; - - // 1. req.dataLen == 0, iff oldSize was already a multiple of blocksize - if(req.dataLen != 0) - { - rDebug("padding block %" PRIi64, oldLastBlock); - memset( mb.data, 0, _blockSize ); - cacheReadOneBlock( req ); - req.dataLen = _blockSize; // expand to full block size - cacheWriteOneBlock( req ); - ++oldLastBlock; - } - - // 2, pad zero blocks unless holes are allowed - if(!_allowHoles) - { - for(; oldLastBlock != newLastBlock; ++oldLastBlock) - { - rDebug("padding block %" PRIi64, oldLastBlock); - req.offset = oldLastBlock * _blockSize; - req.dataLen = _blockSize; - memset( mb.data, 0, req.dataLen ); - cacheWriteOneBlock( req ); - } - } - - // 3. only necessary if write is forced and block is non 0 length - if(forceWrite && newBlockSize) - { - req.offset = newLastBlock * _blockSize; - req.dataLen = newBlockSize; - memset( mb.data, 0, req.dataLen ); - cacheWriteOneBlock( req ); - } + if ((rdSz < 0) || (!wrRes)) { + // rwarning - unlikely to ever occur.. + rWarning(_("truncate failure: read %i bytes, partial block of %i"), + (int)rdSz, partialBlock); } - if(mb.data) - MemoryPool::release( mb ); + MemoryPool::release(mb); + } else { + // truncating on a block bounday. No need to re-encode the last + // block.. + if (base) res = base->truncate(size); + } + + return res; } - -int BlockFileIO::truncateBase( off_t size, FileIO *base ) -{ - int partialBlock = size % _blockSize; - int res = 0; - - off_t oldSize = getSize(); - - if( size > oldSize ) - { - // truncate can be used to extend a file as well. truncate man page - // states that it will pad with 0's. - // do the truncate so that the underlying filesystem can allocate - // the space, and then we'll fill it in padFile.. - if(base) - base->truncate( size ); - - const bool forceWrite = true; - padFile( oldSize, size, forceWrite ); - } else - if( size == oldSize ) - { - // the easiest case, but least likely.... - } else - if( partialBlock ) - { - // partial block after truncate. Need to read in the block being - // truncated before the truncate. Then write it back out afterwards, - // since the encoding will change.. - off_t blockNum = size / _blockSize; - MemBlock mb = MemoryPool::allocate( _blockSize ); - - IORequest req; - req.offset = blockNum * _blockSize; - req.dataLen = _blockSize; - req.data = mb.data; - - ssize_t rdSz = cacheReadOneBlock( req ); - - // do the truncate - if(base) - res = base->truncate( size ); - - // write back out partial block - req.dataLen = partialBlock; - bool wrRes = cacheWriteOneBlock( req ); - - if((rdSz < 0) || (!wrRes)) - { - // rwarning - unlikely to ever occur.. - rWarning(_("truncate failure: read %i bytes, partial block of %i"), - (int)rdSz, partialBlock ); - } - - MemoryPool::release( mb ); - } else - { - // truncating on a block bounday. No need to re-encode the last - // block.. - if(base) - res = base->truncate( size ); - } - - return res; -} - diff --git a/encfs/BlockFileIO.h b/encfs/BlockFileIO.h index 8ce639c..0a09a41 100644 --- a/encfs/BlockFileIO.h +++ b/encfs/BlockFileIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -26,43 +26,40 @@ /* Implements block scatter / gather interface. Requires derived classes to - implement readOneBlock() / writeOneBlock() at a minimum. + implement readOneBlock() / writeOneBlock() at a minimum. When a partial block write is requested it will be turned into a read of the existing block, merge with the write request, and a write of the full block. */ -class BlockFileIO : public FileIO -{ -public: - BlockFileIO( int blockSize, const FSConfigPtr &cfg ); - virtual ~BlockFileIO(); +class BlockFileIO : public FileIO { + public: + BlockFileIO(int blockSize, const FSConfigPtr &cfg); + virtual ~BlockFileIO(); - // implemented in terms of blocks. - virtual ssize_t read( const IORequest &req ) const; - virtual bool write( const IORequest &req ); + // implemented in terms of blocks. + virtual ssize_t read(const IORequest &req) const; + virtual bool write(const IORequest &req); - virtual int blockSize() const; + virtual int blockSize() const; -protected: + protected: + int truncateBase(off_t size, FileIO *base); + void padFile(off_t oldSize, off_t newSize, bool forceWrite); - int truncateBase( off_t size, FileIO *base ); - void padFile( off_t oldSize, off_t newSize, bool forceWrite ); + // same as read(), except that the request.offset field is guarenteed to be + // block aligned, and the request size will not be larger then 1 block. + virtual ssize_t readOneBlock(const IORequest &req) const = 0; + virtual bool writeOneBlock(const IORequest &req) = 0; - // same as read(), except that the request.offset field is guarenteed to be - // block aligned, and the request size will not be larger then 1 block. - virtual ssize_t readOneBlock( const IORequest &req ) const =0; - virtual bool writeOneBlock( const IORequest &req ) =0; - - ssize_t cacheReadOneBlock( const IORequest &req ) const; - bool cacheWriteOneBlock( const IORequest &req ); + ssize_t cacheReadOneBlock(const IORequest &req) const; + bool cacheWriteOneBlock(const IORequest &req); - int _blockSize; - bool _allowHoles; + int _blockSize; + bool _allowHoles; - // cache last block for speed... - mutable IORequest _cache; + // cache last block for speed... + mutable IORequest _cache; }; #endif - diff --git a/encfs/BlockNameIO.cpp b/encfs/BlockNameIO.cpp index 821329c..54e7b21 100644 --- a/encfs/BlockNameIO.cpp +++ b/encfs/BlockNameIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -33,44 +33,42 @@ using namespace rlog; using namespace rel; -static RLogChannel * Info = DEF_CHANNEL( "info/nameio", Log_Info ); +static RLogChannel *Info = DEF_CHANNEL("info/nameio", Log_Info); +static shared_ptr NewBlockNameIO(const Interface &iface, + const shared_ptr &cipher, + const CipherKey &key) { + int blockSize = 8; + if (cipher) blockSize = cipher->cipherBlockSize(); -static shared_ptr NewBlockNameIO( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) -{ - int blockSize = 8; - if(cipher) - blockSize = cipher->cipherBlockSize(); - - return shared_ptr( - new BlockNameIO( iface, cipher, key, blockSize, false)); + return shared_ptr( + new BlockNameIO(iface, cipher, key, blockSize, false)); } -static shared_ptr NewBlockNameIO32( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) -{ - int blockSize = 8; - if(cipher) - blockSize = cipher->cipherBlockSize(); +static shared_ptr NewBlockNameIO32(const Interface &iface, + const shared_ptr &cipher, + const CipherKey &key) { + int blockSize = 8; + if (cipher) blockSize = cipher->cipherBlockSize(); - return shared_ptr( - new BlockNameIO( iface, cipher, key, blockSize, true)); + return shared_ptr( + new BlockNameIO(iface, cipher, key, blockSize, true)); } -static bool BlockIO_registered = NameIO::Register("Block", - // description of block name encoding algorithm.. - // xgroup(setup) - gettext_noop("Block encoding, hides file name size somewhat"), - BlockNameIO::CurrentInterface(false), - NewBlockNameIO); +static bool BlockIO_registered = NameIO::Register( + "Block", + // description of block name encoding algorithm.. + // xgroup(setup) + gettext_noop("Block encoding, hides file name size somewhat"), + BlockNameIO::CurrentInterface(false), NewBlockNameIO); -static bool BlockIO32_registered = NameIO::Register("Block32", - // description of block name encoding algorithm.. - // xgroup(setup) - gettext_noop("Block encoding with base32 output for case-sensitive systems"), - BlockNameIO::CurrentInterface(true), - NewBlockNameIO32); +static bool BlockIO32_registered = NameIO::Register( + "Block32", + // description of block name encoding algorithm.. + // xgroup(setup) + gettext_noop( + "Block encoding with base32 output for case-sensitive systems"), + BlockNameIO::CurrentInterface(true), NewBlockNameIO32); /* - Version 1.0 computed MAC over the filename, but not the padding bytes. @@ -89,180 +87,153 @@ static bool BlockIO32_registered = NameIO::Register("Block32", - Version 4.0 adds support for base32, creating names more suitable for case-insensitive filesystems (eg Mac). */ -Interface BlockNameIO::CurrentInterface(bool caseSensitive) -{ - // implement major version 4 plus support for two prior versions - if (caseSensitive) - return Interface("nameio/block32", 4, 0, 2); - else - return Interface("nameio/block", 4, 0, 2); +Interface BlockNameIO::CurrentInterface(bool caseSensitive) { + // implement major version 4 plus support for two prior versions + if (caseSensitive) + return Interface("nameio/block32", 4, 0, 2); + else + return Interface("nameio/block", 4, 0, 2); } -BlockNameIO::BlockNameIO( const rel::Interface &iface, - const shared_ptr &cipher, - const CipherKey &key, int blockSize, - bool caseSensitiveEncoding ) - : _interface( iface.current() ) - , _bs( blockSize ) - , _cipher( cipher ) - , _key( key ) - , _caseSensitive( caseSensitiveEncoding ) -{ - // just to be safe.. - rAssert( blockSize < 128 ); +BlockNameIO::BlockNameIO(const rel::Interface &iface, + const shared_ptr &cipher, const CipherKey &key, + int blockSize, bool caseSensitiveEncoding) + : _interface(iface.current()), + _bs(blockSize), + _cipher(cipher), + _key(key), + _caseSensitive(caseSensitiveEncoding) { + // just to be safe.. + rAssert(blockSize < 128); } -BlockNameIO::~BlockNameIO() -{ +BlockNameIO::~BlockNameIO() {} + +Interface BlockNameIO::interface() const { + return CurrentInterface(_caseSensitive); } -Interface BlockNameIO::interface() const -{ - return CurrentInterface(_caseSensitive); +int BlockNameIO::maxEncodedNameLen(int plaintextNameLen) const { + // number of blocks, rounded up.. Only an estimate at this point, err on + // the size of too much space rather then too little. + int numBlocks = (plaintextNameLen + _bs) / _bs; + int encodedNameLen = numBlocks * _bs + 2; // 2 checksum bytes + if (_caseSensitive) + return B256ToB32Bytes(encodedNameLen); + else + return B256ToB64Bytes(encodedNameLen); } -int BlockNameIO::maxEncodedNameLen( int plaintextNameLen ) const -{ - // number of blocks, rounded up.. Only an estimate at this point, err on - // the size of too much space rather then too little. - int numBlocks = ( plaintextNameLen + _bs ) / _bs; - int encodedNameLen = numBlocks * _bs + 2; // 2 checksum bytes - if (_caseSensitive) - return B256ToB32Bytes( encodedNameLen ); - else - return B256ToB64Bytes( encodedNameLen ); +int BlockNameIO::maxDecodedNameLen(int encodedNameLen) const { + int decLen256 = _caseSensitive ? B32ToB256Bytes(encodedNameLen) + : B64ToB256Bytes(encodedNameLen); + return decLen256 - 2; // 2 checksum bytes removed.. } -int BlockNameIO::maxDecodedNameLen( int encodedNameLen ) const -{ - int decLen256 = _caseSensitive ? - B32ToB256Bytes( encodedNameLen ) : - B64ToB256Bytes( encodedNameLen ); - return decLen256 - 2; // 2 checksum bytes removed.. +int BlockNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv, + char *encodedName) const { + // copy the data into the encoding buffer.. + memcpy(encodedName + 2, plaintextName, length); + + // Pad encryption buffer to block boundary.. + int padding = _bs - length % _bs; + if (padding == 0) padding = _bs; // padding a full extra block! + + memset(encodedName + length + 2, (unsigned char)padding, padding); + + // store the IV before it is modified by the MAC call. + uint64_t tmpIV = 0; + if (iv && _interface >= 3) tmpIV = *iv; + + // include padding in MAC computation + unsigned int mac = _cipher->MAC_16((unsigned char *)encodedName + 2, + length + padding, _key, iv); + + // add checksum bytes + encodedName[0] = (mac >> 8) & 0xff; + encodedName[1] = (mac) & 0xff; + + _cipher->blockEncode((unsigned char *)encodedName + 2, length + padding, + (uint64_t)mac ^ tmpIV, _key); + + // convert to base 64 ascii + int encodedStreamLen = length + 2 + padding; + int encLen; + + if (_caseSensitive) { + encLen = B256ToB32Bytes(encodedStreamLen); + + changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 5, + true); + B32ToAscii((unsigned char *)encodedName, encLen); + } else { + encLen = B256ToB64Bytes(encodedStreamLen); + + changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 6, + true); + B64ToAscii((unsigned char *)encodedName, encLen); + } + + return encLen; } -int BlockNameIO::encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const -{ - // copy the data into the encoding buffer.. - memcpy( encodedName+2, plaintextName, length ); - - // Pad encryption buffer to block boundary.. - int padding = _bs - length % _bs; - if(padding == 0) - padding = _bs; // padding a full extra block! +int BlockNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, + char *plaintextName) const { + int decLen256 = + _caseSensitive ? B32ToB256Bytes(length) : B64ToB256Bytes(length); + int decodedStreamLen = decLen256 - 2; - memset( encodedName+length+2, (unsigned char)padding, padding ); - - // store the IV before it is modified by the MAC call. - uint64_t tmpIV = 0; - if( iv && _interface >= 3 ) - tmpIV = *iv; + // don't bother trying to decode files which are too small + if (decodedStreamLen < _bs) throw ERROR("Filename too small to decode"); - // include padding in MAC computation - unsigned int mac = _cipher->MAC_16( (unsigned char *)encodedName+2, - length+padding, _key, iv ); + BUFFER_INIT(tmpBuf, 32, (unsigned int)length); - // add checksum bytes - encodedName[0] = (mac >> 8) & 0xff; - encodedName[1] = (mac ) & 0xff; + // decode into tmpBuf, + if (_caseSensitive) { + AsciiToB32((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); + changeBase2Inline((unsigned char *)tmpBuf, length, 5, 8, false); + } else { + AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); + changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); + } - _cipher->blockEncode( (unsigned char *)encodedName+2, length+padding, - (uint64_t)mac ^ tmpIV, _key); + // pull out the header information + unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 | + ((unsigned int)((unsigned char)tmpBuf[1])); - // convert to base 64 ascii - int encodedStreamLen = length + 2 + padding; - int encLen; - - if (_caseSensitive) - { - encLen = B256ToB32Bytes( encodedStreamLen ); + uint64_t tmpIV = 0; + if (iv && _interface >= 3) tmpIV = *iv; - changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, - 8, 5, true ); - B32ToAscii( (unsigned char *)encodedName, encLen ); - } else - { - encLen = B256ToB64Bytes( encodedStreamLen ); + _cipher->blockDecode((unsigned char *)tmpBuf + 2, decodedStreamLen, + (uint64_t)mac ^ tmpIV, _key); - changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, - 8, 6, true ); - B64ToAscii( (unsigned char *)encodedName, encLen ); - } + // find out true string length + int padding = (unsigned char)tmpBuf[2 + decodedStreamLen - 1]; + int finalSize = decodedStreamLen - padding; - return encLen; -} - -int BlockNameIO::decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const -{ - int decLen256 = _caseSensitive ? - B32ToB256Bytes( length ) : - B64ToB256Bytes( length ); - int decodedStreamLen = decLen256 - 2; - - // don't bother trying to decode files which are too small - if(decodedStreamLen < _bs) - throw ERROR("Filename too small to decode"); - - BUFFER_INIT( tmpBuf, 32, (unsigned int)length ); - - // decode into tmpBuf, - if (_caseSensitive) - { - AsciiToB32((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); - changeBase2Inline((unsigned char *)tmpBuf, length, 5, 8, false); - } else - { - AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); - changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); - } - - // pull out the header information - unsigned int mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 - | ((unsigned int)((unsigned char)tmpBuf[1])); - - uint64_t tmpIV = 0; - if( iv && _interface >= 3 ) - tmpIV = *iv; - - _cipher->blockDecode( (unsigned char *)tmpBuf+2, decodedStreamLen, - (uint64_t)mac ^ tmpIV, _key); - - // find out true string length - int padding = (unsigned char)tmpBuf[2+decodedStreamLen-1]; - int finalSize = decodedStreamLen - padding; - - // might happen if there is an error decoding.. - if(padding > _bs || finalSize < 0) - { - rDebug("padding, _bx, finalSize = %i, %i, %i", padding, - _bs, finalSize); - throw ERROR( "invalid padding size" ); - } - - // copy out the result.. - memcpy(plaintextName, tmpBuf+2, finalSize); - plaintextName[finalSize] = '\0'; - - // check the mac - unsigned int mac2 = _cipher->MAC_16((const unsigned char *)tmpBuf+2, - decodedStreamLen, _key, iv); - - BUFFER_RESET( tmpBuf ); - - if(mac2 != mac) - { - rDebug("checksum mismatch: expected %u, got %u", mac, mac2); - rDebug("on decode of %i bytes", finalSize); - throw ERROR( "checksum mismatch in filename decode" ); - } - - return finalSize; -} - -bool BlockNameIO::Enabled() -{ - return true; + // might happen if there is an error decoding.. + if (padding > _bs || finalSize < 0) { + rDebug("padding, _bx, finalSize = %i, %i, %i", padding, _bs, finalSize); + throw ERROR("invalid padding size"); + } + + // copy out the result.. + memcpy(plaintextName, tmpBuf + 2, finalSize); + plaintextName[finalSize] = '\0'; + + // check the mac + unsigned int mac2 = _cipher->MAC_16((const unsigned char *)tmpBuf + 2, + decodedStreamLen, _key, iv); + + BUFFER_RESET(tmpBuf); + + if (mac2 != mac) { + rDebug("checksum mismatch: expected %u, got %u", mac, mac2); + rDebug("on decode of %i bytes", finalSize); + throw ERROR("checksum mismatch in filename decode"); + } + + return finalSize; } +bool BlockNameIO::Enabled() { return true; } diff --git a/encfs/BlockNameIO.h b/encfs/BlockNameIO.h index fb77661..b253d89 100644 --- a/encfs/BlockNameIO.h +++ b/encfs/BlockNameIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -32,38 +32,35 @@ class Cipher; mode to encode filenames. The filenames are padded to be a multiple of the cipher block size. */ -class BlockNameIO : public NameIO -{ -public: - static rel::Interface CurrentInterface(bool caseSensitive = false); +class BlockNameIO : public NameIO { + public: + static rel::Interface CurrentInterface(bool caseSensitive = false); - BlockNameIO( const rel::Interface &iface, - const shared_ptr &cipher, - const CipherKey &key, int blockSize, - bool caseSensitiveEncoding = false ); - virtual ~BlockNameIO(); + BlockNameIO(const rel::Interface &iface, const shared_ptr &cipher, + const CipherKey &key, int blockSize, + bool caseSensitiveEncoding = false); + virtual ~BlockNameIO(); - virtual rel::Interface interface() const; + virtual rel::Interface interface() const; - virtual int maxEncodedNameLen( int plaintextNameLen ) const; - virtual int maxDecodedNameLen( int encodedNameLen ) const; + virtual int maxEncodedNameLen(int plaintextNameLen) const; + virtual int maxDecodedNameLen(int encodedNameLen) const; - // hack to help with static builds - static bool Enabled(); -protected: - virtual int encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const; - virtual int decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const; + // hack to help with static builds + static bool Enabled(); -private: - int _interface; - int _bs; - shared_ptr _cipher; - CipherKey _key; - bool _caseSensitive; + protected: + virtual int encodeName(const char *plaintextName, int length, uint64_t *iv, + char *encodedName) const; + virtual int decodeName(const char *encodedName, int length, uint64_t *iv, + char *plaintextName) const; + + private: + int _interface; + int _bs; + shared_ptr _cipher; + CipherKey _key; + bool _caseSensitive; }; - #endif - diff --git a/encfs/Cipher.cpp b/encfs/Cipher.cpp index fe312b0..48f7681 100644 --- a/encfs/Cipher.cpp +++ b/encfs/Cipher.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -38,191 +38,164 @@ using namespace std; using namespace rel; -#define REF_MODULE(TYPE) \ - if( !TYPE::Enabled() ) \ - cerr << "referenceModule: should never happen\n"; +#define REF_MODULE(TYPE) \ + if (!TYPE::Enabled()) cerr << "referenceModule: should never happen\n"; -static -void AddSymbolReferences() -{ - REF_MODULE(SSL_Cipher) - REF_MODULE(NullCipher) +static void AddSymbolReferences() { + REF_MODULE(SSL_Cipher) + REF_MODULE(NullCipher) } - -struct CipherAlg -{ - bool hidden; - Cipher::CipherConstructor constructor; - string description; - Interface iface; - Range keyLength; - Range blockSize; +struct CipherAlg { + bool hidden; + Cipher::CipherConstructor constructor; + string description; + Interface iface; + Range keyLength; + Range blockSize; }; -typedef multimap< string, CipherAlg> CipherMap_t; +typedef multimap CipherMap_t; static CipherMap_t *gCipherMap = NULL; -std::list -Cipher::GetAlgorithmList( bool includeHidden ) -{ - AddSymbolReferences(); +std::list Cipher::GetAlgorithmList( + bool includeHidden) { + AddSymbolReferences(); - list result; + list result; - if(!gCipherMap) - return result; + if (!gCipherMap) return result; + CipherMap_t::const_iterator it; + CipherMap_t::const_iterator mapEnd = gCipherMap->end(); + for (it = gCipherMap->begin(); it != mapEnd; ++it) { + if (includeHidden || !it->second.hidden) { + CipherAlgorithm tmp; + tmp.name = it->first; + tmp.description = it->second.description; + tmp.iface = it->second.iface; + tmp.keyLength = it->second.keyLength; + tmp.blockSize = it->second.blockSize; + + result.push_back(tmp); + } + } + + return result; +} + +bool Cipher::Register(const char *name, const char *description, + const Interface &iface, CipherConstructor fn, + bool hidden) { + Range keyLength(-1, -1, 1); + Range blockSize(-1, -1, 1); + return Cipher::Register(name, description, iface, keyLength, blockSize, fn, + hidden); +} + +bool Cipher::Register(const char *name, const char *description, + const Interface &iface, const Range &keyLength, + const Range &blockSize, CipherConstructor fn, + bool hidden) { + if (!gCipherMap) gCipherMap = new CipherMap_t; + + CipherAlg ca; + ca.hidden = hidden; + ca.constructor = fn; + ca.description = description; + ca.iface = iface; + ca.keyLength = keyLength; + ca.blockSize = blockSize; + + gCipherMap->insert(make_pair(string(name), ca)); + return true; +} + +shared_ptr Cipher::New(const string &name, int keyLen) { + shared_ptr result; + + if (gCipherMap) { + CipherMap_t::const_iterator it = gCipherMap->find(name); + if (it != gCipherMap->end()) { + CipherConstructor fn = it->second.constructor; + // use current interface.. + result = (*fn)(it->second.iface, keyLen); + } + } + + return result; +} + +shared_ptr Cipher::New(const Interface &iface, int keyLen) { + shared_ptr result; + if (gCipherMap) { CipherMap_t::const_iterator it; CipherMap_t::const_iterator mapEnd = gCipherMap->end(); - for(it = gCipherMap->begin(); it != mapEnd; ++it) - { - if(includeHidden || !it->second.hidden) - { - CipherAlgorithm tmp; - tmp.name = it->first; - tmp.description = it->second.description; - tmp.iface = it->second.iface; - tmp.keyLength = it->second.keyLength; - tmp.blockSize = it->second.blockSize; - result.push_back( tmp ); - } + for (it = gCipherMap->begin(); it != mapEnd; ++it) { + // TODO: we should look for the newest implementation.. + if (it->second.iface.implements(iface)) { + CipherConstructor fn = it->second.constructor; + // pass in requested interface.. + result = (*fn)(iface, keyLen); + + // if we're not going to compare the options, then just stop + // now.. + break; + } } + } - return result; + return result; } -bool Cipher::Register(const char *name, const char *description, - const Interface &iface, CipherConstructor fn, bool hidden) -{ - Range keyLength(-1,-1,1); - Range blockSize(-1,-1,1); - return Cipher::Register( name, description, iface, - keyLength, blockSize, fn, hidden ); +Cipher::Cipher() {} + +Cipher::~Cipher() {} + +unsigned int Cipher::MAC_32(const unsigned char *src, int len, + const CipherKey &key, uint64_t *chainedIV) const { + uint64_t mac64 = MAC_64(src, len, key, chainedIV); + + unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); + + return mac32; } -bool Cipher::Register(const char *name, const char *description, - const Interface &iface, const Range &keyLength, - const Range &blockSize, - CipherConstructor fn, bool hidden) -{ - if(!gCipherMap) - gCipherMap = new CipherMap_t; +unsigned int Cipher::MAC_16(const unsigned char *src, int len, + const CipherKey &key, uint64_t *chainedIV) const { + uint64_t mac64 = MAC_64(src, len, key, chainedIV); - CipherAlg ca; - ca.hidden = hidden; - ca.constructor = fn; - ca.description = description; - ca.iface = iface; - ca.keyLength = keyLength; - ca.blockSize = blockSize; + unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); + unsigned int mac16 = ((mac32 >> 16) & 0xffff) ^ (mac32 & 0xffff); - gCipherMap->insert( make_pair(string(name), ca) ); - return true; + return mac16; } -shared_ptr Cipher::New(const string &name, int keyLen) -{ - shared_ptr result; - - if(gCipherMap) - { - CipherMap_t::const_iterator it = gCipherMap->find( name ); - if(it != gCipherMap->end()) - { - CipherConstructor fn = it->second.constructor; - // use current interface.. - result = (*fn)( it->second.iface, keyLen ); - } - } - - return result; +bool Cipher::nameEncode(unsigned char *data, int len, uint64_t iv64, + const CipherKey &key) const { + return streamEncode(data, len, iv64, key); } -shared_ptr Cipher::New( const Interface &iface, int keyLen ) -{ - shared_ptr result; - if(gCipherMap) - { - CipherMap_t::const_iterator it; - CipherMap_t::const_iterator mapEnd = gCipherMap->end(); - - for(it = gCipherMap->begin(); it != mapEnd; ++it) - { - // TODO: we should look for the newest implementation.. - if( it->second.iface.implements( iface ) ) - { - CipherConstructor fn = it->second.constructor; - // pass in requested interface.. - result = (*fn)( iface, keyLen ); - - // if we're not going to compare the options, then just stop - // now.. - break; - } - } - } - - return result; -} - -Cipher::Cipher() -{ -} - -Cipher::~Cipher() -{ -} - -unsigned int Cipher::MAC_32( const unsigned char *src, int len, - const CipherKey &key, uint64_t *chainedIV ) const -{ - uint64_t mac64 = MAC_64( src, len, key, chainedIV ); - - unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); - - return mac32; -} - -unsigned int Cipher::MAC_16( const unsigned char *src, int len, - const CipherKey &key, uint64_t *chainedIV ) const -{ - uint64_t mac64 = MAC_64( src, len, key, chainedIV ); - - unsigned int mac32 = ((mac64 >> 32) & 0xffffffff) ^ (mac64 & 0xffffffff); - unsigned int mac16 = ((mac32 >> 16) & 0xffff) ^ (mac32 & 0xffff); - - return mac16; -} - -bool Cipher::nameEncode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key ) const -{ - return streamEncode( data, len, iv64, key ); -} - -bool Cipher::nameDecode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key ) const -{ - return streamDecode( data, len, iv64, key ); +bool Cipher::nameDecode(unsigned char *data, int len, uint64_t iv64, + const CipherKey &key) const { + return streamDecode(data, len, iv64, key); } string Cipher::encodeAsString(const CipherKey &key, - const CipherKey &encodingKey ) -{ - int encodedKeySize = this->encodedKeySize(); - unsigned char *keyBuf = new unsigned char[ encodedKeySize ]; + const CipherKey &encodingKey) { + int encodedKeySize = this->encodedKeySize(); + unsigned char *keyBuf = new unsigned char[encodedKeySize]; - // write the key, encoding it with itself. - this->writeKey( key, keyBuf, encodingKey ); + // write the key, encoding it with itself. + this->writeKey(key, keyBuf, encodingKey); - int b64Len = B256ToB64Bytes( encodedKeySize ); - unsigned char *b64Key = new unsigned char[ b64Len + 1 ]; + int b64Len = B256ToB64Bytes(encodedKeySize); + unsigned char *b64Key = new unsigned char[b64Len + 1]; - changeBase2( keyBuf, encodedKeySize, 8, b64Key, b64Len, 6 ); - B64ToAscii( b64Key, b64Len ); - b64Key[ b64Len - 1 ] = '\0'; + changeBase2(keyBuf, encodedKeySize, 8, b64Key, b64Len, 6); + B64ToAscii(b64Key, b64Len); + b64Key[b64Len - 1] = '\0'; - return string( (const char *)b64Key ); + return string((const char *)b64Key); } diff --git a/encfs/Cipher.h b/encfs/Cipher.h index 92391b9..310f60b 100644 --- a/encfs/Cipher.h +++ b/encfs/Cipher.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -37,129 +37,116 @@ Cipher's should register themselves so they can be instanciated via Cipher::New(). */ -class Cipher -{ -public: - // if no key length was indicated when cipher was registered, then keyLen - // <= 0 will be used. - typedef shared_ptr (*CipherConstructor)( const rel::Interface &iface, - int keyLenBits ); +class Cipher { + public: + // if no key length was indicated when cipher was registered, then keyLen + // <= 0 will be used. + typedef shared_ptr(*CipherConstructor)(const rel::Interface &iface, + int keyLenBits); - struct CipherAlgorithm - { - std::string name; - std::string description; - rel::Interface iface; - Range keyLength; - Range blockSize; - }; + struct CipherAlgorithm { + std::string name; + std::string description; + rel::Interface iface; + Range keyLength; + Range blockSize; + }; + typedef std::list AlgorithmList; + static AlgorithmList GetAlgorithmList(bool includeHidden = false); - typedef std::list AlgorithmList; - static AlgorithmList GetAlgorithmList( bool includeHidden = false ); + static shared_ptr New(const rel::Interface &iface, int keyLen = -1); + static shared_ptr New(const std::string &cipherName, int keyLen = -1); + static bool Register(const char *cipherName, const char *description, + const rel::Interface &iface, + CipherConstructor constructor, bool hidden = false); + static bool Register(const char *cipherName, const char *description, + const rel::Interface &iface, const Range &keyLength, + const Range &blockSize, CipherConstructor constructor, + bool hidden = false); - static shared_ptr New( const rel::Interface &iface, - int keyLen = -1); - static shared_ptr New( const std::string &cipherName, - int keyLen = -1 ); + Cipher(); + virtual ~Cipher(); + virtual rel::Interface interface() const = 0; - static bool Register(const char *cipherName, - const char *description, - const rel::Interface &iface, - CipherConstructor constructor, - bool hidden = false); - static bool Register(const char *cipherName, - const char *description, - const rel::Interface &iface, - const Range &keyLength, const Range &blockSize, - CipherConstructor constructor, - bool hidden = false); + // create a new key based on a password + // if iterationCount == 0, then iteration count will be determined + // by newKey function and filled in. + // If iterationCount == 0, then desiredFunctionDuration is how many + // milliseconds the password derivation function should take to run. + virtual CipherKey newKey(const char *password, int passwdLength, + int &iterationCount, long desiredFunctionDuration, + const unsigned char *salt, int saltLen) = 0; + // deprecated - for backward compatibility + virtual CipherKey newKey(const char *password, int passwdLength) = 0; + // create a new random key + virtual CipherKey newRandomKey() = 0; + // data must be len encodedKeySize() + virtual CipherKey readKey(const unsigned char *data, + const CipherKey &encodingKey, + bool checkKey = true) = 0; + virtual void writeKey(const CipherKey &key, unsigned char *data, + const CipherKey &encodingKey) = 0; - Cipher(); - virtual ~Cipher(); + virtual std::string encodeAsString(const CipherKey &key, + const CipherKey &encodingKey); - virtual rel::Interface interface() const =0; + // for testing purposes + virtual bool compareKey(const CipherKey &A, const CipherKey &B) const = 0; - // create a new key based on a password - // if iterationCount == 0, then iteration count will be determined - // by newKey function and filled in. - // If iterationCount == 0, then desiredFunctionDuration is how many - // milliseconds the password derivation function should take to run. - virtual CipherKey newKey(const char *password, int passwdLength, - int &iterationCount, long desiredFunctionDuration, - const unsigned char *salt, int saltLen) =0; - // deprecated - for backward compatibility - virtual CipherKey newKey(const char *password, int passwdLength ) =0; - // create a new random key - virtual CipherKey newRandomKey() =0; + // meta-data about the cypher + virtual int keySize() const = 0; + virtual int encodedKeySize() const = 0; // size + virtual int cipherBlockSize() const = 0; // size of a cipher block - // data must be len encodedKeySize() - virtual CipherKey readKey(const unsigned char *data, - const CipherKey &encodingKey, - bool checkKey = true) =0; - virtual void writeKey(const CipherKey &key, unsigned char *data, - const CipherKey &encodingKey) =0; + // fill the supplied buffer with random data + // The data may be pseudo random and might not be suitable for key + // generation. For generating keys, uses newRandomKey() instead. + // Returns true on success, false on failure. + virtual bool randomize(unsigned char *buf, int len, + bool strongRandom) const = 0; - virtual std::string encodeAsString(const CipherKey &key, - const CipherKey &encodingKey ); + // 64 bit MAC of the data with the given key + virtual uint64_t MAC_64(const unsigned char *src, int len, + const CipherKey &key, + uint64_t *chainedIV = 0) const = 0; + // based on reductions of MAC_64 + unsigned int MAC_32(const unsigned char *src, int len, const CipherKey &key, + uint64_t *chainedIV = 0) const; + unsigned int MAC_16(const unsigned char *src, int len, const CipherKey &key, + uint64_t *chainedIV = 0) const; - // for testing purposes - virtual bool compareKey( const CipherKey &A, const CipherKey &B ) const =0; + // functional interfaces + /* + Stream encoding of data in-place. The stream data can be any length. + */ + virtual bool streamEncode(unsigned char *data, int len, uint64_t iv64, + const CipherKey &key) const = 0; + virtual bool streamDecode(unsigned char *data, int len, uint64_t iv64, + const CipherKey &key) const = 0; - // meta-data about the cypher - virtual int keySize() const=0; - virtual int encodedKeySize() const=0; // size - virtual int cipherBlockSize() const=0; // size of a cipher block + /* + These are just aliases of streamEncode / streamDecode, but there are + provided here for backward compatibility for earlier ciphers that has + effectively two stream modes - one for encoding partial blocks and + another for encoding filenames. + */ + virtual bool nameEncode(unsigned char *data, int len, uint64_t iv64, + const CipherKey &key) const; + virtual bool nameDecode(unsigned char *data, int len, uint64_t iv64, + const CipherKey &key) const; - // fill the supplied buffer with random data - // The data may be pseudo random and might not be suitable for key - // generation. For generating keys, uses newRandomKey() instead. - // Returns true on success, false on failure. - virtual bool randomize( unsigned char *buf, int len, - bool strongRandom ) const =0; - - // 64 bit MAC of the data with the given key - virtual uint64_t MAC_64( const unsigned char *src, int len, - const CipherKey &key, uint64_t *chainedIV = 0 ) const =0; - // based on reductions of MAC_64 - unsigned int MAC_32( const unsigned char *src, int len, - const CipherKey &key, uint64_t *chainedIV = 0 ) const; - unsigned int MAC_16( const unsigned char *src, int len, - const CipherKey &key, uint64_t *chainedIV = 0 ) const; - - // functional interfaces - /* - Stream encoding of data in-place. The stream data can be any length. - */ - virtual bool streamEncode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key) const=0; - virtual bool streamDecode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key) const=0; - - /* - These are just aliases of streamEncode / streamDecode, but there are - provided here for backward compatibility for earlier ciphers that has - effectively two stream modes - one for encoding partial blocks and - another for encoding filenames. - */ - virtual bool nameEncode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key) const; - virtual bool nameDecode( unsigned char *data, int len, - uint64_t iv64, const CipherKey &key) const; - - /* - Block encoding of data in-place. The data size should be a multiple of - the cipher block size. - */ - virtual bool blockEncode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &key) const=0; - virtual bool blockDecode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &key) const=0; + /* + Block encoding of data in-place. The data size should be a multiple of + the cipher block size. + */ + virtual bool blockEncode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &key) const = 0; + virtual bool blockDecode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &key) const = 0; }; - #endif - diff --git a/encfs/CipherFileIO.cpp b/encfs/CipherFileIO.cpp index 957d28c..1f9401a 100644 --- a/encfs/CipherFileIO.cpp +++ b/encfs/CipherFileIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -37,401 +37,326 @@ */ static rel::Interface CipherFileIO_iface("FileIO/Cipher", 2, 0, 1); -const int HEADER_SIZE = 8; // 64 bit initialization vector.. +const int HEADER_SIZE = 8; // 64 bit initialization vector.. -static bool checkSize( int fsBlockSize, int cipherBlockSize ) -{ - int blockBoundary = fsBlockSize % cipherBlockSize ; - if(blockBoundary != 0) - { - rError("CipherFileIO: blocks should be multiple of cipher block size"); - return true; - } else - return false; +static bool checkSize(int fsBlockSize, int cipherBlockSize) { + int blockBoundary = fsBlockSize % cipherBlockSize; + if (blockBoundary != 0) { + rError("CipherFileIO: blocks should be multiple of cipher block size"); + return true; + } else + return false; } -CipherFileIO::CipherFileIO( const shared_ptr &_base, - const FSConfigPtr &cfg) - : BlockFileIO( cfg->config->blockSize, cfg ) - , base( _base ) - , haveHeader( cfg->config->uniqueIV ) - , externalIV( 0 ) - , fileIV( 0 ) - , lastFlags( 0 ) -{ - fsConfig = cfg; - cipher = cfg->cipher; - key = cfg->key; +CipherFileIO::CipherFileIO(const shared_ptr &_base, + const FSConfigPtr &cfg) + : BlockFileIO(cfg->config->blockSize, cfg), + base(_base), + haveHeader(cfg->config->uniqueIV), + externalIV(0), + fileIV(0), + lastFlags(0) { + fsConfig = cfg; + cipher = cfg->cipher; + key = cfg->key; - static bool warnOnce = false; + static bool warnOnce = false; - if(!warnOnce) - warnOnce = checkSize( fsConfig->config->blockSize, - fsConfig->cipher->cipherBlockSize() ); + if (!warnOnce) + warnOnce = checkSize(fsConfig->config->blockSize, + fsConfig->cipher->cipherBlockSize()); } -CipherFileIO::~CipherFileIO() -{ +CipherFileIO::~CipherFileIO() {} + +rel::Interface CipherFileIO::interface() const { return CipherFileIO_iface; } + +int CipherFileIO::open(int flags) { + int res = base->open(flags); + + if (res >= 0) lastFlags = flags; + + return res; } -rel::Interface CipherFileIO::interface() const -{ - return CipherFileIO_iface; +void CipherFileIO::setFileName(const char *fileName) { + base->setFileName(fileName); } -int CipherFileIO::open( int flags ) -{ - int res = base->open( flags ); - - if( res >= 0 ) - lastFlags = flags; +const char *CipherFileIO::getFileName() const { return base->getFileName(); } - return res; -} - -void CipherFileIO::setFileName( const char *fileName ) -{ - base->setFileName( fileName ); -} - -const char *CipherFileIO::getFileName() const -{ - return base->getFileName(); -} - -bool CipherFileIO::setIV( uint64_t iv ) -{ - rDebug("in setIV, current IV = %" PRIu64 ", new IV = %" PRIu64 - ", fileIV = %" PRIu64, - externalIV, iv, fileIV); - if(externalIV == 0) - { - // we're just being told about which IV to use. since we haven't - // initialized the fileIV, there is no need to just yet.. - externalIV = iv; - if(fileIV != 0) - rWarning("fileIV initialized before externalIV! (%" PRIu64 - ", %" PRIu64 ")", fileIV, externalIV); - } else - if(haveHeader) - { - // we have an old IV, and now a new IV, so we need to update the fileIV - // on disk. - if(fileIV == 0) - { - // ensure the file is open for read/write.. - int newFlags = lastFlags | O_RDWR; - int res = base->open( newFlags ); - if(res < 0) - { - if(res == -EISDIR) - { - // duh -- there are no file headers for directories! - externalIV = iv; - return base->setIV( iv ); - } else - { - rDebug("writeHeader failed to re-open for write"); - return false; - } - } - initHeader(); - } - - uint64_t oldIV = externalIV; - externalIV = iv; - if(!writeHeader()) - { - externalIV = oldIV; - return false; - } +bool CipherFileIO::setIV(uint64_t iv) { + rDebug("in setIV, current IV = %" PRIu64 ", new IV = %" PRIu64 + ", fileIV = %" PRIu64, + externalIV, iv, fileIV); + if (externalIV == 0) { + // we're just being told about which IV to use. since we haven't + // initialized the fileIV, there is no need to just yet.. + externalIV = iv; + if (fileIV != 0) + rWarning("fileIV initialized before externalIV! (%" PRIu64 ", %" PRIu64 + ")", + fileIV, externalIV); + } else if (haveHeader) { + // we have an old IV, and now a new IV, so we need to update the fileIV + // on disk. + if (fileIV == 0) { + // ensure the file is open for read/write.. + int newFlags = lastFlags | O_RDWR; + int res = base->open(newFlags); + if (res < 0) { + if (res == -EISDIR) { + // duh -- there are no file headers for directories! + externalIV = iv; + return base->setIV(iv); + } else { + rDebug("writeHeader failed to re-open for write"); + return false; + } + } + initHeader(); } - return base->setIV( iv ); -} - -int CipherFileIO::getAttr( struct stat *stbuf ) const -{ - int res = base->getAttr( stbuf ); - // adjust size if we have a file header - if((res == 0) && haveHeader && - S_ISREG(stbuf->st_mode) && (stbuf->st_size > 0)) - { - rAssert(stbuf->st_size >= HEADER_SIZE); - stbuf->st_size -= HEADER_SIZE; + uint64_t oldIV = externalIV; + externalIV = iv; + if (!writeHeader()) { + externalIV = oldIV; + return false; } + } - return res; + return base->setIV(iv); } -off_t CipherFileIO::getSize() const -{ - off_t size = base->getSize(); - // No check on S_ISREG here -- don't call getSize over getAttr unless this - // is a normal file! - if(haveHeader && size > 0) - { - rAssert(size >= HEADER_SIZE); - size -= HEADER_SIZE; - } - return size; +int CipherFileIO::getAttr(struct stat *stbuf) const { + int res = base->getAttr(stbuf); + // adjust size if we have a file header + if ((res == 0) && haveHeader && S_ISREG(stbuf->st_mode) && + (stbuf->st_size > 0)) { + rAssert(stbuf->st_size >= HEADER_SIZE); + stbuf->st_size -= HEADER_SIZE; + } + + return res; } -void CipherFileIO::initHeader( ) -{ - // check if the file has a header, and read it if it does.. Otherwise, - // create one. - off_t rawSize = base->getSize(); - if(rawSize >= HEADER_SIZE) - { - rDebug("reading existing header, rawSize = %" PRIi64, rawSize); - // has a header.. read it - unsigned char buf[8] = {0}; - - IORequest req; - req.offset = 0; - req.data = buf; - req.dataLen = 8; - base->read( req ); - - cipher->streamDecode( buf, sizeof(buf), - externalIV, key ); - - fileIV = 0; - for(int i=0; i<8; ++i) - fileIV = (fileIV << 8) | (uint64_t)buf[i]; - - rAssert(fileIV != 0); // 0 is never used.. - } else - { - rDebug("creating new file IV header"); - - unsigned char buf[8] = {0}; - do - { - if(!cipher->randomize( buf, 8, false )) - throw ERROR("Unable to generate a random file IV"); - - fileIV = 0; - for(int i=0; i<8; ++i) - fileIV = (fileIV << 8) | (uint64_t)buf[i]; - - if(fileIV == 0) - rWarning("Unexpected result: randomize returned 8 null bytes!"); - } while(fileIV == 0); // don't accept 0 as an option.. - - if( base->isWritable() ) - { - cipher->streamEncode( buf, sizeof(buf), externalIV, key ); - - IORequest req; - req.offset = 0; - req.data = buf; - req.dataLen = 8; - - base->write( req ); - } else - rDebug("base not writable, IV not written.."); - } - rDebug("initHeader finished, fileIV = %" PRIu64 , fileIV); +off_t CipherFileIO::getSize() const { + off_t size = base->getSize(); + // No check on S_ISREG here -- don't call getSize over getAttr unless this + // is a normal file! + if (haveHeader && size > 0) { + rAssert(size >= HEADER_SIZE); + size -= HEADER_SIZE; + } + return size; } -bool CipherFileIO::writeHeader( ) -{ - if( !base->isWritable() ) - { - // open for write.. - int newFlags = lastFlags | O_RDWR; - if( base->open( newFlags ) < 0 ) - { - rDebug("writeHeader failed to re-open for write"); - return false; - } - } - - if(fileIV == 0) - rError("Internal error: fileIV == 0 in writeHeader!!!"); - rDebug("writing fileIV %" PRIu64 , fileIV); - +void CipherFileIO::initHeader() { + // check if the file has a header, and read it if it does.. Otherwise, + // create one. + off_t rawSize = base->getSize(); + if (rawSize >= HEADER_SIZE) { + rDebug("reading existing header, rawSize = %" PRIi64, rawSize); + // has a header.. read it unsigned char buf[8] = {0}; - for(int i=0; i<8; ++i) - { - buf[sizeof(buf)-1-i] = (unsigned char)(fileIV & 0xff); - fileIV >>= 8; - } - - cipher->streamEncode( buf, sizeof(buf), externalIV, key ); IORequest req; req.offset = 0; req.data = buf; req.dataLen = 8; + base->read(req); - base->write( req ); + cipher->streamDecode(buf, sizeof(buf), externalIV, key); - return true; + fileIV = 0; + for (int i = 0; i < 8; ++i) fileIV = (fileIV << 8) | (uint64_t)buf[i]; + + rAssert(fileIV != 0); // 0 is never used.. + } else { + rDebug("creating new file IV header"); + + unsigned char buf[8] = {0}; + do { + if (!cipher->randomize(buf, 8, false)) + throw ERROR("Unable to generate a random file IV"); + + fileIV = 0; + for (int i = 0; i < 8; ++i) fileIV = (fileIV << 8) | (uint64_t)buf[i]; + + if (fileIV == 0) + rWarning("Unexpected result: randomize returned 8 null bytes!"); + } while (fileIV == 0); // don't accept 0 as an option.. + + if (base->isWritable()) { + cipher->streamEncode(buf, sizeof(buf), externalIV, key); + + IORequest req; + req.offset = 0; + req.data = buf; + req.dataLen = 8; + + base->write(req); + } else + rDebug("base not writable, IV not written.."); + } + rDebug("initHeader finished, fileIV = %" PRIu64, fileIV); } -ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const -{ - // read raw data, then decipher it.. - int bs = blockSize(); - off_t blockNum = req.offset / bs; - - ssize_t readSize = 0; - IORequest tmpReq = req; +bool CipherFileIO::writeHeader() { + if (!base->isWritable()) { + // open for write.. + int newFlags = lastFlags | O_RDWR; + if (base->open(newFlags) < 0) { + rDebug("writeHeader failed to re-open for write"); + return false; + } + } - if(haveHeader) - tmpReq.offset += HEADER_SIZE; - readSize = base->read( tmpReq ); + if (fileIV == 0) rError("Internal error: fileIV == 0 in writeHeader!!!"); + rDebug("writing fileIV %" PRIu64, fileIV); - bool ok; - if(readSize > 0) - { - if(haveHeader && fileIV == 0) - const_cast(this)->initHeader(); + unsigned char buf[8] = {0}; + for (int i = 0; i < 8; ++i) { + buf[sizeof(buf) - 1 - i] = (unsigned char)(fileIV & 0xff); + fileIV >>= 8; + } - if(readSize != bs) - { - ok = streamRead( tmpReq.data, (int)readSize, blockNum ^ fileIV); - } else - { - ok = blockRead( tmpReq.data, (int)readSize, blockNum ^ fileIV); - } + cipher->streamEncode(buf, sizeof(buf), externalIV, key); - if(!ok) - { - rDebug("decodeBlock failed for block %" PRIi64 ", size %i", - blockNum, (int)readSize ); - readSize = -1; - } - } else - rDebug("readSize zero for offset %" PRIi64, req.offset); + IORequest req; + req.offset = 0; + req.data = buf; + req.dataLen = 8; - return readSize; + base->write(req); + + return true; } +ssize_t CipherFileIO::readOneBlock(const IORequest &req) const { + // read raw data, then decipher it.. + int bs = blockSize(); + off_t blockNum = req.offset / bs; -bool CipherFileIO::writeOneBlock( const IORequest &req ) -{ - int bs = blockSize(); - off_t blockNum = req.offset / bs; + ssize_t readSize = 0; + IORequest tmpReq = req; - if(haveHeader && fileIV == 0) - initHeader(); + if (haveHeader) tmpReq.offset += HEADER_SIZE; + readSize = base->read(tmpReq); - bool ok; - if( req.dataLen != bs ) - { - ok = streamWrite( req.data, (int)req.dataLen, - blockNum ^ fileIV ); - } else - { - ok = blockWrite( req.data, (int)req.dataLen, - blockNum ^ fileIV ); + bool ok; + if (readSize > 0) { + if (haveHeader && fileIV == 0) + const_cast(this)->initHeader(); + + if (readSize != bs) { + ok = streamRead(tmpReq.data, (int)readSize, blockNum ^ fileIV); + } else { + ok = blockRead(tmpReq.data, (int)readSize, blockNum ^ fileIV); } - if( ok ) - { - if(haveHeader) - { - IORequest tmpReq = req; - tmpReq.offset += HEADER_SIZE; - ok = base->write( tmpReq ); - } else - ok = base->write( req ); + if (!ok) { + rDebug("decodeBlock failed for block %" PRIi64 ", size %i", blockNum, + (int)readSize); + readSize = -1; + } + } else + rDebug("readSize zero for offset %" PRIi64, req.offset); + + return readSize; +} + +bool CipherFileIO::writeOneBlock(const IORequest &req) { + int bs = blockSize(); + off_t blockNum = req.offset / bs; + + if (haveHeader && fileIV == 0) initHeader(); + + bool ok; + if (req.dataLen != bs) { + ok = streamWrite(req.data, (int)req.dataLen, blockNum ^ fileIV); + } else { + ok = blockWrite(req.data, (int)req.dataLen, blockNum ^ fileIV); + } + + if (ok) { + if (haveHeader) { + IORequest tmpReq = req; + tmpReq.offset += HEADER_SIZE; + ok = base->write(tmpReq); } else - { - rDebug("encodeBlock failed for block %" PRIi64 ", size %i", - blockNum, req.dataLen); - ok = false; - } - return ok; + ok = base->write(req); + } else { + rDebug("encodeBlock failed for block %" PRIi64 ", size %i", blockNum, + req.dataLen); + ok = false; + } + return ok; } -bool CipherFileIO::blockWrite( unsigned char *buf, int size, - uint64_t _iv64 ) const -{ - if (!fsConfig->reverseEncryption) - return cipher->blockEncode( buf, size, _iv64, key ); - else - return cipher->blockDecode( buf, size, _iv64, key ); -} +bool CipherFileIO::blockWrite(unsigned char *buf, int size, + uint64_t _iv64) const { + if (!fsConfig->reverseEncryption) + return cipher->blockEncode(buf, size, _iv64, key); + else + return cipher->blockDecode(buf, size, _iv64, key); +} -bool CipherFileIO::streamWrite( unsigned char *buf, int size, - uint64_t _iv64 ) const -{ - if (!fsConfig->reverseEncryption) - return cipher->streamEncode( buf, size, _iv64, key ); - else - return cipher->streamDecode( buf, size, _iv64, key ); -} +bool CipherFileIO::streamWrite(unsigned char *buf, int size, + uint64_t _iv64) const { + if (!fsConfig->reverseEncryption) + return cipher->streamEncode(buf, size, _iv64, key); + else + return cipher->streamDecode(buf, size, _iv64, key); +} +bool CipherFileIO::blockRead(unsigned char *buf, int size, + uint64_t _iv64) const { + if (fsConfig->reverseEncryption) + return cipher->blockEncode(buf, size, _iv64, key); + else { + if (_allowHoles) { + // special case - leave all 0's alone + for (int i = 0; i < size; ++i) + if (buf[i] != 0) return cipher->blockDecode(buf, size, _iv64, key); -bool CipherFileIO::blockRead( unsigned char *buf, int size, - uint64_t _iv64 ) const -{ - if (fsConfig->reverseEncryption) - return cipher->blockEncode( buf, size, _iv64, key ); - else - { - if(_allowHoles) - { - // special case - leave all 0's alone - for(int i=0; iblockDecode( buf, size, _iv64, key ); - - return true; - } else - return cipher->blockDecode( buf, size, _iv64, key ); - } -} - -bool CipherFileIO::streamRead( unsigned char *buf, int size, - uint64_t _iv64 ) const -{ - if (fsConfig->reverseEncryption) - return cipher->streamEncode( buf, size, _iv64, key ); - else - return cipher->streamDecode( buf, size, _iv64, key ); -} - - - -int CipherFileIO::truncate( off_t size ) -{ - int res = 0; - if(!haveHeader) - { - res = BlockFileIO::truncateBase( size, base.get() ); + return true; } else - { - if(0 == fileIV) - { - // empty file.. create the header.. - if( !base->isWritable() ) - { - // open for write.. - int newFlags = lastFlags | O_RDWR; - if( base->open( newFlags ) < 0 ) - rDebug("writeHeader failed to re-open for write"); - } - initHeader(); - } + return cipher->blockDecode(buf, size, _iv64, key); + } +} - // can't let BlockFileIO call base->truncate(), since it would be using - // the wrong size.. - res = BlockFileIO::truncateBase( size, 0 ); +bool CipherFileIO::streamRead(unsigned char *buf, int size, + uint64_t _iv64) const { + if (fsConfig->reverseEncryption) + return cipher->streamEncode(buf, size, _iv64, key); + else + return cipher->streamDecode(buf, size, _iv64, key); +} - if(res == 0) - base->truncate( size + HEADER_SIZE ); +int CipherFileIO::truncate(off_t size) { + int res = 0; + if (!haveHeader) { + res = BlockFileIO::truncateBase(size, base.get()); + } else { + if (0 == fileIV) { + // empty file.. create the header.. + if (!base->isWritable()) { + // open for write.. + int newFlags = lastFlags | O_RDWR; + if (base->open(newFlags) < 0) + rDebug("writeHeader failed to re-open for write"); + } + initHeader(); } - return res; -} - -bool CipherFileIO::isWritable() const -{ - return base->isWritable(); + + // can't let BlockFileIO call base->truncate(), since it would be using + // the wrong size.. + res = BlockFileIO::truncateBase(size, 0); + + if (res == 0) base->truncate(size + HEADER_SIZE); + } + return res; } +bool CipherFileIO::isWritable() const { return base->isWritable(); } diff --git a/encfs/CipherFileIO.h b/encfs/CipherFileIO.h index 5fb6d6a..8012ae1 100644 --- a/encfs/CipherFileIO.h +++ b/encfs/CipherFileIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -30,60 +30,54 @@ class Cipher; /* - Implement the FileIO interface encrypting data in blocks. - + Implement the FileIO interface encrypting data in blocks. + Uses BlockFileIO to handle the block scatter / gather issues. */ -class CipherFileIO : public BlockFileIO -{ -public: - CipherFileIO( const shared_ptr &base, - const FSConfigPtr &cfg); - virtual ~CipherFileIO(); +class CipherFileIO : public BlockFileIO { + public: + CipherFileIO(const shared_ptr &base, const FSConfigPtr &cfg); + virtual ~CipherFileIO(); - virtual rel::Interface interface() const; + virtual rel::Interface interface() const; - virtual void setFileName( const char *fileName ); - virtual const char *getFileName() const; - virtual bool setIV( uint64_t iv ); + virtual void setFileName(const char *fileName); + virtual const char *getFileName() const; + virtual bool setIV(uint64_t iv); - virtual int open( int flags ); + virtual int open(int flags); - virtual int getAttr( struct stat *stbuf ) const; - virtual off_t getSize() const; + virtual int getAttr(struct stat *stbuf) const; + virtual off_t getSize() const; - virtual int truncate( off_t size ); + virtual int truncate(off_t size); - virtual bool isWritable() const; + virtual bool isWritable() const; -private: - virtual ssize_t readOneBlock( const IORequest &req ) const; - virtual bool writeOneBlock( const IORequest &req ); + private: + virtual ssize_t readOneBlock(const IORequest &req) const; + virtual bool writeOneBlock(const IORequest &req); - void initHeader(); - bool writeHeader(); - bool blockRead( unsigned char *buf, int size, - uint64_t iv64 ) const; - bool streamRead( unsigned char *buf, int size, - uint64_t iv64 ) const; - bool blockWrite( unsigned char *buf, int size, - uint64_t iv64 ) const; - bool streamWrite( unsigned char *buf, int size, - uint64_t iv64 ) const; + void initHeader(); + bool writeHeader(); + bool blockRead(unsigned char *buf, int size, uint64_t iv64) const; + bool streamRead(unsigned char *buf, int size, uint64_t iv64) const; + bool blockWrite(unsigned char *buf, int size, uint64_t iv64) const; + bool streamWrite(unsigned char *buf, int size, uint64_t iv64) const; - shared_ptr base; + shared_ptr base; - FSConfigPtr fsConfig; + FSConfigPtr fsConfig; - // if haveHeader is true, then we have a transparent file header which - // contains a 64 bit initialization vector. - bool haveHeader; - uint64_t externalIV; - uint64_t fileIV; - int lastFlags; + // if haveHeader is true, then we have a transparent file header which + // contains a 64 bit initialization vector. + bool haveHeader; + uint64_t externalIV; + uint64_t fileIV; + int lastFlags; - shared_ptr cipher; - CipherKey key; + shared_ptr cipher; + CipherKey key; }; #endif diff --git a/encfs/CipherKey.cpp b/encfs/CipherKey.cpp index c2e815a..ba2f779 100644 --- a/encfs/CipherKey.cpp +++ b/encfs/CipherKey.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -20,11 +20,6 @@ #include "CipherKey.h" -AbstractCipherKey::AbstractCipherKey() -{ -} - -AbstractCipherKey::~AbstractCipherKey() -{ -} +AbstractCipherKey::AbstractCipherKey() {} +AbstractCipherKey::~AbstractCipherKey() {} diff --git a/encfs/CipherKey.h b/encfs/CipherKey.h index b8cf639..1de3bc8 100644 --- a/encfs/CipherKey.h +++ b/encfs/CipherKey.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -23,14 +23,12 @@ #include "shared_ptr.h" -class AbstractCipherKey -{ -public: - AbstractCipherKey(); - virtual ~AbstractCipherKey(); +class AbstractCipherKey { + public: + AbstractCipherKey(); + virtual ~AbstractCipherKey(); }; typedef shared_ptr CipherKey; #endif - diff --git a/encfs/ConfigReader.cpp b/encfs/ConfigReader.cpp index 8218e59..4fa9d04 100644 --- a/encfs/ConfigReader.cpp +++ b/encfs/ConfigReader.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -28,135 +28,109 @@ #include #include - using namespace std; using namespace rlog; +ConfigReader::ConfigReader() {} -ConfigReader::ConfigReader() -{ -} - -ConfigReader::~ConfigReader() -{ -} +ConfigReader::~ConfigReader() {} // read the entire file into a ConfigVar instance and then use that to decode // into mapped variables. -bool -ConfigReader::load(const char *fileName) -{ - struct stat stbuf; - memset( &stbuf, 0, sizeof(struct stat)); - if( lstat( fileName, &stbuf ) != 0) - return false; +bool ConfigReader::load(const char *fileName) { + struct stat stbuf; + memset(&stbuf, 0, sizeof(struct stat)); + if (lstat(fileName, &stbuf) != 0) return false; - int size = stbuf.st_size; + int size = stbuf.st_size; - int fd = open( fileName, O_RDONLY ); - if(fd < 0) - return false; + int fd = open(fileName, O_RDONLY); + if (fd < 0) return false; - char *buf = new char[size]; + char *buf = new char[size]; - int res = ::read( fd, buf, size ); - close( fd ); + int res = ::read(fd, buf, size); + close(fd); - if( res != size ) - { - rWarning("Partial read of config file, expecting %i bytes, got %i", - size, res); - delete[] buf; - return false; - } - - ConfigVar in; - in.write( (unsigned char *)buf, size ); + if (res != size) { + rWarning("Partial read of config file, expecting %i bytes, got %i", size, + res); delete[] buf; + return false; + } - return loadFromVar( in ); + ConfigVar in; + in.write((unsigned char *)buf, size); + delete[] buf; + + return loadFromVar(in); } -bool -ConfigReader::loadFromVar(ConfigVar &in) -{ - in.resetOffset(); +bool ConfigReader::loadFromVar(ConfigVar &in) { + in.resetOffset(); - // parse. - int numEntries = in.readInt(); + // parse. + int numEntries = in.readInt(); - for(int i=0; i> key >> value; + for (int i = 0; i < numEntries; ++i) { + string key, value; + in >> key >> value; - if(key.length() == 0) - { - rError("Invalid key encoding in buffer"); - return false; - } - ConfigVar newVar( value ); - vars.insert( make_pair( key, newVar ) ); + if (key.length() == 0) { + rError("Invalid key encoding in buffer"); + return false; } + ConfigVar newVar(value); + vars.insert(make_pair(key, newVar)); + } - return true; + return true; } -bool -ConfigReader::save(const char *fileName) const -{ - // write everything to a ConfigVar, then output to disk - ConfigVar out = toVar(); +bool ConfigReader::save(const char *fileName) const { + // write everything to a ConfigVar, then output to disk + ConfigVar out = toVar(); - int fd = ::open( fileName, O_RDWR | O_CREAT, 0640 ); - if(fd >= 0) - { - int retVal = ::write( fd, out.buffer(), out.size() ); - close( fd ); - if(retVal != out.size()) - { - rError("Error writing to config file %s", fileName); - return false; - } - } else - { - rError("Unable to open or create file %s", fileName); - return false; + int fd = ::open(fileName, O_RDWR | O_CREAT, 0640); + if (fd >= 0) { + int retVal = ::write(fd, out.buffer(), out.size()); + close(fd); + if (retVal != out.size()) { + rError("Error writing to config file %s", fileName); + return false; } + } else { + rError("Unable to open or create file %s", fileName); + return false; + } - return true; + return true; } -ConfigVar -ConfigReader::toVar() const -{ - // write everything to a ConfigVar, then output to disk - ConfigVar out; - out.writeInt( vars.size() ); - map::const_iterator it; - for(it = vars.begin(); it != vars.end(); ++it) - { - out.writeInt( it->first.size() ); - out.write( (unsigned char*)it->first.data(), it->first.size() ); - out.writeInt( it->second.size() ); - out.write( (unsigned char*)it->second.buffer(), it->second.size() ); - } +ConfigVar ConfigReader::toVar() const { + // write everything to a ConfigVar, then output to disk + ConfigVar out; + out.writeInt(vars.size()); + map::const_iterator it; + for (it = vars.begin(); it != vars.end(); ++it) { + out.writeInt(it->first.size()); + out.write((unsigned char *)it->first.data(), it->first.size()); + out.writeInt(it->second.size()); + out.write((unsigned char *)it->second.buffer(), it->second.size()); + } - return out; + return out; } -ConfigVar ConfigReader::operator[] ( const std::string &varName ) const -{ - // read only - map::const_iterator it = vars.find( varName ); - if( it == vars.end() ) - return ConfigVar(); - else - return it->second; +ConfigVar ConfigReader::operator[](const std::string &varName) const { + // read only + map::const_iterator it = vars.find(varName); + if (it == vars.end()) + return ConfigVar(); + else + return it->second; } -ConfigVar &ConfigReader::operator[] ( const std::string &varName ) -{ - return vars[ varName ]; +ConfigVar &ConfigReader::operator[](const std::string &varName) { + return vars[varName]; } - diff --git a/encfs/ConfigReader.h b/encfs/ConfigReader.h index 4d6bf71..b400013 100644 --- a/encfs/ConfigReader.h +++ b/encfs/ConfigReader.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - + #ifndef _ConfigReader_incl_ #define _ConfigReader_incl_ @@ -43,24 +43,22 @@ ConfigReader cfg; cfg["cipher"] << cipher->interface(); */ -class ConfigReader -{ -public: - ConfigReader(); - ~ConfigReader(); +class ConfigReader { + public: + ConfigReader(); + ~ConfigReader(); - bool load(const char *fileName); - bool save(const char *fileName) const; + bool load(const char *fileName); + bool save(const char *fileName) const; - ConfigVar toVar() const; - bool loadFromVar( ConfigVar &var ); + ConfigVar toVar() const; + bool loadFromVar(ConfigVar &var); - ConfigVar operator[](const std::string &varName) const; - ConfigVar &operator[](const std::string &varName); + ConfigVar operator[](const std::string &varName) const; + ConfigVar &operator[](const std::string &varName); -private: - std::map vars; + private: + std::map vars; }; - #endif diff --git a/encfs/ConfigVar.cpp b/encfs/ConfigVar.cpp index a677236..b107e2a 100644 --- a/encfs/ConfigVar.cpp +++ b/encfs/ConfigVar.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -26,227 +26,175 @@ using namespace rlog; #ifndef MIN -inline int MIN(int a, int b) -{ - return (a < b) ? a : b; -} +inline int MIN(int a, int b) { return (a < b) ? a : b; } #endif +ConfigVar::ConfigVar() : pd(new ConfigVarData) { pd->offset = 0; } -ConfigVar::ConfigVar() - : pd( new ConfigVarData ) -{ - pd->offset = 0; +ConfigVar::ConfigVar(const std::string &buf) : pd(new ConfigVarData) { + pd->buffer = buf; + pd->offset = 0; } -ConfigVar::ConfigVar(const std::string &buf) - : pd( new ConfigVarData ) -{ - pd->buffer = buf; - pd->offset = 0; -} +ConfigVar::ConfigVar(const ConfigVar &src) { pd = src.pd; } -ConfigVar::ConfigVar(const ConfigVar &src) -{ - pd = src.pd; -} - -ConfigVar::~ConfigVar() -{ - pd.reset(); -} - -ConfigVar & ConfigVar::operator = (const ConfigVar &src) -{ - if(src.pd == pd) - return *this; - else - pd = src.pd; +ConfigVar::~ConfigVar() { pd.reset(); } +ConfigVar &ConfigVar::operator=(const ConfigVar &src) { + if (src.pd == pd) return *this; + else + pd = src.pd; + + return *this; } -void ConfigVar::resetOffset() -{ - pd->offset = 0; +void ConfigVar::resetOffset() { pd->offset = 0; } + +int ConfigVar::read(unsigned char *buffer_, int bytes) const { + int toCopy = MIN(bytes, pd->buffer.size() - pd->offset); + + if (toCopy > 0) memcpy(buffer_, pd->buffer.data() + pd->offset, toCopy); + + pd->offset += toCopy; + + return toCopy; } -int ConfigVar::read(unsigned char *buffer_, int bytes) const -{ - int toCopy = MIN( bytes, pd->buffer.size() - pd->offset ); +int ConfigVar::write(const unsigned char *data, int bytes) { + if (pd->buffer.size() == (unsigned int)pd->offset) { + pd->buffer.append((const char *)data, bytes); + } else { + pd->buffer.insert(pd->offset, (const char *)data, bytes); + } - if(toCopy > 0) - memcpy( buffer_, pd->buffer.data() + pd->offset, toCopy ); + pd->offset += bytes; - pd->offset += toCopy; - - return toCopy; + return bytes; } -int ConfigVar::write(const unsigned char *data, int bytes) -{ - if(pd->buffer.size() == (unsigned int)pd->offset) - { - pd->buffer.append( (const char *)data, bytes ); - } else - { - pd->buffer.insert( pd->offset, (const char *)data, bytes ); - } +int ConfigVar::size() const { return pd->buffer.size(); } - pd->offset += bytes; +const char *ConfigVar::buffer() const { return pd->buffer.data(); } - return bytes; +int ConfigVar::at() const { return pd->offset; } + +void ConfigVar::writeString(const char *data, int bytes) { + writeInt(bytes); + write((const unsigned char *)data, bytes); } -int ConfigVar::size() const -{ - return pd->buffer.size(); -} - -const char *ConfigVar::buffer() const -{ - return pd->buffer.data(); -} - -int ConfigVar::at() const -{ - return pd->offset; -} - -void ConfigVar::writeString(const char *data, int bytes) -{ - writeInt( bytes ); - write( (const unsigned char *)data, bytes ); -} - - // convert integer to BER encoded integer -void ConfigVar::writeInt(int val) -{ - // we can represent 7 bits per char output, so a 32bit number may take up - // to 5 bytes. - // first byte: 0x0000007f 0111,1111 - // second byte: 0x00003f80 0011,1111 1000,0000 - // third byte: 0x001fb000 0000,0000 0001,1111 1100,0000 0000,0000 - // fourth byte: 0x0fe00000 0000,1111 1110,0000 - // fifth byte: 0xf0000000 1111,0000 - unsigned char digit[5]; +void ConfigVar::writeInt(int val) { + // we can represent 7 bits per char output, so a 32bit number may take up + // to 5 bytes. + // first byte: 0x0000007f 0111,1111 + // second byte: 0x00003f80 0011,1111 1000,0000 + // third byte: 0x001fb000 0000,0000 0001,1111 1100,0000 0000,0000 + // fourth byte: 0x0fe00000 0000,1111 1110,0000 + // fifth byte: 0xf0000000 1111,0000 + unsigned char digit[5]; - digit[4] = (unsigned char)((val & 0x0000007f)); - digit[3] = 0x80 | (unsigned char)((val & 0x00003f80) >> 7); - digit[2] = 0x80 | (unsigned char)((val & 0x001fc000) >> 14); - digit[1] = 0x80 | (unsigned char)((val & 0x0fe00000) >> 21); - digit[0] = 0x80 | (unsigned char)((val & 0xf0000000) >> 28); + digit[4] = (unsigned char)((val & 0x0000007f)); + digit[3] = 0x80 | (unsigned char)((val & 0x00003f80) >> 7); + digit[2] = 0x80 | (unsigned char)((val & 0x001fc000) >> 14); + digit[1] = 0x80 | (unsigned char)((val & 0x0fe00000) >> 21); + digit[0] = 0x80 | (unsigned char)((val & 0xf0000000) >> 28); - // find the starting point - we only need to output starting at the most - // significant non-zero digit.. - int start = 0; - while(digit[start] == 0x80) - ++start; + // find the starting point - we only need to output starting at the most + // significant non-zero digit.. + int start = 0; + while (digit[start] == 0x80) ++start; - write( digit + start, 5-start ); + write(digit + start, 5 - start); } -int ConfigVar::readInt() const -{ - const unsigned char * buf = (const unsigned char *)buffer(); - int bytes = this->size(); - int offset = at(); - int value = 0; - bool highBitSet; +int ConfigVar::readInt() const { + const unsigned char *buf = (const unsigned char *)buffer(); + int bytes = this->size(); + int offset = at(); + int value = 0; + bool highBitSet; - rAssert( offset < bytes ); + rAssert(offset < bytes); - do - { - unsigned char tmp = buf[offset++]; - highBitSet = tmp & 0x80; + do { + unsigned char tmp = buf[offset++]; + highBitSet = tmp & 0x80; - value = (value << 7) | (int)(tmp & 0x7f); - } while(highBitSet && offset < bytes); + value = (value << 7) | (int)(tmp & 0x7f); + } while (highBitSet && offset < bytes); - pd->offset = offset; + pd->offset = offset; - // should never end up with a negative number.. - rAssert( value >= 0 ); + // should never end up with a negative number.. + rAssert(value >= 0); - return value; + return value; } -int ConfigVar::readInt( int defaultValue ) const -{ - int bytes = this->size(); - int offset = at(); +int ConfigVar::readInt(int defaultValue) const { + int bytes = this->size(); + int offset = at(); - if(offset >= bytes) - return defaultValue; - else - return readInt(); + if (offset >= bytes) + return defaultValue; + else + return readInt(); } -bool ConfigVar::readBool( bool defaultValue ) const -{ - int tmp = readInt( defaultValue ? 1 : 0 ); - return (tmp != 0); +bool ConfigVar::readBool(bool defaultValue) const { + int tmp = readInt(defaultValue ? 1 : 0); + return (tmp != 0); } -ConfigVar & operator << (ConfigVar &src, bool value) -{ - src.writeInt( value ? 1 : 0 ); - return src; +ConfigVar &operator<<(ConfigVar &src, bool value) { + src.writeInt(value ? 1 : 0); + return src; } -ConfigVar & operator << (ConfigVar &src, int var) -{ - src.writeInt( var ); - return src; +ConfigVar &operator<<(ConfigVar &src, int var) { + src.writeInt(var); + return src; } -ConfigVar & operator << (ConfigVar &src, const std::string &str) -{ - src.writeString( str.data(), str.length() ); - return src; +ConfigVar &operator<<(ConfigVar &src, const std::string &str) { + src.writeString(str.data(), str.length()); + return src; } -const ConfigVar & operator >> (const ConfigVar &src, bool &result) -{ - int tmp = src.readInt(); - result = (tmp != 0); - return src; +const ConfigVar &operator>>(const ConfigVar &src, bool &result) { + int tmp = src.readInt(); + result = (tmp != 0); + return src; } -const ConfigVar & operator >> (const ConfigVar &src, int &result) -{ - result = src.readInt(); - return src; +const ConfigVar &operator>>(const ConfigVar &src, int &result) { + result = src.readInt(); + return src; } -const ConfigVar & operator >> (const ConfigVar &src, std::string &result) -{ - int length = src.readInt(); - //rAssert(length > 0); +const ConfigVar &operator>>(const ConfigVar &src, std::string &result) { + int length = src.readInt(); + // rAssert(length > 0); - int readLen; + int readLen; - unsigned char tmpBuf[32]; - if(length > (int)sizeof(tmpBuf)) - { - unsigned char *ptr = new unsigned char[length]; - readLen = src.read( ptr, length ); - result.assign( (char*)ptr, length ); - delete[] ptr; - } else - { - readLen = src.read( tmpBuf, length ); - result.assign( (char*)tmpBuf, length ); - } + unsigned char tmpBuf[32]; + if (length > (int)sizeof(tmpBuf)) { + unsigned char *ptr = new unsigned char[length]; + readLen = src.read(ptr, length); + result.assign((char *)ptr, length); + delete[] ptr; + } else { + readLen = src.read(tmpBuf, length); + result.assign((char *)tmpBuf, length); + } - if(readLen != length) - { - rDebug("string encoded as size %i bytes, read %i", length, readLen ); - } - rAssert(readLen == length); + if (readLen != length) { + rDebug("string encoded as size %i bytes, read %i", length, readLen); + } + rAssert(readLen == length); - return src; + return src; } - diff --git a/encfs/ConfigVar.h b/encfs/ConfigVar.h index 4a51304..f6c40b5 100644 --- a/encfs/ConfigVar.h +++ b/encfs/ConfigVar.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -24,58 +24,55 @@ #include #include "shared_ptr.h" -class ConfigVar -{ - struct ConfigVarData - { - std::string buffer; - int offset; - }; +class ConfigVar { + struct ConfigVarData { + std::string buffer; + int offset; + }; - shared_ptr pd; + shared_ptr pd; -public: - ConfigVar(); - ConfigVar(const std::string &buffer); - ConfigVar(const ConfigVar &src); - ~ConfigVar(); + public: + ConfigVar(); + ConfigVar(const std::string &buffer); + ConfigVar(const ConfigVar &src); + ~ConfigVar(); - ConfigVar & operator = (const ConfigVar &src); + ConfigVar &operator=(const ConfigVar &src); - // reset read/write offset.. - void resetOffset(); + // reset read/write offset.. + void resetOffset(); - // read bytes - int read(unsigned char *buffer, int size) const; + // read bytes + int read(unsigned char *buffer, int size) const; - // write bytes.. - int write(const unsigned char *data, int size); + // write bytes.. + int write(const unsigned char *data, int size); - int readInt() const; - int readInt( int defaultValue ) const; - void writeInt(int value); + int readInt() const; + int readInt(int defaultValue) const; + void writeInt(int value); - bool readBool( bool defaultValue ) const; + bool readBool(bool defaultValue) const; - void writeString(const char *data, int size); + void writeString(const char *data, int size); - // return amount of data in var - int size() const; - // return data pointer - returns front of data pointer, not the current - // position. - const char *buffer() const; + // return amount of data in var + int size() const; + // return data pointer - returns front of data pointer, not the current + // position. + const char *buffer() const; - // return current position in data() buffer. - int at() const; + // return current position in data() buffer. + int at() const; }; -ConfigVar & operator << (ConfigVar &, bool); -ConfigVar & operator << (ConfigVar &, int); -ConfigVar & operator << (ConfigVar &, const std::string &str); +ConfigVar &operator<<(ConfigVar &, bool); +ConfigVar &operator<<(ConfigVar &, int); +ConfigVar &operator<<(ConfigVar &, const std::string &str); -const ConfigVar & operator >> (const ConfigVar &, bool &); -const ConfigVar & operator >> (const ConfigVar &, int &); -const ConfigVar & operator >> (const ConfigVar &, std::string &str); +const ConfigVar &operator>>(const ConfigVar &, bool &); +const ConfigVar &operator>>(const ConfigVar &, int &); +const ConfigVar &operator>>(const ConfigVar &, std::string &str); #endif - diff --git a/encfs/Context.cpp b/encfs/Context.cpp index 3eba9e5..f389f50 100644 --- a/encfs/Context.cpp +++ b/encfs/Context.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -29,149 +29,126 @@ using namespace rel; using namespace rlog; -EncFS_Context::EncFS_Context() -{ - pthread_cond_init( &wakeupCond, 0 ); - pthread_mutex_init( &wakeupMutex, 0 ); - pthread_mutex_init( &contextMutex, 0 ); +EncFS_Context::EncFS_Context() { + pthread_cond_init(&wakeupCond, 0); + pthread_mutex_init(&wakeupMutex, 0); + pthread_mutex_init(&contextMutex, 0); - usageCount = 0; + usageCount = 0; } -EncFS_Context::~EncFS_Context() -{ - pthread_mutex_destroy( &contextMutex ); - pthread_mutex_destroy( &wakeupMutex ); - pthread_cond_destroy( &wakeupCond ); +EncFS_Context::~EncFS_Context() { + pthread_mutex_destroy(&contextMutex); + pthread_mutex_destroy(&wakeupMutex); + pthread_cond_destroy(&wakeupCond); - // release all entries from map - openFiles.clear(); + // release all entries from map + openFiles.clear(); } -shared_ptr EncFS_Context::getRoot(int *errCode) -{ - shared_ptr ret; - do +shared_ptr EncFS_Context::getRoot(int *errCode) { + shared_ptr ret; + do { { - { - Lock lock( contextMutex ); - ret = root; - ++usageCount; - } - - if(!ret) - { - int res = remountFS( this ); - if(res != 0) - { - *errCode = res; - break; - } - } - } while(!ret); - - return ret; -} - -void EncFS_Context::setRoot(const shared_ptr &r) -{ - Lock lock( contextMutex ); - - root = r; - if(r) - rootCipherDir = r->rootDirectory(); -} - -bool EncFS_Context::isMounted() -{ - return root; -} - -int EncFS_Context::getAndResetUsageCounter() -{ - Lock lock( contextMutex ); - - int count = usageCount; - usageCount = 0; - - return count; -} - -int EncFS_Context::openFileCount() const -{ - Lock lock( contextMutex ); - - return openFiles.size(); -} - -shared_ptr EncFS_Context::lookupNode(const char *path) -{ - Lock lock( contextMutex ); - - FileMap::iterator it = openFiles.find( std::string(path) ); - if(it != openFiles.end()) - { - // all the items in the set point to the same node.. so just use the - // first - return (*it->second.begin())->node; - } else - { - return shared_ptr(); - } -} - -void EncFS_Context::renameNode(const char *from, const char *to) -{ - Lock lock( contextMutex ); - - FileMap::iterator it = openFiles.find( std::string(from) ); - if(it != openFiles.end()) - { - std::set val = it->second; - openFiles.erase(it); - openFiles[ std::string(to) ] = val; - } -} - -shared_ptr EncFS_Context::getNode(void *pl) -{ - Placeholder *ph = (Placeholder*)pl; - return ph->node; -} - -void *EncFS_Context::putNode(const char *path, - const shared_ptr &node) -{ - Lock lock( contextMutex ); - Placeholder *pl = new Placeholder( node ); - openFiles[ std::string(path) ].insert(pl); - - return (void *)pl; -} - -void EncFS_Context::eraseNode(const char *path, void *pl) -{ - Lock lock( contextMutex ); - - Placeholder *ph = (Placeholder *)pl; - - FileMap::iterator it = openFiles.find( std::string(path) ); - rAssert(it != openFiles.end()); - - int rmCount = it->second.erase( ph ); - - rAssert(rmCount == 1); - - // if no more references to this file, remove the record all together - if(it->second.empty()) - { - // attempts to make use of shallow copy to clear memory used to hold - // unencrypted filenames.. not sure this does any good.. - std::string storedName = it->first; - openFiles.erase( it ); - storedName.assign( storedName.length(), '\0' ); + Lock lock(contextMutex); + ret = root; + ++usageCount; } - delete ph; + if (!ret) { + int res = remountFS(this); + if (res != 0) { + *errCode = res; + break; + } + } + } while (!ret); + + return ret; } +void EncFS_Context::setRoot(const shared_ptr &r) { + Lock lock(contextMutex); + + root = r; + if (r) rootCipherDir = r->rootDirectory(); +} + +bool EncFS_Context::isMounted() { return root; } + +int EncFS_Context::getAndResetUsageCounter() { + Lock lock(contextMutex); + + int count = usageCount; + usageCount = 0; + + return count; +} + +int EncFS_Context::openFileCount() const { + Lock lock(contextMutex); + + return openFiles.size(); +} + +shared_ptr EncFS_Context::lookupNode(const char *path) { + Lock lock(contextMutex); + + FileMap::iterator it = openFiles.find(std::string(path)); + if (it != openFiles.end()) { + // all the items in the set point to the same node.. so just use the + // first + return (*it->second.begin())->node; + } else { + return shared_ptr(); + } +} + +void EncFS_Context::renameNode(const char *from, const char *to) { + Lock lock(contextMutex); + + FileMap::iterator it = openFiles.find(std::string(from)); + if (it != openFiles.end()) { + std::set val = it->second; + openFiles.erase(it); + openFiles[std::string(to)] = val; + } +} + +shared_ptr EncFS_Context::getNode(void *pl) { + Placeholder *ph = (Placeholder *)pl; + return ph->node; +} + +void *EncFS_Context::putNode(const char *path, + const shared_ptr &node) { + Lock lock(contextMutex); + Placeholder *pl = new Placeholder(node); + openFiles[std::string(path)].insert(pl); + + return (void *)pl; +} + +void EncFS_Context::eraseNode(const char *path, void *pl) { + Lock lock(contextMutex); + + Placeholder *ph = (Placeholder *)pl; + + FileMap::iterator it = openFiles.find(std::string(path)); + rAssert(it != openFiles.end()); + + int rmCount = it->second.erase(ph); + + rAssert(rmCount == 1); + + // if no more references to this file, remove the record all together + if (it->second.empty()) { + // attempts to make use of shallow copy to clear memory used to hold + // unencrypted filenames.. not sure this does any good.. + std::string storedName = it->first; + openFiles.erase(it); + storedName.assign(storedName.length(), '\0'); + } + + delete ph; +} diff --git a/encfs/Context.h b/encfs/Context.h index be5ea5b..f8f64e5 100644 --- a/encfs/Context.h +++ b/encfs/Context.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -37,75 +37,70 @@ struct EncFS_Opts; class FileNode; class DirNode; -class EncFS_Context -{ -public: - EncFS_Context(); - ~EncFS_Context(); +class EncFS_Context { + public: + EncFS_Context(); + ~EncFS_Context(); - shared_ptr getNode(void *ptr); - shared_ptr lookupNode(const char *path); + shared_ptr getNode(void *ptr); + shared_ptr lookupNode(const char *path); - int getAndResetUsageCounter(); - int openFileCount() const; + int getAndResetUsageCounter(); + int openFileCount() const; - void *putNode(const char *path, const shared_ptr &node); + void *putNode(const char *path, const shared_ptr &node); - void eraseNode(const char *path, void *placeholder); + void eraseNode(const char *path, void *placeholder); - void renameNode(const char *oldName, const char *newName); + void renameNode(const char *oldName, const char *newName); - void setRoot(const shared_ptr &root); - shared_ptr getRoot(int *err); - bool isMounted(); + void setRoot(const shared_ptr &root); + shared_ptr getRoot(int *err); + bool isMounted(); - shared_ptr args; - shared_ptr opts; - bool publicFilesystem; + shared_ptr args; + shared_ptr opts; + bool publicFilesystem; - // root path to cipher dir - std::string rootCipherDir; + // root path to cipher dir + std::string rootCipherDir; - // for idle monitor - bool running; - pthread_t monitorThread; - pthread_cond_t wakeupCond; - pthread_mutex_t wakeupMutex; + // for idle monitor + bool running; + pthread_t monitorThread; + pthread_cond_t wakeupCond; + pthread_mutex_t wakeupMutex; -private: - /* This placeholder is what is referenced in FUSE context (passed to - * callbacks). - * - * A FileNode may be opened many times, but only one FileNode instance per - * file is kept. Rather then doing reference counting in FileNode, we - * store a unique Placeholder for each open() until the corresponding - * release() is called. shared_ptr then does our reference counting for - * us. - */ - struct Placeholder - { - shared_ptr node; + private: + /* This placeholder is what is referenced in FUSE context (passed to + * callbacks). + * + * A FileNode may be opened many times, but only one FileNode instance per + * file is kept. Rather then doing reference counting in FileNode, we + * store a unique Placeholder for each open() until the corresponding + * release() is called. shared_ptr then does our reference counting for + * us. + */ + struct Placeholder { + shared_ptr node; - Placeholder( const shared_ptr &ptr ) : node(ptr) {} - }; + Placeholder(const shared_ptr &ptr) : node(ptr) {} + }; - // set of open files, indexed by path #ifdef USE_HASHMAP - typedef __gnu_cxx::hash_map > FileMap; + // set of open files, indexed by path + typedef __gnu_cxx::hash_map > FileMap; #else - typedef std::map< std::string, - std::set > FileMap; + typedef std::map > FileMap; #endif - mutable pthread_mutex_t contextMutex; - FileMap openFiles; + mutable pthread_mutex_t contextMutex; + FileMap openFiles; - int usageCount; - shared_ptr root; + int usageCount; + shared_ptr root; }; -int remountFS( EncFS_Context *ctx ); +int remountFS(EncFS_Context *ctx); #endif - diff --git a/encfs/DirNode.cpp b/encfs/DirNode.cpp index cc49925..ebebacd 100644 --- a/encfs/DirNode.cpp +++ b/encfs/DirNode.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -48,508 +48,407 @@ using namespace std; using namespace rel; using namespace rlog; -static RLogChannel *Info = DEF_CHANNEL( "info/DirNode", Log_Info ); +static RLogChannel *Info = DEF_CHANNEL("info/DirNode", Log_Info); -class DirDeleter -{ -public: - void operator () ( DIR *d ) - { - ::closedir( d ); - } +class DirDeleter { + public: + void operator()(DIR *d) { ::closedir(d); } }; - -DirTraverse::DirTraverse(const shared_ptr &_dirPtr, - uint64_t _iv, const shared_ptr &_naming) - : dir( _dirPtr ) - , iv( _iv ) - , naming( _naming ) -{ -} +DirTraverse::DirTraverse(const shared_ptr &_dirPtr, uint64_t _iv, + const shared_ptr &_naming) + : dir(_dirPtr), iv(_iv), naming(_naming) {} DirTraverse::DirTraverse(const DirTraverse &src) - : dir( src.dir ) - , iv( src.iv ) - , naming( src.naming ) -{ + : dir(src.dir), iv(src.iv), naming(src.naming) {} + +DirTraverse &DirTraverse::operator=(const DirTraverse &src) { + dir = src.dir; + iv = src.iv; + naming = src.naming; + + return *this; } -DirTraverse &DirTraverse::operator = (const DirTraverse &src) -{ - dir = src.dir; - iv = src.iv; - naming = src.naming; - - return *this; +DirTraverse::~DirTraverse() { + dir.reset(); + iv = 0; + naming.reset(); } -DirTraverse::~DirTraverse() -{ - dir.reset(); - iv = 0; - naming.reset(); -} +static bool _nextName(struct dirent *&de, const shared_ptr &dir, + int *fileType, ino_t *inode) { + de = ::readdir(dir.get()); -static -bool _nextName(struct dirent *&de, const shared_ptr &dir, - int *fileType, ino_t *inode) -{ - de = ::readdir( dir.get() ); - - if(de) - { - if(fileType) - { + if (de) { + if (fileType) { #if defined(_DIRENT_HAVE_D_TYPE) || defined(__FreeBSD__) || defined(__APPLE__) - *fileType = de->d_type; + *fileType = de->d_type; #else #warning "struct dirent.d_type not supported" - *fileType = 0; + *fileType = 0; #endif - } - if(inode) - *inode = de->d_ino; - return true; - } else - { - if(fileType) - *fileType = 0; - return false; } + if (inode) *inode = de->d_ino; + return true; + } else { + if (fileType) *fileType = 0; + return false; + } } - -std::string DirTraverse::nextPlaintextName(int *fileType, ino_t *inode) -{ - struct dirent *de=0; - while(_nextName(de, dir, fileType, inode)) - { - try - { - uint64_t localIv = iv; - return naming->decodePath( de->d_name, &localIv ); - } catch ( rlog::Error &ex ) - { - // .. .problem decoding, ignore it and continue on to next name.. - rDebug("error decoding filename: %s", de->d_name); - } +std::string DirTraverse::nextPlaintextName(int *fileType, ino_t *inode) { + struct dirent *de = 0; + while (_nextName(de, dir, fileType, inode)) { + try { + uint64_t localIv = iv; + return naming->decodePath(de->d_name, &localIv); } + catch (rlog::Error &ex) { + // .. .problem decoding, ignore it and continue on to next name.. + rDebug("error decoding filename: %s", de->d_name); + } + } - return string(); + return string(); } -std::string DirTraverse::nextInvalid() -{ - struct dirent *de=0; - // find the first name which produces a decoding error... - while(_nextName(de, dir, (int*)0, (ino_t*)0)) - { - try - { - uint64_t localIv = iv; - naming->decodePath( de->d_name, &localIv ); - continue; - } catch( rlog::Error &ex ) - { - return string( de->d_name ); - } +std::string DirTraverse::nextInvalid() { + struct dirent *de = 0; + // find the first name which produces a decoding error... + while (_nextName(de, dir, (int *)0, (ino_t *)0)) { + try { + uint64_t localIv = iv; + naming->decodePath(de->d_name, &localIv); + continue; } + catch (rlog::Error &ex) { + return string(de->d_name); + } + } - return string(); + return string(); } -struct RenameEl -{ - // ciphertext names - string oldCName; - string newCName; // intermediate name (not final cname) +struct RenameEl { + // ciphertext names + string oldCName; + string newCName; // intermediate name (not final cname) - // plaintext names - string oldPName; - string newPName; + // plaintext names + string oldPName; + string newPName; - bool isDirectory; + bool isDirectory; }; -class RenameOp -{ -private: - DirNode *dn; - shared_ptr< list > renameList; - list::const_iterator last; +class RenameOp { + private: + DirNode *dn; + shared_ptr > renameList; + list::const_iterator last; -public: - RenameOp( DirNode *_dn, const shared_ptr< list > &_renameList ) - : dn(_dn), renameList(_renameList) - { - last = renameList->begin(); - } + public: + RenameOp(DirNode *_dn, const shared_ptr > &_renameList) + : dn(_dn), renameList(_renameList) { + last = renameList->begin(); + } - RenameOp(const RenameOp &src) - : dn(src.dn) - , renameList(src.renameList) - , last(src.last) - { - } + RenameOp(const RenameOp &src) + : dn(src.dn), renameList(src.renameList), last(src.last) {} - ~RenameOp(); + ~RenameOp(); - operator bool () const - { - return renameList; - } + operator bool() const { return renameList; } - bool apply(); - void undo(); + bool apply(); + void undo(); }; -RenameOp::~RenameOp() -{ - if(renameList) - { - // got a bunch of decoded filenames sitting in memory.. do a little - // cleanup before leaving.. - list::iterator it; - for(it = renameList->begin(); it != renameList->end(); ++it) - { - it->oldPName.assign( it->oldPName.size(), ' ' ); - it->newPName.assign( it->newPName.size(), ' ' ); - } +RenameOp::~RenameOp() { + if (renameList) { + // got a bunch of decoded filenames sitting in memory.. do a little + // cleanup before leaving.. + list::iterator it; + for (it = renameList->begin(); it != renameList->end(); ++it) { + it->oldPName.assign(it->oldPName.size(), ' '); + it->newPName.assign(it->newPName.size(), ' '); } + } } -bool RenameOp::apply() -{ - try - { - while(last != renameList->end()) - { - // backing store rename. - rDebug("renaming %s -> %s", - last->oldCName.c_str(), last->newCName.c_str()); +bool RenameOp::apply() { + try { + while (last != renameList->end()) { + // backing store rename. + rDebug("renaming %s -> %s", last->oldCName.c_str(), + last->newCName.c_str()); - struct stat st; - bool preserve_mtime = ::stat(last->oldCName.c_str(), &st) == 0; + struct stat st; + bool preserve_mtime = ::stat(last->oldCName.c_str(), &st) == 0; - // internal node rename.. - dn->renameNode( last->oldPName.c_str(), - last->newPName.c_str() ); + // internal node rename.. + dn->renameNode(last->oldPName.c_str(), last->newPName.c_str()); - // rename on disk.. - if(::rename( last->oldCName.c_str(), - last->newCName.c_str() ) == -1) - { - rWarning("Error renaming %s: %s", - last->oldCName.c_str(), strerror( errno )); - dn->renameNode( last->newPName.c_str(), - last->oldPName.c_str(), false ); - return false; - } - - if(preserve_mtime) - { - struct utimbuf ut; - ut.actime = st.st_atime; - ut.modtime = st.st_mtime; - ::utime(last->newCName.c_str(), &ut); - } - - ++last; - } - - return true; - } catch( rlog::Error &err ) - { - err.log( _RLWarningChannel ); + // rename on disk.. + if (::rename(last->oldCName.c_str(), last->newCName.c_str()) == -1) { + rWarning("Error renaming %s: %s", last->oldCName.c_str(), + strerror(errno)); + dn->renameNode(last->newPName.c_str(), last->oldPName.c_str(), false); return false; - } -} + } -void RenameOp::undo() -{ - rDebug("in undoRename"); + if (preserve_mtime) { + struct utimbuf ut; + ut.actime = st.st_atime; + ut.modtime = st.st_mtime; + ::utime(last->newCName.c_str(), &ut); + } - if(last == renameList->begin()) - { - rDebug("nothing to undo"); - return; // nothing to undo - } - - // list has to be processed backwards, otherwise we may rename - // directories and directory contents in the wrong order! - int undoCount = 0; - list::const_iterator it = last; - - while( it != renameList->begin() ) - { - --it; - - rDebug("undo: renaming %s -> %s", - it->newCName.c_str(), it->oldCName.c_str()); - - ::rename( it->newCName.c_str(), it->oldCName.c_str() ); - try - { - dn->renameNode( it->newPName.c_str(), - it->oldPName.c_str(), false ); - } catch( rlog::Error &err ) - { - err.log( _RLWarningChannel ); - // continue on anyway... - } - ++undoCount; - }; - - rWarning("Undo rename count: %i", undoCount); -} - -DirNode::DirNode(EncFS_Context *_ctx, - const string &sourceDir, - const FSConfigPtr &_config) -{ - pthread_mutex_init( &mutex, 0 ); - - Lock _lock( mutex ); - - ctx = _ctx; - rootDir = sourceDir; - fsConfig = _config; - - // make sure rootDir ends in '/', so that we can form a path by appending - // the rest.. - if( rootDir[ rootDir.length()-1 ] != '/' ) - rootDir.append( 1, '/'); - - naming = fsConfig->nameCoding; -} - -DirNode::~DirNode() -{ -} - -bool -DirNode::hasDirectoryNameDependency() const -{ - return naming ? naming->getChainedNameIV() : false; -} - -string -DirNode::rootDirectory() -{ - // don't update last access here, otherwise 'du' would cause lastAccess to - // be reset. - // chop off '/' terminator from root dir. - return string( rootDir, 0, rootDir.length()-1 ); -} - -string -DirNode::cipherPath( const char *plaintextPath ) -{ - return rootDir + naming->encodePath( plaintextPath ); -} - -string -DirNode::cipherPathWithoutRoot( const char *plaintextPath ) -{ - return naming->encodePath( plaintextPath ); -} - -string -DirNode::plainPath( const char *cipherPath_ ) -{ - try - { - if( !strncmp( cipherPath_, rootDir.c_str(), - rootDir.length() ) ) - { - return naming->decodePath( cipherPath_ + rootDir.length() ); - } else - { - if ( cipherPath_[0] == '+' ) - { - // decode as fully qualified path - return string("/") + naming->decodeName( cipherPath_+1, - strlen(cipherPath_+1) ); - } else - { - return naming->decodePath( cipherPath_ ); - } - } - - } catch( rlog::Error &err ) - { - rError("decode err: %s", err.message()); - err.log( _RLWarningChannel ); - - return string(); - } -} - -string -DirNode::relativeCipherPath( const char *plaintextPath ) -{ - try - { - if(plaintextPath[0] == '/') - { - // mark with '+' to indicate special decoding.. - return string("+") + naming->encodeName(plaintextPath+1, - strlen(plaintextPath+1)); - } else - { - return naming->encodePath( plaintextPath ); - } - } catch( rlog::Error &err ) - { - rError("encode err: %s", err.message()); - err.log( _RLWarningChannel ); - - return string(); - } -} - -DirTraverse DirNode::openDir(const char *plaintextPath) -{ - string cyName = rootDir + naming->encodePath( plaintextPath ); - //rDebug("openDir on %s", cyName.c_str() ); - - DIR *dir = ::opendir( cyName.c_str() ); - if(dir == NULL) - { - rDebug("opendir error %s", strerror(errno)); - return DirTraverse( shared_ptr(), 0, shared_ptr() ); - } else - { - shared_ptr dp( dir, DirDeleter() ); - - uint64_t iv = 0; - // if we're using chained IV mode, then compute the IV at this - // directory level.. - try - { - if( naming->getChainedNameIV() ) - naming->encodePath( plaintextPath, &iv ); - } catch( rlog::Error &err ) - { - rError("encode err: %s", err.message()); - err.log( _RLWarningChannel ); - } - return DirTraverse( dp, iv, naming ); - } -} - -bool DirNode::genRenameList( list &renameList, - const char *fromP, const char *toP ) -{ - uint64_t fromIV = 0, toIV = 0; - - // compute the IV for both paths - string fromCPart = naming->encodePath( fromP, &fromIV ); - string toCPart = naming->encodePath( toP, &toIV ); - - // where the files live before the rename.. - string sourcePath = rootDir + fromCPart; - - // ok..... we wish it was so simple.. should almost never happen - if(fromIV == toIV) - return true; - - // generate the real destination path, where we expect to find the files.. - rDebug("opendir %s", sourcePath.c_str() ); - shared_ptr dir = shared_ptr( - opendir( sourcePath.c_str() ), DirDeleter() ); - if(!dir) - return false; - - struct dirent *de = NULL; - while((de = ::readdir( dir.get() )) != NULL) - { - // decode the name using the oldIV - uint64_t localIV = fromIV; - string plainName; - - if((de->d_name[0] == '.') && - ((de->d_name[1] == '\0') - || ((de->d_name[1] == '.') && (de->d_name[2] == '\0')))) - { - // skip "." and ".." - continue; - } - - try - { - plainName = naming->decodePath( de->d_name, &localIV ); - } catch( rlog::Error &ex ) - { - // if filename can't be decoded, then ignore it.. - continue; - } - - // any error in the following will trigger a rename failure. - try - { - // re-encode using the new IV.. - localIV = toIV; - string newName = naming->encodePath( plainName.c_str(), &localIV ); - - // store rename information.. - string oldFull = sourcePath + '/' + de->d_name; - string newFull = sourcePath + '/' + newName; - - RenameEl ren; - ren.oldCName = oldFull; - ren.newCName = newFull; - ren.oldPName = string(fromP) + '/' + plainName; - ren.newPName = string(toP) + '/' + plainName; - - bool isDir; -#if defined(_DIRENT_HAVE_D_TYPE) - if(de->d_type != DT_UNKNOWN) - { - isDir = (de->d_type == DT_DIR); - } else -#endif - { - isDir = isDirectory( oldFull.c_str() ); - } - - ren.isDirectory = isDir; - - if(isDir) - { - // recurse.. We want to add subdirectory elements before the - // parent, as that is the logical rename order.. - if(!genRenameList( renameList, - ren.oldPName.c_str(), - ren.newPName.c_str())) - { - return false; - } - } - - rDebug("adding file %s to rename list", - oldFull.c_str()); - - renameList.push_back( ren ); - - } catch( rlog::Error &err ) - { - // We can't convert this name, because we don't have a valid IV for - // it (or perhaps a valid key).. It will be inaccessible.. - rWarning("Aborting rename: error on file: %s", - fromCPart.append(1, '/').append(de->d_name).c_str()); - err.log( _RLDebugChannel ); - - // abort.. Err on the side of safety and disallow rename, rather - // then loosing files.. - return false; - } + ++last; } return true; + } + catch (rlog::Error &err) { + err.log(_RLWarningChannel); + return false; + } } +void RenameOp::undo() { + rDebug("in undoRename"); + + if (last == renameList->begin()) { + rDebug("nothing to undo"); + return; // nothing to undo + } + + // list has to be processed backwards, otherwise we may rename + // directories and directory contents in the wrong order! + int undoCount = 0; + list::const_iterator it = last; + + while (it != renameList->begin()) { + --it; + + rDebug("undo: renaming %s -> %s", it->newCName.c_str(), + it->oldCName.c_str()); + + ::rename(it->newCName.c_str(), it->oldCName.c_str()); + try { + dn->renameNode(it->newPName.c_str(), it->oldPName.c_str(), false); + } + catch (rlog::Error &err) { + err.log(_RLWarningChannel); + // continue on anyway... + } + ++undoCount; + }; + + rWarning("Undo rename count: %i", undoCount); +} + +DirNode::DirNode(EncFS_Context *_ctx, const string &sourceDir, + const FSConfigPtr &_config) { + pthread_mutex_init(&mutex, 0); + + Lock _lock(mutex); + + ctx = _ctx; + rootDir = sourceDir; + fsConfig = _config; + + // make sure rootDir ends in '/', so that we can form a path by appending + // the rest.. + if (rootDir[rootDir.length() - 1] != '/') rootDir.append(1, '/'); + + naming = fsConfig->nameCoding; +} + +DirNode::~DirNode() {} + +bool DirNode::hasDirectoryNameDependency() const { + return naming ? naming->getChainedNameIV() : false; +} + +string DirNode::rootDirectory() { + // don't update last access here, otherwise 'du' would cause lastAccess to + // be reset. + // chop off '/' terminator from root dir. + return string(rootDir, 0, rootDir.length() - 1); +} + +string DirNode::cipherPath(const char *plaintextPath) { + return rootDir + naming->encodePath(plaintextPath); +} + +string DirNode::cipherPathWithoutRoot(const char *plaintextPath) { + return naming->encodePath(plaintextPath); +} + +string DirNode::plainPath(const char *cipherPath_) { + try { + if (!strncmp(cipherPath_, rootDir.c_str(), rootDir.length())) { + return naming->decodePath(cipherPath_ + rootDir.length()); + } else { + if (cipherPath_[0] == '+') { + // decode as fully qualified path + return string("/") + + naming->decodeName(cipherPath_ + 1, strlen(cipherPath_ + 1)); + } else { + return naming->decodePath(cipherPath_); + } + } + } + catch (rlog::Error &err) { + rError("decode err: %s", err.message()); + err.log(_RLWarningChannel); + + return string(); + } +} + +string DirNode::relativeCipherPath(const char *plaintextPath) { + try { + if (plaintextPath[0] == '/') { + // mark with '+' to indicate special decoding.. + return string("+") + + naming->encodeName(plaintextPath + 1, strlen(plaintextPath + 1)); + } else { + return naming->encodePath(plaintextPath); + } + } + catch (rlog::Error &err) { + rError("encode err: %s", err.message()); + err.log(_RLWarningChannel); + + return string(); + } +} + +DirTraverse DirNode::openDir(const char *plaintextPath) { + string cyName = rootDir + naming->encodePath(plaintextPath); + // rDebug("openDir on %s", cyName.c_str() ); + + DIR *dir = ::opendir(cyName.c_str()); + if (dir == NULL) { + rDebug("opendir error %s", strerror(errno)); + return DirTraverse(shared_ptr(), 0, shared_ptr()); + } else { + shared_ptr dp(dir, DirDeleter()); + + uint64_t iv = 0; + // if we're using chained IV mode, then compute the IV at this + // directory level.. + try { + if (naming->getChainedNameIV()) naming->encodePath(plaintextPath, &iv); + } + catch (rlog::Error &err) { + rError("encode err: %s", err.message()); + err.log(_RLWarningChannel); + } + return DirTraverse(dp, iv, naming); + } +} + +bool DirNode::genRenameList(list &renameList, const char *fromP, + const char *toP) { + uint64_t fromIV = 0, toIV = 0; + + // compute the IV for both paths + string fromCPart = naming->encodePath(fromP, &fromIV); + string toCPart = naming->encodePath(toP, &toIV); + + // where the files live before the rename.. + string sourcePath = rootDir + fromCPart; + + // ok..... we wish it was so simple.. should almost never happen + if (fromIV == toIV) return true; + + // generate the real destination path, where we expect to find the files.. + rDebug("opendir %s", sourcePath.c_str()); + shared_ptr dir = + shared_ptr(opendir(sourcePath.c_str()), DirDeleter()); + if (!dir) return false; + + struct dirent *de = NULL; + while ((de = ::readdir(dir.get())) != NULL) { + // decode the name using the oldIV + uint64_t localIV = fromIV; + string plainName; + + if ((de->d_name[0] == '.') && + ((de->d_name[1] == '\0') || + ((de->d_name[1] == '.') && (de->d_name[2] == '\0')))) { + // skip "." and ".." + continue; + } + + try { + plainName = naming->decodePath(de->d_name, &localIV); + } + catch (rlog::Error &ex) { + // if filename can't be decoded, then ignore it.. + continue; + } + + // any error in the following will trigger a rename failure. + try { + // re-encode using the new IV.. + localIV = toIV; + string newName = naming->encodePath(plainName.c_str(), &localIV); + + // store rename information.. + string oldFull = sourcePath + '/' + de->d_name; + string newFull = sourcePath + '/' + newName; + + RenameEl ren; + ren.oldCName = oldFull; + ren.newCName = newFull; + ren.oldPName = string(fromP) + '/' + plainName; + ren.newPName = string(toP) + '/' + plainName; + + bool isDir; +#if defined(_DIRENT_HAVE_D_TYPE) + if (de->d_type != DT_UNKNOWN) { + isDir = (de->d_type == DT_DIR); + } else +#endif + { + isDir = isDirectory(oldFull.c_str()); + } + + ren.isDirectory = isDir; + + if (isDir) { + // recurse.. We want to add subdirectory elements before the + // parent, as that is the logical rename order.. + if (!genRenameList(renameList, ren.oldPName.c_str(), + ren.newPName.c_str())) { + return false; + } + } + + rDebug("adding file %s to rename list", oldFull.c_str()); + + renameList.push_back(ren); + } + catch (rlog::Error &err) { + // We can't convert this name, because we don't have a valid IV for + // it (or perhaps a valid key).. It will be inaccessible.. + rWarning("Aborting rename: error on file: %s", + fromCPart.append(1, '/').append(de->d_name).c_str()); + err.log(_RLDebugChannel); + + // abort.. Err on the side of safety and disallow rename, rather + // then loosing files.. + return false; + } + } + + return true; +} /* A bit of a pain.. If a directory is renamed in a filesystem with @@ -559,224 +458,189 @@ bool DirNode::genRenameList( list &renameList, Returns a list of renamed items on success, a null list on failure. */ -shared_ptr -DirNode::newRenameOp( const char *fromP, const char *toP ) -{ - // Do the rename in two stages to avoid chasing our tail - // Undo everything if we encounter an error! - shared_ptr< list > renameList(new list); - if(!genRenameList( *renameList.get(), fromP, toP )) - { - rWarning("Error during generation of recursive rename list"); - return shared_ptr(); - } else - return shared_ptr( new RenameOp(this, renameList) ); +shared_ptr DirNode::newRenameOp(const char *fromP, const char *toP) { + // Do the rename in two stages to avoid chasing our tail + // Undo everything if we encounter an error! + shared_ptr > renameList(new list); + if (!genRenameList(*renameList.get(), fromP, toP)) { + rWarning("Error during generation of recursive rename list"); + return shared_ptr(); + } else + return shared_ptr(new RenameOp(this, renameList)); } +int DirNode::mkdir(const char *plaintextPath, mode_t mode, uid_t uid, + gid_t gid) { + string cyName = rootDir + naming->encodePath(plaintextPath); + rAssert(!cyName.empty()); -int DirNode::mkdir(const char *plaintextPath, mode_t mode, - uid_t uid, gid_t gid) -{ - string cyName = rootDir + naming->encodePath( plaintextPath ); - rAssert( !cyName.empty() ); + rLog(Info, "mkdir on %s", cyName.c_str()); - rLog( Info, "mkdir on %s", cyName.c_str() ); + // if uid or gid are set, then that should be the directory owner + int olduid = -1; + int oldgid = -1; + if (uid != 0) olduid = setfsuid(uid); + if (gid != 0) oldgid = setfsgid(gid); - // if uid or gid are set, then that should be the directory owner - int olduid = -1; - int oldgid = -1; - if(uid != 0) - olduid = setfsuid( uid ); - if(gid != 0) - oldgid = setfsgid( gid ); + int res = ::mkdir(cyName.c_str(), mode); - int res = ::mkdir( cyName.c_str(), mode ); + if (olduid >= 0) setfsuid(olduid); + if (oldgid >= 0) setfsgid(oldgid); - if(olduid >= 0) - setfsuid( olduid ); - if(oldgid >= 0) - setfsgid( oldgid ); + if (res == -1) { + int eno = errno; + rWarning("mkdir error on %s mode %i: %s", cyName.c_str(), mode, + strerror(eno)); + res = -eno; + } else + res = 0; - if(res == -1) - { - int eno = errno; - rWarning("mkdir error on %s mode %i: %s", cyName.c_str(), - mode, strerror(eno)); - res = -eno; - } else - res = 0; - - return res; + return res; } -int -DirNode::rename( const char *fromPlaintext, const char *toPlaintext ) -{ - Lock _lock( mutex ); +int DirNode::rename(const char *fromPlaintext, const char *toPlaintext) { + Lock _lock(mutex); - string fromCName = rootDir + naming->encodePath( fromPlaintext ); - string toCName = rootDir + naming->encodePath( toPlaintext ); - rAssert( !fromCName.empty() ); - rAssert( !toCName.empty() ); - - rLog( Info, "rename %s -> %s", fromCName.c_str(), toCName.c_str() ); - - shared_ptr toNode = findOrCreate( toPlaintext ); + string fromCName = rootDir + naming->encodePath(fromPlaintext); + string toCName = rootDir + naming->encodePath(toPlaintext); + rAssert(!fromCName.empty()); + rAssert(!toCName.empty()); - shared_ptr renameOp; - if( hasDirectoryNameDependency() && isDirectory( fromCName.c_str() )) - { - rLog( Info, "recursive rename begin" ); - renameOp = newRenameOp( fromPlaintext, toPlaintext ); + rLog(Info, "rename %s -> %s", fromCName.c_str(), toCName.c_str()); - if(!renameOp || !renameOp->apply()) - { - if(renameOp) - renameOp->undo(); + shared_ptr toNode = findOrCreate(toPlaintext); - rWarning("rename aborted"); - return -EACCES; - } - rLog( Info, "recursive rename end" ); + shared_ptr renameOp; + if (hasDirectoryNameDependency() && isDirectory(fromCName.c_str())) { + rLog(Info, "recursive rename begin"); + renameOp = newRenameOp(fromPlaintext, toPlaintext); + + if (!renameOp || !renameOp->apply()) { + if (renameOp) renameOp->undo(); + + rWarning("rename aborted"); + return -EACCES; } + rLog(Info, "recursive rename end"); + } - int res = 0; - try - { - struct stat st; - bool preserve_mtime = ::stat(fromCName.c_str(), &st) == 0; + int res = 0; + try { + struct stat st; + bool preserve_mtime = ::stat(fromCName.c_str(), &st) == 0; - renameNode( fromPlaintext, toPlaintext ); - res = ::rename( fromCName.c_str(), toCName.c_str() ); + renameNode(fromPlaintext, toPlaintext); + res = ::rename(fromCName.c_str(), toCName.c_str()); - if(res == -1) - { - // undo - res = -errno; - renameNode( toPlaintext, fromPlaintext, false ); + if (res == -1) { + // undo + res = -errno; + renameNode(toPlaintext, fromPlaintext, false); - if(renameOp) - renameOp->undo(); - } else if(preserve_mtime) - { - struct utimbuf ut; - ut.actime = st.st_atime; - ut.modtime = st.st_mtime; - ::utime(toCName.c_str(), &ut); - } - } catch( rlog::Error &err ) - { - // exception from renameNode, just show the error and continue.. - err.log( _RLWarningChannel ); - res = -EIO; - } - - if(res != 0) - { - rLog( Info, "rename failed: %s", strerror( errno )); - res = -errno; + if (renameOp) renameOp->undo(); + } else if (preserve_mtime) { + struct utimbuf ut; + ut.actime = st.st_atime; + ut.modtime = st.st_mtime; + ::utime(toCName.c_str(), &ut); } + } + catch (rlog::Error &err) { + // exception from renameNode, just show the error and continue.. + err.log(_RLWarningChannel); + res = -EIO; + } - return res; + if (res != 0) { + rLog(Info, "rename failed: %s", strerror(errno)); + res = -errno; + } + + return res; } -int DirNode::link( const char *from, const char *to ) -{ - Lock _lock( mutex ); +int DirNode::link(const char *from, const char *to) { + Lock _lock(mutex); - string fromCName = rootDir + naming->encodePath( from ); - string toCName = rootDir + naming->encodePath( to ); + string fromCName = rootDir + naming->encodePath(from); + string toCName = rootDir + naming->encodePath(to); - rAssert( !fromCName.empty() ); - rAssert( !toCName.empty() ); + rAssert(!fromCName.empty()); + rAssert(!toCName.empty()); - rLog(Info, "link %s -> %s", fromCName.c_str(), toCName.c_str()); + rLog(Info, "link %s -> %s", fromCName.c_str(), toCName.c_str()); - int res = -EPERM; - if( fsConfig->config->externalIVChaining ) - { - rLog(Info, "hard links not supported with external IV chaining!"); - } else - { - res = ::link( fromCName.c_str(), toCName.c_str() ); - if(res == -1) - res = -errno; - else - res = 0; - } + int res = -EPERM; + if (fsConfig->config->externalIVChaining) { + rLog(Info, "hard links not supported with external IV chaining!"); + } else { + res = ::link(fromCName.c_str(), toCName.c_str()); + if (res == -1) + res = -errno; + else + res = 0; + } - return res; + return res; } /* The node is keyed by filename, so a rename means the internal node names must be changed. */ -shared_ptr DirNode::renameNode( const char *from, const char *to ) -{ - return renameNode( from, to, true ); +shared_ptr DirNode::renameNode(const char *from, const char *to) { + return renameNode(from, to, true); } -shared_ptr DirNode::renameNode( const char *from, const char *to, - bool forwardMode ) -{ - shared_ptr node = findOrCreate( from ); +shared_ptr DirNode::renameNode(const char *from, const char *to, + bool forwardMode) { + shared_ptr node = findOrCreate(from); - if(node) - { - uint64_t newIV = 0; - string cname = rootDir + naming->encodePath( to, &newIV ); + if (node) { + uint64_t newIV = 0; + string cname = rootDir + naming->encodePath(to, &newIV); - rLog(Info, "renaming internal node %s -> %s", - node->cipherName(), cname.c_str()); + rLog(Info, "renaming internal node %s -> %s", node->cipherName(), + cname.c_str()); - if(node->setName( to, cname.c_str(), newIV, forwardMode )) - { - if(ctx) - ctx->renameNode( from, to ); - } else - { - // rename error! - put it back - rError("renameNode failed"); - throw ERROR("Internal node name change failed!"); - } + if (node->setName(to, cname.c_str(), newIV, forwardMode)) { + if (ctx) ctx->renameNode(from, to); + } else { + // rename error! - put it back + rError("renameNode failed"); + throw ERROR("Internal node name change failed!"); } + } - return node; + return node; } -shared_ptr DirNode::findOrCreate( const char *plainName) -{ - shared_ptr node; - if(ctx) - node = ctx->lookupNode( plainName ); +shared_ptr DirNode::findOrCreate(const char *plainName) { + shared_ptr node; + if (ctx) node = ctx->lookupNode(plainName); - if(!node) - { - uint64_t iv = 0; - string cipherName = naming->encodePath( plainName, &iv ); - node.reset( new FileNode( this, fsConfig, - plainName, - (rootDir + cipherName).c_str())); - - if(fsConfig->config->externalIVChaining) - node->setName(0, 0, iv); + if (!node) { + uint64_t iv = 0; + string cipherName = naming->encodePath(plainName, &iv); + node.reset(new FileNode(this, fsConfig, plainName, + (rootDir + cipherName).c_str())); - rLog(Info, "created FileNode for %s", node->cipherName()); - } + if (fsConfig->config->externalIVChaining) node->setName(0, 0, iv); - return node; + rLog(Info, "created FileNode for %s", node->cipherName()); + } + + return node; } -shared_ptr -DirNode::lookupNode( const char *plainName, const char * requestor ) -{ - (void)requestor; - Lock _lock( mutex ); +shared_ptr DirNode::lookupNode(const char *plainName, + const char *requestor) { + (void)requestor; + Lock _lock(mutex); - shared_ptr node = findOrCreate( plainName ); + shared_ptr node = findOrCreate(plainName); - return node; + return node; } /* @@ -784,48 +648,45 @@ DirNode::lookupNode( const char *plainName, const char * requestor ) node on sucess.. This is done in one step to avoid any race conditions with the stored state of the file. */ -shared_ptr -DirNode::openNode( const char *plainName, const char * requestor, int flags, - int *result ) -{ - (void)requestor; - rAssert( result != NULL ); - Lock _lock( mutex ); +shared_ptr DirNode::openNode(const char *plainName, + const char *requestor, int flags, + int *result) { + (void)requestor; + rAssert(result != NULL); + Lock _lock(mutex); - shared_ptr node = findOrCreate( plainName ); + shared_ptr node = findOrCreate(plainName); - if( node && (*result = node->open( flags )) >= 0 ) - return node; - else - return shared_ptr(); + if (node && (*result = node->open(flags)) >= 0) + return node; + else + return shared_ptr(); } -int DirNode::unlink( const char *plaintextName ) -{ - string cyName = naming->encodePath( plaintextName ); - rLog( Info, "unlink %s", cyName.c_str() ); +int DirNode::unlink(const char *plaintextName) { + string cyName = naming->encodePath(plaintextName); + rLog(Info, "unlink %s", cyName.c_str()); - Lock _lock( mutex ); - - int res = 0; - if(ctx && ctx->lookupNode( plaintextName )) - { - // If FUSE is running with "hard_remove" option where it doesn't - // hide open files for us, then we can't allow an unlink of an open - // file.. - rWarning("Refusing to unlink open file: %s, hard_remove option " - "is probably in effect", cyName.c_str() ); - res = -EBUSY; - } else - { - string fullName = rootDir + cyName; - res = ::unlink( fullName.c_str() ); - if(res == -1) - { - res = -errno; - rDebug("unlink error: %s", strerror(errno)); - } + Lock _lock(mutex); + + int res = 0; + if (ctx && ctx->lookupNode(plaintextName)) { + // If FUSE is running with "hard_remove" option where it doesn't + // hide open files for us, then we can't allow an unlink of an open + // file.. + rWarning( + "Refusing to unlink open file: %s, hard_remove option " + "is probably in effect", + cyName.c_str()); + res = -EBUSY; + } else { + string fullName = rootDir + cyName; + res = ::unlink(fullName.c_str()); + if (res == -1) { + res = -errno; + rDebug("unlink error: %s", strerror(errno)); } - - return res; + } + + return res; } diff --git a/encfs/DirNode.h b/encfs/DirNode.h index caf51f5..5ec1dc1 100644 --- a/encfs/DirNode.h +++ b/encfs/DirNode.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -40,146 +40,140 @@ class RenameOp; struct RenameEl; class EncFS_Context; -class DirTraverse -{ -public: - DirTraverse(const shared_ptr &dirPtr, uint64_t iv, - const shared_ptr &naming); - DirTraverse(const DirTraverse &src); - ~DirTraverse(); +class DirTraverse { + public: + DirTraverse(const shared_ptr &dirPtr, uint64_t iv, + const shared_ptr &naming); + DirTraverse(const DirTraverse &src); + ~DirTraverse(); - DirTraverse &operator = (const DirTraverse &src); + DirTraverse &operator=(const DirTraverse &src); - // returns FALSE to indicate an invalid DirTraverse (such as when - // an invalid directory is requested for traversal) - bool valid() const; + // returns FALSE to indicate an invalid DirTraverse (such as when + // an invalid directory is requested for traversal) + bool valid() const; - // return next plaintext filename - // If fileType is not 0, then it is used to return the filetype (or 0 if - // unknown) - std::string nextPlaintextName(int *fileType=0, ino_t *inode=0); + // return next plaintext filename + // If fileType is not 0, then it is used to return the filetype (or 0 if + // unknown) + std::string nextPlaintextName(int *fileType = 0, ino_t *inode = 0); - /* Return cipher name of next undecodable filename.. - The opposite of nextPlaintextName(), as that skips undecodable names.. - */ - std::string nextInvalid(); -private: + /* Return cipher name of next undecodable filename.. + The opposite of nextPlaintextName(), as that skips undecodable names.. + */ + std::string nextInvalid(); - shared_ptr dir; // struct DIR - // initialization vector to use. Not very general purpose, but makes it - // more efficient to support filename IV chaining.. - uint64_t iv; - shared_ptr naming; + private: + shared_ptr dir; // struct DIR + // initialization vector to use. Not very general purpose, but makes it + // more efficient to support filename IV chaining.. + uint64_t iv; + shared_ptr naming; }; inline bool DirTraverse::valid() const { return dir.get() != 0; } #ifdef USE_HASHMAP -namespace __gnu_cxx -{ - template<> struct hash - { - size_t operator() (const std::string &__s) const - { - return __stl_hash_string( __s.c_str() ); - } - }; +namespace __gnu_cxx { +template <> +struct hash { + size_t operator()(const std::string &__s) const { + return __stl_hash_string(__s.c_str()); + } +}; } #endif -class DirNode -{ -public: - // sourceDir points to where raw files are stored - DirNode(EncFS_Context *ctx, - const std::string &sourceDir, - const FSConfigPtr &config ); - ~DirNode(); +class DirNode { + public: + // sourceDir points to where raw files are stored + DirNode(EncFS_Context *ctx, const std::string &sourceDir, + const FSConfigPtr &config); + ~DirNode(); - // return the path to the root directory - std::string rootDirectory(); + // return the path to the root directory + std::string rootDirectory(); - // find files - shared_ptr lookupNode( const char *plaintextName, - const char *requestor ); + // find files + shared_ptr lookupNode(const char *plaintextName, + const char *requestor); - /* - Combined lookupNode + node->open() call. If the open fails, then the - node is not retained. If the open succeeds, then the node is returned. - */ - shared_ptr openNode( const char *plaintextName, - const char *requestor, int flags, int *openResult ); + /* + Combined lookupNode + node->open() call. If the open fails, then the + node is not retained. If the open succeeds, then the node is returned. + */ + shared_ptr openNode(const char *plaintextName, + const char *requestor, int flags, + int *openResult); - std::string cipherPath( const char *plaintextPath ); - std::string cipherPathWithoutRoot( const char *plaintextPath ); - std::string plainPath( const char *cipherPath ); + std::string cipherPath(const char *plaintextPath); + std::string cipherPathWithoutRoot(const char *plaintextPath); + std::string plainPath(const char *cipherPath); - // relative cipherPath is the same as cipherPath except that it doesn't - // prepent the mount point. That it, it doesn't return a fully qualified - // name, just a relative path within the encrypted filesystem. - std::string relativeCipherPath( const char *plaintextPath ); + // relative cipherPath is the same as cipherPath except that it doesn't + // prepent the mount point. That it, it doesn't return a fully qualified + // name, just a relative path within the encrypted filesystem. + std::string relativeCipherPath(const char *plaintextPath); - /* - Returns true if file names are dependent on the parent directory name. - If a directory name is changed, then all the filenames must also be - changed. - */ - bool hasDirectoryNameDependency() const; + /* + Returns true if file names are dependent on the parent directory name. + If a directory name is changed, then all the filenames must also be + changed. + */ + bool hasDirectoryNameDependency() const; - // unlink the specified file - int unlink( const char *plaintextName ); + // unlink the specified file + int unlink(const char *plaintextName); - // traverse directory - DirTraverse openDir( const char *plainDirName ); + // traverse directory + DirTraverse openDir(const char *plainDirName); - // uid and gid are used as the directory owner, only if not zero - int mkdir( const char *plaintextPath, mode_t mode, - uid_t uid = 0, gid_t gid = 0); + // uid and gid are used as the directory owner, only if not zero + int mkdir(const char *plaintextPath, mode_t mode, uid_t uid = 0, + gid_t gid = 0); - int rename( const char *fromPlaintext, const char *toPlaintext ); + int rename(const char *fromPlaintext, const char *toPlaintext); - int link( const char *from, const char *to ); - - // returns idle time of filesystem in seconds - int idleSeconds(); + int link(const char *from, const char *to); -protected: + // returns idle time of filesystem in seconds + int idleSeconds(); - /* - notify that a file is being renamed. - This renames the internal node, if any. If the file is not open, then - this call has no effect. - Returns the FileNode if it was found. - */ - shared_ptr renameNode( const char *from, const char *to ); - shared_ptr renameNode( const char *from, const char *to, - bool forwardMode ); + protected: + /* + notify that a file is being renamed. + This renames the internal node, if any. If the file is not open, then + this call has no effect. + Returns the FileNode if it was found. + */ + shared_ptr renameNode(const char *from, const char *to); + shared_ptr renameNode(const char *from, const char *to, + bool forwardMode); - /* - when directory IV chaining is enabled, a directory can't be renamed - without renaming all its contents as well. recursiveRename should be - called after renaming the directory, passing in the plaintext from and - to paths. - */ - shared_ptr newRenameOp( const char *from, const char *to ); + /* + when directory IV chaining is enabled, a directory can't be renamed + without renaming all its contents as well. recursiveRename should be + called after renaming the directory, passing in the plaintext from and + to paths. + */ + shared_ptr newRenameOp(const char *from, const char *to); -private: + private: + friend class RenameOp; - friend class RenameOp; + bool genRenameList(std::list &list, const char *fromP, + const char *toP); - bool genRenameList( std::list &list, const char *fromP, - const char *toP ); - - shared_ptr findOrCreate( const char *plainName); + shared_ptr findOrCreate(const char *plainName); - pthread_mutex_t mutex; + pthread_mutex_t mutex; - EncFS_Context *ctx; + EncFS_Context *ctx; - // passed in as configuration - std::string rootDir; - FSConfigPtr fsConfig; + // passed in as configuration + std::string rootDir; + FSConfigPtr fsConfig; - shared_ptr naming; + shared_ptr naming; }; #endif diff --git a/encfs/FSConfig.h b/encfs/FSConfig.h index b8517bd..4d8a60a 100644 --- a/encfs/FSConfig.h +++ b/encfs/FSConfig.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -28,105 +28,98 @@ #include "CipherKey.h" #include "shared_ptr.h" -enum ConfigType -{ - Config_None = 0, - Config_Prehistoric, - Config_V3, - Config_V4, - Config_V5, - Config_V6 +enum ConfigType { + Config_None = 0, + Config_Prehistoric, + Config_V3, + Config_V4, + Config_V5, + Config_V6 }; struct EncFS_Opts; class Cipher; class NameIO; -struct EncFSConfig -{ - ConfigType cfgType; +struct EncFSConfig { + ConfigType cfgType; - std::string creator; - int subVersion; + std::string creator; + int subVersion; - // interface of cipher - rel::Interface cipherIface; - // interface used for file name coding - rel::Interface nameIface; - int keySize; // reported in bits - int blockSize; // reported in bytes + // interface of cipher + rel::Interface cipherIface; + // interface used for file name coding + rel::Interface nameIface; + int keySize; // reported in bits + int blockSize; // reported in bytes - std::vector keyData; + std::vector keyData; - std::vector salt; - int kdfIterations; - long desiredKDFDuration; + std::vector salt; + int kdfIterations; + long desiredKDFDuration; - int blockMACBytes; // MAC headers on blocks.. - int blockMACRandBytes; // number of random bytes in the block header + int blockMACBytes; // MAC headers on blocks.. + int blockMACRandBytes; // number of random bytes in the block header - bool uniqueIV; // per-file Initialization Vector - bool externalIVChaining; // IV seeding by filename IV chaining + bool uniqueIV; // per-file Initialization Vector + bool externalIVChaining; // IV seeding by filename IV chaining - bool chainedNameIV; // filename IV chaining - bool allowHoles; // allow holes in files (implicit zero blocks) + bool chainedNameIV; // filename IV chaining + bool allowHoles; // allow holes in files (implicit zero blocks) - EncFSConfig() - : keyData() - , salt() - { - cfgType = Config_None; - subVersion = 0; - blockMACBytes = 0; - blockMACRandBytes = 0; - uniqueIV = false; - externalIVChaining = false; - chainedNameIV = false; - allowHoles = false; + EncFSConfig() : keyData(), salt() { + cfgType = Config_None; + subVersion = 0; + blockMACBytes = 0; + blockMACRandBytes = 0; + uniqueIV = false; + externalIVChaining = false; + chainedNameIV = false; + allowHoles = false; - kdfIterations = 0; - desiredKDFDuration = 500; - } + kdfIterations = 0; + desiredKDFDuration = 500; + } - CipherKey getUserKey(bool useStdin); - CipherKey getUserKey(const std::string &passwordProgram, - const std::string &rootDir); - CipherKey getNewUserKey(); - - shared_ptr getCipher() const; + CipherKey getUserKey(bool useStdin); + CipherKey getUserKey(const std::string &passwordProgram, + const std::string &rootDir); + CipherKey getNewUserKey(); - // deprecated - void assignKeyData(const std::string &in); - void assignKeyData(unsigned char *data, int length); - void assignSaltData(unsigned char *data, int length); + shared_ptr getCipher() const; - unsigned char *getKeyData() const; - unsigned char *getSaltData() const; + // deprecated + void assignKeyData(const std::string &in); + void assignKeyData(unsigned char *data, int length); + void assignSaltData(unsigned char *data, int length); -private: - CipherKey makeKey(const char *password, int passwdLen); + unsigned char *getKeyData() const; + unsigned char *getSaltData() const; + + private: + CipherKey makeKey(const char *password, int passwdLen); }; - + // helpers for serializing to/from a stream -std::ostream &operator << (std::ostream &os, const EncFSConfig &cfg); -std::istream &operator >> (std::istream &os, EncFSConfig &cfg); +std::ostream &operator<<(std::ostream &os, const EncFSConfig &cfg); +std::istream &operator>>(std::istream &os, EncFSConfig &cfg); -struct FSConfig -{ - shared_ptr config; - shared_ptr opts; +struct FSConfig { + shared_ptr config; + shared_ptr opts; - shared_ptr cipher; - CipherKey key; - shared_ptr nameCoding; + shared_ptr cipher; + CipherKey key; + shared_ptr nameCoding; - bool forceDecode; // force decode on MAC block failures - bool reverseEncryption; // reverse encryption operation + bool forceDecode; // force decode on MAC block failures + bool reverseEncryption; // reverse encryption operation - bool idleTracking; // turn on idle monitoring of filesystem + bool idleTracking; // turn on idle monitoring of filesystem }; typedef shared_ptr FSConfigPtr; #endif - diff --git a/encfs/FileIO.cpp b/encfs/FileIO.cpp index 3bd226c..1eb6bae 100644 --- a/encfs/FileIO.cpp +++ b/encfs/FileIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -20,22 +20,13 @@ #include "FileIO.h" -FileIO::FileIO() -{ -} +FileIO::FileIO() {} -FileIO::~FileIO() -{ -} +FileIO::~FileIO() {} -int FileIO::blockSize() const -{ - return 1; -} +int FileIO::blockSize() const { return 1; } -bool FileIO::setIV( uint64_t iv ) -{ - (void)iv; - return true; +bool FileIO::setIV(uint64_t iv) { + (void)iv; + return true; } - diff --git a/encfs/FileIO.h b/encfs/FileIO.h index 36f8187..602b02a 100644 --- a/encfs/FileIO.h +++ b/encfs/FileIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -27,61 +27,54 @@ #include "Interface.h" -struct IORequest -{ - off_t offset; +struct IORequest { + off_t offset; - // amount of bytes to read/write. - int dataLen; - unsigned char *data; + // amount of bytes to read/write. + int dataLen; + unsigned char *data; - IORequest(); + IORequest(); }; -inline IORequest::IORequest() - : offset(0) - , dataLen(0) - , data(0) -{ -} +inline IORequest::IORequest() : offset(0), dataLen(0), data(0) {} -class FileIO -{ -public: - FileIO(); - virtual ~FileIO(); +class FileIO { + public: + FileIO(); + virtual ~FileIO(); - virtual rel::Interface interface() const =0; + virtual rel::Interface interface() const = 0; - // default implementation returns 1, meaning this is not block oriented. - virtual int blockSize() const; + // default implementation returns 1, meaning this is not block oriented. + virtual int blockSize() const; - virtual void setFileName(const char *fileName) =0; - virtual const char *getFileName() const =0; + virtual void setFileName(const char *fileName) = 0; + virtual const char *getFileName() const = 0; - // Not sure about this -- it is specific to CipherFileIO, but the - // alternative methods of exposing this interface aren't much nicer.. - virtual bool setIV( uint64_t iv ); + // Not sure about this -- it is specific to CipherFileIO, but the + // alternative methods of exposing this interface aren't much nicer.. + virtual bool setIV(uint64_t iv); - // open file for specified mode. There is no corresponding close, so a - // file is open until the FileIO interface is destroyed. - virtual int open( int flags ) =0; - - // get filesystem attributes for a file - virtual int getAttr( struct stat *stbuf ) const =0; - virtual off_t getSize( ) const =0; + // open file for specified mode. There is no corresponding close, so a + // file is open until the FileIO interface is destroyed. + virtual int open(int flags) = 0; - virtual ssize_t read( const IORequest &req ) const =0; - virtual bool write( const IORequest &req ) =0; + // get filesystem attributes for a file + virtual int getAttr(struct stat *stbuf) const = 0; + virtual off_t getSize() const = 0; - virtual int truncate( off_t size ) =0; + virtual ssize_t read(const IORequest &req) const = 0; + virtual bool write(const IORequest &req) = 0; - virtual bool isWritable() const =0; -private: - // not implemented.. - FileIO( const FileIO & ); - FileIO &operator = ( const FileIO & ); + virtual int truncate(off_t size) = 0; + + virtual bool isWritable() const = 0; + + private: + // not implemented.. + FileIO(const FileIO &); + FileIO &operator=(const FileIO &); }; #endif - diff --git a/encfs/FileNode.cpp b/encfs/FileNode.cpp index 4d898ef..d1abfb9 100644 --- a/encfs/FileNode.cpp +++ b/encfs/FileNode.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -66,242 +66,202 @@ using namespace rlog; static RLogChannel *Info = DEF_CHANNEL("info/FileNode", Log_Info); FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg, - const char *plaintextName_, const char *cipherName_) -{ - pthread_mutex_init( &mutex, 0 ); - - Lock _lock( mutex ); + const char *plaintextName_, const char *cipherName_) { + pthread_mutex_init(&mutex, 0); - this->_pname = plaintextName_; - this->_cname = cipherName_; - this->parent = parent_; + Lock _lock(mutex); - this->fsConfig = cfg; + this->_pname = plaintextName_; + this->_cname = cipherName_; + this->parent = parent_; - // chain RawFileIO & CipherFileIO - shared_ptr rawIO( new RawFileIO( _cname ) ); - io = shared_ptr( new CipherFileIO( rawIO, fsConfig )); + this->fsConfig = cfg; - if(cfg->config->blockMACBytes || cfg->config->blockMACRandBytes) - io = shared_ptr(new MACFileIO(io, fsConfig)); + // chain RawFileIO & CipherFileIO + shared_ptr rawIO(new RawFileIO(_cname)); + io = shared_ptr(new CipherFileIO(rawIO, fsConfig)); + + if (cfg->config->blockMACBytes || cfg->config->blockMACRandBytes) + io = shared_ptr(new MACFileIO(io, fsConfig)); } -FileNode::~FileNode() -{ - // FileNode mutex should be locked before the destructor is called - //pthread_mutex_lock( &mutex ); +FileNode::~FileNode() { + // FileNode mutex should be locked before the destructor is called + // pthread_mutex_lock( &mutex ); - _pname.assign( _pname.length(), '\0' ); - _cname.assign( _cname.length(), '\0' ); - io.reset(); + _pname.assign(_pname.length(), '\0'); + _cname.assign(_cname.length(), '\0'); + io.reset(); - pthread_mutex_destroy( &mutex ); + pthread_mutex_destroy(&mutex); } -const char *FileNode::cipherName() const -{ - return _cname.c_str(); -} +const char *FileNode::cipherName() const { return _cname.c_str(); } -const char *FileNode::plaintextName() const -{ - return _pname.c_str(); -} +const char *FileNode::plaintextName() const { return _pname.c_str(); } -string FileNode::plaintextParent() const -{ - return parentDirectory( _pname ); -} - -static bool setIV(const shared_ptr &io, uint64_t iv) -{ - struct stat stbuf; - if((io->getAttr(&stbuf) < 0) || S_ISREG(stbuf.st_mode)) - return io->setIV( iv ); - else - return true; -} - -bool FileNode::setName( const char *plaintextName_, const char *cipherName_, - uint64_t iv, bool setIVFirst ) -{ - //Lock _lock( mutex ); - rDebug("calling setIV on %s", cipherName_); - if(setIVFirst) - { - if(fsConfig->config->externalIVChaining && !setIV(io, iv)) - return false; - - // now change the name.. - if(plaintextName_) - this->_pname = plaintextName_; - if(cipherName_) - { - this->_cname = cipherName_; - io->setFileName( cipherName_ ); - } - } else - { - std::string oldPName = _pname; - std::string oldCName = _cname; - - if(plaintextName_) - this->_pname = plaintextName_; - if(cipherName_) - { - this->_cname = cipherName_; - io->setFileName( cipherName_ ); - } - - if(fsConfig->config->externalIVChaining && !setIV(io, iv)) - { - _pname = oldPName; - _cname = oldCName; - return false; - } - } +string FileNode::plaintextParent() const { return parentDirectory(_pname); } +static bool setIV(const shared_ptr &io, uint64_t iv) { + struct stat stbuf; + if ((io->getAttr(&stbuf) < 0) || S_ISREG(stbuf.st_mode)) + return io->setIV(iv); + else return true; } -int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) -{ - Lock _lock( mutex ); +bool FileNode::setName(const char *plaintextName_, const char *cipherName_, + uint64_t iv, bool setIVFirst) { + // Lock _lock( mutex ); + rDebug("calling setIV on %s", cipherName_); + if (setIVFirst) { + if (fsConfig->config->externalIVChaining && !setIV(io, iv)) return false; - int res; - int olduid = -1; - int oldgid = -1; - if(uid != 0) - { - olduid = setfsuid( uid ); - if(olduid == -1) - { - rInfo("setfsuid error: %s", strerror(errno)); - return -EPERM; - } + // now change the name.. + if (plaintextName_) this->_pname = plaintextName_; + if (cipherName_) { + this->_cname = cipherName_; + io->setFileName(cipherName_); } - if(gid != 0) - { - oldgid = setfsgid( gid ); - if(oldgid == -1) - { - rInfo("setfsgid error: %s", strerror(errno)); - return -EPERM; - } + } else { + std::string oldPName = _pname; + std::string oldCName = _cname; + + if (plaintextName_) this->_pname = plaintextName_; + if (cipherName_) { + this->_cname = cipherName_; + io->setFileName(cipherName_); } - /* - * cf. xmp_mknod() in fusexmp.c - * The regular file stuff could be stripped off if there - * were a create method (advised to have) - */ - if (S_ISREG( mode )) { - res = ::open( _cname.c_str(), O_CREAT | O_EXCL | O_WRONLY, mode ); - if (res >= 0) - res = ::close( res ); - } else if (S_ISFIFO( mode )) - res = ::mkfifo( _cname.c_str(), mode ); - else - res = ::mknod( _cname.c_str(), mode, rdev ); - - if(olduid >= 0) - setfsuid( olduid ); - if(oldgid >= 0) - setfsgid( oldgid ); - - if(res == -1) - { - int eno = errno; - rDebug("mknod error: %s", strerror(eno)); - res = -eno; + if (fsConfig->config->externalIVChaining && !setIV(io, iv)) { + _pname = oldPName; + _cname = oldCName; + return false; } + } - return res; + return true; } -int FileNode::open(int flags) const -{ - Lock _lock( mutex ); +int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid) { + Lock _lock(mutex); - int res = io->open( flags ); - return res; + int res; + int olduid = -1; + int oldgid = -1; + if (uid != 0) { + olduid = setfsuid(uid); + if (olduid == -1) { + rInfo("setfsuid error: %s", strerror(errno)); + return -EPERM; + } + } + if (gid != 0) { + oldgid = setfsgid(gid); + if (oldgid == -1) { + rInfo("setfsgid error: %s", strerror(errno)); + return -EPERM; + } + } + + /* + * cf. xmp_mknod() in fusexmp.c + * The regular file stuff could be stripped off if there + * were a create method (advised to have) + */ + if (S_ISREG(mode)) { + res = ::open(_cname.c_str(), O_CREAT | O_EXCL | O_WRONLY, mode); + if (res >= 0) res = ::close(res); + } else if (S_ISFIFO(mode)) + res = ::mkfifo(_cname.c_str(), mode); + else + res = ::mknod(_cname.c_str(), mode, rdev); + + if (olduid >= 0) setfsuid(olduid); + if (oldgid >= 0) setfsgid(oldgid); + + if (res == -1) { + int eno = errno; + rDebug("mknod error: %s", strerror(eno)); + res = -eno; + } + + return res; } -int FileNode::getAttr(struct stat *stbuf) const -{ - Lock _lock( mutex ); +int FileNode::open(int flags) const { + Lock _lock(mutex); - int res = io->getAttr( stbuf ); - return res; + int res = io->open(flags); + return res; } -off_t FileNode::getSize() const -{ - Lock _lock( mutex ); +int FileNode::getAttr(struct stat *stbuf) const { + Lock _lock(mutex); - int res = io->getSize(); - return res; + int res = io->getAttr(stbuf); + return res; } -ssize_t FileNode::read( off_t offset, unsigned char *data, ssize_t size ) const -{ - IORequest req; - req.offset = offset; - req.dataLen = size; - req.data = data; +off_t FileNode::getSize() const { + Lock _lock(mutex); - Lock _lock( mutex ); - - return io->read( req ); + int res = io->getSize(); + return res; } -bool FileNode::write(off_t offset, unsigned char *data, ssize_t size) -{ - rLog(Info, "FileNode::write offset %" PRIi64 ", data size %i", - offset, (int)size); +ssize_t FileNode::read(off_t offset, unsigned char *data, ssize_t size) const { + IORequest req; + req.offset = offset; + req.dataLen = size; + req.data = data; - IORequest req; - req.offset = offset; - req.dataLen = size; - req.data = data; - - Lock _lock( mutex ); + Lock _lock(mutex); - return io->write( req ); + return io->read(req); } -int FileNode::truncate( off_t size ) -{ - Lock _lock( mutex ); +bool FileNode::write(off_t offset, unsigned char *data, ssize_t size) { + rLog(Info, "FileNode::write offset %" PRIi64 ", data size %i", offset, + (int)size); - return io->truncate( size ); + IORequest req; + req.offset = offset; + req.dataLen = size; + req.data = data; + + Lock _lock(mutex); + + return io->write(req); } -int FileNode::sync(bool datasync) -{ - Lock _lock( mutex ); +int FileNode::truncate(off_t size) { + Lock _lock(mutex); - int fh = io->open( O_RDONLY ); - if(fh >= 0) - { - int res = -EIO; + return io->truncate(size); +} + +int FileNode::sync(bool datasync) { + Lock _lock(mutex); + + int fh = io->open(O_RDONLY); + if (fh >= 0) { + int res = -EIO; #ifdef linux - if(datasync) - res = fdatasync( fh ); - else - res = fsync( fh ); + if (datasync) + res = fdatasync(fh); + else + res = fsync(fh); #else - (void)datasync; - // no fdatasync support - // TODO: use autoconfig to check for it.. - res = fsync(fh); + (void)datasync; + // no fdatasync support + // TODO: use autoconfig to check for it.. + res = fsync(fh); #endif - - if(res == -1) - res = -errno; - return res; - } else - return fh; + if (res == -1) res = -errno; + + return res; + } else + return fh; } - diff --git a/encfs/FileNode.h b/encfs/FileNode.h index 0037643..4fa5acd 100644 --- a/encfs/FileNode.h +++ b/encfs/FileNode.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - + #ifndef _FileNode_incl_ #define _FileNode_incl_ @@ -33,68 +33,62 @@ class Cipher; class FileIO; class DirNode; -class FileNode -{ -public: - FileNode(DirNode *parent, - const FSConfigPtr &cfg, - const char *plaintextName, - const char *cipherName); - ~FileNode(); +class FileNode { + public: + FileNode(DirNode *parent, const FSConfigPtr &cfg, const char *plaintextName, + const char *cipherName); + ~FileNode(); - const char *plaintextName() const; - const char *cipherName() const; + const char *plaintextName() const; + const char *cipherName() const; - // directory portion of plaintextName - std::string plaintextParent() const; + // directory portion of plaintextName + std::string plaintextParent() const; - // if setIVFirst is true, then the IV is changed before the name is changed - // (default). The reverse is also supported for special cases.. - bool setName( const char *plaintextName, const char *cipherName, - uint64_t iv, bool setIVFirst = true); + // if setIVFirst is true, then the IV is changed before the name is changed + // (default). The reverse is also supported for special cases.. + bool setName(const char *plaintextName, const char *cipherName, uint64_t iv, + bool setIVFirst = true); - // create node - // If uid/gid are not 0, then chown is used change ownership as specified - int mknod(mode_t mode, dev_t rdev, uid_t uid = 0, gid_t gid = 0); + // create node + // If uid/gid are not 0, then chown is used change ownership as specified + int mknod(mode_t mode, dev_t rdev, uid_t uid = 0, gid_t gid = 0); - // Returns < 0 on error (-errno), file descriptor on success. - int open(int flags) const; + // Returns < 0 on error (-errno), file descriptor on success. + int open(int flags) const; - // getAttr returns 0 on success, -errno on failure - int getAttr(struct stat *stbuf) const; - off_t getSize() const; + // getAttr returns 0 on success, -errno on failure + int getAttr(struct stat *stbuf) const; + off_t getSize() const; - ssize_t read(off_t offset, unsigned char *data, ssize_t size) const; - bool write(off_t offset, unsigned char *data, ssize_t size); + ssize_t read(off_t offset, unsigned char *data, ssize_t size) const; + bool write(off_t offset, unsigned char *data, ssize_t size); - // truncate the file to a particular size - int truncate( off_t size ); + // truncate the file to a particular size + int truncate(off_t size); - // datasync or full sync - int sync(bool dataSync); -private: + // datasync or full sync + int sync(bool dataSync); - // doing locking at the FileNode level isn't as efficient as at the - // lowest level of RawFileIO, since that means locks are held longer - // (held during CPU intensive crypto operations!). However it makes it - // easier to avoid any race conditions with operations such as - // truncate() which may result in multiple calls down to the FileIO - // level. - mutable pthread_mutex_t mutex; + private: + // doing locking at the FileNode level isn't as efficient as at the + // lowest level of RawFileIO, since that means locks are held longer + // (held during CPU intensive crypto operations!). However it makes it + // easier to avoid any race conditions with operations such as + // truncate() which may result in multiple calls down to the FileIO + // level. + mutable pthread_mutex_t mutex; - FSConfigPtr fsConfig; + FSConfigPtr fsConfig; - shared_ptr io; - std::string _pname; // plaintext name - std::string _cname; // encrypted name - DirNode *parent; - -private: - FileNode(const FileNode &src); - FileNode &operator = (const FileNode &src); + shared_ptr io; + std::string _pname; // plaintext name + std::string _cname; // encrypted name + DirNode *parent; + private: + FileNode(const FileNode &src); + FileNode &operator=(const FileNode &src); }; - #endif - diff --git a/encfs/FileUtils.cpp b/encfs/FileUtils.cpp index 74580f5..23e8bd2 100644 --- a/encfs/FileUtils.cpp +++ b/encfs/FileUtils.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -20,9 +20,9 @@ // defines needed for RedHat 7.3... #ifdef linux -#define _XOPEN_SOURCE 500 // make sure pwrite() is pulled in +#define _XOPEN_SOURCE 500 // make sure pwrite() is pulled in #endif -#define _BSD_SOURCE // pick up setenv on RH7.3 +#define _BSD_SOURCE // pick up setenv on RH7.3 #include "encfs.h" #include "config.h" @@ -84,8 +84,8 @@ static const int DefaultBlockSize = 1024; // use the extpass option, as extpass can return arbitrary length binary data. static const int MaxPassBuf = 512; -static const int NormalKDFDuration = 500; // 1/2 a second -static const int ParanoiaKDFDuration = 3000; // 3 seconds +static const int NormalKDFDuration = 500; // 1/2 a second +static const int ParanoiaKDFDuration = 3000; // 3 seconds // environment variable names for values encfs stores in the environment when // calling an external password program. @@ -93,1650 +93,1436 @@ static const char ENCFS_ENV_ROOTDIR[] = "encfs_root"; static const char ENCFS_ENV_STDOUT[] = "encfs_stdout"; static const char ENCFS_ENV_STDERR[] = "encfs_stderr"; - -//static int V5SubVersion = 20040518; -//static int V5SubVersion = 20040621; // add external IV chaining -static int V5SubVersion = 20040813; // fix MACFileIO block size issues +// static int V5SubVersion = 20040518; +// static int V5SubVersion = 20040621; // add external IV chaining +static int V5SubVersion = 20040813; // fix MACFileIO block size issues static int V5SubVersionDefault = 0; // 20080813 was really made on 20080413 -- typo on date.. -//const int V6SubVersion = 20080813; // switch to v6/XML, add allowHoles option -//const int V6SubVersion = 20080816; // add salt and iteration count -const int V6SubVersion = 20100713; // add version field for boost 1.42+ +// const int V6SubVersion = 20080813; // switch to v6/XML, add allowHoles option +// const int V6SubVersion = 20080816; // add salt and iteration count +const int V6SubVersion = 20100713; // add version field for boost 1.42+ -struct ConfigInfo -{ - const char *fileName; - ConfigType type; - const char *environmentOverride; - bool (*loadFunc)(const char *fileName, - const shared_ptr &config, - ConfigInfo *cfg); - bool (*saveFunc)(const char *fileName, - const shared_ptr &config); - int currentSubVersion; - int defaultSubVersion; +struct ConfigInfo { + const char *fileName; + ConfigType type; + const char *environmentOverride; + bool (*loadFunc)(const char *fileName, const shared_ptr &config, + ConfigInfo *cfg); + bool (*saveFunc)(const char *fileName, const shared_ptr &config); + int currentSubVersion; + int defaultSubVersion; } ConfigFileMapping[] = { - {".encfs6.xml", Config_V6, "ENCFS6_CONFIG", readV6Config, writeV6Config, - V6SubVersion, 0 }, - // backward compatible support for older versions - {".encfs5", Config_V5, "ENCFS5_CONFIG", readV5Config, writeV5Config, - V5SubVersion, V5SubVersionDefault }, - {".encfs4", Config_V4, NULL, readV4Config, writeV4Config, 0, 0 }, - // no longer support earlier versions - {".encfs3", Config_V3, NULL, NULL, NULL, 0, 0 }, - {".encfs2", Config_Prehistoric, NULL, NULL, NULL, 0, 0 }, - {".encfs", Config_Prehistoric, NULL, NULL, NULL, 0, 0 }, - {NULL,Config_None, NULL, NULL, NULL, 0, 0}}; - + {".encfs6.xml", Config_V6, "ENCFS6_CONFIG", readV6Config, writeV6Config, + V6SubVersion, 0}, + // backward compatible support for older versions + {".encfs5", Config_V5, "ENCFS5_CONFIG", readV5Config, writeV5Config, + V5SubVersion, V5SubVersionDefault}, + {".encfs4", Config_V4, NULL, readV4Config, writeV4Config, 0, 0}, + // no longer support earlier versions + {".encfs3", Config_V3, NULL, NULL, NULL, 0, 0}, + {".encfs2", Config_Prehistoric, NULL, NULL, NULL, 0, 0}, + {".encfs", Config_Prehistoric, NULL, NULL, NULL, 0, 0}, + {NULL, Config_None, NULL, NULL, NULL, 0, 0}}; #include "boost-versioning.h" // define serialization helpers -namespace boost -{ - namespace serialization - { - template - void save(Archive &ar, const EncFSConfig &cfg, - unsigned int version) - { - (void)version; - // version 20 (aka 20100613) - if (cfg.subVersion == 0) - ar << make_nvp("version", V6SubVersion); - else - ar << make_nvp("version", cfg.subVersion); +namespace boost { +namespace serialization { +template +void save(Archive &ar, const EncFSConfig &cfg, unsigned int version) { + (void)version; + // version 20 (aka 20100613) + if (cfg.subVersion == 0) + ar << make_nvp("version", V6SubVersion); + else + ar << make_nvp("version", cfg.subVersion); - ar << make_nvp("creator", cfg.creator); - ar << make_nvp("cipherAlg", cfg.cipherIface); - ar << make_nvp("nameAlg", cfg.nameIface); - ar << make_nvp("keySize", cfg.keySize); - ar << make_nvp("blockSize", cfg.blockSize); - ar << make_nvp("uniqueIV", cfg.uniqueIV); - ar << make_nvp("chainedNameIV", cfg.chainedNameIV); - ar << make_nvp("externalIVChaining", cfg.externalIVChaining); - ar << make_nvp("blockMACBytes", cfg.blockMACBytes); - ar << make_nvp("blockMACRandBytes", cfg.blockMACRandBytes); - ar << make_nvp("allowHoles", cfg.allowHoles); + ar << make_nvp("creator", cfg.creator); + ar << make_nvp("cipherAlg", cfg.cipherIface); + ar << make_nvp("nameAlg", cfg.nameIface); + ar << make_nvp("keySize", cfg.keySize); + ar << make_nvp("blockSize", cfg.blockSize); + ar << make_nvp("uniqueIV", cfg.uniqueIV); + ar << make_nvp("chainedNameIV", cfg.chainedNameIV); + ar << make_nvp("externalIVChaining", cfg.externalIVChaining); + ar << make_nvp("blockMACBytes", cfg.blockMACBytes); + ar << make_nvp("blockMACRandBytes", cfg.blockMACRandBytes); + ar << make_nvp("allowHoles", cfg.allowHoles); - int encodedSize = cfg.keyData.size(); - ar << make_nvp("encodedKeySize", encodedSize); - ar << make_nvp("encodedKeyData", - serial::make_binary_object(cfg.getKeyData(), encodedSize)); + int encodedSize = cfg.keyData.size(); + ar << make_nvp("encodedKeySize", encodedSize); + ar << make_nvp("encodedKeyData", + serial::make_binary_object(cfg.getKeyData(), encodedSize)); - // version 20080816 - int size = cfg.salt.size(); - ar << make_nvp("saltLen", size); - ar << make_nvp("saltData", - serial::make_binary_object(cfg.getSaltData(), size)); - ar << make_nvp("kdfIterations", cfg.kdfIterations); - ar << make_nvp("desiredKDFDuration", cfg.desiredKDFDuration); - } - - template - void load(Archive &ar, EncFSConfig &cfg, unsigned int version) - { - rInfo("version = %i", version); - // TODO: figure out how to deprecate all but the first case.. - if (version == 20 || version >= 20100713) - { - rInfo("found new serialization format"); - ar >> make_nvp("version", cfg.subVersion); - } else if (version == 26800) - { - rInfo("found 20080816 version"); - cfg.subVersion = 20080816; - } else if (version == 26797) - { - rInfo("found 20080813"); - cfg.subVersion = 20080813; - } else if (version < (unsigned int)V5SubVersion) - { - rError("Invalid version %i - please fix config file", version); - } else - { - rInfo("Boost <= 1.41 compatibility mode"); - cfg.subVersion = version; - } - rInfo("subVersion = %i", cfg.subVersion); - - ar >> make_nvp("creator", cfg.creator); - ar >> make_nvp("cipherAlg", cfg.cipherIface); - ar >> make_nvp("nameAlg", cfg.nameIface); - ar >> make_nvp("keySize", cfg.keySize); - ar >> make_nvp("blockSize", cfg.blockSize); - ar >> make_nvp("uniqueIV", cfg.uniqueIV); - ar >> make_nvp("chainedNameIV", cfg.chainedNameIV); - ar >> make_nvp("externalIVChaining", cfg.externalIVChaining); - ar >> make_nvp("blockMACBytes", cfg.blockMACBytes); - ar >> make_nvp("blockMACRandBytes", cfg.blockMACRandBytes); - ar >> make_nvp("allowHoles", cfg.allowHoles); - - int encodedSize; - ar >> make_nvp("encodedKeySize", encodedSize); - rAssert(encodedSize == cfg.getCipher()->encodedKeySize()); - - unsigned char *key = new unsigned char[encodedSize]; - ar >> make_nvp("encodedKeyData", - serial::make_binary_object(key, encodedSize)); - cfg.assignKeyData(key, encodedSize); - delete[] key; - - if(cfg.subVersion >= 20080816) - { - int saltLen; - ar >> make_nvp("saltLen", saltLen); - unsigned char *salt = new unsigned char[saltLen]; - ar >> make_nvp("saltData", - serial::make_binary_object(salt, saltLen)); - cfg.assignSaltData(salt, saltLen); - delete[] salt; - - ar >> make_nvp("kdfIterations", cfg.kdfIterations); - ar >> make_nvp("desiredKDFDuration", cfg.desiredKDFDuration); - } else - { - cfg.salt.clear(); - cfg.kdfIterations = 16; - cfg.desiredKDFDuration = NormalKDFDuration; - } - } - - template - void serialize(Archive &ar, EncFSConfig &cfg, unsigned int version) - { - split_free(ar, cfg, version); - } - - template - void serialize(Archive &ar, Interface &i, const unsigned int version) - { - (void)version; - ar & make_nvp("name", i.name()); - ar & make_nvp("major", i.current()); - ar & make_nvp("minor", i.revision()); - } - } + // version 20080816 + int size = cfg.salt.size(); + ar << make_nvp("saltLen", size); + ar << make_nvp("saltData", + serial::make_binary_object(cfg.getSaltData(), size)); + ar << make_nvp("kdfIterations", cfg.kdfIterations); + ar << make_nvp("desiredKDFDuration", cfg.desiredKDFDuration); } -EncFS_Root::EncFS_Root() -{ +template +void load(Archive &ar, EncFSConfig &cfg, unsigned int version) { + rInfo("version = %i", version); + // TODO: figure out how to deprecate all but the first case.. + if (version == 20 || version >= 20100713) { + rInfo("found new serialization format"); + ar >> make_nvp("version", cfg.subVersion); + } else if (version == 26800) { + rInfo("found 20080816 version"); + cfg.subVersion = 20080816; + } else if (version == 26797) { + rInfo("found 20080813"); + cfg.subVersion = 20080813; + } else if (version < (unsigned int)V5SubVersion) { + rError("Invalid version %i - please fix config file", version); + } else { + rInfo("Boost <= 1.41 compatibility mode"); + cfg.subVersion = version; + } + rInfo("subVersion = %i", cfg.subVersion); + + ar >> make_nvp("creator", cfg.creator); + ar >> make_nvp("cipherAlg", cfg.cipherIface); + ar >> make_nvp("nameAlg", cfg.nameIface); + ar >> make_nvp("keySize", cfg.keySize); + ar >> make_nvp("blockSize", cfg.blockSize); + ar >> make_nvp("uniqueIV", cfg.uniqueIV); + ar >> make_nvp("chainedNameIV", cfg.chainedNameIV); + ar >> make_nvp("externalIVChaining", cfg.externalIVChaining); + ar >> make_nvp("blockMACBytes", cfg.blockMACBytes); + ar >> make_nvp("blockMACRandBytes", cfg.blockMACRandBytes); + ar >> make_nvp("allowHoles", cfg.allowHoles); + + int encodedSize; + ar >> make_nvp("encodedKeySize", encodedSize); + rAssert(encodedSize == cfg.getCipher()->encodedKeySize()); + + unsigned char *key = new unsigned char[encodedSize]; + ar >> + make_nvp("encodedKeyData", serial::make_binary_object(key, encodedSize)); + cfg.assignKeyData(key, encodedSize); + delete[] key; + + if (cfg.subVersion >= 20080816) { + int saltLen; + ar >> make_nvp("saltLen", saltLen); + unsigned char *salt = new unsigned char[saltLen]; + ar >> make_nvp("saltData", serial::make_binary_object(salt, saltLen)); + cfg.assignSaltData(salt, saltLen); + delete[] salt; + + ar >> make_nvp("kdfIterations", cfg.kdfIterations); + ar >> make_nvp("desiredKDFDuration", cfg.desiredKDFDuration); + } else { + cfg.salt.clear(); + cfg.kdfIterations = 16; + cfg.desiredKDFDuration = NormalKDFDuration; + } } -EncFS_Root::~EncFS_Root() -{ +template +void serialize(Archive &ar, EncFSConfig &cfg, unsigned int version) { + split_free(ar, cfg, version); } +template +void serialize(Archive &ar, Interface &i, const unsigned int version) { + (void)version; + ar &make_nvp("name", i.name()); + ar &make_nvp("major", i.current()); + ar &make_nvp("minor", i.revision()); +} +} +} -bool fileExists( const char * fileName ) -{ - struct stat buf; - if(!lstat( fileName, &buf )) - { - return true; +EncFS_Root::EncFS_Root() {} + +EncFS_Root::~EncFS_Root() {} + +bool fileExists(const char *fileName) { + struct stat buf; + if (!lstat(fileName, &buf)) { + return true; + } else { + // XXX show perror? + return false; + } +} + +bool isDirectory(const char *fileName) { + struct stat buf; + if (!lstat(fileName, &buf)) { + return S_ISDIR(buf.st_mode); + } else { + return false; + } +} + +bool isAbsolutePath(const char *fileName) { + if (fileName && fileName[0] != '\0' && fileName[0] == '/') + return true; + else + return false; +} + +const char *lastPathElement(const char *name) { + const char *loc = strrchr(name, '/'); + return loc ? loc + 1 : name; +} + +std::string parentDirectory(const std::string &path) { + size_t last = path.find_last_of('/'); + if (last == string::npos) + return string(""); + else + return path.substr(0, last); +} + +bool userAllowMkdir(const char *path, mode_t mode) { + return userAllowMkdir(0, path, mode); +} + +bool userAllowMkdir(int promptno, const char *path, mode_t mode) { + // TODO: can we internationalize the y/n names? Seems strange to prompt in + // their own language but then have to respond 'y' or 'n'. + // xgroup(setup) + cerr << format( + _("The directory \"%s\" does not exist. Should it be created? " + "(y,n) ")) % + path; + char answer[10]; + char *res; + + switch (promptno) { + case 1: + cerr << endl << "$PROMPT$ create_root_dir" << endl; + break; + case 2: + cerr << endl << "$PROMPT$ create_mount_point" << endl; + break; + default: + break; + } + res = fgets(answer, sizeof(answer), stdin); + + if (res != 0 && toupper(answer[0]) == 'Y') { + int result = mkdir(path, mode); + if (result < 0) { + perror(_("Unable to create directory: ")); + return false; } else - { - // XXX show perror? - return false; - } + return true; + } else { + // Directory not created, by user request + cerr << _("Directory not created.") << "\n"; + return false; + } } -bool isDirectory( const char *fileName ) -{ - struct stat buf; - if( !lstat( fileName, &buf )) - { - return S_ISDIR( buf.st_mode ); - } else - { - return false; - } -} - -bool isAbsolutePath( const char *fileName ) -{ - if(fileName && fileName[0] != '\0' && fileName[0] == '/') - return true; - else - return false; -} - -const char *lastPathElement( const char *name ) -{ - const char *loc = strrchr( name, '/' ); - return loc ? loc + 1 : name; -} - -std::string parentDirectory( const std::string &path ) -{ - size_t last = path.find_last_of( '/' ); - if(last == string::npos) - return string(""); - else - return path.substr(0, last); -} - -bool userAllowMkdir(const char *path, mode_t mode ) -{ - return userAllowMkdir(0, path, mode); -} - -bool userAllowMkdir(int promptno, const char *path, mode_t mode ) -{ - // TODO: can we internationalize the y/n names? Seems strange to prompt in - // their own language but then have to respond 'y' or 'n'. - // xgroup(setup) - cerr << format(_("The directory \"%s\" does not exist. Should it be created? (y,n) ")) % path; - char answer[10]; - char *res; - - switch (promptno) - { - case 1: - cerr << endl << "$PROMPT$ create_root_dir" << endl; - break; - case 2: - cerr << endl << "$PROMPT$ create_mount_point" << endl; - break; - default: - break; - } - res = fgets( answer, sizeof(answer), stdin ); - - if(res != 0 && toupper(answer[0]) == 'Y') - { - int result = mkdir( path, mode ); - if(result < 0) - { - perror( _("Unable to create directory: ") ); - return false; - } else - return true; - } else - { - // Directory not created, by user request - cerr << _("Directory not created.") << "\n"; - return false; - } -} - -ConfigType readConfig_load( ConfigInfo *nm, const char *path, - const shared_ptr &config ) -{ - if( nm->loadFunc ) - { - try - { - if( (*nm->loadFunc)( path, config, nm )) - { - config->cfgType = nm->type; - return nm->type; - } - } catch( rlog::Error & err ) - { - err.log( _RLWarningChannel ); - } - - rError( _("Found config file %s, but failed to load"), path); - return Config_None; - } else - { - // No load function - must be an unsupported type.. +ConfigType readConfig_load(ConfigInfo *nm, const char *path, + const shared_ptr &config) { + if (nm->loadFunc) { + try { + if ((*nm->loadFunc)(path, config, nm)) { config->cfgType = nm->type; - return nm->type; + return nm->type; + } } -} - -ConfigType readConfig( const string &rootDir, - const shared_ptr &config ) -{ - ConfigInfo *nm = ConfigFileMapping; - while(nm->fileName) - { - // allow environment variable to override default config path - if( nm->environmentOverride != NULL ) - { - char *envFile = getenv( nm->environmentOverride ); - if( envFile != NULL ) - return readConfig_load( nm, envFile, config ); - } - // the standard place to look is in the root directory - string path = rootDir + nm->fileName; - if( fileExists( path.c_str() ) ) - return readConfig_load( nm, path.c_str(), config); - - ++nm; + catch (rlog::Error &err) { + err.log(_RLWarningChannel); } + rError(_("Found config file %s, but failed to load"), path); return Config_None; + } else { + // No load function - must be an unsupported type.. + config->cfgType = nm->type; + return nm->type; + } } -bool readV6Config( const char *configFile, - const shared_ptr &config, - ConfigInfo *info) -{ - (void)info; +ConfigType readConfig(const string &rootDir, + const shared_ptr &config) { + ConfigInfo *nm = ConfigFileMapping; + while (nm->fileName) { + // allow environment variable to override default config path + if (nm->environmentOverride != NULL) { + char *envFile = getenv(nm->environmentOverride); + if (envFile != NULL) return readConfig_load(nm, envFile, config); + } + // the standard place to look is in the root directory + string path = rootDir + nm->fileName; + if (fileExists(path.c_str())) + return readConfig_load(nm, path.c_str(), config); - fs::ifstream st( configFile ); - if(st.is_open()) - { - try - { - boost::archive::xml_iarchive ia( st ); - ia >> BOOST_SERIALIZATION_NVP( *config ); - - return true; - } catch(boost::archive::archive_exception &e) - { - rError("Archive exception: %s", e.what()); - return false; - } - } else - { - rInfo("Failed to load config file %s", configFile); + ++nm; + } + + return Config_None; +} + +bool readV6Config(const char *configFile, const shared_ptr &config, + ConfigInfo *info) { + (void)info; + + fs::ifstream st(configFile); + if (st.is_open()) { + try { + boost::archive::xml_iarchive ia(st); + ia >> BOOST_SERIALIZATION_NVP(*config); + + return true; + } + catch (boost::archive::archive_exception &e) { + rError("Archive exception: %s", e.what()); + return false; + } + } else { + rInfo("Failed to load config file %s", configFile); + return false; + } +} + +bool readV5Config(const char *configFile, const shared_ptr &config, + ConfigInfo *info) { + bool ok = false; + + // use Config to parse the file and query it.. + ConfigReader cfgRdr; + if (cfgRdr.load(configFile)) { + try { + config->subVersion = + cfgRdr["subVersion"].readInt(info->defaultSubVersion); + if (config->subVersion > info->currentSubVersion) { + /* config file specifies a version outside our supported + range.. */ + rWarning(_("Config subversion %i found, but this version of" + " encfs only supports up to version %i."), + config->subVersion, info->currentSubVersion); return false; - } -} - -bool readV5Config( const char *configFile, - const shared_ptr &config, - ConfigInfo *info) -{ - bool ok = false; - - // use Config to parse the file and query it.. - ConfigReader cfgRdr; - if(cfgRdr.load( configFile )) - { - try - { - config->subVersion = cfgRdr["subVersion"].readInt( - info->defaultSubVersion ); - if(config->subVersion > info->currentSubVersion) - { - /* config file specifies a version outside our supported - range.. */ - rWarning(_("Config subversion %i found, but this version of" - " encfs only supports up to version %i."), - config->subVersion, info->currentSubVersion); - return false; - } - if( config->subVersion < 20040813 ) - { - rError(_("This version of EncFS doesn't support " - "filesystems created before 2004-08-13")); - return false; - } - - cfgRdr["creator"] >> config->creator; - cfgRdr["cipher"] >> config->cipherIface; - cfgRdr["naming"] >> config->nameIface; - cfgRdr["keySize"] >> config->keySize; - cfgRdr["blockSize"] >> config->blockSize; - - string data; - cfgRdr["keyData"] >> data; - config->assignKeyData(data); - config->uniqueIV = cfgRdr["uniqueIV"].readBool( false ); - config->chainedNameIV = cfgRdr["chainedIV"].readBool( false ); - config->externalIVChaining = cfgRdr["externalIV"].readBool( false ); - config->blockMACBytes = cfgRdr["blockMACBytes"].readInt(0); - config->blockMACRandBytes = - cfgRdr["blockMACRandBytes"].readInt(0); - - ok = true; - } catch( rlog::Error &err) - { - err.log( _RLWarningChannel ); - rDebug("Error parsing data in config file %s", configFile); - ok = false; - } - } - - return ok; -} - -bool readV4Config( const char *configFile, - const shared_ptr &config, - ConfigInfo *info) -{ - bool ok = false; - - // use Config to parse the file and query it.. - ConfigReader cfgRdr; - if(cfgRdr.load( configFile )) - { - try - { - cfgRdr["cipher"] >> config->cipherIface; - cfgRdr["keySize"] >> config->keySize; - cfgRdr["blockSize"] >> config->blockSize; - string data; - cfgRdr["keyData"] >> data; - config->assignKeyData(data); - - // fill in default for V4 - config->nameIface = Interface("nameio/stream", 1, 0, 0); - config->creator = "EncFS 1.0.x"; - config->subVersion = info->defaultSubVersion; - config->blockMACBytes = 0; - config->blockMACRandBytes = 0; - config->uniqueIV = false; - config->externalIVChaining = false; - config->chainedNameIV = false; - - ok = true; - } catch( rlog::Error &err) - { - err.log( _RLWarningChannel ); - rDebug("Error parsing config file %s", configFile); - ok = false; - } - } - - return ok; -} - -bool saveConfig( ConfigType type, const string &rootDir, - const shared_ptr &config ) -{ - bool ok = false; - - ConfigInfo *nm = ConfigFileMapping; - while(nm->fileName) - { - if( nm->type == type && nm->saveFunc ) - { - string path = rootDir + nm->fileName; - if( nm->environmentOverride != NULL ) - { - // use environment file if specified.. - const char *envFile = getenv( nm->environmentOverride ); - if( envFile != NULL ) - path.assign( envFile ); - } - - try - { - ok = (*nm->saveFunc)( path.c_str(), config ); - } catch( rlog::Error &err ) - { - err.log( _RLWarningChannel ); - ok = false; - } - break; - } - ++nm; - } - - return ok; -} - -bool writeV6Config( const char *configFile, - const shared_ptr &config ) -{ - fs::ofstream st( configFile ); - if(!st.is_open()) + } + if (config->subVersion < 20040813) { + rError( + _("This version of EncFS doesn't support " + "filesystems created before 2004-08-13")); return false; + } - st << *config; + cfgRdr["creator"] >> config->creator; + cfgRdr["cipher"] >> config->cipherIface; + cfgRdr["naming"] >> config->nameIface; + cfgRdr["keySize"] >> config->keySize; + cfgRdr["blockSize"] >> config->blockSize; + + string data; + cfgRdr["keyData"] >> data; + config->assignKeyData(data); + config->uniqueIV = cfgRdr["uniqueIV"].readBool(false); + config->chainedNameIV = cfgRdr["chainedIV"].readBool(false); + config->externalIVChaining = cfgRdr["externalIV"].readBool(false); + config->blockMACBytes = cfgRdr["blockMACBytes"].readInt(0); + config->blockMACRandBytes = cfgRdr["blockMACRandBytes"].readInt(0); + + ok = true; + } + catch (rlog::Error &err) { + err.log(_RLWarningChannel); + rDebug("Error parsing data in config file %s", configFile); + ok = false; + } + } + + return ok; +} + +bool readV4Config(const char *configFile, const shared_ptr &config, + ConfigInfo *info) { + bool ok = false; + + // use Config to parse the file and query it.. + ConfigReader cfgRdr; + if (cfgRdr.load(configFile)) { + try { + cfgRdr["cipher"] >> config->cipherIface; + cfgRdr["keySize"] >> config->keySize; + cfgRdr["blockSize"] >> config->blockSize; + string data; + cfgRdr["keyData"] >> data; + config->assignKeyData(data); + + // fill in default for V4 + config->nameIface = Interface("nameio/stream", 1, 0, 0); + config->creator = "EncFS 1.0.x"; + config->subVersion = info->defaultSubVersion; + config->blockMACBytes = 0; + config->blockMACRandBytes = 0; + config->uniqueIV = false; + config->externalIVChaining = false; + config->chainedNameIV = false; + + ok = true; + } + catch (rlog::Error &err) { + err.log(_RLWarningChannel); + rDebug("Error parsing config file %s", configFile); + ok = false; + } + } + + return ok; +} + +bool saveConfig(ConfigType type, const string &rootDir, + const shared_ptr &config) { + bool ok = false; + + ConfigInfo *nm = ConfigFileMapping; + while (nm->fileName) { + if (nm->type == type && nm->saveFunc) { + string path = rootDir + nm->fileName; + if (nm->environmentOverride != NULL) { + // use environment file if specified.. + const char *envFile = getenv(nm->environmentOverride); + if (envFile != NULL) path.assign(envFile); + } + + try { + ok = (*nm->saveFunc)(path.c_str(), config); + } + catch (rlog::Error &err) { + err.log(_RLWarningChannel); + ok = false; + } + break; + } + ++nm; + } + + return ok; +} + +bool writeV6Config(const char *configFile, + const shared_ptr &config) { + fs::ofstream st(configFile); + if (!st.is_open()) return false; + + st << *config; + return true; +} + +std::ostream &operator<<(std::ostream &st, const EncFSConfig &cfg) { + boost::archive::xml_oarchive oa(st); + oa << BOOST_SERIALIZATION_NVP(cfg); + + return st; +} + +std::istream &operator>>(std::istream &st, EncFSConfig &cfg) { + boost::archive::xml_iarchive ia(st); + ia >> BOOST_SERIALIZATION_NVP(cfg); + + return st; +} + +bool writeV5Config(const char *configFile, + const shared_ptr &config) { + ConfigReader cfg; + + cfg["creator"] << config->creator; + cfg["subVersion"] << config->subVersion; + cfg["cipher"] << config->cipherIface; + cfg["naming"] << config->nameIface; + cfg["keySize"] << config->keySize; + cfg["blockSize"] << config->blockSize; + string key; + key.assign((char *)config->getKeyData(), config->keyData.size()); + cfg["keyData"] << key; + cfg["blockMACBytes"] << config->blockMACBytes; + cfg["blockMACRandBytes"] << config->blockMACRandBytes; + cfg["uniqueIV"] << config->uniqueIV; + cfg["chainedIV"] << config->chainedNameIV; + cfg["externalIV"] << config->externalIVChaining; + + return cfg.save(configFile); +} + +bool writeV4Config(const char *configFile, + const shared_ptr &config) { + ConfigReader cfg; + + cfg["cipher"] << config->cipherIface; + cfg["keySize"] << config->keySize; + cfg["blockSize"] << config->blockSize; + string key; + key.assign((char *)config->getKeyData(), config->keyData.size()); + cfg["keyData"] << key; + + return cfg.save(configFile); +} + +static Cipher::CipherAlgorithm findCipherAlgorithm(const char *name, + int keySize) { + Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList(); + Cipher::AlgorithmList::const_iterator it; + for (it = algorithms.begin(); it != algorithms.end(); ++it) { + if (!strcmp(name, it->name.c_str()) && it->keyLength.allowed(keySize)) { + return *it; + } + } + + Cipher::CipherAlgorithm result; + return result; +} + +static Cipher::CipherAlgorithm selectCipherAlgorithm() { + for (;;) { + // figure out what cipher they want to use.. + // xgroup(setup) + cout << _("The following cipher algorithms are available:") << "\n"; + Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList(); + Cipher::AlgorithmList::const_iterator it; + int optNum = 1; + for (it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) { + cout << optNum << ". " << it->name << " : " + << gettext(it->description.c_str()) << "\n"; + if (it->keyLength.min() == it->keyLength.max()) { + // shown after algorithm name and description. + // xgroup(setup) + cout << format(_(" -- key length %i bits")) % it->keyLength.min() + << "\n"; + } else { + cout << format( + // shown after algorithm name and description. + // xgroup(setup) + _(" -- Supports key lengths of %i to %i bits")) % + it->keyLength.min() % it->keyLength.max() << "\n"; + } + + if (it->blockSize.min() == it->blockSize.max()) { + cout << format( + // shown after algorithm name and description. + // xgroup(setup) + _(" -- block size %i bytes")) % + it->blockSize.min() << "\n"; + } else { + cout << format( + // shown after algorithm name and description. + // xgroup(setup) + _(" -- Supports block sizes of %i to %i bytes")) % + it->blockSize.min() % it->blockSize.max() << "\n"; + } + } + + // xgroup(setup) + cout << "\n" << _("Enter the number corresponding to your choice: "); + char answer[10]; + char *res = fgets(answer, sizeof(answer), stdin); + int cipherNum = (res == 0 ? 0 : atoi(answer)); + cout << "\n"; + + if (cipherNum < 1 || cipherNum > (int)algorithms.size()) { + cerr << _("Invalid selection.") << "\n"; + continue; + } + + it = algorithms.begin(); + while (--cipherNum) // numbering starts at 1 + ++it; + + Cipher::CipherAlgorithm alg = *it; + + // xgroup(setup) + cout << format(_("Selected algorithm \"%s\"")) % alg.name << "\n\n"; + + return alg; + } +} + +static Interface selectNameCoding() { + for (;;) { + // figure out what cipher they want to use.. + // xgroup(setup) + cout << _("The following filename encoding algorithms are available:") + << "\n"; + NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList(); + NameIO::AlgorithmList::const_iterator it; + int optNum = 1; + for (it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) { + cout << optNum << ". " << it->name << " : " + << gettext(it->description.c_str()) << "\n"; + } + + // xgroup(setup) + cout << "\n" << _("Enter the number corresponding to your choice: "); + char answer[10]; + char *res = fgets(answer, sizeof(answer), stdin); + int algNum = (res == 0 ? 0 : atoi(answer)); + cout << "\n"; + + if (algNum < 1 || algNum > (int)algorithms.size()) { + cerr << _("Invalid selection.") << "\n"; + continue; + } + + it = algorithms.begin(); + while (--algNum) // numbering starts at 1 + ++it; + + // xgroup(setup) + cout << format(_("Selected algorithm \"%s\"")) % it->name << "\"\n\n"; + + return it->iface; + } +} + +static int selectKeySize(const Cipher::CipherAlgorithm &alg) { + if (alg.keyLength.min() == alg.keyLength.max()) { + cout << format(_("Using key size of %i bits")) % alg.keyLength.min() + << "\n"; + return alg.keyLength.min(); + } + + cout + << format( + // xgroup(setup) + _("Please select a key size in bits. The cipher you have chosen\n" + "supports sizes from %i to %i bits in increments of %i bits.\n" + "For example: ")) % + alg.keyLength.min() % alg.keyLength.max() % alg.keyLength.inc() + << "\n"; + + int numAvail = + (alg.keyLength.max() - alg.keyLength.min()) / alg.keyLength.inc(); + + if (numAvail < 5) { + // show them all + for (int i = 0; i <= numAvail; ++i) { + if (i) cout << ", "; + cout << alg.keyLength.min() + i * alg.keyLength.inc(); + } + } else { + // partial + for (int i = 0; i < 3; ++i) { + if (i) cout << ", "; + cout << alg.keyLength.min() + i * alg.keyLength.inc(); + } + cout << " ... " << alg.keyLength.max() - alg.keyLength.inc(); + cout << ", " << alg.keyLength.max(); + } + // xgroup(setup) + cout << "\n" << _("Selected key size: "); + + char answer[10]; + char *res = fgets(answer, sizeof(answer), stdin); + int keySize = (res == 0 ? 0 : atoi(answer)); + cout << "\n"; + + keySize = alg.keyLength.closest(keySize); + + // xgroup(setup) + cout << format(_("Using key size of %i bits")) % keySize << "\n\n"; + + return keySize; +} + +static int selectBlockSize(const Cipher::CipherAlgorithm &alg) { + if (alg.blockSize.min() == alg.blockSize.max()) { + cout << format( + // xgroup(setup) + _("Using filesystem block size of %i bytes")) % + alg.blockSize.min() << "\n"; + return alg.blockSize.min(); + } + + cout << format( + // xgroup(setup) + _("Select a block size in bytes. The cipher you have chosen\n" + "supports sizes from %i to %i bytes in increments of %i.\n" + "Or just hit enter for the default (%i bytes)\n")) % + alg.blockSize.min() % alg.blockSize.max() % alg.blockSize.inc() % + DefaultBlockSize; + + // xgroup(setup) + cout << "\n" << _("filesystem block size: "); + + int blockSize = DefaultBlockSize; + char answer[10]; + char *res = fgets(answer, sizeof(answer), stdin); + cout << "\n"; + + if (res != 0 && atoi(answer) >= alg.blockSize.min()) blockSize = atoi(answer); + + blockSize = alg.blockSize.closest(blockSize); + + // xgroup(setup) + cout << format(_("Using filesystem block size of %i bytes")) % blockSize + << "\n\n"; + + return blockSize; +} + +static bool boolDefaultNo(const char *prompt) { + cout << prompt << "\n"; + cout << _("The default here is No.\n" + "Any response that does not begin with 'y' will mean No: "); + + char answer[10]; + char *res = fgets(answer, sizeof(answer), stdin); + cout << "\n"; + + if (res != 0 && tolower(answer[0]) == 'y') + return true; + else + return false; +} + +static void selectBlockMAC(int *macBytes, int *macRandBytes) { + // xgroup(setup) + bool addMAC = boolDefaultNo( + _("Enable block authentication code headers\n" + "on every block in a file? This adds about 12 bytes per block\n" + "to the storage requirements for a file, and significantly affects\n" + "performance but it also means [almost] any modifications or errors\n" + "within a block will be caught and will cause a read error.")); + + if (addMAC) + *macBytes = 8; + else + *macBytes = 0; + + // xgroup(setup) + cout << _("Add random bytes to each block header?\n" + "This adds a performance penalty, but ensures that blocks\n" + "have different authentication codes. Note that you can\n" + "have the same benefits by enabling per-file initialization\n" + "vectors, which does not come with as great of performance\n" + "penalty. \n" + "Select a number of bytes, from 0 (no random bytes) to 8: "); + + char answer[10]; + int randSize = 0; + char *res = fgets(answer, sizeof(answer), stdin); + cout << "\n"; + + randSize = (res == 0 ? 0 : atoi(answer)); + if (randSize < 0) randSize = 0; + if (randSize > 8) randSize = 8; + + *macRandBytes = randSize; +} + +static bool boolDefaultYes(const char *prompt) { + cout << prompt << "\n"; + cout << _("The default here is Yes.\n" + "Any response that does not begin with 'n' will mean Yes: "); + + char answer[10]; + char *res = fgets(answer, sizeof(answer), stdin); + cout << "\n"; + + if (res != 0 && tolower(answer[0]) == 'n') + return false; + else return true; } -std::ostream &operator << (std::ostream &st, const EncFSConfig &cfg) -{ - boost::archive::xml_oarchive oa(st); - oa << BOOST_SERIALIZATION_NVP( cfg ); - - return st; +static bool selectUniqueIV() { + // xgroup(setup) + return boolDefaultYes( + _("Enable per-file initialization vectors?\n" + "This adds about 8 bytes per file to the storage requirements.\n" + "It should not affect performance except possibly with applications\n" + "which rely on block-aligned file io for performance.")); } -std::istream &operator >> (std::istream &st, EncFSConfig &cfg) -{ - boost::archive::xml_iarchive ia(st); - ia >> BOOST_SERIALIZATION_NVP( cfg ); - - return st; +static bool selectChainedIV() { + // xgroup(setup) + return boolDefaultYes( + _("Enable filename initialization vector chaining?\n" + "This makes filename encoding dependent on the complete path, \n" + "rather then encoding each path element individually.")); } -bool writeV5Config( const char *configFile, - const shared_ptr &config ) -{ - ConfigReader cfg; - - cfg["creator"] << config->creator; - cfg["subVersion"] << config->subVersion; - cfg["cipher"] << config->cipherIface; - cfg["naming"] << config->nameIface; - cfg["keySize"] << config->keySize; - cfg["blockSize"] << config->blockSize; - string key; - key.assign((char *)config->getKeyData(), config->keyData.size()); - cfg["keyData"] << key; - cfg["blockMACBytes"] << config->blockMACBytes; - cfg["blockMACRandBytes"] << config->blockMACRandBytes; - cfg["uniqueIV"] << config->uniqueIV; - cfg["chainedIV"] << config->chainedNameIV; - cfg["externalIV"] << config->externalIVChaining; - - return cfg.save( configFile ); +static bool selectExternalChainedIV() { + // xgroup(setup) + return boolDefaultNo( + _("Enable filename to IV header chaining?\n" + "This makes file data encoding dependent on the complete file path.\n" + "If a file is renamed, it will not decode sucessfully unless it\n" + "was renamed by encfs with the proper key.\n" + "If this option is enabled, then hard links will not be supported\n" + "in the filesystem.")); } -bool writeV4Config( const char *configFile, - const shared_ptr &config ) -{ - ConfigReader cfg; - - cfg["cipher"] << config->cipherIface; - cfg["keySize"] << config->keySize; - cfg["blockSize"] << config->blockSize; - string key; - key.assign((char *)config->getKeyData(), config->keyData.size()); - cfg["keyData"] << key; - - return cfg.save( configFile ); +static bool selectZeroBlockPassThrough() { + // xgroup(setup) + return boolDefaultYes( + _("Enable file-hole pass-through?\n" + "This avoids writing encrypted blocks when file holes are created.")); } -static -Cipher::CipherAlgorithm findCipherAlgorithm(const char *name, - int keySize ) -{ - Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList(); - Cipher::AlgorithmList::const_iterator it; - for(it = algorithms.begin(); it != algorithms.end(); ++it) - { - if( !strcmp( name, it->name.c_str() ) - && it->keyLength.allowed( keySize )) - { - return *it; - } - } +RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts) { + const std::string rootDir = opts->rootDir; + bool enableIdleTracking = opts->idleTracking; + bool forceDecode = opts->forceDecode; + const std::string passwordProgram = opts->passwordProgram; + bool useStdin = opts->useStdin; + bool reverseEncryption = opts->reverseEncryption; + ConfigMode configMode = opts->configMode; + bool annotate = opts->annotate; - Cipher::CipherAlgorithm result; - return result; -} + RootPtr rootInfo; -static -Cipher::CipherAlgorithm selectCipherAlgorithm() -{ - for(;;) - { - // figure out what cipher they want to use.. - // xgroup(setup) - cout << _("The following cipher algorithms are available:") << "\n"; - Cipher::AlgorithmList algorithms = Cipher::GetAlgorithmList(); - Cipher::AlgorithmList::const_iterator it; - int optNum = 1; - for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) - { - cout << optNum << ". " << it->name - << " : " << gettext(it->description.c_str()) << "\n"; - if(it->keyLength.min() == it->keyLength.max()) - { - // shown after algorithm name and description. - // xgroup(setup) - cout << format(_(" -- key length %i bits")) % it->keyLength.min() << "\n"; - } else - { - cout << format( - // shown after algorithm name and description. - // xgroup(setup) - _(" -- Supports key lengths of %i to %i bits")) % - it->keyLength.min() % it->keyLength.max() << "\n"; - } + // creating new volume key.. should check that is what the user is + // expecting... + // xgroup(setup) + cout << _("Creating new encrypted volume.") << endl; - if(it->blockSize.min() == it->blockSize.max()) - { - cout << format( - // shown after algorithm name and description. - // xgroup(setup) - _(" -- block size %i bytes")) % it->blockSize.min() - << "\n"; - } else - { - cout << format( - // shown after algorithm name and description. - // xgroup(setup) - _(" -- Supports block sizes of %i to %i bytes")) % - it->blockSize.min() % it->blockSize.max() << "\n"; - } - } - - // xgroup(setup) - cout << "\n" << _("Enter the number corresponding to your choice: "); - char answer[10]; - char *res = fgets( answer, sizeof(answer), stdin ); - int cipherNum = (res == 0 ? 0 : atoi( answer )); - cout << "\n"; - - if( cipherNum < 1 || cipherNum > (int)algorithms.size() ) - { - cerr << _("Invalid selection.") << "\n"; - continue; - } - - it = algorithms.begin(); - while(--cipherNum) // numbering starts at 1 - ++it; - - Cipher::CipherAlgorithm alg = *it; - - // xgroup(setup) - cout << format(_("Selected algorithm \"%s\"")) % alg.name - << "\n\n"; - - return alg; - } -} - -static -Interface selectNameCoding() -{ - for(;;) - { - // figure out what cipher they want to use.. - // xgroup(setup) - cout << _("The following filename encoding algorithms are available:") - << "\n"; - NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList(); - NameIO::AlgorithmList::const_iterator it; - int optNum = 1; - for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum) - { - cout << optNum << ". " << it->name - << " : " << gettext(it->description.c_str()) << "\n"; - } - - // xgroup(setup) - cout << "\n" << _("Enter the number corresponding to your choice: "); - char answer[10]; - char *res = fgets( answer, sizeof(answer), stdin ); - int algNum = (res == 0 ? 0 : atoi( answer )); - cout << "\n"; - - if( algNum < 1 || algNum > (int)algorithms.size() ) - { - cerr << _("Invalid selection.") << "\n"; - continue; - } - - it = algorithms.begin(); - while(--algNum) // numbering starts at 1 - ++it; - - // xgroup(setup) - cout << format(_("Selected algorithm \"%s\"")) % it->name - << "\"\n\n"; - - return it->iface; - } -} - -static -int selectKeySize( const Cipher::CipherAlgorithm &alg ) -{ - if(alg.keyLength.min() == alg.keyLength.max()) - { - cout << format(_("Using key size of %i bits")) % - alg.keyLength.min() << "\n"; - return alg.keyLength.min(); - } - - cout << format( - // xgroup(setup) - _("Please select a key size in bits. The cipher you have chosen\n" - "supports sizes from %i to %i bits in increments of %i bits.\n" - "For example: ")) % alg.keyLength.min() % alg.keyLength.max() % - alg.keyLength.inc() << "\n"; - - int numAvail = (alg.keyLength.max() - alg.keyLength.min()) - / alg.keyLength.inc(); - - if(numAvail < 5) - { - // show them all - for(int i=0; i<=numAvail; ++i) - { - if(i) - cout << ", "; - cout << alg.keyLength.min() + i * alg.keyLength.inc(); - } - } else - { - // partial - for(int i=0; i<3; ++i) - { - if(i) - cout << ", "; - cout << alg.keyLength.min() + i * alg.keyLength.inc(); - } - cout << " ... " << alg.keyLength.max() - alg.keyLength.inc(); - cout << ", " << alg.keyLength.max(); - } + char answer[10] = {0}; + if (configMode == Config_Prompt) { // xgroup(setup) - cout << "\n" << _("Selected key size: "); + cout << _("Please choose from one of the following options:\n" + " enter \"x\" for expert configuration mode,\n" + " enter \"p\" for pre-configured paranoia mode,\n" + " anything else, or an empty line will select standard mode.\n" + "?> "); - char answer[10]; - char *res = fgets( answer, sizeof(answer), stdin ); - int keySize = (res == 0 ? 0 : atoi( answer )); + if (annotate) cerr << "$PROMPT$ config_option" << endl; + + char *res = fgets(answer, sizeof(answer), stdin); + (void)res; cout << "\n"; + } - keySize = alg.keyLength.closest( keySize ); + int keySize = 0; + int blockSize = 0; + Cipher::CipherAlgorithm alg; + Interface nameIOIface; + int blockMACBytes = 0; + int blockMACRandBytes = 0; + bool uniqueIV = false; + bool chainedIV = false; + bool externalIV = false; + bool allowHoles = true; + long desiredKDFDuration = NormalKDFDuration; - // xgroup(setup) - cout << format(_("Using key size of %i bits")) % keySize << "\n\n"; + if (reverseEncryption) { + uniqueIV = false; + chainedIV = false; + externalIV = false; + blockMACBytes = 0; + blockMACRandBytes = 0; + } - return keySize; -} - -static -int selectBlockSize( const Cipher::CipherAlgorithm &alg ) -{ - if(alg.blockSize.min() == alg.blockSize.max()) - { - cout << format( - // xgroup(setup) - _("Using filesystem block size of %i bytes")) % - alg.blockSize.min() << "\n"; - return alg.blockSize.min(); + if (configMode == Config_Paranoia || answer[0] == 'p') { + if (reverseEncryption) { + rError(_("Paranoia configuration not supported for --reverse")); + return rootInfo; } - cout << format( - // xgroup(setup) - _("Select a block size in bytes. The cipher you have chosen\n" - "supports sizes from %i to %i bytes in increments of %i.\n" - "Or just hit enter for the default (%i bytes)\n")) % - alg.blockSize.min() % alg.blockSize.max() % alg.blockSize.inc() % - DefaultBlockSize; - // xgroup(setup) - cout << "\n" << _("filesystem block size: "); - - int blockSize = DefaultBlockSize; - char answer[10]; - char *res = fgets( answer, sizeof(answer), stdin ); - cout << "\n"; - - if( res != 0 && atoi( answer ) >= alg.blockSize.min() ) - blockSize = atoi( answer ); - - blockSize = alg.blockSize.closest( blockSize ); - + cout << _("Paranoia configuration selected.") << "\n"; + // look for AES with 256 bit key.. + // Use block filename encryption mode. + // Enable per-block HMAC headers at substantial performance penalty.. + // Enable per-file initialization vector headers. + // Enable filename initialization vector chaning + keySize = 256; + blockSize = DefaultBlockSize; + alg = findCipherAlgorithm("AES", keySize); + nameIOIface = BlockNameIO::CurrentInterface(); + blockMACBytes = 8; + blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary + uniqueIV = true; + chainedIV = true; + externalIV = true; + desiredKDFDuration = ParanoiaKDFDuration; + } else if (configMode == Config_Standard || answer[0] != 'x') { // xgroup(setup) - cout << format(_("Using filesystem block size of %i bytes")) % - blockSize << "\n\n"; + cout << _("Standard configuration selected.") << "\n"; + // AES w/ 192 bit key, block name encoding, per-file initialization + // vectors are all standard. + keySize = 192; + blockSize = DefaultBlockSize; + alg = findCipherAlgorithm("AES", keySize); + blockMACBytes = 0; + externalIV = false; + nameIOIface = BlockNameIO::CurrentInterface(); - return blockSize; -} + if (reverseEncryption) { + cout << _("--reverse specified, not using unique/chained IV") << "\n"; + } else { + uniqueIV = true; + chainedIV = true; + } + } -static -bool boolDefaultNo(const char *prompt) -{ - cout << prompt << "\n"; - cout << _("The default here is No.\n" - "Any response that does not begin with 'y' will mean No: "); + if (answer[0] == 'x' || alg.name.empty()) { + if (answer[0] != 'x') { + // xgroup(setup) + cout << _("Sorry, unable to locate cipher for predefined " + "configuration...\n" + "Falling through to Manual configuration mode."); + } else { + // xgroup(setup) + cout << _("Manual configuration mode selected."); + } + cout << endl; - char answer[10]; - char *res = fgets( answer, sizeof(answer), stdin ); - cout << "\n"; - - if(res != 0 && tolower(answer[0]) == 'y') - return true; - else - return false; -} - -static -void selectBlockMAC(int *macBytes, int *macRandBytes) -{ - // xgroup(setup) - bool addMAC = boolDefaultNo( - _("Enable block authentication code headers\n" - "on every block in a file? This adds about 12 bytes per block\n" - "to the storage requirements for a file, and significantly affects\n" - "performance but it also means [almost] any modifications or errors\n" - "within a block will be caught and will cause a read error.")); - - if(addMAC) - *macBytes = 8; - else - *macBytes = 0; - - // xgroup(setup) - cout << _("Add random bytes to each block header?\n" - "This adds a performance penalty, but ensures that blocks\n" - "have different authentication codes. Note that you can\n" - "have the same benefits by enabling per-file initialization\n" - "vectors, which does not come with as great of performance\n" - "penalty. \n" - "Select a number of bytes, from 0 (no random bytes) to 8: "); - - char answer[10]; - int randSize = 0; - char *res = fgets( answer, sizeof(answer), stdin ); - cout << "\n"; - - randSize = (res == 0 ? 0 : atoi( answer )); - if(randSize < 0) - randSize = 0; - if(randSize > 8) - randSize = 8; - - *macRandBytes = randSize; -} - -static -bool boolDefaultYes(const char *prompt) -{ - cout << prompt << "\n"; - cout << _("The default here is Yes.\n" - "Any response that does not begin with 'n' will mean Yes: "); - - char answer[10]; - char *res = fgets( answer, sizeof(answer), stdin ); - cout << "\n"; - - if(res != 0 && tolower(answer[0]) == 'n') - return false; - else - return true; -} - -static -bool selectUniqueIV() -{ - // xgroup(setup) - return boolDefaultYes( - _("Enable per-file initialization vectors?\n" - "This adds about 8 bytes per file to the storage requirements.\n" - "It should not affect performance except possibly with applications\n" - "which rely on block-aligned file io for performance.")); -} - -static -bool selectChainedIV() -{ - // xgroup(setup) - return boolDefaultYes( - _("Enable filename initialization vector chaining?\n" - "This makes filename encoding dependent on the complete path, \n" - "rather then encoding each path element individually.")); -} - -static -bool selectExternalChainedIV() -{ - // xgroup(setup) - return boolDefaultNo( - _("Enable filename to IV header chaining?\n" - "This makes file data encoding dependent on the complete file path.\n" - "If a file is renamed, it will not decode sucessfully unless it\n" - "was renamed by encfs with the proper key.\n" - "If this option is enabled, then hard links will not be supported\n" - "in the filesystem.")); -} - -static -bool selectZeroBlockPassThrough() -{ - // xgroup(setup) - return boolDefaultYes( - _("Enable file-hole pass-through?\n" - "This avoids writing encrypted blocks when file holes are created.")); -} - -RootPtr createV6Config( EncFS_Context *ctx, - const shared_ptr &opts ) -{ - const std::string rootDir = opts->rootDir; - bool enableIdleTracking = opts->idleTracking; - bool forceDecode = opts->forceDecode; - const std::string passwordProgram = opts->passwordProgram; - bool useStdin = opts->useStdin; - bool reverseEncryption = opts->reverseEncryption; - ConfigMode configMode = opts->configMode; - bool annotate = opts->annotate; - - RootPtr rootInfo; - - // creating new volume key.. should check that is what the user is - // expecting... - // xgroup(setup) - cout << _("Creating new encrypted volume.") << endl; - - char answer[10] = {0}; - if(configMode == Config_Prompt) - { + // query user for settings.. + alg = selectCipherAlgorithm(); + keySize = selectKeySize(alg); + blockSize = selectBlockSize(alg); + nameIOIface = selectNameCoding(); + if (reverseEncryption) { + cout << _("--reverse specified, not using unique/chained IV") << "\n"; + } else { + chainedIV = selectChainedIV(); + uniqueIV = selectUniqueIV(); + if (chainedIV && uniqueIV) + externalIV = selectExternalChainedIV(); + else { // xgroup(setup) - cout << _("Please choose from one of the following options:\n" - " enter \"x\" for expert configuration mode,\n" - " enter \"p\" for pre-configured paranoia mode,\n" - " anything else, or an empty line will select standard mode.\n" - "?> "); - - if (annotate) - cerr << "$PROMPT$ config_option" << endl; + cout << _("External chained IV disabled, as both 'IV chaining'\n" + "and 'unique IV' features are required for this option.") + << "\n"; + externalIV = false; + } + selectBlockMAC(&blockMACBytes, &blockMACRandBytes); + allowHoles = selectZeroBlockPassThrough(); + } + } - char *res = fgets( answer, sizeof(answer), stdin ); - (void)res; + shared_ptr cipher = Cipher::New(alg.name, keySize); + if (!cipher) { + rError(_("Unable to instanciate cipher %s, key size %i, block size %i"), + alg.name.c_str(), keySize, blockSize); + return rootInfo; + } else { + rDebug("Using cipher %s, key size %i, block size %i", alg.name.c_str(), + keySize, blockSize); + } + + shared_ptr config(new EncFSConfig); + + config->cfgType = Config_V6; + config->cipherIface = cipher->interface(); + config->keySize = keySize; + config->blockSize = blockSize; + config->nameIface = nameIOIface; + config->creator = "EncFS " VERSION; + config->subVersion = V6SubVersion; + config->blockMACBytes = blockMACBytes; + config->blockMACRandBytes = blockMACRandBytes; + config->uniqueIV = uniqueIV; + config->chainedNameIV = chainedIV; + config->externalIVChaining = externalIV; + config->allowHoles = allowHoles; + + config->salt.clear(); + config->kdfIterations = 0; // filled in by keying function + config->desiredKDFDuration = desiredKDFDuration; + + cout << "\n"; + // xgroup(setup) + cout << _("Configuration finished. The filesystem to be created has\n" + "the following properties:") << endl; + showFSInfo(config); + + if (config->externalIVChaining) { + cout << _("-------------------------- WARNING --------------------------\n") + << _("The external initialization-vector chaining option has been\n" + "enabled. This option disables the use of hard links on the\n" + "filesystem. Without hard links, some programs may not work.\n" + "The programs 'mutt' and 'procmail' are known to fail. For\n" + "more information, please see the encfs mailing list.\n" + "If you would like to choose another configuration setting,\n" + "please press CTRL-C now to abort and start over.") << endl; + cout << endl; + } + + // xgroup(setup) + cout << _("Now you will need to enter a password for your filesystem.\n" + "You will need to remember this password, as there is absolutely\n" + "no recovery mechanism. However, the password can be changed\n" + "later using encfsctl.\n\n"); + + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *encodedKey = new unsigned char[encodedKeySize]; + + CipherKey volumeKey = cipher->newRandomKey(); + + // get user key and use it to encode volume key + CipherKey userKey; + rDebug("useStdin: %i", useStdin); + if (useStdin) { + if (annotate) cerr << "$PROMPT$ new_passwd" << endl; + userKey = config->getUserKey(useStdin); + } else if (!passwordProgram.empty()) + userKey = config->getUserKey(passwordProgram, rootDir); + else + userKey = config->getNewUserKey(); + + cipher->writeKey(volumeKey, encodedKey, userKey); + userKey.reset(); + + config->assignKeyData(encodedKey, encodedKeySize); + delete[] encodedKey; + + if (!volumeKey) { + rWarning( + _("Failure generating new volume key! " + "Please report this error.")); + return rootInfo; + } + + if (!saveConfig(Config_V6, rootDir, config)) return rootInfo; + + // fill in config struct + shared_ptr nameCoder = + NameIO::New(config->nameIface, cipher, volumeKey); + if (!nameCoder) { + rWarning(_("Name coding interface not supported")); + cout << _("The filename encoding interface requested is not available") + << endl; + return rootInfo; + } + + nameCoder->setChainedNameIV(config->chainedNameIV); + nameCoder->setReverseEncryption(reverseEncryption); + + FSConfigPtr fsConfig(new FSConfig); + fsConfig->cipher = cipher; + fsConfig->key = volumeKey; + fsConfig->nameCoding = nameCoder; + fsConfig->config = config; + fsConfig->forceDecode = forceDecode; + fsConfig->reverseEncryption = reverseEncryption; + fsConfig->idleTracking = enableIdleTracking; + fsConfig->opts = opts; + + rootInfo = RootPtr(new EncFS_Root); + rootInfo->cipher = cipher; + rootInfo->volumeKey = volumeKey; + rootInfo->root = shared_ptr(new DirNode(ctx, rootDir, fsConfig)); + + return rootInfo; +} + +void showFSInfo(const shared_ptr &config) { + shared_ptr cipher = Cipher::New(config->cipherIface, -1); + { + cout << format( + // xgroup(diag) + _("Filesystem cipher: \"%s\", version %i:%i:%i")) % + config->cipherIface.name().c_str() % + config->cipherIface.current() % config->cipherIface.revision() % + config->cipherIface.age(); + // check if we support this interface.. + if (!cipher) + cout << _(" (NOT supported)\n"); + else { + // if we're using a newer interface, show the version number + if (config->cipherIface != cipher->interface()) { + Interface iface = cipher->interface(); + // xgroup(diag) + cout << format(_(" (using %i:%i:%i)\n")) % iface.current() % + iface.revision() % iface.age(); + } else cout << "\n"; } + } + { + // xgroup(diag) + cout << format(_("Filename encoding: \"%s\", version %i:%i:%i")) % + config->nameIface.name().c_str() % config->nameIface.current() % + config->nameIface.revision() % config->nameIface.age(); - int keySize = 0; - int blockSize = 0; - Cipher::CipherAlgorithm alg; - Interface nameIOIface; - int blockMACBytes = 0; - int blockMACRandBytes = 0; - bool uniqueIV = false; - bool chainedIV = false; - bool externalIV = false; - bool allowHoles = true; - long desiredKDFDuration = NormalKDFDuration; - - if (reverseEncryption) - { - uniqueIV = false; - chainedIV = false; - externalIV = false; - blockMACBytes = 0; - blockMACRandBytes = 0; + // check if we support the filename encoding interface.. + shared_ptr nameCoder = + NameIO::New(config->nameIface, cipher, CipherKey()); + if (!nameCoder) { + // xgroup(diag) + cout << _(" (NOT supported)\n"); + } else { + // if we're using a newer interface, show the version number + if (config->nameIface != nameCoder->interface()) { + Interface iface = nameCoder->interface(); + cout << format(_(" (using %i:%i:%i)\n")) % iface.current() % + iface.revision() % iface.age(); + } else + cout << "\n"; } - - if(configMode == Config_Paranoia || answer[0] == 'p') - { - if (reverseEncryption) - { - rError(_("Paranoia configuration not supported for --reverse")); - return rootInfo; - } - - // xgroup(setup) - cout << _("Paranoia configuration selected.") << "\n"; - // look for AES with 256 bit key.. - // Use block filename encryption mode. - // Enable per-block HMAC headers at substantial performance penalty.. - // Enable per-file initialization vector headers. - // Enable filename initialization vector chaning - keySize = 256; - blockSize = DefaultBlockSize; - alg = findCipherAlgorithm("AES", keySize); - nameIOIface = BlockNameIO::CurrentInterface(); - blockMACBytes = 8; - blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary - uniqueIV = true; - chainedIV = true; - externalIV = true; - desiredKDFDuration = ParanoiaKDFDuration; - } else if(configMode == Config_Standard || answer[0] != 'x') - { - // xgroup(setup) - cout << _("Standard configuration selected.") << "\n"; - // AES w/ 192 bit key, block name encoding, per-file initialization - // vectors are all standard. - keySize = 192; - blockSize = DefaultBlockSize; - alg = findCipherAlgorithm("AES", keySize); - blockMACBytes = 0; - externalIV = false; - nameIOIface = BlockNameIO::CurrentInterface(); - - if (reverseEncryption) - { - cout << _("--reverse specified, not using unique/chained IV") - << "\n"; - } else - { - uniqueIV = true; - chainedIV = true; - } - } - - if(answer[0] == 'x' || alg.name.empty()) - { - if(answer[0] != 'x') - { - // xgroup(setup) - cout << _("Sorry, unable to locate cipher for predefined " - "configuration...\n" - "Falling through to Manual configuration mode."); - } else - { - // xgroup(setup) - cout << _("Manual configuration mode selected."); - } - cout << endl; - - // query user for settings.. - alg = selectCipherAlgorithm(); - keySize = selectKeySize( alg ); - blockSize = selectBlockSize( alg ); - nameIOIface = selectNameCoding(); - if (reverseEncryption) - { - cout << _("--reverse specified, not using unique/chained IV") << "\n"; - } else - { - chainedIV = selectChainedIV(); - uniqueIV = selectUniqueIV(); - if(chainedIV && uniqueIV) - externalIV = selectExternalChainedIV(); - else - { - // xgroup(setup) - cout << _("External chained IV disabled, as both 'IV chaining'\n" - "and 'unique IV' features are required for this option.") - << "\n"; - externalIV = false; - } - selectBlockMAC(&blockMACBytes, &blockMACRandBytes); - allowHoles = selectZeroBlockPassThrough(); - } - } - - shared_ptr cipher = Cipher::New( alg.name, keySize ); - if(!cipher) - { - rError(_("Unable to instanciate cipher %s, key size %i, block size %i"), - alg.name.c_str(), keySize, blockSize); - return rootInfo; + } + { + cout << format(_("Key Size: %i bits")) % config->keySize; + cipher = config->getCipher(); + if (!cipher) { + // xgroup(diag) + cout << _(" (NOT supported)\n"); } else - { - rDebug("Using cipher %s, key size %i, block size %i", - alg.name.c_str(), keySize, blockSize); + cout << "\n"; + } + if (config->kdfIterations > 0 && config->salt.size() > 0) { + cout << format(_("Using PBKDF2, with %i iterations")) % + config->kdfIterations << "\n"; + cout << format(_("Salt Size: %i bits")) % (8 * config->salt.size()) << "\n"; + } + if (config->blockMACBytes || config->blockMACRandBytes) { + if (config->subVersion < 20040813) { + cout << format( + // xgroup(diag) + _("Block Size: %i bytes + %i byte MAC header")) % + config->blockSize % + (config->blockMACBytes + config->blockMACRandBytes) << endl; + } else { + // new version stores the header as part of that block size.. + cout << format( + // xgroup(diag) + _("Block Size: %i bytes, including %i byte MAC header")) % + config->blockSize % + (config->blockMACBytes + config->blockMACRandBytes) << endl; } - - shared_ptr config( new EncFSConfig ); - - config->cfgType = Config_V6; - config->cipherIface = cipher->interface(); - config->keySize = keySize; - config->blockSize = blockSize; - config->nameIface = nameIOIface; - config->creator = "EncFS " VERSION; - config->subVersion = V6SubVersion; - config->blockMACBytes = blockMACBytes; - config->blockMACRandBytes = blockMACRandBytes; - config->uniqueIV = uniqueIV; - config->chainedNameIV = chainedIV; - config->externalIVChaining = externalIV; - config->allowHoles = allowHoles; - - config->salt.clear(); - config->kdfIterations = 0; // filled in by keying function - config->desiredKDFDuration = desiredKDFDuration; - + } else { + // xgroup(diag) + cout << format(_("Block Size: %i bytes")) % config->blockSize; cout << "\n"; - // xgroup(setup) - cout << _("Configuration finished. The filesystem to be created has\n" - "the following properties:") << endl; - showFSInfo( config ); - - if( config->externalIVChaining ) - { - cout << - _("-------------------------- WARNING --------------------------\n") - << - _("The external initialization-vector chaining option has been\n" - "enabled. This option disables the use of hard links on the\n" - "filesystem. Without hard links, some programs may not work.\n" - "The programs 'mutt' and 'procmail' are known to fail. For\n" - "more information, please see the encfs mailing list.\n" - "If you would like to choose another configuration setting,\n" - "please press CTRL-C now to abort and start over.") << endl; - cout << endl; + } + + if (config->uniqueIV) { + // xgroup(diag) + cout << _("Each file contains 8 byte header with unique IV data.\n"); + } + if (config->chainedNameIV) { + // xgroup(diag) + cout << _("Filenames encoded using IV chaining mode.\n"); + } + if (config->externalIVChaining) { + // xgroup(diag) + cout << _("File data IV is chained to filename IV.\n"); + } + if (config->allowHoles) { + // xgroup(diag) + cout << _("File holes passed through to ciphertext.\n"); + } + cout << "\n"; +} + +shared_ptr EncFSConfig::getCipher() const { + return Cipher::New(cipherIface, keySize); +} + +void EncFSConfig::assignKeyData(const std::string &in) { + keyData.assign(in.data(), in.data() + in.length()); +} + +void EncFSConfig::assignKeyData(unsigned char *data, int len) { + keyData.assign(data, data + len); +} + +void EncFSConfig::assignSaltData(unsigned char *data, int len) { + salt.assign(data, data + len); +} + +unsigned char *EncFSConfig::getKeyData() const { + return const_cast(&keyData.front()); +} + +unsigned char *EncFSConfig::getSaltData() const { + return const_cast(&salt.front()); +} + +CipherKey EncFSConfig::makeKey(const char *password, int passwdLen) { + CipherKey userKey; + shared_ptr cipher = getCipher(); + + // if no salt is set and we're creating a new password for a new + // FS type, then initialize salt.. + if (salt.size() == 0 && kdfIterations == 0 && cfgType >= Config_V6) { + // upgrade to using salt + salt.resize(20); + } + + if (salt.size() > 0) { + // if iterations isn't known, then we're creating a new key, so + // randomize the salt.. + if (kdfIterations == 0 && + !cipher->randomize(getSaltData(), salt.size(), true)) { + cout << _("Error creating salt\n"); + return userKey; } - // xgroup(setup) - cout << _("Now you will need to enter a password for your filesystem.\n" - "You will need to remember this password, as there is absolutely\n" - "no recovery mechanism. However, the password can be changed\n" - "later using encfsctl.\n\n"); + userKey = cipher->newKey(password, passwdLen, kdfIterations, + desiredKDFDuration, getSaltData(), salt.size()); + } else { + userKey = cipher->newKey(password, passwdLen); + } - int encodedKeySize = cipher->encodedKeySize(); - unsigned char *encodedKey = new unsigned char[ encodedKeySize ]; + return userKey; +} - CipherKey volumeKey = cipher->newRandomKey(); +CipherKey EncFSConfig::getUserKey(bool useStdin) { + char passBuf[MaxPassBuf]; + char *res; - // get user key and use it to encode volume key + if (useStdin) { + res = fgets(passBuf, sizeof(passBuf), stdin); + // Kill the trailing newline. + if (passBuf[strlen(passBuf) - 1] == '\n') + passBuf[strlen(passBuf) - 1] = '\0'; + } else { + // xgroup(common) + res = readpassphrase(_("EncFS Password: "), passBuf, sizeof(passBuf), + RPP_ECHO_OFF); + } + + CipherKey userKey; + if (!res) + cerr << _("Zero length password not allowed\n"); + else + userKey = makeKey(passBuf, strlen(passBuf)); + + memset(passBuf, 0, sizeof(passBuf)); + + return userKey; +} + +std::string readPassword(int FD) { + char buffer[1024]; + string result; + + while (1) { + ssize_t rdSize = recv(FD, buffer, sizeof(buffer), 0); + + if (rdSize > 0) { + result.append(buffer, rdSize); + memset(buffer, 0, sizeof(buffer)); + } else + break; + } + + // chop off trailing "\n" if present.. + // This is done so that we can use standard programs like ssh-askpass + // without modification, as it returns trailing newline.. + if (!result.empty() && result[result.length() - 1] == '\n') + result.resize(result.length() - 1); + + return result; +} + +CipherKey EncFSConfig::getUserKey(const std::string &passProg, + const std::string &rootDir) { + // have a child process run the command and get the result back to us. + int fds[2], pid; + int res; + CipherKey result; + + res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); + if (res == -1) { + perror(_("Internal error: socketpair() failed")); + return result; + } + rDebug("getUserKey: fds = %i, %i", fds[0], fds[1]); + + pid = fork(); + if (pid == -1) { + perror(_("Internal error: fork() failed")); + close(fds[0]); + close(fds[1]); + return result; + } + + if (pid == 0) { + const char *argv[4]; + argv[0] = "/bin/sh"; + argv[1] = "-c"; + argv[2] = passProg.c_str(); + argv[3] = 0; + + // child process.. run the command and send output to fds[0] + close(fds[1]); // we don't use the other half.. + + // make a copy of stdout and stderr descriptors, and set an environment + // variable telling where to find them, in case a child wants it.. + int stdOutCopy = dup(STDOUT_FILENO); + int stdErrCopy = dup(STDERR_FILENO); + // replace STDOUT with our socket, which we'll used to receive the + // password.. + dup2(fds[0], STDOUT_FILENO); + + // ensure that STDOUT_FILENO and stdout/stderr are not closed on exec.. + fcntl(STDOUT_FILENO, F_SETFD, 0); // don't close on exec.. + fcntl(stdOutCopy, F_SETFD, 0); + fcntl(stdErrCopy, F_SETFD, 0); + + char tmpBuf[8]; + + setenv(ENCFS_ENV_ROOTDIR, rootDir.c_str(), 1); + + snprintf(tmpBuf, sizeof(tmpBuf) - 1, "%i", stdOutCopy); + setenv(ENCFS_ENV_STDOUT, tmpBuf, 1); + + snprintf(tmpBuf, sizeof(tmpBuf) - 1, "%i", stdErrCopy); + setenv(ENCFS_ENV_STDERR, tmpBuf, 1); + + execvp(argv[0], (char * const *)argv); // returns only on error.. + + perror(_("Internal error: failed to exec program")); + exit(1); + } + + close(fds[0]); + string password = readPassword(fds[1]); + close(fds[1]); + + waitpid(pid, NULL, 0); + + // convert to key.. + result = makeKey(password.c_str(), password.length()); + + // clear buffer.. + password.assign(password.length(), '\0'); + + return result; +} + +CipherKey EncFSConfig::getNewUserKey() { + CipherKey userKey; + char passBuf[MaxPassBuf]; + char passBuf2[MaxPassBuf]; + + do { + // xgroup(common) + char *res1 = readpassphrase(_("New Encfs Password: "), passBuf, + sizeof(passBuf) - 1, RPP_ECHO_OFF); + // xgroup(common) + char *res2 = readpassphrase(_("Verify Encfs Password: "), passBuf2, + sizeof(passBuf2) - 1, RPP_ECHO_OFF); + + if (res1 && res2 && !strcmp(passBuf, passBuf2)) { + userKey = makeKey(passBuf, strlen(passBuf)); + } else { + // xgroup(common) -- probably not common, but group with the others + cerr << _("Passwords did not match, please try again\n"); + } + + memset(passBuf, 0, sizeof(passBuf)); + memset(passBuf2, 0, sizeof(passBuf2)); + } while (!userKey); + + return userKey; +} + +RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts) { + RootPtr rootInfo; + shared_ptr config(new EncFSConfig); + + if (readConfig(opts->rootDir, config) != Config_None) { + if (opts->reverseEncryption) { + if (config->blockMACBytes != 0 || config->blockMACRandBytes != 0 || + config->uniqueIV || config->externalIVChaining || + config->chainedNameIV) { + cout + << _("The configuration loaded is not compatible with --reverse\n"); + return rootInfo; + } + } + + // first, instanciate the cipher. + shared_ptr cipher = config->getCipher(); + if (!cipher) { + rError(_("Unable to find cipher %s, version %i:%i:%i"), + config->cipherIface.name().c_str(), config->cipherIface.current(), + config->cipherIface.revision(), config->cipherIface.age()); + // xgroup(diag) + cout << _("The requested cipher interface is not available\n"); + return rootInfo; + } + + if (opts->delayMount) { + rootInfo = RootPtr(new EncFS_Root); + rootInfo->cipher = cipher; + rootInfo->root = shared_ptr(); + return rootInfo; + } + + // get user key CipherKey userKey; - rDebug( "useStdin: %i", useStdin ); - if(useStdin) - { - if (annotate) - cerr << "$PROMPT$ new_passwd" << endl; - userKey = config->getUserKey( useStdin ); - } - else if(!passwordProgram.empty()) - userKey = config->getUserKey( passwordProgram, rootDir ); - else - userKey = config->getNewUserKey(); - cipher->writeKey( volumeKey, encodedKey, userKey ); + if (opts->passwordProgram.empty()) { + rDebug("useStdin: %i", opts->useStdin); + if (opts->annotate) cerr << "$PROMPT$ passwd" << endl; + userKey = config->getUserKey(opts->useStdin); + } else + userKey = config->getUserKey(opts->passwordProgram, opts->rootDir); + + if (!userKey) return rootInfo; + + rDebug("cipher key size = %i", cipher->encodedKeySize()); + // decode volume key.. + CipherKey volumeKey = + cipher->readKey(config->getKeyData(), userKey, opts->checkKey); userKey.reset(); - config->assignKeyData(encodedKey, encodedKeySize); - delete[] encodedKey; - - if(!volumeKey) - { - rWarning(_("Failure generating new volume key! " - "Please report this error.")); - return rootInfo; + if (!volumeKey) { + // xgroup(diag) + cout << _("Error decoding volume key, password incorrect\n"); + return rootInfo; } - if(!saveConfig( Config_V6, rootDir, config )) - return rootInfo; - - // fill in config struct - shared_ptr nameCoder = NameIO::New( config->nameIface, - cipher, volumeKey ); - if(!nameCoder) - { - rWarning(_("Name coding interface not supported")); - cout << _("The filename encoding interface requested is not available") - << endl; - return rootInfo; + shared_ptr nameCoder = + NameIO::New(config->nameIface, cipher, volumeKey); + if (!nameCoder) { + rError(_("Unable to find nameio interface %s, version %i:%i:%i"), + config->nameIface.name().c_str(), config->nameIface.current(), + config->nameIface.revision(), config->nameIface.age()); + // xgroup(diag) + cout << _("The requested filename coding interface is " + "not available\n"); + return rootInfo; } - - nameCoder->setChainedNameIV( config->chainedNameIV ); - nameCoder->setReverseEncryption( reverseEncryption ); - FSConfigPtr fsConfig (new FSConfig); + nameCoder->setChainedNameIV(config->chainedNameIV); + nameCoder->setReverseEncryption(opts->reverseEncryption); + + FSConfigPtr fsConfig(new FSConfig); fsConfig->cipher = cipher; fsConfig->key = volumeKey; fsConfig->nameCoding = nameCoder; fsConfig->config = config; - fsConfig->forceDecode = forceDecode; - fsConfig->reverseEncryption = reverseEncryption; - fsConfig->idleTracking = enableIdleTracking; + fsConfig->forceDecode = opts->forceDecode; + fsConfig->reverseEncryption = opts->reverseEncryption; fsConfig->opts = opts; - rootInfo = RootPtr( new EncFS_Root ); + rootInfo = RootPtr(new EncFS_Root); rootInfo->cipher = cipher; rootInfo->volumeKey = volumeKey; - rootInfo->root = shared_ptr( - new DirNode( ctx, rootDir, fsConfig )); + rootInfo->root = + shared_ptr(new DirNode(ctx, opts->rootDir, fsConfig)); + } else { + if (opts->createIfNotFound) { + // creating a new encrypted filesystem + rootInfo = createV6Config(ctx, opts); + } + } - return rootInfo; + return rootInfo; } -void showFSInfo( const shared_ptr &config ) -{ - shared_ptr cipher = Cipher::New( config->cipherIface, -1 ); - { - cout << format( - // xgroup(diag) - _("Filesystem cipher: \"%s\", version %i:%i:%i")) % - config->cipherIface.name().c_str() % - config->cipherIface.current() % - config->cipherIface.revision() % - config->cipherIface.age(); - // check if we support this interface.. - if(!cipher) - cout << _(" (NOT supported)\n"); - else - { - // if we're using a newer interface, show the version number - if( config->cipherIface != cipher->interface() ) - { - Interface iface = cipher->interface(); - // xgroup(diag) - cout << format(_(" (using %i:%i:%i)\n")) % - iface.current() % iface.revision() % iface.age(); - } else - cout << "\n"; - } - } - { - // xgroup(diag) - cout << format(_("Filename encoding: \"%s\", version %i:%i:%i")) % - config->nameIface.name().c_str() % - config->nameIface.current() % - config->nameIface.revision() % - config->nameIface.age(); - - // check if we support the filename encoding interface.. - shared_ptr nameCoder = NameIO::New( config->nameIface, - cipher, CipherKey() ); - if(!nameCoder) - { - // xgroup(diag) - cout << _(" (NOT supported)\n"); - } else - { - // if we're using a newer interface, show the version number - if( config->nameIface != nameCoder->interface() ) - { - Interface iface = nameCoder->interface(); - cout << format(_(" (using %i:%i:%i)\n")) % - iface.current() % iface.revision() % iface.age(); - } else - cout << "\n"; - } - } - { - cout << format(_("Key Size: %i bits")) % config->keySize; - cipher = config->getCipher(); - if(!cipher) - { - // xgroup(diag) - cout << _(" (NOT supported)\n"); - } else - cout << "\n"; - } - if(config->kdfIterations > 0 && config->salt.size() > 0) - { - cout << format(_("Using PBKDF2, with %i iterations")) % - config->kdfIterations << "\n"; - cout << format(_("Salt Size: %i bits")) % - (8*config->salt.size()) << "\n"; - } - if(config->blockMACBytes || config->blockMACRandBytes) - { - if(config->subVersion < 20040813) - { - cout << format( - // xgroup(diag) - _("Block Size: %i bytes + %i byte MAC header")) % - config->blockSize % - (config->blockMACBytes + config->blockMACRandBytes) << endl; - } else - { - // new version stores the header as part of that block size.. - cout << format( - // xgroup(diag) - _("Block Size: %i bytes, including %i byte MAC header")) % - config->blockSize % - (config->blockMACBytes + config->blockMACRandBytes) << endl; - } - } else - { - // xgroup(diag) - cout << format(_("Block Size: %i bytes")) % config->blockSize; - cout << "\n"; - } +int remountFS(EncFS_Context *ctx) { + rDebug("Attempting to reinitialize filesystem"); - if(config->uniqueIV) - { - // xgroup(diag) - cout << _("Each file contains 8 byte header with unique IV data.\n"); - } - if(config->chainedNameIV) - { - // xgroup(diag) - cout << _("Filenames encoded using IV chaining mode.\n"); - } - if(config->externalIVChaining) - { - // xgroup(diag) - cout << _("File data IV is chained to filename IV.\n"); - } - if(config->allowHoles) - { - // xgroup(diag) - cout << _("File holes passed through to ciphertext.\n"); - } - cout << "\n"; + RootPtr rootInfo = initFS(ctx, ctx->opts); + if (rootInfo) { + ctx->setRoot(rootInfo->root); + return 0; + } else { + rInfo(_("Remount failed")); + return -EACCES; + } } - -shared_ptr EncFSConfig::getCipher() const -{ - return Cipher::New( cipherIface, keySize ); -} - -void EncFSConfig::assignKeyData(const std::string &in) -{ - keyData.assign(in.data(), in.data()+in.length()); -} - -void EncFSConfig::assignKeyData(unsigned char *data, int len) -{ - keyData.assign(data, data+len); -} - -void EncFSConfig::assignSaltData(unsigned char *data, int len) -{ - salt.assign(data, data+len); -} - -unsigned char *EncFSConfig::getKeyData() const -{ - return const_cast(&keyData.front()); -} - -unsigned char *EncFSConfig::getSaltData() const -{ - return const_cast(&salt.front()); -} - -CipherKey EncFSConfig::makeKey(const char *password, int passwdLen) -{ - CipherKey userKey; - shared_ptr cipher = getCipher(); - - // if no salt is set and we're creating a new password for a new - // FS type, then initialize salt.. - if(salt.size() == 0 && kdfIterations == 0 && cfgType >= Config_V6) - { - // upgrade to using salt - salt.resize(20); - } - - if(salt.size() > 0) - { - // if iterations isn't known, then we're creating a new key, so - // randomize the salt.. - if(kdfIterations == 0 && !cipher->randomize( - getSaltData(), salt.size(), true)) - { - cout << _("Error creating salt\n"); - return userKey; - } - - userKey = cipher->newKey( password, passwdLen, - kdfIterations, desiredKDFDuration, - getSaltData(), salt.size()); - } else - { - userKey = cipher->newKey( password, passwdLen ); - } - - return userKey; -} - -CipherKey EncFSConfig::getUserKey(bool useStdin) -{ - char passBuf[MaxPassBuf]; - char *res; - - if( useStdin ) - { - res = fgets( passBuf, sizeof(passBuf), stdin ); - // Kill the trailing newline. - if(passBuf[ strlen(passBuf)-1 ] == '\n') - passBuf[ strlen(passBuf)-1 ] = '\0'; - } else - { - // xgroup(common) - res = readpassphrase( _("EncFS Password: "), - passBuf, sizeof(passBuf), RPP_ECHO_OFF ); - } - - CipherKey userKey; - if(!res) - cerr << _("Zero length password not allowed\n"); - else - userKey = makeKey(passBuf, strlen(passBuf)); - - memset( passBuf, 0, sizeof(passBuf) ); - - return userKey; -} - -std::string readPassword( int FD ) -{ - char buffer[1024]; - string result; - - while(1) - { - ssize_t rdSize = recv(FD, buffer, sizeof(buffer), 0); - - if(rdSize > 0) - { - result.append( buffer, rdSize ); - memset(buffer, 0, sizeof(buffer)); - } else - break; - } - - // chop off trailing "\n" if present.. - // This is done so that we can use standard programs like ssh-askpass - // without modification, as it returns trailing newline.. - if(!result.empty() && result[ result.length()-1 ] == '\n' ) - result.resize( result.length() -1 ); - - return result; -} - -CipherKey EncFSConfig::getUserKey( const std::string &passProg, - const std::string &rootDir ) -{ - // have a child process run the command and get the result back to us. - int fds[2], pid; - int res; - CipherKey result; - - res = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); - if(res == -1) - { - perror(_("Internal error: socketpair() failed")); - return result; - } - rDebug("getUserKey: fds = %i, %i", fds[0], fds[1]); - - pid = fork(); - if(pid == -1) - { - perror(_("Internal error: fork() failed")); - close(fds[0]); - close(fds[1]); - return result; - } - - if(pid == 0) - { - const char *argv[4]; - argv[0] = "/bin/sh"; - argv[1] = "-c"; - argv[2] = passProg.c_str(); - argv[3] = 0; - - // child process.. run the command and send output to fds[0] - close(fds[1]); // we don't use the other half.. - - // make a copy of stdout and stderr descriptors, and set an environment - // variable telling where to find them, in case a child wants it.. - int stdOutCopy = dup( STDOUT_FILENO ); - int stdErrCopy = dup( STDERR_FILENO ); - // replace STDOUT with our socket, which we'll used to receive the - // password.. - dup2( fds[0], STDOUT_FILENO ); - - // ensure that STDOUT_FILENO and stdout/stderr are not closed on exec.. - fcntl(STDOUT_FILENO, F_SETFD, 0); // don't close on exec.. - fcntl(stdOutCopy, F_SETFD, 0); - fcntl(stdErrCopy, F_SETFD, 0); - - char tmpBuf[8]; - - setenv(ENCFS_ENV_ROOTDIR, rootDir.c_str(), 1); - - snprintf(tmpBuf, sizeof(tmpBuf)-1, "%i", stdOutCopy); - setenv(ENCFS_ENV_STDOUT, tmpBuf, 1); - - snprintf(tmpBuf, sizeof(tmpBuf)-1, "%i", stdErrCopy); - setenv(ENCFS_ENV_STDERR, tmpBuf, 1); - - execvp( argv[0], (char * const *)argv ); // returns only on error.. - - perror(_("Internal error: failed to exec program")); - exit(1); - } - - close(fds[0]); - string password = readPassword(fds[1]); - close(fds[1]); - - waitpid(pid, NULL, 0); - - // convert to key.. - result = makeKey(password.c_str(), password.length()); - - // clear buffer.. - password.assign( password.length(), '\0' ); - - return result; -} - -CipherKey EncFSConfig::getNewUserKey() -{ - CipherKey userKey; - char passBuf[MaxPassBuf]; - char passBuf2[MaxPassBuf]; - - do - { - // xgroup(common) - char *res1 = readpassphrase(_("New Encfs Password: "), passBuf, - sizeof(passBuf)-1, RPP_ECHO_OFF); - // xgroup(common) - char *res2 = readpassphrase(_("Verify Encfs Password: "), passBuf2, - sizeof(passBuf2)-1, RPP_ECHO_OFF); - - if(res1 && res2 && !strcmp(passBuf, passBuf2)) - { - userKey = makeKey(passBuf, strlen(passBuf)); - } else - { - // xgroup(common) -- probably not common, but group with the others - cerr << _("Passwords did not match, please try again\n"); - } - - memset( passBuf, 0, sizeof(passBuf) ); - memset( passBuf2, 0, sizeof(passBuf2) ); - } while( !userKey ); - - return userKey; -} - -RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ) -{ - RootPtr rootInfo; - shared_ptr config(new EncFSConfig); - - if(readConfig( opts->rootDir, config ) != Config_None) - { - if(opts->reverseEncryption) - { - if (config->blockMACBytes != 0 || config->blockMACRandBytes != 0 - || config->uniqueIV || config->externalIVChaining - || config->chainedNameIV ) - { - cout << _("The configuration loaded is not compatible with --reverse\n"); - return rootInfo; - } - } - - // first, instanciate the cipher. - shared_ptr cipher = config->getCipher(); - if(!cipher) - { - rError(_("Unable to find cipher %s, version %i:%i:%i"), - config->cipherIface.name().c_str(), - config->cipherIface.current(), - config->cipherIface.revision(), - config->cipherIface.age()); - // xgroup(diag) - cout << _("The requested cipher interface is not available\n"); - return rootInfo; - } - - if(opts->delayMount) - { - rootInfo = RootPtr( new EncFS_Root ); - rootInfo->cipher = cipher; - rootInfo->root = shared_ptr(); - return rootInfo; - } - - // get user key - CipherKey userKey; - - if(opts->passwordProgram.empty()) - { - rDebug( "useStdin: %i", opts->useStdin ); - if (opts->annotate) - cerr << "$PROMPT$ passwd" << endl; - userKey = config->getUserKey( opts->useStdin ); - } else - userKey = config->getUserKey( opts->passwordProgram, opts->rootDir ); - - if(!userKey) - return rootInfo; - - rDebug("cipher key size = %i", cipher->encodedKeySize()); - // decode volume key.. - CipherKey volumeKey = cipher->readKey( - config->getKeyData(), userKey, opts->checkKey); - userKey.reset(); - - if(!volumeKey) - { - // xgroup(diag) - cout << _("Error decoding volume key, password incorrect\n"); - return rootInfo; - } - - shared_ptr nameCoder = NameIO::New( config->nameIface, - cipher, volumeKey ); - if(!nameCoder) - { - rError(_("Unable to find nameio interface %s, version %i:%i:%i"), - config->nameIface.name().c_str(), - config->nameIface.current(), - config->nameIface.revision(), - config->nameIface.age()); - // xgroup(diag) - cout << _("The requested filename coding interface is " - "not available\n"); - return rootInfo; - } - - nameCoder->setChainedNameIV( config->chainedNameIV ); - nameCoder->setReverseEncryption( opts->reverseEncryption ); - - FSConfigPtr fsConfig( new FSConfig ); - fsConfig->cipher = cipher; - fsConfig->key = volumeKey; - fsConfig->nameCoding = nameCoder; - fsConfig->config = config; - fsConfig->forceDecode = opts->forceDecode; - fsConfig->reverseEncryption = opts->reverseEncryption; - fsConfig->opts = opts; - - rootInfo = RootPtr( new EncFS_Root ); - rootInfo->cipher = cipher; - rootInfo->volumeKey = volumeKey; - rootInfo->root = shared_ptr( - new DirNode( ctx, opts->rootDir, fsConfig )); - } else - { - if(opts->createIfNotFound) - { - // creating a new encrypted filesystem - rootInfo = createV6Config( ctx, opts ); - } - } - - return rootInfo; -} - -int remountFS(EncFS_Context *ctx) -{ - rDebug("Attempting to reinitialize filesystem"); - - RootPtr rootInfo = initFS( ctx, ctx->opts ); - if(rootInfo) - { - ctx->setRoot(rootInfo->root); - return 0; - } else - { - rInfo(_("Remount failed")); - return -EACCES; - } -} - diff --git a/encfs/FileUtils.h b/encfs/FileUtils.h index a97d2dc..fdfa0cb 100644 --- a/encfs/FileUtils.h +++ b/encfs/FileUtils.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - + #ifndef _FileUtils_incl_ #define _FileUtils_incl_ @@ -27,118 +27,106 @@ #include "FSConfig.h" // true if the path points to an existing node (of any type) -bool fileExists( const char *fileName ); +bool fileExists(const char *fileName); // true if path is a directory -bool isDirectory( const char *fileName ); +bool isDirectory(const char *fileName); // true if starts with '/' -bool isAbsolutePath( const char *fileName ); +bool isAbsolutePath(const char *fileName); // pointer to just after the last '/' -const char *lastPathElement( const char *name ); +const char *lastPathElement(const char *name); -std::string parentDirectory( const std::string &path ); +std::string parentDirectory(const std::string &path); // ask the user for permission to create the directory. If they say ok, then // do it and return true. -bool userAllowMkdir(const char *dirPath, mode_t mode ); -bool userAllowMkdir(int promptno, const char *dirPath, mode_t mode ); +bool userAllowMkdir(const char *dirPath, mode_t mode); +bool userAllowMkdir(int promptno, const char *dirPath, mode_t mode); class Cipher; class DirNode; -struct EncFS_Root -{ - shared_ptr cipher; - CipherKey volumeKey; - shared_ptr root; +struct EncFS_Root { + shared_ptr cipher; + CipherKey volumeKey; + shared_ptr root; - EncFS_Root(); - ~EncFS_Root(); + EncFS_Root(); + ~EncFS_Root(); }; typedef shared_ptr RootPtr; -enum ConfigMode -{ - Config_Prompt, - Config_Standard, - Config_Paranoia -}; +enum ConfigMode { Config_Prompt, Config_Standard, Config_Paranoia }; -struct EncFS_Opts -{ - std::string rootDir; - bool createIfNotFound; // create filesystem if not found - bool idleTracking; // turn on idle monitoring of filesystem - bool mountOnDemand; // mounting on-demand - bool delayMount; // delay initial mount +struct EncFS_Opts { + std::string rootDir; + bool createIfNotFound; // create filesystem if not found + bool idleTracking; // turn on idle monitoring of filesystem + bool mountOnDemand; // mounting on-demand + bool delayMount; // delay initial mount - bool checkKey; // check crypto key decoding - bool forceDecode; // force decode on MAC block failures + bool checkKey; // check crypto key decoding + bool forceDecode; // force decode on MAC block failures - std::string passwordProgram; // path to password program (or empty) - bool useStdin; // read password from stdin rather then prompting - bool annotate; // print annotation line prompt to stderr. + std::string passwordProgram; // path to password program (or empty) + bool useStdin; // read password from stdin rather then prompting + bool annotate; // print annotation line prompt to stderr. - bool ownerCreate; // set owner of new files to caller + bool ownerCreate; // set owner of new files to caller - bool reverseEncryption; // Reverse encryption + bool reverseEncryption; // Reverse encryption - ConfigMode configMode; + ConfigMode configMode; - EncFS_Opts() - { - createIfNotFound = true; - idleTracking = false; - mountOnDemand = false; - delayMount = false; - checkKey = true; - forceDecode = false; - useStdin = false; - annotate = false; - ownerCreate = false; - reverseEncryption = false; - configMode = Config_Prompt; - } + EncFS_Opts() { + createIfNotFound = true; + idleTracking = false; + mountOnDemand = false; + delayMount = false; + checkKey = true; + forceDecode = false; + useStdin = false; + annotate = false; + ownerCreate = false; + reverseEncryption = false; + configMode = Config_Prompt; + } }; /* Read existing config file. Looks for any supported configuration version. */ -ConfigType readConfig( const std::string &rootDir, - const shared_ptr &config ); +ConfigType readConfig(const std::string &rootDir, + const shared_ptr &config); /* Save the configuration. Saves back as the same configuration type as was read from. */ -bool saveConfig( ConfigType type, const std::string &rootdir, - const shared_ptr &config ); +bool saveConfig(ConfigType type, const std::string &rootdir, + const shared_ptr &config); class EncFS_Context; -RootPtr initFS( EncFS_Context *ctx, const shared_ptr &opts ); +RootPtr initFS(EncFS_Context *ctx, const shared_ptr &opts); -RootPtr createV6Config( EncFS_Context *ctx, - const shared_ptr &opts ); +RootPtr createV6Config(EncFS_Context *ctx, const shared_ptr &opts); -void showFSInfo( const shared_ptr &config ); +void showFSInfo(const shared_ptr &config); -bool readV4Config( const char *configFile, - const shared_ptr &config, - struct ConfigInfo *); -bool writeV4Config( const char *configFile, - const shared_ptr &config); +bool readV4Config(const char *configFile, const shared_ptr &config, + struct ConfigInfo *); +bool writeV4Config(const char *configFile, + const shared_ptr &config); -bool readV5Config( const char *configFile, - const shared_ptr &config, - struct ConfigInfo *); -bool writeV5Config( const char *configFile, - const shared_ptr &config); +bool readV5Config(const char *configFile, const shared_ptr &config, + struct ConfigInfo *); +bool writeV5Config(const char *configFile, + const shared_ptr &config); -bool readV6Config( const char *configFile, - const shared_ptr &config, - struct ConfigInfo *); -bool writeV6Config( const char *configFile, - const shared_ptr &config); +bool readV6Config(const char *configFile, const shared_ptr &config, + struct ConfigInfo *); +bool writeV6Config(const char *configFile, + const shared_ptr &config); #endif diff --git a/encfs/Interface.cpp b/encfs/Interface.cpp index 110c5d3..3e78689 100644 --- a/encfs/Interface.cpp +++ b/encfs/Interface.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -17,7 +17,7 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - + #include "Interface.h" #include "ConfigVar.h" @@ -28,106 +28,58 @@ using namespace rel; using namespace rlog; -static RLogChannel * Info = DEF_CHANNEL( "info/iface", Log_Info ); +static RLogChannel *Info = DEF_CHANNEL("info/iface", Log_Info); Interface::Interface(const char *name_, int Current, int Revision, int Age) - : _name( name_ ) - , _current( Current ) - , _revision( Revision ) - , _age( Age ) -{ -} + : _name(name_), _current(Current), _revision(Revision), _age(Age) {} -Interface::Interface(const std::string &name_, int Current, - int Revision, int Age) - : _name( name_ ) - , _current( Current ) - , _revision( Revision ) - , _age( Age ) -{ -} +Interface::Interface(const std::string &name_, int Current, int Revision, + int Age) + : _name(name_), _current(Current), _revision(Revision), _age(Age) {} Interface::Interface(const Interface &src) - : _name( src._name ) - , _current( src._current ) - , _revision( src._revision ) - , _age( src._age ) -{ + : _name(src._name), + _current(src._current), + _revision(src._revision), + _age(src._age) {} + +Interface::Interface() : _current(0), _revision(0), _age(0) {} + +Interface &Interface::operator=(const Interface &src) { + _name = src._name; + _current = src._current; + _revision = src._revision; + _age = src._age; + return *this; } -Interface::Interface() - : _current( 0 ) - , _revision( 0 ) - , _age( 0 ) -{ +const std::string &Interface::name() const { return _name; } + +std::string &Interface::name() { return _name; } + +int Interface::current() const { return _current; } + +int &Interface::current() { return _current; } + +int Interface::revision() const { return _revision; } + +int &Interface::revision() { return _revision; } + +int Interface::age() const { return _age; } + +int &Interface::age() { return _age; } + +bool operator==(const Interface &A, const Interface &B) { + return (A.name() == B.name() && A.current() == B.current() && + A.revision() == B.revision() && A.age() == B.age()); } -Interface &Interface::operator = (const Interface &src) -{ - _name = src._name; - _current = src._current; - _revision = src._revision; - _age = src._age; - return *this; +bool operator!=(const Interface &A, const Interface &B) { + return (A.name() != B.name() || A.current() != B.current() || + A.revision() != B.revision() || A.age() != B.age()); } -const std::string & Interface::name() const -{ - return _name; -} - -std::string & Interface::name() -{ - return _name; -} - -int Interface::current() const -{ - return _current; -} - -int &Interface::current() -{ - return _current; -} - -int Interface::revision() const -{ - return _revision; -} - -int &Interface::revision() -{ - return _revision; -} - -int Interface::age() const -{ - return _age; -} - -int &Interface::age() -{ - return _age; -} - -bool operator == (const Interface &A, const Interface &B) -{ - return ( A.name() == B.name() - && A.current() == B.current() - && A.revision() == B.revision() - && A.age() == B.age() ); -} - -bool operator != (const Interface &A, const Interface &B) -{ - return ( A.name() != B.name() - || A.current() != B.current() - || A.revision() != B.revision() - || A.age() != B.age() ); -} - -// zero branch method of getting comparison sign.. +// zero branch method of getting comparison sign.. // tricky.. makes assumptions #if 0 static int sign( int a, int b ) @@ -139,91 +91,74 @@ static int sign( int a, int b ) } #else // simple, easy to check, unlikely to break due to unforseen events.. -static int sign( int a, int b ) -{ - if(a < b) - return 0; - else if(a == b) - return 1; - else - return 2; +static int sign(int a, int b) { + if (a < b) + return 0; + else if (a == b) + return 1; + else + return 2; } #endif -static int diffSum( const Interface &A, const Interface &B ) -{ - int cS = sign( A.current() , B.current() ); - int aS = sign( A.age(), B.age() ); - int rS = sign( A.revision(), B.revision() ); +static int diffSum(const Interface &A, const Interface &B) { + int cS = sign(A.current(), B.current()); + int aS = sign(A.age(), B.age()); + int rS = sign(A.revision(), B.revision()); - return (cS * 3 + aS) * 3 + rS; + return (cS * 3 + aS) * 3 + rS; } const int EqualVersion = (1 * 3 + 1) * 3 + 1; -bool Interface::implements(const Interface &B) const -{ - rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)", - name().c_str(), current(), revision(), age(), - B.name().c_str(), B.current(), B.revision(), B.age()); +bool Interface::implements(const Interface &B) const { + rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)", name().c_str(), + current(), revision(), age(), B.name().c_str(), B.current(), + B.revision(), B.age()); - if( name() != B.name() ) - return false; + if (name() != B.name()) return false; - int currentDiff = current() - B.current(); - return ( currentDiff >= 0 && currentDiff <= age() ); + int currentDiff = current() - B.current(); + return (currentDiff >= 0 && currentDiff <= age()); } - -bool operator < (const Interface &A, const Interface &B) -{ - if( A.name() == B.name() ) - { - return ( diffSum(A,B) < EqualVersion ); - } else - return A.name() < B.name(); +bool operator<(const Interface &A, const Interface &B) { + if (A.name() == B.name()) { + return (diffSum(A, B) < EqualVersion); + } else + return A.name() < B.name(); } -bool operator > (const Interface &A, const Interface &B) -{ - if( A.name() == B.name() ) - { - return ( diffSum(A,B) > EqualVersion ); - } else - return A.name() < B.name(); +bool operator>(const Interface &A, const Interface &B) { + if (A.name() == B.name()) { + return (diffSum(A, B) > EqualVersion); + } else + return A.name() < B.name(); } -bool operator <= (const Interface &A, const Interface &B) -{ - if( A.name() == B.name() ) - { - return ( diffSum(A,B) <= EqualVersion ); - } else - return A.name() < B.name(); +bool operator<=(const Interface &A, const Interface &B) { + if (A.name() == B.name()) { + return (diffSum(A, B) <= EqualVersion); + } else + return A.name() < B.name(); } -bool operator >= (const Interface &A, const Interface &B) -{ - if( A.name() == B.name() ) - { - return ( diffSum(A,B) >= EqualVersion ); - } else - return A.name() < B.name(); +bool operator>=(const Interface &A, const Interface &B) { + if (A.name() == B.name()) { + return (diffSum(A, B) >= EqualVersion); + } else + return A.name() < B.name(); } - -ConfigVar & operator << (ConfigVar &dst, const rel::Interface &iface) -{ - dst << iface.name() << iface.current() << iface.revision() << iface.age(); - return dst; +ConfigVar &operator<<(ConfigVar &dst, const rel::Interface &iface) { + dst << iface.name() << iface.current() << iface.revision() << iface.age(); + return dst; } -const ConfigVar & operator >> (const ConfigVar &src, Interface &iface) -{ - src >> iface.name(); - src >> iface.current(); - src >> iface.revision(); - src >> iface.age(); - return src; +const ConfigVar &operator>>(const ConfigVar &src, Interface &iface) { + src >> iface.name(); + src >> iface.current(); + src >> iface.revision(); + src >> iface.age(); + return src; } - diff --git a/encfs/Interface.h b/encfs/Interface.h index 6f5904a..7c950db 100644 --- a/encfs/Interface.h +++ b/encfs/Interface.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -26,62 +26,57 @@ class ConfigVar; // part of REL library.. -namespace rel -{ +namespace rel { - class Interface - { - public: +class Interface { + public: + /*! + Version numbers as described by libtool: info://libtool/versioning + Current - the most recent interface api that is implemented. + Revision - the implementation number of the current interface. + Age - the difference between the newest and oldest interfaces that + are implemented. + */ + Interface(const char *name, int Current, int Revision, int Age); + Interface(const std::string &name, int Current, int Revision, int Age); + Interface(const Interface &src); + Interface(); - /*! - Version numbers as described by libtool: info://libtool/versioning - Current - the most recent interface api that is implemented. - Revision - the implementation number of the current interface. - Age - the difference between the newest and oldest interfaces that - are implemented. - */ - Interface( const char *name, int Current, int Revision, int Age ); - Interface( const std::string &name, int Current, int Revision, int Age); - Interface(const Interface &src); - Interface(); + // check if we implement the interface described by B. + // Note that A.implements(B) is not the same as B.implements(A) + // This checks the current() version and age() against B.current() for + // compatibility. Even if A.implements(B) is true, B > A may also be + // true, meaning B is a newer revision of the interface then A. + bool implements(const Interface &dst) const; - // check if we implement the interface described by B. - // Note that A.implements(B) is not the same as B.implements(A) - // This checks the current() version and age() against B.current() for - // compatibility. Even if A.implements(B) is true, B > A may also be - // true, meaning B is a newer revision of the interface then A. - bool implements( const Interface &dst ) const; + const std::string &name() const; + int current() const; + int revision() const; + int age() const; - const std::string &name() const; - int current() const; - int revision() const; - int age() const; - - std::string &name(); - int ¤t(); - int &revision(); - int &age(); + std::string &name(); + int ¤t(); + int &revision(); + int &age(); - Interface &operator = ( const Interface &src ); + Interface &operator=(const Interface &src); - private: - std::string _name; - int _current; - int _revision; - int _age; - }; - + private: + std::string _name; + int _current; + int _revision; + int _age; +}; } -ConfigVar & operator << (ConfigVar &, const rel::Interface &); -const ConfigVar & operator >> (const ConfigVar &, rel::Interface &); - -bool operator < (const rel::Interface &A, const rel::Interface &B); -bool operator > (const rel::Interface &A, const rel::Interface &B); -bool operator <= (const rel::Interface &A, const rel::Interface &B); -bool operator >= (const rel::Interface &A, const rel::Interface &B); -bool operator == (const rel::Interface &A, const rel::Interface &B); -bool operator != (const rel::Interface &A, const rel::Interface &B); +ConfigVar &operator<<(ConfigVar &, const rel::Interface &); +const ConfigVar &operator>>(const ConfigVar &, rel::Interface &); + +bool operator<(const rel::Interface &A, const rel::Interface &B); +bool operator>(const rel::Interface &A, const rel::Interface &B); +bool operator<=(const rel::Interface &A, const rel::Interface &B); +bool operator>=(const rel::Interface &A, const rel::Interface &B); +bool operator==(const rel::Interface &A, const rel::Interface &B); +bool operator!=(const rel::Interface &A, const rel::Interface &B); #endif - diff --git a/encfs/MACFileIO.cpp b/encfs/MACFileIO.cpp index b5ac807..d432a02 100644 --- a/encfs/MACFileIO.cpp +++ b/encfs/MACFileIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -51,66 +51,45 @@ static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info); // static rel::Interface MACFileIO_iface("FileIO/MAC", 2, 1, 0); -int dataBlockSize(const FSConfigPtr &cfg) -{ - return cfg->config->blockSize - - cfg->config->blockMACBytes - - cfg->config->blockMACRandBytes; +int dataBlockSize(const FSConfigPtr &cfg) { + return cfg->config->blockSize - cfg->config->blockMACBytes - + cfg->config->blockMACRandBytes; } -MACFileIO::MACFileIO( const shared_ptr &_base, - const FSConfigPtr &cfg ) - : BlockFileIO( dataBlockSize( cfg ), cfg ) - , base( _base ) - , cipher( cfg->cipher ) - , key( cfg->key ) - , macBytes( cfg->config->blockMACBytes ) - , randBytes( cfg->config->blockMACRandBytes ) - , warnOnly( cfg->opts->forceDecode ) -{ - rAssert( macBytes >= 0 && macBytes <= 8 ); - rAssert( randBytes >= 0 ); - rLog(Info, "fs block size = %i, macBytes = %i, randBytes = %i", - cfg->config->blockSize, - cfg->config->blockMACBytes, - cfg->config->blockMACRandBytes); +MACFileIO::MACFileIO(const shared_ptr &_base, const FSConfigPtr &cfg) + : BlockFileIO(dataBlockSize(cfg), cfg), + base(_base), + cipher(cfg->cipher), + key(cfg->key), + macBytes(cfg->config->blockMACBytes), + randBytes(cfg->config->blockMACRandBytes), + warnOnly(cfg->opts->forceDecode) { + rAssert(macBytes >= 0 && macBytes <= 8); + rAssert(randBytes >= 0); + rLog(Info, "fs block size = %i, macBytes = %i, randBytes = %i", + cfg->config->blockSize, cfg->config->blockMACBytes, + cfg->config->blockMACRandBytes); } -MACFileIO::~MACFileIO() -{ +MACFileIO::~MACFileIO() {} + +rel::Interface MACFileIO::interface() const { return MACFileIO_iface; } + +int MACFileIO::open(int flags) { return base->open(flags); } + +void MACFileIO::setFileName(const char *fileName) { + base->setFileName(fileName); } -rel::Interface MACFileIO::interface() const -{ - return MACFileIO_iface; -} +const char *MACFileIO::getFileName() const { return base->getFileName(); } -int MACFileIO::open( int flags ) -{ - return base->open( flags ); -} +bool MACFileIO::setIV(uint64_t iv) { return base->setIV(iv); } -void MACFileIO::setFileName( const char *fileName ) -{ - base->setFileName( fileName ); -} - -const char *MACFileIO::getFileName() const -{ - return base->getFileName(); -} - -bool MACFileIO::setIV( uint64_t iv ) -{ - return base->setIV( iv ); -} - -inline static off_t roundUpDivide( off_t numerator, int denominator ) -{ - // integer arithmetic always rounds down, so we can round up by adding - // enough so that any value other then a multiple of denominator gets - // rouned to the next highest value. - return ( numerator + denominator - 1 ) / denominator; +inline static off_t roundUpDivide(off_t numerator, int denominator) { + // integer arithmetic always rounds down, so we can round up by adding + // enough so that any value other then a multiple of denominator gets + // rouned to the next highest value. + return (numerator + denominator - 1) / denominator; } // Convert from a location in the raw file to a location when MAC headers are @@ -123,10 +102,9 @@ inline static off_t roundUpDivide( off_t numerator, int denominator ) // ... blockNum = 1 // ... partialBlock = 0 // ... adjLoc = 1 * blockSize -static off_t locWithHeader( off_t offset, int blockSize, int headerSize ) -{ - off_t blockNum = roundUpDivide( offset , blockSize - headerSize ); - return offset + blockNum * headerSize; +static off_t locWithHeader(off_t offset, int blockSize, int headerSize) { + off_t blockNum = roundUpDivide(offset, blockSize - headerSize); + return offset + blockNum * headerSize; } // convert from a given location in the stream containing headers, and return a @@ -134,175 +112,149 @@ static off_t locWithHeader( off_t offset, int blockSize, int headerSize ) // The output value will always be less then the input value, because the // headers are stored at the beginning of the block, so even the first data is // offset by the size of the header. -static off_t locWithoutHeader( off_t offset, int blockSize, int headerSize ) -{ - off_t blockNum = roundUpDivide( offset , blockSize ); - return offset - blockNum * headerSize; +static off_t locWithoutHeader(off_t offset, int blockSize, int headerSize) { + off_t blockNum = roundUpDivide(offset, blockSize); + return offset - blockNum * headerSize; } -int MACFileIO::getAttr( struct stat *stbuf ) const -{ - int res = base->getAttr( stbuf ); +int MACFileIO::getAttr(struct stat *stbuf) const { + int res = base->getAttr(stbuf); - if(res == 0 && S_ISREG(stbuf->st_mode)) - { - // have to adjust size field.. - int headerSize = macBytes + randBytes; - int bs = blockSize() + headerSize; - stbuf->st_size = locWithoutHeader( stbuf->st_size, bs, headerSize ); - } - - return res; -} - -off_t MACFileIO::getSize() const -{ - // adjust the size to hide the header overhead we tack on.. + if (res == 0 && S_ISREG(stbuf->st_mode)) { + // have to adjust size field.. int headerSize = macBytes + randBytes; int bs = blockSize() + headerSize; + stbuf->st_size = locWithoutHeader(stbuf->st_size, bs, headerSize); + } - off_t size = base->getSize(); - if(size > 0) - size = locWithoutHeader( size, bs, headerSize ); - - return size; + return res; } -ssize_t MACFileIO::readOneBlock( const IORequest &req ) const -{ - int headerSize = macBytes + randBytes; +off_t MACFileIO::getSize() const { + // adjust the size to hide the header overhead we tack on.. + int headerSize = macBytes + randBytes; + int bs = blockSize() + headerSize; - int bs = blockSize() + headerSize; + off_t size = base->getSize(); + if (size > 0) size = locWithoutHeader(size, bs, headerSize); - MemBlock mb = MemoryPool::allocate( bs ); + return size; +} - IORequest tmp; - tmp.offset = locWithHeader( req.offset, bs, headerSize ); - tmp.data = mb.data; - tmp.dataLen = headerSize + req.dataLen; +ssize_t MACFileIO::readOneBlock(const IORequest &req) const { + int headerSize = macBytes + randBytes; - // get the data from the base FileIO layer - ssize_t readSize = base->read( tmp ); + int bs = blockSize() + headerSize; - // don't store zeros if configured for zero-block pass-through - bool skipBlock = true; - if( _allowHoles ) - { - for(int i=0; i 0) - skipBlock = false; + MemBlock mb = MemoryPool::allocate(bs); - if(readSize > headerSize) - { - if(!skipBlock) - { - // At this point the data has been decoded. So, compute the MAC of - // the block and check against the checksum stored in the header.. - uint64_t mac = cipher->MAC_64( tmp.data + macBytes, - readSize - macBytes, key ); + IORequest tmp; + tmp.offset = locWithHeader(req.offset, bs, headerSize); + tmp.data = mb.data; + tmp.dataLen = headerSize + req.dataLen; - // Constant time comparision to prevent timing attacks - unsigned char fail = 0; - for(int i=0; i>= 8) - { - int test = mac & 0xff; - int stored = tmp.data[i]; + // get the data from the base FileIO layer + ssize_t readSize = base->read(tmp); - fail |= (test ^ stored); - } + // don't store zeros if configured for zero-block pass-through + bool skipBlock = true; + if (_allowHoles) { + for (int i = 0; i < readSize; ++i) + if (tmp.data[i] != 0) { + skipBlock = false; + break; + } + } else if (macBytes > 0) + skipBlock = false; - if( fail > 0 ) - { - // uh oh.. - long blockNum = req.offset / bs; - rWarning(_("MAC comparison failure in block %li"), - blockNum); - if( !warnOnly ) - { - MemoryPool::release( mb ); - throw ERROR( - _("MAC comparison failure, refusing to read")); - } - } + if (readSize > headerSize) { + if (!skipBlock) { + // At this point the data has been decoded. So, compute the MAC of + // the block and check against the checksum stored in the header.. + uint64_t mac = + cipher->MAC_64(tmp.data + macBytes, readSize - macBytes, key); + + // Constant time comparision to prevent timing attacks + unsigned char fail = 0; + for (int i = 0; i < macBytes; ++i, mac >>= 8) { + int test = mac & 0xff; + int stored = tmp.data[i]; + + fail |= (test ^ stored); + } + + if (fail > 0) { + // uh oh.. + long blockNum = req.offset / bs; + rWarning(_("MAC comparison failure in block %li"), blockNum); + if (!warnOnly) { + MemoryPool::release(mb); + throw ERROR(_("MAC comparison failure, refusing to read")); } - - // now copy the data to the output buffer - readSize -= headerSize; - memcpy( req.data, tmp.data + headerSize, readSize ); - } else - { - rDebug("readSize %i at offset %" PRIi64, (int)readSize, req.offset); - if(readSize > 0) - readSize = 0; + } } - MemoryPool::release( mb ); + // now copy the data to the output buffer + readSize -= headerSize; + memcpy(req.data, tmp.data + headerSize, readSize); + } else { + rDebug("readSize %i at offset %" PRIi64, (int)readSize, req.offset); + if (readSize > 0) readSize = 0; + } - return readSize; + MemoryPool::release(mb); + + return readSize; } -bool MACFileIO::writeOneBlock( const IORequest &req ) -{ - int headerSize = macBytes + randBytes; +bool MACFileIO::writeOneBlock(const IORequest &req) { + int headerSize = macBytes + randBytes; - int bs = blockSize() + headerSize; + int bs = blockSize() + headerSize; - // we have the unencrypted data, so we need to attach a header to it. - MemBlock mb = MemoryPool::allocate( bs ); + // we have the unencrypted data, so we need to attach a header to it. + MemBlock mb = MemoryPool::allocate(bs); - IORequest newReq; - newReq.offset = locWithHeader( req.offset, bs, headerSize ); - newReq.data = mb.data; - newReq.dataLen = headerSize + req.dataLen; + IORequest newReq; + newReq.offset = locWithHeader(req.offset, bs, headerSize); + newReq.data = mb.data; + newReq.dataLen = headerSize + req.dataLen; - memset( newReq.data, 0, headerSize ); - memcpy( newReq.data + headerSize, req.data, req.dataLen ); - if(randBytes > 0) - { - if(!cipher->randomize( newReq.data+macBytes, randBytes, false )) - return false; + memset(newReq.data, 0, headerSize); + memcpy(newReq.data + headerSize, req.data, req.dataLen); + if (randBytes > 0) { + if (!cipher->randomize(newReq.data + macBytes, randBytes, false)) + return false; + } + + if (macBytes > 0) { + // compute the mac (which includes the random data) and fill it in + uint64_t mac = + cipher->MAC_64(newReq.data + macBytes, req.dataLen + randBytes, key); + + for (int i = 0; i < macBytes; ++i) { + newReq.data[i] = mac & 0xff; + mac >>= 8; } + } - if(macBytes > 0) - { - // compute the mac (which includes the random data) and fill it in - uint64_t mac = cipher->MAC_64( newReq.data+macBytes, - req.dataLen + randBytes, key ); + // now, we can let the next level have it.. + bool ok = base->write(newReq); - for(int i=0; i>= 8; - } - } + MemoryPool::release(mb); - // now, we can let the next level have it.. - bool ok = base->write( newReq ); - - MemoryPool::release( mb ); - - return ok; + return ok; } -int MACFileIO::truncate( off_t size ) -{ - int headerSize = macBytes + randBytes; - int bs = blockSize() + headerSize; +int MACFileIO::truncate(off_t size) { + int headerSize = macBytes + randBytes; + int bs = blockSize() + headerSize; - int res = BlockFileIO::truncateBase( size, 0 ); + int res = BlockFileIO::truncateBase(size, 0); - if(res == 0) - base->truncate( locWithHeader( size, bs, headerSize ) ); + if (res == 0) base->truncate(locWithHeader(size, bs, headerSize)); - return res; + return res; } -bool MACFileIO::isWritable() const -{ - return base->isWritable(); -} +bool MACFileIO::isWritable() const { return base->isWritable(); } diff --git a/encfs/MACFileIO.h b/encfs/MACFileIO.h index 26921ec..1e5666d 100644 --- a/encfs/MACFileIO.h +++ b/encfs/MACFileIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -24,44 +24,41 @@ #include "BlockFileIO.h" #include "Cipher.h" -class MACFileIO : public BlockFileIO -{ -public: - /* - If warnOnlyMode is enabled, then a MAC comparison failure will only - result in a warning message from encfs -- the garbled data will still - be made available.. - */ - MACFileIO( const shared_ptr &base, - const FSConfigPtr &cfg ); - MACFileIO(); - virtual ~MACFileIO(); +class MACFileIO : public BlockFileIO { + public: + /* + If warnOnlyMode is enabled, then a MAC comparison failure will only + result in a warning message from encfs -- the garbled data will still + be made available.. + */ + MACFileIO(const shared_ptr &base, const FSConfigPtr &cfg); + MACFileIO(); + virtual ~MACFileIO(); - virtual rel::Interface interface() const; + virtual rel::Interface interface() const; - virtual void setFileName( const char *fileName ); - virtual const char *getFileName() const; - virtual bool setIV( uint64_t iv ); + virtual void setFileName(const char *fileName); + virtual const char *getFileName() const; + virtual bool setIV(uint64_t iv); - virtual int open( int flags ); - virtual int getAttr( struct stat *stbuf ) const; - virtual off_t getSize() const; + virtual int open(int flags); + virtual int getAttr(struct stat *stbuf) const; + virtual off_t getSize() const; - virtual int truncate( off_t size ); + virtual int truncate(off_t size); - virtual bool isWritable() const; + virtual bool isWritable() const; -private: - virtual ssize_t readOneBlock( const IORequest &req ) const; - virtual bool writeOneBlock( const IORequest &req ); + private: + virtual ssize_t readOneBlock(const IORequest &req) const; + virtual bool writeOneBlock(const IORequest &req); - shared_ptr base; - shared_ptr cipher; - CipherKey key; - int macBytes; - int randBytes; - bool warnOnly; + shared_ptr base; + shared_ptr cipher; + CipherKey key; + int macBytes; + int randBytes; + bool warnOnly; }; #endif - diff --git a/encfs/MemoryPool.cpp b/encfs/MemoryPool.cpp index 74159f3..e19d5f4 100644 --- a/encfs/MemoryPool.cpp +++ b/encfs/MemoryPool.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -30,116 +30,101 @@ #ifdef HAVE_VALGRIND_MEMCHECK_H #include #else -#define VALGRIND_MAKE_MEM_NOACCESS( a, b ) -#define VALGRIND_MAKE_MEM_UNDEFINED( a, b ) +#define VALGRIND_MAKE_MEM_NOACCESS(a, b) +#define VALGRIND_MAKE_MEM_UNDEFINED(a, b) #endif using namespace rlog; -# include -#define BLOCKDATA( BLOCK ) (unsigned char*)BLOCK->data->data +#include +#define BLOCKDATA(BLOCK) (unsigned char *) BLOCK->data->data - -struct BlockList -{ - BlockList *next; - int size; - BUF_MEM *data; +struct BlockList { + BlockList *next; + int size; + BUF_MEM *data; }; -static BlockList *allocBlock( int size ) -{ - BlockList *block = new BlockList; - block->size = size; - block->data = BUF_MEM_new( ); - BUF_MEM_grow( block->data, size ); - VALGRIND_MAKE_MEM_NOACCESS( block->data->data, block->data->max ); +static BlockList *allocBlock(int size) { + BlockList *block = new BlockList; + block->size = size; + block->data = BUF_MEM_new(); + BUF_MEM_grow(block->data, size); + VALGRIND_MAKE_MEM_NOACCESS(block->data->data, block->data->max); - return block; + return block; } -static void freeBlock( BlockList *el ) -{ - VALGRIND_MAKE_MEM_UNDEFINED( el->data->data, el->data->max ); - BUF_MEM_free( el->data ); - - delete el; +static void freeBlock(BlockList *el) { + VALGRIND_MAKE_MEM_UNDEFINED(el->data->data, el->data->max); + BUF_MEM_free(el->data); + + delete el; } static pthread_mutex_t gMPoolMutex = PTHREAD_MUTEX_INITIALIZER; static BlockList *gMemPool = NULL; +MemBlock MemoryPool::allocate(int size) { + pthread_mutex_lock(&gMPoolMutex); + BlockList *parent = NULL; + BlockList *block = gMemPool; + // check if we already have a large enough block available.. + while (block != NULL && block->size < size) { + parent = block; + block = block->next; + } -MemBlock MemoryPool::allocate( int size ) -{ - pthread_mutex_lock( &gMPoolMutex ); + // unlink block from list + if (block) { + if (!parent) + gMemPool = block->next; + else + parent->next = block->next; + } + pthread_mutex_unlock(&gMPoolMutex); - BlockList *parent = NULL; - BlockList *block = gMemPool; - // check if we already have a large enough block available.. - while(block != NULL && block->size < size) - { - parent = block; - block = block->next; - } + if (!block) block = allocBlock(size); + block->next = NULL; - // unlink block from list - if(block) - { - if(!parent) - gMemPool = block->next; - else - parent->next = block->next; - } - pthread_mutex_unlock( &gMPoolMutex ); + MemBlock result; + result.data = BLOCKDATA(block); + result.internalData = block; - if(!block) - block = allocBlock( size ); - block->next = NULL; + VALGRIND_MAKE_MEM_UNDEFINED(result.data, size); - MemBlock result; - result.data = BLOCKDATA(block); - result.internalData = block; - - VALGRIND_MAKE_MEM_UNDEFINED( result.data, size ); - - return result; + return result; } -void MemoryPool::release( const MemBlock &mb ) -{ - pthread_mutex_lock( &gMPoolMutex ); +void MemoryPool::release(const MemBlock &mb) { + pthread_mutex_lock(&gMPoolMutex); - BlockList *block = (BlockList*)mb.internalData; + BlockList *block = (BlockList *)mb.internalData; - // just to be sure there's nothing important left in buffers.. - VALGRIND_MAKE_MEM_UNDEFINED( block->data->data, block->size ); - memset( BLOCKDATA(block) , 0, block->size); - VALGRIND_MAKE_MEM_NOACCESS( block->data->data, block->data->max ); + // just to be sure there's nothing important left in buffers.. + VALGRIND_MAKE_MEM_UNDEFINED(block->data->data, block->size); + memset(BLOCKDATA(block), 0, block->size); + VALGRIND_MAKE_MEM_NOACCESS(block->data->data, block->data->max); - block->next = gMemPool; - gMemPool = block; + block->next = gMemPool; + gMemPool = block; - pthread_mutex_unlock( &gMPoolMutex ); + pthread_mutex_unlock(&gMPoolMutex); } -void MemoryPool::destroyAll() -{ - pthread_mutex_lock( &gMPoolMutex ); - - BlockList *block = gMemPool; - gMemPool = NULL; - - pthread_mutex_unlock( &gMPoolMutex ); +void MemoryPool::destroyAll() { + pthread_mutex_lock(&gMPoolMutex); - while(block != NULL) - { - BlockList *next = block->next; + BlockList *block = gMemPool; + gMemPool = NULL; - freeBlock( block ); - block = next; - } + pthread_mutex_unlock(&gMPoolMutex); + + while (block != NULL) { + BlockList *next = block->next; + + freeBlock(block); + block = next; + } } - - diff --git a/encfs/MemoryPool.h b/encfs/MemoryPool.h index b09cbf0..657c689 100644 --- a/encfs/MemoryPool.h +++ b/encfs/MemoryPool.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -21,20 +21,15 @@ #ifndef _MemoryPool_incl_ #define _MemoryPool_incl_ +struct MemBlock { + unsigned char *data; -struct MemBlock -{ - unsigned char *data; + void *internalData; - void *internalData; - - MemBlock(); + MemBlock(); }; -inline MemBlock::MemBlock() - : data(0), internalData(0) -{ -} +inline MemBlock::MemBlock() : data(0), internalData(0) {} /* Memory Pool for fixed sized objects. @@ -45,12 +40,10 @@ inline MemBlock::MemBlock() unsigned char *buffer = mb.data; MemoryPool::release( mb ); */ -namespace MemoryPool -{ - MemBlock allocate( int size ); - void release( const MemBlock &el ); - void destroyAll(); +namespace MemoryPool { +MemBlock allocate(int size); +void release(const MemBlock &el); +void destroyAll(); } #endif - diff --git a/encfs/Mutex.h b/encfs/Mutex.h index 318c56a..abb73b4 100644 --- a/encfs/Mutex.h +++ b/encfs/Mutex.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -23,43 +23,33 @@ #include -namespace rel -{ +namespace rel { - class Lock - { - public: - Lock( pthread_mutex_t &mutex ); - ~Lock(); +class Lock { + public: + Lock(pthread_mutex_t &mutex); + ~Lock(); - // leave the lock as it is. When the Lock wrapper is destroyed, it - // will do nothing with the pthread mutex. - void leave(); + // leave the lock as it is. When the Lock wrapper is destroyed, it + // will do nothing with the pthread mutex. + void leave(); - private: - Lock(const Lock &src); // not allowed - Lock &operator = (const Lock &src); // not allowed + private: + Lock(const Lock &src); // not allowed + Lock &operator=(const Lock &src); // not allowed - pthread_mutex_t *_mutex; - }; + pthread_mutex_t *_mutex; +}; - inline Lock::Lock( pthread_mutex_t &mutex ) - : _mutex( &mutex ) - { - pthread_mutex_lock( _mutex ); - } +inline Lock::Lock(pthread_mutex_t &mutex) : _mutex(&mutex) { + pthread_mutex_lock(_mutex); +} - inline Lock::~Lock( ) - { - if(_mutex) - pthread_mutex_unlock( _mutex ); - } +inline Lock::~Lock() { + if (_mutex) pthread_mutex_unlock(_mutex); +} - inline void Lock::leave() - { - _mutex = 0; - } +inline void Lock::leave() { _mutex = 0; } } #endif - diff --git a/encfs/NameIO.cpp b/encfs/NameIO.cpp index 148abe4..35ed038 100644 --- a/encfs/NameIO.cpp +++ b/encfs/NameIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -39,314 +39,250 @@ using namespace rel; using namespace rlog; #define REF_MODULE(TYPE) \ - if(!TYPE::Enabled() ) \ - cerr << "referenceModule: should never happen\n"; + if (!TYPE::Enabled()) cerr << "referenceModule: should never happen\n"; -static -void AddSymbolReferences() -{ - REF_MODULE(BlockNameIO) - REF_MODULE(StreamNameIO) - REF_MODULE(NullNameIO) +static void AddSymbolReferences() { + REF_MODULE(BlockNameIO) + REF_MODULE(StreamNameIO) + REF_MODULE(NullNameIO) } - -struct NameIOAlg -{ - bool hidden; - NameIO::Constructor constructor; - string description; - Interface iface; +struct NameIOAlg { + bool hidden; + NameIO::Constructor constructor; + string description; + Interface iface; }; -typedef multimap< string, NameIOAlg > NameIOMap_t; +typedef multimap NameIOMap_t; static NameIOMap_t *gNameIOMap = 0; +list NameIO::GetAlgorithmList(bool includeHidden) { + AddSymbolReferences(); -list< NameIO::Algorithm > -NameIO::GetAlgorithmList( bool includeHidden ) -{ - AddSymbolReferences(); + list result; + if (gNameIOMap) { + NameIOMap_t::const_iterator it; + NameIOMap_t::const_iterator end = gNameIOMap->end(); + for (it = gNameIOMap->begin(); it != end; ++it) { + if (includeHidden || !it->second.hidden) { + Algorithm tmp; + tmp.name = it->first; + tmp.description = it->second.description; + tmp.iface = it->second.iface; - list< Algorithm > result; - if(gNameIOMap) - { - NameIOMap_t::const_iterator it; - NameIOMap_t::const_iterator end = gNameIOMap->end(); - for(it = gNameIOMap->begin(); it != end; ++it) - { - if(includeHidden || !it->second.hidden) - { - Algorithm tmp; - tmp.name = it->first; - tmp.description = it->second.description; - tmp.iface = it->second.iface; - - result.push_back( tmp ); - } - } + result.push_back(tmp); + } } + } - return result; + return result; } -bool NameIO::Register( const char *name, const char *description, - const Interface &iface, Constructor constructor, - bool hidden ) -{ - if( !gNameIOMap ) - gNameIOMap = new NameIOMap_t; +bool NameIO::Register(const char *name, const char *description, + const Interface &iface, Constructor constructor, + bool hidden) { + if (!gNameIOMap) gNameIOMap = new NameIOMap_t; - NameIOAlg alg; - alg.hidden = hidden; - alg.constructor = constructor; - alg.description = description; - alg.iface = iface; + NameIOAlg alg; + alg.hidden = hidden; + alg.constructor = constructor; + alg.description = description; + alg.iface = iface; - gNameIOMap->insert( make_pair( string(name), alg )); - return true; + gNameIOMap->insert(make_pair(string(name), alg)); + return true; } -shared_ptr NameIO::New( const string &name, - const shared_ptr &cipher, const CipherKey &key) -{ - shared_ptr result; - if(gNameIOMap) - { - NameIOMap_t::const_iterator it = gNameIOMap->find( name ); - if(it != gNameIOMap->end()) - { - Constructor fn = it->second.constructor; - result = (*fn)( it->second.iface, cipher, key ); - } +shared_ptr NameIO::New(const string &name, + const shared_ptr &cipher, + const CipherKey &key) { + shared_ptr result; + if (gNameIOMap) { + NameIOMap_t::const_iterator it = gNameIOMap->find(name); + if (it != gNameIOMap->end()) { + Constructor fn = it->second.constructor; + result = (*fn)(it->second.iface, cipher, key); } - return result; + } + return result; } -shared_ptr NameIO::New( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key ) -{ - shared_ptr result; - if(gNameIOMap) - { - NameIOMap_t::const_iterator it; - NameIOMap_t::const_iterator end = gNameIOMap->end(); - for(it = gNameIOMap->begin(); it != end; ++it) - { - if( it->second.iface.implements( iface )) - { - Constructor fn = it->second.constructor; - result = (*fn)( iface, cipher, key ); - break; - } - } +shared_ptr NameIO::New(const Interface &iface, + const shared_ptr &cipher, + const CipherKey &key) { + shared_ptr result; + if (gNameIOMap) { + NameIOMap_t::const_iterator it; + NameIOMap_t::const_iterator end = gNameIOMap->end(); + for (it = gNameIOMap->begin(); it != end; ++it) { + if (it->second.iface.implements(iface)) { + Constructor fn = it->second.constructor; + result = (*fn)(iface, cipher, key); + break; + } } - return result; + } + return result; } +NameIO::NameIO() : chainedNameIV(false), reverseEncryption(false) {} +NameIO::~NameIO() {} -NameIO::NameIO() - : chainedNameIV( false ), reverseEncryption( false ) -{ -} +void NameIO::setChainedNameIV(bool enable) { chainedNameIV = enable; } -NameIO::~NameIO() -{ -} +bool NameIO::getChainedNameIV() const { return chainedNameIV; } -void NameIO::setChainedNameIV( bool enable ) -{ - chainedNameIV = enable; -} +void NameIO::setReverseEncryption(bool enable) { reverseEncryption = enable; } -bool NameIO::getChainedNameIV() const -{ - return chainedNameIV; -} +bool NameIO::getReverseEncryption() const { return reverseEncryption; } -void NameIO::setReverseEncryption( bool enable ) -{ - reverseEncryption = enable; -} +std::string NameIO::recodePath(const char *path, + int (NameIO::*_length)(int) const, + int (NameIO::*_code)(const char *, int, + uint64_t *, char *) const, + uint64_t *iv) const { + string output; -bool NameIO::getReverseEncryption() const -{ - return reverseEncryption; -} + while (*path) { + if (*path == '/') { + if (!output.empty()) // don't start the string with '/' + output += '/'; + ++path; + } else { + bool isDotFile = (*path == '.'); + const char *next = strchr(path, '/'); + int len = next ? next - path : strlen(path); + // at this point we know that len > 0 + if (isDotFile && (path[len - 1] == '.') && (len <= 2)) { + output.append(len, '.'); // append [len] copies of '.' + path += len; + continue; + } -std::string NameIO::recodePath( const char *path, - int (NameIO::*_length)(int) const, - int (NameIO::*_code)(const char*, int, uint64_t *, char*) const, - uint64_t *iv ) const -{ - string output; + // figure out buffer sizes + int approxLen = (this->*_length)(len); + if (approxLen <= 0) throw ERROR("Filename too small to decode"); - while( *path ) - { - if( *path == '/' ) - { - if( !output.empty() ) // don't start the string with '/' - output += '/'; - ++path; - } else - { - bool isDotFile = (*path == '.'); - const char *next = strchr( path, '/' ); - int len = next ? next - path : strlen( path ); + BUFFER_INIT(codeBuf, 32, (unsigned int)approxLen + 1) - // at this point we know that len > 0 - if( isDotFile && (path[len-1] == '.') && (len <= 2) ) - { - output.append(len, '.'); // append [len] copies of '.' - path += len; - continue; - } + // code the name + int codedLen = (this->*_code)(path, len, iv, codeBuf); + rAssert(codedLen <= approxLen); + rAssert(codeBuf[codedLen] == '\0'); + path += len; - // figure out buffer sizes - int approxLen = (this->*_length)( len ); - if(approxLen <= 0) - throw ERROR("Filename too small to decode"); + // append result to string + output += (char *)codeBuf; - BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) - - // code the name - int codedLen = (this->*_code)( path, len, iv, codeBuf ); - rAssert( codedLen <= approxLen ); - rAssert( codeBuf[codedLen] == '\0' ); - path += len; - - // append result to string - output += (char*)codeBuf; - - BUFFER_RESET( codeBuf ) - } + BUFFER_RESET(codeBuf) } + } - return output; + return output; } -std::string NameIO::encodePath( const char *plaintextPath ) const -{ - uint64_t iv = 0; - return encodePath( plaintextPath, &iv); +std::string NameIO::encodePath(const char *plaintextPath) const { + uint64_t iv = 0; + return encodePath(plaintextPath, &iv); } -std::string NameIO::decodePath( const char *cipherPath ) const -{ - uint64_t iv = 0; - return decodePath( cipherPath, &iv ); +std::string NameIO::decodePath(const char *cipherPath) const { + uint64_t iv = 0; + return decodePath(cipherPath, &iv); } -std::string NameIO::_encodePath( const char *plaintextPath, uint64_t *iv ) const -{ - // if chaining is not enabled, then the iv pointer is not used.. - if(!chainedNameIV) - iv = 0; - return recodePath( plaintextPath, - &NameIO::maxEncodedNameLen, &NameIO::encodeName, iv); +std::string NameIO::_encodePath(const char *plaintextPath, uint64_t *iv) const { + // if chaining is not enabled, then the iv pointer is not used.. + if (!chainedNameIV) iv = 0; + return recodePath(plaintextPath, &NameIO::maxEncodedNameLen, + &NameIO::encodeName, iv); } -std::string NameIO::_decodePath( const char *cipherPath, uint64_t *iv ) const -{ - // if chaining is not enabled, then the iv pointer is not used.. - if(!chainedNameIV) - iv = 0; - return recodePath( cipherPath, - &NameIO::maxDecodedNameLen, &NameIO::decodeName, iv); +std::string NameIO::_decodePath(const char *cipherPath, uint64_t *iv) const { + // if chaining is not enabled, then the iv pointer is not used.. + if (!chainedNameIV) iv = 0; + return recodePath(cipherPath, &NameIO::maxDecodedNameLen, &NameIO::decodeName, + iv); } -std::string NameIO::encodePath( const char *path, uint64_t *iv ) const -{ - return getReverseEncryption() ? - _decodePath( path, iv ) : - _encodePath( path, iv ); -} - -std::string NameIO::decodePath( const char *path, uint64_t *iv ) const -{ - return getReverseEncryption() ? - _encodePath( path, iv ) : - _decodePath( path, iv ); -} - - -int NameIO::encodeName( const char *input, int length, char *output ) const -{ - return encodeName( input, length, (uint64_t*)0, output ); +std::string NameIO::encodePath(const char *path, uint64_t *iv) const { + return getReverseEncryption() ? _decodePath(path, iv) : _encodePath(path, iv); } -int NameIO::decodeName( const char *input, int length, char *output ) const -{ - return decodeName( input, length, (uint64_t*)0, output ); +std::string NameIO::decodePath(const char *path, uint64_t *iv) const { + return getReverseEncryption() ? _encodePath(path, iv) : _decodePath(path, iv); } -std::string NameIO::_encodeName( const char *plaintextName, int length ) const -{ - int approxLen = maxEncodedNameLen( length ); - - BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) - - // code the name - int codedLen = encodeName( plaintextName, length, 0, codeBuf ); - rAssert( codedLen <= approxLen ); - rAssert( codeBuf[codedLen] == '\0' ); - - // append result to string - std::string result = (char*)codeBuf; - - BUFFER_RESET( codeBuf ) - - return result; +int NameIO::encodeName(const char *input, int length, char *output) const { + return encodeName(input, length, (uint64_t *)0, output); } -std::string NameIO::_decodeName( const char *encodedName, int length ) const -{ - int approxLen = maxDecodedNameLen( length ); - - BUFFER_INIT( codeBuf, 32, (unsigned int)approxLen+1 ) - - // code the name - int codedLen = decodeName( encodedName, length, 0, codeBuf ); - rAssert( codedLen <= approxLen ); - rAssert( codeBuf[codedLen] == '\0' ); - - // append result to string - std::string result = (char*)codeBuf; - - BUFFER_RESET( codeBuf ) - - return result; +int NameIO::decodeName(const char *input, int length, char *output) const { + return decodeName(input, length, (uint64_t *)0, output); } -std::string NameIO::encodeName( const char *path, int length ) const -{ - return getReverseEncryption() ? - _decodeName( path, length ) : - _encodeName( path, length ); +std::string NameIO::_encodeName(const char *plaintextName, int length) const { + int approxLen = maxEncodedNameLen(length); + + BUFFER_INIT(codeBuf, 32, (unsigned int)approxLen + 1) + + // code the name + int codedLen = encodeName(plaintextName, length, 0, codeBuf); + rAssert(codedLen <= approxLen); + rAssert(codeBuf[codedLen] == '\0'); + + // append result to string + std::string result = (char *)codeBuf; + + BUFFER_RESET(codeBuf) + + return result; } -std::string NameIO::decodeName( const char *path, int length ) const -{ - return getReverseEncryption() ? - _encodeName( path, length ) : - _decodeName( path, length ); +std::string NameIO::_decodeName(const char *encodedName, int length) const { + int approxLen = maxDecodedNameLen(length); + + BUFFER_INIT(codeBuf, 32, (unsigned int)approxLen + 1) + + // code the name + int codedLen = decodeName(encodedName, length, 0, codeBuf); + rAssert(codedLen <= approxLen); + rAssert(codeBuf[codedLen] == '\0'); + + // append result to string + std::string result = (char *)codeBuf; + + BUFFER_RESET(codeBuf) + + return result; +} + +std::string NameIO::encodeName(const char *path, int length) const { + return getReverseEncryption() ? _decodeName(path, length) + : _encodeName(path, length); +} + +std::string NameIO::decodeName(const char *path, int length) const { + return getReverseEncryption() ? _encodeName(path, length) + : _decodeName(path, length); } /* -int NameIO::encodeName( const char *path, int length, - char *output ) const +int NameIO::encodeName( const char *path, int length, + char *output ) const { return getReverseEncryption() ? - _decodeName( path, length, output ) : - _encodeName( path, length, output ); + _decodeName( path, length, output ) : + _encodeName( path, length, output ); } int NameIO::decodeName( const char *path, int length, - char *output ) const + char *output ) const { return getReverseEncryption() ? - _encodeName( path, length, output ) : - _decodeName( path, length, output ); + _encodeName( path, length, output ) : + _decodeName( path, length, output ); } */ diff --git a/encfs/NameIO.h b/encfs/NameIO.h index ebef70f..8c0591e 100644 --- a/encfs/NameIO.h +++ b/encfs/NameIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -31,85 +31,80 @@ class Cipher; -class NameIO -{ -public: - typedef shared_ptr (*Constructor)( const rel::Interface &iface, - const shared_ptr &cipher, const CipherKey &key); +class NameIO { + public: + typedef shared_ptr(*Constructor)(const rel::Interface &iface, + const shared_ptr &cipher, + const CipherKey &key); - struct Algorithm - { - std::string name; - std::string description; - rel::Interface iface; - }; + struct Algorithm { + std::string name; + std::string description; + rel::Interface iface; + }; - typedef std::list AlgorithmList; - static AlgorithmList GetAlgorithmList( bool includeHidden = false ); + typedef std::list AlgorithmList; + static AlgorithmList GetAlgorithmList(bool includeHidden = false); - static shared_ptr New( const rel::Interface &iface, - const shared_ptr &cipher, const CipherKey &key); - static shared_ptr New( const std::string &name, - const shared_ptr &cipher, const CipherKey &key); + static shared_ptr New(const rel::Interface &iface, + const shared_ptr &cipher, + const CipherKey &key); + static shared_ptr New(const std::string &name, + const shared_ptr &cipher, + const CipherKey &key); - static bool Register( const char *name, const char *description, - const rel::Interface &iface, Constructor constructor, - bool hidden = false); + static bool Register(const char *name, const char *description, + const rel::Interface &iface, Constructor constructor, + bool hidden = false); + NameIO(); + virtual ~NameIO(); - NameIO(); - virtual ~NameIO(); + virtual rel::Interface interface() const = 0; - virtual rel::Interface interface() const =0; + void setChainedNameIV(bool enable); + bool getChainedNameIV() const; + void setReverseEncryption(bool enable); + bool getReverseEncryption() const; - void setChainedNameIV( bool enable ); - bool getChainedNameIV() const; - void setReverseEncryption( bool enable ); - bool getReverseEncryption() const; + std::string encodePath(const char *plaintextPath) const; + std::string decodePath(const char *encodedPath) const; - std::string encodePath( const char *plaintextPath ) const; - std::string decodePath( const char *encodedPath ) const; + std::string encodePath(const char *plaintextPath, uint64_t *iv) const; + std::string decodePath(const char *encodedPath, uint64_t *iv) const; - std::string encodePath( const char *plaintextPath, uint64_t *iv ) const; - std::string decodePath( const char *encodedPath, uint64_t *iv ) const; + virtual int maxEncodedNameLen(int plaintextNameLen) const = 0; + virtual int maxDecodedNameLen(int encodedNameLen) const = 0; - virtual int maxEncodedNameLen( int plaintextNameLen ) const =0; - virtual int maxDecodedNameLen( int encodedNameLen ) const =0; + std::string encodeName(const char *plaintextName, int length) const; + std::string decodeName(const char *encodedName, int length) const; - std::string encodeName( const char *plaintextName, int length ) const; - std::string decodeName( const char *encodedName, int length ) const; + protected: + virtual int encodeName(const char *plaintextName, int length, + char *encodedName) const; + virtual int decodeName(const char *encodedName, int length, + char *plaintextName) const; -protected: - virtual int encodeName( const char *plaintextName, int length, - char *encodedName ) const; - virtual int decodeName( const char *encodedName, int length, - char *plaintextName ) const; + virtual int encodeName(const char *plaintextName, int length, uint64_t *iv, + char *encodedName) const = 0; + virtual int decodeName(const char *encodedName, int length, uint64_t *iv, + char *plaintextName) const = 0; - virtual int encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const =0; - virtual int decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const =0; + private: + std::string recodePath(const char *path, int (NameIO::*codingLen)(int) const, + int (NameIO::*codingFunc)(const char *, int, + uint64_t *, char *) const, + uint64_t *iv) const; + std::string _encodePath(const char *plaintextPath, uint64_t *iv) const; + std::string _decodePath(const char *encodedPath, uint64_t *iv) const; + std::string _encodeName(const char *plaintextName, int length) const; + std::string _decodeName(const char *encodedName, int length) const; -private: - - std::string recodePath( const char *path, - int (NameIO::*codingLen)(int) const, - int (NameIO::*codingFunc)(const char *, int, - uint64_t *, char *) const, - uint64_t *iv ) const; - - std::string _encodePath( const char *plaintextPath, uint64_t *iv ) const; - std::string _decodePath( const char *encodedPath, uint64_t *iv ) const; - std::string _encodeName( const char *plaintextName, int length ) const; - std::string _decodeName( const char *encodedName, int length ) const; - - bool chainedNameIV; - bool reverseEncryption; + bool chainedNameIV; + bool reverseEncryption; }; - - /* Helper macros for creating temporary buffers with an optimization that below a given size (OptimizedSize) is allocated on the stack, and when a @@ -117,22 +112,18 @@ private: BUFFER_RESET should be called for the same name as BUFFER_INIT */ -#define BUFFER_INIT( Name, OptimizedSize, Size ) \ -char Name ## _Raw [ OptimizedSize ]; \ -char *Name = Name ## _Raw; \ -if( sizeof(Name ## _Raw) < Size ) \ - Name = new char[ Size ];\ -memset( Name, 0, Size ); - -#define BUFFER_RESET( Name ) \ -do { \ - if( Name != Name ## _Raw ) \ - { \ - delete[] Name; \ - Name = Name ## _Raw; \ - } \ -} while(0); +#define BUFFER_INIT(Name, OptimizedSize, Size) \ + char Name##_Raw[OptimizedSize]; \ + char *Name = Name##_Raw; \ + if (sizeof(Name##_Raw) < Size) Name = new char[Size]; \ + memset(Name, 0, Size); +#define BUFFER_RESET(Name) \ + do { \ + if (Name != Name##_Raw) { \ + delete[] Name; \ + Name = Name##_Raw; \ + } \ + } while (0); #endif - diff --git a/encfs/NullCipher.cpp b/encfs/NullCipher.cpp index d8d7fad..93a153f 100644 --- a/encfs/NullCipher.cpp +++ b/encfs/NullCipher.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -18,7 +18,6 @@ * along with this program. If not, see . */ - #include "NullCipher.h" #include @@ -32,153 +31,109 @@ using namespace std; using namespace rel; using namespace rlog; -static Interface NullInterface( "nullCipher", 1, 0, 0 ); +static Interface NullInterface("nullCipher", 1, 0, 0); static Range NullKeyRange(0); -static Range NullBlockRange(1,4096,1); +static Range NullBlockRange(1, 4096, 1); -static shared_ptr NewNullCipher(const Interface &iface, int keyLen) -{ - (void)keyLen; - return shared_ptr( new NullCipher( iface ) ); +static shared_ptr NewNullCipher(const Interface &iface, int keyLen) { + (void)keyLen; + return shared_ptr(new NullCipher(iface)); } const bool HiddenCipher = true; -static bool NullCipher_registered = Cipher::Register("Null", - "Non encrypting cipher. For testing only!", - NullInterface, NullKeyRange, NullBlockRange, NewNullCipher, - HiddenCipher); +static bool NullCipher_registered = Cipher::Register( + "Null", "Non encrypting cipher. For testing only!", NullInterface, + NullKeyRange, NullBlockRange, NewNullCipher, HiddenCipher); -class NullKey : public AbstractCipherKey -{ -public: - NullKey() {} - virtual ~NullKey() {} +class NullKey : public AbstractCipherKey { + public: + NullKey() {} + virtual ~NullKey() {} }; -class NullDestructor -{ -public: - NullDestructor() {} - NullDestructor(const NullDestructor &) {} - ~NullDestructor() {} +class NullDestructor { + public: + NullDestructor() {} + NullDestructor(const NullDestructor &) {} + ~NullDestructor() {} - NullDestructor &operator = (const NullDestructor &){ return *this; } - void operator ()(NullKey *&) {} + NullDestructor &operator=(const NullDestructor &) { return *this; } + void operator()(NullKey *&) {} }; -shared_ptr gNullKey( new NullKey(), NullDestructor() ); +shared_ptr gNullKey(new NullKey(), NullDestructor()); -NullCipher::NullCipher(const Interface &iface_) -{ - this->iface = iface_; +NullCipher::NullCipher(const Interface &iface_) { this->iface = iface_; } + +NullCipher::~NullCipher() {} + +Interface NullCipher::interface() const { return iface; } + +CipherKey NullCipher::newKey(const char *, int, int &, long, + const unsigned char *, int) { + return gNullKey; } -NullCipher::~NullCipher() -{ +CipherKey NullCipher::newKey(const char *, int) { return gNullKey; } + +CipherKey NullCipher::newRandomKey() { return gNullKey; } + +bool NullCipher::randomize(unsigned char *buf, int len, bool) const { + memset(buf, 0, len); + return true; } -Interface NullCipher::interface() const -{ - return iface; +uint64_t NullCipher::MAC_64(const unsigned char *, int, const CipherKey &, + uint64_t *) const { + return 0; } -CipherKey NullCipher::newKey(const char *, int, - int &, long, const unsigned char *, int ) -{ - return gNullKey; +CipherKey NullCipher::readKey(const unsigned char *, const CipherKey &, bool) { + return gNullKey; } -CipherKey NullCipher::newKey(const char *, int) -{ - return gNullKey; +void NullCipher::writeKey(const CipherKey &, unsigned char *, + const CipherKey &) {} + +bool NullCipher::compareKey(const CipherKey &A_, const CipherKey &B_) const { + shared_ptr A = dynamic_pointer_cast(A_); + shared_ptr B = dynamic_pointer_cast(B_); + return A.get() == B.get(); } -CipherKey NullCipher::newRandomKey() -{ - return gNullKey; +int NullCipher::encodedKeySize() const { return 0; } + +int NullCipher::keySize() const { return 0; } + +int NullCipher::cipherBlockSize() const { return 1; } + +bool NullCipher::streamEncode(unsigned char *src, int len, uint64_t iv64, + const CipherKey &key) const { + (void)src; + (void)len; + (void)iv64; + (void)key; + return true; } -bool NullCipher::randomize( unsigned char *buf, int len, bool ) const -{ - memset( buf, 0, len ); - return true; +bool NullCipher::streamDecode(unsigned char *src, int len, uint64_t iv64, + const CipherKey &key) const { + (void)src; + (void)len; + (void)iv64; + (void)key; + return true; } -uint64_t NullCipher::MAC_64(const unsigned char *, int , - const CipherKey &, uint64_t *) const -{ - return 0; +bool NullCipher::blockEncode(unsigned char *, int, uint64_t, + const CipherKey &) const { + return true; } -CipherKey NullCipher::readKey( const unsigned char *, - const CipherKey &, bool) -{ - return gNullKey; -} - -void NullCipher::writeKey(const CipherKey &, unsigned char *, - const CipherKey &) -{ -} - -bool NullCipher::compareKey(const CipherKey &A_, - const CipherKey &B_) const -{ - shared_ptr A = dynamic_pointer_cast(A_); - shared_ptr B = dynamic_pointer_cast(B_); - return A.get() == B.get(); -} - -int NullCipher::encodedKeySize() const -{ - return 0; -} - -int NullCipher::keySize() const -{ - return 0; -} - -int NullCipher::cipherBlockSize() const -{ - return 1; -} - -bool NullCipher::streamEncode( unsigned char *src, int len, - uint64_t iv64, const CipherKey &key) const -{ - (void)src; - (void)len; - (void)iv64; - (void)key; - return true; -} - -bool NullCipher::streamDecode( unsigned char *src, int len, - uint64_t iv64, const CipherKey &key) const -{ - (void)src; - (void)len; - (void)iv64; - (void)key; - return true; -} - -bool NullCipher::blockEncode( unsigned char *, int , uint64_t, - const CipherKey & ) const -{ - return true; -} - -bool NullCipher::blockDecode( unsigned char *, int, uint64_t, - const CipherKey & ) const -{ - return true; -} - -bool NullCipher::Enabled() -{ - return true; +bool NullCipher::blockDecode(unsigned char *, int, uint64_t, + const CipherKey &) const { + return true; } +bool NullCipher::Enabled() { return true; } diff --git a/encfs/NullCipher.h b/encfs/NullCipher.h index 14c6330..0e46ea5 100644 --- a/encfs/NullCipher.h +++ b/encfs/NullCipher.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -28,59 +28,53 @@ Implements Cipher interface for a pass-through mode. May be useful for testing, but that's it. */ -class NullCipher : public Cipher -{ - rel::Interface iface; +class NullCipher : public Cipher { + rel::Interface iface; -public: - NullCipher(const rel::Interface &iface); - virtual ~NullCipher(); + public: + NullCipher(const rel::Interface &iface); + virtual ~NullCipher(); - virtual rel::Interface interface() const; + virtual rel::Interface interface() const; - // create a new key based on a password - virtual CipherKey newKey(const char *password, int passwdLength, - int &iterationCount, long desiredDuration, - const unsigned char *salt, int saltLen); - virtual CipherKey newKey(const char *password, int passwdLength); - // create a new random key - virtual CipherKey newRandomKey(); + // create a new key based on a password + virtual CipherKey newKey(const char *password, int passwdLength, + int &iterationCount, long desiredDuration, + const unsigned char *salt, int saltLen); + virtual CipherKey newKey(const char *password, int passwdLength); + // create a new random key + virtual CipherKey newRandomKey(); - // data must be len keySize() - virtual CipherKey readKey(const unsigned char *data, - const CipherKey &encodingKey, - bool checkKey); - virtual void writeKey(const CipherKey &key, unsigned char *data, - const CipherKey &encodingKey); - virtual bool compareKey( const CipherKey &A, - const CipherKey &B ) const; + // data must be len keySize() + virtual CipherKey readKey(const unsigned char *data, + const CipherKey &encodingKey, bool checkKey); + virtual void writeKey(const CipherKey &key, unsigned char *data, + const CipherKey &encodingKey); + virtual bool compareKey(const CipherKey &A, const CipherKey &B) const; - // meta-data about the cypher - virtual int keySize() const; - virtual int encodedKeySize() const; - virtual int cipherBlockSize() const; + // meta-data about the cypher + virtual int keySize() const; + virtual int encodedKeySize() const; + virtual int cipherBlockSize() const; - virtual bool randomize( unsigned char *buf, int len, - bool strongRandom ) const; + virtual bool randomize(unsigned char *buf, int len, bool strongRandom) const; - virtual uint64_t MAC_64(const unsigned char *data, int len, - const CipherKey &key, uint64_t *chainedIV) const; + virtual uint64_t MAC_64(const unsigned char *data, int len, + const CipherKey &key, uint64_t *chainedIV) const; - // functional interfaces - virtual bool streamEncode(unsigned char *in, int len, - uint64_t iv64, const CipherKey &key) const; - virtual bool streamDecode(unsigned char *in, int len, - uint64_t iv64, const CipherKey &key) const; + // functional interfaces + virtual bool streamEncode(unsigned char *in, int len, uint64_t iv64, + const CipherKey &key) const; + virtual bool streamDecode(unsigned char *in, int len, uint64_t iv64, + const CipherKey &key) const; - virtual bool blockEncode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &key) const; - virtual bool blockDecode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &key) const; + virtual bool blockEncode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &key) const; + virtual bool blockDecode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &key) const; - // hack to help with static builds - static bool Enabled(); + // hack to help with static builds + static bool Enabled(); }; - #endif - diff --git a/encfs/NullNameIO.cpp b/encfs/NullNameIO.cpp index 7c5e3c8..d4b331b 100644 --- a/encfs/NullNameIO.cpp +++ b/encfs/NullNameIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -27,66 +27,45 @@ using namespace rel; -static shared_ptr NewNNIO( const Interface &, - const shared_ptr &, const CipherKey & ) -{ - return shared_ptr( new NullNameIO() ); +static shared_ptr NewNNIO(const Interface &, const shared_ptr &, + const CipherKey &) { + return shared_ptr(new NullNameIO()); } static Interface NNIOIface("nameio/null", 1, 0, 0); -static bool NullNameIO_registered = NameIO::Register("Null", - "No encryption of filenames", NNIOIface, NewNNIO); +static bool NullNameIO_registered = + NameIO::Register("Null", "No encryption of filenames", NNIOIface, NewNNIO); -NullNameIO::NullNameIO( ) -{ +NullNameIO::NullNameIO() {} +NullNameIO::~NullNameIO() {} + +Interface NullNameIO::interface() const { return NNIOIface; } + +Interface NullNameIO::CurrentInterface() { return NNIOIface; } + +int NullNameIO::maxEncodedNameLen(int plaintextNameLen) const { + return plaintextNameLen; } -NullNameIO::~NullNameIO() -{ +int NullNameIO::maxDecodedNameLen(int encodedNameLen) const { + return encodedNameLen; } -Interface NullNameIO::interface() const -{ - return NNIOIface; +int NullNameIO::encodeName(const char *plaintextName, int length, uint64_t *iv, + char *encodedName) const { + (void)iv; + memcpy(encodedName, plaintextName, length); + + return length; } -Interface NullNameIO::CurrentInterface() -{ - return NNIOIface; -} - - -int NullNameIO::maxEncodedNameLen( int plaintextNameLen ) const -{ - return plaintextNameLen; -} - -int NullNameIO::maxDecodedNameLen( int encodedNameLen ) const -{ - return encodedNameLen; -} - -int NullNameIO::encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const -{ - (void)iv; - memcpy( encodedName, plaintextName, length ); - - return length; -} - -int NullNameIO::decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const -{ - (void)iv; - memcpy( plaintextName, encodedName, length ); - - return length; -} - -bool NullNameIO::Enabled() -{ - return true; +int NullNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, + char *plaintextName) const { + (void)iv; + memcpy(plaintextName, encodedName, length); + + return length; } +bool NullNameIO::Enabled() { return true; } diff --git a/encfs/NullNameIO.h b/encfs/NullNameIO.h index 0c12db8..2de8f58 100644 --- a/encfs/NullNameIO.h +++ b/encfs/NullNameIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -23,30 +23,29 @@ #include "NameIO.h" -class NullNameIO : public NameIO -{ -public: - static rel::Interface CurrentInterface(); +class NullNameIO : public NameIO { + public: + static rel::Interface CurrentInterface(); - NullNameIO( ); + NullNameIO(); - virtual ~NullNameIO(); + virtual ~NullNameIO(); - virtual rel::Interface interface() const; + virtual rel::Interface interface() const; - virtual int maxEncodedNameLen( int plaintextNameLen ) const; - virtual int maxDecodedNameLen( int encodedNameLen ) const; + virtual int maxEncodedNameLen(int plaintextNameLen) const; + virtual int maxDecodedNameLen(int encodedNameLen) const; - // hack to help with static builds - static bool Enabled(); -protected: - virtual int encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const; - virtual int decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const; -private: + // hack to help with static builds + static bool Enabled(); + + protected: + virtual int encodeName(const char *plaintextName, int length, uint64_t *iv, + char *encodedName) const; + virtual int decodeName(const char *encodedName, int length, uint64_t *iv, + char *plaintextName) const; + + private: }; - #endif - diff --git a/encfs/Range.h b/encfs/Range.h index f4f3c7d..7ccd3e2 100644 --- a/encfs/Range.h +++ b/encfs/Range.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -17,97 +17,73 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - + #ifndef _Range_incl_ #define _Range_incl_ +class Range { + int minVal; + int maxVal; + int increment; -class Range -{ - int minVal; - int maxVal; - int increment; -public: - Range(); - Range(int minMax); - Range(int min, int max, int increment); + public: + Range(); + Range(int minMax); + Range(int min, int max, int increment); - bool allowed(int value) const; + bool allowed(int value) const; - int closest(int value) const; + int closest(int value) const; - int min() const; - int max() const; - int inc() const; + int min() const; + int max() const; + int inc() const; }; -inline Range::Range(int minMax) -{ - this->minVal = minMax; - this->maxVal = minMax; - this->increment = 1; +inline Range::Range(int minMax) { + this->minVal = minMax; + this->maxVal = minMax; + this->increment = 1; } -inline Range::Range(int min_, int max_, int increment_) -{ - this->minVal = min_; - this->maxVal = max_; - this->increment = increment_; - if(increment == 0) - this->increment = 1; +inline Range::Range(int min_, int max_, int increment_) { + this->minVal = min_; + this->maxVal = max_; + this->increment = increment_; + if (increment == 0) this->increment = 1; } -inline Range::Range() - : minVal(-1) - , maxVal(-1) - , increment(1) -{ -} +inline Range::Range() : minVal(-1), maxVal(-1), increment(1) {} -inline bool Range::allowed(int value) const -{ - if(value >= minVal && value <= maxVal) - { - int tmp = value - minVal; - if((tmp % increment) == 0) - return true; - } - return false; -} - -inline int Range::closest(int value) const -{ - if(allowed(value)) - return value; - else - if(value < minVal) - return minVal; - else - if(value > maxVal) - return maxVal; - - // must be inbetween but not matched with increment +inline bool Range::allowed(int value) const { + if (value >= minVal && value <= maxVal) { int tmp = value - minVal; - // try rounding to the nearest increment.. - tmp += (increment >> 1); - tmp -= (tmp % increment); - - return closest( value + tmp ); + if ((tmp % increment) == 0) return true; + } + return false; } - -inline int Range::min() const -{ + +inline int Range::closest(int value) const { + if (allowed(value)) + return value; + else if (value < minVal) return minVal; -} - -inline int Range::max() const -{ + else if (value > maxVal) return maxVal; + + // must be inbetween but not matched with increment + int tmp = value - minVal; + // try rounding to the nearest increment.. + tmp += (increment >> 1); + tmp -= (tmp % increment); + + return closest(value + tmp); } -inline int Range::inc() const -{ - return increment; -} +inline int Range::min() const { return minVal; } + +inline int Range::max() const { return maxVal; } + +inline int Range::inc() const { return increment; } #endif diff --git a/encfs/RawFileIO.cpp b/encfs/RawFileIO.cpp index b78933e..b567d0b 100644 --- a/encfs/RawFileIO.cpp +++ b/encfs/RawFileIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -19,7 +19,7 @@ */ #ifdef linux -#define _XOPEN_SOURCE 500 // pick up pread , pwrite +#define _XOPEN_SOURCE 500 // pick up pread , pwrite #endif #include @@ -38,57 +38,41 @@ using namespace std; static rel::Interface RawFileIO_iface("FileIO/Raw", 1, 0, 0); -FileIO *NewRawFileIO( const rel::Interface &iface ) -{ - (void)iface; - return new RawFileIO(); +FileIO *NewRawFileIO(const rel::Interface &iface) { + (void)iface; + return new RawFileIO(); } -inline void swap( int &x, int &y ) -{ - int tmp = x; - x = y; - y = tmp; +inline void swap(int &x, int &y) { + int tmp = x; + x = y; + y = tmp; } -RawFileIO::RawFileIO( ) - : knownSize( false ) - , fileSize(0) - , fd( -1 ) - , oldfd( -1 ) - , canWrite( false ) -{ +RawFileIO::RawFileIO() + : knownSize(false), fileSize(0), fd(-1), oldfd(-1), canWrite(false) {} + +RawFileIO::RawFileIO(const std::string &fileName) + : name(fileName), + knownSize(false), + fileSize(0), + fd(-1), + oldfd(-1), + canWrite(false) {} + +RawFileIO::~RawFileIO() { + int _fd = -1; + int _oldfd = -1; + + swap(_fd, fd); + swap(_oldfd, oldfd); + + if (_oldfd != -1) close(_oldfd); + + if (_fd != -1) close(_fd); } -RawFileIO::RawFileIO( const std::string &fileName ) - : name( fileName ) - , knownSize( false ) - , fileSize( 0 ) - , fd( -1 ) - , oldfd( -1 ) - , canWrite( false ) -{ -} - -RawFileIO::~RawFileIO() -{ - int _fd = -1; - int _oldfd = -1; - - swap( _fd, fd ); - swap( _oldfd, oldfd ); - - if( _oldfd != -1 ) - close( _oldfd ); - - if( _fd != -1 ) - close( _fd ); -} - -rel::Interface RawFileIO::interface() const -{ - return RawFileIO_iface; -} +rel::Interface RawFileIO::interface() const { return RawFileIO_iface; } /* Workaround for opening a file for write when permissions don't allow. @@ -97,23 +81,20 @@ rel::Interface RawFileIO::interface() const be called with a lock around it so that there won't be a race condition with calls to lstat picking up the wrong permissions. */ -static int open_readonly_workaround(const char *path, int flags) -{ - int fd = -1; - struct stat stbuf; - memset(&stbuf, 0, sizeof(struct stat)); - if(lstat( path, &stbuf ) != -1) - { - // make sure user has read/write permission.. - chmod( path , stbuf.st_mode | 0600 ); - fd = ::open( path , flags ); - chmod( path , stbuf.st_mode ); - } else - { - rInfo("can't stat file %s", path ); - } +static int open_readonly_workaround(const char *path, int flags) { + int fd = -1; + struct stat stbuf; + memset(&stbuf, 0, sizeof(struct stat)); + if (lstat(path, &stbuf) != -1) { + // make sure user has read/write permission.. + chmod(path, stbuf.st_mode | 0600); + fd = ::open(path, flags); + chmod(path, stbuf.st_mode); + } else { + rInfo("can't stat file %s", path); + } - return fd; + return fd; } /* @@ -125,201 +106,164 @@ static int open_readonly_workaround(const char *path, int flags) - Also keep the O_LARGEFILE flag, in case the underlying filesystem needs it.. */ -int RawFileIO::open(int flags) -{ - bool requestWrite = ((flags & O_RDWR) || (flags & O_WRONLY)); +int RawFileIO::open(int flags) { + bool requestWrite = ((flags & O_RDWR) || (flags & O_WRONLY)); - rDebug("open call for %s file", requestWrite ? "writable" : "read only"); + rDebug("open call for %s file", requestWrite ? "writable" : "read only"); - int result = 0; + int result = 0; - // if we have a descriptor and it is writable, or we don't need writable.. - if((fd >= 0) && (canWrite || !requestWrite)) - { - rDebug("using existing file descriptor"); - result = fd; // success - } else - { - int finalFlags = requestWrite ? O_RDWR : O_RDONLY; + // if we have a descriptor and it is writable, or we don't need writable.. + if ((fd >= 0) && (canWrite || !requestWrite)) { + rDebug("using existing file descriptor"); + result = fd; // success + } else { + int finalFlags = requestWrite ? O_RDWR : O_RDONLY; #if defined(O_LARGEFILE) - if( flags & O_LARGEFILE ) - finalFlags |= O_LARGEFILE; + if (flags & O_LARGEFILE) finalFlags |= O_LARGEFILE; #else #warning O_LARGEFILE not supported #endif - int newFd = ::open( name.c_str(), finalFlags ); - - rDebug("open file with flags %i, result = %i", finalFlags, newFd); + int newFd = ::open(name.c_str(), finalFlags); - if((newFd == -1) && (errno == EACCES)) - { - rDebug("using readonly workaround for open"); - newFd = open_readonly_workaround( name.c_str(), finalFlags ); - } + rDebug("open file with flags %i, result = %i", finalFlags, newFd); - if(newFd >= 0) - { - if(oldfd >= 0) - { - rError("leaking FD?: oldfd = %i, fd = %i, newfd = %i", - oldfd, fd, newFd); - } - - // the old fd might still be in use, so just keep it around for - // now. - canWrite = requestWrite; - oldfd = fd; - result = fd = newFd; - } else - { - result = -errno; - rInfo("::open error: %s", strerror(errno)); - } + if ((newFd == -1) && (errno == EACCES)) { + rDebug("using readonly workaround for open"); + newFd = open_readonly_workaround(name.c_str(), finalFlags); } - if(result < 0) - rInfo("file %s open failure: %i", name.c_str(), -result); + if (newFd >= 0) { + if (oldfd >= 0) { + rError("leaking FD?: oldfd = %i, fd = %i, newfd = %i", oldfd, fd, + newFd); + } - return result; + // the old fd might still be in use, so just keep it around for + // now. + canWrite = requestWrite; + oldfd = fd; + result = fd = newFd; + } else { + result = -errno; + rInfo("::open error: %s", strerror(errno)); + } + } + + if (result < 0) rInfo("file %s open failure: %i", name.c_str(), -result); + + return result; } -int RawFileIO::getAttr( struct stat *stbuf ) const -{ - int res = lstat( name.c_str(), stbuf ); - int eno = errno; +int RawFileIO::getAttr(struct stat *stbuf) const { + int res = lstat(name.c_str(), stbuf); + int eno = errno; - if(res < 0) - rInfo("getAttr error on %s: %s", name.c_str(), strerror( eno )); + if (res < 0) rInfo("getAttr error on %s: %s", name.c_str(), strerror(eno)); - return ( res < 0 ) ? -eno : 0; + return (res < 0) ? -eno : 0; } -void RawFileIO::setFileName( const char *fileName ) -{ - name = fileName; -} +void RawFileIO::setFileName(const char *fileName) { name = fileName; } -const char *RawFileIO::getFileName() const -{ - return name.c_str(); -} +const char *RawFileIO::getFileName() const { return name.c_str(); } -off_t RawFileIO::getSize() const -{ - if(!knownSize) - { - struct stat stbuf; - memset( &stbuf, 0, sizeof( struct stat )); - int res = lstat( name.c_str(), &stbuf ); +off_t RawFileIO::getSize() const { + if (!knownSize) { + struct stat stbuf; + memset(&stbuf, 0, sizeof(struct stat)); + int res = lstat(name.c_str(), &stbuf); - if(res == 0) - { - const_cast(this)->fileSize = stbuf.st_size; - const_cast(this)->knownSize = true; - return fileSize; - } else - return -1; + if (res == 0) { + const_cast(this)->fileSize = stbuf.st_size; + const_cast(this)->knownSize = true; + return fileSize; } else - { - return fileSize; - } + return -1; + } else { + return fileSize; + } } -ssize_t RawFileIO::read( const IORequest &req ) const -{ - rAssert( fd >= 0 ); +ssize_t RawFileIO::read(const IORequest &req) const { + rAssert(fd >= 0); - ssize_t readSize = pread( fd, req.data, req.dataLen, req.offset ); + ssize_t readSize = pread(fd, req.data, req.dataLen, req.offset); - if(readSize < 0) - { - rInfo("read failed at offset %" PRIi64 " for %i bytes: %s", - req.offset, req.dataLen, strerror( errno )); - } + if (readSize < 0) { + rInfo("read failed at offset %" PRIi64 " for %i bytes: %s", req.offset, + req.dataLen, strerror(errno)); + } - return readSize; + return readSize; } -bool RawFileIO::write( const IORequest &req ) -{ - rAssert( fd >= 0 ); - rAssert( true == canWrite ); +bool RawFileIO::write(const IORequest &req) { + rAssert(fd >= 0); + rAssert(true == canWrite); - int retrys = 10; - void *buf = req.data; - ssize_t bytes = req.dataLen; - off_t offset = req.offset; + int retrys = 10; + void *buf = req.data; + ssize_t bytes = req.dataLen; + off_t offset = req.offset; - while( bytes && retrys > 0 ) - { - ssize_t writeSize = ::pwrite( fd, buf, bytes, offset ); + while (bytes && retrys > 0) { + ssize_t writeSize = ::pwrite(fd, buf, bytes, offset); - if( writeSize < 0 ) - { - knownSize = false; - rInfo("write failed at offset %" PRIi64 " for %i bytes: %s", - offset, (int)bytes, strerror( errno )); - return false; - } - - bytes -= writeSize; - offset += writeSize; - buf = (void*)((char*)buf + writeSize); - --retrys; + if (writeSize < 0) { + knownSize = false; + rInfo("write failed at offset %" PRIi64 " for %i bytes: %s", offset, + (int)bytes, strerror(errno)); + return false; } - if(bytes != 0) - { - rError("Write error: wrote %i bytes of %i, max retries reached\n", - (int)(req.dataLen - bytes), req.dataLen ); - knownSize = false; - return false; - } else - { - if(knownSize) - { - off_t last = req.offset + req.dataLen; - if(last > fileSize) - fileSize = last; - } + bytes -= writeSize; + offset += writeSize; + buf = (void *)((char *)buf + writeSize); + --retrys; + } - return true; + if (bytes != 0) { + rError("Write error: wrote %i bytes of %i, max retries reached\n", + (int)(req.dataLen - bytes), req.dataLen); + knownSize = false; + return false; + } else { + if (knownSize) { + off_t last = req.offset + req.dataLen; + if (last > fileSize) fileSize = last; } + + return true; + } } -int RawFileIO::truncate( off_t size ) -{ - int res; +int RawFileIO::truncate(off_t size) { + int res; - if(fd >= 0 && canWrite) - { - res = ::ftruncate( fd, size ); + if (fd >= 0 && canWrite) { + res = ::ftruncate(fd, size); #if !defined(__FreeBSD__) && !defined(__APPLE__) - ::fdatasync( fd ); + ::fdatasync(fd); #endif - } else - res = ::truncate( name.c_str(), size ); + } else + res = ::truncate(name.c_str(), size); - if(res < 0) - { - int eno = errno; - rInfo("truncate failed for %s (%i) size %" PRIi64 ", error %s", - name.c_str(), fd, size, strerror(eno)); - res = -eno; - knownSize = false; - } else - { - res = 0; - fileSize = size; - knownSize = true; - } + if (res < 0) { + int eno = errno; + rInfo("truncate failed for %s (%i) size %" PRIi64 ", error %s", + name.c_str(), fd, size, strerror(eno)); + res = -eno; + knownSize = false; + } else { + res = 0; + fileSize = size; + knownSize = true; + } - return res; + return res; } -bool RawFileIO::isWritable() const -{ - return canWrite; -} +bool RawFileIO::isWritable() const { return canWrite; } diff --git a/encfs/RawFileIO.h b/encfs/RawFileIO.h index a16cbf4..90e68ec 100644 --- a/encfs/RawFileIO.h +++ b/encfs/RawFileIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -25,40 +25,38 @@ #include -class RawFileIO : public FileIO -{ -public: - RawFileIO(); - RawFileIO( const std::string &fileName ); - virtual ~RawFileIO(); +class RawFileIO : public FileIO { + public: + RawFileIO(); + RawFileIO(const std::string &fileName); + virtual ~RawFileIO(); - virtual rel::Interface interface() const; + virtual rel::Interface interface() const; - virtual void setFileName( const char *fileName ); - virtual const char *getFileName() const; + virtual void setFileName(const char *fileName); + virtual const char *getFileName() const; - virtual int open( int flags ); - - virtual int getAttr( struct stat *stbuf ) const; - virtual off_t getSize() const; + virtual int open(int flags); - virtual ssize_t read( const IORequest & req ) const; - virtual bool write( const IORequest &req ); + virtual int getAttr(struct stat *stbuf) const; + virtual off_t getSize() const; - virtual int truncate( off_t size ); + virtual ssize_t read(const IORequest &req) const; + virtual bool write(const IORequest &req); - virtual bool isWritable() const; -protected: + virtual int truncate(off_t size); - std::string name; + virtual bool isWritable() const; - bool knownSize; - off_t fileSize; + protected: + std::string name; - int fd; - int oldfd; - bool canWrite; + bool knownSize; + off_t fileSize; + + int fd; + int oldfd; + bool canWrite; }; #endif - diff --git a/encfs/SSL_Cipher.cpp b/encfs/SSL_Cipher.cpp index a425490..50b3a2a 100644 --- a/encfs/SSL_Cipher.cpp +++ b/encfs/SSL_Cipher.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -48,15 +48,12 @@ using namespace std; using namespace rel; using namespace rlog; -const int MAX_KEYLENGTH = 32; // in bytes (256 bit) +const int MAX_KEYLENGTH = 32; // in bytes (256 bit) const int MAX_IVLENGTH = 16; const int KEY_CHECKSUM_BYTES = 4; #ifndef MIN -inline int MIN(int a, int b) -{ - return (a < b) ? a : b; -} +inline int MIN(int a, int b) { return (a < b) ? a : b; } #endif /* @@ -70,323 +67,286 @@ inline int MIN(int a, int b) plaintext, there is no ability for an attacker to pre-compute known password->data mappings, which is what the salt is meant to frustrate. */ -int BytesToKey( int keyLen, int ivLen, const EVP_MD *md, - const unsigned char *data, int dataLen, - unsigned int rounds, unsigned char *key, unsigned char *iv) -{ - if( data == NULL || dataLen == 0 ) - return 0; // OpenSSL returns nkey here, but why? It is a failure.. - - unsigned char mdBuf[ EVP_MAX_MD_SIZE ]; - unsigned int mds=0; - int addmd =0; - int nkey = key ? keyLen : 0; - int niv = iv ? ivLen : 0; +int BytesToKey(int keyLen, int ivLen, const EVP_MD *md, + const unsigned char *data, int dataLen, unsigned int rounds, + unsigned char *key, unsigned char *iv) { + if (data == NULL || dataLen == 0) + return 0; // OpenSSL returns nkey here, but why? It is a failure.. - EVP_MD_CTX cx; - EVP_MD_CTX_init( &cx ); + unsigned char mdBuf[EVP_MAX_MD_SIZE]; + unsigned int mds = 0; + int addmd = 0; + int nkey = key ? keyLen : 0; + int niv = iv ? ivLen : 0; - for(;;) - { - EVP_DigestInit_ex( &cx, md, NULL ); - if( addmd++ ) - EVP_DigestUpdate( &cx, mdBuf, mds ); - EVP_DigestUpdate( &cx, data, dataLen ); - EVP_DigestFinal_ex( &cx, mdBuf, &mds ); + EVP_MD_CTX cx; + EVP_MD_CTX_init(&cx); - for(unsigned int i=1; i < rounds; ++i) - { - EVP_DigestInit_ex( &cx, md, NULL ); - EVP_DigestUpdate( &cx, mdBuf, mds ); - EVP_DigestFinal_ex( &cx, mdBuf, &mds ); - } + for (;;) { + EVP_DigestInit_ex(&cx, md, NULL); + if (addmd++) EVP_DigestUpdate(&cx, mdBuf, mds); + EVP_DigestUpdate(&cx, data, dataLen); + EVP_DigestFinal_ex(&cx, mdBuf, &mds); - int offset = 0; - int toCopy = MIN( nkey, mds - offset ); - if( toCopy ) - { - memcpy( key, mdBuf+offset, toCopy ); - key += toCopy; - nkey -= toCopy; - offset += toCopy; - } - toCopy = MIN( niv, mds - offset ); - if( toCopy ) - { - memcpy( iv, mdBuf+offset, toCopy ); - iv += toCopy; - niv -= toCopy; - offset += toCopy; - } - if((nkey == 0) && (niv == 0)) break; + for (unsigned int i = 1; i < rounds; ++i) { + EVP_DigestInit_ex(&cx, md, NULL); + EVP_DigestUpdate(&cx, mdBuf, mds); + EVP_DigestFinal_ex(&cx, mdBuf, &mds); } - EVP_MD_CTX_cleanup( &cx ); - OPENSSL_cleanse( mdBuf, sizeof(mdBuf) ); - return keyLen; -} - -long time_diff(const timeval &end, const timeval &start) -{ - return (end.tv_sec - start.tv_sec) * 1000 * 1000 + - (end.tv_usec - start.tv_usec); -} - -int TimedPBKDF2(const char *pass, int passlen, - const unsigned char *salt, int saltlen, - int keylen, unsigned char *out, - long desiredPDFTime) -{ - int iter = 1000; - timeval start, end; - - for(;;) - { - gettimeofday( &start, 0 ); - int res = PKCS5_PBKDF2_HMAC_SHA1( - pass, passlen, const_cast(salt), saltlen, - iter, keylen, out); - if(res != 1) - return -1; - - gettimeofday( &end, 0 ); - - long delta = time_diff(end, start); - if(delta < desiredPDFTime / 8) - { - iter *= 4; - } else if(delta < (5 * desiredPDFTime / 6)) - { - // estimate number of iterations to get close to desired time - iter = (int)((double)iter * (double)desiredPDFTime - / (double)delta); - } else - return iter; + int offset = 0; + int toCopy = MIN(nkey, mds - offset); + if (toCopy) { + memcpy(key, mdBuf + offset, toCopy); + key += toCopy; + nkey -= toCopy; + offset += toCopy; } + toCopy = MIN(niv, mds - offset); + if (toCopy) { + memcpy(iv, mdBuf + offset, toCopy); + iv += toCopy; + niv -= toCopy; + offset += toCopy; + } + if ((nkey == 0) && (niv == 0)) break; + } + EVP_MD_CTX_cleanup(&cx); + OPENSSL_cleanse(mdBuf, sizeof(mdBuf)); + + return keyLen; } +long time_diff(const timeval &end, const timeval &start) { + return (end.tv_sec - start.tv_sec) * 1000 * 1000 + + (end.tv_usec - start.tv_usec); +} + +int TimedPBKDF2(const char *pass, int passlen, const unsigned char *salt, + int saltlen, int keylen, unsigned char *out, + long desiredPDFTime) { + int iter = 1000; + timeval start, end; + + for (;;) { + gettimeofday(&start, 0); + int res = + PKCS5_PBKDF2_HMAC_SHA1(pass, passlen, const_cast(salt), + saltlen, iter, keylen, out); + if (res != 1) return -1; + + gettimeofday(&end, 0); + + long delta = time_diff(end, start); + if (delta < desiredPDFTime / 8) { + iter *= 4; + } else if (delta < (5 * desiredPDFTime / 6)) { + // estimate number of iterations to get close to desired time + iter = (int)((double)iter * (double)desiredPDFTime / (double)delta); + } else + return iter; + } +} // - Version 1:0 used EVP_BytesToKey, which didn't do the right thing for // Blowfish key lengths > 128 bit. -// - Version 2:0 uses BytesToKey. +// - Version 2:0 uses BytesToKey. // We support both 2:0 and 1:0, hence current:revision:age = 2:0:1 // - Version 2:1 adds support for Message Digest function interface // - Version 2:2 adds PBKDF2 for password derivation // - Version 3:0 adds a new IV mechanism -static Interface BlowfishInterface( "ssl/blowfish", 3, 0, 2 ); -static Interface AESInterface( "ssl/aes", 3, 0, 2 ); +static Interface BlowfishInterface("ssl/blowfish", 3, 0, 2); +static Interface AESInterface("ssl/aes", 3, 0, 2); #if defined(HAVE_EVP_BF) -static Range BFKeyRange(128,256,32); -static Range BFBlockRange(64,4096,8); +static Range BFKeyRange(128, 256, 32); +static Range BFBlockRange(64, 4096, 8); -static shared_ptr NewBFCipher( const Interface &iface, int keyLen ) -{ - if( keyLen <= 0 ) - keyLen = 160; +static shared_ptr NewBFCipher(const Interface &iface, int keyLen) { + if (keyLen <= 0) keyLen = 160; - keyLen = BFKeyRange.closest( keyLen ); + keyLen = BFKeyRange.closest(keyLen); - const EVP_CIPHER *blockCipher = EVP_bf_cbc(); - const EVP_CIPHER *streamCipher = EVP_bf_cfb(); + const EVP_CIPHER *blockCipher = EVP_bf_cbc(); + const EVP_CIPHER *streamCipher = EVP_bf_cfb(); - return shared_ptr( new SSL_Cipher(iface, BlowfishInterface, - blockCipher, streamCipher, keyLen / 8) ); + return shared_ptr(new SSL_Cipher( + iface, BlowfishInterface, blockCipher, streamCipher, keyLen / 8)); } -static bool BF_Cipher_registered = Cipher::Register("Blowfish", - // xgroup(setup) - gettext_noop("8 byte block cipher"), - BlowfishInterface, BFKeyRange, BFBlockRange, NewBFCipher); +static bool BF_Cipher_registered = + Cipher::Register("Blowfish", + // xgroup(setup) + gettext_noop("8 byte block cipher"), BlowfishInterface, + BFKeyRange, BFBlockRange, NewBFCipher); #endif - #if defined(HAVE_EVP_AES) -static Range AESKeyRange(128,256,64); -static Range AESBlockRange(64,4096,16); +static Range AESKeyRange(128, 256, 64); +static Range AESBlockRange(64, 4096, 16); -static shared_ptr NewAESCipher( const Interface &iface, int keyLen ) -{ - if( keyLen <= 0 ) - keyLen = 192; +static shared_ptr NewAESCipher(const Interface &iface, int keyLen) { + if (keyLen <= 0) keyLen = 192; - keyLen = AESKeyRange.closest( keyLen ); + keyLen = AESKeyRange.closest(keyLen); - const EVP_CIPHER *blockCipher = 0; - const EVP_CIPHER *streamCipher = 0; + const EVP_CIPHER *blockCipher = 0; + const EVP_CIPHER *streamCipher = 0; - switch(keyLen) - { + switch (keyLen) { case 128: - blockCipher = EVP_aes_128_cbc(); - streamCipher = EVP_aes_128_cfb(); - break; + blockCipher = EVP_aes_128_cbc(); + streamCipher = EVP_aes_128_cfb(); + break; case 192: - blockCipher = EVP_aes_192_cbc(); - streamCipher = EVP_aes_192_cfb(); - break; + blockCipher = EVP_aes_192_cbc(); + streamCipher = EVP_aes_192_cfb(); + break; case 256: default: - blockCipher = EVP_aes_256_cbc(); - streamCipher = EVP_aes_256_cfb(); - break; - } - - return shared_ptr( new SSL_Cipher(iface, AESInterface, - blockCipher, streamCipher, keyLen / 8) ); + blockCipher = EVP_aes_256_cbc(); + streamCipher = EVP_aes_256_cfb(); + break; + } + + return shared_ptr(new SSL_Cipher(iface, AESInterface, blockCipher, + streamCipher, keyLen / 8)); } -static bool AES_Cipher_registered = Cipher::Register("AES", - "16 byte block cipher", - AESInterface, AESKeyRange, AESBlockRange, NewAESCipher); +static bool AES_Cipher_registered = + Cipher::Register("AES", "16 byte block cipher", AESInterface, AESKeyRange, + AESBlockRange, NewAESCipher); #endif -class SSLKey : public AbstractCipherKey -{ -public: - pthread_mutex_t mutex; - - unsigned int keySize; // in bytes - unsigned int ivLength; +class SSLKey : public AbstractCipherKey { + public: + pthread_mutex_t mutex; - // key data is first _keySize bytes, - // followed by iv of _ivLength bytes, - unsigned char *buffer; + unsigned int keySize; // in bytes + unsigned int ivLength; - EVP_CIPHER_CTX block_enc; - EVP_CIPHER_CTX block_dec; - EVP_CIPHER_CTX stream_enc; - EVP_CIPHER_CTX stream_dec; + // key data is first _keySize bytes, + // followed by iv of _ivLength bytes, + unsigned char *buffer; - HMAC_CTX mac_ctx; + EVP_CIPHER_CTX block_enc; + EVP_CIPHER_CTX block_dec; + EVP_CIPHER_CTX stream_enc; + EVP_CIPHER_CTX stream_dec; - SSLKey(int keySize, int ivLength); - ~SSLKey(); + HMAC_CTX mac_ctx; + + SSLKey(int keySize, int ivLength); + ~SSLKey(); }; -SSLKey::SSLKey(int keySize_, int ivLength_) -{ - this->keySize = keySize_; - this->ivLength = ivLength_; - pthread_mutex_init( &mutex, 0 ); - buffer = (unsigned char *)OPENSSL_malloc( keySize + ivLength ); - memset( buffer, 0, keySize + ivLength ); +SSLKey::SSLKey(int keySize_, int ivLength_) { + this->keySize = keySize_; + this->ivLength = ivLength_; + pthread_mutex_init(&mutex, 0); + buffer = (unsigned char *)OPENSSL_malloc(keySize + ivLength); + memset(buffer, 0, keySize + ivLength); - // most likely fails unless we're running as root, or a user-page-lock - // kernel patch is applied.. - mlock( buffer, keySize + ivLength ); + // most likely fails unless we're running as root, or a user-page-lock + // kernel patch is applied.. + mlock(buffer, keySize + ivLength); } -SSLKey::~SSLKey() -{ - memset( buffer, 0, keySize + ivLength ); +SSLKey::~SSLKey() { + memset(buffer, 0, keySize + ivLength); - OPENSSL_free( buffer ); - munlock( buffer, keySize + ivLength ); + OPENSSL_free(buffer); + munlock(buffer, keySize + ivLength); - keySize = 0; - ivLength = 0; - buffer = 0; - - EVP_CIPHER_CTX_cleanup( &block_enc ); - EVP_CIPHER_CTX_cleanup( &block_dec ); - EVP_CIPHER_CTX_cleanup( &stream_enc ); - EVP_CIPHER_CTX_cleanup( &stream_dec ); + keySize = 0; + ivLength = 0; + buffer = 0; - HMAC_CTX_cleanup( &mac_ctx ); + EVP_CIPHER_CTX_cleanup(&block_enc); + EVP_CIPHER_CTX_cleanup(&block_dec); + EVP_CIPHER_CTX_cleanup(&stream_enc); + EVP_CIPHER_CTX_cleanup(&stream_dec); - pthread_mutex_destroy( &mutex ); + HMAC_CTX_cleanup(&mac_ctx); + + pthread_mutex_destroy(&mutex); } - -inline unsigned char* KeyData( const shared_ptr &key ) -{ - return key->buffer; +inline unsigned char *KeyData(const shared_ptr &key) { + return key->buffer; } -inline unsigned char* IVData( const shared_ptr &key ) -{ - return key->buffer + key->keySize; +inline unsigned char *IVData(const shared_ptr &key) { + return key->buffer + key->keySize; } void initKey(const shared_ptr &key, const EVP_CIPHER *_blockCipher, - const EVP_CIPHER *_streamCipher, int _keySize) -{ - Lock lock( key->mutex ); - // initialize the cipher context once so that we don't have to do it for - // every block.. - EVP_CIPHER_CTX_init( &key->block_enc ); - EVP_CIPHER_CTX_init( &key->block_dec ); - EVP_CIPHER_CTX_init( &key->stream_enc ); - EVP_CIPHER_CTX_init( &key->stream_dec ); - - EVP_EncryptInit_ex( &key->block_enc, _blockCipher, NULL, NULL, NULL); - EVP_DecryptInit_ex( &key->block_dec, _blockCipher, NULL, NULL, NULL); - EVP_EncryptInit_ex( &key->stream_enc, _streamCipher, NULL, NULL, NULL); - EVP_DecryptInit_ex( &key->stream_dec, _streamCipher, NULL, NULL, NULL); + const EVP_CIPHER *_streamCipher, int _keySize) { + Lock lock(key->mutex); + // initialize the cipher context once so that we don't have to do it for + // every block.. + EVP_CIPHER_CTX_init(&key->block_enc); + EVP_CIPHER_CTX_init(&key->block_dec); + EVP_CIPHER_CTX_init(&key->stream_enc); + EVP_CIPHER_CTX_init(&key->stream_dec); - EVP_CIPHER_CTX_set_key_length( &key->block_enc, _keySize ); - EVP_CIPHER_CTX_set_key_length( &key->block_dec, _keySize ); - EVP_CIPHER_CTX_set_key_length( &key->stream_enc, _keySize ); - EVP_CIPHER_CTX_set_key_length( &key->stream_dec, _keySize ); - - EVP_CIPHER_CTX_set_padding( &key->block_enc, 0 ); - EVP_CIPHER_CTX_set_padding( &key->block_dec, 0 ); - EVP_CIPHER_CTX_set_padding( &key->stream_enc, 0 ); - EVP_CIPHER_CTX_set_padding( &key->stream_dec, 0 ); + EVP_EncryptInit_ex(&key->block_enc, _blockCipher, NULL, NULL, NULL); + EVP_DecryptInit_ex(&key->block_dec, _blockCipher, NULL, NULL, NULL); + EVP_EncryptInit_ex(&key->stream_enc, _streamCipher, NULL, NULL, NULL); + EVP_DecryptInit_ex(&key->stream_dec, _streamCipher, NULL, NULL, NULL); - EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, KeyData(key), NULL); - EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, KeyData(key), NULL); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, KeyData(key), NULL); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, KeyData(key), NULL); - - HMAC_CTX_init( &key->mac_ctx ); - HMAC_Init_ex( &key->mac_ctx, KeyData(key), _keySize, EVP_sha1(), 0 ); + EVP_CIPHER_CTX_set_key_length(&key->block_enc, _keySize); + EVP_CIPHER_CTX_set_key_length(&key->block_dec, _keySize); + EVP_CIPHER_CTX_set_key_length(&key->stream_enc, _keySize); + EVP_CIPHER_CTX_set_key_length(&key->stream_dec, _keySize); + + EVP_CIPHER_CTX_set_padding(&key->block_enc, 0); + EVP_CIPHER_CTX_set_padding(&key->block_dec, 0); + EVP_CIPHER_CTX_set_padding(&key->stream_enc, 0); + EVP_CIPHER_CTX_set_padding(&key->stream_dec, 0); + + EVP_EncryptInit_ex(&key->block_enc, NULL, NULL, KeyData(key), NULL); + EVP_DecryptInit_ex(&key->block_dec, NULL, NULL, KeyData(key), NULL); + EVP_EncryptInit_ex(&key->stream_enc, NULL, NULL, KeyData(key), NULL); + EVP_DecryptInit_ex(&key->stream_dec, NULL, NULL, KeyData(key), NULL); + + HMAC_CTX_init(&key->mac_ctx); + HMAC_Init_ex(&key->mac_ctx, KeyData(key), _keySize, EVP_sha1(), 0); } +static RLogChannel *CipherInfo = DEF_CHANNEL("info/cipher", Log_Info); -static RLogChannel * CipherInfo = DEF_CHANNEL( "info/cipher", Log_Info ); +SSL_Cipher::SSL_Cipher(const Interface &iface_, const Interface &realIface_, + const EVP_CIPHER *blockCipher, + const EVP_CIPHER *streamCipher, int keySize_) { + this->iface = iface_; + this->realIface = realIface_; + this->_blockCipher = blockCipher; + this->_streamCipher = streamCipher; + this->_keySize = keySize_; + this->_ivLength = EVP_CIPHER_iv_length(_blockCipher); -SSL_Cipher::SSL_Cipher(const Interface &iface_, - const Interface &realIface_, - const EVP_CIPHER *blockCipher, - const EVP_CIPHER *streamCipher, - int keySize_) -{ - this->iface = iface_; - this->realIface = realIface_; - this->_blockCipher = blockCipher; - this->_streamCipher = streamCipher; - this->_keySize = keySize_; - this->_ivLength = EVP_CIPHER_iv_length( _blockCipher ); - - rAssert(_ivLength == 8 || _ivLength == 16); + rAssert(_ivLength == 8 || _ivLength == 16); - rLog(CipherInfo, "allocated cipher %s, keySize %i, ivlength %i", - iface.name().c_str(), _keySize, _ivLength); - - if( (EVP_CIPHER_key_length( _blockCipher ) != (int )_keySize) - && iface.current() == 1) - { - rWarning("Running in backward compatibilty mode for 1.0 - \n" - "key is really %i bits, not %i.\n" - "Consider creating a new filesystem and moving your data.", - EVP_CIPHER_key_length( _blockCipher ) * 8, - _keySize * 8 ); - } + rLog(CipherInfo, "allocated cipher %s, keySize %i, ivlength %i", + iface.name().c_str(), _keySize, _ivLength); + + if ((EVP_CIPHER_key_length(_blockCipher) != (int)_keySize) && + iface.current() == 1) { + rWarning( + "Running in backward compatibilty mode for 1.0 - \n" + "key is really %i bits, not %i.\n" + "Consider creating a new filesystem and moving your data.", + EVP_CIPHER_key_length(_blockCipher) * 8, _keySize * 8); + } } -SSL_Cipher::~SSL_Cipher() -{ -} +SSL_Cipher::~SSL_Cipher() {} -Interface SSL_Cipher::interface() const -{ - return realIface; -} +Interface SSL_Cipher::interface() const { return realIface; } /* create a key from the password. @@ -396,72 +356,61 @@ Interface SSL_Cipher::interface() const is used to encipher/decipher the master key. */ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength, - int &iterationCount, long desiredDuration, - const unsigned char *salt, int saltLen) -{ - shared_ptr key( new SSLKey( _keySize, _ivLength) ); - - if(iterationCount == 0) - { - // timed run, fills in iteration count - int res = TimedPBKDF2(password, passwdLength, - salt, saltLen, - _keySize+_ivLength, KeyData(key), - 1000 * desiredDuration); - if(res <= 0) - { - rWarning("openssl error, PBKDF2 failed"); - return CipherKey(); - } else - iterationCount = res; - } else - { - // known iteration length - if(PKCS5_PBKDF2_HMAC_SHA1( - password, passwdLength, - const_cast(salt), saltLen, - iterationCount, _keySize + _ivLength, KeyData(key)) != 1) - { - rWarning("openssl error, PBKDF2 failed"); - return CipherKey(); - } - } - - initKey( key, _blockCipher, _streamCipher, _keySize ); + int &iterationCount, long desiredDuration, + const unsigned char *salt, int saltLen) { + shared_ptr key(new SSLKey(_keySize, _ivLength)); - return key; + if (iterationCount == 0) { + // timed run, fills in iteration count + int res = + TimedPBKDF2(password, passwdLength, salt, saltLen, _keySize + _ivLength, + KeyData(key), 1000 * desiredDuration); + if (res <= 0) { + rWarning("openssl error, PBKDF2 failed"); + return CipherKey(); + } else + iterationCount = res; + } else { + // known iteration length + if (PKCS5_PBKDF2_HMAC_SHA1( + password, passwdLength, const_cast(salt), saltLen, + iterationCount, _keySize + _ivLength, KeyData(key)) != 1) { + rWarning("openssl error, PBKDF2 failed"); + return CipherKey(); + } + } + + initKey(key, _blockCipher, _streamCipher, _keySize); + + return key; } -CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) -{ - shared_ptr key( new SSLKey( _keySize, _ivLength) ); - - int bytes = 0; - if( iface.current() > 1 ) - { - // now we use BytesToKey, which can deal with Blowfish keys larger then - // 128 bits. - bytes = BytesToKey( _keySize, _ivLength, EVP_sha1(), - (unsigned char *)password, passwdLength, 16, - KeyData(key), IVData(key) ); +CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) { + shared_ptr key(new SSLKey(_keySize, _ivLength)); - // the reason for moving from EVP_BytesToKey to BytesToKey function.. - if(bytes != (int)_keySize) - { - rWarning("newKey: BytesToKey returned %i, expecting %i key bytes", - bytes, _keySize); - } - } else - { - // for backward compatibility with filesystems created with 1:0 - bytes = EVP_BytesToKey( _blockCipher, EVP_sha1(), NULL, - (unsigned char *)password, passwdLength, 16, - KeyData(key), IVData(key) ); + int bytes = 0; + if (iface.current() > 1) { + // now we use BytesToKey, which can deal with Blowfish keys larger then + // 128 bits. + bytes = + BytesToKey(_keySize, _ivLength, EVP_sha1(), (unsigned char *)password, + passwdLength, 16, KeyData(key), IVData(key)); + + // the reason for moving from EVP_BytesToKey to BytesToKey function.. + if (bytes != (int)_keySize) { + rWarning("newKey: BytesToKey returned %i, expecting %i key bytes", bytes, + _keySize); } - - initKey( key, _blockCipher, _streamCipher, _keySize ); + } else { + // for backward compatibility with filesystems created with 1:0 + bytes = EVP_BytesToKey(_blockCipher, EVP_sha1(), NULL, + (unsigned char *)password, passwdLength, 16, + KeyData(key), IVData(key)); + } - return key; + initKey(key, _blockCipher, _streamCipher, _keySize); + + return key; } /* @@ -472,238 +421,213 @@ CipherKey SSL_Cipher::newKey(const char *password, int passwdLength) This algorithm can change at any time without affecting backward compatibility. */ -CipherKey SSL_Cipher::newRandomKey() -{ - const int bufLen = MAX_KEYLENGTH; - unsigned char tmpBuf[ bufLen ]; - int saltLen = 20; - unsigned char saltBuf[ saltLen ]; +CipherKey SSL_Cipher::newRandomKey() { + const int bufLen = MAX_KEYLENGTH; + unsigned char tmpBuf[bufLen]; + int saltLen = 20; + unsigned char saltBuf[saltLen]; - if(!randomize(tmpBuf, bufLen, true) || - !randomize(saltBuf, saltLen, true)) - return CipherKey(); + if (!randomize(tmpBuf, bufLen, true) || !randomize(saltBuf, saltLen, true)) + return CipherKey(); - shared_ptr key( new SSLKey( _keySize, _ivLength) ); + shared_ptr key(new SSLKey(_keySize, _ivLength)); - // doesn't need to be versioned, because a random key is a random key.. - // Doesn't need to be reproducable.. - if(PKCS5_PBKDF2_HMAC_SHA1((char*)tmpBuf, bufLen, saltBuf, saltLen, - 1000, _keySize + _ivLength, KeyData(key)) != 1) - { - rWarning("openssl error, PBKDF2 failed"); - return CipherKey(); - } + // doesn't need to be versioned, because a random key is a random key.. + // Doesn't need to be reproducable.. + if (PKCS5_PBKDF2_HMAC_SHA1((char *)tmpBuf, bufLen, saltBuf, saltLen, 1000, + _keySize + _ivLength, KeyData(key)) != 1) { + rWarning("openssl error, PBKDF2 failed"); + return CipherKey(); + } - OPENSSL_cleanse(tmpBuf, bufLen); + OPENSSL_cleanse(tmpBuf, bufLen); - initKey( key, _blockCipher, _streamCipher, _keySize ); + initKey(key, _blockCipher, _streamCipher, _keySize); - return key; + return key; } /* compute a 64-bit check value for the data using HMAC. */ -static uint64_t _checksum_64( SSLKey *key, - const unsigned char *data, int dataLen, uint64_t *chainedIV) -{ - rAssert( dataLen > 0 ); - Lock lock( key->mutex ); +static uint64_t _checksum_64(SSLKey *key, const unsigned char *data, + int dataLen, uint64_t *chainedIV) { + rAssert(dataLen > 0); + Lock lock(key->mutex); + + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int mdLen = EVP_MAX_MD_SIZE; + + HMAC_Init_ex(&key->mac_ctx, 0, 0, 0, 0); + HMAC_Update(&key->mac_ctx, data, dataLen); + if (chainedIV) { + // toss in the chained IV as well + uint64_t tmp = *chainedIV; + unsigned char h[8]; + for (unsigned int i = 0; i < 8; ++i) { + h[i] = tmp & 0xff; + tmp >>= 8; + } + + HMAC_Update(&key->mac_ctx, h, 8); + } + + HMAC_Final(&key->mac_ctx, md, &mdLen); + + rAssert(mdLen >= 8); + + // chop this down to a 64bit value.. + unsigned char h[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + for (unsigned int i = 0; i < (mdLen - 1); ++i) + h[i % 8] ^= (unsigned char)(md[i]); + + uint64_t value = (uint64_t)h[0]; + for (int i = 1; i < 8; ++i) value = (value << 8) | (uint64_t)h[i]; + + return value; +} + +bool SSL_Cipher::randomize(unsigned char *buf, int len, + bool strongRandom) const { + // to avoid warnings of uninitialized data from valgrind + memset(buf, 0, len); + int result; + if (strongRandom) + result = RAND_bytes(buf, len); + else + result = RAND_pseudo_bytes(buf, len); + + if (result != 1) { + char errStr[120]; // specs require string at least 120 bytes long.. + unsigned long errVal = 0; + if ((errVal = ERR_get_error()) != 0) + rWarning("openssl error: %s", ERR_error_string(errVal, errStr)); + + return false; + } else + return true; +} + +uint64_t SSL_Cipher::MAC_64(const unsigned char *data, int len, + const CipherKey &key, uint64_t *chainedIV) const { + shared_ptr mk = dynamic_pointer_cast(key); + uint64_t tmp = _checksum_64(mk.get(), data, len, chainedIV); + + if (chainedIV) *chainedIV = tmp; + + return tmp; +} + +CipherKey SSL_Cipher::readKey(const unsigned char *data, + const CipherKey &masterKey, bool checkKey) { + shared_ptr mk = dynamic_pointer_cast(masterKey); + rAssert(mk->keySize == _keySize); + + unsigned char tmpBuf[MAX_KEYLENGTH + MAX_IVLENGTH]; + + // First N bytes are checksum bytes. + unsigned int checksum = 0; + for (int i = 0; i < KEY_CHECKSUM_BYTES; ++i) + checksum = (checksum << 8) | (unsigned int)data[i]; + + memcpy(tmpBuf, data + KEY_CHECKSUM_BYTES, _keySize + _ivLength); + streamDecode(tmpBuf, _keySize + _ivLength, checksum, masterKey); + + // check for success + unsigned int checksum2 = MAC_32(tmpBuf, _keySize + _ivLength, masterKey); + if (checksum2 != checksum && checkKey) { + rDebug("checksum mismatch: expected %u, got %u", checksum, checksum2); + rDebug("on decode of %i bytes", _keySize + _ivLength); + memset(tmpBuf, 0, sizeof(tmpBuf)); + return CipherKey(); + } + + shared_ptr key(new SSLKey(_keySize, _ivLength)); + + memcpy(key->buffer, tmpBuf, _keySize + _ivLength); + memset(tmpBuf, 0, sizeof(tmpBuf)); + + initKey(key, _blockCipher, _streamCipher, _keySize); + + return key; +} + +void SSL_Cipher::writeKey(const CipherKey &ckey, unsigned char *data, + const CipherKey &masterKey) { + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); + + shared_ptr mk = dynamic_pointer_cast(masterKey); + rAssert(mk->keySize == _keySize); + rAssert(mk->ivLength == _ivLength); + + unsigned char tmpBuf[MAX_KEYLENGTH + MAX_IVLENGTH]; + + int bufLen = _keySize + _ivLength; + memcpy(tmpBuf, key->buffer, bufLen); + + unsigned int checksum = MAC_32(tmpBuf, bufLen, masterKey); + + streamEncode(tmpBuf, bufLen, checksum, masterKey); + memcpy(data + KEY_CHECKSUM_BYTES, tmpBuf, bufLen); + + // first N bytes contain HMAC derived checksum.. + for (int i = 1; i <= KEY_CHECKSUM_BYTES; ++i) { + data[KEY_CHECKSUM_BYTES - i] = checksum & 0xff; + checksum >>= 8; + } + + memset(tmpBuf, 0, sizeof(tmpBuf)); +} + +bool SSL_Cipher::compareKey(const CipherKey &A, const CipherKey &B) const { + shared_ptr key1 = dynamic_pointer_cast(A); + shared_ptr key2 = dynamic_pointer_cast(B); + + rAssert(key1->keySize == _keySize); + rAssert(key2->keySize == _keySize); + + if (memcmp(key1->buffer, key2->buffer, _keySize + _ivLength) != 0) + return false; + else + return true; +} + +int SSL_Cipher::encodedKeySize() const { + return _keySize + _ivLength + KEY_CHECKSUM_BYTES; +} + +int SSL_Cipher::keySize() const { return _keySize; } + +int SSL_Cipher::cipherBlockSize() const { + return EVP_CIPHER_block_size(_blockCipher); +} + +void SSL_Cipher::setIVec(unsigned char *ivec, uint64_t seed, + const shared_ptr &key) const { + if (iface.current() >= 3) { + memcpy(ivec, IVData(key), _ivLength); unsigned char md[EVP_MAX_MD_SIZE]; unsigned int mdLen = EVP_MAX_MD_SIZE; - HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); - HMAC_Update( &key->mac_ctx, data, dataLen ); - if(chainedIV) - { - // toss in the chained IV as well - uint64_t tmp = *chainedIV; - unsigned char h[8]; - for(unsigned int i=0; i<8; ++i) - { - h[i] = tmp & 0xff; - tmp >>= 8; - } - - HMAC_Update( &key->mac_ctx, h, 8 ); + for (int i = 0; i < 8; ++i) { + md[i] = (unsigned char)(seed & 0xff); + seed >>= 8; } - HMAC_Final( &key->mac_ctx, md, &mdLen ); + // combine ivec and seed with HMAC + HMAC_Init_ex(&key->mac_ctx, 0, 0, 0, 0); + HMAC_Update(&key->mac_ctx, ivec, _ivLength); + HMAC_Update(&key->mac_ctx, md, 8); + HMAC_Final(&key->mac_ctx, md, &mdLen); + rAssert(mdLen >= _ivLength); - rAssert(mdLen >= 8); - - // chop this down to a 64bit value.. - unsigned char h[8] = {0,0,0,0,0,0,0,0}; - for(unsigned int i=0; i<(mdLen-1); ++i) - h[i%8] ^= (unsigned char)(md[i]); - - uint64_t value = (uint64_t)h[0]; - for(int i=1; i<8; ++i) - value = (value << 8) | (uint64_t)h[i]; - - return value; -} - -bool SSL_Cipher::randomize( unsigned char *buf, int len, - bool strongRandom ) const -{ - // to avoid warnings of uninitialized data from valgrind - memset(buf, 0, len); - int result; - if(strongRandom) - result = RAND_bytes( buf, len ); - else - result = RAND_pseudo_bytes( buf, len ); - - if(result != 1) - { - char errStr[120]; // specs require string at least 120 bytes long.. - unsigned long errVal = 0; - if((errVal = ERR_get_error()) != 0) - rWarning("openssl error: %s", ERR_error_string( errVal, errStr )); - - return false; - } else - return true; -} - -uint64_t SSL_Cipher::MAC_64( const unsigned char *data, int len, - const CipherKey &key, uint64_t *chainedIV ) const -{ - shared_ptr mk = dynamic_pointer_cast(key); - uint64_t tmp = _checksum_64( mk.get(), data, len, chainedIV ); - - if(chainedIV) - *chainedIV = tmp; - - return tmp; -} - -CipherKey SSL_Cipher::readKey(const unsigned char *data, - const CipherKey &masterKey, bool checkKey) -{ - shared_ptr mk = dynamic_pointer_cast(masterKey); - rAssert(mk->keySize == _keySize); - - unsigned char tmpBuf[ MAX_KEYLENGTH + MAX_IVLENGTH ]; - - // First N bytes are checksum bytes. - unsigned int checksum = 0; - for(int i=0; i key( new SSLKey( _keySize, _ivLength) ); - - memcpy( key->buffer, tmpBuf, _keySize + _ivLength ); - memset( tmpBuf, 0, sizeof(tmpBuf) ); - - initKey( key, _blockCipher, _streamCipher, _keySize ); - - return key; -} - -void SSL_Cipher::writeKey(const CipherKey &ckey, unsigned char *data, - const CipherKey &masterKey) -{ - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - shared_ptr mk = dynamic_pointer_cast(masterKey); - rAssert(mk->keySize == _keySize); - rAssert(mk->ivLength == _ivLength); - - unsigned char tmpBuf[ MAX_KEYLENGTH + MAX_IVLENGTH ]; - - int bufLen = _keySize + _ivLength; - memcpy( tmpBuf, key->buffer, bufLen ); - - unsigned int checksum = MAC_32( tmpBuf, bufLen, masterKey ); - - streamEncode(tmpBuf, bufLen, checksum, masterKey); - memcpy( data+KEY_CHECKSUM_BYTES, tmpBuf, bufLen ); - - // first N bytes contain HMAC derived checksum.. - for(int i=1; i<=KEY_CHECKSUM_BYTES; ++i) - { - data[KEY_CHECKSUM_BYTES-i] = checksum & 0xff; - checksum >>= 8; - } - - memset( tmpBuf, 0, sizeof(tmpBuf) ); -} - -bool SSL_Cipher::compareKey( const CipherKey &A, const CipherKey &B) const -{ - shared_ptr key1 = dynamic_pointer_cast(A); - shared_ptr key2 = dynamic_pointer_cast(B); - - rAssert(key1->keySize == _keySize); - rAssert(key2->keySize == _keySize); - - if(memcmp(key1->buffer, key2->buffer, _keySize + _ivLength) != 0) - return false; - else - return true; -} - -int SSL_Cipher::encodedKeySize() const -{ - return _keySize + _ivLength + KEY_CHECKSUM_BYTES; -} - -int SSL_Cipher::keySize() const -{ - return _keySize; -} - -int SSL_Cipher::cipherBlockSize() const -{ - return EVP_CIPHER_block_size( _blockCipher ); -} - -void SSL_Cipher::setIVec( unsigned char *ivec, uint64_t seed, - const shared_ptr &key) const -{ - if (iface.current() >= 3) - { - memcpy( ivec, IVData(key), _ivLength ); - - unsigned char md[EVP_MAX_MD_SIZE]; - unsigned int mdLen = EVP_MAX_MD_SIZE; - - for(int i=0; i<8; ++i) - { - md[i] = (unsigned char)(seed & 0xff); - seed >>= 8; - } - - // combine ivec and seed with HMAC - HMAC_Init_ex( &key->mac_ctx, 0, 0, 0, 0 ); - HMAC_Update( &key->mac_ctx, ivec, _ivLength ); - HMAC_Update( &key->mac_ctx, md, 8 ); - HMAC_Final( &key->mac_ctx, md, &mdLen ); - rAssert(mdLen >= _ivLength); - - memcpy( ivec, md, _ivLength ); - } else - { - setIVec_old(ivec, seed, key); - } + memcpy(ivec, md, _ivLength); + } else { + setIVec_old(ivec, seed, key); + } } /** For backward compatibility. @@ -712,225 +636,202 @@ void SSL_Cipher::setIVec( unsigned char *ivec, uint64_t seed, determine if the victim had the file in encrypted storage (without decrypting the file). */ -void SSL_Cipher::setIVec_old(unsigned char *ivec, - unsigned int seed, - const shared_ptr &key) const -{ - /* These multiplication constants chosen as they represent (non optimal) - Golumb rulers, the idea being to spread around the information in the - seed. - - 0x060a4011 : ruler length 26, 7 marks, 21 measurable lengths - 0x0221040d : ruler length 25, 7 marks, 21 measurable lengths - */ - unsigned int var1 = 0x060a4011 * seed; - unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); - - memcpy( ivec, IVData(key), _ivLength ); +void SSL_Cipher::setIVec_old(unsigned char *ivec, unsigned int seed, + const shared_ptr &key) const { + /* These multiplication constants chosen as they represent (non optimal) + Golumb rulers, the idea being to spread around the information in the + seed. - ivec[0] ^= (var1 >> 24) & 0xff; - ivec[1] ^= (var2 >> 16) & 0xff; - ivec[2] ^= (var1 >> 8 ) & 0xff; - ivec[3] ^= (var2 ) & 0xff; - ivec[4] ^= (var2 >> 24) & 0xff; - ivec[5] ^= (var1 >> 16) & 0xff; - ivec[6] ^= (var2 >> 8 ) & 0xff; - ivec[7] ^= (var1 ) & 0xff; + 0x060a4011 : ruler length 26, 7 marks, 21 measurable lengths + 0x0221040d : ruler length 25, 7 marks, 21 measurable lengths + */ + unsigned int var1 = 0x060a4011 * seed; + unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C); - if(_ivLength > 8) - { - ivec[8+0] ^= (var1 ) & 0xff; - ivec[8+1] ^= (var2 >> 8 ) & 0xff; - ivec[8+2] ^= (var1 >> 16) & 0xff; - ivec[8+3] ^= (var2 >> 24) & 0xff; - ivec[8+4] ^= (var1 >> 24) & 0xff; - ivec[8+5] ^= (var2 >> 16) & 0xff; - ivec[8+6] ^= (var1 >> 8 ) & 0xff; - ivec[8+7] ^= (var2 ) & 0xff; - } + memcpy(ivec, IVData(key), _ivLength); + + ivec[0] ^= (var1 >> 24) & 0xff; + ivec[1] ^= (var2 >> 16) & 0xff; + ivec[2] ^= (var1 >> 8) & 0xff; + ivec[3] ^= (var2) & 0xff; + ivec[4] ^= (var2 >> 24) & 0xff; + ivec[5] ^= (var1 >> 16) & 0xff; + ivec[6] ^= (var2 >> 8) & 0xff; + ivec[7] ^= (var1) & 0xff; + + if (_ivLength > 8) { + ivec[8 + 0] ^= (var1) & 0xff; + ivec[8 + 1] ^= (var2 >> 8) & 0xff; + ivec[8 + 2] ^= (var1 >> 16) & 0xff; + ivec[8 + 3] ^= (var2 >> 24) & 0xff; + ivec[8 + 4] ^= (var1 >> 24) & 0xff; + ivec[8 + 5] ^= (var2 >> 16) & 0xff; + ivec[8 + 6] ^= (var1 >> 8) & 0xff; + ivec[8 + 7] ^= (var2) & 0xff; + } } -static void flipBytes(unsigned char *buf, int size) -{ - unsigned char revBuf[64]; +static void flipBytes(unsigned char *buf, int size) { + unsigned char revBuf[64]; - int bytesLeft = size; - while(bytesLeft) - { - int toFlip = MIN( sizeof(revBuf), bytesLeft ); + int bytesLeft = size; + while (bytesLeft) { + int toFlip = MIN(sizeof(revBuf), bytesLeft); - for(int i=0; i 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); +bool SSL_Cipher::streamEncode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &ckey) const { + rAssert(size > 0); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); - Lock lock( key->mutex ); + Lock lock(key->mutex); - unsigned char ivec[ MAX_IVLENGTH ]; - int dstLen=0, tmpLen=0; + unsigned char ivec[MAX_IVLENGTH]; + int dstLen = 0, tmpLen = 0; - shuffleBytes( buf, size ); + shuffleBytes(buf, size); - setIVec( ivec, iv64, key ); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); + setIVec(ivec, iv64, key); + EVP_EncryptInit_ex(&key->stream_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate(&key->stream_enc, buf, &dstLen, buf, size); + EVP_EncryptFinal_ex(&key->stream_enc, buf + dstLen, &tmpLen); - flipBytes( buf, size ); - shuffleBytes( buf, size ); + flipBytes(buf, size); + shuffleBytes(buf, size); - setIVec( ivec, iv64 + 1, key ); - EVP_EncryptInit_ex( &key->stream_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->stream_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->stream_enc, buf+dstLen, &tmpLen ); + setIVec(ivec, iv64 + 1, key); + EVP_EncryptInit_ex(&key->stream_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate(&key->stream_enc, buf, &dstLen, buf, size); + EVP_EncryptFinal_ex(&key->stream_enc, buf + dstLen, &tmpLen); - dstLen += tmpLen; - if(dstLen != size) - { - rError("encoding %i bytes, got back %i (%i in final_ex)", - size, dstLen, tmpLen); - } + dstLen += tmpLen; + if (dstLen != size) { + rError("encoding %i bytes, got back %i (%i in final_ex)", size, dstLen, + tmpLen); + } - return true; + return true; } -bool SSL_Cipher::streamDecode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &ckey) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); +bool SSL_Cipher::streamDecode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &ckey) const { + rAssert(size > 0); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); - Lock lock( key->mutex ); + Lock lock(key->mutex); - unsigned char ivec[ MAX_IVLENGTH ]; - int dstLen=0, tmpLen=0; + unsigned char ivec[MAX_IVLENGTH]; + int dstLen = 0, tmpLen = 0; - setIVec( ivec, iv64 + 1, key ); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); + setIVec(ivec, iv64 + 1, key); + EVP_DecryptInit_ex(&key->stream_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate(&key->stream_dec, buf, &dstLen, buf, size); + EVP_DecryptFinal_ex(&key->stream_dec, buf + dstLen, &tmpLen); - unshuffleBytes( buf, size ); - flipBytes( buf, size ); + unshuffleBytes(buf, size); + flipBytes(buf, size); - setIVec( ivec, iv64, key ); - EVP_DecryptInit_ex( &key->stream_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->stream_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->stream_dec, buf+dstLen, &tmpLen ); - - unshuffleBytes( buf, size ); - - dstLen += tmpLen; - if(dstLen != size) - { - rError("encoding %i bytes, got back %i (%i in final_ex)", - size, dstLen, tmpLen); - } + setIVec(ivec, iv64, key); + EVP_DecryptInit_ex(&key->stream_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate(&key->stream_dec, buf, &dstLen, buf, size); + EVP_DecryptFinal_ex(&key->stream_dec, buf + dstLen, &tmpLen); - return true; + unshuffleBytes(buf, size); + + dstLen += tmpLen; + if (dstLen != size) { + rError("encoding %i bytes, got back %i (%i in final_ex)", size, dstLen, + tmpLen); + } + + return true; } +bool SSL_Cipher::blockEncode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &ckey) const { + rAssert(size > 0); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); -bool SSL_Cipher::blockEncode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &ckey ) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); - - // data must be integer number of blocks - const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_enc ); - if(blockMod != 0) - throw ERROR("Invalid data size, not multiple of block size"); - - Lock lock( key->mutex ); + // data must be integer number of blocks + const int blockMod = size % EVP_CIPHER_CTX_block_size(&key->block_enc); + if (blockMod != 0) + throw ERROR("Invalid data size, not multiple of block size"); - unsigned char ivec[ MAX_IVLENGTH ]; + Lock lock(key->mutex); - int dstLen = 0, tmpLen = 0; - setIVec( ivec, iv64, key ); + unsigned char ivec[MAX_IVLENGTH]; - EVP_EncryptInit_ex( &key->block_enc, NULL, NULL, NULL, ivec); - EVP_EncryptUpdate( &key->block_enc, buf, &dstLen, buf, size ); - EVP_EncryptFinal_ex( &key->block_enc, buf+dstLen, &tmpLen ); - dstLen += tmpLen; - - if(dstLen != size) - { - rError("encoding %i bytes, got back %i (%i in final_ex)", - size, dstLen, tmpLen); - } + int dstLen = 0, tmpLen = 0; + setIVec(ivec, iv64, key); - return true; + EVP_EncryptInit_ex(&key->block_enc, NULL, NULL, NULL, ivec); + EVP_EncryptUpdate(&key->block_enc, buf, &dstLen, buf, size); + EVP_EncryptFinal_ex(&key->block_enc, buf + dstLen, &tmpLen); + dstLen += tmpLen; + + if (dstLen != size) { + rError("encoding %i bytes, got back %i (%i in final_ex)", size, dstLen, + tmpLen); + } + + return true; } -bool SSL_Cipher::blockDecode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &ckey ) const -{ - rAssert( size > 0 ); - shared_ptr key = dynamic_pointer_cast(ckey); - rAssert(key->keySize == _keySize); - rAssert(key->ivLength == _ivLength); +bool SSL_Cipher::blockDecode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &ckey) const { + rAssert(size > 0); + shared_ptr key = dynamic_pointer_cast(ckey); + rAssert(key->keySize == _keySize); + rAssert(key->ivLength == _ivLength); - // data must be integer number of blocks - const int blockMod = size % EVP_CIPHER_CTX_block_size( &key->block_dec ); - if(blockMod != 0) - throw ERROR("Invalid data size, not multiple of block size"); - - Lock lock( key->mutex ); + // data must be integer number of blocks + const int blockMod = size % EVP_CIPHER_CTX_block_size(&key->block_dec); + if (blockMod != 0) + throw ERROR("Invalid data size, not multiple of block size"); - unsigned char ivec[ MAX_IVLENGTH ]; + Lock lock(key->mutex); - int dstLen = 0, tmpLen = 0; - setIVec( ivec, iv64, key ); + unsigned char ivec[MAX_IVLENGTH]; - EVP_DecryptInit_ex( &key->block_dec, NULL, NULL, NULL, ivec); - EVP_DecryptUpdate( &key->block_dec, buf, &dstLen, buf, size ); - EVP_DecryptFinal_ex( &key->block_dec, buf+dstLen, &tmpLen ); - dstLen += tmpLen; - - if(dstLen != size) - { - rError("decoding %i bytes, got back %i (%i in final_ex)", - size, dstLen, tmpLen); - } + int dstLen = 0, tmpLen = 0; + setIVec(ivec, iv64, key); - return true; -} - -bool SSL_Cipher::Enabled() -{ - return true; + EVP_DecryptInit_ex(&key->block_dec, NULL, NULL, NULL, ivec); + EVP_DecryptUpdate(&key->block_dec, buf, &dstLen, buf, size); + EVP_DecryptFinal_ex(&key->block_dec, buf + dstLen, &tmpLen); + dstLen += tmpLen; + + if (dstLen != size) { + rError("decoding %i bytes, got back %i (%i in final_ex)", size, dstLen, + tmpLen); + } + + return true; } +bool SSL_Cipher::Enabled() { return true; } diff --git a/encfs/SSL_Cipher.h b/encfs/SSL_Cipher.h index efadde7..f79f2b5 100644 --- a/encfs/SSL_Cipher.h +++ b/encfs/SSL_Cipher.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -55,9 +55,9 @@ typedef struct evp_cipher_st EVP_CIPHER; as: 1. shuffle 2. encrypt - 3. reverse - 4. shuffle - 5. encrypt + 3. reverse + 4. shuffle + 5. encrypt The reason for the shuffle and reverse steps (and the second encrypt pass) is to try and propogate any changed bits to a larger set. If only a single pass was made with the stream cipher in CFB mode, then a change to one byte @@ -68,83 +68,78 @@ typedef struct evp_cipher_st EVP_CIPHER; initial value vector to randomize the output. But it makes the code simpler to reuse the encryption algorithm as is. */ -class SSL_Cipher : public Cipher -{ - rel::Interface iface; - rel::Interface realIface; - const EVP_CIPHER *_blockCipher; - const EVP_CIPHER *_streamCipher; - unsigned int _keySize; // in bytes - unsigned int _ivLength; +class SSL_Cipher : public Cipher { + rel::Interface iface; + rel::Interface realIface; + const EVP_CIPHER *_blockCipher; + const EVP_CIPHER *_streamCipher; + unsigned int _keySize; // in bytes + unsigned int _ivLength; -public: - SSL_Cipher(const rel::Interface &iface, const rel::Interface &realIface, - const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, - int keyLength); - virtual ~SSL_Cipher(); + public: + SSL_Cipher(const rel::Interface &iface, const rel::Interface &realIface, + const EVP_CIPHER *blockCipher, const EVP_CIPHER *streamCipher, + int keyLength); + virtual ~SSL_Cipher(); - // returns the real interface, not the one we're emulating (if any).. - virtual rel::Interface interface() const; + // returns the real interface, not the one we're emulating (if any).. + virtual rel::Interface interface() const; - // create a new key based on a password - virtual CipherKey newKey(const char *password, int passwdLength, - int &iterationCount, long desiredDuration, - const unsigned char *salt, int saltLen); - // deprecated - for backward compatibility - virtual CipherKey newKey(const char *password, int passwdLength); - // create a new random key - virtual CipherKey newRandomKey(); + // create a new key based on a password + virtual CipherKey newKey(const char *password, int passwdLength, + int &iterationCount, long desiredDuration, + const unsigned char *salt, int saltLen); + // deprecated - for backward compatibility + virtual CipherKey newKey(const char *password, int passwdLength); + // create a new random key + virtual CipherKey newRandomKey(); - // data must be len keySize() - virtual CipherKey readKey(const unsigned char *data, - const CipherKey &encodingKey, - bool checkKey); - virtual void writeKey(const CipherKey &key, unsigned char *data, - const CipherKey &encodingKey); - virtual bool compareKey( const CipherKey &A, - const CipherKey &B ) const; + // data must be len keySize() + virtual CipherKey readKey(const unsigned char *data, + const CipherKey &encodingKey, bool checkKey); + virtual void writeKey(const CipherKey &key, unsigned char *data, + const CipherKey &encodingKey); + virtual bool compareKey(const CipherKey &A, const CipherKey &B) const; - // meta-data about the cypher - virtual int keySize() const; - virtual int encodedKeySize() const; - virtual int cipherBlockSize() const; + // meta-data about the cypher + virtual int keySize() const; + virtual int encodedKeySize() const; + virtual int cipherBlockSize() const; - virtual bool randomize( unsigned char *buf, int len, - bool strongRandom ) const; + virtual bool randomize(unsigned char *buf, int len, bool strongRandom) const; - virtual uint64_t MAC_64( const unsigned char *src, int len, - const CipherKey &key, uint64_t *augment ) const; + virtual uint64_t MAC_64(const unsigned char *src, int len, + const CipherKey &key, uint64_t *augment) const; - // functional interfaces - /* - Stream encoding in-place. - */ - virtual bool streamEncode(unsigned char *in, int len, - uint64_t iv64, const CipherKey &key) const; - virtual bool streamDecode(unsigned char *in, int len, - uint64_t iv64, const CipherKey &key) const; + // functional interfaces + /* + Stream encoding in-place. + */ + virtual bool streamEncode(unsigned char *in, int len, uint64_t iv64, + const CipherKey &key) const; + virtual bool streamDecode(unsigned char *in, int len, uint64_t iv64, + const CipherKey &key) const; - /* - Block encoding is done in-place. Partial blocks are supported, but - blocks are always expected to begin on a block boundary. See - blockSize(). - */ - virtual bool blockEncode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &key) const; - virtual bool blockDecode(unsigned char *buf, int size, - uint64_t iv64, const CipherKey &key) const; + /* + Block encoding is done in-place. Partial blocks are supported, but + blocks are always expected to begin on a block boundary. See + blockSize(). + */ + virtual bool blockEncode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &key) const; + virtual bool blockDecode(unsigned char *buf, int size, uint64_t iv64, + const CipherKey &key) const; - // hack to help with static builds - static bool Enabled(); -private: - void setIVec( unsigned char *ivec, uint64_t seed, - const shared_ptr &key ) const; + // hack to help with static builds + static bool Enabled(); - // deprecated - for backward compatibility - void setIVec_old( unsigned char *ivec, unsigned int seed, - const shared_ptr &key ) const; + private: + void setIVec(unsigned char *ivec, uint64_t seed, + const shared_ptr &key) const; + + // deprecated - for backward compatibility + void setIVec_old(unsigned char *ivec, unsigned int seed, + const shared_ptr &key) const; }; - #endif - diff --git a/encfs/StreamNameIO.cpp b/encfs/StreamNameIO.cpp index 722c691..1cd94f9 100644 --- a/encfs/StreamNameIO.cpp +++ b/encfs/StreamNameIO.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -32,17 +32,16 @@ using namespace rel; using namespace std; -static shared_ptr NewStreamNameIO( const Interface &iface, - const shared_ptr &cipher, const CipherKey &key) -{ - return shared_ptr( new StreamNameIO( iface, cipher, key ) ); +static shared_ptr NewStreamNameIO(const Interface &iface, + const shared_ptr &cipher, + const CipherKey &key) { + return shared_ptr(new StreamNameIO(iface, cipher, key)); } -static bool StreamIO_registered = NameIO::Register("Stream", - gettext_noop("Stream encoding, keeps filenames as short as possible"), - StreamNameIO::CurrentInterface(), - NewStreamNameIO); - +static bool StreamIO_registered = NameIO::Register( + "Stream", + gettext_noop("Stream encoding, keeps filenames as short as possible"), + StreamNameIO::CurrentInterface(), NewStreamNameIO); /* - Version 0.1 is for EncFS 0.x support. The difference to 1.0 is that 0.x @@ -65,145 +64,117 @@ static bool StreamIO_registered = NameIO::Register("Stream", - Version 2.1 adds support for version 0 for EncFS 0.x compatibility. */ -Interface StreamNameIO::CurrentInterface() -{ - // implement major version 2, 1, and 0 - return Interface("nameio/stream", 2, 1, 2); +Interface StreamNameIO::CurrentInterface() { + // implement major version 2, 1, and 0 + return Interface("nameio/stream", 2, 1, 2); } -StreamNameIO::StreamNameIO( const rel::Interface &iface, - const shared_ptr &cipher, - const CipherKey &key ) - : _interface( iface.current() ) - , _cipher( cipher ) - , _key( key ) -{ +StreamNameIO::StreamNameIO(const rel::Interface &iface, + const shared_ptr &cipher, + const CipherKey &key) + : _interface(iface.current()), _cipher(cipher), _key(key) {} +StreamNameIO::~StreamNameIO() {} + +Interface StreamNameIO::interface() const { return CurrentInterface(); } + +int StreamNameIO::maxEncodedNameLen(int plaintextStreamLen) const { + int encodedStreamLen = 2 + plaintextStreamLen; + return B256ToB64Bytes(encodedStreamLen); } -StreamNameIO::~StreamNameIO() -{ +int StreamNameIO::maxDecodedNameLen(int encodedStreamLen) const { + int decLen256 = B64ToB256Bytes(encodedStreamLen); + return decLen256 - 2; } -Interface StreamNameIO::interface() const -{ - return CurrentInterface(); +int StreamNameIO::encodeName(const char *plaintextName, int length, + uint64_t *iv, char *encodedName) const { + uint64_t tmpIV = 0; + if (iv && _interface >= 2) tmpIV = *iv; + + unsigned int mac = + _cipher->MAC_16((const unsigned char *)plaintextName, length, _key, iv); + + // add on checksum bytes + unsigned char *encodeBegin; + if (_interface >= 1) { + // current versions store the checksum at the beginning + encodedName[0] = (mac >> 8) & 0xff; + encodedName[1] = (mac) & 0xff; + encodeBegin = (unsigned char *)encodedName + 2; + } else { + // encfs 0.x stored checksums at the end. + encodedName[length] = (mac >> 8) & 0xff; + encodedName[length + 1] = (mac) & 0xff; + encodeBegin = (unsigned char *)encodedName; + } + + // stream encode the plaintext bytes + memcpy(encodeBegin, plaintextName, length); + _cipher->nameEncode(encodeBegin, length, (uint64_t)mac ^ tmpIV, _key); + + // convert the entire thing to base 64 ascii.. + int encodedStreamLen = length + 2; + int encLen64 = B256ToB64Bytes(encodedStreamLen); + + changeBase2Inline((unsigned char *)encodedName, encodedStreamLen, 8, 6, true); + B64ToAscii((unsigned char *)encodedName, encLen64); + + return encLen64; } -int StreamNameIO::maxEncodedNameLen( int plaintextStreamLen ) const -{ - int encodedStreamLen = 2 + plaintextStreamLen; - return B256ToB64Bytes( encodedStreamLen ); -} - -int StreamNameIO::maxDecodedNameLen( int encodedStreamLen ) const -{ - int decLen256 = B64ToB256Bytes( encodedStreamLen ); - return decLen256 - 2; -} - -int StreamNameIO::encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const -{ - uint64_t tmpIV = 0; - if( iv && _interface >= 2 ) - tmpIV = *iv; - - unsigned int mac = _cipher->MAC_16( (const unsigned char *)plaintextName, - length, _key, iv ); - - // add on checksum bytes - unsigned char *encodeBegin; - if(_interface >= 1) - { - // current versions store the checksum at the beginning - encodedName[0] = (mac >> 8) & 0xff; - encodedName[1] = (mac ) & 0xff; - encodeBegin = (unsigned char *)encodedName+2; - } else - { - // encfs 0.x stored checksums at the end. - encodedName[length] = (mac >> 8) & 0xff; - encodedName[length+1] = (mac ) & 0xff; - encodeBegin = (unsigned char *)encodedName; - } - - // stream encode the plaintext bytes - memcpy( encodeBegin, plaintextName, length ); - _cipher->nameEncode( encodeBegin, length, (uint64_t)mac ^ tmpIV, _key); - - // convert the entire thing to base 64 ascii.. - int encodedStreamLen = length + 2; - int encLen64 = B256ToB64Bytes( encodedStreamLen ); - - changeBase2Inline( (unsigned char *)encodedName, encodedStreamLen, - 8, 6, true ); - B64ToAscii( (unsigned char *)encodedName, encLen64 ); - - return encLen64; -} - -int StreamNameIO::decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const -{ - rAssert(length > 2); - int decLen256 = B64ToB256Bytes( length ); - int decodedStreamLen = decLen256 - 2; - - if(decodedStreamLen <= 0) - throw ERROR("Filename too small to decode"); - - BUFFER_INIT( tmpBuf, 32, (unsigned int)length ); - - // decode into tmpBuf, because this step produces more data then we can fit - // into the result buffer.. - AsciiToB64( (unsigned char *)tmpBuf, (unsigned char *)encodedName, length ); - changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); - - // pull out the checksum value which is used as an initialization vector - uint64_t tmpIV = 0; - unsigned int mac; - if(_interface >= 1) - { - // current versions store the checksum at the beginning - mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 - | ((unsigned int)((unsigned char)tmpBuf[1])); - - // version 2 adds support for IV chaining.. - if( iv && _interface >= 2 ) - tmpIV = *iv; - - memcpy( plaintextName, tmpBuf+2, decodedStreamLen ); - } else - { - // encfs 0.x stored checksums at the end. - mac = ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen])) << 8 - | ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen+1])); - - memcpy( plaintextName, tmpBuf, decodedStreamLen ); - } - - // use nameDeocde instead of streamDecode for backward compatibility - _cipher->nameDecode( (unsigned char *)plaintextName, decodedStreamLen, - (uint64_t)mac ^ tmpIV, _key); - - // compute MAC to check with stored value - unsigned int mac2 = _cipher->MAC_16((const unsigned char *)plaintextName, - decodedStreamLen, _key, iv); - - BUFFER_RESET( tmpBuf ); - if(mac2 != mac) - { - rDebug("checksum mismatch: expected %u, got %u", mac, mac2); - rDebug("on decode of %i bytes", decodedStreamLen); - throw ERROR( "checksum mismatch in filename decode" ); - } - - return decodedStreamLen; -} - -bool StreamNameIO::Enabled() -{ - return true; +int StreamNameIO::decodeName(const char *encodedName, int length, uint64_t *iv, + char *plaintextName) const { + rAssert(length > 2); + int decLen256 = B64ToB256Bytes(length); + int decodedStreamLen = decLen256 - 2; + + if (decodedStreamLen <= 0) throw ERROR("Filename too small to decode"); + + BUFFER_INIT(tmpBuf, 32, (unsigned int)length); + + // decode into tmpBuf, because this step produces more data then we can fit + // into the result buffer.. + AsciiToB64((unsigned char *)tmpBuf, (unsigned char *)encodedName, length); + changeBase2Inline((unsigned char *)tmpBuf, length, 6, 8, false); + + // pull out the checksum value which is used as an initialization vector + uint64_t tmpIV = 0; + unsigned int mac; + if (_interface >= 1) { + // current versions store the checksum at the beginning + mac = ((unsigned int)((unsigned char)tmpBuf[0])) << 8 | + ((unsigned int)((unsigned char)tmpBuf[1])); + + // version 2 adds support for IV chaining.. + if (iv && _interface >= 2) tmpIV = *iv; + + memcpy(plaintextName, tmpBuf + 2, decodedStreamLen); + } else { + // encfs 0.x stored checksums at the end. + mac = ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen])) << 8 | + ((unsigned int)((unsigned char)tmpBuf[decodedStreamLen + 1])); + + memcpy(plaintextName, tmpBuf, decodedStreamLen); + } + + // use nameDeocde instead of streamDecode for backward compatibility + _cipher->nameDecode((unsigned char *)plaintextName, decodedStreamLen, + (uint64_t)mac ^ tmpIV, _key); + + // compute MAC to check with stored value + unsigned int mac2 = _cipher->MAC_16((const unsigned char *)plaintextName, + decodedStreamLen, _key, iv); + + BUFFER_RESET(tmpBuf); + if (mac2 != mac) { + rDebug("checksum mismatch: expected %u, got %u", mac, mac2); + rDebug("on decode of %i bytes", decodedStreamLen); + throw ERROR("checksum mismatch in filename decode"); + } + + return decodedStreamLen; } +bool StreamNameIO::Enabled() { return true; } diff --git a/encfs/StreamNameIO.h b/encfs/StreamNameIO.h index 3d83c19..3149ce8 100644 --- a/encfs/StreamNameIO.h +++ b/encfs/StreamNameIO.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -26,34 +26,32 @@ class Cipher; -class StreamNameIO : public NameIO -{ -public: - static rel::Interface CurrentInterface(); +class StreamNameIO : public NameIO { + public: + static rel::Interface CurrentInterface(); - StreamNameIO( const rel::Interface &iface, - const shared_ptr &cipher, - const CipherKey &key ); - virtual ~StreamNameIO(); + StreamNameIO(const rel::Interface &iface, const shared_ptr &cipher, + const CipherKey &key); + virtual ~StreamNameIO(); - virtual rel::Interface interface() const; + virtual rel::Interface interface() const; - virtual int maxEncodedNameLen( int plaintextNameLen ) const; - virtual int maxDecodedNameLen( int encodedNameLen ) const; + virtual int maxEncodedNameLen(int plaintextNameLen) const; + virtual int maxDecodedNameLen(int encodedNameLen) const; - // hack to help with static builds - static bool Enabled(); -protected: - virtual int encodeName( const char *plaintextName, int length, - uint64_t *iv, char *encodedName ) const; - virtual int decodeName( const char *encodedName, int length, - uint64_t *iv, char *plaintextName ) const; -private: - int _interface; - shared_ptr _cipher; - CipherKey _key; + // hack to help with static builds + static bool Enabled(); + + protected: + virtual int encodeName(const char *plaintextName, int length, uint64_t *iv, + char *encodedName) const; + virtual int decodeName(const char *encodedName, int length, uint64_t *iv, + char *plaintextName) const; + + private: + int _interface; + shared_ptr _cipher; + CipherKey _key; }; - #endif - diff --git a/encfs/base64.cpp b/encfs/base64.cpp index 2f0da1d..bd2e768 100644 --- a/encfs/base64.cpp +++ b/encfs/base64.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -27,32 +27,28 @@ // It is the caller's responsibility to make sure the output array is large // enough. void changeBase2(unsigned char *src, int srcLen, int src2Pow, - unsigned char *dst, int dstLen, int dst2Pow) -{ - unsigned long work = 0; - int workBits = 0; // number of bits left in the work buffer - unsigned char *end = src + srcLen; - unsigned char *origDst = dst; - const int mask = (1 << dst2Pow) -1; + unsigned char *dst, int dstLen, int dst2Pow) { + unsigned long work = 0; + int workBits = 0; // number of bits left in the work buffer + unsigned char *end = src + srcLen; + unsigned char *origDst = dst; + const int mask = (1 << dst2Pow) - 1; - // copy the new bits onto the high bits of the stream. - // The bits that fall off the low end are the output bits. - while(src != end) - { - work |= ((unsigned long)(*src++)) << workBits; - workBits += src2Pow; + // copy the new bits onto the high bits of the stream. + // The bits that fall off the low end are the output bits. + while (src != end) { + work |= ((unsigned long)(*src++)) << workBits; + workBits += src2Pow; - while(workBits >= dst2Pow) - { - *dst++ = work & mask; - work >>= dst2Pow; - workBits -= dst2Pow; - } + while (workBits >= dst2Pow) { + *dst++ = work & mask; + work >>= dst2Pow; + workBits -= dst2Pow; } + } - // now, we could have a partial value left in the work buffer.. - if(workBits && ((dst - origDst) < dstLen)) - *dst++ = work & mask; + // now, we could have a partial value left in the work buffer.. + if (workBits && ((dst - origDst) < dstLen)) *dst++ = work & mask; } /* @@ -62,65 +58,52 @@ void changeBase2(unsigned char *src, int srcLen, int src2Pow, Uses the stack to store output values. Recurse every time a new value is to be written, then write the value at the tail end of the recursion. */ -static -void changeBase2Inline(unsigned char *src, int srcLen, - int src2Pow, int dst2Pow, - bool outputPartialLastByte, - unsigned long work, - int workBits, - unsigned char *outLoc) -{ - const int mask = (1 << dst2Pow) -1; - if(!outLoc) - outLoc = src; +static void changeBase2Inline(unsigned char *src, int srcLen, int src2Pow, + int dst2Pow, bool outputPartialLastByte, + unsigned long work, int workBits, + unsigned char *outLoc) { + const int mask = (1 << dst2Pow) - 1; + if (!outLoc) outLoc = src; - // copy the new bits onto the high bits of the stream. - // The bits that fall off the low end are the output bits. - while(srcLen && workBits < dst2Pow) - { - work |= ((unsigned long)(*src++)) << workBits; - workBits += src2Pow; - --srcLen; - } - - // we have at least one value that can be output - unsigned char outVal = work & mask; - work >>= dst2Pow; - workBits -= dst2Pow; - - if(srcLen) - { - // more input left, so recurse - changeBase2Inline( src, srcLen, src2Pow, dst2Pow, - outputPartialLastByte, work, workBits, outLoc+1); - *outLoc = outVal; - } else - { - // no input left, we can write remaining values directly - *outLoc++ = outVal; - - // we could have a partial value left in the work buffer.. - if(outputPartialLastByte) - { - while(workBits > 0) - { - *outLoc++ = work & mask; - work >>= dst2Pow; - workBits -= dst2Pow; - } - } + // copy the new bits onto the high bits of the stream. + // The bits that fall off the low end are the output bits. + while (srcLen && workBits < dst2Pow) { + work |= ((unsigned long)(*src++)) << workBits; + workBits += src2Pow; + --srcLen; + } + + // we have at least one value that can be output + unsigned char outVal = work & mask; + work >>= dst2Pow; + workBits -= dst2Pow; + + if (srcLen) { + // more input left, so recurse + changeBase2Inline(src, srcLen, src2Pow, dst2Pow, outputPartialLastByte, + work, workBits, outLoc + 1); + *outLoc = outVal; + } else { + // no input left, we can write remaining values directly + *outLoc++ = outVal; + + // we could have a partial value left in the work buffer.. + if (outputPartialLastByte) { + while (workBits > 0) { + *outLoc++ = work & mask; + work >>= dst2Pow; + workBits -= dst2Pow; + } } + } } -void changeBase2Inline(unsigned char *src, int srcLen, - int src2Pow, int dst2Pow, - bool outputPartialLastByte) -{ - changeBase2Inline(src, srcLen, src2Pow, dst2Pow, - outputPartialLastByte, 0, 0, 0); +void changeBase2Inline(unsigned char *src, int srcLen, int src2Pow, int dst2Pow, + bool outputPartialLastByte) { + changeBase2Inline(src, srcLen, src2Pow, dst2Pow, outputPartialLastByte, 0, 0, + 0); } - // character set for ascii b64: // ",-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // a standard base64 (eg a64l doesn't use ',-' but uses './'. We don't @@ -128,83 +111,69 @@ void changeBase2Inline(unsigned char *src, int srcLen, // '.' included in the encrypted names, so that it can be reserved for files // with special meaning. static const char B642AsciiTable[] = ",-0123456789"; -void B64ToAscii(unsigned char *in, int length) -{ - for(int offset=0; offset 11) - { - if(ch > 37) - ch += 'a' - 38; - else - ch += 'A' - 12; - } else - ch = B642AsciiTable[ ch ]; - - in[offset] = ch; - } +void B64ToAscii(unsigned char *in, int length) { + for (int offset = 0; offset < length; ++offset) { + int ch = in[offset]; + if (ch > 11) { + if (ch > 37) + ch += 'a' - 38; + else + ch += 'A' - 12; + } else + ch = B642AsciiTable[ch]; + + in[offset] = ch; + } } -static const unsigned char Ascii2B64Table[] = - " 01 23456789:; "; - // 0123456789 123456789 123456789 123456789 123456789 123456789 1234 - // 0 1 2 3 4 5 6 -void AsciiToB64(unsigned char *in, int length) -{ - return AsciiToB64(in, in, length); +static const unsigned char Ascii2B64Table[] = + " 01 23456789:; "; +// 0123456789 123456789 123456789 123456789 123456789 123456789 1234 +// 0 1 2 3 4 5 6 +void AsciiToB64(unsigned char *in, int length) { + return AsciiToB64(in, in, length); } -void AsciiToB64(unsigned char *out, const unsigned char *in, int length) -{ - while(length--) - { - unsigned char ch = *in++; - if(ch >= 'A') - { - if(ch >= 'a') - ch += 38 - 'a'; - else - ch += 12 - 'A'; - } else - ch = Ascii2B64Table[ ch ] - '0'; +void AsciiToB64(unsigned char *out, const unsigned char *in, int length) { + while (length--) { + unsigned char ch = *in++; + if (ch >= 'A') { + if (ch >= 'a') + ch += 38 - 'a'; + else + ch += 12 - 'A'; + } else + ch = Ascii2B64Table[ch] - '0'; - *out++ = ch; - } + *out++ = ch; + } } +void B32ToAscii(unsigned char *buf, int len) { + for (int offset = 0; offset < len; ++offset) { + int ch = buf[offset]; + if (ch >= 0 && ch < 26) + ch += 'A'; + else + ch += '2' - 26; -void B32ToAscii(unsigned char *buf, int len) -{ - for(int offset=0; offset= 0 && ch < 26) - ch += 'A'; - else - ch += '2' - 26; - - buf[offset] = ch; - } + buf[offset] = ch; + } } -void AsciiToB32(unsigned char *in, int length) -{ - return AsciiToB32(in, in, length); +void AsciiToB32(unsigned char *in, int length) { + return AsciiToB32(in, in, length); } -void AsciiToB32(unsigned char *out, const unsigned char *in, int length) -{ - while(length--) - { - unsigned char ch = *in++; - int lch = toupper(ch); - if (lch >= 'A') - lch -= 'A'; - else - lch += 26 - '2'; +void AsciiToB32(unsigned char *out, const unsigned char *in, int length) { + while (length--) { + unsigned char ch = *in++; + int lch = toupper(ch); + if (lch >= 'A') + lch -= 'A'; + else + lch += 26 - '2'; - *out++ = (unsigned char)lch; - } + *out++ = (unsigned char)lch; + } } - diff --git a/encfs/base64.h b/encfs/base64.h index 2d7b9b2..2b648ed 100644 --- a/encfs/base64.h +++ b/encfs/base64.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -21,41 +21,33 @@ #ifndef _base64_incl_ #define _base64_incl_ - -inline int B64ToB256Bytes( int numB64Bytes ) -{ - return (numB64Bytes * 6) / 8; // round down +inline int B64ToB256Bytes(int numB64Bytes) { + return (numB64Bytes * 6) / 8; // round down } -inline int B32ToB256Bytes( int numB32Bytes ) -{ - return (numB32Bytes * 5) / 8; // round down +inline int B32ToB256Bytes(int numB32Bytes) { + return (numB32Bytes * 5) / 8; // round down } -inline int B256ToB64Bytes( int numB256Bytes ) -{ - return (numB256Bytes * 8 + 5) / 6; // round up +inline int B256ToB64Bytes(int numB256Bytes) { + return (numB256Bytes * 8 + 5) / 6; // round up } -inline int B256ToB32Bytes( int numB256Bytes ) -{ - return (numB256Bytes * 8 + 4) / 5; // round up +inline int B256ToB32Bytes(int numB256Bytes) { + return (numB256Bytes * 8 + 4) / 5; // round up } - /* convert data between different bases - each being a power of 2. */ void changeBase2(unsigned char *src, int srcLength, int srcPow2, - unsigned char *dst, int dstLength, int dstPow2); + unsigned char *dst, int dstLength, int dstPow2); /* same as changeBase2, but writes output over the top of input data. */ -void changeBase2Inline(unsigned char *buf, int srcLength, - int srcPow2, int dst2Pow, - bool outputPartialLastByte); - +void changeBase2Inline(unsigned char *buf, int srcLength, int srcPow2, + int dst2Pow, bool outputPartialLastByte); // inplace translation from values [0,2^6] => base64 ASCII void B64ToAscii(unsigned char *buf, int length); @@ -71,4 +63,3 @@ void AsciiToB32(unsigned char *buf, int length); void AsciiToB32(unsigned char *out, const unsigned char *in, int length); #endif - diff --git a/encfs/boost-versioning.h b/encfs/boost-versioning.h index b3cfa51..8d9c00e 100644 --- a/encfs/boost-versioning.h +++ b/encfs/boost-versioning.h @@ -4,7 +4,6 @@ // This header stores workaround code for dealing with incompatible changes // made to boost archive/serialization classes. - #if (BOOST_VERSION <= 104100) // Easy case, boost archive serial numbers are sizeof(int) BOOST_CLASS_VERSION(EncFSConfig, V6SubVersion) @@ -23,74 +22,60 @@ BOOST_CLASS_VERSION(EncFSConfig, V6SubVersion) BOOST_CLASS_VERSION(EncFSConfig, 20) - namespace boost { namespace archive { namespace detail { - // Specialize iserializer class in order to get rid of version check -template -class iserializer : public basic_iserializer -{ -private: - virtual void destroy(/*const*/ void *address) const { - boost::serialization::access::destroy(static_cast(address)); - } -protected: - explicit iserializer() : - basic_iserializer( - boost::serialization::singleton< - BOOST_DEDUCED_TYPENAME - boost::serialization::type_info_implementation::type - >::get_const_instance() - ) - {} -public: - virtual BOOST_DLLEXPORT void load_object_data( - basic_iarchive & ar, - void *x, - const unsigned int file_version - ) const BOOST_USED; - virtual bool class_info() const { - return boost::serialization::implementation_level::value - >= boost::serialization::object_class_info; - } - virtual bool tracking(const unsigned int /* flags */) const { - return boost::serialization::tracking_level::value - == boost::serialization::track_always - || ( boost::serialization::tracking_level::value - == boost::serialization::track_selectively - && serialized_as_pointer()); - } - virtual version_type version() const { - return version_type(::boost::serialization::version::value); - } - virtual bool is_polymorphic() const { - return boost::is_polymorphic::value; - } - virtual ~iserializer(){}; +template +class iserializer : public basic_iserializer { + private: + virtual void destroy(/*const*/ void *address) const { + boost::serialization::access::destroy(static_cast(address)); + } + + protected: + explicit iserializer() + : basic_iserializer(boost::serialization::singleton< + BOOST_DEDUCED_TYPENAME boost::serialization:: + type_info_implementation< + EncFSConfig>::type>::get_const_instance()) {} + + public: + virtual BOOST_DLLEXPORT void load_object_data( + basic_iarchive &ar, void *x, + const unsigned int file_version) const BOOST_USED; + virtual bool class_info() const { + return boost::serialization::implementation_level::value >= + boost::serialization::object_class_info; + } + virtual bool tracking(const unsigned int /* flags */) const { + return boost::serialization::tracking_level::value == + boost::serialization::track_always || + (boost::serialization::tracking_level::value == + boost::serialization::track_selectively && + serialized_as_pointer()); + } + virtual version_type version() const { + return version_type(::boost::serialization::version::value); + } + virtual bool is_polymorphic() const { + return boost::is_polymorphic::value; + } + virtual ~iserializer() {}; }; -template +template BOOST_DLLEXPORT void iserializer::load_object_data( - basic_iarchive & ar, - void *x, - const unsigned int file_version -) const { - boost::serialization::serialize_adl( - boost::serialization::smart_cast_reference(ar), - * static_cast(x), - file_version - ); + basic_iarchive &ar, void *x, const unsigned int file_version) const { + boost::serialization::serialize_adl( + boost::serialization::smart_cast_reference(ar), + *static_cast(x), file_version); } - } } } #endif - - -#endif // BOOST_VERSIONING_INCL +#endif // BOOST_VERSIONING_INCL diff --git a/encfs/encfs.cpp b/encfs/encfs.cpp index fcade38..087c4ff 100644 --- a/encfs/encfs.cpp +++ b/encfs/encfs.cpp @@ -3,8 +3,8 @@ * ***************************************************************************** * Copyright (c) 2003-2007, Valient Gough - * - * This program is free software; you can distribute it and/or modify it under + * + * This program is free software; you can distribute it and/or modify it under * the terms of the GNU General Public License (GPL), as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. @@ -37,7 +37,6 @@ #include #endif - #include #include @@ -54,7 +53,7 @@ #include #ifndef MIN -#define MIN(a,b) (((a)<(b)) ? (a): (b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif #define ESUCCESS 0 @@ -64,84 +63,76 @@ using namespace rlog; using rel::Lock; using namespace boost; -#define GET_FN(ctx, finfo) ctx->getNode((void*)(uintptr_t)finfo->fh) +#define GET_FN(ctx, finfo) ctx->getNode((void *)(uintptr_t)finfo->fh) static RLogChannel *Info = DEF_CHANNEL("info", Log_Info); -static EncFS_Context * context() -{ - return (EncFS_Context*)fuse_get_context()->private_data; +static EncFS_Context *context() { + return (EncFS_Context *)fuse_get_context()->private_data; } // helper function -- apply a functor to a cipher path, given the plain path -template -static int withCipherPath( const char *opName, const char *path, - int (*op)(EncFS_Context *, const string &name, T data ), T data, - bool passReturnCode = false ) -{ - EncFS_Context *ctx = context(); +template +static int withCipherPath(const char *opName, const char *path, + int (*op)(EncFS_Context *, const string &name, + T data), + T data, bool passReturnCode = false) { + EncFS_Context *ctx = context(); - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; - try - { - string cyName = FSRoot->cipherPath( path ); - rLog(Info, "%s %s", opName, cyName.c_str()); + try { + string cyName = FSRoot->cipherPath(path); + rLog(Info, "%s %s", opName, cyName.c_str()); - res = op( ctx, cyName, data ); - - if(res == -1) - { - int eno = errno; - rInfo("%s error: %s", opName, strerror(eno)); - res = -eno; - } else if(!passReturnCode) - res = ESUCCESS; - } catch( rlog::Error &err ) - { - rError("error caught in %s", opName); - err.log( _RLWarningChannel ); - } - return res; + res = op(ctx, cyName, data); + + if (res == -1) { + int eno = errno; + rInfo("%s error: %s", opName, strerror(eno)); + res = -eno; + } else if (!passReturnCode) + res = ESUCCESS; + } + catch (rlog::Error &err) { + rError("error caught in %s", opName); + err.log(_RLWarningChannel); + } + return res; } // helper function -- apply a functor to a node -template -static int withFileNode( const char *opName, - const char *path, struct fuse_file_info *fi, - int (*op)(FileNode *, T data ), T data ) -{ - EncFS_Context *ctx = context(); +template +static int withFileNode(const char *opName, const char *path, + struct fuse_file_info *fi, + int (*op)(FileNode *, T data), T data) { + EncFS_Context *ctx = context(); - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; - try - { - shared_ptr fnode; + try { + shared_ptr fnode; - if(fi != NULL) - fnode = GET_FN(ctx, fi); - else - fnode = FSRoot->lookupNode( path, opName ); + if (fi != NULL) + fnode = GET_FN(ctx, fi); + else + fnode = FSRoot->lookupNode(path, opName); - rAssert(fnode.get() != NULL); - rLog(Info, "%s %s", opName, fnode->cipherName()); - res = op( fnode.get(), data ); + rAssert(fnode.get() != NULL); + rLog(Info, "%s %s", opName, fnode->cipherName()); + res = op(fnode.get(), data); - if(res < 0) - rInfo("%s error: %s", opName, strerror(-res)); - } catch( rlog::Error &err ) - { - rError("error caught in %s", opName); - err.log( _RLWarningChannel ); - } - return res; + if (res < 0) rInfo("%s error: %s", opName, strerror(-res)); + } + catch (rlog::Error &err) { + rError("error caught in %s", opName); + err.log(_RLWarningChannel); + } + return res; } /* @@ -155,459 +146,387 @@ static int withFileNode( const char *opName, can be done here. */ -int _do_getattr(FileNode *fnode, struct stat *stbuf) -{ - int res = fnode->getAttr(stbuf); - if(res == ESUCCESS && S_ISLNK(stbuf->st_mode)) - { - EncFS_Context *ctx = context(); - shared_ptr FSRoot = ctx->getRoot(&res); - if(FSRoot) - { - // determine plaintext link size.. Easiest to read and decrypt.. - scoped_array buf(new char[stbuf->st_size+1]); +int _do_getattr(FileNode *fnode, struct stat *stbuf) { + int res = fnode->getAttr(stbuf); + if (res == ESUCCESS && S_ISLNK(stbuf->st_mode)) { + EncFS_Context *ctx = context(); + shared_ptr FSRoot = ctx->getRoot(&res); + if (FSRoot) { + // determine plaintext link size.. Easiest to read and decrypt.. + scoped_array buf(new char[stbuf->st_size + 1]); - res = ::readlink( fnode->cipherName(), buf.get(), stbuf->st_size ); - if(res >= 0) - { - // other functions expect c-strings to be null-terminated, which - // readlink doesn't provide - buf[res] = '\0'; + res = ::readlink(fnode->cipherName(), buf.get(), stbuf->st_size); + if (res >= 0) { + // other functions expect c-strings to be null-terminated, which + // readlink doesn't provide + buf[res] = '\0'; - stbuf->st_size = FSRoot->plainPath( buf.get() ).length(); + stbuf->st_size = FSRoot->plainPath(buf.get()).length(); - res = ESUCCESS; - } else - res = -errno; - } + res = ESUCCESS; + } else + res = -errno; } + } - return res; + return res; } -int encfs_getattr(const char *path, struct stat *stbuf) -{ - return withFileNode( "getattr", path, NULL, _do_getattr, stbuf ); +int encfs_getattr(const char *path, struct stat *stbuf) { + return withFileNode("getattr", path, NULL, _do_getattr, stbuf); } int encfs_fgetattr(const char *path, struct stat *stbuf, - struct fuse_file_info *fi) -{ - return withFileNode( "fgetattr", path, fi, _do_getattr, stbuf ); + struct fuse_file_info *fi) { + return withFileNode("fgetattr", path, fi, _do_getattr, stbuf); } -int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) -{ - EncFS_Context *ctx = context(); +int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) { + EncFS_Context *ctx = context(); - int res = ESUCCESS; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; + int res = ESUCCESS; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; - try - { + try { - DirTraverse dt = FSRoot->openDir( path ); + DirTraverse dt = FSRoot->openDir(path); - rLog(Info, "getdir on %s", FSRoot->cipherPath(path).c_str()); + rLog(Info, "getdir on %s", FSRoot->cipherPath(path).c_str()); - if(dt.valid()) - { - int fileType = 0; - ino_t inode = 0; + if (dt.valid()) { + int fileType = 0; + ino_t inode = 0; - std::string name = dt.nextPlaintextName( &fileType, &inode ); - while( !name.empty() ) - { - res = filler( h, name.c_str(), fileType, inode ); + std::string name = dt.nextPlaintextName(&fileType, &inode); + while (!name.empty()) { + res = filler(h, name.c_str(), fileType, inode); - if(res != ESUCCESS) - break; + if (res != ESUCCESS) break; - name = dt.nextPlaintextName( &fileType, &inode ); - } - } else - { - rInfo("getdir request invalid, path: '%s'", path); - } - - return res; - } catch( rlog::Error &err ) - { - rError("Error caught in getdir"); - err.log( _RLWarningChannel ); - return -EIO; + name = dt.nextPlaintextName(&fileType, &inode); + } + } else { + rInfo("getdir request invalid, path: '%s'", path); } -} -int encfs_mknod(const char *path, mode_t mode, dev_t rdev) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - shared_ptr fnode = FSRoot->lookupNode( path, "mknod" ); - - rLog(Info, "mknod on %s, mode %i, dev %" PRIi64, - fnode->cipherName(), mode, (int64_t)rdev); - - uid_t uid = 0; - gid_t gid = 0; - if(ctx->publicFilesystem) - { - fuse_context *context = fuse_get_context(); - uid = context->uid; - gid = context->gid; - } - res = fnode->mknod( mode, rdev, uid, gid ); - // Is this error due to access problems? - if(ctx->publicFilesystem && -res == EACCES) - { - // try again using the parent dir's group - string parent = fnode->plaintextParent(); - rInfo("trying public filesystem workaround for %s", parent.c_str()); - shared_ptr dnode = - FSRoot->lookupNode( parent.c_str(), "mknod" ); - - struct stat st; - if(dnode->getAttr( &st ) == 0) - res = fnode->mknod( mode, rdev, uid, st.st_gid ); - } - } catch( rlog::Error &err ) - { - rError("error caught in mknod"); - err.log( _RLWarningChannel ); - } return res; + } + catch (rlog::Error &err) { + rError("Error caught in getdir"); + err.log(_RLWarningChannel); + return -EIO; + } } -int encfs_mkdir(const char *path, mode_t mode) -{ - fuse_context *fctx = fuse_get_context(); - EncFS_Context *ctx = context(); +int encfs_mknod(const char *path, mode_t mode, dev_t rdev) { + EncFS_Context *ctx = context(); - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; - try - { - uid_t uid = 0; - gid_t gid = 0; - if(ctx->publicFilesystem) - { - uid = fctx->uid; - gid = fctx->gid; - } - res = FSRoot->mkdir( path, mode, uid, gid ); - // Is this error due to access problems? - if(ctx->publicFilesystem && -res == EACCES) - { - // try again using the parent dir's group - string parent = parentDirectory( path ); - shared_ptr dnode = - FSRoot->lookupNode( parent.c_str(), "mkdir" ); + try { + shared_ptr fnode = FSRoot->lookupNode(path, "mknod"); - struct stat st; - if(dnode->getAttr( &st ) == 0) - res = FSRoot->mkdir( path, mode, uid, st.st_gid ); - } - } catch( rlog::Error &err ) - { - rError("error caught in mkdir"); - err.log( _RLWarningChannel ); + rLog(Info, "mknod on %s, mode %i, dev %" PRIi64, fnode->cipherName(), mode, + (int64_t)rdev); + + uid_t uid = 0; + gid_t gid = 0; + if (ctx->publicFilesystem) { + fuse_context *context = fuse_get_context(); + uid = context->uid; + gid = context->gid; } - return res; -} + res = fnode->mknod(mode, rdev, uid, gid); + // Is this error due to access problems? + if (ctx->publicFilesystem && -res == EACCES) { + // try again using the parent dir's group + string parent = fnode->plaintextParent(); + rInfo("trying public filesystem workaround for %s", parent.c_str()); + shared_ptr dnode = FSRoot->lookupNode(parent.c_str(), "mknod"); -int encfs_unlink(const char *path) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - // let DirNode handle it atomically so that it can handle race - // conditions - res = FSRoot->unlink( path ); - } catch( rlog::Error &err ) - { - rError("error caught in unlink"); - err.log( _RLWarningChannel ); + struct stat st; + if (dnode->getAttr(&st) == 0) + res = fnode->mknod(mode, rdev, uid, st.st_gid); } - return res; + } + catch (rlog::Error &err) { + rError("error caught in mknod"); + err.log(_RLWarningChannel); + } + return res; } +int encfs_mkdir(const char *path, mode_t mode) { + fuse_context *fctx = fuse_get_context(); + EncFS_Context *ctx = context(); -int _do_rmdir(EncFS_Context *, const string &cipherPath, int ) -{ - return rmdir( cipherPath.c_str() ); + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; + + try { + uid_t uid = 0; + gid_t gid = 0; + if (ctx->publicFilesystem) { + uid = fctx->uid; + gid = fctx->gid; + } + res = FSRoot->mkdir(path, mode, uid, gid); + // Is this error due to access problems? + if (ctx->publicFilesystem && -res == EACCES) { + // try again using the parent dir's group + string parent = parentDirectory(path); + shared_ptr dnode = FSRoot->lookupNode(parent.c_str(), "mkdir"); + + struct stat st; + if (dnode->getAttr(&st) == 0) + res = FSRoot->mkdir(path, mode, uid, st.st_gid); + } + } + catch (rlog::Error &err) { + rError("error caught in mkdir"); + err.log(_RLWarningChannel); + } + return res; } -int encfs_rmdir(const char *path) -{ - return withCipherPath( "rmdir", path, _do_rmdir, 0 ); +int encfs_unlink(const char *path) { + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; + + try { + // let DirNode handle it atomically so that it can handle race + // conditions + res = FSRoot->unlink(path); + } + catch (rlog::Error &err) { + rError("error caught in unlink"); + err.log(_RLWarningChannel); + } + return res; +} + +int _do_rmdir(EncFS_Context *, const string &cipherPath, int) { + return rmdir(cipherPath.c_str()); +} + +int encfs_rmdir(const char *path) { + return withCipherPath("rmdir", path, _do_rmdir, 0); } int _do_readlink(EncFS_Context *ctx, const string &cyName, - tuple data ) -{ - char *buf = data.get<0>(); - size_t size = data.get<1>(); + tuple data) { + char *buf = data.get<0>(); + size_t size = data.get<1>(); - int res = ESUCCESS; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; + int res = ESUCCESS; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; - res = ::readlink( cyName.c_str(), buf, size-1 ); + res = ::readlink(cyName.c_str(), buf, size - 1); - if(res == -1) - return -errno; + if (res == -1) return -errno; - buf[res] = '\0'; // ensure null termination - string decodedName; - try - { - decodedName = FSRoot->plainPath( buf ); - } catch(...) { } + buf[res] = '\0'; // ensure null termination + string decodedName; + try { + decodedName = FSRoot->plainPath(buf); + } + catch (...) { + } - if(!decodedName.empty()) - { - strncpy(buf, decodedName.c_str(), size-1); - buf[size-1] = '\0'; + if (!decodedName.empty()) { + strncpy(buf, decodedName.c_str(), size - 1); + buf[size - 1] = '\0'; - return ESUCCESS; - } else - { - rWarning("Error decoding link"); - return -1; + return ESUCCESS; + } else { + rWarning("Error decoding link"); + return -1; + } +} + +int encfs_readlink(const char *path, char *buf, size_t size) { + return withCipherPath("readlink", path, _do_readlink, make_tuple(buf, size)); +} + +int encfs_symlink(const char *from, const char *to) { + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; + + try { + // allow fully qualified names in symbolic links. + string fromCName = FSRoot->relativeCipherPath(from); + string toCName = FSRoot->cipherPath(to); + + rLog(Info, "symlink %s -> %s", fromCName.c_str(), toCName.c_str()); + + // use setfsuid / setfsgid so that the new link will be owned by the + // uid/gid provided by the fuse_context. + int olduid = -1; + int oldgid = -1; + if (ctx->publicFilesystem) { + fuse_context *context = fuse_get_context(); + olduid = setfsuid(context->uid); + oldgid = setfsgid(context->gid); } + res = ::symlink(fromCName.c_str(), toCName.c_str()); + if (olduid >= 0) setfsuid(olduid); + if (oldgid >= 0) setfsgid(oldgid); + + if (res == -1) + res = -errno; + else + res = ESUCCESS; + } + catch (rlog::Error &err) { + rError("error caught in symlink"); + err.log(_RLWarningChannel); + } + return res; } -int encfs_readlink(const char *path, char *buf, size_t size) -{ - return withCipherPath( "readlink", path, _do_readlink, - make_tuple(buf, size) ); +int encfs_link(const char *from, const char *to) { + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; + + try { + res = FSRoot->link(from, to); + } + catch (rlog::Error &err) { + rError("error caught in link"); + err.log(_RLWarningChannel); + } + return res; } -int encfs_symlink(const char *from, const char *to) -{ - EncFS_Context *ctx = context(); +int encfs_rename(const char *from, const char *to) { + EncFS_Context *ctx = context(); - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; - try - { - // allow fully qualified names in symbolic links. - string fromCName = FSRoot->relativeCipherPath( from ); - string toCName = FSRoot->cipherPath( to ); - - rLog(Info, "symlink %s -> %s", fromCName.c_str(), toCName.c_str()); + try { + res = FSRoot->rename(from, to); + } + catch (rlog::Error &err) { + rError("error caught in rename"); + err.log(_RLWarningChannel); + } + return res; +} - // use setfsuid / setfsgid so that the new link will be owned by the - // uid/gid provided by the fuse_context. - int olduid = -1; - int oldgid = -1; - if(ctx->publicFilesystem) - { - fuse_context *context = fuse_get_context(); - olduid = setfsuid( context->uid ); - oldgid = setfsgid( context->gid ); - } - res = ::symlink( fromCName.c_str(), toCName.c_str() ); - if(olduid >= 0) - setfsuid( olduid ); - if(oldgid >= 0) - setfsgid( oldgid ); +int _do_chmod(EncFS_Context *, const string &cipherPath, mode_t mode) { + return chmod(cipherPath.c_str(), mode); +} - if(res == -1) - res = -errno; - else - res = ESUCCESS; - } catch( rlog::Error &err ) - { - rError("error caught in symlink"); - err.log( _RLWarningChannel ); +int encfs_chmod(const char *path, mode_t mode) { + return withCipherPath("chmod", path, _do_chmod, mode); +} + +int _do_chown(EncFS_Context *, const string &cyName, tuple data) { + int res = lchown(cyName.c_str(), data.get<0>(), data.get<1>()); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_chown(const char *path, uid_t uid, gid_t gid) { + return withCipherPath("chown", path, _do_chown, make_tuple(uid, gid)); +} + +int _do_truncate(FileNode *fnode, off_t size) { return fnode->truncate(size); } + +int encfs_truncate(const char *path, off_t size) { + return withFileNode("truncate", path, NULL, _do_truncate, size); +} + +int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) { + return withFileNode("ftruncate", path, fi, _do_truncate, size); +} + +int _do_utime(EncFS_Context *, const string &cyName, struct utimbuf *buf) { + int res = utime(cyName.c_str(), buf); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_utime(const char *path, struct utimbuf *buf) { + return withCipherPath("utime", path, _do_utime, buf); +} + +int _do_utimens(EncFS_Context *, const string &cyName, + const struct timespec ts[2]) { + struct timeval tv[2]; + tv[0].tv_sec = ts[0].tv_sec; + tv[0].tv_usec = ts[0].tv_nsec / 1000; + tv[1].tv_sec = ts[1].tv_sec; + tv[1].tv_usec = ts[1].tv_nsec / 1000; + + int res = lutimes(cyName.c_str(), tv); + return (res == -1) ? -errno : ESUCCESS; +} + +int encfs_utimens(const char *path, const struct timespec ts[2]) { + return withCipherPath("utimens", path, _do_utimens, ts); +} + +int encfs_open(const char *path, struct fuse_file_info *file) { + EncFS_Context *ctx = context(); + + int res = -EIO; + shared_ptr FSRoot = ctx->getRoot(&res); + if (!FSRoot) return res; + + try { + shared_ptr fnode = + FSRoot->openNode(path, "open", file->flags, &res); + + if (fnode) { + rLog(Info, "encfs_open for %s, flags %i", fnode->cipherName(), + file->flags); + + if (res >= 0) { + file->fh = (uintptr_t)ctx->putNode(path, fnode); + res = ESUCCESS; + } } - return res; + } + catch (rlog::Error &err) { + rError("error caught in open"); + err.log(_RLWarningChannel); + } + + return res; } -int encfs_link(const char *from, const char *to) -{ - EncFS_Context *ctx = context(); +int _do_flush(FileNode *fnode, int) { + /* Flush can be called multiple times for an open file, so it doesn't + close the file. However it is important to call close() for some + underlying filesystems (like NFS). + */ + int res = fnode->open(O_RDONLY); + if (res >= 0) { + int fh = res; + res = close(dup(fh)); + if (res == -1) res = -errno; + } - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - res = FSRoot->link( from, to ); - } catch( rlog::Error &err ) - { - rError("error caught in link"); - err.log( _RLWarningChannel ); - } - return res; + return res; } -int encfs_rename(const char *from, const char *to) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - res = FSRoot->rename( from, to ); - } catch( rlog::Error &err ) - { - rError("error caught in rename"); - err.log( _RLWarningChannel ); - } - return res; -} - -int _do_chmod(EncFS_Context *, const string &cipherPath, mode_t mode) -{ - return chmod( cipherPath.c_str(), mode ); -} - -int encfs_chmod(const char *path, mode_t mode) -{ - return withCipherPath( "chmod", path, _do_chmod, mode ); -} - -int _do_chown(EncFS_Context *, const string &cyName, - tuple data) -{ - int res = lchown( cyName.c_str(), data.get<0>(), data.get<1>() ); - return (res == -1) ? -errno : ESUCCESS; -} - -int encfs_chown(const char *path, uid_t uid, gid_t gid) -{ - return withCipherPath( "chown", path, _do_chown, make_tuple(uid, gid)); -} - -int _do_truncate( FileNode *fnode, off_t size ) -{ - return fnode->truncate( size ); -} - -int encfs_truncate(const char *path, off_t size) -{ - return withFileNode( "truncate", path, NULL, _do_truncate, size ); -} - -int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi) -{ - return withFileNode( "ftruncate", path, fi, _do_truncate, size ); -} - -int _do_utime(EncFS_Context *, const string &cyName, struct utimbuf *buf) -{ - int res = utime( cyName.c_str(), buf); - return (res == -1) ? -errno : ESUCCESS; -} - -int encfs_utime(const char *path, struct utimbuf *buf) -{ - return withCipherPath( "utime", path, _do_utime, buf ); -} - -int _do_utimens(EncFS_Context *, const string &cyName, - const struct timespec ts[2]) -{ - struct timeval tv[2]; - tv[0].tv_sec = ts[0].tv_sec; - tv[0].tv_usec = ts[0].tv_nsec / 1000; - tv[1].tv_sec = ts[1].tv_sec; - tv[1].tv_usec = ts[1].tv_nsec / 1000; - - int res = lutimes( cyName.c_str(), tv); - return (res == -1) ? -errno : ESUCCESS; -} - -int encfs_utimens(const char *path, const struct timespec ts[2] ) -{ - return withCipherPath( "utimens", path, _do_utimens, ts ); -} - -int encfs_open(const char *path, struct fuse_file_info *file) -{ - EncFS_Context *ctx = context(); - - int res = -EIO; - shared_ptr FSRoot = ctx->getRoot(&res); - if(!FSRoot) - return res; - - try - { - shared_ptr fnode = - FSRoot->openNode( path, "open", file->flags, &res ); - - if(fnode) - { - rLog(Info, "encfs_open for %s, flags %i", fnode->cipherName(), - file->flags); - - if( res >= 0 ) - { - file->fh = (uintptr_t)ctx->putNode(path, fnode); - res = ESUCCESS; - } - } - } catch( rlog::Error &err ) - { - rError("error caught in open"); - err.log( _RLWarningChannel ); - } - - return res; -} - -int _do_flush(FileNode *fnode, int ) -{ - /* Flush can be called multiple times for an open file, so it doesn't - close the file. However it is important to call close() for some - underlying filesystems (like NFS). - */ - int res = fnode->open( O_RDONLY ); - if(res >= 0) - { - int fh = res; - res = close(dup(fh)); - if(res == -1) - res = -errno; - } - - return res; -} - -int encfs_flush(const char *path, struct fuse_file_info *fi) -{ - return withFileNode( "flush", path, fi, _do_flush, 0 ); +int encfs_flush(const char *path, struct fuse_file_info *fi) { + return withFileNode("flush", path, fi, _do_flush, 0); } /* @@ -615,188 +534,158 @@ Note: This is advisory -- it might benefit us to keep file nodes around for a bit after they are released just in case they are reopened soon. But that requires a cache layer. */ -int encfs_release(const char *path, struct fuse_file_info *finfo) -{ - EncFS_Context *ctx = context(); +int encfs_release(const char *path, struct fuse_file_info *finfo) { + EncFS_Context *ctx = context(); - try - { - ctx->eraseNode( path, (void*)(uintptr_t)finfo->fh ); - return ESUCCESS; - } catch( rlog::Error &err ) - { - rError("error caught in release"); - err.log( _RLWarningChannel ); - return -EIO; - } + try { + ctx->eraseNode(path, (void *)(uintptr_t)finfo->fh); + return ESUCCESS; + } + catch (rlog::Error &err) { + rError("error caught in release"); + err.log(_RLWarningChannel); + return -EIO; + } } -int _do_read(FileNode *fnode, tuple data) -{ - return fnode->read( data.get<2>(), data.get<0>(), data.get<1>()); +int _do_read(FileNode *fnode, tuple data) { + return fnode->read(data.get<2>(), data.get<0>(), data.get<1>()); } int encfs_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *file) -{ - return withFileNode( "read", path, file, _do_read, - make_tuple((unsigned char *)buf, size, offset)); + struct fuse_file_info *file) { + return withFileNode("read", path, file, _do_read, + make_tuple((unsigned char *)buf, size, offset)); } -int _do_fsync(FileNode *fnode, int dataSync) -{ - return fnode->sync( dataSync != 0 ); +int _do_fsync(FileNode *fnode, int dataSync) { + return fnode->sync(dataSync != 0); } -int encfs_fsync(const char *path, int dataSync, - struct fuse_file_info *file) -{ - return withFileNode( "fsync", path, file, _do_fsync, dataSync ); +int encfs_fsync(const char *path, int dataSync, struct fuse_file_info *file) { + return withFileNode("fsync", path, file, _do_fsync, dataSync); } -int _do_write(FileNode *fnode, tuple data) -{ - size_t size = data.get<1>(); - if(fnode->write( data.get<2>(), (unsigned char *)data.get<0>(), size )) - return size; - else - return -EIO; +int _do_write(FileNode *fnode, tuple data) { + size_t size = data.get<1>(); + if (fnode->write(data.get<2>(), (unsigned char *)data.get<0>(), size)) + return size; + else + return -EIO; } -int encfs_write(const char *path, const char *buf, size_t size, - off_t offset, struct fuse_file_info *file) -{ - return withFileNode("write", path, file, _do_write, - make_tuple(buf, size, offset)); +int encfs_write(const char *path, const char *buf, size_t size, off_t offset, + struct fuse_file_info *file) { + return withFileNode("write", path, file, _do_write, + make_tuple(buf, size, offset)); } // statfs works even if encfs is detached.. -int encfs_statfs(const char *path, struct statvfs *st) -{ - EncFS_Context *ctx = context(); +int encfs_statfs(const char *path, struct statvfs *st) { + EncFS_Context *ctx = context(); - int res = -EIO; - try - { - (void)path; // path should always be '/' for now.. - rAssert( st != NULL ); - string cyName = ctx->rootCipherDir; + int res = -EIO; + try { + (void)path; // path should always be '/' for now.. + rAssert(st != NULL); + string cyName = ctx->rootCipherDir; - rLog(Info, "doing statfs of %s", cyName.c_str()); - res = statvfs( cyName.c_str(), st ); - if(!res) - { - // adjust maximum name length.. - st->f_namemax = 6 * (st->f_namemax - 2) / 8; // approx.. - } - if(res == -1) - res = -errno; - } catch( rlog::Error &err ) - { - rError("error caught in statfs"); - err.log( _RLWarningChannel ); + rLog(Info, "doing statfs of %s", cyName.c_str()); + res = statvfs(cyName.c_str(), st); + if (!res) { + // adjust maximum name length.. + st->f_namemax = 6 * (st->f_namemax - 2) / 8; // approx.. } - return res; + if (res == -1) res = -errno; + } + catch (rlog::Error &err) { + rError("error caught in statfs"); + err.log(_RLWarningChannel); + } + return res; } #ifdef HAVE_XATTR - #ifdef XATTR_ADD_OPT -int _do_setxattr(EncFS_Context *, const string &cyName, - tuple data) -{ - int options = 0; - return ::setxattr( cyName.c_str(), data.get<0>(), data.get<1>(), - data.get<2>(), data.get<3>(), options ); +int _do_setxattr(EncFS_Context *, const string &cyName, + tuple data) { + int options = 0; + return ::setxattr(cyName.c_str(), data.get<0>(), data.get<1>(), data.get<2>(), + data.get<3>(), options); } -int encfs_setxattr( const char *path, const char *name, - const char *value, size_t size, int flags, uint32_t position ) -{ - (void)flags; - return withCipherPath( "setxattr", path, _do_setxattr, - make_tuple(name, value, size, position) ); +int encfs_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags, uint32_t position) { + (void)flags; + return withCipherPath("setxattr", path, _do_setxattr, + make_tuple(name, value, size, position)); } #else -int _do_setxattr(EncFS_Context *, const string &cyName, - tuple data) -{ - return ::setxattr( cyName.c_str(), data.get<0>(), data.get<1>(), - data.get<2>(), data.get<3>() ); +int _do_setxattr(EncFS_Context *, const string &cyName, + tuple data) { + return ::setxattr(cyName.c_str(), data.get<0>(), data.get<1>(), data.get<2>(), + data.get<3>()); } -int encfs_setxattr( const char *path, const char *name, - const char *value, size_t size, int flags ) -{ - return withCipherPath( "setxattr", path, _do_setxattr, - make_tuple(name, value, size, flags) ); +int encfs_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags) { + return withCipherPath("setxattr", path, _do_setxattr, + make_tuple(name, value, size, flags)); } #endif - #ifdef XATTR_ADD_OPT int _do_getxattr(EncFS_Context *, const string &cyName, - tuple data) -{ - int options = 0; - return ::getxattr( cyName.c_str(), data.get<0>(), - data.get<1>(), data.get<2>(), data.get<3>(), options ); + tuple data) { + int options = 0; + return ::getxattr(cyName.c_str(), data.get<0>(), data.get<1>(), data.get<2>(), + data.get<3>(), options); } -int encfs_getxattr( const char *path, const char *name, - char *value, size_t size, uint32_t position ) -{ - return withCipherPath( "getxattr", path, _do_getxattr, - make_tuple(name, (void *)value, size, position), true ); +int encfs_getxattr(const char *path, const char *name, char *value, size_t size, + uint32_t position) { + return withCipherPath("getxattr", path, _do_getxattr, + make_tuple(name, (void *)value, size, position), true); } #else int _do_getxattr(EncFS_Context *, const string &cyName, - tuple data) -{ - return ::getxattr( cyName.c_str(), data.get<0>(), - data.get<1>(), data.get<2>()); + tuple data) { + return ::getxattr(cyName.c_str(), data.get<0>(), data.get<1>(), + data.get<2>()); } -int encfs_getxattr( const char *path, const char *name, - char *value, size_t size ) -{ - return withCipherPath( "getxattr", path, _do_getxattr, - make_tuple(name, (void *)value, size), true ); +int encfs_getxattr(const char *path, const char *name, char *value, + size_t size) { + return withCipherPath("getxattr", path, _do_getxattr, + make_tuple(name, (void *)value, size), true); } #endif - int _do_listxattr(EncFS_Context *, const string &cyName, - tuple data) -{ + tuple data) { #ifdef XATTR_ADD_OPT - int options = 0; - int res = ::listxattr( cyName.c_str(), data.get<0>(), data.get<1>(), - options ); + int options = 0; + int res = ::listxattr(cyName.c_str(), data.get<0>(), data.get<1>(), options); #else - int res = ::listxattr( cyName.c_str(), data.get<0>(), data.get<1>() ); + int res = ::listxattr(cyName.c_str(), data.get<0>(), data.get<1>()); #endif - return (res == -1) ? -errno : res; + return (res == -1) ? -errno : res; } -int encfs_listxattr( const char *path, char *list, size_t size ) -{ - return withCipherPath( "listxattr", path, _do_listxattr, - make_tuple(list, size), true ); +int encfs_listxattr(const char *path, char *list, size_t size) { + return withCipherPath("listxattr", path, _do_listxattr, + make_tuple(list, size), true); } -int _do_removexattr(EncFS_Context *, const string &cyName, const char *name) -{ +int _do_removexattr(EncFS_Context *, const string &cyName, const char *name) { #ifdef XATTR_ADD_OPT - int options = 0; - int res = ::removexattr( cyName.c_str(), name, options ); + int options = 0; + int res = ::removexattr(cyName.c_str(), name, options); #else - int res = ::removexattr( cyName.c_str(), name ); + int res = ::removexattr(cyName.c_str(), name); #endif - return (res == -1) ? -errno : res; + return (res == -1) ? -errno : res; } -int encfs_removexattr( const char *path, const char *name ) -{ - return withCipherPath( "removexattr", path, _do_removexattr, name ); +int encfs_removexattr(const char *path, const char *name) { + return withCipherPath("removexattr", path, _do_removexattr, name); } -#endif // HAVE_XATTR - +#endif // HAVE_XATTR diff --git a/encfs/encfs.h b/encfs/encfs.h index 3256300..d524640 100644 --- a/encfs/encfs.h +++ b/encfs/encfs.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -32,34 +32,30 @@ #ifndef linux #include -static __inline int setfsuid(uid_t uid) -{ - uid_t olduid = geteuid(); +static __inline int setfsuid(uid_t uid) { + uid_t olduid = geteuid(); - seteuid(uid); + seteuid(uid); - if (errno != EINVAL) - errno = 0; + if (errno != EINVAL) errno = 0; - return olduid; + return olduid; } -static __inline int setfsgid(gid_t gid) -{ - gid_t oldgid = getegid(); +static __inline int setfsgid(gid_t gid) { + gid_t oldgid = getegid(); - setegid(gid); + setegid(gid); - if (errno != EINVAL) - errno = 0; + if (errno != EINVAL) errno = 0; - return oldgid; + return oldgid; } #endif int encfs_getattr(const char *path, struct stat *stbuf); -int encfs_fgetattr(const char *path, struct stat *stbuf, - struct fuse_file_info *fi); +int encfs_fgetattr(const char *path, struct stat *stbuf, + struct fuse_file_info *fi); int encfs_readlink(const char *path, char *buf, size_t size); int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler); int encfs_mknod(const char *path, mode_t mode, dev_t rdev); @@ -72,38 +68,36 @@ int encfs_link(const char *from, const char *to); int encfs_chmod(const char *path, mode_t mode); int encfs_chown(const char *path, uid_t uid, gid_t gid); int encfs_truncate(const char *path, off_t size); -int encfs_ftruncate(const char *path, off_t size, - struct fuse_file_info *fi); +int encfs_ftruncate(const char *path, off_t size, struct fuse_file_info *fi); int encfs_utime(const char *path, struct utimbuf *buf); int encfs_open(const char *path, struct fuse_file_info *info); int encfs_release(const char *path, struct fuse_file_info *info); int encfs_read(const char *path, char *buf, size_t size, off_t offset, - struct fuse_file_info *info); + struct fuse_file_info *info); int encfs_write(const char *path, const char *buf, size_t size, off_t offset, - struct fuse_file_info *info); + struct fuse_file_info *info); int encfs_statfs(const char *, struct statvfs *fst); int encfs_flush(const char *, struct fuse_file_info *info); int encfs_fsync(const char *path, int flags, struct fuse_file_info *info); #ifdef HAVE_XATTR -# ifdef XATTR_ADD_OPT -int encfs_setxattr( const char *path, const char *name, const char *value, - size_t size, int flags, uint32_t position); -int encfs_getxattr( const char *path, const char *name, char *value, - size_t size, uint32_t position ); -# else -int encfs_setxattr( const char *path, const char *name, const char *value, - size_t size, int flags); -int encfs_getxattr( const char *path, const char *name, char *value, - size_t size ); -# endif - -int encfs_listxattr( const char *path, char *list, size_t size ); -int encfs_removexattr( const char *path, const char *name ); +#ifdef XATTR_ADD_OPT +int encfs_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags, uint32_t position); +int encfs_getxattr(const char *path, const char *name, char *value, size_t size, + uint32_t position); +#else +int encfs_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags); +int encfs_getxattr(const char *path, const char *name, char *value, + size_t size); #endif -int encfs_utimens( const char *path, const struct timespec ts[2] ); - +int encfs_listxattr(const char *path, char *list, size_t size); +int encfs_removexattr(const char *path, const char *name); #endif +int encfs_utimens(const char *path, const struct timespec ts[2]); + +#endif diff --git a/encfs/encfsctl.cpp b/encfs/encfsctl.cpp index ec0ff05..d1c5e80 100644 --- a/encfs/encfsctl.cpp +++ b/encfs/encfsctl.cpp @@ -3,8 +3,8 @@ * ***************************************************************************** * Copyright (c) 2004, Valient Gough - * - * This program is free software; you can distribute it and/or modify it under + * + * This program is free software; you can distribute it and/or modify it under * the terms of the GNU General Public License (GPL), as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. @@ -15,7 +15,6 @@ * more details. */ - #include "encfs.h" #include "config.h" @@ -55,805 +54,687 @@ using namespace std; using boost::format; using boost::scoped_array; -static int showInfo( int argc, char **argv ); -static int showVersion( int argc, char **argv ); -static int chpasswd( int argc, char **argv ); -static int chpasswdAutomaticly( int argc, char **argv ); -static int cmd_ls( int argc, char **argv ); -static int cmd_decode( int argc, char **argv ); -static int cmd_encode( int argc, char **argv ); -static int cmd_showcruft( int argc, char **argv ); -static int cmd_cat( int argc, char **argv ); -static int cmd_export( int argc, char **argv ); -static int cmd_showKey( int argc, char **argv ); +static int showInfo(int argc, char **argv); +static int showVersion(int argc, char **argv); +static int chpasswd(int argc, char **argv); +static int chpasswdAutomaticly(int argc, char **argv); +static int cmd_ls(int argc, char **argv); +static int cmd_decode(int argc, char **argv); +static int cmd_encode(int argc, char **argv); +static int cmd_showcruft(int argc, char **argv); +static int cmd_cat(int argc, char **argv); +static int cmd_export(int argc, char **argv); +static int cmd_showKey(int argc, char **argv); -struct CommandOpts -{ - const char *name; - int minOptions; - int maxOptions; - int (*func)(int argc, char **argv); - const char *argStr; - const char *usageStr; -} commands[] = -{ - {"info", 1, 1, showInfo, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show information (Default command)")}, - {"showKey", 1, 1, cmd_showKey, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show key")}, - {"passwd", 1, 1, chpasswd, "(root dir)", - // xgroup(usage) - gettext_noop(" -- change password for volume")}, - {"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)", - // xgroup(usage) - gettext_noop(" -- change password for volume, taking password" - " from standard input.\n\tNo prompts are issued.")}, - {"ls", 1, 2, cmd_ls, 0,0}, - {"showcruft", 1, 1, cmd_showcruft, "(root dir)", - // xgroup(usage) - gettext_noop(" -- show undecodable filenames in the volume")}, - {"cat", 2, 2, cmd_cat, "(root dir) path", - // xgroup(usage) - gettext_noop(" -- decodes the file and cats it to standard out")}, - {"decode", 1, 100, cmd_decode, "[--extpass=prog] (root dir) [encoded-name ...]", - // xgroup(usage) - gettext_noop(" -- decodes name and prints plaintext version")}, - {"encode", 1, 100, cmd_encode, "[--extpass=prog] (root dir) [plaintext-name ...]", - // xgroup(usage) - gettext_noop(" -- encodes a filename and print result")}, - {"export", 2, 2, cmd_export, "(root dir) path", - // xgroup(usage) - gettext_noop(" -- decrypts a volume and writes results to path")}, - {"--version", 0, 0, showVersion, "", - // xgroup(usage) - gettext_noop(" -- print version number and exit")}, - {0,0,0,0,0,0} -}; +struct CommandOpts { + const char *name; + int minOptions; + int maxOptions; + int (*func)(int argc, char **argv); + const char *argStr; + const char *usageStr; +} commands[] = { + {"info", 1, 1, showInfo, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show information (Default command)")}, + {"showKey", 1, 1, cmd_showKey, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show key")}, + {"passwd", 1, 1, chpasswd, "(root dir)", + // xgroup(usage) + gettext_noop(" -- change password for volume")}, + {"autopasswd", 1, 1, chpasswdAutomaticly, "(root dir)", + // xgroup(usage) + gettext_noop( + " -- change password for volume, taking password" + " from standard input.\n\tNo prompts are issued.")}, + {"ls", 1, 2, cmd_ls, 0, 0}, + {"showcruft", 1, 1, cmd_showcruft, "(root dir)", + // xgroup(usage) + gettext_noop(" -- show undecodable filenames in the volume")}, + {"cat", 2, 2, cmd_cat, "(root dir) path", + // xgroup(usage) + gettext_noop(" -- decodes the file and cats it to standard out")}, + {"decode", 1, 100, cmd_decode, + "[--extpass=prog] (root dir) [encoded-name ...]", + // xgroup(usage) + gettext_noop(" -- decodes name and prints plaintext version")}, + {"encode", 1, 100, cmd_encode, + "[--extpass=prog] (root dir) [plaintext-name ...]", + // xgroup(usage) + gettext_noop(" -- encodes a filename and print result")}, + {"export", 2, 2, cmd_export, "(root dir) path", + // xgroup(usage) + gettext_noop(" -- decrypts a volume and writes results to path")}, + {"--version", 0, 0, showVersion, "", + // xgroup(usage) + gettext_noop(" -- print version number and exit")}, + {0, 0, 0, 0, 0, 0}}; +static void usage(const char *name) { + cerr << format(_("encfsctl version %s")) % VERSION << "\n" << _("Usage:\n") + // displays usage commands, eg "./encfs (root dir) ..." + // xgroup(usage) + << format( + _("%s (root dir)\n" + " -- displays information about the filesystem, or \n")) % + name; - -static -void usage(const char *name) -{ - cerr << format(_("encfsctl version %s")) % VERSION << "\n" - << _("Usage:\n") - // displays usage commands, eg "./encfs (root dir) ..." - // xgroup(usage) - << format(_("%s (root dir)\n" - " -- displays information about the filesystem, or \n")) % name; - - int offset = 0; - while(commands[offset].name != 0) - { - if( commands[offset].argStr != 0 ) - { - cerr << "encfsctl " << commands[offset].name << " " - << commands[offset].argStr << "\n" - << gettext( commands[offset].usageStr ) << "\n"; - } - ++offset; + int offset = 0; + while (commands[offset].name != 0) { + if (commands[offset].argStr != 0) { + cerr << "encfsctl " << commands[offset].name << " " + << commands[offset].argStr << "\n" + << gettext(commands[offset].usageStr) << "\n"; } + ++offset; + } - cerr << "\n" - // xgroup(usage) - << format(_("Example: \n%s info ~/.crypt\n")) % name - << "\n"; + cerr << "\n" + // xgroup(usage) + << format(_("Example: \n%s info ~/.crypt\n")) % name << "\n"; } -static bool checkDir( string &rootDir ) -{ - if( !isDirectory( rootDir.c_str() )) - { - cerr << format(_("directory %s does not exist.\n")) % - rootDir.c_str(); - return false; - } - if(rootDir[ rootDir.length()-1 ] != '/') - rootDir.append("/"); +static bool checkDir(string &rootDir) { + if (!isDirectory(rootDir.c_str())) { + cerr << format(_("directory %s does not exist.\n")) % rootDir.c_str(); + return false; + } + if (rootDir[rootDir.length() - 1] != '/') rootDir.append("/"); - return true; + return true; } -static int showVersion( int argc, char **argv ) -{ - (void)argc; - (void)argv; - // xgroup(usage) - cerr << format(_("encfsctl version %s")) % VERSION << "\n"; +static int showVersion(int argc, char **argv) { + (void)argc; + (void)argv; + // xgroup(usage) + cerr << format(_("encfsctl version %s")) % VERSION << "\n"; - return EXIT_SUCCESS; + return EXIT_SUCCESS; } -static int showInfo( int argc, char **argv ) -{ - (void)argc; - string rootDir = argv[1]; - if( !checkDir( rootDir )) - return EXIT_FAILURE; +static int showInfo(int argc, char **argv) { + (void)argc; + string rootDir = argv[1]; + if (!checkDir(rootDir)) return EXIT_FAILURE; - shared_ptr config(new EncFSConfig); - ConfigType type = readConfig( rootDir, config ); + shared_ptr config(new EncFSConfig); + ConfigType type = readConfig(rootDir, config); - // show information stored in config.. - switch(type) - { + // show information stored in config.. + switch (type) { case Config_None: - // xgroup(diag) - cout << _("Unable to load or parse config file\n"); - return EXIT_FAILURE; + // xgroup(diag) + cout << _("Unable to load or parse config file\n"); + return EXIT_FAILURE; case Config_Prehistoric: - // xgroup(diag) - cout << _("A really old EncFS filesystem was found. \n" - "It is not supported in this EncFS build.\n"); - return EXIT_FAILURE; + // xgroup(diag) + cout << _("A really old EncFS filesystem was found. \n" + "It is not supported in this EncFS build.\n"); + return EXIT_FAILURE; case Config_V3: - // xgroup(diag) - cout << "\n" << format(_("Version 3 configuration; " - "created by %s\n")) % config->creator.c_str(); - break; + // xgroup(diag) + cout << "\n" << format( + _("Version 3 configuration; " + "created by %s\n")) % + config->creator.c_str(); + break; case Config_V4: - // xgroup(diag) - cout << "\n" << format(_("Version 4 configuration; " - "created by %s\n")) % config->creator.c_str(); - break; + // xgroup(diag) + cout << "\n" << format( + _("Version 4 configuration; " + "created by %s\n")) % + config->creator.c_str(); + break; case Config_V5: - // xgroup(diag) - cout << "\n" << format(_("Version 5 configuration; " - "created by %s (revision %i)\n")) % config->creator % - config->subVersion; - break; + // xgroup(diag) + cout << "\n" << format( + _("Version 5 configuration; " + "created by %s (revision %i)\n")) % + config->creator % config->subVersion; + break; case Config_V6: - // xgroup(diag) - cout << "\n" << format(_("Version 6 configuration; " - "created by %s (revision %i)\n")) % config->creator % - config->subVersion; - break; - } + // xgroup(diag) + cout << "\n" << format( + _("Version 6 configuration; " + "created by %s (revision %i)\n")) % + config->creator % config->subVersion; + break; + } - showFSInfo( config ); + showFSInfo(config); - return EXIT_SUCCESS; + return EXIT_SUCCESS; } -static RootPtr initRootInfo(int &argc, char ** &argv) -{ - RootPtr result; - shared_ptr opts( new EncFS_Opts() ); +static RootPtr initRootInfo(int &argc, char **&argv) { + RootPtr result; + shared_ptr opts(new EncFS_Opts()); + opts->createIfNotFound = false; + opts->checkKey = false; + + static struct option long_options[] = {{"extpass", 1, 0, 'p'}, {0, 0, 0, 0}}; + + for (;;) { + int option_index = 0; + + int res = getopt_long(argc, argv, "", long_options, &option_index); + if (res == -1) break; + + switch (res) { + case 'p': + opts->passwordProgram.assign(optarg); + break; + default: + rWarning(_("getopt error: %i"), res); + break; + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) { + cerr << _("Incorrect number of arguments") << "\n"; + } else { + opts->rootDir = string(argv[0]); + + --argc; + ++argv; + + if (checkDir(opts->rootDir)) result = initFS(NULL, opts); + + if (!result) + cerr << _("Unable to initialize encrypted filesystem - check path.\n"); + } + + return result; +} + +static RootPtr initRootInfo(const char *crootDir) { + string rootDir(crootDir); + RootPtr result; + + if (checkDir(rootDir)) { + shared_ptr opts(new EncFS_Opts()); + opts->rootDir = rootDir; opts->createIfNotFound = false; opts->checkKey = false; + result = initFS(NULL, opts); + } - static struct option long_options[] = { - {"extpass", 1, 0, 'p'}, - {0,0,0,0} - }; + if (!result) + cerr << _("Unable to initialize encrypted filesystem - check path.\n"); - for(;;) - { - int option_index = 0; - - int res = getopt_long( argc, argv, "", - long_options, &option_index); - if(res == -1) - break; - - switch(res) - { - case 'p': - opts->passwordProgram.assign(optarg); - break; - default: - rWarning(_("getopt error: %i"), res); - break; - } - } - - argc -= optind; - argv += optind; - - if(argc == 0) - { - cerr << _("Incorrect number of arguments") << "\n"; - } else - { - opts->rootDir = string( argv[0] ); - - --argc; - ++argv; - - if(checkDir( opts->rootDir )) - result = initFS( NULL, opts ); - - if(!result) - cerr << _("Unable to initialize encrypted filesystem - check path.\n"); - } - - return result; + return result; } -static RootPtr initRootInfo(const char* crootDir) -{ - string rootDir(crootDir); - RootPtr result; +static int cmd_showKey(int argc, char **argv) { + (void)argc; + RootPtr rootInfo = initRootInfo(argv[1]); - if(checkDir( rootDir )) - { - shared_ptr opts( new EncFS_Opts() ); - opts->rootDir = rootDir; - opts->createIfNotFound = false; - opts->checkKey = false; - result = initFS( NULL, opts ); - } + if (!rootInfo) + return EXIT_FAILURE; + else { + // encode with itself + string b64Key = rootInfo->cipher->encodeAsString(rootInfo->volumeKey, + rootInfo->volumeKey); - if(!result) - cerr << _("Unable to initialize encrypted filesystem - check path.\n"); - - return result; -} - -static int cmd_showKey( int argc, char **argv ) -{ - (void)argc; - RootPtr rootInfo = initRootInfo(argv[1]); - - if(!rootInfo) - return EXIT_FAILURE; - else - { - // encode with itself - string b64Key = rootInfo->cipher->encodeAsString( - rootInfo->volumeKey, rootInfo->volumeKey ); - - cout << b64Key << "\n"; - - return EXIT_SUCCESS; - } -} - -static int cmd_decode( int argc, char **argv ) -{ - RootPtr rootInfo = initRootInfo(argc, argv); - if(!rootInfo) - return EXIT_FAILURE; - - if(argc > 0) - { - for(int i=0; iroot->plainPath( argv[i] ); - cout << name << "\n"; - } - } else - { - char buf[PATH_MAX+1]; - while(cin.getline(buf,PATH_MAX)) - { - cout << rootInfo->root->plainPath( buf ) << "\n"; - } - } - return EXIT_SUCCESS; -} - -static int cmd_encode( int argc, char **argv ) -{ - RootPtr rootInfo = initRootInfo(argc, argv); - if(!rootInfo) - return EXIT_FAILURE; - - if(argc > 0) - { - for(int i=0; iroot->cipherPathWithoutRoot(argv[i]); - cout << name << "\n"; - } - } else - { - char buf[PATH_MAX+1]; - while(cin.getline(buf,PATH_MAX)) - { - cout << rootInfo->root->cipherPathWithoutRoot( buf ) << "\n"; - } - } - return EXIT_SUCCESS; -} - -static int cmd_ls( int argc, char **argv ) -{ - (void)argc; - - RootPtr rootInfo = initRootInfo(argv[1]); - - if(!rootInfo) - return EXIT_FAILURE; - - // show files in directory - { - DirTraverse dt = rootInfo->root->openDir("/"); - if(dt.valid()) - { - for(string name = dt.nextPlaintextName(); !name.empty(); - name = dt.nextPlaintextName()) - { - shared_ptr fnode = - rootInfo->root->lookupNode( name.c_str(), "encfsctl-ls" ); - struct stat stbuf; - fnode->getAttr( &stbuf ); - - struct tm stm; - localtime_r( &stbuf.st_mtime, &stm ); - stm.tm_year += 1900; - // TODO: when I add "%s" to the end and name.c_str(), I get a - // seg fault from within strlen. Why ??? - printf("%11i %4i-%02i-%02i %02i:%02i:%02i %s\n", - int(stbuf.st_size), - int(stm.tm_year), int(stm.tm_mon), int(stm.tm_mday), - int(stm.tm_hour), int(stm.tm_min), int(stm.tm_sec), - name.c_str()); - } - } - } + cout << b64Key << "\n"; return EXIT_SUCCESS; + } +} + +static int cmd_decode(int argc, char **argv) { + RootPtr rootInfo = initRootInfo(argc, argv); + if (!rootInfo) return EXIT_FAILURE; + + if (argc > 0) { + for (int i = 0; i < argc; ++i) { + string name = rootInfo->root->plainPath(argv[i]); + cout << name << "\n"; + } + } else { + char buf[PATH_MAX + 1]; + while (cin.getline(buf, PATH_MAX)) { + cout << rootInfo->root->plainPath(buf) << "\n"; + } + } + return EXIT_SUCCESS; +} + +static int cmd_encode(int argc, char **argv) { + RootPtr rootInfo = initRootInfo(argc, argv); + if (!rootInfo) return EXIT_FAILURE; + + if (argc > 0) { + for (int i = 0; i < argc; ++i) { + string name = rootInfo->root->cipherPathWithoutRoot(argv[i]); + cout << name << "\n"; + } + } else { + char buf[PATH_MAX + 1]; + while (cin.getline(buf, PATH_MAX)) { + cout << rootInfo->root->cipherPathWithoutRoot(buf) << "\n"; + } + } + return EXIT_SUCCESS; +} + +static int cmd_ls(int argc, char **argv) { + (void)argc; + + RootPtr rootInfo = initRootInfo(argv[1]); + + if (!rootInfo) return EXIT_FAILURE; + + // show files in directory + { + DirTraverse dt = rootInfo->root->openDir("/"); + if (dt.valid()) { + for (string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) { + shared_ptr fnode = + rootInfo->root->lookupNode(name.c_str(), "encfsctl-ls"); + struct stat stbuf; + fnode->getAttr(&stbuf); + + struct tm stm; + localtime_r(&stbuf.st_mtime, &stm); + stm.tm_year += 1900; + // TODO: when I add "%s" to the end and name.c_str(), I get a + // seg fault from within strlen. Why ??? + printf("%11i %4i-%02i-%02i %02i:%02i:%02i %s\n", int(stbuf.st_size), + int(stm.tm_year), int(stm.tm_mon), int(stm.tm_mday), + int(stm.tm_hour), int(stm.tm_min), int(stm.tm_sec), + name.c_str()); + } + } + } + + return EXIT_SUCCESS; } // apply an operation to every block in the file -template -int processContents( const shared_ptr &rootInfo, - const char *path, T &op ) -{ - int errCode = 0; - shared_ptr node = rootInfo->root->openNode( path, "encfsctl", - O_RDONLY, &errCode ); +template +int processContents(const shared_ptr &rootInfo, const char *path, + T &op) { + int errCode = 0; + shared_ptr node = + rootInfo->root->openNode(path, "encfsctl", O_RDONLY, &errCode); - if(!node) - { - // try treating filename as an enciphered path - string plainName = rootInfo->root->plainPath( path ); - node = rootInfo->root->lookupNode( plainName.c_str(), "encfsctl" ); - if(node) - { - errCode = node->open( O_RDONLY ); - if(errCode < 0) - node.reset(); - } + if (!node) { + // try treating filename as an enciphered path + string plainName = rootInfo->root->plainPath(path); + node = rootInfo->root->lookupNode(plainName.c_str(), "encfsctl"); + if (node) { + errCode = node->open(O_RDONLY); + if (errCode < 0) node.reset(); } + } - if(!node) - { - cerr << "unable to open " << path << "\n"; - return errCode; - } else - { - unsigned char buf[512]; - int blocks = (node->getSize() + sizeof(buf)-1) / sizeof(buf); - // read all the data in blocks - for(int i=0; iread(i*sizeof(buf), buf, sizeof(buf)); - int res = op(buf, bytes); - if(res < 0) - return res; - } + if (!node) { + cerr << "unable to open " << path << "\n"; + return errCode; + } else { + unsigned char buf[512]; + int blocks = (node->getSize() + sizeof(buf) - 1) / sizeof(buf); + // read all the data in blocks + for (int i = 0; i < blocks; ++i) { + int bytes = node->read(i * sizeof(buf), buf, sizeof(buf)); + int res = op(buf, bytes); + if (res < 0) return res; } - return 0; -} - -class WriteOutput -{ - int _fd; -public: - WriteOutput(int fd) { _fd = fd; } - ~WriteOutput() { close(_fd); } + } + return 0; +} - int operator()(const void *buf, int count) - { - return (int)write(_fd, buf, count); - } +class WriteOutput { + int _fd; + + public: + WriteOutput(int fd) { _fd = fd; } + ~WriteOutput() { close(_fd); } + + int operator()(const void *buf, int count) { + return (int)write(_fd, buf, count); + } }; -static int cmd_cat( int argc, char **argv ) -{ - (void)argc; - RootPtr rootInfo = initRootInfo(argv[1]); +static int cmd_cat(int argc, char **argv) { + (void)argc; + RootPtr rootInfo = initRootInfo(argv[1]); - if(!rootInfo) - return EXIT_FAILURE; + if (!rootInfo) return EXIT_FAILURE; - const char *path = argv[2]; - WriteOutput output(STDOUT_FILENO); - int errCode = processContents( rootInfo, path, output ); - - return errCode; + const char *path = argv[2]; + WriteOutput output(STDOUT_FILENO); + int errCode = processContents(rootInfo, path, output); + + return errCode; } -static int copyLink(const struct stat &stBuf, - const shared_ptr &rootInfo, - const string &cpath, const string &destName ) -{ - scoped_array buf(new char[stBuf.st_size+1]); - int res = ::readlink( cpath.c_str(), buf.get(), stBuf.st_size ); - if(res == -1) - { - cerr << "unable to readlink of " << cpath << "\n"; - return EXIT_FAILURE; - } +static int copyLink(const struct stat &stBuf, + const shared_ptr &rootInfo, const string &cpath, + const string &destName) { + scoped_array buf(new char[stBuf.st_size + 1]); + int res = ::readlink(cpath.c_str(), buf.get(), stBuf.st_size); + if (res == -1) { + cerr << "unable to readlink of " << cpath << "\n"; + return EXIT_FAILURE; + } - buf[res] = '\0'; - string decodedLink = rootInfo->root->plainPath(buf.get()); + buf[res] = '\0'; + string decodedLink = rootInfo->root->plainPath(buf.get()); - res = ::symlink( decodedLink.c_str(), destName.c_str() ); - if(res == -1) - { - cerr << "unable to create symlink for " << cpath - << " to " << decodedLink << "\n"; - } + res = ::symlink(decodedLink.c_str(), destName.c_str()); + if (res == -1) { + cerr << "unable to create symlink for " << cpath << " to " << decodedLink + << "\n"; + } - return EXIT_SUCCESS; + return EXIT_SUCCESS; } -static int copyContents(const shared_ptr &rootInfo, - const char* encfsName, const char* targetName) -{ - shared_ptr node = - rootInfo->root->lookupNode( encfsName, "encfsctl" ); +static int copyContents(const shared_ptr &rootInfo, + const char *encfsName, const char *targetName) { + shared_ptr node = rootInfo->root->lookupNode(encfsName, "encfsctl"); - if(!node) - { - cerr << "unable to open " << encfsName << "\n"; + if (!node) { + cerr << "unable to open " << encfsName << "\n"; + return EXIT_FAILURE; + } else { + struct stat st; + + if (node->getAttr(&st) != 0) return EXIT_FAILURE; + + if ((st.st_mode & S_IFLNK) == S_IFLNK) { + string d = rootInfo->root->cipherPath(encfsName); + char linkContents[PATH_MAX + 2]; + + if (readlink(d.c_str(), linkContents, PATH_MAX + 1) <= 0) { + cerr << "unable to read link " << encfsName << "\n"; return EXIT_FAILURE; - } else - { - struct stat st; + } + symlink(rootInfo->root->plainPath(linkContents).c_str(), targetName); + } else { + int outfd = creat(targetName, st.st_mode); - if(node->getAttr(&st) != 0) - return EXIT_FAILURE; + WriteOutput output(outfd); + processContents(rootInfo, encfsName, output); + } + } + return EXIT_SUCCESS; +} - if((st.st_mode & S_IFLNK) == S_IFLNK) - { - string d = rootInfo->root->cipherPath(encfsName); - char linkContents[PATH_MAX+2]; +static bool endsWith(const string &str, char ch) { + if (str.empty()) + return false; + else + return str[str.length() - 1] == ch; +} - if(readlink (d.c_str(), linkContents, PATH_MAX + 1) <= 0) - { - cerr << "unable to read link " << encfsName << "\n"; - return EXIT_FAILURE; - } - symlink(rootInfo->root->plainPath(linkContents).c_str(), - targetName); - } else - { - int outfd = creat(targetName, st.st_mode); - - WriteOutput output(outfd); - processContents( rootInfo, encfsName, output ); +static int traverseDirs(const shared_ptr &rootInfo, + string volumeDir, string destDir) { + if (!endsWith(volumeDir, '/')) volumeDir.append("/"); + if (!endsWith(destDir, '/')) destDir.append("/"); + + // Lookup directory node so we can create a destination directory + // with the same permissions + { + struct stat st; + shared_ptr dirNode = + rootInfo->root->lookupNode(volumeDir.c_str(), "encfsctl"); + if (dirNode->getAttr(&st)) return EXIT_FAILURE; + + mkdir(destDir.c_str(), st.st_mode); + } + + // show files in directory + DirTraverse dt = rootInfo->root->openDir(volumeDir.c_str()); + if (dt.valid()) { + for (string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) { + // Recurse to subdirectories + if (name != "." && name != "..") { + string plainPath = volumeDir + name; + string cpath = rootInfo->root->cipherPath(plainPath.c_str()); + string destName = destDir + name; + + int r = EXIT_SUCCESS; + struct stat stBuf; + if (!lstat(cpath.c_str(), &stBuf)) { + if (S_ISDIR(stBuf.st_mode)) { + traverseDirs(rootInfo, (plainPath + '/').c_str(), destName + '/'); + } else if (S_ISLNK(stBuf.st_mode)) { + r = copyLink(stBuf, rootInfo, cpath, destName); + } else { + r = copyContents(rootInfo, plainPath.c_str(), destName.c_str()); + } + } else { + r = EXIT_FAILURE; } + + if (r != EXIT_SUCCESS) return r; + } } - return EXIT_SUCCESS; + } + return EXIT_SUCCESS; } -static bool endsWith(const string &str, char ch) -{ - if(str.empty()) - return false; - else - return str[str.length()-1] == ch; +static int cmd_export(int argc, char **argv) { + (void)argc; + + RootPtr rootInfo = initRootInfo(argv[1]); + + if (!rootInfo) return EXIT_FAILURE; + + string destDir = argv[2]; + // if the dir doesn't exist, then create it (with user permission) + if (!checkDir(destDir) && !userAllowMkdir(destDir.c_str(), 0700)) + return EXIT_FAILURE; + + return traverseDirs(rootInfo, "/", destDir); } -static int traverseDirs(const shared_ptr &rootInfo, - string volumeDir, string destDir) -{ - if(!endsWith(volumeDir, '/')) - volumeDir.append("/"); - if(!endsWith(destDir, '/')) - destDir.append("/"); +int showcruft(const shared_ptr &rootInfo, const char *dirName) { + int found = 0; + DirTraverse dt = rootInfo->root->openDir(dirName); + if (dt.valid()) { + bool showedDir = false; + for (string name = dt.nextInvalid(); !name.empty(); + name = dt.nextInvalid()) { + string cpath = rootInfo->root->cipherPath(dirName); + cpath += '/'; + cpath += name; - // Lookup directory node so we can create a destination directory - // with the same permissions - { - struct stat st; - shared_ptr dirNode = - rootInfo->root->lookupNode( volumeDir.c_str(), "encfsctl" ); - if(dirNode->getAttr(&st)) - return EXIT_FAILURE; - - mkdir(destDir.c_str(), st.st_mode); + if (!showedDir) { + // just before showing a list of files in a directory + cout << format(_("In directory %s: \n")) % dirName; + showedDir = true; + } + ++found; + cout << cpath << "\n"; } - // show files in directory - DirTraverse dt = rootInfo->root->openDir(volumeDir.c_str()); - if(dt.valid()) - { - for(string name = dt.nextPlaintextName(); !name.empty(); - name = dt.nextPlaintextName()) - { - // Recurse to subdirectories - if(name != "." && name != "..") - { - string plainPath = volumeDir + name; - string cpath = rootInfo->root->cipherPath(plainPath.c_str()); - string destName = destDir + name; + // now go back and look for directories to recurse into.. + dt = rootInfo->root->openDir(dirName); + if (dt.valid()) { + for (string name = dt.nextPlaintextName(); !name.empty(); + name = dt.nextPlaintextName()) { + if (name == "." || name == "..") continue; - int r = EXIT_SUCCESS; - struct stat stBuf; - if( !lstat( cpath.c_str(), &stBuf )) - { - if( S_ISDIR( stBuf.st_mode ) ) - { - traverseDirs(rootInfo, (plainPath + '/').c_str(), - destName + '/'); - } else if( S_ISLNK( stBuf.st_mode )) - { - r = copyLink( stBuf, rootInfo, cpath, destName ); - } else - { - r = copyContents(rootInfo, plainPath.c_str(), - destName.c_str()); - } - } else - { - r = EXIT_FAILURE; - } - - if(r != EXIT_SUCCESS) - return r; - } - } + string plainPath = dirName; + plainPath += '/'; + plainPath += name; + + string cpath = rootInfo->root->cipherPath(plainPath.c_str()); + + if (isDirectory(cpath.c_str())) + found += showcruft(rootInfo, plainPath.c_str()); + } } - return EXIT_SUCCESS; -} + } -static int cmd_export( int argc, char **argv ) -{ - (void)argc; - - RootPtr rootInfo = initRootInfo(argv[1]); - - if(!rootInfo) - return EXIT_FAILURE; - - string destDir = argv[2]; - // if the dir doesn't exist, then create it (with user permission) - if(!checkDir(destDir) && !userAllowMkdir(destDir.c_str(), 0700)) - return EXIT_FAILURE; - - return traverseDirs(rootInfo, "/", destDir); -} - -int showcruft( const shared_ptr &rootInfo, const char *dirName ) -{ - int found = 0; - DirTraverse dt = rootInfo->root->openDir( dirName ); - if(dt.valid()) - { - bool showedDir = false; - for(string name = dt.nextInvalid(); !name.empty(); - name = dt.nextInvalid()) - { - string cpath = rootInfo->root->cipherPath( dirName ); - cpath += '/'; - cpath += name; - - if(!showedDir) - { - // just before showing a list of files in a directory - cout << format(_("In directory %s: \n")) % dirName; - showedDir = true; - } - ++found; - cout << cpath << "\n"; - } - - // now go back and look for directories to recurse into.. - dt = rootInfo->root->openDir( dirName ); - if(dt.valid()) - { - for(string name = dt.nextPlaintextName(); !name.empty(); - name = dt.nextPlaintextName()) - { - if( name == "." || name == "..") - continue; - - string plainPath = dirName; - plainPath += '/'; - plainPath += name; - - string cpath = rootInfo->root->cipherPath( plainPath.c_str() ); - - if(isDirectory( cpath.c_str() )) - found += showcruft( rootInfo, plainPath.c_str() ); - } - } - } - - return found; + return found; } /* iterate recursively through the filesystem and print out names of files which have filenames which cannot be decoded with the given key.. */ -static int cmd_showcruft( int argc, char **argv ) -{ - (void)argc; +static int cmd_showcruft(int argc, char **argv) { + (void)argc; - RootPtr rootInfo = initRootInfo(argv[1]); + RootPtr rootInfo = initRootInfo(argv[1]); - if(!rootInfo) - return EXIT_FAILURE; + if (!rootInfo) return EXIT_FAILURE; - int filesFound = showcruft( rootInfo, "/" ); + int filesFound = showcruft(rootInfo, "/"); - cerr << format( - ngettext("Found %i invalid file.", "Found %i invalid files.", - filesFound)) % filesFound << "\n"; + cerr << format(ngettext("Found %i invalid file.", "Found %i invalid files.", + filesFound)) % + filesFound << "\n"; - return EXIT_SUCCESS; + return EXIT_SUCCESS; } -static int do_chpasswd( bool useStdin, bool annotate, int argc, char **argv ) -{ - (void)argc; - string rootDir = argv[1]; - if( !checkDir( rootDir )) - return EXIT_FAILURE; +static int do_chpasswd(bool useStdin, bool annotate, int argc, char **argv) { + (void)argc; + string rootDir = argv[1]; + if (!checkDir(rootDir)) return EXIT_FAILURE; - shared_ptr config(new EncFSConfig); - ConfigType cfgType = readConfig( rootDir, config ); + shared_ptr config(new EncFSConfig); + ConfigType cfgType = readConfig(rootDir, config); - if(cfgType == Config_None) - { - cout << _("Unable to load or parse config file\n"); - return EXIT_FAILURE; - } + if (cfgType == Config_None) { + cout << _("Unable to load or parse config file\n"); + return EXIT_FAILURE; + } - // instanciate proper cipher - shared_ptr cipher = Cipher::New( - config->cipherIface, config->keySize ); - if(!cipher) - { - cout << format(_("Unable to find specified cipher \"%s\"\n")) % + // instanciate proper cipher + shared_ptr cipher = Cipher::New(config->cipherIface, config->keySize); + if (!cipher) { + cout << format(_("Unable to find specified cipher \"%s\"\n")) % config->cipherIface.name(); - return EXIT_FAILURE; - } + return EXIT_FAILURE; + } - // ask for existing password - cout << _("Enter current Encfs password\n"); - if (annotate) - cerr << "$PROMPT$ passwd" << endl; - CipherKey userKey = config->getUserKey( useStdin ); - if(!userKey) - return EXIT_FAILURE; + // ask for existing password + cout << _("Enter current Encfs password\n"); + if (annotate) cerr << "$PROMPT$ passwd" << endl; + CipherKey userKey = config->getUserKey(useStdin); + if (!userKey) return EXIT_FAILURE; - // decode volume key using user key -- at this point we detect an incorrect - // password if the key checksum does not match (causing readKey to fail). - CipherKey volumeKey = cipher->readKey( config->getKeyData(), userKey ); + // decode volume key using user key -- at this point we detect an incorrect + // password if the key checksum does not match (causing readKey to fail). + CipherKey volumeKey = cipher->readKey(config->getKeyData(), userKey); - if(!volumeKey) - { - cout << _("Invalid password\n"); - return EXIT_FAILURE; - } + if (!volumeKey) { + cout << _("Invalid password\n"); + return EXIT_FAILURE; + } - // Now, get New user key.. + // Now, get New user key.. + userKey.reset(); + cout << _("Enter new Encfs password\n"); + // reinitialize salt and iteration count + config->kdfIterations = 0; // generate new + + if (useStdin) { + if (annotate) cerr << "$PROMPT$ new_passwd" << endl; + userKey = config->getUserKey(true); + } else + userKey = config->getNewUserKey(); + + // re-encode the volume key using the new user key and write it out.. + int result = EXIT_FAILURE; + if (userKey) { + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char[encodedKeySize]; + + // encode volume key with new user key + cipher->writeKey(volumeKey, keyBuf, userKey); userKey.reset(); - cout << _("Enter new Encfs password\n"); - // reinitialize salt and iteration count - config->kdfIterations = 0; // generate new - if( useStdin ) - { - if (annotate) - cerr << "$PROMPT$ new_passwd" << endl; - userKey = config->getUserKey( true ); + config->assignKeyData(keyBuf, encodedKeySize); + delete[] keyBuf; + + if (saveConfig(cfgType, rootDir, config)) { + // password modified -- changes volume key of filesystem.. + cout << _("Volume Key successfully updated.\n"); + result = EXIT_SUCCESS; + } else { + cout << _("Error saving modified config file.\n"); } - else - userKey = config->getNewUserKey(); + } else { + cout << _("Error creating key\n"); + } - // re-encode the volume key using the new user key and write it out.. - int result = EXIT_FAILURE; - if(userKey) - { - int encodedKeySize = cipher->encodedKeySize(); - unsigned char *keyBuf = new unsigned char[ encodedKeySize ]; + volumeKey.reset(); - // encode volume key with new user key - cipher->writeKey( volumeKey, keyBuf, userKey ); - userKey.reset(); - - config->assignKeyData( keyBuf, encodedKeySize ); - delete[] keyBuf; - - if(saveConfig( cfgType, rootDir, config )) - { - // password modified -- changes volume key of filesystem.. - cout << _("Volume Key successfully updated.\n"); - result = EXIT_SUCCESS; - } else - { - cout << _("Error saving modified config file.\n"); - } - } else - { - cout << _("Error creating key\n"); - } - - volumeKey.reset(); - - return result; + return result; } -static int chpasswd( int argc, char **argv ) -{ - return do_chpasswd( false, false, argc, argv ); +static int chpasswd(int argc, char **argv) { + return do_chpasswd(false, false, argc, argv); } -static int chpasswdAutomaticly( int argc, char **argv ) -{ - return do_chpasswd( true, false, argc, argv ); +static int chpasswdAutomaticly(int argc, char **argv) { + return do_chpasswd(true, false, argc, argv); } - -int main(int argc, char **argv) -{ - RLogInit( argc, argv ); +int main(int argc, char **argv) { + RLogInit(argc, argv); #if defined(ENABLE_NLS) && defined(LOCALEDIR) - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE, LOCALEDIR ); - textdomain( PACKAGE ); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); #endif #ifdef HAVE_SSL - SSL_load_error_strings(); - SSL_library_init(); + SSL_load_error_strings(); + SSL_library_init(); #endif - StdioNode *slog = new StdioNode( STDERR_FILENO ); - slog->subscribeTo( GetGlobalChannel("error") ); - slog->subscribeTo( GetGlobalChannel("warning") ); + StdioNode *slog = new StdioNode(STDERR_FILENO); + slog->subscribeTo(GetGlobalChannel("error")); + slog->subscribeTo(GetGlobalChannel("warning")); #ifndef NO_DEBUG - //slog->subscribeTo( GetGlobalChannel("debug") ); +// slog->subscribeTo( GetGlobalChannel("debug") ); #endif - if(argc < 2) - { - usage( argv[0] ); - return EXIT_FAILURE; - } - - if(argc == 2 && !(*argv[1] == '-' && *(argv[1]+1) == '-')) - { - // default command when only 1 argument given -- treat the argument as - // a directory.. - return showInfo( argc, argv ); - } else - { - // find the specified command - int offset = 0; - while(commands[offset].name != 0) - { - if(!strcmp( argv[1], commands[offset].name )) - break; - ++offset; - } - - if(commands[offset].name == 0) - { - cerr << format(_("invalid command: \"%s\"")) % argv[1] << "\n"; - } else - { - if((argc-2 < commands[offset].minOptions) || - (argc-2 > commands[offset].maxOptions)) - { - cerr << format( - _("Incorrect number of arguments for command \"%s\"")) % - argv[1] << "\n"; - } else - return (*commands[offset].func)( argc-1, argv+1 ); - } - } - + if (argc < 2) { + usage(argv[0]); return EXIT_FAILURE; + } + + if (argc == 2 && !(*argv[1] == '-' && *(argv[1] + 1) == '-')) { + // default command when only 1 argument given -- treat the argument as + // a directory.. + return showInfo(argc, argv); + } else { + // find the specified command + int offset = 0; + while (commands[offset].name != 0) { + if (!strcmp(argv[1], commands[offset].name)) break; + ++offset; + } + + if (commands[offset].name == 0) { + cerr << format(_("invalid command: \"%s\"")) % argv[1] << "\n"; + } else { + if ((argc - 2 < commands[offset].minOptions) || + (argc - 2 > commands[offset].maxOptions)) { + cerr << format(_("Incorrect number of arguments for command \"%s\"")) % + argv[1] << "\n"; + } else + return (*commands[offset].func)(argc - 1, argv + 1); + } + } + + return EXIT_FAILURE; } diff --git a/encfs/i18n.h b/encfs/i18n.h index 21c709b..d68d911 100644 --- a/encfs/i18n.h +++ b/encfs/i18n.h @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -25,8 +25,6 @@ #include "intl/gettext.h" // make shortcut for gettext -# define _(STR) gettext (STR) +#define _(STR) gettext(STR) #endif - - diff --git a/encfs/main.cpp b/encfs/main.cpp index 5492027..8366f7f 100644 --- a/encfs/main.cpp +++ b/encfs/main.cpp @@ -59,10 +59,7 @@ extern "C" void fuse_unmount_compat22(const char *mountpoint); #define fuse_unmount fuse_unmount_compat22 #ifndef MAX -inline static int MAX(int a, int b) -{ - return (a > b) ? a : b; -} +inline static int MAX(int a, int b) { return (a > b) ? a : b; } #endif using namespace std; @@ -74,638 +71,595 @@ using boost::scoped_ptr; // Maximum number of arguments that we're going to pass on to fuse. Doesn't // affect how many arguments we can handle, just how many we can pass on.. const int MaxFuseArgs = 32; -struct EncFS_Args -{ - string mountPoint; // where to make filesystem visible - bool isDaemon; // true == spawn in background, log to syslog - bool isThreaded; // true == threaded - bool isVerbose; // false == only enable warning/error messages - int idleTimeout; // 0 == idle time in minutes to trigger unmount - const char *fuseArgv[MaxFuseArgs]; - int fuseArgc; +struct EncFS_Args { + string mountPoint; // where to make filesystem visible + bool isDaemon; // true == spawn in background, log to syslog + bool isThreaded; // true == threaded + bool isVerbose; // false == only enable warning/error messages + int idleTimeout; // 0 == idle time in minutes to trigger unmount + const char *fuseArgv[MaxFuseArgs]; + int fuseArgc; - shared_ptr opts; + shared_ptr opts; - // for debugging - // In case someone sends me a log dump, I want to know how what options are - // in effect. Not internationalized, since it is something that is mostly - // useful for me! - string toString() - { - ostringstream ss; - ss << (isDaemon ? "(daemon) " : "(fg) "); - ss << (isThreaded ? "(threaded) " : "(UP) "); - if(idleTimeout > 0) - ss << "(timeout " << idleTimeout << ") "; - if(opts->checkKey) ss << "(keyCheck) "; - if(opts->forceDecode) ss << "(forceDecode) "; - if(opts->ownerCreate) ss << "(ownerCreate) "; - if(opts->useStdin) ss << "(useStdin) "; - if(opts->annotate) ss << "(annotate) "; - if(opts->reverseEncryption) ss << "(reverseEncryption) "; - if(opts->mountOnDemand) ss << "(mountOnDemand) "; - if(opts->delayMount) ss << "(delayMount) "; - for(int i=0; i 0) ss << "(timeout " << idleTimeout << ") "; + if (opts->checkKey) ss << "(keyCheck) "; + if (opts->forceDecode) ss << "(forceDecode) "; + if (opts->ownerCreate) ss << "(ownerCreate) "; + if (opts->useStdin) ss << "(useStdin) "; + if (opts->annotate) ss << "(annotate) "; + if (opts->reverseEncryption) ss << "(reverseEncryption) "; + if (opts->mountOnDemand) ss << "(mountOnDemand) "; + if (opts->delayMount) ss << "(delayMount) "; + for (int i = 0; i < fuseArgc; ++i) ss << fuseArgv[i] << ' '; - return ss.str(); - } + return ss.str(); + } - EncFS_Args() - : opts( new EncFS_Opts() ) - { - } + EncFS_Args() : opts(new EncFS_Opts()) {} }; static int oldStderr = STDERR_FILENO; -static -void usage(const char *name) -{ - // xgroup(usage) - cerr << format( _("Build: encfs version %s")) % VERSION - << "\n\n" - // xgroup(usage) - << format(_("Usage: %s [options] rootDir mountPoint [-- [FUSE Mount Options]]")) % name << "\n\n" - // xgroup(usage) - << _("Common Options:\n" - " -H\t\t\t" "show optional FUSE Mount Options\n" - " -s\t\t\t" "disable multithreaded operation\n" - " -f\t\t\t" "run in foreground (don't spawn daemon).\n" - "\t\t\tError messages will be sent to stderr\n" - "\t\t\tinstead of syslog.\n") +static void usage(const char *name) { + // xgroup(usage) + cerr << format(_("Build: encfs version %s")) % VERSION << "\n\n" + // xgroup(usage) + << format( + _("Usage: %s [options] rootDir mountPoint [-- [FUSE Mount " + "Options]]")) % + name << "\n\n" + // xgroup(usage) + << _("Common Options:\n" + " -H\t\t\t" + "show optional FUSE Mount Options\n" + " -s\t\t\t" + "disable multithreaded operation\n" + " -f\t\t\t" + "run in foreground (don't spawn daemon).\n" + "\t\t\tError messages will be sent to stderr\n" + "\t\t\tinstead of syslog.\n") - // xgroup(usage) - << _(" -v, --verbose\t\t" "verbose: output encfs debug messages\n" - " -i, --idle=MINUTES\t""Auto unmount after period of inactivity\n" - " --anykey\t\t" "Do not verify correct key is being used\n" - " --forcedecode\t\t" "decode data even if an error is detected\n" - "\t\t\t(for filesystems using MAC block headers)\n") - << _(" --public\t\t" "act as a typical multi-user filesystem\n" - "\t\t\t(encfs must be run as root)\n") - << _(" --reverse\t\t" "reverse encryption\n") + // xgroup(usage) + << _(" -v, --verbose\t\t" + "verbose: output encfs debug messages\n" + " -i, --idle=MINUTES\t" + "Auto unmount after period of inactivity\n" + " --anykey\t\t" + "Do not verify correct key is being used\n" + " --forcedecode\t\t" + "decode data even if an error is detected\n" + "\t\t\t(for filesystems using MAC block headers)\n") + << _(" --public\t\t" + "act as a typical multi-user filesystem\n" + "\t\t\t(encfs must be run as root)\n") << _(" --reverse\t\t" + "reverse encryption\n") - // xgroup(usage) - << _(" --extpass=program\tUse external program for password prompt\n" - "\n" - "Example, to mount at ~/crypt with raw storage in ~/.crypt :\n" - " encfs ~/.crypt ~/crypt\n" - "\n") - // xgroup(usage) - << _("For more information, see the man page encfs(1)") << "\n" - << endl; + // xgroup(usage) + << _(" --extpass=program\tUse external program for password prompt\n" + "\n" + "Example, to mount at ~/crypt with raw storage in ~/.crypt :\n" + " encfs ~/.crypt ~/crypt\n" + "\n") + // xgroup(usage) + << _("For more information, see the man page encfs(1)") << "\n" << endl; } -static -void FuseUsage() -{ - // xgroup(usage) - cerr << _("encfs [options] rootDir mountPoint -- [FUSE Mount Options]\n" - "valid FUSE Mount Options follow:\n") << endl; +static void FuseUsage() { + // xgroup(usage) + cerr << _("encfs [options] rootDir mountPoint -- [FUSE Mount Options]\n" + "valid FUSE Mount Options follow:\n") << endl; - int argc = 2; - const char *argv[] = {"...", "-h"}; - fuse_main( argc, const_cast(argv), (fuse_operations*)NULL, NULL); + int argc = 2; + const char *argv[] = {"...", "-h"}; + fuse_main(argc, const_cast(argv), (fuse_operations *)NULL, NULL); } -#define PUSHARG(ARG) do { \ -rAssert(out->fuseArgc < MaxFuseArgs); \ -out->fuseArgv[out->fuseArgc++] = (ARG); } while(0) +#define PUSHARG(ARG) \ + do { \ + rAssert(out->fuseArgc < MaxFuseArgs); \ + out->fuseArgv[out->fuseArgc++] = (ARG); \ + } while (0) -static -string slashTerminate( const string &src ) -{ - string result = src; - if( result[ result.length()-1 ] != '/' ) - result.append( "/" ); - return result; +static string slashTerminate(const string &src) { + string result = src; + if (result[result.length() - 1] != '/') result.append("/"); + return result; } -static -bool processArgs(int argc, char *argv[], const shared_ptr &out) -{ - // set defaults - out->isDaemon = true; - out->isThreaded = true; - out->isVerbose = false; - out->idleTimeout = 0; - out->fuseArgc = 0; - out->opts->idleTracking = false; - out->opts->checkKey = true; - out->opts->forceDecode = false; - out->opts->ownerCreate = false; - out->opts->useStdin = false; - out->opts->annotate = false; - out->opts->reverseEncryption = false; - - bool useDefaultFlags = true; +static bool processArgs(int argc, char *argv[], + const shared_ptr &out) { + // set defaults + out->isDaemon = true; + out->isThreaded = true; + out->isVerbose = false; + out->idleTimeout = 0; + out->fuseArgc = 0; + out->opts->idleTracking = false; + out->opts->checkKey = true; + out->opts->forceDecode = false; + out->opts->ownerCreate = false; + out->opts->useStdin = false; + out->opts->annotate = false; + out->opts->reverseEncryption = false; - // pass executable name through - out->fuseArgv[0] = lastPathElement(argv[0]); - ++out->fuseArgc; + bool useDefaultFlags = true; - // leave a space for mount point, as FUSE expects the mount point before - // any flags - out->fuseArgv[1] = NULL; - ++out->fuseArgc; - - // TODO: can flags be internationalized? - static struct option long_options[] = { - {"fuse-debug", 0, 0, 'd'}, // Fuse debug mode - {"forcedecode", 0, 0, 'D'}, // force decode - // {"foreground", 0, 0, 'f'}, // foreground mode (no daemon) - {"fuse-help", 0, 0, 'H'}, // fuse_mount usage - {"idle", 1, 0, 'i'}, // idle timeout - {"anykey", 0, 0, 'k'}, // skip key checks - {"no-default-flags", 0, 0, 'N'}, // don't use default fuse flags - {"ondemand", 0, 0, 'm'}, // mount on-demand - {"delaymount", 0, 0, 'M'}, // delay initial mount until use - {"public", 0, 0, 'P'}, // public mode - {"extpass", 1, 0, 'p'}, // external password program - // {"single-thread", 0, 0, 's'}, // single-threaded mode - {"stdinpass", 0, 0, 'S'}, // read password from stdin - {"annotate", 0, 0, 513}, // Print annotation lines to stderr - {"verbose", 0, 0, 'v'}, // verbose mode - {"version", 0, 0, 'V'}, //version - {"reverse", 0, 0, 'r'}, // reverse encryption - {"standard", 0, 0, '1'}, // standard configuration - {"paranoia", 0, 0, '2'}, // standard configuration - {0,0,0,0} - }; + // pass executable name through + out->fuseArgv[0] = lastPathElement(argv[0]); + ++out->fuseArgc; - while (1) - { - int option_index = 0; + // leave a space for mount point, as FUSE expects the mount point before + // any flags + out->fuseArgv[1] = NULL; + ++out->fuseArgc; - // 's' : single-threaded mode - // 'f' : foreground mode - // 'v' : verbose mode (same as --verbose) - // 'd' : fuse debug mode (same as --fusedebug) - // 'i' : idle-timeout, takes argument - // 'm' : mount-on-demand - // 'S' : password from stdin - // 'o' : arguments meant for fuse - int res = getopt_long( argc, argv, "HsSfvdmi:o:", - long_options, &option_index); + // TODO: can flags be internationalized? + static struct option long_options[] = { + {"fuse-debug", 0, 0, 'd'}, // Fuse debug mode + {"forcedecode", 0, 0, 'D'}, // force decode + // {"foreground", 0, 0, 'f'}, // foreground mode (no daemon) + {"fuse-help", 0, 0, 'H'}, // fuse_mount usage + {"idle", 1, 0, 'i'}, // idle timeout + {"anykey", 0, 0, 'k'}, // skip key checks + {"no-default-flags", 0, 0, 'N'}, // don't use default fuse flags + {"ondemand", 0, 0, 'm'}, // mount on-demand + {"delaymount", 0, 0, 'M'}, // delay initial mount until use + {"public", 0, 0, 'P'}, // public mode + {"extpass", 1, 0, 'p'}, // external password program + // {"single-thread", 0, 0, 's'}, // single-threaded mode + {"stdinpass", 0, 0, 'S'}, // read password from stdin + {"annotate", 0, 0, 513}, // Print annotation lines to stderr + {"verbose", 0, 0, 'v'}, // verbose mode + {"version", 0, 0, 'V'}, // version + {"reverse", 0, 0, 'r'}, // reverse encryption + {"standard", 0, 0, '1'}, // standard configuration + {"paranoia", 0, 0, '2'}, // standard configuration + {0, 0, 0, 0}}; - if(res == -1) - break; + while (1) { + int option_index = 0; - switch( res ) - { - case '1': - out->opts->configMode = Config_Standard; - break; - case '2': - out->opts->configMode = Config_Paranoia; - break; - case 's': - out->isThreaded = false; - break; - case 'S': - out->opts->useStdin = true; - break; - case 513: - out->opts->annotate = true; - break; - case 'f': - out->isDaemon = false; - // this option was added in fuse 2.x - PUSHARG("-f"); - break; - case 'v': - out->isVerbose = true; - break; - case 'd': - PUSHARG("-d"); - break; - case 'i': - out->idleTimeout = strtol( optarg, (char**)NULL, 10); - out->opts->idleTracking = true; - break; - case 'k': - out->opts->checkKey = false; - break; - case 'D': - out->opts->forceDecode = true; - break; - case 'r': - out->opts->reverseEncryption = true; - break; - case 'm': - out->opts->mountOnDemand = true; - break; - case 'M': - out->opts->delayMount = true; - break; - case 'N': - useDefaultFlags = false; - break; - case 'o': - PUSHARG("-o"); - PUSHARG( optarg ); - break; - case 'p': - out->opts->passwordProgram.assign( optarg ); - break; - case 'P': - if(geteuid() != 0) - rWarning(_("option '--public' ignored for non-root user")); - else - { - out->opts->ownerCreate = true; - // add 'allow_other' option - // add 'default_permissions' option (default) - PUSHARG("-o"); - PUSHARG("allow_other"); - } - break; - case 'V': - // xgroup(usage) - cerr << format(_("encfs version %s")) % VERSION << endl; - exit(EXIT_SUCCESS); - break; - case 'H': - FuseUsage(); - exit(EXIT_SUCCESS); - break; - case '?': - // invalid options.. - break; - case ':': - // missing parameter for option.. - break; - default: - rWarning(_("getopt error: %i"), res); - break; - } + // 's' : single-threaded mode + // 'f' : foreground mode + // 'v' : verbose mode (same as --verbose) + // 'd' : fuse debug mode (same as --fusedebug) + // 'i' : idle-timeout, takes argument + // 'm' : mount-on-demand + // 'S' : password from stdin + // 'o' : arguments meant for fuse + int res = + getopt_long(argc, argv, "HsSfvdmi:o:", long_options, &option_index); + + if (res == -1) break; + + switch (res) { + case '1': + out->opts->configMode = Config_Standard; + break; + case '2': + out->opts->configMode = Config_Paranoia; + break; + case 's': + out->isThreaded = false; + break; + case 'S': + out->opts->useStdin = true; + break; + case 513: + out->opts->annotate = true; + break; + case 'f': + out->isDaemon = false; + // this option was added in fuse 2.x + PUSHARG("-f"); + break; + case 'v': + out->isVerbose = true; + break; + case 'd': + PUSHARG("-d"); + break; + case 'i': + out->idleTimeout = strtol(optarg, (char **)NULL, 10); + out->opts->idleTracking = true; + break; + case 'k': + out->opts->checkKey = false; + break; + case 'D': + out->opts->forceDecode = true; + break; + case 'r': + out->opts->reverseEncryption = true; + break; + case 'm': + out->opts->mountOnDemand = true; + break; + case 'M': + out->opts->delayMount = true; + break; + case 'N': + useDefaultFlags = false; + break; + case 'o': + PUSHARG("-o"); + PUSHARG(optarg); + break; + case 'p': + out->opts->passwordProgram.assign(optarg); + break; + case 'P': + if (geteuid() != 0) + rWarning(_("option '--public' ignored for non-root user")); + else { + out->opts->ownerCreate = true; + // add 'allow_other' option + // add 'default_permissions' option (default) + PUSHARG("-o"); + PUSHARG("allow_other"); + } + break; + case 'V': + // xgroup(usage) + cerr << format(_("encfs version %s")) % VERSION << endl; + exit(EXIT_SUCCESS); + break; + case 'H': + FuseUsage(); + exit(EXIT_SUCCESS); + break; + case '?': + // invalid options.. + break; + case ':': + // missing parameter for option.. + break; + default: + rWarning(_("getopt error: %i"), res); + break; } + } - if(!out->isThreaded) - PUSHARG("-s"); + if (!out->isThreaded) PUSHARG("-s"); - if(useDefaultFlags) - { - PUSHARG("-o"); - PUSHARG("use_ino"); - PUSHARG("-o"); - PUSHARG("default_permissions"); + if (useDefaultFlags) { + PUSHARG("-o"); + PUSHARG("use_ino"); + PUSHARG("-o"); + PUSHARG("default_permissions"); + } + + // we should have at least 2 arguments left over - the source directory and + // the mount point. + if (optind + 2 <= argc) { + out->opts->rootDir = slashTerminate(argv[optind++]); + out->mountPoint = argv[optind++]; + } else { + // no mount point specified + rWarning(_("Missing one or more arguments, aborting.")); + return false; + } + + // If there are still extra unparsed arguments, pass them onto FUSE.. + if (optind < argc) { + rAssert(out->fuseArgc < MaxFuseArgs); + + while (optind < argc) { + rAssert(out->fuseArgc < MaxFuseArgs); + out->fuseArgv[out->fuseArgc++] = argv[optind]; + ++optind; } - - // we should have at least 2 arguments left over - the source directory and - // the mount point. - if(optind+2 <= argc) - { - out->opts->rootDir = slashTerminate( argv[optind++] ); - out->mountPoint = argv[optind++]; - } else - { - // no mount point specified - rWarning(_("Missing one or more arguments, aborting.")); - return false; + } + + // sanity check + if (out->isDaemon && (!isAbsolutePath(out->mountPoint.c_str()) || + !isAbsolutePath(out->opts->rootDir.c_str()))) { + cerr << + // xgroup(usage) + _("When specifying daemon mode, you must use absolute paths " + "(beginning with '/')") << endl; + return false; + } + + // the raw directory may not be a subdirectory of the mount point. + { + string testMountPoint = slashTerminate(out->mountPoint); + string testRootDir = out->opts->rootDir.substr(0, testMountPoint.length()); + + if (testMountPoint == testRootDir) { + cerr << + // xgroup(usage) + _("The raw directory may not be a subdirectory of the " + "mount point.") << endl; + return false; } + } - // If there are still extra unparsed arguments, pass them onto FUSE.. - if(optind < argc) - { - rAssert(out->fuseArgc < MaxFuseArgs); + if (out->opts->delayMount && !out->opts->mountOnDemand) { + cerr << + // xgroup(usage) + _("You must use mount-on-demand with delay-mount") << endl; + return false; + } - while(optind < argc) - { - rAssert(out->fuseArgc < MaxFuseArgs); - out->fuseArgv[out->fuseArgc++] = argv[optind]; - ++optind; - } - } + if (out->opts->mountOnDemand && out->opts->passwordProgram.empty()) { + cerr << + // xgroup(usage) + _("Must set password program when using mount-on-demand") << endl; + return false; + } - // sanity check - if(out->isDaemon && - (!isAbsolutePath( out->mountPoint.c_str() ) || - !isAbsolutePath( out->opts->rootDir.c_str() ) ) - ) - { - cerr << - // xgroup(usage) - _("When specifying daemon mode, you must use absolute paths " - "(beginning with '/')") - << endl; - return false; - } + // check that the directories exist, or that we can create them.. + if (!isDirectory(out->opts->rootDir.c_str()) && + !userAllowMkdir(out->opts->annotate ? 1 : 0, out->opts->rootDir.c_str(), + 0700)) { + rWarning(_("Unable to locate root directory, aborting.")); + return false; + } + if (!isDirectory(out->mountPoint.c_str()) && + !userAllowMkdir(out->opts->annotate ? 2 : 0, out->mountPoint.c_str(), + 0700)) { + rWarning(_("Unable to locate mount point, aborting.")); + return false; + } - // the raw directory may not be a subdirectory of the mount point. - { - string testMountPoint = slashTerminate( out->mountPoint ); - string testRootDir = - out->opts->rootDir.substr(0, testMountPoint.length()); + // fill in mount path for fuse + out->fuseArgv[1] = out->mountPoint.c_str(); - if( testMountPoint == testRootDir ) - { - cerr << - // xgroup(usage) - _("The raw directory may not be a subdirectory of the " - "mount point.") << endl; - return false; - } - } - - if(out->opts->delayMount && ! out->opts->mountOnDemand) - { - cerr << - // xgroup(usage) - _("You must use mount-on-demand with delay-mount") - << endl; - return false; - } - - if(out->opts->mountOnDemand && out->opts->passwordProgram.empty()) - { - cerr << - // xgroup(usage) - _("Must set password program when using mount-on-demand") - << endl; - return false; - } - - // check that the directories exist, or that we can create them.. - if(!isDirectory( out->opts->rootDir.c_str() ) && - !userAllowMkdir( out->opts->annotate? 1:0, - out->opts->rootDir.c_str() ,0700)) - { - rWarning(_("Unable to locate root directory, aborting.")); - return false; - } - if(!isDirectory( out->mountPoint.c_str() ) && - !userAllowMkdir( out->opts->annotate? 2:0, - out->mountPoint.c_str(),0700)) - { - rWarning(_("Unable to locate mount point, aborting.")); - return false; - } - - // fill in mount path for fuse - out->fuseArgv[1] = out->mountPoint.c_str(); - - return true; + return true; } -static void * idleMonitor(void *); +static void *idleMonitor(void *); -void *encfs_init(fuse_conn_info *conn) -{ - EncFS_Context *ctx = (EncFS_Context*)fuse_get_context()->private_data; +void *encfs_init(fuse_conn_info *conn) { + EncFS_Context *ctx = (EncFS_Context *)fuse_get_context()->private_data; - // set fuse connection options - conn->async_read = true; + // set fuse connection options + conn->async_read = true; - // if an idle timeout is specified, then setup a thread to monitor the - // filesystem. - if(ctx->args->idleTimeout > 0) - { - rDebug("starting idle monitoring thread"); - ctx->running = true; + // if an idle timeout is specified, then setup a thread to monitor the + // filesystem. + if (ctx->args->idleTimeout > 0) { + rDebug("starting idle monitoring thread"); + ctx->running = true; - int res = pthread_create( &ctx->monitorThread, 0, idleMonitor, - (void*)ctx ); - if(res != 0) - { - rError("error starting idle monitor thread, " - "res = %i, errno = %i", res, errno); - } + int res = pthread_create(&ctx->monitorThread, 0, idleMonitor, (void *)ctx); + if (res != 0) { + rError( + "error starting idle monitor thread, " + "res = %i, errno = %i", + res, errno); } + } - if(ctx->args->isDaemon && oldStderr >= 0) - { - rInfo("Closing stderr"); - close(oldStderr); - oldStderr = -1; - } + if (ctx->args->isDaemon && oldStderr >= 0) { + rInfo("Closing stderr"); + close(oldStderr); + oldStderr = -1; + } - return (void*)ctx; -} - -void encfs_destroy( void *_ctx ) -{ - EncFS_Context *ctx = (EncFS_Context*)_ctx; - if(ctx->args->idleTimeout > 0) - { - ctx->running = false; - - // wake up the thread if it is waiting.. - rDebug("waking up monitoring thread"); - pthread_mutex_lock( &ctx->wakeupMutex ); - pthread_cond_signal( &ctx->wakeupCond ); - pthread_mutex_unlock( &ctx->wakeupMutex ); - rDebug("joining with idle monitoring thread"); - pthread_join( ctx->monitorThread , 0 ); - rDebug("join done"); - } + return (void *)ctx; } -int main(int argc, char *argv[]) -{ - // initialize the logging library - RLogInit( argc, argv ); +void encfs_destroy(void *_ctx) { + EncFS_Context *ctx = (EncFS_Context *)_ctx; + if (ctx->args->idleTimeout > 0) { + ctx->running = false; + + // wake up the thread if it is waiting.. + rDebug("waking up monitoring thread"); + pthread_mutex_lock(&ctx->wakeupMutex); + pthread_cond_signal(&ctx->wakeupCond); + pthread_mutex_unlock(&ctx->wakeupMutex); + rDebug("joining with idle monitoring thread"); + pthread_join(ctx->monitorThread, 0); + rDebug("join done"); + } +} + +int main(int argc, char *argv[]) { + // initialize the logging library + RLogInit(argc, argv); #if defined(ENABLE_NLS) && defined(LOCALEDIR) - setlocale( LC_ALL, "" ); - bindtextdomain( PACKAGE, LOCALEDIR ); - textdomain( PACKAGE ); + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); #endif - // log to stderr by default.. - scoped_ptr slog( new StdioNode( STDERR_FILENO ) ); - scoped_ptr logNode; + // log to stderr by default.. + scoped_ptr slog(new StdioNode(STDERR_FILENO)); + scoped_ptr logNode; - // show error and warning output - slog->subscribeTo( GetGlobalChannel("error") ); - slog->subscribeTo( GetGlobalChannel("warning") ); + // show error and warning output + slog->subscribeTo(GetGlobalChannel("error")); + slog->subscribeTo(GetGlobalChannel("warning")); - // anything that comes from the user should be considered tainted until - // we've processed it and only allowed through what we support. - shared_ptr encfsArgs( new EncFS_Args ); - for(int i=0; ifuseArgv[i] = NULL; // libfuse expects null args.. + // anything that comes from the user should be considered tainted until + // we've processed it and only allowed through what we support. + shared_ptr encfsArgs(new EncFS_Args); + for (int i = 0; i < MaxFuseArgs; ++i) + encfsArgs->fuseArgv[i] = NULL; // libfuse expects null args.. - if(argc == 1 || !processArgs(argc, argv, encfsArgs)) - { - usage(argv[0]); - return EXIT_FAILURE; - } + if (argc == 1 || !processArgs(argc, argv, encfsArgs)) { + usage(argv[0]); + return EXIT_FAILURE; + } - if(encfsArgs->isVerbose) - { - // subscribe to more logging channels.. - slog->subscribeTo( GetGlobalChannel("info") ); - slog->subscribeTo( GetGlobalChannel("debug") ); - } - - rDebug("Root directory: %s", encfsArgs->opts->rootDir.c_str()); - rDebug("Fuse arguments: %s", encfsArgs->toString().c_str()); - - fuse_operations encfs_oper; - // in case this code is compiled against a newer FUSE library and new - // members have been added to fuse_operations, make sure they get set to - // 0.. - memset(&encfs_oper, 0, sizeof(fuse_operations)); + if (encfsArgs->isVerbose) { + // subscribe to more logging channels.. + slog->subscribeTo(GetGlobalChannel("info")); + slog->subscribeTo(GetGlobalChannel("debug")); + } - encfs_oper.getattr = encfs_getattr; - encfs_oper.readlink = encfs_readlink; - encfs_oper.getdir = encfs_getdir; // deprecated for readdir - encfs_oper.mknod = encfs_mknod; - encfs_oper.mkdir = encfs_mkdir; - encfs_oper.unlink = encfs_unlink; - encfs_oper.rmdir = encfs_rmdir; - encfs_oper.symlink = encfs_symlink; - encfs_oper.rename = encfs_rename; - encfs_oper.link = encfs_link; - encfs_oper.chmod = encfs_chmod; - encfs_oper.chown = encfs_chown; - encfs_oper.truncate = encfs_truncate; - encfs_oper.utime = encfs_utime; // deprecated for utimens - encfs_oper.open = encfs_open; - encfs_oper.read = encfs_read; - encfs_oper.write = encfs_write; - encfs_oper.statfs = encfs_statfs; - encfs_oper.flush = encfs_flush; - encfs_oper.release = encfs_release; - encfs_oper.fsync = encfs_fsync; + rDebug("Root directory: %s", encfsArgs->opts->rootDir.c_str()); + rDebug("Fuse arguments: %s", encfsArgs->toString().c_str()); + + fuse_operations encfs_oper; + // in case this code is compiled against a newer FUSE library and new + // members have been added to fuse_operations, make sure they get set to + // 0.. + memset(&encfs_oper, 0, sizeof(fuse_operations)); + + encfs_oper.getattr = encfs_getattr; + encfs_oper.readlink = encfs_readlink; + encfs_oper.getdir = encfs_getdir; // deprecated for readdir + encfs_oper.mknod = encfs_mknod; + encfs_oper.mkdir = encfs_mkdir; + encfs_oper.unlink = encfs_unlink; + encfs_oper.rmdir = encfs_rmdir; + encfs_oper.symlink = encfs_symlink; + encfs_oper.rename = encfs_rename; + encfs_oper.link = encfs_link; + encfs_oper.chmod = encfs_chmod; + encfs_oper.chown = encfs_chown; + encfs_oper.truncate = encfs_truncate; + encfs_oper.utime = encfs_utime; // deprecated for utimens + encfs_oper.open = encfs_open; + encfs_oper.read = encfs_read; + encfs_oper.write = encfs_write; + encfs_oper.statfs = encfs_statfs; + encfs_oper.flush = encfs_flush; + encfs_oper.release = encfs_release; + encfs_oper.fsync = encfs_fsync; #ifdef HAVE_XATTR - encfs_oper.setxattr = encfs_setxattr; - encfs_oper.getxattr = encfs_getxattr; - encfs_oper.listxattr = encfs_listxattr; - encfs_oper.removexattr = encfs_removexattr; -#endif // HAVE_XATTR - //encfs_oper.opendir = encfs_opendir; - //encfs_oper.readdir = encfs_readdir; - //encfs_oper.releasedir = encfs_releasedir; - //encfs_oper.fsyncdir = encfs_fsyncdir; - encfs_oper.init = encfs_init; - encfs_oper.destroy = encfs_destroy; - //encfs_oper.access = encfs_access; - //encfs_oper.create = encfs_create; - encfs_oper.ftruncate = encfs_ftruncate; - encfs_oper.fgetattr = encfs_fgetattr; - //encfs_oper.lock = encfs_lock; - encfs_oper.utimens = encfs_utimens; - //encfs_oper.bmap = encfs_bmap; + encfs_oper.setxattr = encfs_setxattr; + encfs_oper.getxattr = encfs_getxattr; + encfs_oper.listxattr = encfs_listxattr; + encfs_oper.removexattr = encfs_removexattr; +#endif // HAVE_XATTR + // encfs_oper.opendir = encfs_opendir; + // encfs_oper.readdir = encfs_readdir; + // encfs_oper.releasedir = encfs_releasedir; + // encfs_oper.fsyncdir = encfs_fsyncdir; + encfs_oper.init = encfs_init; + encfs_oper.destroy = encfs_destroy; + // encfs_oper.access = encfs_access; + // encfs_oper.create = encfs_create; + encfs_oper.ftruncate = encfs_ftruncate; + encfs_oper.fgetattr = encfs_fgetattr; + // encfs_oper.lock = encfs_lock; + encfs_oper.utimens = encfs_utimens; +// encfs_oper.bmap = encfs_bmap; #if (__FreeBSD__ >= 10) || defined(__APPLE__) - // encfs_oper.setvolname - // encfs_oper.exchange - // encfs_oper.getxtimes - // encfs_oper.setbkuptime - // encfs_oper.setchgtime - // encfs_oper.setcrtime - // encfs_oper.chflags - // encfs_oper.setattr_x - // encfs_oper.fsetattr_x +// encfs_oper.setvolname +// encfs_oper.exchange +// encfs_oper.getxtimes +// encfs_oper.setbkuptime +// encfs_oper.setchgtime +// encfs_oper.setcrtime +// encfs_oper.chflags +// encfs_oper.setattr_x +// encfs_oper.fsetattr_x #endif - openssl_init( encfsArgs->isThreaded ); + openssl_init(encfsArgs->isThreaded); - // context is not a smart pointer because it will live for the life of - // the filesystem. - EncFS_Context *ctx = new EncFS_Context; - ctx->publicFilesystem = encfsArgs->opts->ownerCreate; - RootPtr rootInfo = initFS( ctx, encfsArgs->opts ); - - int returnCode = EXIT_FAILURE; + // context is not a smart pointer because it will live for the life of + // the filesystem. + EncFS_Context *ctx = new EncFS_Context; + ctx->publicFilesystem = encfsArgs->opts->ownerCreate; + RootPtr rootInfo = initFS(ctx, encfsArgs->opts); - if( rootInfo ) - { - // turn off delayMount, as our prior call to initFS has already - // respected any delay, and we want future calls to actually - // mount. - encfsArgs->opts->delayMount = false; + int returnCode = EXIT_FAILURE; - // set the globally visible root directory node - ctx->setRoot( rootInfo->root ); - ctx->args = encfsArgs; - ctx->opts = encfsArgs->opts; - - if(encfsArgs->isThreaded == false && encfsArgs->idleTimeout > 0) - { - // xgroup(usage) - cerr << _("Note: requested single-threaded mode, but an idle\n" - "timeout was specified. The filesystem will operate\n" - "single-threaded, but threads will still be used to\n" - "implement idle checking.") << endl; - } + if (rootInfo) { + // turn off delayMount, as our prior call to initFS has already + // respected any delay, and we want future calls to actually + // mount. + encfsArgs->opts->delayMount = false; - // reset umask now, since we don't want it to interfere with the - // pass-thru calls.. - umask( 0 ); + // set the globally visible root directory node + ctx->setRoot(rootInfo->root); + ctx->args = encfsArgs; + ctx->opts = encfsArgs->opts; - if(encfsArgs->isDaemon) - { - // switch to logging just warning and error messages via syslog - logNode.reset( new SyslogNode( "encfs" ) ); - logNode->subscribeTo( GetGlobalChannel("warning") ); - logNode->subscribeTo( GetGlobalChannel("error") ); - - // disable stderr reporting.. - slog.reset(); - - // keep around a pointer just in case we end up needing it to - // report a fatal condition later (fuse_main exits unexpectedly)... - oldStderr = dup( STDERR_FILENO ); - } - - try - { - time_t startTime, endTime; - - if (encfsArgs->opts->annotate) - cerr << "$STATUS$ fuse_main_start" << endl; - - // FIXME: workaround for fuse_main returning an error on normal - // exit. Only print information if fuse_main returned - // immediately.. - time( &startTime ); - - // fuse_main returns an error code in newer versions of fuse.. - int res = fuse_main( encfsArgs->fuseArgc, - const_cast(encfsArgs->fuseArgv), - &encfs_oper, (void*)ctx); - - time( &endTime ); - - if (encfsArgs->opts->annotate) - cerr << "$STATUS$ fuse_main_end" << endl; - - if(res == 0) - returnCode = EXIT_SUCCESS; - - if(res != 0 && encfsArgs->isDaemon && (oldStderr >= 0) - && (endTime - startTime <= 1) ) - { - // the users will not have seen any message from fuse, so say a - // few words in libfuse's memory.. - FILE *out = fdopen( oldStderr, "a" ); - // xgroup(usage) - fprintf(out, _("fuse failed. Common problems:\n" - " - fuse kernel module not installed (modprobe fuse)\n" - " - invalid options -- see usage message\n")); - fclose(out); - } - } catch(std::exception &ex) - { - rError(_("Internal error: Caught exception from main loop: %s"), - ex.what()); - } catch(...) - { - rError(_("Internal error: Caught unexpected exception")); - } + if (encfsArgs->isThreaded == false && encfsArgs->idleTimeout > 0) { + // xgroup(usage) + cerr << _("Note: requested single-threaded mode, but an idle\n" + "timeout was specified. The filesystem will operate\n" + "single-threaded, but threads will still be used to\n" + "implement idle checking.") << endl; } - // cleanup so that we can check for leaked resources.. - rootInfo.reset(); - ctx->setRoot( shared_ptr() ); + // reset umask now, since we don't want it to interfere with the + // pass-thru calls.. + umask(0); - MemoryPool::destroyAll(); - openssl_shutdown( encfsArgs->isThreaded ); + if (encfsArgs->isDaemon) { + // switch to logging just warning and error messages via syslog + logNode.reset(new SyslogNode("encfs")); + logNode->subscribeTo(GetGlobalChannel("warning")); + logNode->subscribeTo(GetGlobalChannel("error")); - return returnCode; + // disable stderr reporting.. + slog.reset(); + + // keep around a pointer just in case we end up needing it to + // report a fatal condition later (fuse_main exits unexpectedly)... + oldStderr = dup(STDERR_FILENO); + } + + try { + time_t startTime, endTime; + + if (encfsArgs->opts->annotate) cerr << "$STATUS$ fuse_main_start" << endl; + + // FIXME: workaround for fuse_main returning an error on normal + // exit. Only print information if fuse_main returned + // immediately.. + time(&startTime); + + // fuse_main returns an error code in newer versions of fuse.. + int res = fuse_main(encfsArgs->fuseArgc, + const_cast(encfsArgs->fuseArgv), &encfs_oper, + (void *)ctx); + + time(&endTime); + + if (encfsArgs->opts->annotate) cerr << "$STATUS$ fuse_main_end" << endl; + + if (res == 0) returnCode = EXIT_SUCCESS; + + if (res != 0 && encfsArgs->isDaemon && (oldStderr >= 0) && + (endTime - startTime <= 1)) { + // the users will not have seen any message from fuse, so say a + // few words in libfuse's memory.. + FILE *out = fdopen(oldStderr, "a"); + // xgroup(usage) + fprintf(out, _("fuse failed. Common problems:\n" + " - fuse kernel module not installed (modprobe fuse)\n" + " - invalid options -- see usage message\n")); + fclose(out); + } + } + catch (std::exception &ex) { + rError(_("Internal error: Caught exception from main loop: %s"), + ex.what()); + } + catch (...) { + rError(_("Internal error: Caught unexpected exception")); + } + } + + // cleanup so that we can check for leaked resources.. + rootInfo.reset(); + ctx->setRoot(shared_ptr()); + + MemoryPool::destroyAll(); + openssl_shutdown(encfsArgs->isThreaded); + + return returnCode; } /* @@ -718,76 +672,65 @@ int main(int argc, char *argv[]) const int ActivityCheckInterval = 10; static bool unmountFS(EncFS_Context *ctx); -static -void * idleMonitor(void *_arg) -{ - EncFS_Context *ctx = (EncFS_Context*)_arg; - shared_ptr arg = ctx->args; +static void *idleMonitor(void *_arg) { + EncFS_Context *ctx = (EncFS_Context *)_arg; + shared_ptr arg = ctx->args; - const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; - int idleCycles = 0; + const int timeoutCycles = 60 * arg->idleTimeout / ActivityCheckInterval; + int idleCycles = 0; - pthread_mutex_lock( &ctx->wakeupMutex ); - - while(ctx->running) - { - int usage = ctx->getAndResetUsageCounter(); + pthread_mutex_lock(&ctx->wakeupMutex); - if(usage == 0 && ctx->isMounted()) - ++idleCycles; - else - idleCycles = 0; - - if(idleCycles >= timeoutCycles) - { - int openCount = ctx->openFileCount(); - if( openCount == 0 && unmountFS( ctx ) ) - { - // wait for main thread to wake us up - pthread_cond_wait( &ctx->wakeupCond, &ctx->wakeupMutex ); - break; - } - - rDebug("num open files: %i", openCount ); - } - - rDebug("idle cycle count: %i, timeout after %i", idleCycles, - timeoutCycles); + while (ctx->running) { + int usage = ctx->getAndResetUsageCounter(); - struct timeval currentTime; - gettimeofday( ¤tTime, 0 ); - struct timespec wakeupTime; - wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval; - wakeupTime.tv_nsec = currentTime.tv_usec * 1000; - pthread_cond_timedwait( &ctx->wakeupCond, - &ctx->wakeupMutex, &wakeupTime ); + if (usage == 0 && ctx->isMounted()) + ++idleCycles; + else + idleCycles = 0; + + if (idleCycles >= timeoutCycles) { + int openCount = ctx->openFileCount(); + if (openCount == 0 && unmountFS(ctx)) { + // wait for main thread to wake us up + pthread_cond_wait(&ctx->wakeupCond, &ctx->wakeupMutex); + break; + } + + rDebug("num open files: %i", openCount); } - - pthread_mutex_unlock( &ctx->wakeupMutex ); - rDebug("Idle monitoring thread exiting"); + rDebug("idle cycle count: %i, timeout after %i", idleCycles, timeoutCycles); - return 0; + struct timeval currentTime; + gettimeofday(¤tTime, 0); + struct timespec wakeupTime; + wakeupTime.tv_sec = currentTime.tv_sec + ActivityCheckInterval; + wakeupTime.tv_nsec = currentTime.tv_usec * 1000; + pthread_cond_timedwait(&ctx->wakeupCond, &ctx->wakeupMutex, &wakeupTime); + } + + pthread_mutex_unlock(&ctx->wakeupMutex); + + rDebug("Idle monitoring thread exiting"); + + return 0; } -static bool unmountFS(EncFS_Context *ctx) -{ - shared_ptr arg = ctx->args; - if( arg->opts->mountOnDemand ) - { - rDebug("Detaching filesystem %s due to inactivity", - arg->mountPoint.c_str()); +static bool unmountFS(EncFS_Context *ctx) { + shared_ptr arg = ctx->args; + if (arg->opts->mountOnDemand) { + rDebug("Detaching filesystem %s due to inactivity", + arg->mountPoint.c_str()); - ctx->setRoot( shared_ptr() ); - return false; - } else - { - // Time to unmount! - // xgroup(diag) - rWarning(_("Unmounting filesystem %s due to inactivity"), - arg->mountPoint.c_str()); - fuse_unmount( arg->mountPoint.c_str() ); - return true; - } + ctx->setRoot(shared_ptr()); + return false; + } else { + // Time to unmount! + // xgroup(diag) + rWarning(_("Unmounting filesystem %s due to inactivity"), + arg->mountPoint.c_str()); + fuse_unmount(arg->mountPoint.c_str()); + return true; + } } - diff --git a/encfs/makeKey.cpp b/encfs/makeKey.cpp index d5e4479..f4e2195 100644 --- a/encfs/makeKey.cpp +++ b/encfs/makeKey.cpp @@ -4,10 +4,10 @@ ***************************************************************************** * Copyright (c) 2008, Valient Gough * - * This program is free software: you can redistribute it and/or modify it + * 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. + * (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 @@ -31,36 +31,32 @@ using namespace std; -void genKey( const shared_ptr &cipher ) -{ - CipherKey key = cipher->newRandomKey(); +void genKey(const shared_ptr &cipher) { + CipherKey key = cipher->newRandomKey(); - // encode with itself - string b64Key = cipher->encodeAsString( key, key ); + // encode with itself + string b64Key = cipher->encodeAsString(key, key); - cout << b64Key << "\n"; + cout << b64Key << "\n"; } -int main(int argc, char **argv) -{ - pid_t pid = getpid(); - cerr << "pid = " << pid << "\n"; +int main(int argc, char **argv) { + pid_t pid = getpid(); + cerr << "pid = " << pid << "\n"; - if(argc != 3) - { - cerr << "usage: makeKey [AES|Blowfish] [128|160|192|224|256]\n"; - return 1; - } + if (argc != 3) { + cerr << "usage: makeKey [AES|Blowfish] [128|160|192|224|256]\n"; + return 1; + } - const char *type = argv[1]; - int size = atoi(argv[2]); + const char *type = argv[1]; + int size = atoi(argv[2]); - openssl_init(false); + openssl_init(false); - // get a list of the available algorithms - shared_ptr cipher = Cipher::New( type, size ); - genKey( cipher ); - - //openssl_shutdown(false); -} + // get a list of the available algorithms + shared_ptr cipher = Cipher::New(type, size); + genKey(cipher); + // openssl_shutdown(false); +} diff --git a/encfs/openssl.cpp b/encfs/openssl.cpp index dc388da..db7ff03 100644 --- a/encfs/openssl.cpp +++ b/encfs/openssl.cpp @@ -7,7 +7,7 @@ * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by the * Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. + * 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 @@ -31,79 +31,65 @@ #include #endif -unsigned long pthreads_thread_id() -{ - return (unsigned long)pthread_self(); -} +unsigned long pthreads_thread_id() { return (unsigned long)pthread_self(); } static pthread_mutex_t *crypto_locks = NULL; -void pthreads_locking_callback( int mode, int n, - const char *caller_file, int caller_line ) -{ - (void)caller_file; - (void)caller_line; +void pthreads_locking_callback(int mode, int n, const char *caller_file, + int caller_line) { + (void)caller_file; + (void)caller_line; - if(!crypto_locks) - { - rDebug("Allocating %i locks for OpenSSL", CRYPTO_num_locks() ); - crypto_locks = new pthread_mutex_t[ CRYPTO_num_locks() ]; - for(int i=0; i @@ -28,7 +29,8 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:00 millert Exp $"; +static const char rcsid[] = + "$OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:00 millert Exp $"; #endif /* LIBC_SCCS and not lint */ //#include "includes.h" @@ -50,134 +52,126 @@ static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41: #include #ifdef TCSASOFT -# define _T_FLUSH (TCSAFLUSH|TCSASOFT) +#define _T_FLUSH (TCSAFLUSH | TCSASOFT) #else -# define _T_FLUSH (TCSAFLUSH) +#define _T_FLUSH (TCSAFLUSH) #endif /* SunOS 4.x which lacks _POSIX_VDISABLE, but has VDISABLE */ #if !defined(_POSIX_VDISABLE) && defined(VDISABLE) -# define _POSIX_VDISABLE VDISABLE +#define _POSIX_VDISABLE VDISABLE #endif static volatile sig_atomic_t signo; static void handler(int); -char * -readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) -{ - ssize_t nr; - int input, output, save_errno; - char ch, *p, *end; - struct termios term, oterm; - struct sigaction sa, saveint, savehup, savequit, saveterm; - struct sigaction savetstp, savettin, savettou; +char *readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) { + ssize_t nr; + int input, output, save_errno; + char ch, *p, *end; + struct termios term, oterm; + struct sigaction sa, saveint, savehup, savequit, saveterm; + struct sigaction savetstp, savettin, savettou; - /* I suppose we could alloc on demand in this case (XXX). */ - if (bufsiz == 0) { - errno = EINVAL; - return(NULL); - } + /* I suppose we could alloc on demand in this case (XXX). */ + if (bufsiz == 0) { + errno = EINVAL; + return (NULL); + } restart: - /* - * Read and write to /dev/tty if available. If not, read from - * stdin and write to stderr unless a tty is required. - */ - if ((input = output = open(_PATH_TTY, O_RDWR)) == -1) { - if (flags & RPP_REQUIRE_TTY) { - errno = ENOTTY; - return(NULL); - } - input = STDIN_FILENO; - output = STDERR_FILENO; - } + /* + * Read and write to /dev/tty if available. If not, read from + * stdin and write to stderr unless a tty is required. + */ + if ((input = output = open(_PATH_TTY, O_RDWR)) == -1) { + if (flags & RPP_REQUIRE_TTY) { + errno = ENOTTY; + return (NULL); + } + input = STDIN_FILENO; + output = STDERR_FILENO; + } - /* - * Catch signals that would otherwise cause the user to end - * up with echo turned off in the shell. Don't worry about - * things like SIGALRM and SIGPIPE for now. - */ - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; /* don't restart system calls */ - sa.sa_handler = handler; - (void)sigaction(SIGINT, &sa, &saveint); - (void)sigaction(SIGHUP, &sa, &savehup); - (void)sigaction(SIGQUIT, &sa, &savequit); - (void)sigaction(SIGTERM, &sa, &saveterm); - (void)sigaction(SIGTSTP, &sa, &savetstp); - (void)sigaction(SIGTTIN, &sa, &savettin); - (void)sigaction(SIGTTOU, &sa, &savettou); + /* + * Catch signals that would otherwise cause the user to end + * up with echo turned off in the shell. Don't worry about + * things like SIGALRM and SIGPIPE for now. + */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; /* don't restart system calls */ + sa.sa_handler = handler; + (void)sigaction(SIGINT, &sa, &saveint); + (void)sigaction(SIGHUP, &sa, &savehup); + (void)sigaction(SIGQUIT, &sa, &savequit); + (void)sigaction(SIGTERM, &sa, &saveterm); + (void)sigaction(SIGTSTP, &sa, &savetstp); + (void)sigaction(SIGTTIN, &sa, &savettin); + (void)sigaction(SIGTTOU, &sa, &savettou); - /* Turn off echo if possible. */ - if (tcgetattr(input, &oterm) == 0) { - memcpy(&term, &oterm, sizeof(term)); - if (!(flags & RPP_ECHO_ON)) - term.c_lflag &= ~(ECHO | ECHONL); + /* Turn off echo if possible. */ + if (tcgetattr(input, &oterm) == 0) { + memcpy(&term, &oterm, sizeof(term)); + if (!(flags & RPP_ECHO_ON)) term.c_lflag &= ~(ECHO | ECHONL); #ifdef VSTATUS - if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) - term.c_cc[VSTATUS] = _POSIX_VDISABLE; + if (term.c_cc[VSTATUS] != _POSIX_VDISABLE) + term.c_cc[VSTATUS] = _POSIX_VDISABLE; #endif - (void)tcsetattr(input, _T_FLUSH, &term); - } else { - memset(&term, 0, sizeof(term)); - memset(&oterm, 0, sizeof(oterm)); - } + (void)tcsetattr(input, _T_FLUSH, &term); + } else { + memset(&term, 0, sizeof(term)); + memset(&oterm, 0, sizeof(oterm)); + } - (void)write(output, prompt, strlen(prompt)); - end = buf + bufsiz - 1; - for (p = buf; (nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r';) { - if (p < end) { - if ((flags & RPP_SEVENBIT)) - ch &= 0x7f; - if (isalpha(ch)) { - if ((flags & RPP_FORCELOWER)) - ch = tolower(ch); - if ((flags & RPP_FORCEUPPER)) - ch = toupper(ch); - } - *p++ = ch; - } - } - *p = '\0'; - save_errno = errno; - if (!(term.c_lflag & ECHO)) - (void)write(output, "\n", 1); + (void)write(output, prompt, strlen(prompt)); + end = buf + bufsiz - 1; + for (p = buf; (nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r';) { + if (p < end) { + if ((flags & RPP_SEVENBIT)) ch &= 0x7f; + if (isalpha(ch)) { + if ((flags & RPP_FORCELOWER)) ch = tolower(ch); + if ((flags & RPP_FORCEUPPER)) ch = toupper(ch); + } + *p++ = ch; + } + } + *p = '\0'; + save_errno = errno; + if (!(term.c_lflag & ECHO)) (void)write(output, "\n", 1); - /* Restore old terminal settings and signals. */ - if (memcmp(&term, &oterm, sizeof(term)) != 0) - (void)tcsetattr(input, _T_FLUSH, &oterm); - (void)sigaction(SIGINT, &saveint, NULL); - (void)sigaction(SIGHUP, &savehup, NULL); - (void)sigaction(SIGQUIT, &savequit, NULL); - (void)sigaction(SIGTERM, &saveterm, NULL); - (void)sigaction(SIGTSTP, &savetstp, NULL); - (void)sigaction(SIGTTIN, &savettin, NULL); - (void)sigaction(SIGTTOU, &savettou, NULL); - if (input != STDIN_FILENO) - (void)close(input); + /* Restore old terminal settings and signals. */ + if (memcmp(&term, &oterm, sizeof(term)) != 0) + (void)tcsetattr(input, _T_FLUSH, &oterm); + (void)sigaction(SIGINT, &saveint, NULL); + (void)sigaction(SIGHUP, &savehup, NULL); + (void)sigaction(SIGQUIT, &savequit, NULL); + (void)sigaction(SIGTERM, &saveterm, NULL); + (void)sigaction(SIGTSTP, &savetstp, NULL); + (void)sigaction(SIGTTIN, &savettin, NULL); + (void)sigaction(SIGTTOU, &savettou, NULL); + if (input != STDIN_FILENO) (void)close(input); - /* - * If we were interrupted by a signal, resend it to ourselves - * now that we have restored the signal handlers. - */ - if (signo) { - kill(getpid(), signo); - switch (signo) { - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - signo = 0; - goto restart; - } - } + /* + * If we were interrupted by a signal, resend it to ourselves + * now that we have restored the signal handlers. + */ + if (signo) { + kill(getpid(), signo); + switch (signo) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + signo = 0; + goto restart; + } + } - errno = save_errno; - return(nr == -1 ? NULL : buf); + errno = save_errno; + return (nr == -1 ? NULL : buf); } #endif /* HAVE_READPASSPHRASE */ - + #if 0 char * getpass(const char *prompt) @@ -188,8 +182,4 @@ getpass(const char *prompt) } #endif -static void handler(int s) -{ - - signo = s; -} +static void handler(int s) { signo = s; } diff --git a/encfs/test.cpp b/encfs/test.cpp index bea0d44..732ca4d 100644 --- a/encfs/test.cpp +++ b/encfs/test.cpp @@ -47,491 +47,398 @@ #endif #endif - using namespace std; using namespace rel; using namespace rlog; const int FSBlockSize = 256; -static -int checkErrorPropogation( const shared_ptr &cipher, - int size, int byteToChange, const CipherKey &key ) -{ - MemBlock orig = MemoryPool::allocate(size); - MemBlock data = MemoryPool::allocate(size); +static int checkErrorPropogation(const shared_ptr &cipher, int size, + int byteToChange, const CipherKey &key) { + MemBlock orig = MemoryPool::allocate(size); + MemBlock data = MemoryPool::allocate(size); - for(int i=0; istreamEncode( data.data, size, 0, key ); - else - cipher->blockEncode( data.data, size, 0, key ); - - // intoduce an error in the encoded data, so we can check error propogation - if(byteToChange >= 0 && byteToChange < size) - { - unsigned char previousValue = data.data[byteToChange]; - do - { - data.data[byteToChange] = rand(); - } while(data.data[byteToChange] == previousValue); - } + if (size != FSBlockSize) + cipher->streamEncode(data.data, size, 0, key); + else + cipher->blockEncode(data.data, size, 0, key); - if(size != FSBlockSize) - cipher->streamDecode( data.data, size, 0, key ); - else - cipher->blockDecode( data.data, size, 0, key ); + // intoduce an error in the encoded data, so we can check error propogation + if (byteToChange >= 0 && byteToChange < size) { + unsigned char previousValue = data.data[byteToChange]; + do { + data.data[byteToChange] = rand(); + } while (data.data[byteToChange] == previousValue); + } - int numByteErrors = 0; - for(int i=0; istreamDecode(data.data, size, 0, key); + else + cipher->blockDecode(data.data, size, 0, key); - MemoryPool::release( data ); - MemoryPool::release( orig ); + int numByteErrors = 0; + for (int i = 0; i < size; ++i) { + if (data.data[i] != orig.data[i]) ++numByteErrors; + } - return numByteErrors; + MemoryPool::release(data); + MemoryPool::release(orig); + + return numByteErrors; } const char TEST_ROOTDIR[] = "/foo"; -static -bool testNameCoding( DirNode &dirNode, bool verbose ) -{ - // encrypt a name - const char *name[] = { - "1234567", - "12345678", - "123456789", - "123456789ABCDEF", - "123456789ABCDEF0", - "123456789ABCDEF01", - "test-name", - "test-name2", - "test", - "../test", - "/foo/bar/blah", - "test-name.21", - "test-name.22", - "test-name.o", - "1.test", - "2.test", - "a/b/c/d", - "a/c/d/e", - "b/c/d/e", - "b/a/c/d", - NULL - }; +static bool testNameCoding(DirNode &dirNode, bool verbose) { + // encrypt a name + const char *name[] = { + "1234567", "12345678", "123456789", + "123456789ABCDEF", "123456789ABCDEF0", "123456789ABCDEF01", + "test-name", "test-name2", "test", + "../test", "/foo/bar/blah", "test-name.21", + "test-name.22", "test-name.o", "1.test", + "2.test", "a/b/c/d", "a/c/d/e", + "b/c/d/e", "b/a/c/d", NULL}; - const char **orig = name; - while(*orig) - { - if(verbose) - cerr << " coding name \"" << *orig << "\""; + const char **orig = name; + while (*orig) { + if (verbose) cerr << " coding name \"" << *orig << "\""; - string encName = dirNode.relativeCipherPath( *orig ); + string encName = dirNode.relativeCipherPath(*orig); - if(verbose) - cerr << " -> \"" << encName.c_str() << "\""; + if (verbose) cerr << " -> \"" << encName.c_str() << "\""; - // decrypt name - string decName = dirNode.plainPath( encName.c_str() ); + // decrypt name + string decName = dirNode.plainPath(encName.c_str()); - if(decName == *orig) - { - if(verbose) - cerr << " OK\n"; - } else - { - if(verbose) - cerr << " FAILED (got " << decName << ")\n"; - return false; - } - - orig++; + if (decName == *orig) { + if (verbose) cerr << " OK\n"; + } else { + if (verbose) cerr << " FAILED (got " << decName << ")\n"; + return false; } - return true; + orig++; + } + + return true; } -bool runTests(const shared_ptr &cipher, bool verbose) -{ - // create a random key - if(verbose) - cerr << "Generating new key, output will be different on each run\n\n"; - CipherKey key = cipher->newRandomKey(); +bool runTests(const shared_ptr &cipher, bool verbose) { + // create a random key + if (verbose) + cerr << "Generating new key, output will be different on each run\n\n"; + CipherKey key = cipher->newRandomKey(); - if(verbose) - cerr << "Testing key save / restore :"; - { - CipherKey encodingKey = cipher->newRandomKey(); - int encodedKeySize = cipher->encodedKeySize(); - unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; + if (verbose) cerr << "Testing key save / restore :"; + { + CipherKey encodingKey = cipher->newRandomKey(); + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char[encodedKeySize]; - cipher->writeKey( key, keyBuf, encodingKey ); - CipherKey key2 = cipher->readKey( keyBuf, encodingKey ); - if(!key2) - { - if(verbose) - cerr << " FAILED (decode error)\n"; - return false; - } - - if(cipher->compareKey( key, key2 )) - { - if(verbose) - cerr << " OK\n"; - } else - { - if(verbose) - cerr << " FAILED\n"; - return false; - } - } - - if(verbose) - cerr << "Testing Config interface load / store :"; - { - CipherKey encodingKey = cipher->newRandomKey(); - int encodedKeySize = cipher->encodedKeySize(); - unsigned char *keyBuf = new unsigned char [ encodedKeySize ]; - - cipher->writeKey( key, keyBuf, encodingKey ); - - // store in config struct.. - EncFSConfig cfg; - cfg.cipherIface = cipher->interface(); - cfg.keySize = 8 * cipher->keySize(); - cfg.blockSize = FSBlockSize; - cfg.assignKeyData( keyBuf, encodedKeySize ); - - // save config - string data; - { - ostringstream st; - st << cfg; - data = st.str(); - } - - // read back in and check everything.. - EncFSConfig cfg2; - { - istringstream st(data); - st >> cfg2; - } - - // check.. - rAssert( cfg.cipherIface.implements(cfg2.cipherIface) ); - rAssert( cfg.keySize == cfg2.keySize ); - rAssert( cfg.blockSize == cfg2.blockSize ); - - // try decoding key.. - - CipherKey key2 = cipher->readKey( cfg2.getKeyData(), encodingKey ); - if(!key2) - { - if(verbose) - cerr << " FAILED (decode error)\n"; - return false; - } - - if(cipher->compareKey( key, key2 )) - { - if(verbose) - cerr << " OK\n"; - } else - { - if(verbose) - cerr << " FAILED\n"; - return false; - } + cipher->writeKey(key, keyBuf, encodingKey); + CipherKey key2 = cipher->readKey(keyBuf, encodingKey); + if (!key2) { + if (verbose) cerr << " FAILED (decode error)\n"; + return false; } - FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); - fsCfg->cipher = cipher; - fsCfg->key = key; - fsCfg->config.reset(new EncFSConfig); - fsCfg->config->blockSize = FSBlockSize; - - if(verbose) - cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; - { - fsCfg->opts.reset(new EncFS_Opts); - fsCfg->opts->idleTracking = false; - fsCfg->config->uniqueIV = false; - - fsCfg->nameCoding.reset( new StreamNameIO( - StreamNameIO::CurrentInterface(), cipher, key ) ); - fsCfg->nameCoding->setChainedNameIV( true ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; + if (cipher->compareKey(key, key2)) { + if (verbose) cerr << " OK\n"; + } else { + if (verbose) cerr << " FAILED\n"; + return false; } - - if(verbose) - cerr << "Testing name encode/decode (block coding w/ IV chaining)\n"; + } + + if (verbose) cerr << "Testing Config interface load / store :"; + { + CipherKey encodingKey = cipher->newRandomKey(); + int encodedKeySize = cipher->encodedKeySize(); + unsigned char *keyBuf = new unsigned char[encodedKeySize]; + + cipher->writeKey(key, keyBuf, encodingKey); + + // store in config struct.. + EncFSConfig cfg; + cfg.cipherIface = cipher->interface(); + cfg.keySize = 8 * cipher->keySize(); + cfg.blockSize = FSBlockSize; + cfg.assignKeyData(keyBuf, encodedKeySize); + + // save config + string data; { - fsCfg->opts->idleTracking = false; - fsCfg->config->uniqueIV = false; - fsCfg->nameCoding.reset( new BlockNameIO( - BlockNameIO::CurrentInterface(), cipher, key, - cipher->cipherBlockSize() ) ); - fsCfg->nameCoding->setChainedNameIV( true ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } - - if(verbose) - cerr << "Testing name encode/decode (block coding w/ IV chaining, base32)\n"; - { - fsCfg->opts->idleTracking = false; - fsCfg->config->uniqueIV = false; - fsCfg->nameCoding.reset( new BlockNameIO( - BlockNameIO::CurrentInterface(), cipher, key, - cipher->cipherBlockSize(), true ) ); - fsCfg->nameCoding->setChainedNameIV( true ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } - - if(!verbose) - { - { - // test stream mode, this time without IV chaining - fsCfg->nameCoding = - shared_ptr( new StreamNameIO( - StreamNameIO::CurrentInterface(), cipher, key ) ); - fsCfg->nameCoding->setChainedNameIV( false ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } - - { - // test block mode, this time without IV chaining - fsCfg->nameCoding = shared_ptr( new BlockNameIO( - BlockNameIO::CurrentInterface(), cipher, key, - cipher->cipherBlockSize() ) ); - fsCfg->nameCoding->setChainedNameIV( false ); - - DirNode dirNode( NULL, TEST_ROOTDIR, fsCfg ); - - if(!testNameCoding( dirNode, verbose )) - return false; - } + ostringstream st; + st << cfg; + data = st.str(); } - if(verbose) - cerr << "Testing block encode/decode on full block - "; + // read back in and check everything.. + EncFSConfig cfg2; { - int numErrors = checkErrorPropogation( cipher, - FSBlockSize, -1, key ); - if(numErrors) - { - if(verbose) - cerr << " FAILED!\n"; - return false; - } else - { - if(verbose) - cerr << " OK\n"; - } - } - if(verbose) - cerr << "Testing block encode/decode on partial block - "; - { - int numErrors = checkErrorPropogation( cipher, - FSBlockSize-1, -1, key ); - if(numErrors) - { - if(verbose) - cerr << " FAILED!\n"; - return false; - } else - { - if(verbose) - cerr << " OK\n"; - } + istringstream st(data); + st >> cfg2; } - if(verbose) - cerr << "Checking error propogation in partial block:\n"; - { - int minChanges = FSBlockSize-1; - int maxChanges = 0; - int minAt = 0; - int maxAt = 0; - for(int i=0; i maxChanges) - { - maxChanges = numErrors; - maxAt = i; - } - } + // try decoding key.. - if(verbose) - { - cerr << "modification of 1 byte affected between " << minChanges - << " and " << maxChanges << " decoded bytes\n"; - cerr << "minimum change at byte " << minAt - << " and maximum at byte " << maxAt << "\n"; - } - } - if(verbose) - cerr << "Checking error propogation on full block:\n"; - { - int minChanges = FSBlockSize; - int maxChanges = 0; - int minAt = 0; - int maxAt = 0; - for(int i=0; i maxChanges) - { - maxChanges = numErrors; - maxAt = i; - } - } - - if(verbose) - { - cerr << "modification of 1 byte affected between " << minChanges - << " and " << maxChanges << " decoded bytes\n"; - cerr << "minimum change at byte " << minAt - << " and maximum at byte " << maxAt << "\n"; - } + CipherKey key2 = cipher->readKey(cfg2.getKeyData(), encodingKey); + if (!key2) { + if (verbose) cerr << " FAILED (decode error)\n"; + return false; } - return true; + if (cipher->compareKey(key, key2)) { + if (verbose) cerr << " OK\n"; + } else { + if (verbose) cerr << " FAILED\n"; + return false; + } + } + + FSConfigPtr fsCfg = FSConfigPtr(new FSConfig); + fsCfg->cipher = cipher; + fsCfg->key = key; + fsCfg->config.reset(new EncFSConfig); + fsCfg->config->blockSize = FSBlockSize; + + if (verbose) + cerr << "Testing name encode/decode (stream coding w/ IV chaining)\n"; + { + fsCfg->opts.reset(new EncFS_Opts); + fsCfg->opts->idleTracking = false; + fsCfg->config->uniqueIV = false; + + fsCfg->nameCoding.reset( + new StreamNameIO(StreamNameIO::CurrentInterface(), cipher, key)); + fsCfg->nameCoding->setChainedNameIV(true); + + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + + if (!testNameCoding(dirNode, verbose)) return false; + } + + if (verbose) + cerr << "Testing name encode/decode (block coding w/ IV chaining)\n"; + { + fsCfg->opts->idleTracking = false; + fsCfg->config->uniqueIV = false; + fsCfg->nameCoding.reset(new BlockNameIO(BlockNameIO::CurrentInterface(), + cipher, key, + cipher->cipherBlockSize())); + fsCfg->nameCoding->setChainedNameIV(true); + + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + + if (!testNameCoding(dirNode, verbose)) return false; + } + + if (verbose) + cerr + << "Testing name encode/decode (block coding w/ IV chaining, base32)\n"; + { + fsCfg->opts->idleTracking = false; + fsCfg->config->uniqueIV = false; + fsCfg->nameCoding.reset(new BlockNameIO(BlockNameIO::CurrentInterface(), + cipher, key, + cipher->cipherBlockSize(), true)); + fsCfg->nameCoding->setChainedNameIV(true); + + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + + if (!testNameCoding(dirNode, verbose)) return false; + } + + if (!verbose) { + { + // test stream mode, this time without IV chaining + fsCfg->nameCoding = shared_ptr( + new StreamNameIO(StreamNameIO::CurrentInterface(), cipher, key)); + fsCfg->nameCoding->setChainedNameIV(false); + + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + + if (!testNameCoding(dirNode, verbose)) return false; + } + + { + // test block mode, this time without IV chaining + fsCfg->nameCoding = shared_ptr( + new BlockNameIO(BlockNameIO::CurrentInterface(), cipher, key, + cipher->cipherBlockSize())); + fsCfg->nameCoding->setChainedNameIV(false); + + DirNode dirNode(NULL, TEST_ROOTDIR, fsCfg); + + if (!testNameCoding(dirNode, verbose)) return false; + } + } + + if (verbose) cerr << "Testing block encode/decode on full block - "; + { + int numErrors = checkErrorPropogation(cipher, FSBlockSize, -1, key); + if (numErrors) { + if (verbose) cerr << " FAILED!\n"; + return false; + } else { + if (verbose) cerr << " OK\n"; + } + } + if (verbose) cerr << "Testing block encode/decode on partial block - "; + { + int numErrors = checkErrorPropogation(cipher, FSBlockSize - 1, -1, key); + if (numErrors) { + if (verbose) cerr << " FAILED!\n"; + return false; + } else { + if (verbose) cerr << " OK\n"; + } + } + + if (verbose) cerr << "Checking error propogation in partial block:\n"; + { + int minChanges = FSBlockSize - 1; + int maxChanges = 0; + int minAt = 0; + int maxAt = 0; + for (int i = 0; i < FSBlockSize - 1; ++i) { + int numErrors = checkErrorPropogation(cipher, FSBlockSize - 1, i, key); + + if (numErrors < minChanges) { + minChanges = numErrors; + minAt = i; + } + if (numErrors > maxChanges) { + maxChanges = numErrors; + maxAt = i; + } + } + + if (verbose) { + cerr << "modification of 1 byte affected between " << minChanges + << " and " << maxChanges << " decoded bytes\n"; + cerr << "minimum change at byte " << minAt << " and maximum at byte " + << maxAt << "\n"; + } + } + if (verbose) cerr << "Checking error propogation on full block:\n"; + { + int minChanges = FSBlockSize; + int maxChanges = 0; + int minAt = 0; + int maxAt = 0; + for (int i = 0; i < FSBlockSize; ++i) { + int numErrors = checkErrorPropogation(cipher, FSBlockSize, i, key); + + if (numErrors < minChanges) { + minChanges = numErrors; + minAt = i; + } + if (numErrors > maxChanges) { + maxChanges = numErrors; + maxAt = i; + } + } + + if (verbose) { + cerr << "modification of 1 byte affected between " << minChanges + << " and " << maxChanges << " decoded bytes\n"; + cerr << "minimum change at byte " << minAt << " and maximum at byte " + << maxAt << "\n"; + } + } + + return true; } +int main(int argc, char *argv[]) { + RLogInit(argc, argv); -int main(int argc, char *argv[]) -{ - RLogInit( argc, argv ); - - StdioNode stdLog( STDERR_FILENO ); - stdLog.subscribeTo( RLOG_CHANNEL("error") ); - stdLog.subscribeTo( RLOG_CHANNEL("warning") ); + StdioNode stdLog(STDERR_FILENO); + stdLog.subscribeTo(RLOG_CHANNEL("error")); + stdLog.subscribeTo(RLOG_CHANNEL("warning")); #ifndef NO_DEBUG - stdLog.subscribeTo( RLOG_CHANNEL("debug") ); + stdLog.subscribeTo(RLOG_CHANNEL("debug")); #endif #ifdef HAVE_SSL - SSL_load_error_strings(); - SSL_library_init(); + SSL_load_error_strings(); + SSL_library_init(); #ifndef OPENSSL_NO_ENGINE - ENGINE_load_builtin_engines(); - ENGINE_register_all_ciphers(); - ENGINE_register_all_digests(); - ENGINE_register_all_RAND(); + ENGINE_load_builtin_engines(); + ENGINE_register_all_ciphers(); + ENGINE_register_all_digests(); + ENGINE_register_all_RAND(); #endif #endif - srand( time(0) ); + srand(time(0)); - // get a list of the available algorithms - std::list algorithms = - Cipher::GetAlgorithmList(); - std::list::const_iterator it; - cerr << "Supported Crypto interfaces:\n"; - for(it = algorithms.begin(); it != algorithms.end(); ++it) - { - cerr << it->name - << " ( " << it->iface.name() << " " - << it->iface.current() << ":" - << it->iface.revision() << ":" - << it->iface.age() << " ) : " << it->description << "\n"; - cerr << " - key length " << it->keyLength.min() << " to " - << it->keyLength.max() << " , block size " << it->blockSize.min() - << " to " << it->blockSize.max() << "\n"; + // get a list of the available algorithms + std::list algorithms = Cipher::GetAlgorithmList(); + std::list::const_iterator it; + cerr << "Supported Crypto interfaces:\n"; + for (it = algorithms.begin(); it != algorithms.end(); ++it) { + cerr << it->name << " ( " << it->iface.name() << " " << it->iface.current() + << ":" << it->iface.revision() << ":" << it->iface.age() + << " ) : " << it->description << "\n"; + cerr << " - key length " << it->keyLength.min() << " to " + << it->keyLength.max() << " , block size " << it->blockSize.min() + << " to " << it->blockSize.max() << "\n"; + } + cerr << "\n"; + + cerr << "Testing interfaces\n"; + for (it = algorithms.begin(); it != algorithms.end(); ++it) { + int blockSize = it->blockSize.closest(256); + for (int keySize = it->keyLength.min(); keySize <= it->keyLength.max(); + keySize += it->keyLength.inc()) { + cerr << it->name << ", key length " << keySize << ", block size " + << blockSize << ": "; + + shared_ptr cipher = Cipher::New(it->name, keySize); + if (!cipher) { + cerr << "FAILED TO CREATE\n"; + } else { + try { + if (runTests(cipher, false)) + cerr << "OK\n"; + else + cerr << "FAILED\n"; + } + catch (rlog::Error &er) { + cerr << "Error: " << er.what() << "\n"; + } + } } - cerr << "\n"; + } - cerr << "Testing interfaces\n"; - for(it = algorithms.begin(); it != algorithms.end(); ++it) - { - int blockSize = it->blockSize.closest( 256 ); - for(int keySize = it->keyLength.min(); keySize <= it->keyLength.max(); - keySize += it->keyLength.inc()) - { - cerr << it->name << ", key length " << keySize - << ", block size " << blockSize << ": "; + // run one test with verbose output too.. + shared_ptr cipher = Cipher::New("AES", 192); + if (!cipher) { + cerr << "\nNo AES cipher found, skipping verbose test.\n"; + } else { + cerr << "\nVerbose output for " << cipher->interface().name() + << " test, key length " << cipher->keySize() * 8 << ", block size " + << FSBlockSize << ":\n"; - shared_ptr cipher = Cipher::New( it->name, keySize ); - if(!cipher) - { - cerr << "FAILED TO CREATE\n"; - } else - { - try - { - if(runTests( cipher, false )) - cerr << "OK\n"; - else - cerr << "FAILED\n"; - } catch( rlog::Error &er ) - { - cerr << "Error: " << er.what() << "\n"; - } - } - } - } + runTests(cipher, true); + } - // run one test with verbose output too.. - shared_ptr cipher = Cipher::New("AES", 192); - if(!cipher) - { - cerr << "\nNo AES cipher found, skipping verbose test.\n"; - } else - { - cerr << "\nVerbose output for " << cipher->interface().name() - << " test, key length " << cipher->keySize()*8 << ", block size " - << FSBlockSize << ":\n"; + MemoryPool::destroyAll(); - runTests( cipher, true ); - } - - MemoryPool::destroyAll(); - - return 0; + return 0; } - - -