mirror of
https://github.com/vgough/encfs.git
synced 2025-06-19 19:27:48 +02:00
Reformat files, replace autoconf with CMake, and RLog with GLog.
git-svn-id: http://encfs.googlecode.com/svn/trunk@92 db9cf616-1c43-0410-9cb8-a902689de0d6
This commit is contained in:
parent
efe358c2d5
commit
fb9a8ff879
14
AUTHORS
14
AUTHORS
@ -2,7 +2,15 @@
|
|||||||
Valient Gough <vgough@pobox.com>
|
Valient Gough <vgough@pobox.com>
|
||||||
|
|
||||||
|
|
||||||
Also, thanks to the work of many contributors, encfs as of 1.1.11 now has
|
With significant contributions from:
|
||||||
full or partial translations for many languages.
|
|
||||||
See README-NLS and TRANSLATORS for more details.
|
Csaba Henk
|
||||||
|
David Rosenstrauch
|
||||||
|
Gerald Klix
|
||||||
|
Janne Hellsten
|
||||||
|
p.kosseff
|
||||||
|
|
||||||
|
|
||||||
|
Also, thanks to the work of many contributors, encfs has full or partial
|
||||||
|
translations for many languages. See README-NLS for more details.
|
||||||
|
|
||||||
|
67
CMakeLists.txt
Normal file
67
CMakeLists.txt
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
project(Encfs)
|
||||||
|
|
||||||
|
set (ENCFS_MAJOR 2)
|
||||||
|
set (ENCFS_MINOR 0)
|
||||||
|
set (ENCFS_PATCH 0)
|
||||||
|
set (ENCFS_VERSION "${ENCFS_MAJOR}.${ENCFS_MINOR}.${ENCFS_PATCH}")
|
||||||
|
|
||||||
|
option (BUILD_SHARED_LIBS "Build dynamic link libraries" OFF)
|
||||||
|
|
||||||
|
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
||||||
|
"${CMAKE_SOURCE_DIR}/CMakeModules/")
|
||||||
|
|
||||||
|
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall")
|
||||||
|
|
||||||
|
set (CPACK_PACKAGE_NAME "Encfs")
|
||||||
|
set (CPACK_PACKAGE_VERSION_MAJOR ${ENCFS_MAJOR})
|
||||||
|
set (CPACK_PACKAGE_VERSION_MINOR ${ENCFS_MINOR})
|
||||||
|
set (CPACK_PACKAGE_VERSION_PATCH ${ENCFS_PATCH})
|
||||||
|
set (CPACK_SOURCE_GENERATOR TGZ)
|
||||||
|
set (CPACK_SOURCE_IGNORE_FILES
|
||||||
|
"/_darcs/"
|
||||||
|
"/build/")
|
||||||
|
include (CPack)
|
||||||
|
|
||||||
|
include (CheckIncludeFileCXX)
|
||||||
|
check_include_file_cxx (attr/xattr.h HAVE_ATTR_XATTR_H)
|
||||||
|
check_include_file_cxx (sys/xattr.h HAVE_SYS_XATTR_H)
|
||||||
|
|
||||||
|
check_include_file_cxx (tr1/memory HAVE_TR1_MEMORY)
|
||||||
|
check_include_file_cxx (tr1/unordered_map HAVE_TR1_UNORDERED_MAP)
|
||||||
|
check_include_file_cxx (tr1/unordered_set HAVE_TR1_UNORDERED_SET)
|
||||||
|
check_include_file_cxx (tr1/tuple HAVE_TR1_TUPLE)
|
||||||
|
|
||||||
|
check_include_file_cxx (valgrind/valgrind.h HAVE_VALGRIND_VALGRIND_H)
|
||||||
|
check_include_file_cxx (valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||||
|
|
||||||
|
# TODO: move this to cipher directory.
|
||||||
|
find_package (OpenSSL REQUIRED)
|
||||||
|
include (OpenSSLTests)
|
||||||
|
|
||||||
|
# Check if xattr functions take extra argument.
|
||||||
|
include (CheckCXXSourceCompiles)
|
||||||
|
CHECK_CXX_SOURCE_COMPILES ("#include <sys/types.h>
|
||||||
|
#include <sys/xattr.h>
|
||||||
|
int main() { getxattr(0,0,0,0,0,0); return 1; } " XATTR_ADD_OPT)
|
||||||
|
|
||||||
|
add_definitions (-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26)
|
||||||
|
if (APPLE)
|
||||||
|
add_definitions (-D__FreeBSD__=10)
|
||||||
|
endif (APPLE)
|
||||||
|
|
||||||
|
find_package (GLog REQUIRED)
|
||||||
|
include_directories (${GLOG_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
find_program (POD2MAN pod2man)
|
||||||
|
|
||||||
|
include_directories (${Encfs_BINARY_DIR})
|
||||||
|
include_directories (${Encfs_SOURCE_DIR})
|
||||||
|
|
||||||
|
add_subdirectory(base)
|
||||||
|
add_subdirectory(cipher)
|
||||||
|
add_subdirectory(fs)
|
||||||
|
add_subdirectory(encfs)
|
||||||
|
add_subdirectory(util)
|
||||||
|
add_subdirectory(po)
|
||||||
|
|
35
CMakeModules/FindFUSE.cmake
Normal file
35
CMakeModules/FindFUSE.cmake
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Find the FUSE includes and library
|
||||||
|
#
|
||||||
|
# FUSE_INCLUDE_DIR - where to find fuse.h, etc.
|
||||||
|
# FUSE_LIBRARIES - List of libraries when using FUSE.
|
||||||
|
# FUSE_FOUND - True if FUSE lib is found.
|
||||||
|
|
||||||
|
# check if already in cache, be silent
|
||||||
|
IF (FUSE_INCLUDE_DIR)
|
||||||
|
SET (FUSE_FIND_QUIETLY TRUE)
|
||||||
|
ENDIF (FUSE_INCLUDE_DIR)
|
||||||
|
|
||||||
|
# find includes
|
||||||
|
FIND_PATH (FUSE_INCLUDE_DIR fuse.h
|
||||||
|
/usr/local/include/osxfuse
|
||||||
|
/usr/local/include
|
||||||
|
/usr/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# find lib
|
||||||
|
if (APPLE)
|
||||||
|
SET(FUSE_NAMES libosxfuse.dylib fuse)
|
||||||
|
else (APPLE)
|
||||||
|
SET(FUSE_NAMES fuse)
|
||||||
|
endif (APPLE)
|
||||||
|
FIND_LIBRARY(FUSE_LIBRARIES
|
||||||
|
NAMES ${FUSE_NAMES}
|
||||||
|
PATHS /lib64 /lib /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
include ("FindPackageHandleStandardArgs")
|
||||||
|
find_package_handle_standard_args ("FUSE" DEFAULT_MSG
|
||||||
|
FUSE_INCLUDE_DIR FUSE_LIBRARIES)
|
||||||
|
|
||||||
|
mark_as_advanced (FUSE_INCLUDE_DIR FUSE_LIBRARIES)
|
||||||
|
|
59
CMakeModules/FindGLog.cmake
Normal file
59
CMakeModules/FindGLog.cmake
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Try to find the libglog libraries
|
||||||
|
# Once done this will define :
|
||||||
|
#
|
||||||
|
# Glog_FOUND - system has libglog
|
||||||
|
# Glog_INCLUDE_DIRS - the libglog include directory
|
||||||
|
# Glog_LIBRARIES - libglog library
|
||||||
|
|
||||||
|
# Inputs to this module:
|
||||||
|
# GLOG_ROOT The preferred installation prefix for searching for glog. Set
|
||||||
|
# this if the module has problems finding the proper glog installation.
|
||||||
|
|
||||||
|
# If GLOG_ROOT was defined in the environment, use it.
|
||||||
|
IF (NOT GLOG_ROOT AND NOT $ENV{GLOG_ROOT} STREQUAL "")
|
||||||
|
SET(GLOG_ROOT $ENV{GLOG_ROOT})
|
||||||
|
ENDIF(NOT GLOG_ROOT AND NOT $ENV{GLOG_ROOT} STREQUAL "")
|
||||||
|
IF( GLOG_ROOT )
|
||||||
|
file(TO_CMAKE_PATH ${GLOG_ROOT} GLOG_ROOT)
|
||||||
|
ENDIF( GLOG_ROOT )
|
||||||
|
|
||||||
|
SET (GLOG_INCLUDE_DIRS)
|
||||||
|
SET (GLOG_LIBRARIES)
|
||||||
|
IF(WIN32)
|
||||||
|
IF(MSVC)
|
||||||
|
FIND_PATH(GLOG_INCLUDE_DIRS NAMES src/windows/glog/logging.h HINTS ${GLOG_ROOT})
|
||||||
|
IF(GLOG_INCLUDE_DIRS)
|
||||||
|
SET(GLOG_INCLUDE_DIRS ${GLOG_INCLUDE_DIRS}/src/windows)
|
||||||
|
ENDIF(GLOG_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
IF (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
message (STATUS " searching ${GLOG_ROOT}/Release/libglog.lib ...")
|
||||||
|
FIND_LIBRARY(GLOG_LIBRARIES NAMES libglog.lib HINTS ${GLOG_ROOT}/Release $ENV{LIB} PATH_SUFFIXES ".lib")
|
||||||
|
ELSE (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
message (STATUS " searching ${GLOG_ROOT}/Debug/libglog.lib ...")
|
||||||
|
FIND_LIBRARY(GLOG_LIBRARIES NAMES libglog.lib HINTS ${GLOG_ROOT}/Debug $ENV{LIB} PATH_SUFFIXES ".lib")
|
||||||
|
ENDIF (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
ELSE(MSVC)
|
||||||
|
SET(Glog_FOUND FALSE)
|
||||||
|
message (STATUS " Crap. this module supports only MSVC in Windows.")
|
||||||
|
ENDIF(MSVC)
|
||||||
|
ELSE(WIN32)
|
||||||
|
FIND_PATH(GLOG_INCLUDE_DIRS NAMES glog/logging.h HINTS ${GLOG_ROOT}/include ${GLOG_ROOT} /include/ /usr/include/ /usr/local/include/ /opt/local/include/)
|
||||||
|
FIND_LIBRARY(GLOG_LIBRARIES NAMES glog HINTS ${GLOG_ROOT}/lib ${GLOG_ROOT} /lib /usr/lib /usr/local/lib /opt/local/lib)
|
||||||
|
ENDIF(WIN32)
|
||||||
|
|
||||||
|
IF(GLOG_INCLUDE_DIRS AND GLOG_LIBRARIES)
|
||||||
|
SET(Glog_FOUND TRUE)
|
||||||
|
message (STATUS " glog found in include=${GLOG_INCLUDE_DIRS},lib=${GLOG_LIBRARIES}")
|
||||||
|
ELSE(GLOG_INCLUDE_DIRS AND GLOG_LIBRARIES)
|
||||||
|
SET(Glog_FOUND FALSE)
|
||||||
|
message (STATUS " glog not found. Please set GLOG_ROOT to the root directory containing glog.")
|
||||||
|
IF(GLOG_INCLUDE_DIRS)
|
||||||
|
message (STATUS " include=${GLOG_INCLUDE_DIRS}, but lib not found")
|
||||||
|
ENDIF(GLOG_INCLUDE_DIRS)
|
||||||
|
IF(GLOG_LIBRARIES)
|
||||||
|
message (STATUS " lib=${GLOG_LIBRARIES}, but include not found")
|
||||||
|
ENDIF(GLOG_LIBRARIES)
|
||||||
|
ENDIF(GLOG_INCLUDE_DIRS AND GLOG_LIBRARIES)
|
||||||
|
|
||||||
|
MARK_AS_ADVANCED(GLOG_INCLUDE_DIRS GLOG_LIBRARIES)
|
26
CMakeModules/FindTinyXML.cmake
Normal file
26
CMakeModules/FindTinyXML.cmake
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# - Find TinyXML
|
||||||
|
# Find the native TinyXML includes and library
|
||||||
|
#
|
||||||
|
# TINYXML_FOUND - True if TinyXML found.
|
||||||
|
# TINYXML_INCLUDE_DIR - where to find tinyxml.h, etc.
|
||||||
|
# TINYXML_LIBRARIES - List of libraries when using TinyXML.
|
||||||
|
#
|
||||||
|
|
||||||
|
IF( TINYXML_INCLUDE_DIR )
|
||||||
|
# Already in cache, be silent
|
||||||
|
SET( TinyXML_FIND_QUIETLY TRUE )
|
||||||
|
ENDIF( TINYXML_INCLUDE_DIR )
|
||||||
|
|
||||||
|
FIND_PATH( TINYXML_INCLUDE_DIR "tinyxml.h"
|
||||||
|
PATH_SUFFIXES "tinyxml" )
|
||||||
|
|
||||||
|
FIND_LIBRARY( TINYXML_LIBRARIES
|
||||||
|
NAMES "tinyxml"
|
||||||
|
PATH_SUFFIXES "tinyxml" )
|
||||||
|
|
||||||
|
# handle the QUIETLY and REQUIRED arguments and set TINYXML_FOUND to TRUE if
|
||||||
|
# all listed variables are TRUE
|
||||||
|
INCLUDE( "FindPackageHandleStandardArgs" )
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS( "TinyXML" DEFAULT_MSG TINYXML_INCLUDE_DIR TINYXML_LIBRARIES )
|
||||||
|
|
||||||
|
MARK_AS_ADVANCED( TINYXML_INCLUDE_DIR TINYXML_LIBRARIES )
|
287
CMakeModules/GettextTranslate.cmake
Normal file
287
CMakeModules/GettextTranslate.cmake
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
# Copyright (c) 2012, Jarryd Beck
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# Redistributions of source code must retain the above copyright notice, this
|
||||||
|
# list of conditions and the following disclaimer.
|
||||||
|
# Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||||
|
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
# This module creates build rules for updating translation files made
|
||||||
|
# with gettext
|
||||||
|
# In your top level CMakeLists.txt, do
|
||||||
|
# include(GettextTranslate)
|
||||||
|
# then in any po directory where you want things to be translated, write
|
||||||
|
# GettextTranslate()
|
||||||
|
#
|
||||||
|
# This module also finds the gettext binaries. If these are in a non-standard
|
||||||
|
# location, you can define the following variables to provide paths to search
|
||||||
|
# in
|
||||||
|
# GettextTranslate_BINARIES --- a path in which to look for every program
|
||||||
|
# GettextTranslate_XGETTEXT --- the xgettext program
|
||||||
|
# GettextTranslate_MSGINIT --- the msginit program
|
||||||
|
# GettextTranslate_MSGFILTER --- the msgfilter program
|
||||||
|
# GettextTranslate_MSGCONV --- the msgconv program
|
||||||
|
# GettextTranslate_MSGMERGE --- the msgmerge program
|
||||||
|
# GettextTranslate_MSGFMT --- the msgfmt program
|
||||||
|
# these are searched first before $PATH, so set this if you have your own
|
||||||
|
# version that overrides the system version
|
||||||
|
#
|
||||||
|
# it reads variables from Makevars, one of the most important being DOMAIN
|
||||||
|
# it reads the languages to generate from LINGUAS
|
||||||
|
#
|
||||||
|
# it adds the following targets
|
||||||
|
# update-po
|
||||||
|
# update-gmo
|
||||||
|
# ${DOMAIN}-pot.update
|
||||||
|
# generate-${DOMAIN}-${lang}-po
|
||||||
|
# generate-${DOMAIN}-${lang}-gmo
|
||||||
|
#
|
||||||
|
# where ${DOMAIN} is the DOMAIN variable read from Makevars
|
||||||
|
# and ${lang} is each language mentioned in LINGUAS
|
||||||
|
#
|
||||||
|
# if you want update-gmo to be added to the "all" target, then define the
|
||||||
|
# variable GettextTranslate_ALL before including this file
|
||||||
|
#
|
||||||
|
# by default, the gmo files are built in the source directory. If you want
|
||||||
|
# them to be built in the binary directory, then define the variable
|
||||||
|
# GettextTranslate_GMO_BINARY
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# add the update-po and update-gmo targets, the actual files that need to
|
||||||
|
# depend on this will be added as we go
|
||||||
|
|
||||||
|
if (DEFINED GettextTranslate_ALL)
|
||||||
|
set(_addToALL "ALL")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_target(update-po)
|
||||||
|
add_custom_target(update-gmo ${_addToALL})
|
||||||
|
|
||||||
|
#look for all the programs
|
||||||
|
#xgettext, msginit, msgfilter, msgconv, msgmerge, msgfmt
|
||||||
|
|
||||||
|
function(REQUIRE_BINARY binname varname)
|
||||||
|
if (defined ${${varname}-NOTFOUND})
|
||||||
|
message(FATAL_ERROR "Could not find " binname)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
find_program(GettextTranslate_XGETTEXT_EXECUTABLE xgettext
|
||||||
|
HINTS ${GettextTranslate_XGETTEXT} ${GettextTranslate_BINARIES}
|
||||||
|
)
|
||||||
|
REQUIRE_BINARY(xgettext GettextTranslate_XGETTEXT_EXECUTABLE)
|
||||||
|
|
||||||
|
find_program(GettextTranslate_MSGINIT_EXECUTABLE msginit
|
||||||
|
HINTS ${GettextTranslate_MSGINIT} ${GettextTranslate_BINARIES}
|
||||||
|
)
|
||||||
|
REQUIRE_BINARY(msginit GettextTranslate_MSGINIT_EXECUTABLE)
|
||||||
|
|
||||||
|
find_program(GettextTranslate_MSGFILTER_EXECUTABLE msgfilter
|
||||||
|
HINTS ${GettextTranslate_MSGFILTER} ${GettextTranslate_BINARIES}
|
||||||
|
)
|
||||||
|
REQUIRE_BINARY(msgfilter GettextTranslate_MSGFILTER_EXECUTABLE)
|
||||||
|
|
||||||
|
find_program(GettextTranslate_MSGCONV_EXECUTABLE msgconv
|
||||||
|
HINTS ${GettextTranslate_MSGCONV} ${GettextTranslate_BINARIES}
|
||||||
|
)
|
||||||
|
REQUIRE_BINARY(msgconv GettextTranslate_MSGCONV_EXECUTABLE)
|
||||||
|
|
||||||
|
find_program(GettextTranslate_MSGMERGE_EXECUTABLE msgmerge
|
||||||
|
HINTS ${GettextTranslate_MSGMERGE} ${GettextTranslate_BINARIES}
|
||||||
|
)
|
||||||
|
REQUIRE_BINARY(msgmerge GettextTranslate_MSGMERGE_EXECUTABLE)
|
||||||
|
|
||||||
|
find_program(GettextTranslate_MSGFMT_EXECUTABLE msgfmt
|
||||||
|
HINTS ${GettextTranslate_MSGFMT} ${GettextTranslate_BINARIES}
|
||||||
|
)
|
||||||
|
REQUIRE_BINARY(msgfmt GettextTranslate_MSGFMT_EXECUTABLE)
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
GettextTranslate_MSGCONV_EXECUTABLE
|
||||||
|
GettextTranslate_MSGFILTER_EXECUTABLE
|
||||||
|
GettextTranslate_MSGFMT_EXECUTABLE
|
||||||
|
GettextTranslate_MSGINIT_EXECUTABLE
|
||||||
|
GettextTranslate_MSGMERGE_EXECUTABLE
|
||||||
|
GettextTranslate_XGETTEXT_EXECUTABLE
|
||||||
|
)
|
||||||
|
|
||||||
|
macro(GettextTranslate)
|
||||||
|
|
||||||
|
if(GettextTranslate_GMO_BINARY)
|
||||||
|
set (GMO_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
else()
|
||||||
|
set (GMO_BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in)
|
||||||
|
message(FATAL_ERROR "There is no POTFILES.in in
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Makevars)
|
||||||
|
message(FATAL_ERROR "There is no Makevars in ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/Makevars makevars
|
||||||
|
REGEX "^[^=]+=(.*)$"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(makevar ${makevars})
|
||||||
|
string(REGEX REPLACE "^([^= ]+) =[ ]?(.*)$" "\\1" MAKEVAR_KEY ${makevar})
|
||||||
|
string(REGEX REPLACE "^([^= ]+) =[ ]?(.*)$" "\\2"
|
||||||
|
MAKEVAR_${MAKEVAR_KEY} ${makevar})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/POTFILES
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/LINGUAS
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/LINGUAS
|
||||||
|
COPYONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
#set the directory to not clean
|
||||||
|
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
PROPERTY CLEAN_NO_CUSTOM true)
|
||||||
|
|
||||||
|
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in potfiles
|
||||||
|
REGEX "^[^#].*"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(potfile ${potfiles})
|
||||||
|
list(APPEND source_translatable
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/${MAKEVAR_top_builddir}/${potfile})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set(TEMPLATE_FILE ${MAKEVAR_DOMAIN}.pot)
|
||||||
|
set(TEMPLATE_FILE_ABS ${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_FILE})
|
||||||
|
string(REGEX MATCHALL "[^ ]+" XGETTEXT_OPTS ${MAKEVAR_XGETTEXT_OPTIONS})
|
||||||
|
#add_custom_target(${MAKEVAR_DOMAIN}.pot-update DEPENDS
|
||||||
|
# ${TEMPLATE_FILE_ABS}
|
||||||
|
#)
|
||||||
|
|
||||||
|
add_custom_target(${MAKEVAR_DOMAIN}.pot-update
|
||||||
|
COMMAND ${GettextTranslate_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTS}
|
||||||
|
-o ${TEMPLATE_FILE_ABS}
|
||||||
|
--default-domain=${MAKEVAR_DOMAIN}
|
||||||
|
--add-comments=TRANSLATORS:
|
||||||
|
--copyright-holder=${MAKEVAR_COPYRIGHT_HOLDER}
|
||||||
|
--msgid-bugs-address="${MAKEVAR_MSGID_BUGS_ADDRESS}"
|
||||||
|
--directory=${MAKEVAR_top_builddir}
|
||||||
|
--files-from=${CMAKE_CURRENT_BINARY_DIR}/POTFILES
|
||||||
|
--package-version=${VERSION}
|
||||||
|
--package-name=${CMAKE_PROJECT_NAME}
|
||||||
|
DEPENDS ${source_translatable}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
#add_custom_command(OUTPUT ${TEMPLATE_FILE_ABS}
|
||||||
|
# COMMAND ${GettextTranslate_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTS}
|
||||||
|
# -o ${TEMPLATE_FILE_ABS}
|
||||||
|
# --default-domain=${MAKEVAR_DOMAIN}
|
||||||
|
# --add-comments=TRANSLATORS:
|
||||||
|
# --copyright-holder=${MAKEVAR_COPYRIGHT_HOLDER}
|
||||||
|
# --msgid-bugs-address="${MAKEVAR_MSGID_BUGS_ADDRESS}"
|
||||||
|
# --directory=${MAKEVAR_top_builddir}
|
||||||
|
# --files-from=${CMAKE_CURRENT_BINARY_DIR}/POTFILES
|
||||||
|
# --package-version=${VERSION}
|
||||||
|
# --package-name=${CMAKE_PROJECT_NAME}
|
||||||
|
# DEPENDS ${source_translatable}
|
||||||
|
# ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in
|
||||||
|
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
#)
|
||||||
|
|
||||||
|
#add_dependencies(update-po ${MAKEVAR_DOMAIN}.pot-update)
|
||||||
|
|
||||||
|
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/LINGUAS LINGUAS
|
||||||
|
REGEX "^[^#].*")
|
||||||
|
string(REGEX MATCHALL "[^ ]+" languages ${LINGUAS})
|
||||||
|
|
||||||
|
foreach(lang ${languages})
|
||||||
|
set(PO_FILE_NAME "${CMAKE_CURRENT_SOURCE_DIR}/${lang}.po")
|
||||||
|
set(GMO_FILE_NAME "${GMO_BUILD_DIR}/${lang}.gmo")
|
||||||
|
set(PO_TARGET "generate-${MAKEVAR_DOMAIN}-${lang}-po")
|
||||||
|
set(GMO_TARGET "generate-${MAKEVAR_DOMAIN}-${lang}-gmo")
|
||||||
|
list(APPEND po_files ${PO_TARGET})
|
||||||
|
list(APPEND gmo_files ${GMO_TARGET})
|
||||||
|
|
||||||
|
if(${lang} MATCHES "en@(.*)quot")
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT ${lang}.insert-header
|
||||||
|
COMMAND
|
||||||
|
sed -e "'/^#/d'" -e 's/HEADER/${lang}.header/g'
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/insert-header.sin > ${lang}.insert-header
|
||||||
|
)
|
||||||
|
|
||||||
|
#generate the en@quot files
|
||||||
|
add_custom_command(OUTPUT ${PO_FILE_NAME}
|
||||||
|
COMMAND
|
||||||
|
${GettextTranslate_MSGINIT_EXECUTABLE} -i ${TEMPLATE_FILE_ABS}
|
||||||
|
--no-translator -l ${lang}
|
||||||
|
-o - 2>/dev/null
|
||||||
|
| sed -f ${CMAKE_CURRENT_BINARY_DIR}/${lang}.insert-header
|
||||||
|
| ${GettextTranslate_MSGCONV_EXECUTABLE} -t UTF-8
|
||||||
|
| ${GettextTranslate_MSGFILTER_EXECUTABLE} sed -f
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/`echo ${lang}
|
||||||
|
| sed -e 's/.*@//'`.sed 2>/dev/null >
|
||||||
|
${PO_FILE_NAME}
|
||||||
|
DEPENDS ${lang}.insert-header ${TEMPLATE_FILE_ABS}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
else()
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT ${PO_FILE_NAME}
|
||||||
|
COMMAND ${GettextTranslate_MSGMERGE_EXECUTABLE} --lang=${lang}
|
||||||
|
${PO_FILE_NAME} ${TEMPLATE_FILE_ABS}
|
||||||
|
-o ${PO_FILE_NAME}.new
|
||||||
|
COMMAND mv ${PO_FILE_NAME}.new ${PO_FILE_NAME}
|
||||||
|
DEPENDS ${TEMPLATE_FILE_ABS}
|
||||||
|
)
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_custom_command(OUTPUT ${GMO_FILE_NAME}
|
||||||
|
COMMAND ${GettextTranslate_MSGFMT_EXECUTABLE} -c --statistics --verbose
|
||||||
|
-o ${GMO_FILE_NAME} ${PO_FILE_NAME}
|
||||||
|
DEPENDS ${PO_TARGET}
|
||||||
|
)
|
||||||
|
add_custom_target(${GMO_TARGET} DEPENDS ${GMO_FILE_NAME})
|
||||||
|
|
||||||
|
add_custom_target(${PO_TARGET} DEPENDS ${PO_FILE_NAME})
|
||||||
|
add_dependencies(${PO_TARGET} ${MAKEVAR_DOMAIN}.pot-update)
|
||||||
|
|
||||||
|
install(FILES ${GMO_FILE_NAME} DESTINATION
|
||||||
|
${LOCALEDIR}/${lang}/LC_MESSAGES
|
||||||
|
RENAME ${MAKEVAR_DOMAIN}.mo
|
||||||
|
)
|
||||||
|
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_dependencies(update-po ${po_files})
|
||||||
|
add_dependencies(update-gmo ${gmo_files})
|
||||||
|
|
||||||
|
#string(REGEX MATCH "^[^=]+=(.*)$" parsed_variables ${makevars})
|
||||||
|
|
||||||
|
endmacro()
|
24
CMakeModules/OpenSSLTests.cmake
Normal file
24
CMakeModules/OpenSSLTests.cmake
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
|
||||||
|
include (CheckFunctionExists)
|
||||||
|
|
||||||
|
set (CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
|
||||||
|
set (CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
|
||||||
|
|
||||||
|
check_function_exists (EVP_bf_cbc HAVE_EVP_BF)
|
||||||
|
if (NOT HAVE_EVP_BF)
|
||||||
|
message (STATUS " Blowfish support disabled.")
|
||||||
|
endif (NOT HAVE_EVP_BF)
|
||||||
|
|
||||||
|
check_function_exists (EVP_aes_128_cbc HAVE_EVP_AES)
|
||||||
|
if (NOT HAVE_EVP_AES)
|
||||||
|
message (STATUS " AES support disabled.")
|
||||||
|
endif (NOT HAVE_EVP_AES)
|
||||||
|
|
||||||
|
check_function_exists (EVP_aes_128_xts HAVE_EVP_AES_XTS)
|
||||||
|
if (NOT HAVE_EVP_AES_XTS)
|
||||||
|
message (STATUS " AES/XTS support disabled.")
|
||||||
|
endif (NOT HAVE_EVP_AES_XTS)
|
||||||
|
|
||||||
|
set (CMAKE_REQUIRED_LIBRARIES)
|
||||||
|
set (CMAKE_REQUIRED_INCLUDES)
|
||||||
|
|
17
Makefile.am
17
Makefile.am
@ -1,17 +0,0 @@
|
|||||||
|
|
||||||
if BUILD_NLS
|
|
||||||
NLS_DIR = po
|
|
||||||
endif
|
|
||||||
|
|
||||||
SUBDIRS = encfs m4 $(NLS_DIR)
|
|
||||||
|
|
||||||
|
|
||||||
EXTRA_DIST = config.rpath mkinstalldirs encfs.spec makedist.sh makedist2.sh \
|
|
||||||
intl/autosprintf.h intl/autosprintf.cpp intl/gettext.h
|
|
||||||
|
|
||||||
AUTOMAKE_OPTIONS = foreign
|
|
||||||
|
|
||||||
MAINTAINERCLEANFILES = aclocal.m4
|
|
||||||
|
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
|
@ -1,2 +0,0 @@
|
|||||||
KDE_OPTIONS = qtonly
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
|||||||
default: all
|
|
||||||
|
|
||||||
all:
|
|
||||||
autoreconf -if
|
|
||||||
|
|
11
README
11
README
@ -35,12 +35,13 @@ Usage:
|
|||||||
|
|
||||||
Technology:
|
Technology:
|
||||||
|
|
||||||
- Encfs uses algorithms from third-party libraries (OpenSSL is the default) to
|
- Encfs uses algorithms from the third-party library OpenSSL to encrypt data
|
||||||
encrypt data and filenames.
|
and filenames.
|
||||||
|
|
||||||
- a user supplied password is used to decrypt a volume key, and the volume key
|
- a user supplied password is used to decrypt a randomly generated volume key,
|
||||||
is used for encrypting all file names and contents. This makes it possible
|
and the volume key is used for encrypting all file names and contents. This
|
||||||
to change the password without needing to re-encrypt all files.
|
makes it possible to change the password without needing to re-encrypt all
|
||||||
|
files.
|
||||||
|
|
||||||
- EncFS has two encryption modes, which are used in different places:
|
- EncFS has two encryption modes, which are used in different places:
|
||||||
- Stream encryption:
|
- Stream encryption:
|
||||||
|
63
README-NLS
63
README-NLS
@ -1,67 +1,6 @@
|
|||||||
|
|
||||||
Quick configuration advice
|
|
||||||
==========================
|
|
||||||
|
|
||||||
The configuration script will automatically find and make use of your installed
|
|
||||||
'gettext' package. If you do not have gettext installed, or do not want
|
|
||||||
internationalization support included in the build, then you can disable native
|
|
||||||
language support using
|
|
||||||
./configu --disable-nls
|
|
||||||
|
|
||||||
Using This Package
|
|
||||||
==================
|
|
||||||
|
|
||||||
As a user, if your language has been installed for this package, you
|
|
||||||
only have to set the `LANG' environment variable to the appropriate
|
|
||||||
`LL_CC' combination. Here `LL' is an ISO 639 two-letter language code,
|
|
||||||
and `CC' is an ISO 3166 two-letter country code. For example, let's
|
|
||||||
suppose that you speak German and live in Germany. At the shell
|
|
||||||
prompt, merely execute `setenv LANG de_DE' (in `csh'),
|
|
||||||
`export LANG; LANG=de_DE' (in `sh') or `export LANG=de_DE' (in `bash').
|
|
||||||
This can be done from your `.login' or `.profile' file, once and for
|
|
||||||
all.
|
|
||||||
|
|
||||||
You might think that the country code specification is redundant.
|
|
||||||
But in fact, some languages have dialects in different countries. For
|
|
||||||
example, `de_AT' is used for Austria, and `pt_BR' for Brazil. The
|
|
||||||
country code serves to distinguish the dialects.
|
|
||||||
|
|
||||||
The locale naming convention of `LL_CC', with `LL' denoting the
|
|
||||||
language and `CC' denoting the country, is the one use on systems based
|
|
||||||
on GNU libc. On other systems, some variations of this scheme are
|
|
||||||
used, such as `LL' or `LL_CC.ENCODING'. You can get the list of
|
|
||||||
locales supported by your system for your country by running the command
|
|
||||||
`locale -a | grep '^LL''.
|
|
||||||
|
|
||||||
Not all programs have translations for all languages. By default, an
|
|
||||||
English message is shown in place of a nonexistent translation. If you
|
|
||||||
understand other languages, you can set up a priority list of languages.
|
|
||||||
This is done through a different environment variable, called
|
|
||||||
`LANGUAGE'. GNU `gettext' gives preference to `LANGUAGE' over `LANG'
|
|
||||||
for the purpose of message handling, but you still need to have `LANG'
|
|
||||||
set to the primary language; this is required by other parts of the
|
|
||||||
system libraries. For example, some Swedish users who would rather
|
|
||||||
read translations in German than English for when Swedish is not
|
|
||||||
available, set `LANGUAGE' to `sv:de' while leaving `LANG' to `sv_SE'.
|
|
||||||
|
|
||||||
Special advice for Norwegian users: The language code for Norwegian
|
|
||||||
bokma*l changed from `no' to `nb' recently (in 2003). During the
|
|
||||||
transition period, while some message catalogs for this language are
|
|
||||||
installed under `nb' and some older ones under `no', it's recommended
|
|
||||||
for Norwegian users to set `LANGUAGE' to `nb:no' so that both newer and
|
|
||||||
older translations are used.
|
|
||||||
|
|
||||||
In the `LANGUAGE' environment variable, but not in the `LANG'
|
|
||||||
environment variable, `LL_CC' combinations can be abbreviated as `LL'
|
|
||||||
to denote the language's main dialect. For example, `de' is equivalent
|
|
||||||
to `de_DE' (German as spoken in Germany), and `pt' to `pt_PT'
|
|
||||||
(Portuguese as spoken in Portugal) in this context.
|
|
||||||
|
|
||||||
Translating
|
|
||||||
===========
|
|
||||||
|
|
||||||
EncFS is registered with Rosetta - an online interface for supplying
|
EncFS is registered with Rosetta - an online interface for supplying
|
||||||
translations. See https://launchpad.ubuntu.com/rosetta/products/encfs
|
translations. See https://translations.launchpad.net/encfs
|
||||||
|
|
||||||
If your language is not included in this distribution, you may want
|
If your language is not included in this distribution, you may want
|
||||||
to check if translated text is already available online in Rosetta.
|
to check if translated text is already available online in Rosetta.
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
Many people have contributed translations for EncFS. Thank you for making
|
|
||||||
EncFS easier for everyone to use!
|
|
||||||
|
|
||||||
If you would like to help with translations, please use the online
|
|
||||||
interface provided by Canonical Ltd (the makers of Ubuntu Linux):
|
|
||||||
https://translations.launchpad.net/encfs/main/
|
|
||||||
|
|
29
base/CMakeLists.txt
Normal file
29
base/CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
find_package (Protobuf REQUIRED)
|
||||||
|
include_directories (${PROTOBUF_INCLUDE_DIR})
|
||||||
|
|
||||||
|
find_package (TinyXML REQUIRED)
|
||||||
|
include_directories (${TINYXML_INCLUDE_DIR})
|
||||||
|
set (LIBS ${LIBS} ${TINYXML_LIBRARIES})
|
||||||
|
|
||||||
|
protobuf_generate_cpp (PROTO_SRCS PROTO_HDRS config.proto)
|
||||||
|
|
||||||
|
configure_file (${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
|
|
||||||
|
add_library (encfs-base
|
||||||
|
autosprintf.cpp
|
||||||
|
base64.cpp
|
||||||
|
ConfigReader.cpp
|
||||||
|
ConfigVar.cpp
|
||||||
|
Error.cpp
|
||||||
|
Interface.cpp
|
||||||
|
XmlReader.cpp
|
||||||
|
${PROTO_SRCS}
|
||||||
|
${PROTO_HDRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries (encfs-base
|
||||||
|
${PROTOBUF_LIBRARY}
|
||||||
|
${TINYXML_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
157
base/ConfigReader.cpp
Normal file
157
base/ConfigReader.cpp
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2004-2013, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "base/ConfigReader.h"
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
int size = stbuf.st_size;
|
||||||
|
|
||||||
|
int fd = open( fileName, O_RDONLY );
|
||||||
|
if(fd < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char *buf = new char[size];
|
||||||
|
|
||||||
|
int res = ::read( fd, buf, size );
|
||||||
|
close( fd );
|
||||||
|
|
||||||
|
if( res != size )
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "Partial read of config file, expecting "
|
||||||
|
<< size << " bytes, got " << res;
|
||||||
|
delete[] buf;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigVar in;
|
||||||
|
in.write( (unsigned char *)buf, size );
|
||||||
|
delete[] buf;
|
||||||
|
|
||||||
|
return loadFromVar( in );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConfigReader::loadFromVar(ConfigVar &in)
|
||||||
|
{
|
||||||
|
in.resetOffset();
|
||||||
|
|
||||||
|
// parse.
|
||||||
|
int numEntries = in.readInt();
|
||||||
|
|
||||||
|
for(int i=0; i<numEntries; ++i)
|
||||||
|
{
|
||||||
|
string key, value;
|
||||||
|
in >> key >> value;
|
||||||
|
|
||||||
|
if(key.length() == 0)
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "Invalid key encoding in buffer";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ConfigVar newVar( value );
|
||||||
|
vars.insert( make_pair( key, newVar ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "Error writing to config file " << fileName;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "Unable to open or create file " << fileName;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigVar ConfigReader::toVar() const
|
||||||
|
{
|
||||||
|
// write everything to a ConfigVar, then output to disk
|
||||||
|
ConfigVar out;
|
||||||
|
out.writeInt( vars.size() );
|
||||||
|
map<string, ConfigVar>::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigVar ConfigReader::operator[] ( const std::string &varName ) const
|
||||||
|
{
|
||||||
|
// read only
|
||||||
|
map<string, ConfigVar>::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 ];
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "ConfigVar.h"
|
#include "base/ConfigVar.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
handles Configuration load / store for Encfs filesystems.
|
handles Configuration load / store for Encfs filesystems.
|
253
base/ConfigVar.cpp
Normal file
253
base/ConfigVar.cpp
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2004, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "base/ConfigVar.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#ifndef MIN
|
||||||
|
inline int MIN(int a, int b)
|
||||||
|
{
|
||||||
|
return (a < b) ? a : b;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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::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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
pd->offset += bytes;
|
||||||
|
|
||||||
|
return 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];
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
rAssert( offset < bytes );
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
unsigned char tmp = buf[offset++];
|
||||||
|
highBitSet = tmp & 0x80;
|
||||||
|
|
||||||
|
value = (value << 7) | (int)(tmp & 0x7f);
|
||||||
|
} while(highBitSet && offset < bytes);
|
||||||
|
|
||||||
|
pd->offset = offset;
|
||||||
|
|
||||||
|
// should never end up with a negative number..
|
||||||
|
rAssert( value >= 0 );
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConfigVar::readInt( int defaultValue ) const
|
||||||
|
{
|
||||||
|
int bytes = this->size();
|
||||||
|
int offset = at();
|
||||||
|
|
||||||
|
if(offset >= bytes)
|
||||||
|
return defaultValue;
|
||||||
|
else
|
||||||
|
return readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
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, int var)
|
||||||
|
{
|
||||||
|
src.writeInt( var );
|
||||||
|
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, int &result)
|
||||||
|
{
|
||||||
|
result = src.readInt();
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfigVar & operator >> (const ConfigVar &src, std::string &result)
|
||||||
|
{
|
||||||
|
int length = src.readInt();
|
||||||
|
LOG_IF(WARNING, length <= 0) << "Invalid config length " << length;
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if(readLen != length)
|
||||||
|
{
|
||||||
|
VLOG(1) << "string encoded as size " << length
|
||||||
|
<< " bytes, read " << readLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
rAssert(readLen == length);
|
||||||
|
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
@ -22,7 +22,7 @@
|
|||||||
#define _ConfigVar_incl_
|
#define _ConfigVar_incl_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "shared_ptr.h"
|
#include "base/shared_ptr.h"
|
||||||
|
|
||||||
class ConfigVar
|
class ConfigVar
|
||||||
{
|
{
|
7
base/Error.cpp
Normal file
7
base/Error.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "base/Error.h"
|
||||||
|
|
||||||
|
Error::Error(const char *msg)
|
||||||
|
: runtime_error(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
25
base/Error.h
Normal file
25
base/Error.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef _Error_incl_
|
||||||
|
#define _Error_incl_
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
class Error : public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Error(const char *msg);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define STR(X) #X
|
||||||
|
|
||||||
|
#define rAssert( cond ) \
|
||||||
|
do { \
|
||||||
|
if( (cond) == false) \
|
||||||
|
{ LOG(ERROR) << "Assert failed: " << STR(cond); \
|
||||||
|
throw Error(STR(cond)); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -2,7 +2,7 @@
|
|||||||
* Author: Valient Gough <vgough@pobox.com>
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
*
|
*
|
||||||
*****************************************************************************
|
*****************************************************************************
|
||||||
* Copyright (c) 2004, Valient Gough
|
* Copyright (c) 2004-2013, 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 Lesser General Public License as published by the
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
@ -18,22 +18,23 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Interface.h"
|
#include "base/Interface.h"
|
||||||
|
|
||||||
#include "ConfigVar.h"
|
#include "base/ConfigVar.h"
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
#include <glog/logging.h>
|
||||||
#include <rlog/RLogChannel.h>
|
#include <ostream>
|
||||||
|
|
||||||
using namespace rlog;
|
std::ostream& operator << (std::ostream& out, const Interface &iface)
|
||||||
|
{
|
||||||
static RLogChannel * Info = DEF_CHANNEL( "info/iface", Log_Info );
|
out << iface.name() << "(" << iface.major()
|
||||||
|
<< ":" << iface.minor() << ":" << iface.age() << ")";
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
bool implements(const Interface &A, const Interface &B)
|
bool implements(const Interface &A, const Interface &B)
|
||||||
{
|
{
|
||||||
rLog(Info, "checking if %s(%i:%i:%i) implements %s(%i:%i:%i)",
|
VLOG(1) << "checking if " << A << " implements " << B;
|
||||||
A.name().c_str(), A.major(), A.minor(), A.age(),
|
|
||||||
B.name().c_str(), B.major(), B.minor(), B.age());
|
|
||||||
|
|
||||||
if( A.name() != B.name() )
|
if( A.name() != B.name() )
|
||||||
return false;
|
return false;
|
||||||
@ -54,7 +55,8 @@ Interface makeInterface(const char *name, int major, int minor, int age)
|
|||||||
|
|
||||||
ConfigVar & operator << (ConfigVar &dst, const Interface &iface)
|
ConfigVar & operator << (ConfigVar &dst, const Interface &iface)
|
||||||
{
|
{
|
||||||
dst << iface.name() << (int)iface.major() << (int)iface.minor() << (int)iface.age();
|
dst << iface.name() << (int)iface.major() << (int)iface.minor()
|
||||||
|
<< (int)iface.age();
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@
|
|||||||
#define _Interface_incl_
|
#define _Interface_incl_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "config.pb.h"
|
#include "base/config.pb.h"
|
||||||
|
|
||||||
// check if A implements the interface described by B.
|
// check if A implements the interface described by B.
|
||||||
// Note that implements(A, B) is not the same as implements(B, A)
|
// Note that implements(A, B) is not the same as implements(B, A)
|
||||||
@ -32,7 +32,7 @@
|
|||||||
bool implements( const Interface &a, const Interface &b );
|
bool implements( const Interface &a, const Interface &b );
|
||||||
Interface makeInterface( const char *name, int major, int minor, int age );
|
Interface makeInterface( const char *name, int major, int minor, int age );
|
||||||
|
|
||||||
// Reae operation
|
// Read operation
|
||||||
class ConfigVar;
|
class ConfigVar;
|
||||||
const ConfigVar & operator >> (const ConfigVar &, Interface &);
|
const ConfigVar & operator >> (const ConfigVar &, Interface &);
|
||||||
|
|
@ -26,40 +26,41 @@
|
|||||||
namespace rel
|
namespace rel
|
||||||
{
|
{
|
||||||
|
|
||||||
class Lock
|
class Lock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Lock( pthread_mutex_t &mutex );
|
Lock( pthread_mutex_t &mutex );
|
||||||
~Lock();
|
~Lock();
|
||||||
|
|
||||||
// leave the lock as it is. When the Lock wrapper is destroyed, it
|
// leave the lock as it is. When the Lock wrapper is destroyed, it
|
||||||
// will do nothing with the pthread mutex.
|
// will do nothing with the pthread mutex.
|
||||||
void leave();
|
void leave();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Lock(const Lock &src); // not allowed
|
Lock(const Lock &src); // not allowed
|
||||||
Lock &operator = (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 )
|
inline Lock::Lock( pthread_mutex_t &mutex )
|
||||||
: _mutex( &mutex )
|
: _mutex( &mutex )
|
||||||
{
|
{
|
||||||
pthread_mutex_lock( _mutex );
|
pthread_mutex_lock( _mutex );
|
||||||
}
|
|
||||||
|
|
||||||
inline Lock::~Lock( )
|
|
||||||
{
|
|
||||||
if(_mutex)
|
|
||||||
pthread_mutex_unlock( _mutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Lock::leave()
|
|
||||||
{
|
|
||||||
_mutex = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline Lock::~Lock( )
|
||||||
|
{
|
||||||
|
if(_mutex)
|
||||||
|
pthread_mutex_unlock( _mutex );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Lock::leave()
|
||||||
|
{
|
||||||
|
_mutex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace rel
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -66,6 +66,8 @@ inline Range::Range()
|
|||||||
|
|
||||||
inline bool Range::allowed(int value) const
|
inline bool Range::allowed(int value) const
|
||||||
{
|
{
|
||||||
|
if(minVal < 0 && maxVal < 0)
|
||||||
|
return true;
|
||||||
if(value >= minVal && value <= maxVal)
|
if(value >= minVal && value <= maxVal)
|
||||||
{
|
{
|
||||||
int tmp = value - minVal;
|
int tmp = value - minVal;
|
@ -2,7 +2,7 @@
|
|||||||
* Author: Valient Gough <vgough@pobox.com>
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
*
|
*
|
||||||
*****************************************************************************
|
*****************************************************************************
|
||||||
* Copyright (c) 2012, Valient Gough
|
* Copyright (c) 2012-2013, 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 Lesser General Public License as published by the
|
* under the terms of the GNU Lesser General Public License as published by the
|
||||||
@ -18,7 +18,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "XmlReader.h"
|
#include "base/XmlReader.h"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -35,12 +35,11 @@
|
|||||||
#include <openssl/bio.h>
|
#include <openssl/bio.h>
|
||||||
#include <openssl/buffer.h>
|
#include <openssl/buffer.h>
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
#include <glog/logging.h>
|
||||||
#include "base64.h"
|
#include "base/base64.h"
|
||||||
#include "Interface.h"
|
#include "base/Interface.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
XmlValue::~XmlValue()
|
XmlValue::~XmlValue()
|
||||||
{
|
{
|
||||||
@ -53,7 +52,7 @@ XmlValuePtr XmlValue::operator[] (const char *path) const
|
|||||||
|
|
||||||
XmlValuePtr XmlValue::find(const char *path) const
|
XmlValuePtr XmlValue::find(const char *path) const
|
||||||
{
|
{
|
||||||
rError("in XmlValue::find(%s)", path);
|
LOG_FIRST_N(ERROR, 1) << "in XmlValue::find( " << path << ")";
|
||||||
return XmlValuePtr();
|
return XmlValuePtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,8 +126,9 @@ bool XmlValue::readB64(const char *path, unsigned char *data, int length) const
|
|||||||
|
|
||||||
if (decodedSize != length)
|
if (decodedSize != length)
|
||||||
{
|
{
|
||||||
rError("decoding bytes len %i, expecting output len %i, got %i",
|
LOG(ERROR) << "decoding bytes len " << s.size()
|
||||||
(int)s.size(), length, decodedSize);
|
<< ", expecting output len " << length
|
||||||
|
<< ", got " << decodedSize;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,14 +231,15 @@ XmlValuePtr XmlReader::operator[] ( const char *name ) const
|
|||||||
TiXmlNode *node = pd->doc->FirstChild(name);
|
TiXmlNode *node = pd->doc->FirstChild(name);
|
||||||
if (node == NULL)
|
if (node == NULL)
|
||||||
{
|
{
|
||||||
rError("Xml node %s not found", name);
|
LOG(ERROR) << "Xml node " << name << " not found";
|
||||||
return XmlValuePtr(new XmlValue());
|
return XmlValuePtr(new XmlValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
TiXmlElement *element = node->ToElement();
|
TiXmlElement *element = node->ToElement();
|
||||||
if (element == NULL)
|
if (element == NULL)
|
||||||
{
|
{
|
||||||
rError("Xml node %s not element, type = %i", name, node->Type());
|
LOG(ERROR) << "Xml node " << name
|
||||||
|
<< " not element, type = " << node->Type();
|
||||||
return XmlValuePtr(new XmlValue());
|
return XmlValuePtr(new XmlValue());
|
||||||
}
|
}
|
||||||
|
|
@ -22,7 +22,7 @@
|
|||||||
#define _XmlReader_incl_
|
#define _XmlReader_incl_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "shared_ptr.h"
|
#include "base/shared_ptr.h"
|
||||||
|
|
||||||
class XmlValue;
|
class XmlValue;
|
||||||
typedef shared_ptr<XmlValue> XmlValuePtr;
|
typedef shared_ptr<XmlValue> XmlValuePtr;
|
@ -25,7 +25,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Specification. */
|
/* Specification. */
|
||||||
#include "autosprintf.h"
|
#include "base/autosprintf.h"
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
@ -18,7 +18,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "base64.h"
|
#include "base/base64.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
19
base/config.h.cmake
Normal file
19
base/config.h.cmake
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#cmakedefine HAVE_ATTR_XATTR_H
|
||||||
|
#cmakedefine HAVE_SYS_XATTR_H
|
||||||
|
#cmakedefine XATTR_ADD_OPT
|
||||||
|
#cmakedefine HAVE_COMMON_CRYPTO
|
||||||
|
|
||||||
|
#cmakedefine HAVE_TR1_MEMORY
|
||||||
|
#cmakedefine HAVE_TR1_UNORDERED_MAP
|
||||||
|
#cmakedefine HAVE_TR1_UNORDERED_SET
|
||||||
|
#cmakedefine HAVE_TR1_TUPLE
|
||||||
|
|
||||||
|
#cmakedefine HAVE_EVP_BF
|
||||||
|
#cmakedefine HAVE_EVP_AES
|
||||||
|
#cmakedefine HAVE_EVP_AES_XTS
|
||||||
|
|
||||||
|
#cmakedefine HAVE_VALGRIND_VALGRIND_H
|
||||||
|
#cmakedefine HAVE_VALGRIND_MEMCHECK_H
|
||||||
|
|
||||||
|
#define VERSION "@ENCFS_VERSION@"
|
||||||
|
|
@ -2,9 +2,13 @@
|
|||||||
message EncfsConfig
|
message EncfsConfig
|
||||||
{
|
{
|
||||||
optional string creator = 1;
|
optional string creator = 1;
|
||||||
|
optional string writer = 11;
|
||||||
optional int32 revision = 2 [default=0];
|
optional int32 revision = 2 [default=0];
|
||||||
|
|
||||||
required Interface cipher = 3;
|
required Interface cipher = 3;
|
||||||
|
// added for FileIO/Cipher 3.0 (encfs 1.8)
|
||||||
|
// Use only block encryption, no stream encryption.
|
||||||
|
optional bool block_mode_only = 31;
|
||||||
required EncryptedKey key = 4;
|
required EncryptedKey key = 4;
|
||||||
|
|
||||||
optional Interface naming = 5;
|
optional Interface naming = 5;
|
||||||
@ -16,6 +20,7 @@ message EncfsConfig
|
|||||||
optional int32 block_mac_bytes = 61 [default=0];
|
optional int32 block_mac_bytes = 61 [default=0];
|
||||||
optional int32 block_mac_rand_bytes = 611 [default=0];
|
optional int32 block_mac_rand_bytes = 611 [default=0];
|
||||||
optional bool allow_holes = 62 [default = false];
|
optional bool allow_holes = 62 [default = false];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message EncryptedKey
|
message EncryptedKey
|
@ -23,11 +23,11 @@
|
|||||||
|
|
||||||
#if defined(LOCALEDIR)
|
#if defined(LOCALEDIR)
|
||||||
|
|
||||||
# include "gettext.h"
|
# include "base/gettext.h"
|
||||||
// make shortcut for gettext
|
// make shortcut for gettext
|
||||||
# define _(STR) gettext (STR)
|
# define _(STR) gettext (STR)
|
||||||
|
|
||||||
# include "autosprintf.h"
|
# include "base/autosprintf.h"
|
||||||
using gnu::autosprintf;
|
using gnu::autosprintf;
|
||||||
|
|
||||||
#else
|
#else
|
@ -22,7 +22,7 @@
|
|||||||
#ifndef _SHARED_PTR_incl_
|
#ifndef _SHARED_PTR_incl_
|
||||||
#define _SHARED_PTR_incl_
|
#define _SHARED_PTR_incl_
|
||||||
|
|
||||||
#include "config.h"
|
#include "base/config.h"
|
||||||
|
|
||||||
#ifdef HAVE_TR1_MEMORY
|
#ifdef HAVE_TR1_MEMORY
|
||||||
#include <tr1/memory>
|
#include <tr1/memory>
|
41
cipher/CMakeLists.txt
Normal file
41
cipher/CMakeLists.txt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
include_directories (${OPENSSL_INCLUDE_DIR})
|
||||||
|
|
||||||
|
link_directories (${Encfs_BINARY_DIR}/base)
|
||||||
|
|
||||||
|
enable_testing ()
|
||||||
|
find_package (GTest REQUIRED)
|
||||||
|
|
||||||
|
add_library (encfs-cipher
|
||||||
|
readpassphrase.cpp
|
||||||
|
Cipher.cpp
|
||||||
|
CipherKey.cpp
|
||||||
|
MemoryPool.cpp
|
||||||
|
NullCipher.cpp
|
||||||
|
openssl.cpp
|
||||||
|
SSL_Cipher.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries (encfs-cipher
|
||||||
|
${OPENSSL_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
#include_directories (${GTEST_INCLUDE_DIR})
|
||||||
|
#add_executable (unittests
|
||||||
|
#MemBlockFileIO.cpp
|
||||||
|
#MemFileIO.cpp
|
||||||
|
#testing.cpp
|
||||||
|
#test_IO.cpp
|
||||||
|
#test_BlockIO.cpp
|
||||||
|
#)
|
||||||
|
|
||||||
|
#target_link_libraries (unittests
|
||||||
|
#${GTEST_BOTH_LIBRARIES}
|
||||||
|
#encfs-fs
|
||||||
|
#encfs-base
|
||||||
|
#${GLOG_LIBRARIES}
|
||||||
|
#)
|
||||||
|
|
||||||
|
#add_test (UnitTests unittests)
|
||||||
|
#GTEST_ADD_TESTS (unittests "${UnitTestArgs}" test_IO.cpp test_BlockIO.cpp)
|
||||||
|
#add_custom_target (test COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unittests)
|
||||||
|
|
@ -18,12 +18,12 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "base/config.h"
|
||||||
|
#include "cipher/Cipher.h"
|
||||||
|
|
||||||
#include "Cipher.h"
|
#include "base/Interface.h"
|
||||||
#include "Interface.h"
|
#include "base/Range.h"
|
||||||
#include "Range.h"
|
#include "base/base64.h"
|
||||||
#include "base64.h"
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <list>
|
#include <list>
|
||||||
@ -57,6 +57,7 @@ struct CipherAlg
|
|||||||
Interface iface;
|
Interface iface;
|
||||||
Range keyLength;
|
Range keyLength;
|
||||||
Range blockSize;
|
Range blockSize;
|
||||||
|
bool hasStreamMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef multimap< string, CipherAlg> CipherMap_t;
|
typedef multimap< string, CipherAlg> CipherMap_t;
|
||||||
@ -84,6 +85,7 @@ Cipher::GetAlgorithmList( bool includeHidden )
|
|||||||
tmp.iface = it->second.iface;
|
tmp.iface = it->second.iface;
|
||||||
tmp.keyLength = it->second.keyLength;
|
tmp.keyLength = it->second.keyLength;
|
||||||
tmp.blockSize = it->second.blockSize;
|
tmp.blockSize = it->second.blockSize;
|
||||||
|
tmp.hasStreamMode = it->second.hasStreamMode;
|
||||||
|
|
||||||
result.push_back( tmp );
|
result.push_back( tmp );
|
||||||
}
|
}
|
||||||
@ -93,18 +95,21 @@ Cipher::GetAlgorithmList( bool includeHidden )
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Cipher::Register(const char *name, const char *description,
|
bool Cipher::Register(const char *name, const char *description,
|
||||||
const Interface &iface, CipherConstructor fn, bool hidden)
|
const Interface &iface, CipherConstructor fn,
|
||||||
|
bool hasStreamMode, bool hidden)
|
||||||
{
|
{
|
||||||
Range keyLength(-1,-1,1);
|
Range keyLength(-1,-1,1);
|
||||||
Range blockSize(-1,-1,1);
|
Range blockSize(-1,-1,1);
|
||||||
return Cipher::Register( name, description, iface,
|
return Cipher::Register( name, description, iface,
|
||||||
keyLength, blockSize, fn, hidden );
|
keyLength, blockSize, fn, hasStreamMode, hidden );
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Cipher::Register(const char *name, const char *description,
|
bool Cipher::Register(const char *name, const char *description,
|
||||||
const Interface &iface, const Range &keyLength,
|
const Interface &iface, const Range &keyLength,
|
||||||
const Range &blockSize,
|
const Range &blockSize,
|
||||||
CipherConstructor fn, bool hidden)
|
CipherConstructor fn,
|
||||||
|
bool hasStreamMode,
|
||||||
|
bool hidden)
|
||||||
{
|
{
|
||||||
if(!gCipherMap)
|
if(!gCipherMap)
|
||||||
gCipherMap = new CipherMap_t;
|
gCipherMap = new CipherMap_t;
|
||||||
@ -116,6 +121,7 @@ bool Cipher::Register(const char *name, const char *description,
|
|||||||
ca.iface = iface;
|
ca.iface = iface;
|
||||||
ca.keyLength = keyLength;
|
ca.keyLength = keyLength;
|
||||||
ca.blockSize = blockSize;
|
ca.blockSize = blockSize;
|
||||||
|
ca.hasStreamMode = hasStreamMode;
|
||||||
|
|
||||||
gCipherMap->insert( make_pair(string(name), ca) );
|
gCipherMap->insert( make_pair(string(name), ca) );
|
||||||
return true;
|
return true;
|
||||||
@ -195,26 +201,13 @@ unsigned int Cipher::MAC_16( const unsigned char *src, int len,
|
|||||||
return mac16;
|
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
string Cipher::encodeAsString(const CipherKey &key,
|
string Cipher::encodeAsString(const CipherKey &key,
|
||||||
const CipherKey &encodingKey )
|
const CipherKey &encodingKey )
|
||||||
{
|
{
|
||||||
int encodedKeySize = this->encodedKeySize();
|
int encodedKeySize = this->encodedKeySize();
|
||||||
unsigned char *keyBuf = new unsigned char[ encodedKeySize ];
|
unsigned char *keyBuf = new unsigned char[ encodedKeySize ];
|
||||||
|
|
||||||
// write the key, encoding it with itself.
|
this->writeKey( key, keyBuf, encodingKey );
|
||||||
this->writeKey( key, keyBuf, key );
|
|
||||||
|
|
||||||
int b64Len = B256ToB64Bytes( encodedKeySize );
|
int b64Len = B256ToB64Bytes( encodedKeySize );
|
||||||
unsigned char *b64Key = new unsigned char[ b64Len + 1 ];
|
unsigned char *b64Key = new unsigned char[ b64Len + 1 ];
|
||||||
@ -226,3 +219,9 @@ string Cipher::encodeAsString(const CipherKey &key,
|
|||||||
|
|
||||||
return string( (const char *)b64Key );
|
return string( (const char *)b64Key );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Cipher::hasStreamMode() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -21,11 +21,9 @@
|
|||||||
#ifndef _Cipher_incl_
|
#ifndef _Cipher_incl_
|
||||||
#define _Cipher_incl_
|
#define _Cipher_incl_
|
||||||
|
|
||||||
#include "encfs.h"
|
#include "cipher/CipherKey.h"
|
||||||
|
#include "base/Interface.h"
|
||||||
#include "Range.h"
|
#include "base/Range.h"
|
||||||
#include "Interface.h"
|
|
||||||
#include "CipherKey.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <list>
|
#include <list>
|
||||||
@ -52,6 +50,7 @@ public:
|
|||||||
Interface iface;
|
Interface iface;
|
||||||
Range keyLength;
|
Range keyLength;
|
||||||
Range blockSize;
|
Range blockSize;
|
||||||
|
bool hasStreamMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -60,15 +59,16 @@ public:
|
|||||||
|
|
||||||
|
|
||||||
static shared_ptr<Cipher> New( const Interface &iface,
|
static shared_ptr<Cipher> New( const Interface &iface,
|
||||||
int keyLen = -1);
|
int keyLen = -1);
|
||||||
static shared_ptr<Cipher> New( const std::string &cipherName,
|
static shared_ptr<Cipher> New( const std::string &cipherName,
|
||||||
int keyLen = -1 );
|
int keyLen = -1 );
|
||||||
|
|
||||||
|
|
||||||
static bool Register(const char *cipherName,
|
static bool Register(const char *cipherName,
|
||||||
const char *description,
|
const char *description,
|
||||||
const Interface &iface,
|
const Interface &iface,
|
||||||
CipherConstructor constructor,
|
CipherConstructor constructor,
|
||||||
|
bool hasStreamMode,
|
||||||
bool hidden = false);
|
bool hidden = false);
|
||||||
|
|
||||||
static bool Register(const char *cipherName,
|
static bool Register(const char *cipherName,
|
||||||
@ -76,6 +76,7 @@ public:
|
|||||||
const Interface &iface,
|
const Interface &iface,
|
||||||
const Range &keyLength, const Range &blockSize,
|
const Range &keyLength, const Range &blockSize,
|
||||||
CipherConstructor constructor,
|
CipherConstructor constructor,
|
||||||
|
bool hasStreamMode,
|
||||||
bool hidden = false);
|
bool hidden = false);
|
||||||
|
|
||||||
Cipher();
|
Cipher();
|
||||||
@ -117,6 +118,8 @@ public:
|
|||||||
virtual int encodedKeySize() const=0; // size
|
virtual int encodedKeySize() const=0; // size
|
||||||
virtual int cipherBlockSize() const=0; // size of a cipher block
|
virtual int cipherBlockSize() const=0; // size of a cipher block
|
||||||
|
|
||||||
|
virtual bool hasStreamMode() const;
|
||||||
|
|
||||||
// fill the supplied buffer with random data
|
// fill the supplied buffer with random data
|
||||||
// The data may be pseudo random and might not be suitable for key
|
// The data may be pseudo random and might not be suitable for key
|
||||||
// generation. For generating keys, uses newRandomKey() instead.
|
// generation. For generating keys, uses newRandomKey() instead.
|
||||||
@ -143,17 +146,6 @@ public:
|
|||||||
virtual bool streamDecode( unsigned char *data, int len,
|
virtual bool streamDecode( unsigned char *data, int len,
|
||||||
uint64_t iv64, const CipherKey &key) const=0;
|
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
|
Block encoding of data in-place. The data size should be a multiple of
|
||||||
the cipher block size.
|
the cipher block size.
|
@ -18,7 +18,7 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CipherKey.h"
|
#include "cipher/CipherKey.h"
|
||||||
|
|
||||||
AbstractCipherKey::AbstractCipherKey()
|
AbstractCipherKey::AbstractCipherKey()
|
||||||
{
|
{
|
@ -21,7 +21,7 @@
|
|||||||
#ifndef _CipherKey_incl_
|
#ifndef _CipherKey_incl_
|
||||||
#define _CipherKey_incl_
|
#define _CipherKey_incl_
|
||||||
|
|
||||||
#include "shared_ptr.h"
|
#include "base/shared_ptr.h"
|
||||||
|
|
||||||
class AbstractCipherKey
|
class AbstractCipherKey
|
||||||
{
|
{
|
164
cipher/MemoryPool.cpp
Normal file
164
cipher/MemoryPool.cpp
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2003-2013, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cipher/MemoryPool.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "base/config.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
||||||
|
#include <valgrind/memcheck.h>
|
||||||
|
#else
|
||||||
|
#define VALGRIND_MAKE_MEM_NOACCESS( a, b )
|
||||||
|
#define VALGRIND_MAKE_MEM_UNDEFINED( a, b )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
# include <openssl/crypto.h>
|
||||||
|
# include <openssl/buffer.h>
|
||||||
|
|
||||||
|
static BUF_MEM *allocBlock( int size )
|
||||||
|
{
|
||||||
|
BUF_MEM *block = BUF_MEM_new( );
|
||||||
|
BUF_MEM_grow( block, size );
|
||||||
|
VALGRIND_MAKE_MEM_NOACCESS( block->data, block->max );
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeBlock( BUF_MEM *block )
|
||||||
|
{
|
||||||
|
VALGRIND_MAKE_MEM_UNDEFINED( block->data, block->max );
|
||||||
|
BUF_MEM_free( block );
|
||||||
|
}
|
||||||
|
|
||||||
|
static pthread_mutex_t gMPoolMutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
typedef std::map<int, std::list<BUF_MEM* > > FreeBlockMap;
|
||||||
|
static FreeBlockMap gFreeBlocks;
|
||||||
|
|
||||||
|
void MemBlock::allocate(int size)
|
||||||
|
{
|
||||||
|
rAssert(size > 0);
|
||||||
|
pthread_mutex_lock( &gMPoolMutex );
|
||||||
|
|
||||||
|
list<BUF_MEM*> &freeList = gFreeBlocks[size];
|
||||||
|
BUF_MEM *mem;
|
||||||
|
|
||||||
|
if (!freeList.empty())
|
||||||
|
{
|
||||||
|
mem = freeList.front();
|
||||||
|
freeList.pop_front();
|
||||||
|
pthread_mutex_unlock( &gMPoolMutex );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock( &gMPoolMutex );
|
||||||
|
mem = allocBlock( size );
|
||||||
|
}
|
||||||
|
|
||||||
|
internalData = mem;
|
||||||
|
data = reinterpret_cast<unsigned char *>(mem->data);
|
||||||
|
VALGRIND_MAKE_MEM_UNDEFINED( data, size );
|
||||||
|
}
|
||||||
|
|
||||||
|
MemBlock::~MemBlock()
|
||||||
|
{
|
||||||
|
BUF_MEM *block = (BUF_MEM*)internalData;
|
||||||
|
data = NULL;
|
||||||
|
internalData = NULL;
|
||||||
|
|
||||||
|
if (block)
|
||||||
|
{
|
||||||
|
// wipe the buffer..
|
||||||
|
VALGRIND_MAKE_MEM_UNDEFINED( block->data, block->max );
|
||||||
|
memset( block->data , 0, block->max);
|
||||||
|
VALGRIND_MAKE_MEM_NOACCESS( block->data, block->max );
|
||||||
|
|
||||||
|
pthread_mutex_lock( &gMPoolMutex );
|
||||||
|
gFreeBlocks[ block->max ].push_front(block);
|
||||||
|
pthread_mutex_unlock( &gMPoolMutex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MemoryPool::destroyAll()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock( &gMPoolMutex );
|
||||||
|
|
||||||
|
for (FreeBlockMap::const_iterator it = gFreeBlocks.begin();
|
||||||
|
it != gFreeBlocks.end(); it++)
|
||||||
|
{
|
||||||
|
for (list<BUF_MEM*>::const_iterator bIt = it->second.begin();
|
||||||
|
bIt != it->second.end(); bIt++)
|
||||||
|
{
|
||||||
|
freeBlock( *bIt );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gFreeBlocks.clear();
|
||||||
|
|
||||||
|
pthread_mutex_unlock( &gMPoolMutex );
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureMem::SecureMem(int len)
|
||||||
|
{
|
||||||
|
rAssert(len > 0);
|
||||||
|
data = (char *)OPENSSL_malloc(len);
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
size = len;
|
||||||
|
mlock(data, size);
|
||||||
|
memset(data, '\0', size);
|
||||||
|
VALGRIND_MAKE_MEM_UNDEFINED( data, size );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SecureMem::~SecureMem()
|
||||||
|
{
|
||||||
|
if (size)
|
||||||
|
{
|
||||||
|
memset(data, '\0', size);
|
||||||
|
OPENSSL_cleanse(data, size);
|
||||||
|
|
||||||
|
munlock(data, size);
|
||||||
|
OPENSSL_free(data);
|
||||||
|
VALGRIND_MAKE_MEM_NOACCESS( data, size );
|
||||||
|
|
||||||
|
data = NULL;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -21,14 +21,25 @@
|
|||||||
#ifndef _MemoryPool_incl_
|
#ifndef _MemoryPool_incl_
|
||||||
#define _MemoryPool_incl_
|
#define _MemoryPool_incl_
|
||||||
|
|
||||||
|
/*
|
||||||
|
Memory Pool for fixed sized objects.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
MemBlock mb( size );
|
||||||
|
// do things with storage in mb.data
|
||||||
|
unsigned char *buffer = mb.data;
|
||||||
|
|
||||||
|
// memblock freed when destructed
|
||||||
|
*/
|
||||||
struct MemBlock
|
struct MemBlock
|
||||||
{
|
{
|
||||||
unsigned char *data;
|
unsigned char *data;
|
||||||
|
|
||||||
void *internalData;
|
void *internalData;
|
||||||
|
|
||||||
MemBlock();
|
MemBlock();
|
||||||
|
~MemBlock();
|
||||||
|
|
||||||
|
void allocate(int size);
|
||||||
};
|
};
|
||||||
|
|
||||||
inline MemBlock::MemBlock()
|
inline MemBlock::MemBlock()
|
||||||
@ -36,19 +47,8 @@ inline MemBlock::MemBlock()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Memory Pool for fixed sized objects.
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
MemBlock mb = MemoryPool::allocate( size );
|
|
||||||
// do things with storage in mb.data
|
|
||||||
unsigned char *buffer = mb.data;
|
|
||||||
MemoryPool::release( mb );
|
|
||||||
*/
|
|
||||||
namespace MemoryPool
|
namespace MemoryPool
|
||||||
{
|
{
|
||||||
MemBlock allocate( int size );
|
|
||||||
void release( const MemBlock &el );
|
|
||||||
void destroyAll();
|
void destroyAll();
|
||||||
}
|
}
|
||||||
|
|
@ -18,18 +18,15 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "NullCipher.h"
|
#include "cipher/NullCipher.h"
|
||||||
|
|
||||||
#include "Range.h"
|
#include "base/Range.h"
|
||||||
#include "Interface.h"
|
#include "base/Interface.h"
|
||||||
#include "shared_ptr.h"
|
#include "base/shared_ptr.h"
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
|
|
||||||
static Interface NullInterface = makeInterface( "nullCipher", 1, 0, 0 );
|
static Interface NullInterface = makeInterface( "nullCipher", 1, 0, 0 );
|
||||||
@ -38,40 +35,40 @@ static Range NullBlockRange(1,4096,1);
|
|||||||
|
|
||||||
static shared_ptr<Cipher> NewNullCipher(const Interface &iface, int keyLen)
|
static shared_ptr<Cipher> NewNullCipher(const Interface &iface, int keyLen)
|
||||||
{
|
{
|
||||||
(void)keyLen;
|
(void)keyLen;
|
||||||
return shared_ptr<Cipher>( new NullCipher( iface ) );
|
return shared_ptr<Cipher>( new NullCipher( iface ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool HiddenCipher = true;
|
const bool HiddenCipher = true;
|
||||||
|
|
||||||
static bool NullCipher_registered = Cipher::Register("Null",
|
static bool NullCipher_registered = Cipher::Register("Null",
|
||||||
"Non encrypting cipher. For testing only!",
|
"Non encrypting cipher. For testing only!",
|
||||||
NullInterface, NullKeyRange, NullBlockRange, NewNullCipher,
|
NullInterface, NullKeyRange, NullBlockRange, NewNullCipher,
|
||||||
HiddenCipher);
|
HiddenCipher);
|
||||||
|
|
||||||
class NullKey : public AbstractCipherKey
|
class NullKey : public AbstractCipherKey
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NullKey() {}
|
NullKey() {}
|
||||||
virtual ~NullKey() {}
|
virtual ~NullKey() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class NullDestructor
|
class NullDestructor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NullDestructor() {}
|
NullDestructor() {}
|
||||||
NullDestructor(const NullDestructor &) {}
|
NullDestructor(const NullDestructor &) {}
|
||||||
~NullDestructor() {}
|
~NullDestructor() {}
|
||||||
|
|
||||||
NullDestructor &operator = (const NullDestructor &){ return *this; }
|
NullDestructor &operator = (const NullDestructor &){ return *this; }
|
||||||
void operator ()(NullKey *&) {}
|
void operator ()(NullKey *&) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
shared_ptr<AbstractCipherKey> gNullKey( new NullKey(), NullDestructor() );
|
shared_ptr<AbstractCipherKey> gNullKey( new NullKey(), NullDestructor() );
|
||||||
|
|
||||||
NullCipher::NullCipher(const Interface &iface_)
|
NullCipher::NullCipher(const Interface &iface_)
|
||||||
{
|
{
|
||||||
this->iface = iface_;
|
this->iface = iface_;
|
||||||
}
|
}
|
||||||
|
|
||||||
NullCipher::~NullCipher()
|
NullCipher::~NullCipher()
|
||||||
@ -80,105 +77,105 @@ NullCipher::~NullCipher()
|
|||||||
|
|
||||||
Interface NullCipher::interface() const
|
Interface NullCipher::interface() const
|
||||||
{
|
{
|
||||||
return iface;
|
return iface;
|
||||||
}
|
}
|
||||||
|
|
||||||
CipherKey NullCipher::newKey(const char *, int,
|
CipherKey NullCipher::newKey(const char *, int,
|
||||||
int &, long, const unsigned char *, int )
|
int &, long, const unsigned char *, int )
|
||||||
{
|
{
|
||||||
return gNullKey;
|
return gNullKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
CipherKey NullCipher::newKey(const char *, int)
|
CipherKey NullCipher::newKey(const char *, int)
|
||||||
{
|
{
|
||||||
return gNullKey;
|
return gNullKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
CipherKey NullCipher::newRandomKey()
|
CipherKey NullCipher::newRandomKey()
|
||||||
{
|
{
|
||||||
return gNullKey;
|
return gNullKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullCipher::randomize( unsigned char *buf, int len, bool ) const
|
bool NullCipher::randomize( unsigned char *buf, int len, bool ) const
|
||||||
{
|
{
|
||||||
memset( buf, 0, len );
|
memset( buf, 0, len );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t NullCipher::MAC_64(const unsigned char *, int ,
|
uint64_t NullCipher::MAC_64(const unsigned char *, int ,
|
||||||
const CipherKey &, uint64_t *) const
|
const CipherKey &, uint64_t *) const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
CipherKey NullCipher::readKey( const unsigned char *,
|
CipherKey NullCipher::readKey( const unsigned char *,
|
||||||
const CipherKey &, bool)
|
const CipherKey &, bool)
|
||||||
{
|
{
|
||||||
return gNullKey;
|
return gNullKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NullCipher::writeKey(const CipherKey &, unsigned char *,
|
void NullCipher::writeKey(const CipherKey &, unsigned char *,
|
||||||
const CipherKey &)
|
const CipherKey &)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullCipher::compareKey(const CipherKey &A_,
|
bool NullCipher::compareKey(const CipherKey &A_,
|
||||||
const CipherKey &B_) const
|
const CipherKey &B_) const
|
||||||
{
|
{
|
||||||
shared_ptr<NullKey> A = dynamic_pointer_cast<NullKey>(A_);
|
shared_ptr<NullKey> A = dynamic_pointer_cast<NullKey>(A_);
|
||||||
shared_ptr<NullKey> B = dynamic_pointer_cast<NullKey>(B_);
|
shared_ptr<NullKey> B = dynamic_pointer_cast<NullKey>(B_);
|
||||||
return A.get() == B.get();
|
return A.get() == B.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
int NullCipher::encodedKeySize() const
|
int NullCipher::encodedKeySize() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NullCipher::keySize() const
|
int NullCipher::keySize() const
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NullCipher::cipherBlockSize() const
|
int NullCipher::cipherBlockSize() const
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullCipher::streamEncode( unsigned char *src, int len,
|
bool NullCipher::streamEncode( unsigned char *src, int len,
|
||||||
uint64_t iv64, const CipherKey &key) const
|
uint64_t iv64, const CipherKey &key) const
|
||||||
{
|
{
|
||||||
(void)src;
|
(void)src;
|
||||||
(void)len;
|
(void)len;
|
||||||
(void)iv64;
|
(void)iv64;
|
||||||
(void)key;
|
(void)key;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullCipher::streamDecode( unsigned char *src, int len,
|
bool NullCipher::streamDecode( unsigned char *src, int len,
|
||||||
uint64_t iv64, const CipherKey &key) const
|
uint64_t iv64, const CipherKey &key) const
|
||||||
{
|
{
|
||||||
(void)src;
|
(void)src;
|
||||||
(void)len;
|
(void)len;
|
||||||
(void)iv64;
|
(void)iv64;
|
||||||
(void)key;
|
(void)key;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullCipher::blockEncode( unsigned char *, int , uint64_t,
|
bool NullCipher::blockEncode( unsigned char *, int , uint64_t,
|
||||||
const CipherKey & ) const
|
const CipherKey & ) const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullCipher::blockDecode( unsigned char *, int, uint64_t,
|
bool NullCipher::blockDecode( unsigned char *, int, uint64_t,
|
||||||
const CipherKey & ) const
|
const CipherKey & ) const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NullCipher::Enabled()
|
bool NullCipher::Enabled()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -21,8 +21,8 @@
|
|||||||
#ifndef _NullCipher_incl_
|
#ifndef _NullCipher_incl_
|
||||||
#define _NullCipher_incl_
|
#define _NullCipher_incl_
|
||||||
|
|
||||||
#include "Cipher.h"
|
#include "cipher/Cipher.h"
|
||||||
#include "Interface.h"
|
#include "base/Interface.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Implements Cipher interface for a pass-through mode. May be useful for
|
Implements Cipher interface for a pass-through mode. May be useful for
|
973
cipher/SSL_Cipher.cpp
Normal file
973
cipher/SSL_Cipher.cpp
Normal file
@ -0,0 +1,973 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2004, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "base/config.h"
|
||||||
|
|
||||||
|
#include <openssl/blowfish.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
|
||||||
|
#include "cipher/SSL_Cipher.h"
|
||||||
|
#include "cipher/MemoryPool.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "base/Mutex.h"
|
||||||
|
#include "base/Range.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include "base/i18n.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace rel;
|
||||||
|
|
||||||
|
const int MAX_KEYLENGTH = 64; // 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;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
This produces the same result as OpenSSL's EVP_BytesToKey. The difference
|
||||||
|
is that here we can explicitly specify the key size, instead of relying on
|
||||||
|
the state of EVP_CIPHER struct. EVP_BytesToKey will only produce 128 bit
|
||||||
|
keys for the EVP Blowfish interface, which is not what we want.
|
||||||
|
|
||||||
|
DEPRECATED: this is here for backward compatibilty only. Use PBKDF
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
EVP_MD_CTX cx;
|
||||||
|
EVP_MD_CTX_init( &cx );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
|
int toCopy = MIN( nkey, (int)mds - offset );
|
||||||
|
if( toCopy )
|
||||||
|
{
|
||||||
|
memcpy( key, mdBuf+offset, toCopy );
|
||||||
|
key += toCopy;
|
||||||
|
nkey -= toCopy;
|
||||||
|
offset += toCopy;
|
||||||
|
}
|
||||||
|
toCopy = MIN( niv, (int)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 SSL_Cipher::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<unsigned char*>(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.
|
||||||
|
// 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
|
||||||
|
// - Version 3:1 adds ssl/aes_xts
|
||||||
|
static Interface BlowfishInterface = makeInterface( "ssl/blowfish", 3, 0, 2 );
|
||||||
|
static Interface AESInterface = makeInterface( "ssl/aes", 3, 0, 2 );
|
||||||
|
static Interface AesXtsInterface = makeInterface( "ssl/aes_xts", 3, 1, 2 );
|
||||||
|
|
||||||
|
#if defined(HAVE_EVP_BF)
|
||||||
|
|
||||||
|
static Range BFKeyRange(128,256,32);
|
||||||
|
static Range BFBlockRange(64,4096,8);
|
||||||
|
|
||||||
|
static shared_ptr<Cipher> NewBFCipher( const Interface &iface, int keyLen )
|
||||||
|
{
|
||||||
|
if( keyLen <= 0 )
|
||||||
|
keyLen = 160;
|
||||||
|
|
||||||
|
keyLen = BFKeyRange.closest( keyLen );
|
||||||
|
|
||||||
|
const EVP_CIPHER *blockCipher = EVP_bf_cbc();
|
||||||
|
const EVP_CIPHER *streamCipher = EVP_bf_cfb();
|
||||||
|
|
||||||
|
return shared_ptr<Cipher>( 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, true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(HAVE_EVP_AES)
|
||||||
|
|
||||||
|
static Range AESKeyRange(128,256,64);
|
||||||
|
static Range AESBlockRange(64,4096,16);
|
||||||
|
|
||||||
|
static shared_ptr<Cipher> NewAESCipher( const Interface &iface, int keyLen )
|
||||||
|
{
|
||||||
|
if( keyLen <= 0 )
|
||||||
|
keyLen = 192;
|
||||||
|
|
||||||
|
keyLen = AESKeyRange.closest( keyLen );
|
||||||
|
|
||||||
|
const EVP_CIPHER *blockCipher = 0;
|
||||||
|
const EVP_CIPHER *streamCipher = 0;
|
||||||
|
|
||||||
|
switch(keyLen)
|
||||||
|
{
|
||||||
|
case 128:
|
||||||
|
blockCipher = EVP_aes_128_cbc();
|
||||||
|
streamCipher = EVP_aes_128_cfb();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 192:
|
||||||
|
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<Cipher>( 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, true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_EVP_AES_XTS)
|
||||||
|
|
||||||
|
static Range AesXtsKeyRange(128,256,128);
|
||||||
|
static Range AesXtsBlockRange(1024,8192,256);
|
||||||
|
|
||||||
|
static shared_ptr<Cipher> NewAesXtsCipher( const Interface &iface, int keyLen )
|
||||||
|
{
|
||||||
|
if( keyLen <= 0 )
|
||||||
|
keyLen = 256;
|
||||||
|
|
||||||
|
keyLen = AesXtsKeyRange.closest( keyLen );
|
||||||
|
|
||||||
|
const EVP_CIPHER *blockCipher = 0;
|
||||||
|
|
||||||
|
switch(keyLen)
|
||||||
|
{
|
||||||
|
case 128:
|
||||||
|
blockCipher = EVP_aes_128_xts();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 256:
|
||||||
|
default:
|
||||||
|
blockCipher = EVP_aes_256_xts();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XTS uses 2 keys, so the key size is doubled here.
|
||||||
|
// Eg XTS-AES-256 uses two 256 bit keys.
|
||||||
|
return shared_ptr<Cipher>( new SSL_Cipher(iface, AesXtsInterface,
|
||||||
|
blockCipher, NULL, 2 * keyLen / 8) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool AES_XTS_Cipher_registered = Cipher::Register(
|
||||||
|
"AES_XTS", "Tweakable wide-block cipher",
|
||||||
|
AesXtsInterface, AesXtsKeyRange, AesXtsBlockRange, NewAesXtsCipher, false);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SSLKey : public AbstractCipherKey
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
unsigned int keySize; // in bytes
|
||||||
|
unsigned int ivLength;
|
||||||
|
|
||||||
|
// key data is first _keySize bytes,
|
||||||
|
// followed by iv of _ivLength bytes,
|
||||||
|
SecureMem buf;
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX block_enc;
|
||||||
|
EVP_CIPHER_CTX block_dec;
|
||||||
|
EVP_CIPHER_CTX stream_enc;
|
||||||
|
EVP_CIPHER_CTX stream_dec;
|
||||||
|
|
||||||
|
HMAC_CTX mac_ctx;
|
||||||
|
|
||||||
|
SSLKey(int keySize, int ivLength);
|
||||||
|
~SSLKey();
|
||||||
|
};
|
||||||
|
|
||||||
|
SSLKey::SSLKey(int keySize_, int ivLength_)
|
||||||
|
: buf(keySize_ + ivLength_)
|
||||||
|
{
|
||||||
|
rAssert(keySize_ >= 8);
|
||||||
|
rAssert(ivLength_ >= 8);
|
||||||
|
|
||||||
|
this->keySize = keySize_;
|
||||||
|
this->ivLength = ivLength_;
|
||||||
|
pthread_mutex_init( &mutex, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
SSLKey::~SSLKey()
|
||||||
|
{
|
||||||
|
keySize = 0;
|
||||||
|
ivLength = 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 );
|
||||||
|
|
||||||
|
HMAC_CTX_cleanup( &mac_ctx );
|
||||||
|
|
||||||
|
pthread_mutex_destroy( &mutex );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned char* KeyData( const shared_ptr<SSLKey> &key )
|
||||||
|
{
|
||||||
|
return (unsigned char *)key->buf.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned char* IVData( const shared_ptr<SSLKey> &key )
|
||||||
|
{
|
||||||
|
return (unsigned char *)key->buf.data + key->keySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initKey(const shared_ptr<SSLKey> &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_EncryptInit_ex( &key->block_enc, _blockCipher, NULL, NULL, NULL);
|
||||||
|
EVP_DecryptInit_ex( &key->block_dec, _blockCipher, NULL, NULL, NULL);
|
||||||
|
EVP_CIPHER_CTX_set_key_length( &key->block_enc, _keySize );
|
||||||
|
EVP_CIPHER_CTX_set_key_length( &key->block_dec, _keySize );
|
||||||
|
EVP_CIPHER_CTX_set_padding( &key->block_enc, 0 );
|
||||||
|
EVP_CIPHER_CTX_set_padding( &key->block_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_CIPHER_CTX_init( &key->stream_enc );
|
||||||
|
EVP_CIPHER_CTX_init( &key->stream_dec );
|
||||||
|
if (_streamCipher != NULL)
|
||||||
|
{
|
||||||
|
EVP_EncryptInit_ex( &key->stream_enc, _streamCipher, NULL, NULL, NULL);
|
||||||
|
EVP_DecryptInit_ex( &key->stream_dec, _streamCipher, NULL, NULL, NULL);
|
||||||
|
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->stream_enc, 0 );
|
||||||
|
EVP_CIPHER_CTX_set_padding( &key->stream_dec, 0 );
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 <= _keySize);
|
||||||
|
|
||||||
|
VLOG(1) << "allocated cipher " << iface.name()
|
||||||
|
<< ", keySize " << _keySize
|
||||||
|
<< ", ivlength " << _ivLength;
|
||||||
|
|
||||||
|
// EVP_CIPHER_key_length isn't useful for variable-length ciphers like
|
||||||
|
// Blowfish. Version 1 relied upon it incorrectly.
|
||||||
|
if( (EVP_CIPHER_key_length( _blockCipher ) != (int )_keySize)
|
||||||
|
&& iface.major() == 1)
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "Running in backward compatibilty mode for 1.0 - \n"
|
||||||
|
<< "key is really " << EVP_CIPHER_key_length( _blockCipher ) * 8
|
||||||
|
<< " bits, not " << _keySize * 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SSL_Cipher::~SSL_Cipher()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Interface SSL_Cipher::interface() const
|
||||||
|
{
|
||||||
|
return realIface;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create a key from the password.
|
||||||
|
Use SHA to distribute entropy from the password into the key.
|
||||||
|
|
||||||
|
This algorithm must remain constant for backward compatibility, as this key
|
||||||
|
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<SSLKey> 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)
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "openssl error, PBKDF2 failed";
|
||||||
|
return CipherKey();
|
||||||
|
} else
|
||||||
|
iterationCount = res;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// known iteration length
|
||||||
|
if(PKCS5_PBKDF2_HMAC_SHA1(
|
||||||
|
password, passwdLength,
|
||||||
|
const_cast<unsigned char*>(salt), saltLen,
|
||||||
|
iterationCount, _keySize + _ivLength, KeyData(key)) != 1)
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "openssl error, PBKDF2 failed";
|
||||||
|
return CipherKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initKey( key, _blockCipher, _streamCipher, _keySize );
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
CipherKey SSL_Cipher::newKey(const char *password, int passwdLength)
|
||||||
|
{
|
||||||
|
shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) );
|
||||||
|
|
||||||
|
int bytes = 0;
|
||||||
|
if( iface.major() > 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)
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "newKey: BytesToKey returned " << bytes
|
||||||
|
<< ", expecting " << _keySize << " key bytes";
|
||||||
|
}
|
||||||
|
} 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) );
|
||||||
|
}
|
||||||
|
|
||||||
|
initKey( key, _blockCipher, _streamCipher, _keySize );
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create a random key.
|
||||||
|
We use the OpenSSL library to generate random bytes, then take the hash of
|
||||||
|
those bytes to use as the key.
|
||||||
|
|
||||||
|
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 ];
|
||||||
|
|
||||||
|
if(!randomize(tmpBuf, bufLen, true) ||
|
||||||
|
!randomize(saltBuf, saltLen, true))
|
||||||
|
return CipherKey();
|
||||||
|
|
||||||
|
shared_ptr<SSLKey> 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)
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "openssl error, PBKDF2 failed";
|
||||||
|
return CipherKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
OPENSSL_cleanse(tmpBuf, bufLen);
|
||||||
|
|
||||||
|
initKey( key, _blockCipher, _streamCipher, _keySize );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
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)
|
||||||
|
LOG(ERROR) << "openssl error: " << 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<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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<SSLKey> mk = dynamic_pointer_cast<SSLKey>(masterKey);
|
||||||
|
rAssert(mk->keySize == _keySize);
|
||||||
|
|
||||||
|
unsigned char tmpBuf[ 2 * MAX_KEYLENGTH ];
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
|
||||||
|
if (_streamCipher != NULL)
|
||||||
|
{
|
||||||
|
memcpy( tmpBuf, data+KEY_CHECKSUM_BYTES, _keySize + _ivLength );
|
||||||
|
streamDecode(tmpBuf, _keySize + _ivLength, checksum, masterKey);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
memcpy( tmpBuf, data+KEY_CHECKSUM_BYTES, 2 * _keySize );
|
||||||
|
blockDecode(tmpBuf, 2 * _keySize, checksum, masterKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for success
|
||||||
|
unsigned int checksum2 = MAC_32( tmpBuf, _keySize + _ivLength, masterKey );
|
||||||
|
if(checksum2 != checksum && checkKey)
|
||||||
|
{
|
||||||
|
VLOG(1) << "checksum mismatch: expected " << checksum
|
||||||
|
<< ", got " << checksum2
|
||||||
|
<< "on decode of " << _keySize + _ivLength << " bytes";
|
||||||
|
OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf));
|
||||||
|
return CipherKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<SSLKey> key( new SSLKey( _keySize, _ivLength) );
|
||||||
|
|
||||||
|
rAssert(_keySize + _ivLength == (unsigned int)key->buf.size );
|
||||||
|
memcpy( key->buf.data, tmpBuf, key->buf.size );
|
||||||
|
OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf));
|
||||||
|
|
||||||
|
initKey( key, _blockCipher, _streamCipher, _keySize );
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSL_Cipher::writeKey(const CipherKey &ckey, unsigned char *data,
|
||||||
|
const CipherKey &masterKey)
|
||||||
|
{
|
||||||
|
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
|
||||||
|
rAssert(key->keySize == _keySize);
|
||||||
|
rAssert(key->ivLength == _ivLength);
|
||||||
|
|
||||||
|
shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(masterKey);
|
||||||
|
rAssert(mk->keySize == _keySize);
|
||||||
|
rAssert(mk->ivLength == _ivLength);
|
||||||
|
|
||||||
|
unsigned char tmpBuf[ 2 * MAX_KEYLENGTH ];
|
||||||
|
|
||||||
|
unsigned int bufLen = key->buf.size;
|
||||||
|
rAssert(_keySize + _ivLength == bufLen );
|
||||||
|
memcpy( tmpBuf, key->buf.data, bufLen );
|
||||||
|
|
||||||
|
unsigned int checksum = MAC_32( tmpBuf, bufLen, masterKey );
|
||||||
|
|
||||||
|
if (_streamCipher != NULL)
|
||||||
|
streamEncode(tmpBuf, bufLen, checksum, masterKey);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bufLen = 2 * _keySize;
|
||||||
|
blockEncode(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
OPENSSL_cleanse(tmpBuf, sizeof(tmpBuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSL_Cipher::compareKey( const CipherKey &A, const CipherKey &B) const
|
||||||
|
{
|
||||||
|
shared_ptr<SSLKey> key1 = dynamic_pointer_cast<SSLKey>(A);
|
||||||
|
shared_ptr<SSLKey> key2 = dynamic_pointer_cast<SSLKey>(B);
|
||||||
|
|
||||||
|
rAssert(key1->buf.size == key2->buf.size);
|
||||||
|
|
||||||
|
if(memcmp(key1->buf.data, key2->buf.data, key1->buf.size) != 0)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SSL_Cipher::encodedKeySize() const
|
||||||
|
{
|
||||||
|
if (_streamCipher != NULL)
|
||||||
|
return _keySize + _ivLength + KEY_CHECKSUM_BYTES;
|
||||||
|
else
|
||||||
|
return 2 * _keySize + KEY_CHECKSUM_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SSL_Cipher::keySize() const
|
||||||
|
{
|
||||||
|
return _keySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SSL_Cipher::cipherBlockSize() const
|
||||||
|
{
|
||||||
|
int size = EVP_CIPHER_block_size( _blockCipher );
|
||||||
|
// OpenSSL (1.0.1-4ubuntu5.5) reports a block size of 1 for aes_xts.
|
||||||
|
// If this happens, use a single key width (ie 32 bytes for aes-xts-256).
|
||||||
|
if (size == 1)
|
||||||
|
size = _keySize / 2;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSL_Cipher::setIVec(unsigned char *ivec, uint64_t seed,
|
||||||
|
const shared_ptr<SSLKey> &key) const
|
||||||
|
{
|
||||||
|
if (iface.major() >= 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: For backward compatibility only.
|
||||||
|
// A watermark attack was discovered against this IV setup. If an attacker
|
||||||
|
// could get a victim to store a carefully crafted file, they could later
|
||||||
|
// 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<SSLKey> &key) const
|
||||||
|
{
|
||||||
|
unsigned int var1 = 0x060a4011 * seed;
|
||||||
|
unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C);
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
int bytesLeft = size;
|
||||||
|
while(bytesLeft)
|
||||||
|
{
|
||||||
|
int toFlip = MIN( (int)sizeof(revBuf), bytesLeft );
|
||||||
|
|
||||||
|
for(int i=0; i<toFlip; ++i)
|
||||||
|
revBuf[i] = buf[toFlip - (i+1)];
|
||||||
|
|
||||||
|
memcpy( buf, revBuf, toFlip );
|
||||||
|
bytesLeft -= toFlip;
|
||||||
|
buf += toFlip;
|
||||||
|
}
|
||||||
|
memset(revBuf, 0, sizeof(revBuf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void shuffleBytes(unsigned char *buf, int size)
|
||||||
|
{
|
||||||
|
for(int i=0; i<size-1; ++i)
|
||||||
|
buf[i+1] ^= buf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unshuffleBytes(unsigned char *buf, int size)
|
||||||
|
{
|
||||||
|
for(int i=size-1; i; --i)
|
||||||
|
buf[i] ^= buf[i-1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Partial blocks are encoded with a stream cipher. We make multiple passes on
|
||||||
|
the data to ensure that the ends of the data depend on each other.
|
||||||
|
*/
|
||||||
|
bool SSL_Cipher::streamEncode(unsigned char *buf, int size,
|
||||||
|
uint64_t iv64, const CipherKey &ckey) const
|
||||||
|
{
|
||||||
|
rAssert( size > 0 );
|
||||||
|
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
|
||||||
|
rAssert(key->keySize == _keySize);
|
||||||
|
rAssert(key->ivLength == _ivLength);
|
||||||
|
rAssert( key->stream_enc.key_len > 0 );
|
||||||
|
|
||||||
|
Lock lock( key->mutex );
|
||||||
|
|
||||||
|
unsigned char ivec[ MAX_IVLENGTH ];
|
||||||
|
int dstLen=0, tmpLen=0;
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
dstLen += tmpLen;
|
||||||
|
LOG_IF(ERROR, dstLen != size) << "encoding " << size
|
||||||
|
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSL_Cipher::streamDecode(unsigned char *buf, int size,
|
||||||
|
uint64_t iv64, const CipherKey &ckey) const
|
||||||
|
{
|
||||||
|
rAssert( size > 0 );
|
||||||
|
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
|
||||||
|
rAssert(key->keySize == _keySize);
|
||||||
|
rAssert(key->ivLength == _ivLength);
|
||||||
|
rAssert( key->stream_dec.key_len > 0 );
|
||||||
|
|
||||||
|
Lock lock( key->mutex );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
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;
|
||||||
|
LOG_IF(ERROR, dstLen != size) << "encoding " << size
|
||||||
|
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool SSL_Cipher::blockEncode(unsigned char *buf, int size,
|
||||||
|
uint64_t iv64, const CipherKey &ckey ) const
|
||||||
|
{
|
||||||
|
rAssert( size > 0 );
|
||||||
|
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(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 );
|
||||||
|
rAssert(blockMod == 0);
|
||||||
|
|
||||||
|
Lock lock( key->mutex );
|
||||||
|
|
||||||
|
unsigned char ivec[ MAX_IVLENGTH ];
|
||||||
|
|
||||||
|
int dstLen = 0, tmpLen = 0;
|
||||||
|
setIVec( ivec, iv64, key );
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
LOG_IF(ERROR, dstLen != size) << "encoding " << size
|
||||||
|
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSL_Cipher::blockDecode(unsigned char *buf, int size,
|
||||||
|
uint64_t iv64, const CipherKey &ckey ) const
|
||||||
|
{
|
||||||
|
rAssert( size > 0 );
|
||||||
|
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(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 );
|
||||||
|
rAssert(blockMod == 0);
|
||||||
|
|
||||||
|
Lock lock( key->mutex );
|
||||||
|
|
||||||
|
unsigned char ivec[ MAX_IVLENGTH ];
|
||||||
|
|
||||||
|
int dstLen = 0, tmpLen = 0;
|
||||||
|
setIVec( ivec, iv64, key );
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
LOG_IF(ERROR, dstLen != size) << "decoding " << size
|
||||||
|
<< " bytes, got back " << dstLen << " (" << tmpLen << " in final_ex)";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSL_Cipher::Enabled()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSL_Cipher::hasStreamMode() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
159
cipher/SSL_Cipher.h
Normal file
159
cipher/SSL_Cipher.h
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2004, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _SSL_Cipher_incl_
|
||||||
|
#define _SSL_Cipher_incl_
|
||||||
|
|
||||||
|
#include "cipher/Cipher.h"
|
||||||
|
#include "base/Interface.h"
|
||||||
|
|
||||||
|
class SSLKey;
|
||||||
|
#ifndef EVP_CIPHER
|
||||||
|
struct evp_cipher_st;
|
||||||
|
typedef struct evp_cipher_st EVP_CIPHER;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implements Cipher interface for OpenSSL's ciphers.
|
||||||
|
|
||||||
|
Design:
|
||||||
|
Variable algorithm, key size, and block size.
|
||||||
|
|
||||||
|
Partial blocks, keys, and names are encrypted using the cipher in a pseudo
|
||||||
|
stream mode (CFB).
|
||||||
|
|
||||||
|
Keys are encrypted with 2-4 (KEY_CHECKSUM_BYTES define) checksum bytes
|
||||||
|
derived from an HMAC over both they key data and the initial value vector
|
||||||
|
associated with the key. This allows a good chance at detecting an
|
||||||
|
incorrect password when we try and decrypt the master key.
|
||||||
|
|
||||||
|
File names are encrypted in the same way, with 2 checksum bytes derived
|
||||||
|
from an HMAC over the filename. This is done not to allow checking the
|
||||||
|
results, but to make the output much more random. Changing one letter in a
|
||||||
|
filename should result in a completely different encrypted filename, to
|
||||||
|
help frustrate any attempt to guess information about files from their
|
||||||
|
encrypted names.
|
||||||
|
|
||||||
|
Stream encryption involves two encryption passes over the data, implemented
|
||||||
|
as:
|
||||||
|
1. shuffle
|
||||||
|
2. 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
|
||||||
|
may only affect one byte of output, allowing some XOR attacks.
|
||||||
|
|
||||||
|
The shuffle/encrypt is used as above in filename encryption as well,
|
||||||
|
although it is not necessary as they have checksum bytes which augment the
|
||||||
|
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
|
||||||
|
{
|
||||||
|
Interface iface;
|
||||||
|
Interface realIface;
|
||||||
|
const EVP_CIPHER *_blockCipher;
|
||||||
|
const EVP_CIPHER *_streamCipher;
|
||||||
|
unsigned int _keySize; // in bytes
|
||||||
|
unsigned int _ivLength;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SSL_Cipher(const Interface &iface, const 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 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();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
virtual bool hasStreamMode() 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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// hack to help with static builds
|
||||||
|
static bool Enabled();
|
||||||
|
|
||||||
|
// Password-based key derivation function which determines the
|
||||||
|
// number of iterations based on a desired execution time (in microseconds).
|
||||||
|
// Returns the number of iterations applied.
|
||||||
|
static int TimedPBKDF2(const char *pass, int passLen,
|
||||||
|
const unsigned char *salt, int saltLen,
|
||||||
|
int keyLen, unsigned char *out,
|
||||||
|
long desiredPDFTimeMicroseconds);
|
||||||
|
private:
|
||||||
|
void setIVec( unsigned char *ivec, uint64_t seed,
|
||||||
|
const shared_ptr<SSLKey> &key ) const;
|
||||||
|
|
||||||
|
// deprecated - for backward compatibility
|
||||||
|
void setIVec_old( unsigned char *ivec, unsigned int seed,
|
||||||
|
const shared_ptr<SSLKey> &key ) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
109
cipher/openssl.cpp
Normal file
109
cipher/openssl.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2007, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cipher/openssl.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#define NO_DES
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
#include <openssl/engine.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if(!crypto_locks)
|
||||||
|
{
|
||||||
|
VLOG(1) << "Allocating " << CRYPTO_num_locks() << " locks for OpenSSL";
|
||||||
|
crypto_locks = new pthread_mutex_t[ CRYPTO_num_locks() ];
|
||||||
|
for(int i=0; i<CRYPTO_num_locks(); ++i)
|
||||||
|
pthread_mutex_init( crypto_locks+i, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mode & CRYPTO_LOCK)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock( crypto_locks + n );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock( crypto_locks + n );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pthreads_locking_cleanup()
|
||||||
|
{
|
||||||
|
if(crypto_locks)
|
||||||
|
{
|
||||||
|
for(int i=0; i<CRYPTO_num_locks(); ++i)
|
||||||
|
pthread_mutex_destroy( crypto_locks+i );
|
||||||
|
delete[] crypto_locks;
|
||||||
|
crypto_locks = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openssl_init(bool threaded)
|
||||||
|
{
|
||||||
|
// initialize the SSL library
|
||||||
|
SSL_load_error_strings();
|
||||||
|
SSL_library_init();
|
||||||
|
|
||||||
|
unsigned int randSeed = 0;
|
||||||
|
RAND_bytes( (unsigned char*)&randSeed, sizeof(randSeed) );
|
||||||
|
srand( randSeed );
|
||||||
|
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
/* Load all bundled ENGINEs into memory and make them visible */
|
||||||
|
ENGINE_load_builtin_engines();
|
||||||
|
/* Register all of them for every algorithm they collectively implement */
|
||||||
|
ENGINE_register_all_complete();
|
||||||
|
#endif // NO_ENGINE
|
||||||
|
|
||||||
|
if(threaded)
|
||||||
|
{
|
||||||
|
// provide locking functions to OpenSSL since we'll be running with
|
||||||
|
// threads accessing openssl in parallel.
|
||||||
|
CRYPTO_set_id_callback( pthreads_thread_id );
|
||||||
|
CRYPTO_set_locking_callback( pthreads_locking_callback );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void openssl_shutdown(bool threaded)
|
||||||
|
{
|
||||||
|
#ifndef OPENSSL_NO_ENGINE
|
||||||
|
ENGINE_cleanup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(threaded)
|
||||||
|
pthreads_locking_cleanup();
|
||||||
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ static const char rcsid[] = "$OpenBSD: readpassphrase.c,v 1.12 2001/12/15 05:41:
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <readpassphrase.h>
|
#include "cipher/readpassphrase.h"
|
||||||
|
|
||||||
#ifdef TCSASOFT
|
#ifdef TCSASOFT
|
||||||
# define _T_FLUSH (TCSAFLUSH|TCSASOFT)
|
# define _T_FLUSH (TCSAFLUSH|TCSASOFT)
|
235
configure.ac
235
configure.ac
@ -1,235 +0,0 @@
|
|||||||
dnl Process this file with autoconf to produce a configure script.
|
|
||||||
|
|
||||||
AC_INIT(encfs/encfs.h) dnl a source file from your sub dir
|
|
||||||
AC_CONFIG_AUX_DIR([build-aux])
|
|
||||||
AM_INIT_AUTOMAKE(encfs, 1.8.0) dnl searches for some needed programs
|
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
|
||||||
|
|
||||||
AC_CANONICAL_HOST
|
|
||||||
AM_CONDITIONAL([DARWIN],
|
|
||||||
[case $host_os in darwin*) true;; *) false;; esac])
|
|
||||||
|
|
||||||
dnl without this order in this file, automake will be confused!
|
|
||||||
dnl
|
|
||||||
AM_CONFIG_HEADER(config.h)
|
|
||||||
|
|
||||||
dnl This ksh/zsh feature conflicts with `cd blah ; pwd`
|
|
||||||
unset CDPATH
|
|
||||||
|
|
||||||
AC_LANG_CPLUSPLUS
|
|
||||||
AC_PROG_CXX
|
|
||||||
|
|
||||||
dnl almost the same like KDE_SET_PEFIX but the path is /usr/local
|
|
||||||
dnl
|
|
||||||
unset CDPATH
|
|
||||||
dnl make /usr/local the default for the installation
|
|
||||||
AC_PREFIX_DEFAULT(/usr/local)
|
|
||||||
|
|
||||||
AM_GNU_GETTEXT([external])
|
|
||||||
AM_GNU_GETTEXT_VERSION([0.17])
|
|
||||||
dnl AC_LIB_LINKFLAGS([asprintf]) # use internal copy
|
|
||||||
LIBINTL=-lgettextlib
|
|
||||||
|
|
||||||
dnl create only shared libtool-libraries
|
|
||||||
dnl These can be overridden by command line arguments
|
|
||||||
AC_ENABLE_SHARED(yes)
|
|
||||||
AC_ENABLE_STATIC(no)
|
|
||||||
|
|
||||||
AM_CONDITIONAL( BUILD_STATIC, test "x$enable_static" = "xyes" )
|
|
||||||
dnl only build either static or shared, not both..
|
|
||||||
if test "x$enable_static" = "xyes"; then
|
|
||||||
enable_shared=no
|
|
||||||
AC_DEFINE(BUILD_STATIC, [1], [Building static library])
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_PROG_LIBTOOL
|
|
||||||
|
|
||||||
AX_PTHREAD
|
|
||||||
|
|
||||||
AC_CHECK_HEADERS([tr1/memory tr1/unordered_map tr1/unordered_set tr1/tuple])
|
|
||||||
|
|
||||||
dnl Need to include any user specified flags in the tests below, as they might
|
|
||||||
dnl specify required include directories..
|
|
||||||
FUSE_FLAGS="-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=26"
|
|
||||||
CPPFLAGS="$CPPFLAGS $USER_INCLUDES $FUSE_FLAGS -D__STDC_FORMAT_MACROS"
|
|
||||||
CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $USER_INCLUDES"
|
|
||||||
LDFLAGS="$LDFLAGS $PTHREAD_LIBS $USER_LDFLAGS $FUSE_LIBS"
|
|
||||||
|
|
||||||
dnl Look for fuse headers.
|
|
||||||
AX_EXT_HAVE_HEADER(fuse.h, /usr/include/fuse /usr/local/include/fuse \
|
|
||||||
/opt/include/fuse /opt/local/include/fuse \
|
|
||||||
/usr/include/osxfuse /usr/local/include/osxfuse)
|
|
||||||
|
|
||||||
dnl Ensure the necessary paths are added to LDPATH
|
|
||||||
AX_EXT_HAVE_LIB(/usr/lib /usr/local/lib /opt/lib /opt/local/lib, fuse,
|
|
||||||
fuse_new, [])
|
|
||||||
AX_EXT_HAVE_LIB(/usr/lib /usr/local/lib /opt/lib /opt/local/lib, osxfuse,
|
|
||||||
fuse_new, [])
|
|
||||||
|
|
||||||
if test "$GXX" = "yes"; then
|
|
||||||
CXXFLAGS="-W -Wall -Wpointer-arith -Wwrite-strings $CXXFLAGS"
|
|
||||||
dnl CXXFLAGS="$CXXFLAGS -Wformat=2 -Wconversion"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test -z "${DARWIN_TRUE}"; then
|
|
||||||
dnl Prefer OSXFuse, but fall back to libfuse.
|
|
||||||
AC_CHECK_LIB(osxfuse, fuse_new, [FUSE_LIBS="$FUSE_LIBS -losxfuse"],
|
|
||||||
AC_CHECK_LIB(fuse, fuse_new, [FUSE_LIBS="$FUSE_LIBS -lfuse"],
|
|
||||||
AC_MSG_ERROR([Unable to find libfuse or libosxfuse.])))
|
|
||||||
else
|
|
||||||
AC_CHECK_LIB(fuse, fuse_new, [FUSE_LIBS="$FUSE_LIBS -lfuse"],
|
|
||||||
AC_MSG_ERROR([Unable to find libfuse.]))
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check for a supported FUSE_MAJOR_VERSION.
|
|
||||||
AC_MSG_CHECKING([For supported FUSE API version])
|
|
||||||
AC_RUN_IFELSE([
|
|
||||||
AC_LANG_PROGRAM([[#include "fuse.h"]],
|
|
||||||
[[
|
|
||||||
if(FUSE_MAJOR_VERSION == 2 && FUSE_MINOR_VERSION >= 5)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
} else
|
|
||||||
return -1;
|
|
||||||
]])],
|
|
||||||
[AC_MSG_RESULT([yes])],
|
|
||||||
[AC_MSG_RESULT([no])
|
|
||||||
AC_MSG_FAILURE([
|
|
||||||
Encfs 1.3 requires FUSE 2.5 or newer. Please check config.log for errors. If
|
|
||||||
you cannot determine the problem, mail encfs-users@lists.sourceforge.net
|
|
||||||
and include the config.log file])
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
dnl fuse_operations.setxattr was added 2004-03-31
|
|
||||||
dnl only enable it if setxattr function is found..
|
|
||||||
AC_CHECK_HEADERS([attr/xattr.h sys/xattr.h])
|
|
||||||
|
|
||||||
dnl xattr functions take additional arguments on some systems (eg Darwin).
|
|
||||||
AC_CACHE_CHECK([whether xattr interface takes additional options],
|
|
||||||
smb_attr_cv_xattr_add_opt, [
|
|
||||||
old_LIBS=$LIBS
|
|
||||||
LIBS="$LIBS $ACL_LIBS"
|
|
||||||
AC_TRY_COMPILE([
|
|
||||||
#include <sys/types.h>
|
|
||||||
#if HAVE_ATTR_XATTR_H
|
|
||||||
#include <attr/xattr.h>
|
|
||||||
#elif HAVE_SYS_XATTR_H
|
|
||||||
#include <sys/xattr.h>
|
|
||||||
#endif
|
|
||||||
],[
|
|
||||||
getxattr(0, 0, 0, 0, 0, 0);
|
|
||||||
],
|
|
||||||
[smb_attr_cv_xattr_add_opt=yes],
|
|
||||||
[smb_attr_cv_xattr_add_opt=no;LIBS=$old_LIBS])
|
|
||||||
])
|
|
||||||
if test x"$smb_attr_cv_xattr_add_opt" = x"yes"; then
|
|
||||||
AC_DEFINE(XATTR_ADD_OPT, 1, [xattr functions have additional options])
|
|
||||||
fi
|
|
||||||
|
|
||||||
dnl Check for valgrind headers..
|
|
||||||
AC_ARG_ENABLE(valgrind,
|
|
||||||
AC_HELP_STRING([--enable-valgrind],
|
|
||||||
[build with valgrind support.]),
|
|
||||||
AC_CHECK_HEADERS([valgrind/valgrind.h valgrind/memcheck.h])
|
|
||||||
)
|
|
||||||
|
|
||||||
# allow user option of not using ssl..
|
|
||||||
AC_ARG_ENABLE(openssl,
|
|
||||||
AC_HELP_STRING([--disable-openssl],
|
|
||||||
[disables openssl library usage.]),
|
|
||||||
with_openssl=$enableval, with_openssl="yes" )
|
|
||||||
|
|
||||||
# try checking for openssl using
|
|
||||||
if test "x$with_openssl" = "xyes"; then
|
|
||||||
# look for openssl using pkg-config first..
|
|
||||||
PKG_CHECK_MODULES(OPENSSL, openssl >= 0.9.7,
|
|
||||||
with_openssl="yes", with_openssl="old-test")
|
|
||||||
|
|
||||||
# If that fails, try checking via old methods - which isn't as robust when
|
|
||||||
# it comes to extra include paths, etc..
|
|
||||||
if test "x$with_openssl" = "xold-test"; then
|
|
||||||
AC_CHECK_HEADER(openssl/ssl.h,
|
|
||||||
AC_CHECK_LIB(ssl,SSL_new,
|
|
||||||
[with_openssl="yes"]))
|
|
||||||
OPENSSL_LIBS="-lssl"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if we have openssl, then examine available interfaces.
|
|
||||||
if test "x$with_openssl" = "xyes"; then
|
|
||||||
AC_DEFINE(HAVE_SSL, [1], [Linking with OpenSSL])
|
|
||||||
|
|
||||||
# add in the libs just for the test..
|
|
||||||
oldflags=$CXXFLAGS
|
|
||||||
oldlibs=$LIBS
|
|
||||||
CXXFLAGS="$CXXFLAGS $OPENSSL_CFLAGS"
|
|
||||||
LIBS="$LIBS $OPENSSL_LIBS"
|
|
||||||
AC_CHECK_FUNCS(EVP_aes_128_cbc EVP_aes_192_cbc EVP_aes_256_cbc,
|
|
||||||
AC_DEFINE(HAVE_EVP_AES, [1], [Have EVP AES interfaces]))
|
|
||||||
AC_CHECK_FUNCS(EVP_bf_cbc,
|
|
||||||
AC_DEFINE(HAVE_EVP_BF, [1], [Have EVP Blowfish interfaces]))
|
|
||||||
AC_CHECK_FUNCS(EVP_CIPHER_CTX_set_padding,
|
|
||||||
with_opensslevp=yes,
|
|
||||||
AC_MSG_WARN([New SSL cipher code only enabled for OpenSSL 0.9.7 or later]))
|
|
||||||
AC_CHECK_FUNCS(HMAC_Init_ex,
|
|
||||||
AC_DEFINE(HAVE_HMAC_INIT_EX, [1], [Have HMAC_Init_ex function]))
|
|
||||||
|
|
||||||
CXXFLAGS="$oldflags"
|
|
||||||
LIBS="$oldlibs"
|
|
||||||
|
|
||||||
AC_SUBST(HAVE_EVP_AES)
|
|
||||||
AC_SUBST(HAVE_EVP_BF)
|
|
||||||
AC_SUBST(HAVE_HMAC_INIT_EX)
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
AM_CONDITIONAL( BUILD_OPENSSL, test "x$with_openssl" = "xyes" )
|
|
||||||
AM_CONDITIONAL( BUILD_SSLCIPHER, test "x$with_opensslevp" = "xyes" )
|
|
||||||
AC_SUBST(HAVE_SSL)
|
|
||||||
|
|
||||||
if test "x$with_openssl" != "xyes"; then
|
|
||||||
AC_MSG_ERROR( [Encfs requires OpenSSL])
|
|
||||||
fi
|
|
||||||
|
|
||||||
# check for RLOG
|
|
||||||
PKG_CHECK_MODULES(RLOG, librlog >= 1.3, with_rlog="yes", with_rlog="test")
|
|
||||||
|
|
||||||
# manual check for rlog, unless environment variable already set
|
|
||||||
if test "$with_rlog" = "test" && test "x$RLOG_LIBS" = "x"; then
|
|
||||||
AC_MSG_WARN([Checking for librlog the hard way])
|
|
||||||
AC_CHECK_LIB(rlog, RLogVersion, [RLOG_LIBS="-lrlog"],
|
|
||||||
[AC_MSG_ERROR([EncFS depends on librlog])])
|
|
||||||
fi
|
|
||||||
|
|
||||||
# find Protocol Buffers
|
|
||||||
PKG_CHECK_MODULES(PROTOBUF, protobuf >= 2.0)
|
|
||||||
AC_PATH_PROG(PROTOC, protoc, [no])
|
|
||||||
if test "$PROTOC" == "no"; then
|
|
||||||
AC_MSG_FAILURE([Protocol Buffers compiler 'protoc' is required to build.])
|
|
||||||
fi
|
|
||||||
|
|
||||||
# find TinyXML
|
|
||||||
AC_LANG_PUSH([C++])
|
|
||||||
CPPFLAGS="$CPPFLAGS -DTIXML_USE_STL"
|
|
||||||
AC_CHECK_HEADER([tinyxml.h],,[AC_MSG_ERROR([tinyxml.h not found])])
|
|
||||||
AC_CHECK_LIB([tinyxml],[main],,
|
|
||||||
[AC_MSG_ERROR([you must install libtinyxml dev])])
|
|
||||||
AC_LANG_POP([C++])
|
|
||||||
|
|
||||||
# look for pod2man program for building man pages
|
|
||||||
AC_PATH_PROG(POD2MAN, pod2man, [no])
|
|
||||||
AC_PATH_PROG(POD2HTML, pod2html, [no])
|
|
||||||
AM_CONDITIONAL( BUILD_MAN, test "x$POD2MAN" != "xno" )
|
|
||||||
AM_CONDITIONAL( BUILD_MANHTML, test "x$POD2HTML" != "xno" )
|
|
||||||
AM_CONDITIONAL( BUILD_NLS, test "x$USE_NLS" != "xno" )
|
|
||||||
|
|
||||||
|
|
||||||
AC_CONFIG_FILES([Makefile] \
|
|
||||||
[encfs/Makefile] \
|
|
||||||
[encfs.spec] \
|
|
||||||
[makedist2.sh] \
|
|
||||||
[m4/Makefile] \
|
|
||||||
[po/Makefile.in] \
|
|
||||||
[po/Makefile])
|
|
||||||
|
|
||||||
AC_OUTPUT
|
|
||||||
|
|
2
devmode
Normal file
2
devmode
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
mkdir build
|
||||||
|
cd build && cmake .. -DCMAKE_BUILD_TYPE=Debug $@
|
207
encfs.spec.in
207
encfs.spec.in
@ -1,207 +0,0 @@
|
|||||||
Name: encfs
|
|
||||||
Summary: Encrypted pass-thru filesystem for Linux
|
|
||||||
Version: @VERSION@
|
|
||||||
Release: @RELEASE@
|
|
||||||
License: GPL
|
|
||||||
Group: System/Filesystems
|
|
||||||
Source: %{name}-%{version}-%{release}.tgz
|
|
||||||
BuildRoot: %{_tmppath}/build-root-%{name}
|
|
||||||
Packager: Valient Gough <vgough at pobox dot com>
|
|
||||||
#Distribution: Suse 9.1
|
|
||||||
Prefix: /usr
|
|
||||||
Url: http://pobox.com/~vgough/encfs
|
|
||||||
Provides: encfs
|
|
||||||
Provides: encfsctl
|
|
||||||
Provides: libencfs.1
|
|
||||||
|
|
||||||
Requires: rlog >= 1.3
|
|
||||||
Requires: openssl
|
|
||||||
Requires: fuse >= 2.2
|
|
||||||
|
|
||||||
%description
|
|
||||||
EncFS implements an encrypted filesystem in userspace using FUSE. FUSE
|
|
||||||
provides a Linux kernel module which allows virtual filesystems to be written
|
|
||||||
in userspace. EncFS encrypts all data and filenames in the filesystem and
|
|
||||||
passes access through to the underlying filesystem. Similar to CFS except that
|
|
||||||
it does not use NFS.
|
|
||||||
|
|
||||||
%changelog
|
|
||||||
* Fri Nov 11 2005 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.2.5
|
|
||||||
- Fix race condition when using newer versions of GCC. Fixes problem reported
|
|
||||||
by Chris at x.nu.
|
|
||||||
- add encfssh script, thanks to David Rosenstrauch
|
|
||||||
* Fri Aug 26 2005 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.2.4
|
|
||||||
- fix segfault if small invalid filenames were encountered in the encrypted
|
|
||||||
directory, reported by paulgfx.
|
|
||||||
- try and detect if user tries to mount the filesystem over the top of the
|
|
||||||
encrypted directory, problem reported by paulgfx.
|
|
||||||
- environment variable ENCFS5_CONFIG can be used to override the location of
|
|
||||||
the .encfs5 configuration file.
|
|
||||||
- add encfsctl 'export' command, patch from Janne Hellsten
|
|
||||||
|
|
||||||
* Tue Apr 19 2005 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.2.1
|
|
||||||
- add --public mount option
|
|
||||||
- add --stdinpass option to read password from stdin for scripting
|
|
||||||
- import latest rosetta translation updates
|
|
||||||
|
|
||||||
* Thu Feb 10 2005 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.2.0
|
|
||||||
- Fix bug with MAC headers and files > 2GB, reported by Damian Frank
|
|
||||||
- Fix bug with external password interface which could result in problems
|
|
||||||
communicating with external password program. Found by Olivier Dournaux.
|
|
||||||
- Switch to FUSE 2.2 API -- support for FUSE 1.x has been dropped.
|
|
||||||
- Add support for inode numbering pass-thru (when used 'use_ino' option to
|
|
||||||
fuse). This allows encoded filesystem to use the same inode numbers as the
|
|
||||||
underlying filesystem.
|
|
||||||
|
|
||||||
* Wed Jan 12 2005 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.11
|
|
||||||
- add internationalization support. Thanks to lots of contributors, there are
|
|
||||||
translations for serveral languages.
|
|
||||||
- added workaround for libfuse mount failure with FUSE 1.4
|
|
||||||
- fix compile failure with FUSE 1.4
|
|
||||||
|
|
||||||
* Mon Nov 8 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.10
|
|
||||||
- fix problems with recursive rename
|
|
||||||
- fix incorrect error codes from xattr functions
|
|
||||||
|
|
||||||
* Tue Aug 15 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.9
|
|
||||||
- fix another rename bug (affected filesystems with 'paranoia' configuration)
|
|
||||||
|
|
||||||
* Mon Aug 14 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.8
|
|
||||||
- Improve MAC block header processing.
|
|
||||||
|
|
||||||
* Sat Aug 12 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.7
|
|
||||||
- fix bug in truncate() for unopened files.
|
|
||||||
|
|
||||||
* Mon Aug 9 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.6
|
|
||||||
- fix header IV creation when truncate() used to create files.
|
|
||||||
- add support for IV chaining to old 0.x filesystem support code (useful for
|
|
||||||
systems with old OpenSSL, like RedHat 7.x).
|
|
||||||
|
|
||||||
* Tue Jul 22 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.5
|
|
||||||
|
|
||||||
* Sat Jul 10 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.4
|
|
||||||
- add external password prompt support.
|
|
||||||
|
|
||||||
* Thu Jun 24 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.3
|
|
||||||
|
|
||||||
* Fri May 28 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.2
|
|
||||||
- Fix bug affecting filesystems with small empty directories (like XFS)
|
|
||||||
- Updates to recursive rename code to undo all changes on failure.
|
|
||||||
- Fix OpenSSL dependency path inclusion in build.
|
|
||||||
|
|
||||||
* Wed May 19 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.1.1
|
|
||||||
- Fix MAC header memory size allocation error.
|
|
||||||
- Add file rename-while-open support needed for Evolution.
|
|
||||||
|
|
||||||
* Thu May 13 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Second release candidate for version 1.1
|
|
||||||
- Add support for block mode filename encryption.
|
|
||||||
- Add support for per-file initialization vectors.
|
|
||||||
- Add support for directory IV chaining for per-directory initialization
|
|
||||||
vectors.
|
|
||||||
- Add support for per-block MAC headers for file contents.
|
|
||||||
- Backward compatibility support dropped for filesystems created by version
|
|
||||||
0.x. Maintains backward compatible support for versions 1.0.x.
|
|
||||||
|
|
||||||
* Sun Apr 4 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.0.5
|
|
||||||
- Allow truncate call to extend file (only shrinking was supported)
|
|
||||||
|
|
||||||
* Fri Mar 26 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.0.4
|
|
||||||
- Large speed improvement.
|
|
||||||
- Add support for FUSE major version 2 API.
|
|
||||||
|
|
||||||
* Thu Mar 18 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.0.3
|
|
||||||
- Fix bugs in truncation and padding code.
|
|
||||||
|
|
||||||
* Sat Mar 13 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.0.2
|
|
||||||
- Use pkg-config to check for OpenSSL and RLog build settings
|
|
||||||
- Add support for '--' argument to encfs to pass arbitrary options to FUSE /
|
|
||||||
fusermount.
|
|
||||||
- Add man pages.
|
|
||||||
|
|
||||||
* Tue Mar 2 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.0.1
|
|
||||||
- Fix problem with using OpenSSL's EVP_BytesToKey function with variable
|
|
||||||
key length ciphers like Blowfish, as it would only generate 128 bit keys.
|
|
||||||
- Some configure script changes to make it possible to use --with-extra-include
|
|
||||||
configure option to pick up any necessary directories for OpenSSL.
|
|
||||||
|
|
||||||
* Fri Feb 27 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Release 1.0
|
|
||||||
- Added some pre-defined configuration options at startup to make filesystem
|
|
||||||
creation a bit more user friendly.
|
|
||||||
|
|
||||||
* Mon Feb 23 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Merge development branch to mainline. Source modularized to make it easier
|
|
||||||
to support different algorithms.
|
|
||||||
- Added encfsctl program which can show information about an encrypted
|
|
||||||
directory and can change the user password used to store the volume key.
|
|
||||||
- Added support for AES and BlowFish with user specified keys and block sizes
|
|
||||||
(when building with OpenSSL >= 0.9.7).
|
|
||||||
- Backward compatible with old format, but new filesystems store configuration
|
|
||||||
information in a new format which is not readable by old encfs versions.
|
|
||||||
|
|
||||||
* Sat Feb 7 2004 Valient Gough <vgough@pobox.com>
|
|
||||||
- Improved performance by fixing cache bug which caused cached data to not be
|
|
||||||
used as often as it could have been. Random seek performance improved by
|
|
||||||
600% according to Bonnie++ benchmark.
|
|
||||||
- Fixed bugs preventing files larger then 2GB. Limit should now be around
|
|
||||||
128GB (untested - I don't have that much drive space). > 2GB also requires
|
|
||||||
recent version of FUSE module (from Feb 6 or later) and an underlying
|
|
||||||
filesystem which supports large files.
|
|
||||||
- Release 0.6
|
|
||||||
|
|
||||||
%prep
|
|
||||||
rm -rf $RPM_BUILD_ROOT
|
|
||||||
mkdir $RPM_BUILD_ROOT
|
|
||||||
|
|
||||||
%setup -q
|
|
||||||
|
|
||||||
%build
|
|
||||||
CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" \
|
|
||||||
./configure --enable-debug=no --prefix=%{prefix} --mandir=%{_mandir}
|
|
||||||
make SED=/usr/bin/sed -j 2
|
|
||||||
|
|
||||||
%install
|
|
||||||
make DESTDIR=$RPM_BUILD_ROOT install-strip
|
|
||||||
|
|
||||||
cd $RPM_BUILD_ROOT
|
|
||||||
|
|
||||||
find . -type d -fprint $RPM_BUILD_DIR/file.list.%{name}.dirs
|
|
||||||
find . -type f -fprint $RPM_BUILD_DIR/file.list.%{name}.files.tmp
|
|
||||||
sed '/\/man\//s/$/.gz/g' $RPM_BUILD_DIR/file.list.%{name}.files.tmp > $RPM_BUILD_DIR/file.list.%{name}.files
|
|
||||||
find . -type l -fprint $RPM_BUILD_DIR/file.list.%{name}.libs
|
|
||||||
sed '1,2d;s,^\.,\%attr(-\,root\,root) \%dir ,' $RPM_BUILD_DIR/file.list.%{name}.dirs > $RPM_BUILD_DIR/file.list.%{name}
|
|
||||||
sed 's,^\.,\%attr(-\,root\,root) ,' $RPM_BUILD_DIR/file.list.%{name}.files >> $RPM_BUILD_DIR/file.list.%{name}
|
|
||||||
sed 's,^\.,\%attr(-\,root\,root) ,' $RPM_BUILD_DIR/file.list.%{name}.libs >> $RPM_BUILD_DIR/file.list.%{name}
|
|
||||||
|
|
||||||
%clean
|
|
||||||
case "$RPM_BUILD_ROOT" in build-root-*) rm -rf $RPM_BUILD_ROOT ;; esac
|
|
||||||
rm -f $RPM_BUILD_DIR/file.list.%{name}
|
|
||||||
rm -f $RPM_BUILD_DIR/file.list.%{name}.libs
|
|
||||||
rm -f $RPM_BUILD_DIR/file.list.%{name}.files
|
|
||||||
rm -f $RPM_BUILD_DIR/file.list.%{name}.files.tmp
|
|
||||||
rm -f $RPM_BUILD_DIR/file.list.%{name}.dirs
|
|
||||||
|
|
||||||
%files -f ../file.list.%{name}
|
|
||||||
|
|
||||||
%defattr(-,root,root,0755)
|
|
@ -1,433 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "BlockFileIO.h"
|
|
||||||
|
|
||||||
#include "MemoryPool.h"
|
|
||||||
#include "config.pb.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
|
|
||||||
#include "i18n.h"
|
|
||||||
|
|
||||||
template<typename Type>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockFileIO::BlockFileIO( int blockSize, const FSConfigPtr &cfg )
|
|
||||||
: _blockSize( blockSize )
|
|
||||||
, _allowHoles( cfg->config->allow_holes() )
|
|
||||||
{
|
|
||||||
rAssert( _blockSize > 1 );
|
|
||||||
_cache.data = new unsigned char [ _blockSize ];
|
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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::truncate( 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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,267 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004-2011, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "BlockNameIO.h"
|
|
||||||
|
|
||||||
#include "Cipher.h"
|
|
||||||
#include "base64.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
#include <rlog/RLogChannel.h>
|
|
||||||
|
|
||||||
#include "i18n.h"
|
|
||||||
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
static RLogChannel * Info = DEF_CHANNEL( "info/nameio", Log_Info );
|
|
||||||
|
|
||||||
|
|
||||||
static shared_ptr<NameIO> NewBlockNameIO( const Interface &iface,
|
|
||||||
const shared_ptr<Cipher> &cipher, const CipherKey &key )
|
|
||||||
{
|
|
||||||
int blockSize = 8;
|
|
||||||
if(cipher)
|
|
||||||
blockSize = cipher->cipherBlockSize();
|
|
||||||
|
|
||||||
return shared_ptr<NameIO>(
|
|
||||||
new BlockNameIO( iface, cipher, key, blockSize, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
static shared_ptr<NameIO> NewBlockNameIO32( const Interface &iface,
|
|
||||||
const shared_ptr<Cipher> &cipher, const CipherKey &key )
|
|
||||||
{
|
|
||||||
int blockSize = 8;
|
|
||||||
if(cipher)
|
|
||||||
blockSize = cipher->cipherBlockSize();
|
|
||||||
|
|
||||||
return shared_ptr<NameIO>(
|
|
||||||
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 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.
|
|
||||||
This version was from pre-release 1.1, never publically released, so no
|
|
||||||
backward compatibility necessary.
|
|
||||||
|
|
||||||
- Version 2.0 includes padding bytes in MAC computation. This way the MAC
|
|
||||||
computation uses the same number of bytes regardless of the number of
|
|
||||||
padding bytes.
|
|
||||||
|
|
||||||
- Version 3.0 uses full 64 bit initialization vector during IV chaining.
|
|
||||||
Prior versions used only the output from the MAC_16 call, giving a 1 in
|
|
||||||
2^16 chance of the same name being produced. Using the full 64 bit IV
|
|
||||||
changes that to a 1 in 2^64 chance..
|
|
||||||
|
|
||||||
- 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 makeInterface("nameio/block32", 4, 0, 2);
|
|
||||||
else
|
|
||||||
return makeInterface("nameio/block", 4, 0, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockNameIO::BlockNameIO( const Interface &iface,
|
|
||||||
const shared_ptr<Cipher> &cipher,
|
|
||||||
const CipherKey &key, int blockSize,
|
|
||||||
bool caseSensitiveEncoding )
|
|
||||||
: _interface( iface.major() )
|
|
||||||
, _bs( blockSize )
|
|
||||||
, _cipher( cipher )
|
|
||||||
, _key( key )
|
|
||||||
, _caseSensitive( caseSensitiveEncoding )
|
|
||||||
{
|
|
||||||
// just to be safe..
|
|
||||||
rAssert( blockSize < 128 );
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockNameIO::~BlockNameIO()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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::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::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;
|
|
||||||
}
|
|
||||||
|
|
39
encfs/CMakeLists.txt
Normal file
39
encfs/CMakeLists.txt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
include_directories (${Encfs_SOURCE_DIR}/base)
|
||||||
|
link_directories (${Encfs_BINARY_DIR}/base)
|
||||||
|
|
||||||
|
include_directories (${Encfs_SOURCE_DIR}/cipher)
|
||||||
|
link_directories (${Encfs_BINARY_DIR}/cipher)
|
||||||
|
|
||||||
|
include_directories (${Encfs_SOURCE_DIR}/fs)
|
||||||
|
link_directories (${Encfs_BINARY_DIR}/fs)
|
||||||
|
|
||||||
|
# TODO: move FUSE code into encfs-fs.
|
||||||
|
find_package (FUSE REQUIRED)
|
||||||
|
include_directories (${FUSE_INCLUDE_DIR})
|
||||||
|
|
||||||
|
include_directories (${CMAKE_BINARY_DIR}/base)
|
||||||
|
|
||||||
|
add_executable (encfs
|
||||||
|
main.cpp)
|
||||||
|
target_link_libraries (encfs
|
||||||
|
encfs-fs
|
||||||
|
encfs-cipher
|
||||||
|
encfs-base
|
||||||
|
${GLOG_LIBRARIES}
|
||||||
|
${FUSE_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (POD2MAN)
|
||||||
|
add_custom_target(man ALL
|
||||||
|
COMMAND ${POD2MAN} -u --section=1 --release=${ENCFS_VERSION}
|
||||||
|
--center="Encrypted Filesystem"
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/encfs.pod
|
||||||
|
encfs.1)
|
||||||
|
install (FILES ${CMAKE_CURRENT_BINARY_DIR}/encfs.1
|
||||||
|
DESTINATION
|
||||||
|
share/man/man1)
|
||||||
|
endif (POD2MAN)
|
||||||
|
|
||||||
|
install (TARGETS encfs DESTINATION bin)
|
||||||
|
|
@ -1,438 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "CipherFileIO.h"
|
|
||||||
|
|
||||||
#include "Cipher.h"
|
|
||||||
#include "MemoryPool.h"
|
|
||||||
#include "config.pb.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <cerrno>
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Version 2:0 adds support for a per-file initialization vector with a
|
|
||||||
fixed 8 byte header. The headers are enabled globally within a
|
|
||||||
filesystem at the filesystem configuration level.
|
|
||||||
When headers are disabled, 2:0 is compatible with version 1:0.
|
|
||||||
*/
|
|
||||||
static Interface CipherFileIO_iface = makeInterface("FileIO/Cipher", 2, 0, 1);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
CipherFileIO::CipherFileIO( const shared_ptr<FileIO> &_base,
|
|
||||||
const FSConfigPtr &cfg)
|
|
||||||
: BlockFileIO( cfg->config->block_size(), cfg )
|
|
||||||
, base( _base )
|
|
||||||
, haveHeader( cfg->config->unique_iv() )
|
|
||||||
, externalIV( 0 )
|
|
||||||
, fileIV( 0 )
|
|
||||||
, lastFlags( 0 )
|
|
||||||
{
|
|
||||||
fsConfig = cfg;
|
|
||||||
cipher = cfg->cipher;
|
|
||||||
key = cfg->key;
|
|
||||||
|
|
||||||
static bool warnOnce = false;
|
|
||||||
|
|
||||||
if(!warnOnce)
|
|
||||||
warnOnce = checkSize( fsConfig->config->block_size(),
|
|
||||||
fsConfig->cipher->cipherBlockSize() );
|
|
||||||
}
|
|
||||||
|
|
||||||
CipherFileIO::~CipherFileIO()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Interface CipherFileIO::interface() const
|
|
||||||
{
|
|
||||||
return CipherFileIO_iface;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CipherFileIO::open( int flags )
|
|
||||||
{
|
|
||||||
int res = base->open( flags );
|
|
||||||
|
|
||||||
if( res >= 0 )
|
|
||||||
lastFlags = flags;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
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->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;
|
|
||||||
|
|
||||||
ssize_t readSize = 0;
|
|
||||||
IORequest tmpReq = req;
|
|
||||||
|
|
||||||
if(haveHeader)
|
|
||||||
tmpReq.offset += HEADER_SIZE;
|
|
||||||
readSize = base->read( tmpReq );
|
|
||||||
|
|
||||||
bool ok;
|
|
||||||
if(readSize > 0)
|
|
||||||
{
|
|
||||||
if(haveHeader && fileIV == 0)
|
|
||||||
const_cast<CipherFileIO*>(this)->initHeader();
|
|
||||||
|
|
||||||
if(readSize != bs)
|
|
||||||
{
|
|
||||||
ok = streamRead( tmpReq.data, (int)readSize, blockNum ^ fileIV);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
ok = blockRead( tmpReq.data, (int)readSize, blockNum ^ fileIV);
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
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::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 );
|
|
||||||
|
|
||||||
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::truncate( 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// can't let BlockFileIO call base->truncate(), since it would be using
|
|
||||||
// the wrong size..
|
|
||||||
res = BlockFileIO::truncate( size, 0 );
|
|
||||||
|
|
||||||
if(res == 0)
|
|
||||||
base->truncate( size + HEADER_SIZE );
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CipherFileIO::isWritable() const
|
|
||||||
{
|
|
||||||
return base->isWritable();
|
|
||||||
}
|
|
||||||
|
|
@ -1,162 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ConfigReader.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
int size = stbuf.st_size;
|
|
||||||
|
|
||||||
int fd = open( fileName, O_RDONLY );
|
|
||||||
if(fd < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
char *buf = new char[size];
|
|
||||||
|
|
||||||
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 );
|
|
||||||
delete[] buf;
|
|
||||||
|
|
||||||
return loadFromVar( in );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
ConfigReader::loadFromVar(ConfigVar &in)
|
|
||||||
{
|
|
||||||
in.resetOffset();
|
|
||||||
|
|
||||||
// parse.
|
|
||||||
int numEntries = in.readInt();
|
|
||||||
|
|
||||||
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 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigVar
|
|
||||||
ConfigReader::toVar() const
|
|
||||||
{
|
|
||||||
// write everything to a ConfigVar, then output to disk
|
|
||||||
ConfigVar out;
|
|
||||||
out.writeInt( vars.size() );
|
|
||||||
map<string, ConfigVar>::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;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigVar ConfigReader::operator[] ( const std::string &varName ) const
|
|
||||||
{
|
|
||||||
// read only
|
|
||||||
map<string, ConfigVar>::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 ];
|
|
||||||
}
|
|
||||||
|
|
@ -1,252 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ConfigVar.h"
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
#ifndef MIN
|
|
||||||
inline int MIN(int a, int b)
|
|
||||||
{
|
|
||||||
return (a < b) ? a : b;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
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 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;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
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::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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
pd->offset += bytes;
|
|
||||||
|
|
||||||
return 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];
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
rAssert( offset < bytes );
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
unsigned char tmp = buf[offset++];
|
|
||||||
highBitSet = tmp & 0x80;
|
|
||||||
|
|
||||||
value = (value << 7) | (int)(tmp & 0x7f);
|
|
||||||
} while(highBitSet && offset < bytes);
|
|
||||||
|
|
||||||
pd->offset = offset;
|
|
||||||
|
|
||||||
// should never end up with a negative number..
|
|
||||||
rAssert( value >= 0 );
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ConfigVar::readInt( int defaultValue ) const
|
|
||||||
{
|
|
||||||
int bytes = this->size();
|
|
||||||
int offset = at();
|
|
||||||
|
|
||||||
if(offset >= bytes)
|
|
||||||
return defaultValue;
|
|
||||||
else
|
|
||||||
return readInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
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, int var)
|
|
||||||
{
|
|
||||||
src.writeInt( var );
|
|
||||||
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, int &result)
|
|
||||||
{
|
|
||||||
result = src.readInt();
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ConfigVar & operator >> (const ConfigVar &src, std::string &result)
|
|
||||||
{
|
|
||||||
int length = src.readInt();
|
|
||||||
//rAssert(length > 0);
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if(readLen != length)
|
|
||||||
{
|
|
||||||
rDebug("string encoded as size %i bytes, read %i", length, readLen );
|
|
||||||
}
|
|
||||||
rAssert(readLen == length);
|
|
||||||
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
|
|
@ -1,177 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2007, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "FileNode.h"
|
|
||||||
#include "Context.h"
|
|
||||||
#include "Mutex.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
#include "DirNode.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
usageCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
EncFS_Context::~EncFS_Context()
|
|
||||||
{
|
|
||||||
pthread_mutex_destroy( &contextMutex );
|
|
||||||
pthread_mutex_destroy( &wakeupMutex );
|
|
||||||
pthread_cond_destroy( &wakeupCond );
|
|
||||||
|
|
||||||
// release all entries from map
|
|
||||||
openFiles.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<DirNode> EncFS_Context::getRoot(int *errCode)
|
|
||||||
{
|
|
||||||
shared_ptr<DirNode> 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<DirNode> &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<FileNode> 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<FileNode>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Placeholder *> val = it->second;
|
|
||||||
openFiles.erase(it);
|
|
||||||
openFiles[ std::string(to) ] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<FileNode> EncFS_Context::getNode(void *pl)
|
|
||||||
{
|
|
||||||
Placeholder *ph = (Placeholder*)pl;
|
|
||||||
return ph->node;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *EncFS_Context::putNode(const char *path,
|
|
||||||
const shared_ptr<FileNode> &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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,831 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2003-2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "encfs.h"
|
|
||||||
|
|
||||||
#include "DirNode.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#ifdef linux
|
|
||||||
#include <sys/fsuid.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "Context.h"
|
|
||||||
#include "Cipher.h"
|
|
||||||
#include "Mutex.h"
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace rel;
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
static RLogChannel *Info = DEF_CHANNEL( "info/DirNode", Log_Info );
|
|
||||||
|
|
||||||
class DirDeleter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void operator () ( DIR *d )
|
|
||||||
{
|
|
||||||
::closedir( d );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
DirTraverse::DirTraverse(const shared_ptr<DIR> &_dirPtr,
|
|
||||||
uint64_t _iv, const shared_ptr<NameIO> &_naming)
|
|
||||||
: dir( _dirPtr )
|
|
||||||
, iv( _iv )
|
|
||||||
, naming( _naming )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTraverse::DirTraverse(const DirTraverse &src)
|
|
||||||
: 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()
|
|
||||||
{
|
|
||||||
dir.reset();
|
|
||||||
iv = 0;
|
|
||||||
naming.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
|
||||||
bool _nextName(struct dirent *&de, const shared_ptr<DIR> &dir,
|
|
||||||
int *fileType, ino_t *inode)
|
|
||||||
{
|
|
||||||
de = ::readdir( dir.get() );
|
|
||||||
|
|
||||||
if(de)
|
|
||||||
{
|
|
||||||
if(fileType)
|
|
||||||
{
|
|
||||||
#if defined(_DIRENT_HAVE_D_TYPE) || defined(__FreeBSD__)
|
|
||||||
*fileType = de->d_type;
|
|
||||||
#else
|
|
||||||
#warning "struct dirent.d_type not supported"
|
|
||||||
*fileType = 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct RenameEl
|
|
||||||
{
|
|
||||||
// ciphertext names
|
|
||||||
string oldCName;
|
|
||||||
string newCName; // intermediate name (not final cname)
|
|
||||||
|
|
||||||
// plaintext names
|
|
||||||
string oldPName;
|
|
||||||
string newPName;
|
|
||||||
|
|
||||||
bool isDirectory;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RenameOp
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
DirNode *dn;
|
|
||||||
shared_ptr< list<RenameEl> > renameList;
|
|
||||||
list<RenameEl>::const_iterator last;
|
|
||||||
|
|
||||||
public:
|
|
||||||
RenameOp( DirNode *_dn, const shared_ptr< list<RenameEl> > &_renameList )
|
|
||||||
: dn(_dn), renameList(_renameList)
|
|
||||||
{
|
|
||||||
last = renameList->begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
RenameOp(const RenameOp &src)
|
|
||||||
: dn(src.dn)
|
|
||||||
, renameList(src.renameList)
|
|
||||||
, last(src.last)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~RenameOp();
|
|
||||||
|
|
||||||
operator bool () const
|
|
||||||
{
|
|
||||||
return renameList;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool apply();
|
|
||||||
void undo();
|
|
||||||
};
|
|
||||||
|
|
||||||
RenameOp::~RenameOp()
|
|
||||||
{
|
|
||||||
if(renameList)
|
|
||||||
{
|
|
||||||
// got a bunch of decoded filenames sitting in memory.. do a little
|
|
||||||
// cleanup before leaving..
|
|
||||||
list<RenameEl>::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());
|
|
||||||
|
|
||||||
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() );
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
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<RenameEl>::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<DIR>(), 0, shared_ptr<NameIO>() );
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
shared_ptr<DIR> 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<RenameEl> &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> dir = shared_ptr<DIR>(
|
|
||||||
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
|
|
||||||
directory initialization vector chaining, then we have to recursively
|
|
||||||
rename every descendent of this directory, as all initialization vectors
|
|
||||||
will have changed..
|
|
||||||
|
|
||||||
Returns a list of renamed items on success, a null list on failure.
|
|
||||||
*/
|
|
||||||
shared_ptr<RenameOp>
|
|
||||||
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<RenameEl> > renameList(new list<RenameEl>);
|
|
||||||
if(!genRenameList( *renameList.get(), fromP, toP ))
|
|
||||||
{
|
|
||||||
rWarning("Error during generation of recursive rename list");
|
|
||||||
return shared_ptr<RenameOp>();
|
|
||||||
} else
|
|
||||||
return shared_ptr<RenameOp>( 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() );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
int res = ::mkdir( cyName.c_str(), mode );
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<FileNode> toNode = findOrCreate( toPlaintext );
|
|
||||||
|
|
||||||
shared_ptr<RenameOp> 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;
|
|
||||||
|
|
||||||
renameNode( fromPlaintext, toPlaintext );
|
|
||||||
res = ::rename( fromCName.c_str(), toCName.c_str() );
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DirNode::link( const char *from, const char *to )
|
|
||||||
{
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
string fromCName = rootDir + naming->encodePath( from );
|
|
||||||
string toCName = rootDir + naming->encodePath( to );
|
|
||||||
|
|
||||||
rAssert( !fromCName.empty() );
|
|
||||||
rAssert( !toCName.empty() );
|
|
||||||
|
|
||||||
rLog(Info, "link %s -> %s", fromCName.c_str(), toCName.c_str());
|
|
||||||
|
|
||||||
int res = -EPERM;
|
|
||||||
if( fsConfig->config->external_iv() )
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The node is keyed by filename, so a rename means the internal node names
|
|
||||||
must be changed.
|
|
||||||
*/
|
|
||||||
shared_ptr<FileNode> DirNode::renameNode( const char *from, const char *to )
|
|
||||||
{
|
|
||||||
return renameNode( from, to, true );
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<FileNode> DirNode::renameNode( const char *from, const char *to,
|
|
||||||
bool forwardMode )
|
|
||||||
{
|
|
||||||
shared_ptr<FileNode> node = findOrCreate( from );
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<FileNode> DirNode::findOrCreate( const char *plainName)
|
|
||||||
{
|
|
||||||
shared_ptr<FileNode> 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->external_iv())
|
|
||||||
node->setName(0, 0, iv);
|
|
||||||
|
|
||||||
rLog(Info, "created FileNode for %s", node->cipherName());
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<FileNode>
|
|
||||||
DirNode::lookupNode( const char *plainName, const char * requestor )
|
|
||||||
{
|
|
||||||
(void)requestor;
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
shared_ptr<FileNode> node = findOrCreate( plainName );
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Similar to lookupNode, except that we also call open() and only return a
|
|
||||||
node on sucess.. This is done in one step to avoid any race conditions
|
|
||||||
with the stored state of the file.
|
|
||||||
*/
|
|
||||||
shared_ptr<FileNode>
|
|
||||||
DirNode::openNode( const char *plainName, const char * requestor, int flags,
|
|
||||||
int *result )
|
|
||||||
{
|
|
||||||
(void)requestor;
|
|
||||||
rAssert( result != NULL );
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
shared_ptr<FileNode> node = findOrCreate( plainName );
|
|
||||||
|
|
||||||
if( node && (*result = node->open( flags )) >= 0 )
|
|
||||||
return node;
|
|
||||||
else
|
|
||||||
return shared_ptr<FileNode>();
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
@ -1,306 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2003-2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include encfs first, because we need to include fuse.h before any inclusion
|
|
||||||
// of sys/stat.h or other system headers (to be safe)
|
|
||||||
#include "encfs.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#ifdef linux
|
|
||||||
#include <sys/fsuid.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "FileNode.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
#include "Cipher.h"
|
|
||||||
#include "CipherFileIO.h"
|
|
||||||
#include "RawFileIO.h"
|
|
||||||
#include "MACFileIO.h"
|
|
||||||
#include "DirNode.h"
|
|
||||||
|
|
||||||
#include "FileIO.h"
|
|
||||||
#include "MemoryPool.h"
|
|
||||||
#include "Mutex.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace rel;
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
/*
|
|
||||||
TODO: locking at the FileNode level is inefficient, since this precludes
|
|
||||||
multiple IO operations from going on concurrently within the same file.
|
|
||||||
|
|
||||||
There is no reason why simultainous reads cannot be satisfied, or why one
|
|
||||||
read has to wait for the decoding of the previous read before it can be
|
|
||||||
sent to the IO subsystem!
|
|
||||||
*/
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
this->_pname = plaintextName_;
|
|
||||||
this->_cname = cipherName_;
|
|
||||||
this->parent = parent_;
|
|
||||||
|
|
||||||
this->fsConfig = cfg;
|
|
||||||
|
|
||||||
// chain RawFileIO & CipherFileIO
|
|
||||||
shared_ptr<FileIO> rawIO( new RawFileIO( _cname ) );
|
|
||||||
io = shared_ptr<FileIO>( new CipherFileIO( rawIO, fsConfig ));
|
|
||||||
|
|
||||||
if(cfg->config->block_mac_bytes() || cfg->config->block_mac_rand_bytes())
|
|
||||||
io = shared_ptr<FileIO>(new MACFileIO(io, fsConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
pthread_mutex_destroy( &mutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *FileNode::cipherName() const
|
|
||||||
{
|
|
||||||
return _cname.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *FileNode::plaintextName() const
|
|
||||||
{
|
|
||||||
return _pname.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
string FileNode::plaintextParent() const
|
|
||||||
{
|
|
||||||
return parentDirectory( _pname );
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool setIV(const shared_ptr<FileIO> &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->external_iv() && !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->external_iv() && !setIV(io, iv))
|
|
||||||
{
|
|
||||||
_pname = oldPName;
|
|
||||||
_cname = oldCName;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid)
|
|
||||||
{
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
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::open(int flags) const
|
|
||||||
{
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
int res = io->open( flags );
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FileNode::getAttr(struct stat *stbuf) const
|
|
||||||
{
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
int res = io->getAttr( stbuf );
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
off_t FileNode::getSize() const
|
|
||||||
{
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
int res = io->getSize();
|
|
||||||
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;
|
|
||||||
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
return io->read( req );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileNode::write(off_t offset, unsigned char *data, ssize_t size)
|
|
||||||
{
|
|
||||||
rLog(Info, "FileNode::write offset %" PRIi64 ", data size %i",
|
|
||||||
offset, (int)size);
|
|
||||||
|
|
||||||
IORequest req;
|
|
||||||
req.offset = offset;
|
|
||||||
req.dataLen = size;
|
|
||||||
req.data = data;
|
|
||||||
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
return io->write( req );
|
|
||||||
}
|
|
||||||
|
|
||||||
int FileNode::truncate( off_t size )
|
|
||||||
{
|
|
||||||
Lock _lock( mutex );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
#else
|
|
||||||
// no fdatasync support
|
|
||||||
// TODO: use autoconfig to check for it..
|
|
||||||
res = fsync(fh);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(res == -1)
|
|
||||||
res = -errno;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
} else
|
|
||||||
return fh;
|
|
||||||
}
|
|
||||||
|
|
@ -1,304 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "MACFileIO.h"
|
|
||||||
|
|
||||||
#include "MemoryPool.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
#include "config.pb.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
#include <rlog/RLogChannel.h>
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "i18n.h"
|
|
||||||
|
|
||||||
using namespace rlog;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static RLogChannel *Info = DEF_CHANNEL("info/MACFileIO", Log_Info);
|
|
||||||
//
|
|
||||||
// Version 1.0 worked on blocks of size (blockSize + headerSize).
|
|
||||||
// That is, it took [blockSize] worth of user data and added headers.
|
|
||||||
// Version 2.0 takes [blockSize - headerSize] worth of user data and writes
|
|
||||||
// [blockSize] bytes. That way the size going into the crypto engine is
|
|
||||||
// valid from what was selected based on the crypto module allowed ranges!
|
|
||||||
// Version 2.1 allows per-block rand bytes to be used without enabling MAC.
|
|
||||||
//
|
|
||||||
// The information about MACFileIO currently does not make its way into the
|
|
||||||
// configuration file, so there is no easy way to make this backward
|
|
||||||
// compatible, except at a high level by checking a revision number for the
|
|
||||||
// filesystem...
|
|
||||||
//
|
|
||||||
static Interface MACFileIO_iface = makeInterface("FileIO/MAC", 2, 1, 0);
|
|
||||||
|
|
||||||
int dataBlockSize(const FSConfigPtr &cfg)
|
|
||||||
{
|
|
||||||
return cfg->config->block_size()
|
|
||||||
- cfg->config->block_mac_bytes()
|
|
||||||
- cfg->config->block_mac_rand_bytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
MACFileIO::MACFileIO( const shared_ptr<FileIO> &_base,
|
|
||||||
const FSConfigPtr &cfg )
|
|
||||||
: BlockFileIO( dataBlockSize( cfg ), cfg )
|
|
||||||
, base( _base )
|
|
||||||
, cipher( cfg->cipher )
|
|
||||||
, key( cfg->key )
|
|
||||||
, macBytes( cfg->config->block_mac_bytes() )
|
|
||||||
, randBytes( cfg->config->block_mac_rand_bytes() )
|
|
||||||
, warnOnly( cfg->opts->forceDecode )
|
|
||||||
{
|
|
||||||
rAssert( macBytes >= 0 && macBytes <= 8 );
|
|
||||||
rAssert( randBytes >= 0 );
|
|
||||||
rLog(Info, "fs block size = %i, macBytes = %i, randBytes = %i",
|
|
||||||
cfg->config->block_size(),
|
|
||||||
cfg->config->block_mac_bytes(),
|
|
||||||
cfg->config->block_mac_rand_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
MACFileIO::~MACFileIO()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert from a location in the raw file to a location when MAC headers are
|
|
||||||
// interleved with the data.
|
|
||||||
// So, if the filesystem stores/encrypts [blockSize] bytes per block, then
|
|
||||||
// [blockSize - headerSize] of those bytes will contain user-supplied data,
|
|
||||||
// and the rest ([headerSize]) will contain the MAC header for this block.
|
|
||||||
// Example, offset points to second block (of user-data)
|
|
||||||
// offset = blockSize - headerSize
|
|
||||||
// ... 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert from a given location in the stream containing headers, and return a
|
|
||||||
// location in the user-data stream (which doesn't contain MAC headers)..
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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..
|
|
||||||
int headerSize = macBytes + randBytes;
|
|
||||||
int bs = blockSize() + headerSize;
|
|
||||||
|
|
||||||
off_t size = base->getSize();
|
|
||||||
if(size > 0)
|
|
||||||
size = locWithoutHeader( size, bs, headerSize );
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
|
|
||||||
{
|
|
||||||
int headerSize = macBytes + randBytes;
|
|
||||||
|
|
||||||
int bs = blockSize() + headerSize;
|
|
||||||
|
|
||||||
MemBlock mb = MemoryPool::allocate( bs );
|
|
||||||
|
|
||||||
IORequest tmp;
|
|
||||||
tmp.offset = locWithHeader( req.offset, bs, headerSize );
|
|
||||||
tmp.data = mb.data;
|
|
||||||
tmp.dataLen = headerSize + req.dataLen;
|
|
||||||
|
|
||||||
// get the data from the base FileIO layer
|
|
||||||
ssize_t readSize = base->read( tmp );
|
|
||||||
|
|
||||||
// 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(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 );
|
|
||||||
|
|
||||||
for(int i=0; i<macBytes; ++i, mac >>= 8)
|
|
||||||
{
|
|
||||||
int test = mac & 0xff;
|
|
||||||
int stored = tmp.data[i];
|
|
||||||
if(test != stored)
|
|
||||||
{
|
|
||||||
// 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"));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 );
|
|
||||||
|
|
||||||
return readSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MACFileIO::writeOneBlock( const IORequest &req )
|
|
||||||
{
|
|
||||||
int headerSize = macBytes + randBytes;
|
|
||||||
|
|
||||||
int bs = blockSize() + headerSize;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now, we can let the next level have it..
|
|
||||||
bool ok = base->write( newReq );
|
|
||||||
|
|
||||||
MemoryPool::release( mb );
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MACFileIO::truncate( off_t size )
|
|
||||||
{
|
|
||||||
int headerSize = macBytes + randBytes;
|
|
||||||
int bs = blockSize() + headerSize;
|
|
||||||
|
|
||||||
int res = BlockFileIO::truncate( size, 0 );
|
|
||||||
|
|
||||||
if(res == 0)
|
|
||||||
base->truncate( locWithHeader( size, bs, headerSize ) );
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MACFileIO::isWritable() const
|
|
||||||
{
|
|
||||||
return base->isWritable();
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
|
|
||||||
include $(top_srcdir)/Makefile.common
|
|
||||||
|
|
||||||
ALL_INCLUDES = @RLOG_CFLAGS@ @OPENSSL_CFLAGS@
|
|
||||||
ALL_INCLUDES += @PROTOBUF_CFLAGS@
|
|
||||||
ALL_LDFLAGS = @RLOG_LIBS@ @OPENSSL_LIBS@
|
|
||||||
ALL_LDFLAGS += @PROTOBUF_LIBS@
|
|
||||||
|
|
||||||
INCLUDES = $(all_includes) -I../intl
|
|
||||||
|
|
||||||
AM_CXXFLAGS = -DRLOG_COMPONENT="encfs" $(ALL_INCLUDES)
|
|
||||||
|
|
||||||
if DARWIN
|
|
||||||
# needed to select correct API in fuse.h
|
|
||||||
AM_CXXFLAGS += -D__FreeBSD__=10
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BUILD_NLS
|
|
||||||
# define a C macro LOCALEDIR indicating where catalogs will be installed
|
|
||||||
#localedir = $(datadir)/locale
|
|
||||||
|
|
||||||
AM_CXXFLAGS += -DLOCALEDIR=\"$(localedir)\"
|
|
||||||
ALL_LDFLAGS += @LIBINTL@
|
|
||||||
endif
|
|
||||||
|
|
||||||
lib_LTLIBRARIES = libencfs.la
|
|
||||||
bin_PROGRAMS = encfs encfsctl
|
|
||||||
dist_bin_SCRIPTS = encfssh
|
|
||||||
noinst_PROGRAMS = test makeKey
|
|
||||||
|
|
||||||
all-local: encfs-man.html
|
|
||||||
|
|
||||||
encfs_LDADD = libencfs.la $(ALL_LDFLAGS)
|
|
||||||
encfsctl_LDADD = libencfs.la $(ALL_LDFLAGS)
|
|
||||||
test_LDADD = libencfs.la $(ALL_LDFLAGS)
|
|
||||||
makeKey_LDADD = libencfs.la $(ALL_LDFLAGS)
|
|
||||||
|
|
||||||
if BUILD_STATIC
|
|
||||||
encfs_LDFLAGS = -all-static
|
|
||||||
encfsctl_LDFLAGS = -all-static
|
|
||||||
test_LDFLAGS = -all-static
|
|
||||||
makeKey_LDFLAGS = -all-static
|
|
||||||
endif
|
|
||||||
|
|
||||||
# CURRENT : REVISION : AGE
|
|
||||||
# +1 : 0 : +1 => new interface that does not break old one
|
|
||||||
# +1 : 0 : 0 => new interface that breaks old one
|
|
||||||
# : : 0 => no new interfaces, but breaks old apps
|
|
||||||
# : +1 : => internal changes, nothing breaks
|
|
||||||
#
|
|
||||||
libencfs_la_LDFLAGS = -version-info 7:0:0
|
|
||||||
libencfs_la_LIBADD = @RLOG_LIBS@ @OPENSSL_LIBS@
|
|
||||||
|
|
||||||
EXTRASRC = ../intl/autosprintf.cpp
|
|
||||||
if BUILD_OPENSSL
|
|
||||||
if BUILD_SSLCIPHER
|
|
||||||
EXTRASRC += SSL_Cipher.cpp
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
libencfs_la_SOURCES = \
|
|
||||||
readpassphrase.cpp \
|
|
||||||
base64.cpp \
|
|
||||||
config.pb.cc \
|
|
||||||
ConfigReader.cpp \
|
|
||||||
ConfigVar.cpp \
|
|
||||||
Context.cpp \
|
|
||||||
Cipher.cpp \
|
|
||||||
CipherKey.cpp \
|
|
||||||
FileIO.cpp \
|
|
||||||
RawFileIO.cpp \
|
|
||||||
BlockFileIO.cpp \
|
|
||||||
CipherFileIO.cpp \
|
|
||||||
MACFileIO.cpp \
|
|
||||||
NameIO.cpp \
|
|
||||||
StreamNameIO.cpp \
|
|
||||||
BlockNameIO.cpp \
|
|
||||||
NullNameIO.cpp \
|
|
||||||
Interface.cpp \
|
|
||||||
MemoryPool.cpp \
|
|
||||||
NullCipher.cpp \
|
|
||||||
DirNode.cpp \
|
|
||||||
FileNode.cpp \
|
|
||||||
FileUtils.cpp \
|
|
||||||
openssl.cpp \
|
|
||||||
XmlReader.cpp \
|
|
||||||
${EXTRASRC}
|
|
||||||
|
|
||||||
|
|
||||||
encfs_SOURCES = \
|
|
||||||
encfs.cpp \
|
|
||||||
main.cpp
|
|
||||||
|
|
||||||
test_SOURCES = \
|
|
||||||
test.cpp
|
|
||||||
|
|
||||||
makeKey_SOURCES = \
|
|
||||||
makeKey.cpp
|
|
||||||
|
|
||||||
encfsctl_SOURCES = \
|
|
||||||
encfsctl.cpp
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
|
||||||
base64.h \
|
|
||||||
BlockFileIO.h \
|
|
||||||
BlockNameIO.h \
|
|
||||||
CipherFileIO.h \
|
|
||||||
Cipher.h \
|
|
||||||
CipherKey.h \
|
|
||||||
ConfigReader.h \
|
|
||||||
ConfigVar.h \
|
|
||||||
config.pb.h \
|
|
||||||
Context.h \
|
|
||||||
DirNode.h \
|
|
||||||
encfs.h \
|
|
||||||
FileIO.h \
|
|
||||||
FileNode.h \
|
|
||||||
FileUtils.h \
|
|
||||||
FSConfig.h \
|
|
||||||
Interface.h \
|
|
||||||
i18n.h \
|
|
||||||
MACFileIO.h \
|
|
||||||
MemoryPool.h \
|
|
||||||
Mutex.h \
|
|
||||||
NameIO.h \
|
|
||||||
NullCipher.h \
|
|
||||||
NullNameIO.h \
|
|
||||||
openssl.h \
|
|
||||||
Range.h \
|
|
||||||
RawFileIO.h \
|
|
||||||
readpassphrase.h \
|
|
||||||
SSL_Cipher.h \
|
|
||||||
StreamNameIO.h
|
|
||||||
|
|
||||||
man_MANS=encfs.1 encfsctl.1
|
|
||||||
EXTRA_DIST = encfs.pod encfsctl.pod encfs.1 encfsctl.1 encfs-man.html
|
|
||||||
|
|
||||||
SUFFIXES = .1 .pod .proto
|
|
||||||
|
|
||||||
config.pb.cc: config.pb.h
|
|
||||||
|
|
||||||
config.pb.h: config.proto
|
|
||||||
@PROTOC@ --cpp_out=. config.proto
|
|
||||||
|
|
||||||
if BUILD_MAN
|
|
||||||
# since we have POD2MAN, we can specify how to rebuild encfs.1 if necessary
|
|
||||||
.pod.1:
|
|
||||||
@POD2MAN@ --section=1 --release=@VERSION@ --center="Encrypted Filesystem" $< $@
|
|
||||||
|
|
||||||
CLEANFILES = encfs.1 encfsctl.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
if BUILD_MANHTML
|
|
||||||
encfs-man.html: encfs.pod
|
|
||||||
@POD2HTML@ encfs.pod > $@
|
|
||||||
endif
|
|
||||||
|
|
||||||
tests:
|
|
||||||
perl -MTest::Harness -e '$$Test::Harness::verbose=0; runtests @ARGV;' *.t
|
|
||||||
|
|
||||||
tests-verbose:
|
|
||||||
perl -MTest::Harness -e '$$Test::Harness::verbose=1; runtests @ARGV;' *.t
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2003, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "MemoryPool.h"
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
|
||||||
#include <valgrind/memcheck.h>
|
|
||||||
#else
|
|
||||||
#define VALGRIND_MAKE_MEM_NOACCESS( a, b )
|
|
||||||
#define VALGRIND_MAKE_MEM_UNDEFINED( a, b )
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
# include <openssl/crypto.h>
|
|
||||||
# include <openssl/buffer.h>
|
|
||||||
#define BLOCKDATA( BLOCK ) (unsigned char*)BLOCK->data->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 );
|
|
||||||
|
|
||||||
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 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// unlink block from list
|
|
||||||
if(block)
|
|
||||||
{
|
|
||||||
if(!parent)
|
|
||||||
gMemPool = block->next;
|
|
||||||
else
|
|
||||||
parent->next = block->next;
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock( &gMPoolMutex );
|
|
||||||
|
|
||||||
if(!block)
|
|
||||||
block = allocBlock( size );
|
|
||||||
block->next = NULL;
|
|
||||||
|
|
||||||
MemBlock result;
|
|
||||||
result.data = BLOCKDATA(block);
|
|
||||||
result.internalData = block;
|
|
||||||
|
|
||||||
VALGRIND_MAKE_MEM_UNDEFINED( result.data, size );
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryPool::release( const MemBlock &mb )
|
|
||||||
{
|
|
||||||
pthread_mutex_lock( &gMPoolMutex );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
block->next = gMemPool;
|
|
||||||
gMemPool = block;
|
|
||||||
|
|
||||||
pthread_mutex_unlock( &gMPoolMutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryPool::destroyAll()
|
|
||||||
{
|
|
||||||
pthread_mutex_lock( &gMPoolMutex );
|
|
||||||
|
|
||||||
BlockList *block = gMemPool;
|
|
||||||
gMemPool = NULL;
|
|
||||||
|
|
||||||
pthread_mutex_unlock( &gMPoolMutex );
|
|
||||||
|
|
||||||
while(block != NULL)
|
|
||||||
{
|
|
||||||
BlockList *next = block->next;
|
|
||||||
|
|
||||||
freeBlock( block );
|
|
||||||
block = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SecureMem::SecureMem(int len)
|
|
||||||
{
|
|
||||||
data = (char *)OPENSSL_malloc(len);
|
|
||||||
if (data)
|
|
||||||
{
|
|
||||||
size = len;
|
|
||||||
mlock(data, size);
|
|
||||||
memset(data, '\0', size);
|
|
||||||
VALGRIND_MAKE_MEM_UNDEFINED( data, size );
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SecureMem::~SecureMem()
|
|
||||||
{
|
|
||||||
if (size)
|
|
||||||
{
|
|
||||||
memset(data, '\0', size);
|
|
||||||
munlock(data, size);
|
|
||||||
|
|
||||||
OPENSSL_free(data);
|
|
||||||
VALGRIND_MAKE_MEM_NOACCESS( data, size );
|
|
||||||
|
|
||||||
data = NULL;
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
351
encfs/NameIO.cpp
351
encfs/NameIO.cpp
@ -1,351 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "NameIO.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
// for static build. Need to reference the modules which are registered at
|
|
||||||
// run-time, to ensure that the linker doesn't optimize them away.
|
|
||||||
#include <iostream>
|
|
||||||
#include "BlockNameIO.h"
|
|
||||||
#include "StreamNameIO.h"
|
|
||||||
#include "NullNameIO.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
#define REF_MODULE(TYPE) \
|
|
||||||
if(!TYPE::Enabled() ) \
|
|
||||||
cerr << "referenceModule: should never happen\n";
|
|
||||||
|
|
||||||
static
|
|
||||||
void AddSymbolReferences()
|
|
||||||
{
|
|
||||||
REF_MODULE(BlockNameIO)
|
|
||||||
REF_MODULE(StreamNameIO)
|
|
||||||
REF_MODULE(NullNameIO)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct NameIOAlg
|
|
||||||
{
|
|
||||||
bool hidden;
|
|
||||||
NameIO::Constructor constructor;
|
|
||||||
string description;
|
|
||||||
Interface iface;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef multimap< string, NameIOAlg > NameIOMap_t;
|
|
||||||
static NameIOMap_t *gNameIOMap = 0;
|
|
||||||
|
|
||||||
|
|
||||||
list< NameIO::Algorithm >
|
|
||||||
NameIO::GetAlgorithmList( bool includeHidden )
|
|
||||||
{
|
|
||||||
AddSymbolReferences();
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
gNameIOMap->insert( make_pair( string(name), alg ));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<NameIO> NameIO::New( const string &name,
|
|
||||||
const shared_ptr<Cipher> &cipher, const CipherKey &key)
|
|
||||||
{
|
|
||||||
shared_ptr<NameIO> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_ptr<NameIO> NameIO::New( const Interface &iface,
|
|
||||||
const shared_ptr<Cipher> &cipher, const CipherKey &key )
|
|
||||||
{
|
|
||||||
shared_ptr<NameIO> result;
|
|
||||||
if(gNameIOMap)
|
|
||||||
{
|
|
||||||
NameIOMap_t::const_iterator it;
|
|
||||||
NameIOMap_t::const_iterator end = gNameIOMap->end();
|
|
||||||
for(it = gNameIOMap->begin(); it != end; ++it)
|
|
||||||
{
|
|
||||||
if( implements(it->second.iface, iface ))
|
|
||||||
{
|
|
||||||
Constructor fn = it->second.constructor;
|
|
||||||
result = (*fn)( iface, cipher, key );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
NameIO::NameIO()
|
|
||||||
: chainedNameIV( false ), reverseEncryption( false )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
NameIO::~NameIO()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameIO::setChainedNameIV( bool enable )
|
|
||||||
{
|
|
||||||
chainedNameIV = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NameIO::getChainedNameIV() const
|
|
||||||
{
|
|
||||||
return chainedNameIV;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NameIO::setReverseEncryption( bool enable )
|
|
||||||
{
|
|
||||||
reverseEncryption = enable;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NameIO::getReverseEncryption() const
|
|
||||||
{
|
|
||||||
return reverseEncryption;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// figure out buffer sizes
|
|
||||||
int approxLen = (this->*_length)( len );
|
|
||||||
if(approxLen <= 0)
|
|
||||||
throw ERROR("Filename too small to decode");
|
|
||||||
|
|
||||||
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 )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
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::_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::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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
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 *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 *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
|
|
||||||
{
|
|
||||||
return getReverseEncryption() ?
|
|
||||||
_decodeName( path, length, output ) :
|
|
||||||
_encodeName( path, length, output );
|
|
||||||
}
|
|
||||||
|
|
||||||
int NameIO::decodeName( const char *path, int length,
|
|
||||||
char *output ) const
|
|
||||||
{
|
|
||||||
return getReverseEncryption() ?
|
|
||||||
_encodeName( path, length, output ) :
|
|
||||||
_decodeName( path, length, output );
|
|
||||||
}
|
|
||||||
*/
|
|
@ -1,325 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef linux
|
|
||||||
#define _XOPEN_SOURCE 500 // pick up pread , pwrite
|
|
||||||
#endif
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "RawFileIO.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static Interface RawFileIO_iface = makeInterface("FileIO/Raw", 1, 0, 0);
|
|
||||||
|
|
||||||
FileIO *NewRawFileIO( const Interface &iface )
|
|
||||||
{
|
|
||||||
(void)iface;
|
|
||||||
return new RawFileIO();
|
|
||||||
}
|
|
||||||
|
|
||||||
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( 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
Interface RawFileIO::interface() const
|
|
||||||
{
|
|
||||||
return RawFileIO_iface;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Workaround for opening a file for write when permissions don't allow.
|
|
||||||
Since the kernel has already checked permissions, we can assume it is ok to
|
|
||||||
provide access. So force it by changing permissions temporarily. Should
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
We shouldn't have to support all possible open flags, so untaint the flags
|
|
||||||
argument by only taking ones we understand and accept.
|
|
||||||
- Since the kernel has already done permission tests before calling us, we
|
|
||||||
shouldn't have to worry about access control.
|
|
||||||
- Basically we just need to distinguish between read and write 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));
|
|
||||||
|
|
||||||
rDebug("open call for %s file", requestWrite ? "writable" : "read only");
|
|
||||||
|
|
||||||
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 defined(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);
|
|
||||||
|
|
||||||
if((newFd == -1) && (errno == EACCES))
|
|
||||||
{
|
|
||||||
rDebug("using readonly workaround for open");
|
|
||||||
newFd = open_readonly_workaround( name.c_str(), finalFlags );
|
|
||||||
}
|
|
||||||
|
|
||||||
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(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;
|
|
||||||
|
|
||||||
if(res < 0)
|
|
||||||
rInfo("getAttr error on %s: %s", name.c_str(), strerror( eno ));
|
|
||||||
|
|
||||||
return ( res < 0 ) ? -eno : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RawFileIO::setFileName( const char *fileName )
|
|
||||||
{
|
|
||||||
name = fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
if(res == 0)
|
|
||||||
{
|
|
||||||
const_cast<RawFileIO*>(this)->fileSize = stbuf.st_size;
|
|
||||||
const_cast<RawFileIO*>(this)->knownSize = true;
|
|
||||||
return fileSize;
|
|
||||||
} else
|
|
||||||
return -1;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
return fileSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t RawFileIO::read( const IORequest &req ) const
|
|
||||||
{
|
|
||||||
rAssert( fd >= 0 );
|
|
||||||
|
|
||||||
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 ));
|
|
||||||
}
|
|
||||||
|
|
||||||
return readSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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(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;
|
|
||||||
|
|
||||||
if(fd >= 0 && canWrite)
|
|
||||||
{
|
|
||||||
res = ::ftruncate( fd, size );
|
|
||||||
#ifndef __FreeBSD__
|
|
||||||
::fdatasync( fd );
|
|
||||||
#endif
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RawFileIO::isWritable() const
|
|
||||||
{
|
|
||||||
return canWrite;
|
|
||||||
}
|
|
@ -1,927 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "encfs.h"
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <openssl/blowfish.h>
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include <openssl/err.h>
|
|
||||||
#include <openssl/hmac.h>
|
|
||||||
|
|
||||||
#include "SSL_Cipher.h"
|
|
||||||
#include "Range.h"
|
|
||||||
#include "MemoryPool.h"
|
|
||||||
#include "Mutex.h"
|
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
|
|
||||||
#include "i18n.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace rel;
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
This produces the same result as OpenSSL's EVP_BytesToKey. The difference
|
|
||||||
is that here we can explicitly specify the key size, instead of relying on
|
|
||||||
the state of EVP_CIPHER struct. EVP_BytesToKey will only produce 128 bit
|
|
||||||
keys for the EVP Blowfish interface, which is not what we want.
|
|
||||||
|
|
||||||
DEPRECATED: this is here for backward compatibilty only. Use PBKDF
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
|
|
||||||
EVP_MD_CTX cx;
|
|
||||||
EVP_MD_CTX_init( &cx );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
int offset = 0;
|
|
||||||
int toCopy = MIN( nkey, (int)mds - offset );
|
|
||||||
if( toCopy )
|
|
||||||
{
|
|
||||||
memcpy( key, mdBuf+offset, toCopy );
|
|
||||||
key += toCopy;
|
|
||||||
nkey -= toCopy;
|
|
||||||
offset += toCopy;
|
|
||||||
}
|
|
||||||
toCopy = MIN( niv, (int)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<unsigned char*>(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.
|
|
||||||
// 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 = makeInterface( "ssl/blowfish", 3, 0, 2 );
|
|
||||||
static Interface AESInterface = makeInterface( "ssl/aes", 3, 0, 2 );
|
|
||||||
|
|
||||||
#if defined(HAVE_EVP_BF)
|
|
||||||
|
|
||||||
static Range BFKeyRange(128,256,32);
|
|
||||||
static Range BFBlockRange(64,4096,8);
|
|
||||||
|
|
||||||
static shared_ptr<Cipher> NewBFCipher( const Interface &iface, int keyLen )
|
|
||||||
{
|
|
||||||
if( keyLen <= 0 )
|
|
||||||
keyLen = 160;
|
|
||||||
|
|
||||||
keyLen = BFKeyRange.closest( keyLen );
|
|
||||||
|
|
||||||
const EVP_CIPHER *blockCipher = EVP_bf_cbc();
|
|
||||||
const EVP_CIPHER *streamCipher = EVP_bf_cfb();
|
|
||||||
|
|
||||||
return shared_ptr<Cipher>( 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);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(HAVE_EVP_AES)
|
|
||||||
|
|
||||||
static Range AESKeyRange(128,256,64);
|
|
||||||
static Range AESBlockRange(64,4096,16);
|
|
||||||
|
|
||||||
static shared_ptr<Cipher> NewAESCipher( const Interface &iface, int keyLen )
|
|
||||||
{
|
|
||||||
if( keyLen <= 0 )
|
|
||||||
keyLen = 192;
|
|
||||||
|
|
||||||
keyLen = AESKeyRange.closest( keyLen );
|
|
||||||
|
|
||||||
const EVP_CIPHER *blockCipher = 0;
|
|
||||||
const EVP_CIPHER *streamCipher = 0;
|
|
||||||
|
|
||||||
switch(keyLen)
|
|
||||||
{
|
|
||||||
case 128:
|
|
||||||
blockCipher = EVP_aes_128_cbc();
|
|
||||||
streamCipher = EVP_aes_128_cfb();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 192:
|
|
||||||
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<Cipher>( 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);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class SSLKey : public AbstractCipherKey
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
pthread_mutex_t mutex;
|
|
||||||
|
|
||||||
unsigned int keySize; // in bytes
|
|
||||||
unsigned int ivLength;
|
|
||||||
|
|
||||||
// key data is first _keySize bytes,
|
|
||||||
// followed by iv of _ivLength bytes,
|
|
||||||
unsigned char *buffer;
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX block_enc;
|
|
||||||
EVP_CIPHER_CTX block_dec;
|
|
||||||
EVP_CIPHER_CTX stream_enc;
|
|
||||||
EVP_CIPHER_CTX stream_dec;
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
// most likely fails unless we're running as root, or a user-page-lock
|
|
||||||
// kernel patch is applied..
|
|
||||||
mlock( buffer, keySize + ivLength );
|
|
||||||
|
|
||||||
memset( buffer, 0, keySize + ivLength );
|
|
||||||
}
|
|
||||||
|
|
||||||
SSLKey::~SSLKey()
|
|
||||||
{
|
|
||||||
memset( buffer, 0, keySize + ivLength );
|
|
||||||
|
|
||||||
munlock( buffer, keySize + ivLength );
|
|
||||||
OPENSSL_free( buffer );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
HMAC_CTX_cleanup( &mac_ctx );
|
|
||||||
|
|
||||||
pthread_mutex_destroy( &mutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
inline unsigned char* KeyData( const shared_ptr<SSLKey> &key )
|
|
||||||
{
|
|
||||||
return key->buffer;
|
|
||||||
}
|
|
||||||
inline unsigned char* IVData( const shared_ptr<SSLKey> &key )
|
|
||||||
{
|
|
||||||
return key->buffer + key->keySize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initKey(const shared_ptr<SSLKey> &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);
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
rLog(CipherInfo, "allocated cipher %s, keySize %i, ivlength %i",
|
|
||||||
iface.name().c_str(), _keySize, _ivLength);
|
|
||||||
|
|
||||||
if( (EVP_CIPHER_key_length( _blockCipher ) != (int )_keySize)
|
|
||||||
&& iface.major() == 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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Interface SSL_Cipher::interface() const
|
|
||||||
{
|
|
||||||
return realIface;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
create a key from the password.
|
|
||||||
Use SHA to distribute entropy from the password into the key.
|
|
||||||
|
|
||||||
This algorithm must remain constant for backward compatibility, as this key
|
|
||||||
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<SSLKey> 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<unsigned char*>(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<SSLKey> key( new SSLKey( _keySize, _ivLength) );
|
|
||||||
|
|
||||||
int bytes = 0;
|
|
||||||
if( iface.major() > 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);
|
|
||||||
}
|
|
||||||
} 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) );
|
|
||||||
}
|
|
||||||
|
|
||||||
initKey( key, _blockCipher, _streamCipher, _keySize );
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Create a random key.
|
|
||||||
We use the OpenSSL library to generate random bytes, then take the hash of
|
|
||||||
those bytes to use as the key.
|
|
||||||
|
|
||||||
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 ];
|
|
||||||
|
|
||||||
if(!randomize(tmpBuf, bufLen, true) ||
|
|
||||||
!randomize(saltBuf, saltLen, true))
|
|
||||||
return CipherKey();
|
|
||||||
|
|
||||||
shared_ptr<SSLKey> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
OPENSSL_cleanse(tmpBuf, bufLen);
|
|
||||||
|
|
||||||
initKey( key, _blockCipher, _streamCipher, _keySize );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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<SSLKey> 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<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
|
|
||||||
rAssert(key->keySize == _keySize);
|
|
||||||
rAssert(key->ivLength == _ivLength);
|
|
||||||
|
|
||||||
shared_ptr<SSLKey> mk = dynamic_pointer_cast<SSLKey>(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<SSLKey> key1 = dynamic_pointer_cast<SSLKey>(A);
|
|
||||||
shared_ptr<SSLKey> key2 = dynamic_pointer_cast<SSLKey>(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<SSLKey> &key) const
|
|
||||||
{
|
|
||||||
if (iface.major() >= 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Deprecated: For backward compatibility only.
|
|
||||||
A watermark attack was discovered against this IV setup. If an attacker
|
|
||||||
could get a victim to store a carefully crafted file, they could later
|
|
||||||
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<SSLKey> &key) const
|
|
||||||
{
|
|
||||||
unsigned int var1 = 0x060a4011 * seed;
|
|
||||||
unsigned int var2 = 0x0221040d * (seed ^ 0xD3FEA11C);
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
int bytesLeft = size;
|
|
||||||
while(bytesLeft)
|
|
||||||
{
|
|
||||||
int toFlip = MIN( sizeof(revBuf), bytesLeft );
|
|
||||||
|
|
||||||
for(int i=0; i<toFlip; ++i)
|
|
||||||
revBuf[i] = buf[toFlip - (i+1)];
|
|
||||||
|
|
||||||
memcpy( buf, revBuf, toFlip );
|
|
||||||
bytesLeft -= toFlip;
|
|
||||||
buf += toFlip;
|
|
||||||
}
|
|
||||||
memset(revBuf, 0, sizeof(revBuf));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void shuffleBytes(unsigned char *buf, int size)
|
|
||||||
{
|
|
||||||
for(int i=0; i<size-1; ++i)
|
|
||||||
buf[i+1] ^= buf[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unshuffleBytes(unsigned char *buf, int size)
|
|
||||||
{
|
|
||||||
for(int i=size-1; i; --i)
|
|
||||||
buf[i] ^= buf[i-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Partial blocks are encoded with a stream cipher. We make multiple passes on
|
|
||||||
the data to ensure that the ends of the data depend on each other.
|
|
||||||
*/
|
|
||||||
bool SSL_Cipher::streamEncode(unsigned char *buf, int size,
|
|
||||||
uint64_t iv64, const CipherKey &ckey) const
|
|
||||||
{
|
|
||||||
rAssert( size > 0 );
|
|
||||||
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
|
|
||||||
rAssert(key->keySize == _keySize);
|
|
||||||
rAssert(key->ivLength == _ivLength);
|
|
||||||
|
|
||||||
Lock lock( key->mutex );
|
|
||||||
|
|
||||||
unsigned char ivec[ MAX_IVLENGTH ];
|
|
||||||
int dstLen=0, tmpLen=0;
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
dstLen += tmpLen;
|
|
||||||
if(dstLen != size)
|
|
||||||
{
|
|
||||||
rError("encoding %i bytes, got back %i (%i in final_ex)",
|
|
||||||
size, dstLen, tmpLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SSL_Cipher::streamDecode(unsigned char *buf, int size,
|
|
||||||
uint64_t iv64, const CipherKey &ckey) const
|
|
||||||
{
|
|
||||||
rAssert( size > 0 );
|
|
||||||
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(ckey);
|
|
||||||
rAssert(key->keySize == _keySize);
|
|
||||||
rAssert(key->ivLength == _ivLength);
|
|
||||||
|
|
||||||
Lock lock( key->mutex );
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool SSL_Cipher::blockEncode(unsigned char *buf, int size,
|
|
||||||
uint64_t iv64, const CipherKey &ckey ) const
|
|
||||||
{
|
|
||||||
rAssert( size > 0 );
|
|
||||||
shared_ptr<SSLKey> key = dynamic_pointer_cast<SSLKey>(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 );
|
|
||||||
|
|
||||||
unsigned char ivec[ MAX_IVLENGTH ];
|
|
||||||
|
|
||||||
int dstLen = 0, tmpLen = 0;
|
|
||||||
setIVec( ivec, iv64, key );
|
|
||||||
|
|
||||||
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<SSLKey> key = dynamic_pointer_cast<SSLKey>(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 );
|
|
||||||
|
|
||||||
unsigned char ivec[ MAX_IVLENGTH ];
|
|
||||||
|
|
||||||
int dstLen = 0, tmpLen = 0;
|
|
||||||
setIVec( ivec, iv64, key );
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,150 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SSL_Cipher_incl_
|
|
||||||
#define _SSL_Cipher_incl_
|
|
||||||
|
|
||||||
#include "Cipher.h"
|
|
||||||
#include "Interface.h"
|
|
||||||
|
|
||||||
struct SSLKey;
|
|
||||||
#ifndef EVP_CIPHER
|
|
||||||
struct evp_cipher_st;
|
|
||||||
typedef struct evp_cipher_st EVP_CIPHER;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
Implements Cipher interface for OpenSSL's ciphers.
|
|
||||||
|
|
||||||
Design:
|
|
||||||
Variable algorithm, key size, and block size.
|
|
||||||
|
|
||||||
Partial blocks, keys, and names are encrypted using the cipher in a pseudo
|
|
||||||
stream mode (CFB).
|
|
||||||
|
|
||||||
Keys are encrypted with 2-4 (KEY_CHECKSUM_BYTES define) checksum bytes
|
|
||||||
derived from an HMAC over both they key data and the initial value vector
|
|
||||||
associated with the key. This allows a good chance at detecting an
|
|
||||||
incorrect password when we try and decrypt the master key.
|
|
||||||
|
|
||||||
File names are encrypted in the same way, with 2 checksum bytes derived
|
|
||||||
from an HMAC over the filename. This is done not to allow checking the
|
|
||||||
results, but to make the output much more random. Changing one letter in a
|
|
||||||
filename should result in a completely different encrypted filename, to
|
|
||||||
help frustrate any attempt to guess information about files from their
|
|
||||||
encrypted names.
|
|
||||||
|
|
||||||
Stream encryption involves two encryption passes over the data, implemented
|
|
||||||
as:
|
|
||||||
1. shuffle
|
|
||||||
2. 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
|
|
||||||
may only affect one byte of output, allowing some XOR attacks.
|
|
||||||
|
|
||||||
The shuffle/encrypt is used as above in filename encryption as well,
|
|
||||||
although it is not necessary as they have checksum bytes which augment the
|
|
||||||
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
|
|
||||||
{
|
|
||||||
Interface iface;
|
|
||||||
Interface realIface;
|
|
||||||
const EVP_CIPHER *_blockCipher;
|
|
||||||
const EVP_CIPHER *_streamCipher;
|
|
||||||
unsigned int _keySize; // in bytes
|
|
||||||
unsigned int _ivLength;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SSL_Cipher(const Interface &iface, const 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 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();
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
// hack to help with static builds
|
|
||||||
static bool Enabled();
|
|
||||||
private:
|
|
||||||
void setIVec( unsigned char *ivec, uint64_t seed,
|
|
||||||
const shared_ptr<SSLKey> &key ) const;
|
|
||||||
|
|
||||||
// deprecated - for backward compatibility
|
|
||||||
void setIVec_old( unsigned char *ivec, unsigned int seed,
|
|
||||||
const shared_ptr<SSLKey> &key ) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,208 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "StreamNameIO.h"
|
|
||||||
|
|
||||||
#include "Cipher.h"
|
|
||||||
#include "base64.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
|
|
||||||
#include "i18n.h"
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
static shared_ptr<NameIO> NewStreamNameIO( const Interface &iface,
|
|
||||||
const shared_ptr<Cipher> &cipher, const CipherKey &key)
|
|
||||||
{
|
|
||||||
return shared_ptr<NameIO>( 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);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
- Version 0.1 is for EncFS 0.x support. The difference to 1.0 is that 0.x
|
|
||||||
stores the file checksums at the end of the encoded name, where 1.0
|
|
||||||
stores them at the beginning.
|
|
||||||
|
|
||||||
- Version 1.0 is the basic stream encoding mode used since the beginning of
|
|
||||||
EncFS. There is a slight difference in filename encodings from EncFS 0.x
|
|
||||||
to 1.0.x. This implements just the 1.0.x method.
|
|
||||||
|
|
||||||
- Version 1.1 adds support for IV chaining. This is transparently
|
|
||||||
backward compatible, since older filesystems do not use IV chaining.
|
|
||||||
|
|
||||||
- Version 2.0 uses full 64 bit IV during IV chaining mode. Prior versions
|
|
||||||
used only the 16 bit output from MAC_16. This reduces the theoretical
|
|
||||||
possibility (unlikely to make any difference in practice) of two files
|
|
||||||
with the same name in different directories ending up with the same
|
|
||||||
encrypted name. Added because there is no good reason to chop to 16
|
|
||||||
bits.
|
|
||||||
|
|
||||||
- Version 2.1 adds support for version 0 for EncFS 0.x compatibility.
|
|
||||||
*/
|
|
||||||
Interface StreamNameIO::CurrentInterface()
|
|
||||||
{
|
|
||||||
// implement major version 2, 1, and 0
|
|
||||||
return makeInterface("nameio/stream", 2, 1, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamNameIO::StreamNameIO( const Interface &iface,
|
|
||||||
const shared_ptr<Cipher> &cipher,
|
|
||||||
const CipherKey &key )
|
|
||||||
: _interface( iface.major() )
|
|
||||||
, _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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
####### kdevelop will overwrite this part!!! (begin)##########
|
|
||||||
|
|
||||||
|
|
||||||
####### kdevelop will overwrite this part!!! (end)############
|
|
@ -1,4 +0,0 @@
|
|||||||
####### kdevelop will overwrite this part!!! (begin)##########
|
|
||||||
|
|
||||||
|
|
||||||
####### kdevelop will overwrite this part!!! (end)############
|
|
807
encfs/encfs.cpp
807
encfs/encfs.cpp
@ -1,807 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2003-2007, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "encfs.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstring>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <sys/statvfs.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#ifdef linux
|
|
||||||
#include <sys/fsuid.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_ATTR_XATTR_H
|
|
||||||
#include <attr/xattr.h>
|
|
||||||
#elif HAVE_SYS_XATTR_H
|
|
||||||
#include <sys/xattr.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#if HAVE_TR1_TUPLE
|
|
||||||
#include <tr1/tuple>
|
|
||||||
using namespace std;
|
|
||||||
using namespace std::tr1;
|
|
||||||
#else
|
|
||||||
#include <tuple>
|
|
||||||
using namespace std;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "DirNode.h"
|
|
||||||
#include "MemoryPool.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
#include "Mutex.h"
|
|
||||||
#include "Context.h"
|
|
||||||
#include "shared_ptr.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
|
|
||||||
#ifndef MIN
|
|
||||||
#define MIN(a,b) (((a)<(b)) ? (a): (b))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define ESUCCESS 0
|
|
||||||
|
|
||||||
using namespace rlog;
|
|
||||||
using rel::Lock;
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper function -- apply a functor to a cipher path, given the plain path
|
|
||||||
template<typename T>
|
|
||||||
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<DirNode> FSRoot = ctx->getRoot(&res);
|
|
||||||
if(!FSRoot)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper function -- apply a functor to a node
|
|
||||||
template<typename T>
|
|
||||||
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<DirNode> FSRoot = ctx->getRoot(&res);
|
|
||||||
if(!FSRoot)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
shared_ptr<FileNode> fnode;
|
|
||||||
|
|
||||||
if(fi != NULL)
|
|
||||||
fnode = GET_FN(ctx, fi);
|
|
||||||
else
|
|
||||||
fnode = FSRoot->lookupNode( path, opName );
|
|
||||||
|
|
||||||
rAssert(fnode != 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
The rLog messages below always print out encrypted filenames, not
|
|
||||||
plaintext. The reason is so that it isn't possible to leak information
|
|
||||||
about the encrypted data through rlog interfaces.
|
|
||||||
|
|
||||||
|
|
||||||
The purpose of this layer of code is to take the FUSE request and dispatch
|
|
||||||
to the internal interfaces. Any marshaling of arguments and return types
|
|
||||||
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<DirNode> FSRoot = ctx->getRoot(&res);
|
|
||||||
if(FSRoot)
|
|
||||||
{
|
|
||||||
// determine plaintext link size.. Easiest to read and decrypt..
|
|
||||||
vector<char> buf(stbuf->st_size+1, 0);
|
|
||||||
|
|
||||||
res = ::readlink( fnode->cipherName(), &buf[0], 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[0] ).length();
|
|
||||||
|
|
||||||
res = ESUCCESS;
|
|
||||||
} else
|
|
||||||
res = -errno;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
int encfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
|
|
||||||
{
|
|
||||||
EncFS_Context *ctx = context();
|
|
||||||
|
|
||||||
int res = ESUCCESS;
|
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
|
||||||
if(!FSRoot)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
DirTraverse dt = FSRoot->openDir( path );
|
|
||||||
|
|
||||||
rLog(Info, "getdir on %s", FSRoot->cipherPath(path).c_str());
|
|
||||||
|
|
||||||
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 );
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int encfs_mknod(const char *path, mode_t mode, dev_t rdev)
|
|
||||||
{
|
|
||||||
EncFS_Context *ctx = context();
|
|
||||||
|
|
||||||
int res = -EIO;
|
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
|
||||||
if(!FSRoot)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
shared_ptr<FileNode> 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<FileNode> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int encfs_mkdir(const char *path, mode_t mode)
|
|
||||||
{
|
|
||||||
fuse_context *fctx = fuse_get_context();
|
|
||||||
EncFS_Context *ctx = context();
|
|
||||||
|
|
||||||
int res = -EIO;
|
|
||||||
shared_ptr<DirNode> 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<FileNode> 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_unlink(const char *path)
|
|
||||||
{
|
|
||||||
EncFS_Context *ctx = context();
|
|
||||||
|
|
||||||
int res = -EIO;
|
|
||||||
shared_ptr<DirNode> 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<char *, size_t> data )
|
|
||||||
{
|
|
||||||
char *buf = get<0>(data);
|
|
||||||
size_t size = get<1>(data);
|
|
||||||
|
|
||||||
int res = ESUCCESS;
|
|
||||||
shared_ptr<DirNode> FSRoot = ctx->getRoot(&res);
|
|
||||||
if(!FSRoot)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
res = ::readlink( cyName.c_str(), buf, size-1 );
|
|
||||||
|
|
||||||
if(res == -1)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
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';
|
|
||||||
|
|
||||||
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<DirNode> 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_link(const char *from, const char *to)
|
|
||||||
{
|
|
||||||
EncFS_Context *ctx = context();
|
|
||||||
|
|
||||||
int res = -EIO;
|
|
||||||
shared_ptr<DirNode> 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_rename(const char *from, const char *to)
|
|
||||||
{
|
|
||||||
EncFS_Context *ctx = context();
|
|
||||||
|
|
||||||
int res = -EIO;
|
|
||||||
shared_ptr<DirNode> 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<uid_t, gid_t> data)
|
|
||||||
{
|
|
||||||
int res = lchown( cyName.c_str(), get<0>(data), get<1>(data) );
|
|
||||||
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<DirNode> FSRoot = ctx->getRoot(&res);
|
|
||||||
if(!FSRoot)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
shared_ptr<FileNode> 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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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();
|
|
||||||
|
|
||||||
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<unsigned char *, size_t, off_t> data)
|
|
||||||
{
|
|
||||||
return fnode->read( get<2>(data), get<0>(data), get<1>(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 _do_write(FileNode *fnode, tuple<const char *, size_t, off_t> data)
|
|
||||||
{
|
|
||||||
size_t size = get<1>(data);
|
|
||||||
if(fnode->write( get<2>(data), (unsigned char *)get<0>(data), 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
// statfs works even if encfs is detached..
|
|
||||||
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;
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_XATTR
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef XATTR_ADD_OPT
|
|
||||||
int _do_setxattr(EncFS_Context *, const string &cyName,
|
|
||||||
tuple<const char *, const char *, size_t, uint32_t> data)
|
|
||||||
{
|
|
||||||
int options = 0;
|
|
||||||
return ::setxattr( cyName.c_str(), get<0>(data), get<1>(data),
|
|
||||||
get<2>(data), get<3>(data), 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) );
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int _do_setxattr(EncFS_Context *, const string &cyName,
|
|
||||||
tuple<const char *, const char *, size_t, int> data)
|
|
||||||
{
|
|
||||||
return ::setxattr( cyName.c_str(), get<0>(data), get<1>(data),
|
|
||||||
get<2>(data), get<3>(data) );
|
|
||||||
}
|
|
||||||
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<const char *, void *, size_t, uint32_t> data)
|
|
||||||
{
|
|
||||||
int options = 0;
|
|
||||||
return ::getxattr( cyName.c_str(), get<0>(data),
|
|
||||||
get<1>(data), get<2>(data), get<3>(data), 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 );
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
int _do_getxattr(EncFS_Context *, const string &cyName,
|
|
||||||
tuple<const char *, void *, size_t> data)
|
|
||||||
{
|
|
||||||
return ::getxattr( cyName.c_str(), get<0>(data),
|
|
||||||
get<1>(data), get<2>(data));
|
|
||||||
}
|
|
||||||
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<char *, size_t> data)
|
|
||||||
{
|
|
||||||
#ifdef XATTR_ADD_OPT
|
|
||||||
int options = 0;
|
|
||||||
int res = ::listxattr( cyName.c_str(), get<0>(data), get<1>(data),
|
|
||||||
options );
|
|
||||||
#else
|
|
||||||
int res = ::listxattr( cyName.c_str(), get<0>(data), get<1>(data) );
|
|
||||||
#endif
|
|
||||||
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 _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 );
|
|
||||||
#else
|
|
||||||
int res = ::removexattr( cyName.c_str(), name );
|
|
||||||
#endif
|
|
||||||
return (res == -1) ? -errno : res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int encfs_removexattr( const char *path, const char *name )
|
|
||||||
{
|
|
||||||
return withCipherPath( "removexattr", path, _do_removexattr, name );
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // HAVE_XATTR
|
|
||||||
|
|
154
encfs/encfs.pod
154
encfs/encfs.pod
@ -1,12 +1,3 @@
|
|||||||
=cut
|
|
||||||
Copyright (c) 2003-2008, Valient Gough <vgough@pobox.com>
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
EncFS 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 3 of the License, or (at your option) any later
|
|
||||||
version.
|
|
||||||
|
|
||||||
=pod
|
=pod
|
||||||
|
|
||||||
=head1 NAME
|
=head1 NAME
|
||||||
@ -174,9 +165,8 @@ the B<fusermount> help page for information on available commands.
|
|||||||
=item B<--no-default-flags>
|
=item B<--no-default-flags>
|
||||||
|
|
||||||
B<Encfs> adds the FUSE flags "use_ino" and "default_permissions" by default, as
|
B<Encfs> adds the FUSE flags "use_ino" and "default_permissions" by default, as
|
||||||
of version 1.2.2, because that improves compatibility with some programs.. If
|
that improves compatibility with some programs.. If you need to disable one or
|
||||||
for some reason you need to disable one or both of these flags, use the option
|
both of these flags, use the option B<--no-default-flags>.
|
||||||
B<--no-default-flags>.
|
|
||||||
|
|
||||||
The following command lines produce the same result:
|
The following command lines produce the same result:
|
||||||
|
|
||||||
@ -196,10 +186,6 @@ for a trailing newline (\n) which will be removed.
|
|||||||
For example, specifying B<--extpass>=I</usr/lib/ssh/ssh-askpass> will cause
|
For example, specifying B<--extpass>=I</usr/lib/ssh/ssh-askpass> will cause
|
||||||
B<EncFS> to use ssh's password prompt program.
|
B<EncFS> to use ssh's password prompt program.
|
||||||
|
|
||||||
B<Note>: B<EncFS> reads at most 2k of data from the password program, and it
|
|
||||||
removes any trailing newline. Versions before 1.4.x accepted only 64 bytes of
|
|
||||||
text.
|
|
||||||
|
|
||||||
=item B<-S>, B<--stdinpass>
|
=item B<-S>, B<--stdinpass>
|
||||||
|
|
||||||
Read password from standard input, without prompting. This may be useful for
|
Read password from standard input, without prompting. This may be useful for
|
||||||
@ -209,6 +195,10 @@ Note that you should make sure the filesystem and mount points exist first.
|
|||||||
Otherwise encfs will prompt for the filesystem creation options, which may
|
Otherwise encfs will prompt for the filesystem creation options, which may
|
||||||
interfere with your script.
|
interfere with your script.
|
||||||
|
|
||||||
|
B<Note>: B<EncFS> reads a limited amount of data from the console (roughly 2k
|
||||||
|
bytes), and it removes any trailing newline. If your password is larger than
|
||||||
|
this, use --extpass.
|
||||||
|
|
||||||
=item B<--anykey>
|
=item B<--anykey>
|
||||||
|
|
||||||
Turn off key validation checking. This allows B<EncFS> to be used with
|
Turn off key validation checking. This allows B<EncFS> to be used with
|
||||||
@ -275,10 +265,10 @@ to decode filenames if desired.
|
|||||||
=head1 CAVEATS
|
=head1 CAVEATS
|
||||||
|
|
||||||
B<EncFS> is not a true filesystem. It does not deal with any of the actual
|
B<EncFS> is not a true filesystem. It does not deal with any of the actual
|
||||||
storage or maintenance of files. It simply translates requests (encrypting or
|
storage or maintenance of files. It translates requests (encrypting or
|
||||||
decrypting as necessary) and passes the requests through to the underlying
|
decrypting as necessary) and passes the requests through to the underlying
|
||||||
host filesystem. Therefor any limitations of the host filesystem will likely
|
host filesystem. Therefor any limitations of the host filesystem will be
|
||||||
be inherited by B<EncFS> (or possibly be further limited).
|
inherited by B<EncFS> (or possibly be further limited).
|
||||||
|
|
||||||
One such limitation is filename length. If your underlying filesystem limits
|
One such limitation is filename length. If your underlying filesystem limits
|
||||||
you to N characters in a filename, then B<EncFS> will limit you to approximately
|
you to N characters in a filename, then B<EncFS> will limit you to approximately
|
||||||
@ -328,20 +318,20 @@ they mean:
|
|||||||
|
|
||||||
=head1 Key Derivation Function
|
=head1 Key Derivation Function
|
||||||
|
|
||||||
As of version 1.5, B<EncFS> now uses PBKDF2 as the default key derivation
|
B<EncFS> uses PBKDF2 as the key derivation function. The number of iterations
|
||||||
function. The number of iterations in the keying function is selected based on
|
in the keying function is selected based on wall clock time to generate the
|
||||||
wall clock time to generate the key. In standard mode, a target time of 0.5
|
key. In standard mode, a target time of 0.5 seconds is used, and in paranoia
|
||||||
seconds is used, and in paranoia mode a target of 3.0 seconds is used.
|
mode a target of 3.0 seconds is used.
|
||||||
|
|
||||||
On a 1.6Ghz AMD 64 system, it rougly 64k iterations of the key derivation
|
On a 1.6Ghz AMD 64 system, it rougly 64k iterations of the key derivation
|
||||||
function can be handled in half a second. The exact number of iterations to
|
function can be handled in half a second. The exact number of iterations to
|
||||||
use is stored in the configuration file, as it is needed to remount the
|
use is stored in the configuration file, as it is needed to remount the
|
||||||
filesystem.
|
filesystem.
|
||||||
|
|
||||||
If an B<EncFS> filesystem configuration from 1.4.x is modified with version 1.5
|
If an B<EncFS> filesystem configuration from 1.4.x is modified with a later
|
||||||
(such as when using encfsctl to change the password), then the new PBKDF2
|
version (such as when using encfsctl to change the password), then the new
|
||||||
function will be used and the filesystem will no longer be readable by older
|
PBKDF2 function will be used and the filesystem will no longer be readable by
|
||||||
versions.
|
older versions.
|
||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
@ -375,18 +365,16 @@ read and decoded, so a large block size adds overhead to small requests. With
|
|||||||
write calls it is even worse, as a block must be read and decoded, the change
|
write calls it is even worse, as a block must be read and decoded, the change
|
||||||
applied and the block encoded and written back out.
|
applied and the block encoded and written back out.
|
||||||
|
|
||||||
The default is 512 bytes as of version 1.0. It was hard coded to 64 bytes in
|
The default block size is currently 2k.
|
||||||
version 0.x, which was not as efficient as the current setting for general
|
|
||||||
usage.
|
|
||||||
|
|
||||||
=item I<Filename Encoding>
|
=item I<Filename Encoding>
|
||||||
|
|
||||||
B<New in 1.1>. A choice is given between stream encoding of filename and block
|
A choice is given between stream encoding of filename and block encoding. The
|
||||||
encoding. The advantage of stream encoding is that the encoded filenames will
|
advantage of stream encoding is that the encoded filenames will be as short as
|
||||||
be as short as possible. If you have a filename with a single letter, it will
|
possible. If you have a filename with a single letter, it will be very short
|
||||||
be very short in the encoded form, where as block encoded filenames are always
|
in the encoded form, where as block encoded filenames are always rounded up to
|
||||||
rounded up to the block size of the encryption cipher (8 bytes for Blowfish and
|
the block size of the encryption cipher (8 bytes for Blowfish and 16 bytes for
|
||||||
16 bytes for AES).
|
AES).
|
||||||
|
|
||||||
The advantage of block encoding mode is that filename lenths all come out as a
|
The advantage of block encoding mode is that filename lenths all come out as a
|
||||||
multiple of the cipher block size. This means that someone looking at your
|
multiple of the cipher block size. This means that someone looking at your
|
||||||
@ -395,16 +383,14 @@ on by default, as it takes a similar amount of time to using the stream cipher.
|
|||||||
However stream cipher mode may be useful if you want shorter encrypted
|
However stream cipher mode may be useful if you want shorter encrypted
|
||||||
filenames for some reason.
|
filenames for some reason.
|
||||||
|
|
||||||
Prior to version 1.1, only stream encoding was supported.
|
|
||||||
|
|
||||||
=item I<Filename Initialization Vector Chaining>
|
=item I<Filename Initialization Vector Chaining>
|
||||||
|
|
||||||
B<New in 1.1>. In previous versions of B<EncFS>, each filename element in
|
In previous versions of B<EncFS>, each filename element in a path was encoded
|
||||||
a path was encoded separately. So if "foo" encoded to "XXX", then it would
|
separately. So if "foo" encoded to "XXX", then it would always encode that way
|
||||||
always encode that way (given the same encryption key), no matter if the path
|
(given the same encryption key), no matter if the path was "a/b/foo", or
|
||||||
was "a/b/foo", or "aa/foo/cc", etc. That meant it was possible for someone
|
"aa/foo/cc", etc. That meant it was possible for someone looking at the
|
||||||
looking at the encrypted data to see if two files in different directories had
|
encrypted data to see if two files in different directories had the same name,
|
||||||
the same name, even though they wouldn't know what that name decoded to.
|
even though they wouldn't know what that name decoded to.
|
||||||
|
|
||||||
With initialization vector chaining, each directory gets its own initialization
|
With initialization vector chaining, each directory gets its own initialization
|
||||||
vector. So "a/foo" and "b/foo" will have completely different encoded names
|
vector. So "a/foo" and "b/foo" will have completely different encoded names
|
||||||
@ -422,11 +408,11 @@ rename will fail.
|
|||||||
|
|
||||||
=item I<Per-File Initialization Vectors>
|
=item I<Per-File Initialization Vectors>
|
||||||
|
|
||||||
B<New in 1.1>. In previous versions of B<EncFS>, each file was encoded in the
|
In previous versions of B<EncFS>, each file was encoded in the same way. Each
|
||||||
same way. Each block in a file has always had its own initialization vector,
|
block in a file has always had its own initialization vector, but in a
|
||||||
but in a deterministic way so that block N in one file is encoded in the same
|
deterministic way so that block N in one file is encoded in the same was as
|
||||||
was as block N in another file. That made it possible for someone to tell if
|
block N in another file. That made it possible for someone to tell if two
|
||||||
two files were identical (or parts of the file were identical) by comparing the
|
files were identical (or parts of the file were identical) by comparing the
|
||||||
encoded data.
|
encoded data.
|
||||||
|
|
||||||
With per-file initialization vectors, each file gets its own 64bit random
|
With per-file initialization vectors, each file gets its own 64bit random
|
||||||
@ -436,10 +422,9 @@ This option is enabled by default.
|
|||||||
|
|
||||||
=item I<External IV Chaining>
|
=item I<External IV Chaining>
|
||||||
|
|
||||||
B<New in 1.1.3>. This option is closely related to Per-File Initialization
|
This option is closely related to Per-File Initialization Vectors and Filename
|
||||||
Vectors and Filename Initialization Vector Chaining. Basically it extends the
|
Initialization Vector Chaining. Basically it extends the initialization vector
|
||||||
initialization vector chaining from filenames to the per-file initialization
|
chaining from filenames to the per-file initialization vector.
|
||||||
vector.
|
|
||||||
|
|
||||||
When this option is enabled, the per-file initialization vector is encoded
|
When this option is enabled, the per-file initialization vector is encoded
|
||||||
using the initialization vector derived from the filename initialization vector
|
using the initialization vector derived from the filename initialization vector
|
||||||
@ -461,11 +446,11 @@ Because of these limits, this option is disabled by default for standard mode
|
|||||||
|
|
||||||
=item I<Block MAC headers>
|
=item I<Block MAC headers>
|
||||||
|
|
||||||
B<New to 1.1>. If this is enabled, every block in every file is stored along
|
If this is enabled, every block in every file is stored along with a
|
||||||
with a cryptographic checksum (Message Authentication Code). This makes it
|
cryptographic checksum (Message Authentication Code). This makes it virtually
|
||||||
virtually impossible to modify a file without the change being detected by
|
impossible to modify a file without the change being detected by B<EncFS>.
|
||||||
B<EncFS>. B<EncFS> will refuse to read data which does not pass the checksum,
|
B<EncFS> will refuse to read data which does not pass the checksum, and will
|
||||||
and will log the error and return an IO error to the application.
|
log the error and return an IO error to the application.
|
||||||
|
|
||||||
This adds substantial overhead (default being 8 bytes per filesystem block),
|
This adds substantial overhead (default being 8 bytes per filesystem block),
|
||||||
plus computational overhead, and is not enabled by default except in paranoia
|
plus computational overhead, and is not enabled by default except in paranoia
|
||||||
@ -498,28 +483,6 @@ filesystem contents along with the algorithms B<EncFS> supports to thwart them:
|
|||||||
|
|
||||||
=over 4
|
=over 4
|
||||||
|
|
||||||
=item B<Attack>: modifying a few bytes of an encrypted file (without knowing
|
|
||||||
what they will decode to).
|
|
||||||
|
|
||||||
B<EncFS> does not use any form of XOR encryption which would allow
|
|
||||||
single bytes to be modified without affecting others. Most modifications
|
|
||||||
would affect dozens or more bytes. Additionally, MAC Block headers can be
|
|
||||||
used to identify any changes to files.
|
|
||||||
|
|
||||||
=item B<Attack>: copying a random block of one file to a random block of another file.
|
|
||||||
|
|
||||||
Each block has its own [deterministic] initialization vector.
|
|
||||||
|
|
||||||
=item B<Attack>: copying block N to block N of another file.
|
|
||||||
|
|
||||||
When the Per-File Initialization Vector support is enabled (default
|
|
||||||
in 1.1.x filesystems), a copied block will not decode properly when copied to
|
|
||||||
another file.
|
|
||||||
|
|
||||||
=item B<Attack>: copying an entire file to another file.
|
|
||||||
|
|
||||||
Can be prevented by enabling External IV Chaining mode.
|
|
||||||
|
|
||||||
=item B<Attack>: determine if two filenames are the same by looking at encrypted names.
|
=item B<Attack>: determine if two filenames are the same by looking at encrypted names.
|
||||||
|
|
||||||
Filename Initialization Vector chaining prevents this by giving each file a
|
Filename Initialization Vector chaining prevents this by giving each file a
|
||||||
@ -529,8 +492,39 @@ Filename Initialization Vector chaining prevents this by giving each file a
|
|||||||
|
|
||||||
Per-File Initialization Vector support prevents this.
|
Per-File Initialization Vector support prevents this.
|
||||||
|
|
||||||
|
=item B<Attack>: copying an entire file to another file.
|
||||||
|
|
||||||
|
Can be prevented by enabling External IV Chaining mode.
|
||||||
|
|
||||||
|
=item B<Attack>: copying a random block of one file to a random block of another file.
|
||||||
|
|
||||||
|
Each block has its own [deterministic] initialization vector.
|
||||||
|
|
||||||
|
=item B<Attack>: copying block N to block N of another file.
|
||||||
|
|
||||||
|
When the Per-File Initialization Vector support is enabled (the default), a
|
||||||
|
copied block will not decode properly when copied to another file.
|
||||||
|
|
||||||
|
=item B<Attack>: modifying a few bytes of an encrypted file (without knowing
|
||||||
|
what they will decode to).
|
||||||
|
|
||||||
|
B<EncFS> does not use any form of XOR encryption which would allow
|
||||||
|
single bytes to be modified without affecting others. Most modifications
|
||||||
|
would affect dozens or more bytes. Additionally, MAC Block headers can be
|
||||||
|
used to identify any changes to files.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
=head1 LICENSE
|
||||||
|
|
||||||
|
EncFS 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 3 of the License, or (at your option) any later
|
||||||
|
version.
|
||||||
|
|
||||||
|
The library portion of EncFS is licensed under the LGPL version 3. See the
|
||||||
|
COPYING files in the source distribution for details.
|
||||||
|
|
||||||
=head1 DISCLAIMER
|
=head1 DISCLAIMER
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful, but WITHOUT ANY
|
This library is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
@ -1,853 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2004, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "encfs.h"
|
|
||||||
|
|
||||||
#include "autosprintf.h"
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "FileUtils.h"
|
|
||||||
#include "Cipher.h"
|
|
||||||
|
|
||||||
#include "Context.h"
|
|
||||||
#include "FileNode.h"
|
|
||||||
#include "DirNode.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/StdioNode.h>
|
|
||||||
#include <rlog/RLogChannel.h>
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
#include <libintl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "i18n.h"
|
|
||||||
|
|
||||||
#ifdef HAVE_SSL
|
|
||||||
#define NO_DES
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace rlog;
|
|
||||||
using namespace std;
|
|
||||||
using namespace gnu;
|
|
||||||
|
|
||||||
|
|
||||||
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}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static
|
|
||||||
void usage(const char *name)
|
|
||||||
{
|
|
||||||
cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n"
|
|
||||||
<< _("Usage:\n")
|
|
||||||
// displays usage commands, eg "./encfs (root dir) ..."
|
|
||||||
// xgroup(usage)
|
|
||||||
<< autosprintf(_("%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;
|
|
||||||
}
|
|
||||||
|
|
||||||
cerr << "\n"
|
|
||||||
// xgroup(usage)
|
|
||||||
<< autosprintf(_("Example: \n%s info ~/.crypt\n"), name)
|
|
||||||
<< "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool checkDir( string &rootDir )
|
|
||||||
{
|
|
||||||
if( !isDirectory( rootDir.c_str() ))
|
|
||||||
{
|
|
||||||
cerr << autosprintf(_("directory %s does not exist.\n"),
|
|
||||||
rootDir.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(rootDir[ rootDir.length()-1 ] != '/')
|
|
||||||
rootDir.append("/");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int showVersion( int argc, char **argv )
|
|
||||||
{
|
|
||||||
(void)argc;
|
|
||||||
(void)argv;
|
|
||||||
// xgroup(usage)
|
|
||||||
cerr << autosprintf(_("encfsctl version %s"), VERSION) << "\n";
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int showInfo( int argc, char **argv )
|
|
||||||
{
|
|
||||||
(void)argc;
|
|
||||||
string rootDir = argv[1];
|
|
||||||
if( !checkDir( rootDir ))
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
|
||||||
EncfsConfig config;
|
|
||||||
ConfigType type = readConfig( rootDir, config );
|
|
||||||
|
|
||||||
// show information stored in config..
|
|
||||||
switch(type)
|
|
||||||
{
|
|
||||||
case Config_None:
|
|
||||||
// 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;
|
|
||||||
case Config_V3:
|
|
||||||
// xgroup(diag)
|
|
||||||
cout << "\n" << autosprintf(_("Version 3 configuration; "
|
|
||||||
"created by %s\n"), config.creator().c_str());
|
|
||||||
break;
|
|
||||||
case Config_V4:
|
|
||||||
// xgroup(diag)
|
|
||||||
cout << "\n" << autosprintf(_("Version 4 configuration; "
|
|
||||||
"created by %s\n"), config.creator().c_str());
|
|
||||||
break;
|
|
||||||
case Config_V5:
|
|
||||||
case Config_V6:
|
|
||||||
case Config_V7:
|
|
||||||
// xgroup(diag)
|
|
||||||
cout << "\n" << autosprintf(_("Version %i configuration; "
|
|
||||||
"created by %s (revision %i)\n"),
|
|
||||||
type,
|
|
||||||
config.creator().c_str(),
|
|
||||||
config.revision());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
showFSInfo( config );
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static RootPtr initRootInfo(int &argc, char ** &argv)
|
|
||||||
{
|
|
||||||
RootPtr result;
|
|
||||||
shared_ptr<EncFS_Opts> 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<EncFS_Opts> opts( new EncFS_Opts() );
|
|
||||||
opts->rootDir = rootDir;
|
|
||||||
opts->createIfNotFound = false;
|
|
||||||
opts->checkKey = false;
|
|
||||||
result = initFS( NULL, opts );
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!result)
|
|
||||||
cerr << _("Unable to initialize encrypted filesystem - check path.\n");
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cmd_showKey( int argc, char **argv )
|
|
||||||
{
|
|
||||||
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; 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<FileNode> 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<typename T>
|
|
||||||
int processContents( const shared_ptr<EncFS_Root> &rootInfo,
|
|
||||||
const char *path, T &op )
|
|
||||||
{
|
|
||||||
int errCode = 0;
|
|
||||||
shared_ptr<FileNode> 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)
|
|
||||||
{
|
|
||||||
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); }
|
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
if(!rootInfo)
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
|
||||||
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<EncFS_Root> &rootInfo,
|
|
||||||
const string &cpath, const string &destName )
|
|
||||||
{
|
|
||||||
vector<char> buf(stBuf.st_size+1, 0);
|
|
||||||
int res = ::readlink( cpath.c_str(), &buf[0], 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[0]);
|
|
||||||
|
|
||||||
res = ::symlink( decodedLink.c_str(), destName.c_str() );
|
|
||||||
if(res == -1)
|
|
||||||
{
|
|
||||||
cerr << "unable to create symlink for " << cpath
|
|
||||||
<< " to " << decodedLink << "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int copyContents(const shared_ptr<EncFS_Root> &rootInfo,
|
|
||||||
const char* encfsName, const char* targetName)
|
|
||||||
{
|
|
||||||
shared_ptr<FileNode> node =
|
|
||||||
rootInfo->root->lookupNode( encfsName, "encfsctl" );
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
symlink(rootInfo->root->plainPath(linkContents).c_str(),
|
|
||||||
targetName);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
int outfd = creat(targetName, st.st_mode);
|
|
||||||
|
|
||||||
WriteOutput output(outfd);
|
|
||||||
processContents( rootInfo, encfsName, output );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 traverseDirs(const shared_ptr<EncFS_Root> &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<FileNode> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<EncFS_Root> &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 << autosprintf(_("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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
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;
|
|
||||||
|
|
||||||
RootPtr rootInfo = initRootInfo(argv[1]);
|
|
||||||
|
|
||||||
if(!rootInfo)
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
|
|
||||||
int filesFound = showcruft( rootInfo, "/" );
|
|
||||||
|
|
||||||
cerr << autosprintf("Found %i invalid file(s).", filesFound) << "\n";
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
EncfsConfig config;
|
|
||||||
ConfigType cfgType = readConfig( rootDir, config );
|
|
||||||
|
|
||||||
if(cfgType == Config_None)
|
|
||||||
{
|
|
||||||
cout << _("Unable to load or parse config file\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// instanciate proper cipher
|
|
||||||
shared_ptr<Cipher> cipher = getCipher(config);
|
|
||||||
if(!cipher)
|
|
||||||
{
|
|
||||||
cout << autosprintf(_("Unable to find specified cipher \"%s\"\n"),
|
|
||||||
config.cipher().name().c_str());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ask for existing password
|
|
||||||
cout << _("Enter current Encfs password\n");
|
|
||||||
if (annotate)
|
|
||||||
cerr << "$PROMPT$ passwd" << endl;
|
|
||||||
CipherKey userKey = getUserKey( config, 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(
|
|
||||||
(const unsigned char *)config.key().ciphertext().data(), userKey );
|
|
||||||
|
|
||||||
if(!volumeKey)
|
|
||||||
{
|
|
||||||
cout << _("Invalid password\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now, get New user key..
|
|
||||||
userKey.reset();
|
|
||||||
cout << _("Enter new Encfs password\n");
|
|
||||||
|
|
||||||
// create new key
|
|
||||||
if( useStdin )
|
|
||||||
{
|
|
||||||
if (annotate)
|
|
||||||
cerr << "$PROMPT$ new_passwd" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
userKey = getNewUserKey( config, useStdin, string(), string() );
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
EncryptedKey *key = config.mutable_key();
|
|
||||||
key->set_ciphertext( keyBuf, encodedKeySize );
|
|
||||||
delete[] keyBuf;
|
|
||||||
|
|
||||||
if(saveConfig( 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
RLogInit( argc, argv );
|
|
||||||
|
|
||||||
#ifdef LOCALEDIR
|
|
||||||
setlocale( LC_ALL, "" );
|
|
||||||
bindtextdomain( PACKAGE, LOCALEDIR );
|
|
||||||
textdomain( PACKAGE );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SSL
|
|
||||||
SSL_load_error_strings();
|
|
||||||
SSL_library_init();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
StdioNode *slog = new StdioNode( STDERR_FILENO );
|
|
||||||
slog->subscribeTo( GetGlobalChannel("error") );
|
|
||||||
slog->subscribeTo( GetGlobalChannel("warning") );
|
|
||||||
#ifndef NO_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 << autosprintf(_("invalid command: \"%s\""), argv[1]) << "\n";
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
if((argc-2 < commands[offset].minOptions) ||
|
|
||||||
(argc-2 > commands[offset].maxOptions))
|
|
||||||
{
|
|
||||||
cerr << autosprintf(
|
|
||||||
_("Incorrect number of arguments for command \"%s\""),
|
|
||||||
argv[1]) << "\n";
|
|
||||||
} else
|
|
||||||
return (*commands[offset].func)( argc-1, argv+1 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
1173
encfs/main.cpp
1173
encfs/main.cpp
File diff suppressed because it is too large
Load Diff
@ -1,109 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2007, Valient Gough
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
||||||
* for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "openssl.h"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
|
|
||||||
#define NO_DES
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#ifndef OPENSSL_NO_ENGINE
|
|
||||||
#include <openssl/engine.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
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<CRYPTO_num_locks(); ++i)
|
|
||||||
pthread_mutex_init( crypto_locks+i, 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mode & CRYPTO_LOCK)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock( crypto_locks + n );
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock( crypto_locks + n );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void pthreads_locking_cleanup()
|
|
||||||
{
|
|
||||||
if(crypto_locks)
|
|
||||||
{
|
|
||||||
for(int i=0; i<CRYPTO_num_locks(); ++i)
|
|
||||||
pthread_mutex_destroy( crypto_locks+i );
|
|
||||||
delete[] crypto_locks;
|
|
||||||
crypto_locks = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void openssl_init(bool threaded)
|
|
||||||
{
|
|
||||||
// initialize the SSL library
|
|
||||||
SSL_load_error_strings();
|
|
||||||
SSL_library_init();
|
|
||||||
|
|
||||||
unsigned int randSeed = 0;
|
|
||||||
RAND_bytes( (unsigned char*)&randSeed, sizeof(randSeed) );
|
|
||||||
srand( randSeed );
|
|
||||||
|
|
||||||
#ifndef OPENSSL_NO_ENGINE
|
|
||||||
/* Load all bundled ENGINEs into memory and make them visible */
|
|
||||||
ENGINE_load_builtin_engines();
|
|
||||||
/* Register all of them for every algorithm they collectively implement */
|
|
||||||
ENGINE_register_all_complete();
|
|
||||||
#endif // NO_ENGINE
|
|
||||||
|
|
||||||
if(threaded)
|
|
||||||
{
|
|
||||||
// provide locking functions to OpenSSL since we'll be running with
|
|
||||||
// threads accessing openssl in parallel.
|
|
||||||
CRYPTO_set_id_callback( pthreads_thread_id );
|
|
||||||
CRYPTO_set_locking_callback( pthreads_locking_callback );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void openssl_shutdown(bool threaded)
|
|
||||||
{
|
|
||||||
#ifndef OPENSSL_NO_ENGINE
|
|
||||||
ENGINE_cleanup();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(threaded)
|
|
||||||
pthreads_locking_cleanup();
|
|
||||||
}
|
|
||||||
|
|
563
encfs/test.cpp
563
encfs/test.cpp
@ -1,563 +0,0 @@
|
|||||||
/*****************************************************************************
|
|
||||||
* Author: Valient Gough <vgough@pobox.com>
|
|
||||||
*
|
|
||||||
*****************************************************************************
|
|
||||||
* Copyright (c) 2003, Valient Gough
|
|
||||||
*
|
|
||||||
* This library 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.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GPL in the file COPYING for more
|
|
||||||
* details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "encfs.h"
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "Cipher.h"
|
|
||||||
#include "DirNode.h"
|
|
||||||
#include "MemoryPool.h"
|
|
||||||
#include "Interface.h"
|
|
||||||
#include "FileUtils.h"
|
|
||||||
#include "StreamNameIO.h"
|
|
||||||
#include "BlockNameIO.h"
|
|
||||||
#include "NullNameIO.h"
|
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
|
||||||
#include <rlog/Error.h>
|
|
||||||
#include <rlog/StdioNode.h>
|
|
||||||
#include <rlog/RLogChannel.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_SSL
|
|
||||||
#define NO_DES
|
|
||||||
#include <openssl/ssl.h>
|
|
||||||
#ifndef OPENSSL_NO_ENGINE
|
|
||||||
#include <openssl/engine.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <google/protobuf/text_format.h>
|
|
||||||
|
|
||||||
#if HAVE_TR1_UNORDERED_SET
|
|
||||||
#include <tr1/unordered_set>
|
|
||||||
using std::tr1::unordered_set;
|
|
||||||
#else
|
|
||||||
#include <unordered_set>
|
|
||||||
using std::unordered_set;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace rlog;
|
|
||||||
|
|
||||||
const int FSBlockSize = 256;
|
|
||||||
|
|
||||||
static
|
|
||||||
int checkErrorPropogation( const shared_ptr<Cipher> &cipher,
|
|
||||||
int size, int byteToChange, const CipherKey &key )
|
|
||||||
{
|
|
||||||
MemBlock orig = MemoryPool::allocate(size);
|
|
||||||
MemBlock data = MemoryPool::allocate(size);
|
|
||||||
|
|
||||||
for(int i=0; i<size; ++i)
|
|
||||||
{
|
|
||||||
unsigned char tmp = rand();
|
|
||||||
orig.data[i] = tmp;
|
|
||||||
data.data[i] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(size != FSBlockSize)
|
|
||||||
cipher->streamEncode( 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->streamDecode( data.data, size, 0, key );
|
|
||||||
else
|
|
||||||
cipher->blockDecode( data.data, size, 0, key );
|
|
||||||
|
|
||||||
int numByteErrors = 0;
|
|
||||||
for(int i=0; i<size; ++i)
|
|
||||||
{
|
|
||||||
if( data.data[i] != orig.data[i] )
|
|
||||||
++numByteErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryPool::release( data );
|
|
||||||
MemoryPool::release( orig );
|
|
||||||
|
|
||||||
return numByteErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char TEST_ROOTDIR[] = "/foo";
|
|
||||||
|
|
||||||
static
|
|
||||||
bool testNameCoding( DirNode &dirNode, bool verbose,
|
|
||||||
bool collisionTest = false )
|
|
||||||
{
|
|
||||||
// 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 << "\"";
|
|
||||||
|
|
||||||
string encName = dirNode.relativeCipherPath( *orig );
|
|
||||||
|
|
||||||
if(verbose)
|
|
||||||
cerr << " -> \"" << 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 (collisionTest)
|
|
||||||
{
|
|
||||||
if (verbose)
|
|
||||||
cerr << "Checking for name collections, this will take a while..\n";
|
|
||||||
// check for collision rate
|
|
||||||
char buf[64];
|
|
||||||
unordered_set<string> encryptedNames;
|
|
||||||
for (long i=0; i < 10000000; i++)
|
|
||||||
{
|
|
||||||
snprintf(buf, sizeof(buf), "%li", i);
|
|
||||||
string encName = dirNode.relativeCipherPath( buf );
|
|
||||||
// simulate a case-insisitive filesystem..
|
|
||||||
std::transform(encName.begin(), encName.end(), encName.begin(),
|
|
||||||
::toupper);
|
|
||||||
|
|
||||||
if (encryptedNames.insert(encName).second == false) {
|
|
||||||
cerr << "collision detected after " << i << " iterations";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cerr << "NO collisions detected";
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool runTests(const shared_ptr<Cipher> &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 ];
|
|
||||||
|
|
||||||
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.mutable_cipher()->MergeFrom(cipher->interface());
|
|
||||||
EncryptedKey *encryptedKey = cfg.mutable_key();
|
|
||||||
encryptedKey->set_size(8 * cipher->keySize());
|
|
||||||
encryptedKey->set_ciphertext( keyBuf, encodedKeySize );
|
|
||||||
cfg.set_block_size(FSBlockSize);
|
|
||||||
|
|
||||||
// save config
|
|
||||||
string data;
|
|
||||||
google::protobuf::TextFormat::PrintToString(cfg, &data);
|
|
||||||
|
|
||||||
// read back in and check everything..
|
|
||||||
EncfsConfig cfg2;
|
|
||||||
google::protobuf::TextFormat::ParseFromString(data, &cfg2);
|
|
||||||
|
|
||||||
// check..
|
|
||||||
rAssert( implements(cfg.cipher(),cfg2.cipher()) );
|
|
||||||
rAssert( cfg.key().size() == cfg2.key().size() );
|
|
||||||
rAssert( cfg.block_size() == cfg2.block_size() );
|
|
||||||
|
|
||||||
// try decoding key..
|
|
||||||
|
|
||||||
CipherKey key2 = cipher->readKey( (unsigned char *)cfg2.key().ciphertext().data(), 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FSConfigPtr fsCfg = FSConfigPtr(new FSConfig);
|
|
||||||
fsCfg->cipher = cipher;
|
|
||||||
fsCfg->key = key;
|
|
||||||
fsCfg->config.reset(new EncfsConfig);
|
|
||||||
fsCfg->config->set_block_size(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->set_unique_iv(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->set_unique_iv(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->set_unique_iv(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<NameIO>( 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<NameIO>( 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 );
|
|
||||||
|
|
||||||
StdioNode stdLog( STDERR_FILENO );
|
|
||||||
stdLog.subscribeTo( RLOG_CHANNEL("error") );
|
|
||||||
stdLog.subscribeTo( RLOG_CHANNEL("warning") );
|
|
||||||
#ifndef NO_DEBUG
|
|
||||||
stdLog.subscribeTo( RLOG_CHANNEL("debug") );
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef HAVE_SSL
|
|
||||||
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();
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
srand( time(0) );
|
|
||||||
|
|
||||||
// get a list of the available algorithms
|
|
||||||
std::list<Cipher::CipherAlgorithm> algorithms =
|
|
||||||
Cipher::GetAlgorithmList();
|
|
||||||
std::list<Cipher::CipherAlgorithm>::const_iterator it;
|
|
||||||
cerr << "Supported Crypto interfaces:\n";
|
|
||||||
for(it = algorithms.begin(); it != algorithms.end(); ++it)
|
|
||||||
{
|
|
||||||
cerr << it->name
|
|
||||||
<< " ( " << it->iface.name() << " "
|
|
||||||
<< it->iface.major() << ":"
|
|
||||||
<< it->iface.minor() << ":"
|
|
||||||
<< 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 = 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";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// run one test with verbose output too..
|
|
||||||
shared_ptr<Cipher> 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";
|
|
||||||
|
|
||||||
runTests( cipher, true );
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryPool::destroyAll();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
213
encfs/tests.t
213
encfs/tests.t
@ -1,213 +0,0 @@
|
|||||||
#!/usr/bin/perl -w
|
|
||||||
|
|
||||||
use Test::More qw( no_plan );
|
|
||||||
use File::Path;
|
|
||||||
use IO::Handle;
|
|
||||||
use Digest::MD5;
|
|
||||||
|
|
||||||
my $tempDir = $ENV{'TMPDIR'} || "/tmp";
|
|
||||||
|
|
||||||
my $raw = "$tempDir/crypt-raw-$$";
|
|
||||||
my $crypt = "$tempDir/crypt-$$";
|
|
||||||
|
|
||||||
|
|
||||||
# test filesystem in standard config mode
|
|
||||||
&runTests('standard');
|
|
||||||
|
|
||||||
# test in paranoia mode
|
|
||||||
&runTests('paranoia');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sub runTests
|
|
||||||
{
|
|
||||||
my $mode = shift;
|
|
||||||
|
|
||||||
my $hardlinks = 1;
|
|
||||||
if($mode eq 'standard')
|
|
||||||
{
|
|
||||||
&mount("--standard");
|
|
||||||
} elsif($mode eq 'paranoia')
|
|
||||||
{
|
|
||||||
&mount("--paranoia");
|
|
||||||
$hardlinks = 0; # no hardlinks in paranoia mode
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
die "invalid test mode";
|
|
||||||
}
|
|
||||||
|
|
||||||
# tests..
|
|
||||||
&fileCreation;
|
|
||||||
&links($hardlinks);
|
|
||||||
&truncate;
|
|
||||||
&renames;
|
|
||||||
|
|
||||||
&cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub renames
|
|
||||||
{
|
|
||||||
ok( open(F, ">$crypt/orig-name") && close F, "create file for rename test");
|
|
||||||
ok( -f "$crypt/orig-name", "file exists");
|
|
||||||
|
|
||||||
ok( rename("$crypt/orig-name", "$crypt/2nd-name"), "rename");
|
|
||||||
ok( ! -f "$crypt/orig-name", "file exists");
|
|
||||||
ok( -f "$crypt/2nd-name", "file exists");
|
|
||||||
|
|
||||||
# rename directory with contents
|
|
||||||
ok( mkpath("$crypt/orig-dir/foo"), "mkdir for rename test");
|
|
||||||
ok( open(F, ">$crypt/orig-dir/foo/bar") && close F, "make file");
|
|
||||||
|
|
||||||
ok( rename("$crypt/orig-dir", "$crypt/new-dir"), "rename dir");
|
|
||||||
ok( -f "$crypt/new-dir/foo/bar", "dir rename contents");
|
|
||||||
|
|
||||||
# TODO: rename failure? (check undo works)
|
|
||||||
|
|
||||||
# check time stamps of files on rename
|
|
||||||
my $mtime = (stat "$crypt/2nd-name")[9];
|
|
||||||
# change time to 60 seconds earlier
|
|
||||||
my $olderTime = $mtime - 60;
|
|
||||||
ok( utime($olderTime, $olderTime, "$crypt/2nd-name"), "change time");
|
|
||||||
|
|
||||||
ok( rename("$crypt/2nd-name", "$crypt/3rd-name"), "rename");
|
|
||||||
is( (stat "$crypt/3rd-name")[9], $olderTime, "time unchanged by rename");
|
|
||||||
}
|
|
||||||
|
|
||||||
sub truncate
|
|
||||||
{
|
|
||||||
# write to file, then truncate it
|
|
||||||
ok( open(OUT, "+> $crypt/trunc"), "create truncate-test file");
|
|
||||||
autoflush OUT 1;
|
|
||||||
print OUT "12345678901234567890";
|
|
||||||
|
|
||||||
is( -s "$crypt/trunc", 20, "initial file size" );
|
|
||||||
|
|
||||||
ok( truncate(OUT, 10), "truncate" );
|
|
||||||
|
|
||||||
is( -s "$crypt/trunc", 10, "truncated file size");
|
|
||||||
is( qx(cat "$crypt/trunc"), "1234567890", "truncated file contents");
|
|
||||||
|
|
||||||
# try growing the file as well.
|
|
||||||
ok( truncate(OUT, 30), "truncate extend");
|
|
||||||
is( -s "$crypt/trunc", 30, "truncated file size");
|
|
||||||
|
|
||||||
seek(OUT, 30, 0);
|
|
||||||
print OUT "12345";
|
|
||||||
is( -s "$crypt/trunc", 35, "truncated file size");
|
|
||||||
|
|
||||||
seek(OUT, 0, 0);
|
|
||||||
is( Digest::MD5->new->addfile(*OUT)->hexdigest,
|
|
||||||
"5f170cc34b1944d75d86cc01496292df", "content digest");
|
|
||||||
|
|
||||||
# try crossing block boundaries
|
|
||||||
seek(OUT, 10000,0);
|
|
||||||
print OUT "abcde";
|
|
||||||
seek(OUT, 0, 0);
|
|
||||||
is( Digest::MD5->new->addfile(*OUT)->hexdigest,
|
|
||||||
"117a51c980b64dcd21df097d02206f98", "content digest");
|
|
||||||
|
|
||||||
# then truncate back to 35 chars
|
|
||||||
truncate(OUT, 35);
|
|
||||||
seek(OUT, 0, 0);
|
|
||||||
is( Digest::MD5->new->addfile(*OUT)->hexdigest,
|
|
||||||
"5f170cc34b1944d75d86cc01496292df", "content digest");
|
|
||||||
|
|
||||||
close OUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub fileCreation
|
|
||||||
{
|
|
||||||
# create a file
|
|
||||||
qx(df -ah > "$crypt/df.txt");
|
|
||||||
ok( -f "$crypt/df.txt", "file created" );
|
|
||||||
|
|
||||||
# ensure there is an encrypted version.
|
|
||||||
my $c = qx(./encfsctl encode --extpass="echo test" $raw df.txt);
|
|
||||||
chomp($c);
|
|
||||||
cmp_ok( length($c), '>', 8, "encrypted name ok" );
|
|
||||||
ok( -f "$raw/$c", "encrypted file created" );
|
|
||||||
|
|
||||||
# check contents
|
|
||||||
my $count = qx(grep -c crypt-$$ "$crypt/df.txt");
|
|
||||||
isnt(scalar($count), 0, "encrypted file readable");
|
|
||||||
|
|
||||||
unlink "$crypt/df.txt";
|
|
||||||
ok( ! -f "$crypt/df.txt", "file removal" );
|
|
||||||
ok( ! -f "$raw/$c", "file removal" );
|
|
||||||
}
|
|
||||||
|
|
||||||
sub checkContents
|
|
||||||
{
|
|
||||||
my ($file, $expected, $testName) = @_;
|
|
||||||
|
|
||||||
open(IN, "< $file");
|
|
||||||
my $line = <IN>;
|
|
||||||
is( $line, $expected, $testName );
|
|
||||||
|
|
||||||
close IN;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub links
|
|
||||||
{
|
|
||||||
my $hardlinkTests = shift;
|
|
||||||
|
|
||||||
my $contents = "hello world\n";
|
|
||||||
ok( open(OUT, "> $crypt/data"), "create file for link test" );
|
|
||||||
print OUT $contents;
|
|
||||||
close OUT;
|
|
||||||
|
|
||||||
# symlinks
|
|
||||||
ok( symlink("$crypt/data", "$crypt/data-fqn") , "fqn symlink");
|
|
||||||
checkContents("$crypt/data-fqn", $contents, "fqn link traversal");
|
|
||||||
is( readlink("$crypt/data-fqn"), "$crypt/data", "read fqn symlink");
|
|
||||||
|
|
||||||
ok( symlink("data", "$crypt/data-rel"), "local symlink");
|
|
||||||
checkContents("$crypt/data-rel", $contents, "rel link traversal");
|
|
||||||
is( readlink("$crypt/data-rel"), "data", "read rel symlink");
|
|
||||||
|
|
||||||
SKIP: {
|
|
||||||
skip "No hardlink support" unless $hardlinkTests;
|
|
||||||
|
|
||||||
ok( link("$crypt/data", "$crypt/data.2"), "hard link");
|
|
||||||
checkContents("$crypt/data.2", $contents, "hardlink read");
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
sub mount
|
|
||||||
{
|
|
||||||
my $args = shift;
|
|
||||||
|
|
||||||
ok( ! -d $raw, "no existing dir");
|
|
||||||
ok( ! -d $crypt, "no existing dir");
|
|
||||||
|
|
||||||
mkdir $raw;
|
|
||||||
ok( -d $raw, "created dir" );
|
|
||||||
mkdir $crypt;
|
|
||||||
ok( -d $crypt, "created dir" );
|
|
||||||
|
|
||||||
qx(./encfs --extpass="echo test" $args $raw $crypt);
|
|
||||||
|
|
||||||
ok( -f "$raw/.encfs6.xml", "created control file");
|
|
||||||
}
|
|
||||||
|
|
||||||
sub cleanup
|
|
||||||
{
|
|
||||||
my $fusermount = qx(which fusermount);
|
|
||||||
if(-f $fusermount)
|
|
||||||
{
|
|
||||||
qx($fusermount -u "$crypt");
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
qx(umount "$crypt");
|
|
||||||
}
|
|
||||||
|
|
||||||
rmdir $crypt;
|
|
||||||
ok( ! -d $crypt, "unmount ok, mount point removed");
|
|
||||||
|
|
||||||
if(-d $raw)
|
|
||||||
{
|
|
||||||
rmtree($raw);
|
|
||||||
}
|
|
||||||
ok( ! -d $raw, "encrypted directory removed");
|
|
||||||
}
|
|
||||||
|
|
426
fs/BlockFileIO.cpp
Normal file
426
fs/BlockFileIO.cpp
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2004, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fs/BlockFileIO.h"
|
||||||
|
|
||||||
|
#include "base/config.pb.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "base/i18n.h"
|
||||||
|
#include "cipher/MemoryPool.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockFileIO::BlockFileIO( int blockSize, const FSConfigPtr &cfg )
|
||||||
|
: _blockSize( blockSize )
|
||||||
|
, _allowHoles( cfg->config->allow_holes() )
|
||||||
|
{
|
||||||
|
rAssert( _blockSize > 1 );
|
||||||
|
_cache.data = new unsigned char [ _blockSize ];
|
||||||
|
}
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.allocate( _blockSize );
|
||||||
|
blockReq.data = mb.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t readSize = cacheReadOneBlock( blockReq );
|
||||||
|
if(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(readSize < _blockSize)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 lastBlockSize = 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.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
|
||||||
|
VLOG(1) << "optimization: not padding last block";
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
mb.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)
|
||||||
|
{
|
||||||
|
VLOG(1) << "padding block " << 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)
|
||||||
|
{
|
||||||
|
VLOG(1) << "padding block " << 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 && lastBlockSize)
|
||||||
|
{
|
||||||
|
req.offset = newLastBlock * _blockSize;
|
||||||
|
req.dataLen = lastBlockSize;
|
||||||
|
memset( mb.data, 0, req.dataLen );
|
||||||
|
cacheWriteOneBlock( req );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int BlockFileIO::blockTruncate( off_t size, FileIO *base )
|
||||||
|
{
|
||||||
|
rAssert(size >= 0);
|
||||||
|
|
||||||
|
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;
|
||||||
|
mb.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))
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "truncate failure: read size " << rdSz
|
||||||
|
<< ", partial block of " << partialBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// truncating on a block bounday. No need to re-encode the last
|
||||||
|
// block..
|
||||||
|
if(base)
|
||||||
|
res = base->truncate( size );
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
@ -46,7 +46,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
int truncate( off_t size, FileIO *base );
|
int blockTruncate( off_t size, FileIO *base );
|
||||||
void padFile( off_t oldSize, off_t newSize, bool forceWrite );
|
void padFile( off_t oldSize, off_t newSize, bool forceWrite );
|
||||||
|
|
||||||
// same as read(), except that the request.offset field is guarenteed to be
|
// same as read(), except that the request.offset field is guarenteed to be
|
250
fs/BlockNameIO.cpp
Normal file
250
fs/BlockNameIO.cpp
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2004-2011, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fs/BlockNameIO.h"
|
||||||
|
|
||||||
|
#include "base/base64.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "base/i18n.h"
|
||||||
|
#include "cipher/Cipher.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
static shared_ptr<NameIO> NewBlockNameIO( const Interface &iface,
|
||||||
|
const shared_ptr<Cipher> &cipher, const CipherKey &key )
|
||||||
|
{
|
||||||
|
return shared_ptr<NameIO>(
|
||||||
|
new BlockNameIO( iface, cipher, key, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
static shared_ptr<NameIO> NewBlockNameIO32( const Interface &iface,
|
||||||
|
const shared_ptr<Cipher> &cipher, const CipherKey &key )
|
||||||
|
{
|
||||||
|
return shared_ptr<NameIO>(
|
||||||
|
new BlockNameIO( iface, cipher, key, 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, false);
|
||||||
|
|
||||||
|
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, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
- Version 1.0 computed MAC over the filename, but not the padding bytes.
|
||||||
|
This version was from pre-release 1.1, never publically released, so no
|
||||||
|
backward compatibility necessary.
|
||||||
|
|
||||||
|
- Version 2.0 includes padding bytes in MAC computation. This way the MAC
|
||||||
|
computation uses the same number of bytes regardless of the number of
|
||||||
|
padding bytes.
|
||||||
|
|
||||||
|
- Version 3.0 uses full 64 bit initialization vector during IV chaining.
|
||||||
|
Prior versions used only the output from the MAC_16 call, giving a 1 in
|
||||||
|
2^16 chance of the same name being produced. Using the full 64 bit IV
|
||||||
|
changes that to a 1 in 2^64 chance..
|
||||||
|
|
||||||
|
- 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 makeInterface("nameio/block32", 4, 0, 2);
|
||||||
|
else
|
||||||
|
return makeInterface("nameio/block", 4, 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockNameIO::BlockNameIO( const Interface &iface,
|
||||||
|
const shared_ptr<Cipher> &cipher,
|
||||||
|
const CipherKey &key, bool caseSensitiveEncoding )
|
||||||
|
: _interface( iface.major() )
|
||||||
|
, _bs( cipher->cipherBlockSize() )
|
||||||
|
, _cipher( cipher )
|
||||||
|
, _key( key )
|
||||||
|
, _caseSensitive( caseSensitiveEncoding )
|
||||||
|
{
|
||||||
|
rAssert( _bs < 128 );
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockNameIO::~BlockNameIO()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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::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::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)
|
||||||
|
{
|
||||||
|
VLOG(1) << "padding, _bx, finalSize = " << 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)
|
||||||
|
{
|
||||||
|
LOG(INFO) << "checksum mismatch: expected " << mac << ", got "
|
||||||
|
<< mac2 << " on decode of " << finalSize << " bytes";
|
||||||
|
throw Error( "checksum mismatch in filename decode" );
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BlockNameIO::Enabled()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
@ -21,8 +21,8 @@
|
|||||||
#ifndef _BlockNameIO_incl_
|
#ifndef _BlockNameIO_incl_
|
||||||
#define _BlockNameIO_incl_
|
#define _BlockNameIO_incl_
|
||||||
|
|
||||||
#include "NameIO.h"
|
#include "cipher/CipherKey.h"
|
||||||
#include "CipherKey.h"
|
#include "fs/NameIO.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ public:
|
|||||||
|
|
||||||
BlockNameIO( const Interface &iface,
|
BlockNameIO( const Interface &iface,
|
||||||
const shared_ptr<Cipher> &cipher,
|
const shared_ptr<Cipher> &cipher,
|
||||||
const CipherKey &key, int blockSize,
|
const CipherKey &key,
|
||||||
bool caseSensitiveEncoding = false );
|
bool caseSensitiveEncoding = false );
|
||||||
virtual ~BlockNameIO();
|
virtual ~BlockNameIO();
|
||||||
|
|
56
fs/CMakeLists.txt
Normal file
56
fs/CMakeLists.txt
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
find_package (FUSE REQUIRED)
|
||||||
|
include_directories (${FUSE_INCLUDE_DIR})
|
||||||
|
|
||||||
|
enable_testing ()
|
||||||
|
find_package (GTest)
|
||||||
|
|
||||||
|
add_library (encfs-fs
|
||||||
|
encfs.cpp
|
||||||
|
Context.cpp
|
||||||
|
FileIO.cpp
|
||||||
|
RawFileIO.cpp
|
||||||
|
BlockFileIO.cpp
|
||||||
|
CipherFileIO.cpp
|
||||||
|
MACFileIO.cpp
|
||||||
|
NameIO.cpp
|
||||||
|
StreamNameIO.cpp
|
||||||
|
BlockNameIO.cpp
|
||||||
|
NullNameIO.cpp
|
||||||
|
DirNode.cpp
|
||||||
|
FileNode.cpp
|
||||||
|
FileUtils.cpp
|
||||||
|
${PROTO_SRCS}
|
||||||
|
${PROTO_HDRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries (encfs-fs
|
||||||
|
${PROTOBUF_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Unit tests are optional, depends on libgtest (Google's C++ test framework).
|
||||||
|
if (GTEST_FOUND)
|
||||||
|
link_directories (${Encfs_BINARY_DIR}/base)
|
||||||
|
link_directories (${Encfs_BINARY_DIR}/cipher)
|
||||||
|
|
||||||
|
include_directories (${GTEST_INCLUDE_DIR})
|
||||||
|
add_executable (unittests
|
||||||
|
MemBlockFileIO.cpp
|
||||||
|
MemFileIO.cpp
|
||||||
|
testing.cpp
|
||||||
|
test_IO.cpp
|
||||||
|
test_BlockIO.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries (unittests
|
||||||
|
${GTEST_BOTH_LIBRARIES}
|
||||||
|
encfs-fs
|
||||||
|
encfs-cipher
|
||||||
|
encfs-base
|
||||||
|
${GLOG_LIBRARIES}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test (UnitTests unittests)
|
||||||
|
GTEST_ADD_TESTS (unittests "${UnitTestArgs}" test_IO.cpp test_BlockIO.cpp)
|
||||||
|
add_custom_target (test COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS unittests)
|
||||||
|
|
||||||
|
endif (GTEST_FOUND)
|
513
fs/CipherFileIO.cpp
Normal file
513
fs/CipherFileIO.cpp
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2004-2013, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fs/CipherFileIO.h"
|
||||||
|
|
||||||
|
#include "base/config.pb.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "cipher/Cipher.h"
|
||||||
|
#include "cipher/MemoryPool.h"
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Version 3:0 adds support for block-only encryption by adding space for
|
||||||
|
a full block to the file header.
|
||||||
|
|
||||||
|
Version 2:0 adds support for a per-file initialization vector with a
|
||||||
|
fixed 8 byte header. The headers are enabled globally within a
|
||||||
|
filesystem at the filesystem configuration level.
|
||||||
|
When headers are disabled, 2:0 is compatible with version 1:0.
|
||||||
|
*/
|
||||||
|
static Interface CipherFileIO_iface = makeInterface("FileIO/Cipher", 3, 0, 2);
|
||||||
|
|
||||||
|
CipherFileIO::CipherFileIO( const shared_ptr<FileIO> &_base,
|
||||||
|
const FSConfigPtr &cfg)
|
||||||
|
: BlockFileIO( cfg->config->block_size(), cfg )
|
||||||
|
, base( _base )
|
||||||
|
, headerLen( 0 )
|
||||||
|
, blockOnlyMode( cfg->config->block_mode_only() )
|
||||||
|
, perFileIV( cfg->config->unique_iv() )
|
||||||
|
, externalIV( 0 )
|
||||||
|
, fileIV( 0 )
|
||||||
|
, lastFlags( 0 )
|
||||||
|
{
|
||||||
|
fsConfig = cfg;
|
||||||
|
cipher = cfg->cipher;
|
||||||
|
key = cfg->key;
|
||||||
|
|
||||||
|
if ( blockOnlyMode )
|
||||||
|
{
|
||||||
|
headerLen += blockSize();
|
||||||
|
if ( perFileIV )
|
||||||
|
headerLen += cipher->cipherBlockSize();
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
if ( perFileIV )
|
||||||
|
headerLen += sizeof(uint64_t); // 64bit IV per file
|
||||||
|
}
|
||||||
|
|
||||||
|
int blockBoundary = fsConfig->config->block_size() %
|
||||||
|
fsConfig->cipher->cipherBlockSize();
|
||||||
|
if(blockBoundary != 0)
|
||||||
|
{
|
||||||
|
LOG_FIRST_N(ERROR, 1)
|
||||||
|
<< "CipherFileIO: blocks should be multiple of cipher block size";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CipherFileIO::~CipherFileIO()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Interface CipherFileIO::interface() const
|
||||||
|
{
|
||||||
|
return CipherFileIO_iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CipherFileIO::open( int flags )
|
||||||
|
{
|
||||||
|
int res = base->open( flags );
|
||||||
|
|
||||||
|
if( res >= 0 )
|
||||||
|
lastFlags = flags;
|
||||||
|
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
VLOG(1) << "in setIV, current IV = " << externalIV
|
||||||
|
<< ", new IV = " << iv << ", fileIV = " << 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;
|
||||||
|
LOG_IF(WARNING, fileIV != 0)
|
||||||
|
<< "fileIV initialized before externalIV! (" << fileIV
|
||||||
|
<< ", " << externalIV << ")";
|
||||||
|
} else if(perFileIV)
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
VLOG(1) << "writeHeader failed to re-open for write";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
initHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t oldIV = externalIV;
|
||||||
|
externalIV = iv;
|
||||||
|
if(!writeHeader())
|
||||||
|
{
|
||||||
|
externalIV = oldIV;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return base->setIV( iv );
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t CipherFileIO::adjustedSize(off_t rawSize) const
|
||||||
|
{
|
||||||
|
off_t size = rawSize;
|
||||||
|
|
||||||
|
if (rawSize >= headerLen)
|
||||||
|
size -= headerLen;
|
||||||
|
|
||||||
|
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) && S_ISREG(stbuf->st_mode))
|
||||||
|
stbuf->st_size = adjustedSize(stbuf->st_size);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t CipherFileIO::getSize() const
|
||||||
|
{
|
||||||
|
// No check on S_ISREG here -- getSize only for normal files!
|
||||||
|
off_t size = base->getSize();
|
||||||
|
return adjustedSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CipherFileIO::initHeader( )
|
||||||
|
{
|
||||||
|
int cbs = cipher->cipherBlockSize();
|
||||||
|
|
||||||
|
MemBlock mb;
|
||||||
|
mb.allocate(cbs);
|
||||||
|
|
||||||
|
// check if the file has a header, and read it if it does.. Otherwise,
|
||||||
|
// create one.
|
||||||
|
off_t rawSize = base->getSize();
|
||||||
|
if(rawSize >= headerLen)
|
||||||
|
{
|
||||||
|
VLOG(1) << "reading existing header, rawSize = " << rawSize;
|
||||||
|
|
||||||
|
IORequest req;
|
||||||
|
req.offset = 0;
|
||||||
|
if (blockOnlyMode)
|
||||||
|
req.offset += blockSize();
|
||||||
|
|
||||||
|
req.data = mb.data;
|
||||||
|
req.dataLen = blockOnlyMode ? cbs : sizeof(uint64_t);
|
||||||
|
base->read( req );
|
||||||
|
|
||||||
|
if (perFileIV)
|
||||||
|
{
|
||||||
|
if (blockOnlyMode)
|
||||||
|
cipher->blockDecode( mb.data, cbs, externalIV, key );
|
||||||
|
else
|
||||||
|
cipher->streamDecode( mb.data, sizeof(uint64_t), externalIV, key );
|
||||||
|
|
||||||
|
fileIV = 0;
|
||||||
|
for(unsigned int i=0; i<sizeof(uint64_t); ++i)
|
||||||
|
fileIV = (fileIV << 8) | (uint64_t)mb.data[i];
|
||||||
|
|
||||||
|
rAssert(fileIV != 0); // 0 is never used..
|
||||||
|
}
|
||||||
|
} else if (perFileIV)
|
||||||
|
{
|
||||||
|
VLOG(1) << "creating new file IV header";
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if(!cipher->randomize( mb.data, 8, false ))
|
||||||
|
throw Error("Unable to generate a random file IV");
|
||||||
|
|
||||||
|
fileIV = 0;
|
||||||
|
for(unsigned int i=0; i<sizeof(uint64_t); ++i)
|
||||||
|
fileIV = (fileIV << 8) | (uint64_t)mb.data[i];
|
||||||
|
|
||||||
|
LOG_IF(WARNING, fileIV == 0)
|
||||||
|
<< "Unexpected result: randomize returned 8 null bytes!";
|
||||||
|
} while(fileIV == 0); // don't accept 0 as an option..
|
||||||
|
|
||||||
|
if (blockOnlyMode)
|
||||||
|
cipher->blockEncode( mb.data, cbs, externalIV, key );
|
||||||
|
else
|
||||||
|
cipher->streamEncode( mb.data, sizeof(uint64_t), externalIV, key );
|
||||||
|
|
||||||
|
if( base->isWritable() )
|
||||||
|
{
|
||||||
|
IORequest req;
|
||||||
|
req.offset = 0;
|
||||||
|
if (blockOnlyMode)
|
||||||
|
req.offset += blockSize();
|
||||||
|
|
||||||
|
req.data = mb.data;
|
||||||
|
req.dataLen = blockOnlyMode ? cbs : sizeof(uint64_t);
|
||||||
|
|
||||||
|
base->write( req );
|
||||||
|
} else
|
||||||
|
VLOG(1) << "base not writable, IV not written..";
|
||||||
|
}
|
||||||
|
VLOG(1) << "initHeader finished, fileIV = " << fileIV;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CipherFileIO::writeHeader( )
|
||||||
|
{
|
||||||
|
if( !base->isWritable() )
|
||||||
|
{
|
||||||
|
// open for write..
|
||||||
|
int newFlags = lastFlags | O_RDWR;
|
||||||
|
if( base->open( newFlags ) < 0 )
|
||||||
|
{
|
||||||
|
VLOG(1) << "writeHeader failed to re-open for write";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_IF(ERROR, fileIV == 0)
|
||||||
|
<< "Internal error: fileIV == 0 in writeHeader!!!";
|
||||||
|
VLOG(1) << "writing fileIV " << fileIV;
|
||||||
|
|
||||||
|
MemBlock mb;
|
||||||
|
mb.allocate(headerLen);
|
||||||
|
|
||||||
|
if (perFileIV)
|
||||||
|
{
|
||||||
|
int cbs = cipher->cipherBlockSize();
|
||||||
|
unsigned char *buf = mb.data + (blockOnlyMode ? blockSize() : 0);
|
||||||
|
|
||||||
|
for(int i=sizeof(buf)-1; i>=0; --i)
|
||||||
|
{
|
||||||
|
buf[i] = (unsigned char)(fileIV & 0xff);
|
||||||
|
fileIV >>= 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockOnlyMode)
|
||||||
|
cipher->blockEncode( buf, cbs, externalIV, key );
|
||||||
|
else
|
||||||
|
cipher->streamEncode( buf, sizeof(uint64_t), externalIV, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
IORequest req;
|
||||||
|
req.offset = 0;
|
||||||
|
req.data = mb.data;
|
||||||
|
req.dataLen = headerLen;
|
||||||
|
|
||||||
|
base->write( req );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t CipherFileIO::readOneBlock( const IORequest &req ) const
|
||||||
|
{
|
||||||
|
// read raw data, then decipher it..
|
||||||
|
int bs = blockSize();
|
||||||
|
rAssert(req.dataLen <= bs);
|
||||||
|
|
||||||
|
off_t blockNum = req.offset / bs;
|
||||||
|
|
||||||
|
ssize_t readSize = 0;
|
||||||
|
IORequest tmpReq = req;
|
||||||
|
|
||||||
|
MemBlock mb;
|
||||||
|
if (headerLen != 0)
|
||||||
|
tmpReq.offset += headerLen;
|
||||||
|
|
||||||
|
int maxReadSize = req.dataLen;
|
||||||
|
if (blockOnlyMode)
|
||||||
|
{
|
||||||
|
off_t size = getSize();
|
||||||
|
if (req.offset + req.dataLen > size)
|
||||||
|
{
|
||||||
|
// Last block written as full block at front of the file header.
|
||||||
|
mb.allocate(bs);
|
||||||
|
|
||||||
|
tmpReq.offset = 0;
|
||||||
|
tmpReq.dataLen = bs;
|
||||||
|
tmpReq.data = mb.data;
|
||||||
|
|
||||||
|
// TODO: what is the expected behavior if req.offset >= size?
|
||||||
|
maxReadSize = size - req.offset;
|
||||||
|
if (maxReadSize <= 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readSize = base->read( tmpReq );
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
if(readSize > 0)
|
||||||
|
{
|
||||||
|
if(headerLen != 0 && fileIV == 0)
|
||||||
|
const_cast<CipherFileIO*>(this)->initHeader();
|
||||||
|
|
||||||
|
if(blockOnlyMode || readSize == bs)
|
||||||
|
{
|
||||||
|
ok = blockRead( tmpReq.data, bs, blockNum ^ fileIV);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ok = streamRead( tmpReq.data, (int)readSize, blockNum ^ fileIV);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ok)
|
||||||
|
{
|
||||||
|
VLOG(1) << "decodeBlock failed for block " << blockNum
|
||||||
|
<< ", size " << readSize;
|
||||||
|
readSize = -1;
|
||||||
|
} else if (tmpReq.data != req.data)
|
||||||
|
{
|
||||||
|
if (readSize > maxReadSize)
|
||||||
|
readSize = maxReadSize;
|
||||||
|
memcpy(req.data, tmpReq.data, readSize);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
VLOG(1) << "readSize zero for offset " << req.offset;
|
||||||
|
|
||||||
|
return readSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CipherFileIO::writeOneBlock( const IORequest &req )
|
||||||
|
{
|
||||||
|
int bs = blockSize();
|
||||||
|
int cbs = cipher->cipherBlockSize();
|
||||||
|
off_t blockNum = req.offset / bs;
|
||||||
|
|
||||||
|
if(headerLen != 0 && fileIV == 0)
|
||||||
|
initHeader();
|
||||||
|
|
||||||
|
MemBlock mb;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
if (req.dataLen == bs)
|
||||||
|
{
|
||||||
|
ok = blockWrite( req.data, bs, blockNum ^ fileIV );
|
||||||
|
} else if (blockOnlyMode)
|
||||||
|
{
|
||||||
|
mb.allocate(bs);
|
||||||
|
cipher->randomize(mb.data + bs - cbs, cbs, false);
|
||||||
|
memcpy(mb.data, req.data, req.dataLen);
|
||||||
|
|
||||||
|
ok = blockWrite( mb.data, bs, blockNum ^ fileIV );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ok = streamWrite( req.data, (int)req.dataLen,
|
||||||
|
blockNum ^ fileIV );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( ok )
|
||||||
|
{
|
||||||
|
if(headerLen != 0)
|
||||||
|
{
|
||||||
|
IORequest nreq = req;
|
||||||
|
|
||||||
|
if (mb.data == NULL)
|
||||||
|
{
|
||||||
|
nreq.offset += headerLen;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
// Partial block is stored at front of file.
|
||||||
|
nreq.offset = 0;
|
||||||
|
nreq.data = mb.data;
|
||||||
|
nreq.dataLen = bs;
|
||||||
|
base->truncate(req.offset + req.dataLen + headerLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = base->write( nreq );
|
||||||
|
} else
|
||||||
|
ok = base->write( req );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
VLOG(1) << "encodeBlock failed for block " << blockNum
|
||||||
|
<< ", size " << 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::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 );
|
||||||
|
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
rAssert(size >= 0);
|
||||||
|
|
||||||
|
if(headerLen == 0)
|
||||||
|
{
|
||||||
|
return blockTruncate( 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 )
|
||||||
|
VLOG(1) << "writeHeader failed to re-open for write";
|
||||||
|
}
|
||||||
|
initHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't let BlockFileIO call base->truncate(), since it would be using
|
||||||
|
// the wrong size..
|
||||||
|
int res = blockTruncate( size, 0 );
|
||||||
|
|
||||||
|
if(res == 0)
|
||||||
|
base->truncate( size + headerLen );
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CipherFileIO::isWritable() const
|
||||||
|
{
|
||||||
|
return base->isWritable();
|
||||||
|
}
|
||||||
|
|
@ -21,9 +21,9 @@
|
|||||||
#ifndef _CipherFileIO_incl_
|
#ifndef _CipherFileIO_incl_
|
||||||
#define _CipherFileIO_incl_
|
#define _CipherFileIO_incl_
|
||||||
|
|
||||||
#include "BlockFileIO.h"
|
#include "cipher/CipherKey.h"
|
||||||
#include "CipherKey.h"
|
#include "fs/BlockFileIO.h"
|
||||||
#include "FileUtils.h"
|
#include "fs/FileUtils.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
@ -52,6 +52,9 @@ public:
|
|||||||
virtual int getAttr( struct stat *stbuf ) const;
|
virtual int getAttr( struct stat *stbuf ) const;
|
||||||
virtual off_t getSize() const;
|
virtual off_t getSize() const;
|
||||||
|
|
||||||
|
// NOTE: if truncate is used to extend the file, the extended plaintext is
|
||||||
|
// not 0. The extended ciphertext may be 0, resulting in non-zero
|
||||||
|
// plaintext.
|
||||||
virtual int truncate( off_t size );
|
virtual int truncate( off_t size );
|
||||||
|
|
||||||
virtual bool isWritable() const;
|
virtual bool isWritable() const;
|
||||||
@ -71,13 +74,18 @@ private:
|
|||||||
bool streamWrite( unsigned char *buf, int size,
|
bool streamWrite( unsigned char *buf, int size,
|
||||||
uint64_t iv64 ) const;
|
uint64_t iv64 ) const;
|
||||||
|
|
||||||
|
off_t adjustedSize(off_t size) const;
|
||||||
|
|
||||||
shared_ptr<FileIO> base;
|
shared_ptr<FileIO> base;
|
||||||
|
|
||||||
FSConfigPtr fsConfig;
|
FSConfigPtr fsConfig;
|
||||||
|
|
||||||
// if haveHeader is true, then we have a transparent file header which
|
// if haveHeader is true, then we have a transparent file header which
|
||||||
// contains a 64 bit initialization vector.
|
int headerLen;
|
||||||
bool haveHeader;
|
// Use block only encryption, no stream encryption.
|
||||||
|
bool blockOnlyMode;
|
||||||
|
|
||||||
|
bool perFileIV;
|
||||||
bool externalIVChaining;
|
bool externalIVChaining;
|
||||||
uint64_t externalIV;
|
uint64_t externalIV;
|
||||||
uint64_t fileIV;
|
uint64_t fileIV;
|
175
fs/Context.cpp
Normal file
175
fs/Context.cpp
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2007, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "base/Mutex.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "fs/FileNode.h"
|
||||||
|
#include "fs/Context.h"
|
||||||
|
#include "fs/FileUtils.h"
|
||||||
|
#include "fs/DirNode.h"
|
||||||
|
|
||||||
|
using namespace rel;
|
||||||
|
|
||||||
|
EncFS_Context::EncFS_Context()
|
||||||
|
{
|
||||||
|
pthread_cond_init( &wakeupCond, 0 );
|
||||||
|
pthread_mutex_init( &wakeupMutex, 0 );
|
||||||
|
pthread_mutex_init( &contextMutex, 0 );
|
||||||
|
|
||||||
|
usageCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncFS_Context::~EncFS_Context()
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy( &contextMutex );
|
||||||
|
pthread_mutex_destroy( &wakeupMutex );
|
||||||
|
pthread_cond_destroy( &wakeupCond );
|
||||||
|
|
||||||
|
// release all entries from map
|
||||||
|
openFiles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<DirNode> EncFS_Context::getRoot(int *errCode)
|
||||||
|
{
|
||||||
|
shared_ptr<DirNode> 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<DirNode> &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<FileNode> 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<FileNode>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Placeholder *> val = it->second;
|
||||||
|
openFiles.erase(it);
|
||||||
|
openFiles[ std::string(to) ] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<FileNode> EncFS_Context::getNode(void *pl)
|
||||||
|
{
|
||||||
|
Placeholder *ph = (Placeholder*)pl;
|
||||||
|
return ph->node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *EncFS_Context::putNode(const char *path,
|
||||||
|
const shared_ptr<FileNode> &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;
|
||||||
|
}
|
||||||
|
|
@ -21,11 +21,11 @@
|
|||||||
#ifndef _Context_incl_
|
#ifndef _Context_incl_
|
||||||
#define _Context_incl_
|
#define _Context_incl_
|
||||||
|
|
||||||
#include "encfs.h"
|
#include "base/shared_ptr.h"
|
||||||
#include "shared_ptr.h"
|
#include "fs/encfs.h"
|
||||||
#include <set>
|
#include <set>
|
||||||
|
|
||||||
#if HAVE_TR1_UNORDERED_MAP
|
#ifdef HAVE_TR1_UNORDERED_MAP
|
||||||
#include <tr1/unordered_map>
|
#include <tr1/unordered_map>
|
||||||
using std::tr1::unordered_map;
|
using std::tr1::unordered_map;
|
||||||
#else
|
#else
|
816
fs/DirNode.cpp
Normal file
816
fs/DirNode.cpp
Normal file
@ -0,0 +1,816 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2003-2004, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fs/encfs.h"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#ifdef linux
|
||||||
|
#include <sys/fsuid.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "cipher/Cipher.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "base/Mutex.h"
|
||||||
|
#include "fs/Context.h"
|
||||||
|
#include "fs/DirNode.h"
|
||||||
|
#include "fs/FileUtils.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace rel;
|
||||||
|
|
||||||
|
class DirDeleter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void operator () ( DIR *d )
|
||||||
|
{
|
||||||
|
::closedir( d );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
DirTraverse::DirTraverse(const shared_ptr<DIR> &_dirPtr,
|
||||||
|
uint64_t _iv, const shared_ptr<NameIO> &_naming)
|
||||||
|
: dir( _dirPtr )
|
||||||
|
, iv( _iv )
|
||||||
|
, naming( _naming )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DirTraverse::DirTraverse(const DirTraverse &src)
|
||||||
|
: 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()
|
||||||
|
{
|
||||||
|
dir.reset();
|
||||||
|
iv = 0;
|
||||||
|
naming.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
bool _nextName(struct dirent *&de, const shared_ptr<DIR> &dir,
|
||||||
|
int *fileType, ino_t *inode)
|
||||||
|
{
|
||||||
|
de = ::readdir( dir.get() );
|
||||||
|
|
||||||
|
if(de)
|
||||||
|
{
|
||||||
|
if(fileType)
|
||||||
|
{
|
||||||
|
#if defined(_DIRENT_HAVE_D_TYPE) || defined(__FreeBSD__)
|
||||||
|
*fileType = de->d_type;
|
||||||
|
#else
|
||||||
|
#warning "struct dirent.d_type not supported"
|
||||||
|
*fileType = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
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 ( Error &ex )
|
||||||
|
{
|
||||||
|
// .. .problem decoding, ignore it and continue on to next name..
|
||||||
|
VLOG(1) << "error decoding filename " << de->d_name
|
||||||
|
<< " : " << ex.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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( Error &ex )
|
||||||
|
{
|
||||||
|
return string( de->d_name );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RenameEl
|
||||||
|
{
|
||||||
|
// ciphertext names
|
||||||
|
string oldCName;
|
||||||
|
string newCName; // intermediate name (not final cname)
|
||||||
|
|
||||||
|
// plaintext names
|
||||||
|
string oldPName;
|
||||||
|
string newPName;
|
||||||
|
|
||||||
|
bool isDirectory;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RenameOp
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
DirNode *dn;
|
||||||
|
shared_ptr< list<RenameEl> > renameList;
|
||||||
|
list<RenameEl>::const_iterator last;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RenameOp( DirNode *_dn, const shared_ptr< list<RenameEl> > &_renameList )
|
||||||
|
: dn(_dn), renameList(_renameList)
|
||||||
|
{
|
||||||
|
last = renameList->begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
RenameOp(const RenameOp &src)
|
||||||
|
: dn(src.dn)
|
||||||
|
, renameList(src.renameList)
|
||||||
|
, last(src.last)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~RenameOp();
|
||||||
|
|
||||||
|
operator bool () const
|
||||||
|
{
|
||||||
|
return renameList;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool apply();
|
||||||
|
void undo();
|
||||||
|
};
|
||||||
|
|
||||||
|
RenameOp::~RenameOp()
|
||||||
|
{
|
||||||
|
if(renameList)
|
||||||
|
{
|
||||||
|
// got a bunch of decoded filenames sitting in memory.. do a little
|
||||||
|
// cleanup before leaving..
|
||||||
|
list<RenameEl>::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.
|
||||||
|
VLOG(2) << "renaming " << last->oldCName << "-> " << last->newCName;
|
||||||
|
|
||||||
|
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() );
|
||||||
|
|
||||||
|
// rename on disk..
|
||||||
|
if(::rename( last->oldCName.c_str(),
|
||||||
|
last->newCName.c_str() ) == -1)
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "Error renaming " << last->oldCName << ": " <<
|
||||||
|
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( Error &err )
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "caught error in rename application: " << err.what();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RenameOp::undo()
|
||||||
|
{
|
||||||
|
VLOG(1) << "in undoRename";
|
||||||
|
|
||||||
|
if(last == renameList->begin())
|
||||||
|
{
|
||||||
|
VLOG(1) << "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;
|
||||||
|
int errorCount = 0;
|
||||||
|
list<RenameEl>::const_iterator it = last;
|
||||||
|
|
||||||
|
while( it != renameList->begin() )
|
||||||
|
{
|
||||||
|
--it;
|
||||||
|
|
||||||
|
VLOG(1) << "undo: renaming " << it->newCName << " -> " << it->oldCName;
|
||||||
|
|
||||||
|
::rename( it->newCName.c_str(), it->oldCName.c_str() );
|
||||||
|
try
|
||||||
|
{
|
||||||
|
dn->renameNode( it->newPName.c_str(),
|
||||||
|
it->oldPName.c_str(), false );
|
||||||
|
} catch( Error &err )
|
||||||
|
{
|
||||||
|
if (++errorCount == 1)
|
||||||
|
LOG(WARNING) << "error in rename und: " << err.what();
|
||||||
|
// continue on anyway...
|
||||||
|
}
|
||||||
|
++undoCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
LOG(WARNING) << "Undo rename count: " << 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( Error &err )
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "decode err: " << err.what();
|
||||||
|
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( Error &err )
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "encode err: " << err.what();
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
VLOG(1) << "opendir error " << strerror(errno);
|
||||||
|
return DirTraverse( shared_ptr<DIR>(), 0, shared_ptr<NameIO>() );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
shared_ptr<DIR> 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( Error &err )
|
||||||
|
{
|
||||||
|
LOG(ERROR) << "encode err: " << err.what();
|
||||||
|
}
|
||||||
|
return DirTraverse( dp, iv, naming );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirNode::genRenameList( list<RenameEl> &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..
|
||||||
|
VLOG(1) << "opendir " << sourcePath;
|
||||||
|
shared_ptr<DIR> dir = shared_ptr<DIR>(
|
||||||
|
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( 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VLOG(1) << "adding file " << oldFull << " to rename list";
|
||||||
|
renameList.push_back( ren );
|
||||||
|
|
||||||
|
} catch( 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..
|
||||||
|
LOG(WARNING) << "Aborting rename: error on file " <<
|
||||||
|
fromCPart.append(1, '/').append(de->d_name) << ":" << err.what();
|
||||||
|
|
||||||
|
// 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
|
||||||
|
directory initialization vector chaining, then we have to recursively
|
||||||
|
rename every descendent of this directory, as all initialization vectors
|
||||||
|
will have changed..
|
||||||
|
|
||||||
|
Returns a list of renamed items on success, a null list on failure.
|
||||||
|
*/
|
||||||
|
shared_ptr<RenameOp>
|
||||||
|
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<RenameEl> > renameList(new list<RenameEl>);
|
||||||
|
if(!genRenameList( *renameList.get(), fromP, toP ))
|
||||||
|
{
|
||||||
|
LOG(WARNING) << "Error during generation of recursive rename list";
|
||||||
|
return shared_ptr<RenameOp>();
|
||||||
|
} else
|
||||||
|
return shared_ptr<RenameOp>( 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() );
|
||||||
|
|
||||||
|
VLOG(1) << "mkdir on " << cyName;
|
||||||
|
|
||||||
|
// 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 );
|
||||||
|
|
||||||
|
if(olduid >= 0)
|
||||||
|
setfsuid( olduid );
|
||||||
|
if(oldgid >= 0)
|
||||||
|
setfsgid( oldgid );
|
||||||
|
|
||||||
|
if(res == -1)
|
||||||
|
{
|
||||||
|
int eno = errno;
|
||||||
|
LOG(WARNING) << "mkdir error on " << cyName
|
||||||
|
<< " mode " << mode << ": " << strerror(eno);
|
||||||
|
res = -eno;
|
||||||
|
} else
|
||||||
|
res = 0;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() );
|
||||||
|
|
||||||
|
VLOG(1) << "rename " << fromCName << " -> " << toCName;
|
||||||
|
|
||||||
|
shared_ptr<FileNode> toNode = findOrCreate( toPlaintext );
|
||||||
|
|
||||||
|
shared_ptr<RenameOp> renameOp;
|
||||||
|
if( hasDirectoryNameDependency() && isDirectory( fromCName.c_str() ))
|
||||||
|
{
|
||||||
|
VLOG(1) << "recursive rename begin";
|
||||||
|
renameOp = newRenameOp( fromPlaintext, toPlaintext );
|
||||||
|
|
||||||
|
if(!renameOp || !renameOp->apply())
|
||||||
|
{
|
||||||
|
if(renameOp)
|
||||||
|
renameOp->undo();
|
||||||
|
|
||||||
|
LOG(WARNING) << "rename aborted";
|
||||||
|
return -EACCES;
|
||||||
|
}
|
||||||
|
VLOG(1) << "recursive rename end";
|
||||||
|
}
|
||||||
|
|
||||||
|
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() );
|
||||||
|
|
||||||
|
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( Error &err )
|
||||||
|
{
|
||||||
|
// exception from renameNode, just show the error and continue..
|
||||||
|
LOG(ERROR) << "rename err: " << err.what();
|
||||||
|
res = -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res != 0)
|
||||||
|
{
|
||||||
|
VLOG(1) << "rename failed: " << strerror( errno );
|
||||||
|
res = -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirNode::link( const char *from, const char *to )
|
||||||
|
{
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
string fromCName = rootDir + naming->encodePath( from );
|
||||||
|
string toCName = rootDir + naming->encodePath( to );
|
||||||
|
|
||||||
|
rAssert( !fromCName.empty() );
|
||||||
|
rAssert( !toCName.empty() );
|
||||||
|
|
||||||
|
VLOG(1) << "link " << fromCName << " -> " << toCName;
|
||||||
|
|
||||||
|
int res = -EPERM;
|
||||||
|
if( fsConfig->config->external_iv() )
|
||||||
|
{
|
||||||
|
VLOG(1) << "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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
The node is keyed by filename, so a rename means the internal node names
|
||||||
|
must be changed.
|
||||||
|
*/
|
||||||
|
shared_ptr<FileNode> DirNode::renameNode( const char *from, const char *to )
|
||||||
|
{
|
||||||
|
return renameNode( from, to, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<FileNode> DirNode::renameNode( const char *from, const char *to,
|
||||||
|
bool forwardMode )
|
||||||
|
{
|
||||||
|
shared_ptr<FileNode> node = findOrCreate( from );
|
||||||
|
|
||||||
|
if(node)
|
||||||
|
{
|
||||||
|
uint64_t newIV = 0;
|
||||||
|
string cname = rootDir + naming->encodePath( to, &newIV );
|
||||||
|
|
||||||
|
VLOG(1) << "renaming internal node " << 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
|
||||||
|
LOG(ERROR) << "renameNode failed";
|
||||||
|
throw Error("Internal node name change failed!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<FileNode> DirNode::findOrCreate( const char *plainName)
|
||||||
|
{
|
||||||
|
shared_ptr<FileNode> 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->external_iv())
|
||||||
|
node->setName(0, 0, iv);
|
||||||
|
|
||||||
|
VLOG(1) << "created FileNode for " << node->cipherName();
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<FileNode>
|
||||||
|
DirNode::lookupNode( const char *plainName, const char * requestor )
|
||||||
|
{
|
||||||
|
(void)requestor;
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
shared_ptr<FileNode> node = findOrCreate( plainName );
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Similar to lookupNode, except that we also call open() and only return a
|
||||||
|
node on sucess.. This is done in one step to avoid any race conditions
|
||||||
|
with the stored state of the file.
|
||||||
|
*/
|
||||||
|
shared_ptr<FileNode>
|
||||||
|
DirNode::openNode( const char *plainName, const char * requestor, int flags,
|
||||||
|
int *result )
|
||||||
|
{
|
||||||
|
(void)requestor;
|
||||||
|
rAssert( result != NULL );
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
shared_ptr<FileNode> node = findOrCreate( plainName );
|
||||||
|
|
||||||
|
if( node && (*result = node->open( flags )) >= 0 )
|
||||||
|
return node;
|
||||||
|
else
|
||||||
|
return shared_ptr<FileNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirNode::unlink( const char *plaintextName )
|
||||||
|
{
|
||||||
|
string cyName = naming->encodePath( plaintextName );
|
||||||
|
VLOG(1) << "unlink " << cyName;
|
||||||
|
|
||||||
|
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..
|
||||||
|
LOG(WARNING) << "Refusing to unlink open file: "
|
||||||
|
<< cyName << ", hard_remove option is probably in effect";
|
||||||
|
res = -EBUSY;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
string fullName = rootDir + cyName;
|
||||||
|
res = ::unlink( fullName.c_str() );
|
||||||
|
if(res == -1)
|
||||||
|
{
|
||||||
|
res = -errno;
|
||||||
|
VLOG(1) << "unlink error: " << strerror(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
@ -30,11 +30,11 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "FileNode.h"
|
#include "base/shared_ptr.h"
|
||||||
#include "NameIO.h"
|
#include "cipher/CipherKey.h"
|
||||||
#include "CipherKey.h"
|
#include "fs/FileNode.h"
|
||||||
#include "FSConfig.h"
|
#include "fs/NameIO.h"
|
||||||
#include "shared_ptr.h"
|
#include "fs/FSConfig.h"
|
||||||
|
|
||||||
class Cipher;
|
class Cipher;
|
||||||
class RenameOp;
|
class RenameOp;
|
@ -21,10 +21,10 @@
|
|||||||
#ifndef _FSConfig_incl_
|
#ifndef _FSConfig_incl_
|
||||||
#define _FSConfig_incl_
|
#define _FSConfig_incl_
|
||||||
|
|
||||||
#include "encfs.h"
|
#include "base/Interface.h"
|
||||||
#include "Interface.h"
|
#include "base/shared_ptr.h"
|
||||||
#include "CipherKey.h"
|
#include "cipher/CipherKey.h"
|
||||||
#include "shared_ptr.h"
|
#include "fs/encfs.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ enum ConfigType
|
|||||||
Config_V7 = 7
|
Config_V7 = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
class EncFS_Opts;
|
struct EncFS_Opts;
|
||||||
class Cipher;
|
class Cipher;
|
||||||
class NameIO;
|
class NameIO;
|
||||||
class EncfsConfig;
|
class EncfsConfig;
|
@ -21,12 +21,11 @@
|
|||||||
#ifndef _FileIO_incl_
|
#ifndef _FileIO_incl_
|
||||||
#define _FileIO_incl_
|
#define _FileIO_incl_
|
||||||
|
|
||||||
#include "encfs.h"
|
#include "base/Interface.h"
|
||||||
|
#include "fs/encfs.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include "Interface.h"
|
|
||||||
|
|
||||||
struct IORequest
|
struct IORequest
|
||||||
{
|
{
|
||||||
off_t offset;
|
off_t offset;
|
303
fs/FileNode.cpp
Normal file
303
fs/FileNode.cpp
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2003-2004, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Include encfs first, because we need to include fuse.h before any inclusion
|
||||||
|
// of sys/stat.h or other system headers (to be safe)
|
||||||
|
#include "fs/encfs.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#ifdef linux
|
||||||
|
#include <sys/fsuid.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "base/config.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "base/Mutex.h"
|
||||||
|
#include "cipher/Cipher.h"
|
||||||
|
#include "cipher/MemoryPool.h"
|
||||||
|
|
||||||
|
#include "fs/FileNode.h"
|
||||||
|
#include "fs/FileUtils.h"
|
||||||
|
#include "fs/CipherFileIO.h"
|
||||||
|
#include "fs/RawFileIO.h"
|
||||||
|
#include "fs/MACFileIO.h"
|
||||||
|
#include "fs/DirNode.h"
|
||||||
|
|
||||||
|
#include "fs/FileIO.h"
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace rel;
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: locking at the FileNode level is inefficient, since this precludes
|
||||||
|
multiple concurrent IO operations within the same file.
|
||||||
|
|
||||||
|
There is no reason why simultainous reads cannot be satisfied, or why one
|
||||||
|
read has to wait for the decoding of the previous read before it can be
|
||||||
|
sent to the IO subsystem!
|
||||||
|
*/
|
||||||
|
|
||||||
|
FileNode::FileNode(DirNode *parent_, const FSConfigPtr &cfg,
|
||||||
|
const char *plaintextName_, const char *cipherName_)
|
||||||
|
{
|
||||||
|
pthread_mutex_init( &mutex, 0 );
|
||||||
|
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
this->_pname = plaintextName_;
|
||||||
|
this->_cname = cipherName_;
|
||||||
|
this->parent = parent_;
|
||||||
|
|
||||||
|
this->fsConfig = cfg;
|
||||||
|
|
||||||
|
// chain RawFileIO & CipherFileIO
|
||||||
|
shared_ptr<FileIO> rawIO( new RawFileIO( _cname ) );
|
||||||
|
io = shared_ptr<FileIO>( new CipherFileIO( rawIO, fsConfig ));
|
||||||
|
|
||||||
|
if(cfg->config->block_mac_bytes() || cfg->config->block_mac_rand_bytes())
|
||||||
|
io = shared_ptr<FileIO>(new MACFileIO(io, fsConfig));
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
pthread_mutex_destroy( &mutex );
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *FileNode::cipherName() const
|
||||||
|
{
|
||||||
|
return _cname.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *FileNode::plaintextName() const
|
||||||
|
{
|
||||||
|
return _pname.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
string FileNode::plaintextParent() const
|
||||||
|
{
|
||||||
|
return parentDirectory( _pname );
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setIV(const shared_ptr<FileIO> &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 );
|
||||||
|
VLOG(1) << "calling setIV on " << cipherName_;
|
||||||
|
if(setIVFirst)
|
||||||
|
{
|
||||||
|
if(fsConfig->config->external_iv() && !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->external_iv() && !setIV(io, iv))
|
||||||
|
{
|
||||||
|
_pname = oldPName;
|
||||||
|
_cname = oldCName;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileNode::mknod(mode_t mode, dev_t rdev, uid_t uid, gid_t gid)
|
||||||
|
{
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
int res;
|
||||||
|
int olduid = -1;
|
||||||
|
int oldgid = -1;
|
||||||
|
if(uid != 0)
|
||||||
|
{
|
||||||
|
olduid = setfsuid( uid );
|
||||||
|
if(olduid == -1)
|
||||||
|
{
|
||||||
|
LOG(INFO) << "setfsuid error: " << strerror(errno);
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(gid != 0)
|
||||||
|
{
|
||||||
|
oldgid = setfsgid( gid );
|
||||||
|
if(oldgid == -1)
|
||||||
|
{
|
||||||
|
LOG(INFO) << "setfsgid error: " << 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;
|
||||||
|
VLOG(1) << "mknod error: " << strerror(eno);
|
||||||
|
res = -eno;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileNode::open(int flags) const
|
||||||
|
{
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
int res = io->open( flags );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileNode::getAttr(struct stat *stbuf) const
|
||||||
|
{
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
int res = io->getAttr( stbuf );
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t FileNode::getSize() const
|
||||||
|
{
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
int res = io->getSize();
|
||||||
|
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;
|
||||||
|
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
return io->read( req );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileNode::write(off_t offset, unsigned char *data, ssize_t size)
|
||||||
|
{
|
||||||
|
VLOG(1) << "FileNode::write offset " << offset
|
||||||
|
<< ", data size " << size;
|
||||||
|
|
||||||
|
IORequest req;
|
||||||
|
req.offset = offset;
|
||||||
|
req.dataLen = size;
|
||||||
|
req.data = data;
|
||||||
|
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
return io->write( req );
|
||||||
|
}
|
||||||
|
|
||||||
|
int FileNode::truncate( off_t size )
|
||||||
|
{
|
||||||
|
Lock _lock( mutex );
|
||||||
|
|
||||||
|
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 );
|
||||||
|
#else
|
||||||
|
// no fdatasync support
|
||||||
|
// TODO: use autoconfig to check for it..
|
||||||
|
res = fsync(fh);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(res == -1)
|
||||||
|
res = -errno;
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} else
|
||||||
|
return fh;
|
||||||
|
}
|
||||||
|
|
@ -21,9 +21,9 @@
|
|||||||
#ifndef _FileNode_incl_
|
#ifndef _FileNode_incl_
|
||||||
#define _FileNode_incl_
|
#define _FileNode_incl_
|
||||||
|
|
||||||
#include "encfs.h"
|
#include "cipher/CipherKey.h"
|
||||||
#include "CipherKey.h"
|
#include "fs/encfs.h"
|
||||||
#include "FileUtils.h"
|
#include "fs/FileUtils.h"
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
@ -24,28 +24,29 @@
|
|||||||
#endif
|
#endif
|
||||||
#define _BSD_SOURCE // pick up setenv on RH7.3
|
#define _BSD_SOURCE // pick up setenv on RH7.3
|
||||||
|
|
||||||
#include "encfs.h"
|
#include "fs/encfs.h"
|
||||||
#include "config.h"
|
|
||||||
#include "config.pb.h"
|
|
||||||
|
|
||||||
#include "readpassphrase.h"
|
#include "base/autosprintf.h"
|
||||||
#include "autosprintf.h"
|
#include "base/config.h"
|
||||||
|
#include "base/config.pb.h"
|
||||||
|
#include "base/ConfigReader.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "base/i18n.h"
|
||||||
|
#include "base/XmlReader.h"
|
||||||
|
|
||||||
#include "FileUtils.h"
|
#include "cipher/Cipher.h"
|
||||||
#include "ConfigReader.h"
|
#include "cipher/MemoryPool.h"
|
||||||
#include "XmlReader.h"
|
#include "cipher/readpassphrase.h"
|
||||||
#include "FSConfig.h"
|
|
||||||
|
|
||||||
#include "DirNode.h"
|
#include "fs/BlockNameIO.h"
|
||||||
#include "Cipher.h"
|
#include "fs/Context.h"
|
||||||
#include "StreamNameIO.h"
|
#include "fs/DirNode.h"
|
||||||
#include "BlockNameIO.h"
|
#include "fs/FileUtils.h"
|
||||||
#include "NullNameIO.h"
|
#include "fs/FSConfig.h"
|
||||||
#include "Context.h"
|
#include "fs/NullNameIO.h"
|
||||||
#include "MemoryPool.h"
|
#include "fs/StreamNameIO.h"
|
||||||
|
|
||||||
#include <rlog/rlog.h>
|
#include <glog/logging.h>
|
||||||
#include <rlog/Error.h>
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -62,23 +63,16 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "i18n.h"
|
|
||||||
|
|
||||||
#include <google/protobuf/text_format.h>
|
#include <google/protobuf/text_format.h>
|
||||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||||
|
|
||||||
// disable rlog section grouping for this file.. seems to cause problems
|
|
||||||
#undef RLOG_SECTION
|
|
||||||
#define RLOG_SECTION
|
|
||||||
|
|
||||||
using namespace rlog;
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace gnu;
|
using namespace gnu;
|
||||||
|
|
||||||
static const int DefaultBlockSize = 1024;
|
static const int DefaultBlockSize = 2048;
|
||||||
// The maximum length of text passwords. If longer are needed,
|
// The maximum length of text passwords. If longer are needed,
|
||||||
// use the extpass option, as extpass can return arbitrary length binary data.
|
// use the extpass option, as extpass can return arbitrary length binary data.
|
||||||
static const int MaxPassBuf = 512;
|
static const int MaxPassBuf = 2048;
|
||||||
|
|
||||||
static const int NormalKDFDuration = 500; // 1/2 a second
|
static const int NormalKDFDuration = 500; // 1/2 a second
|
||||||
static const int ParanoiaKDFDuration = 3000; // 3 seconds
|
static const int ParanoiaKDFDuration = 3000; // 3 seconds
|
||||||
@ -90,7 +84,7 @@ static const char ENCFS_ENV_STDOUT[] = "encfs_stdout";
|
|||||||
static const char ENCFS_ENV_STDERR[] = "encfs_stderr";
|
static const char ENCFS_ENV_STDERR[] = "encfs_stderr";
|
||||||
|
|
||||||
const int V5Latest = 20040813; // fix MACFileIO block size issues
|
const int V5Latest = 20040813; // fix MACFileIO block size issues
|
||||||
const int ProtoSubVersion = 20120819;
|
const int ProtoSubVersion = 20120902;
|
||||||
|
|
||||||
const char ConfigFileName[] = ".encfs.txt";
|
const char ConfigFileName[] = ".encfs.txt";
|
||||||
|
|
||||||
@ -223,12 +217,12 @@ ConfigType readConfig_load( ConfigInfo *nm, const char *path,
|
|||||||
{
|
{
|
||||||
if( (*nm->loadFunc)( path, config, nm ))
|
if( (*nm->loadFunc)( path, config, nm ))
|
||||||
return nm->type;
|
return nm->type;
|
||||||
} catch( rlog::Error & err )
|
} catch( Error &err )
|
||||||
{
|
{
|
||||||
err.log( _RLWarningChannel );
|
LOG(WARNING) << "readConfig failed: " << err.what();
|
||||||
}
|
}
|
||||||
|
|
||||||
rError( _("Found config file %s, but failed to load"), path);
|
LOG(ERROR) << "Found config file " << path << ", but failed to load";
|
||||||
return Config_None;
|
return Config_None;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
@ -269,7 +263,7 @@ bool readV6Config( const char *configFile,
|
|||||||
XmlReader rdr;
|
XmlReader rdr;
|
||||||
if (!rdr.load(configFile))
|
if (!rdr.load(configFile))
|
||||||
{
|
{
|
||||||
rError("Failed to load config file %s", configFile);
|
LOG(ERROR) << "Failed to load config file " << configFile;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,39 +273,40 @@ bool readV6Config( const char *configFile,
|
|||||||
config = (*serialization)["config"];
|
config = (*serialization)["config"];
|
||||||
}
|
}
|
||||||
if (!config) {
|
if (!config) {
|
||||||
rError("Unable to find XML configuration in file %s", configFile);
|
LOG(ERROR) << "Unable to find XML configuration in file " << configFile;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int version;
|
int version;
|
||||||
if (!config->read("version", &version) &&
|
if (!config->read("version", &version) &&
|
||||||
!config->read("@version", &version)) {
|
!config->read("@version", &version)) {
|
||||||
rError("Unable to find version in config file");
|
LOG(ERROR) << "Unable to find version in config file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// version numbering was complicated by boost::archive
|
// version numbering was complicated by boost::archive
|
||||||
if (version == 20 || version >= 20100713)
|
if (version == 20 || version >= 20100713)
|
||||||
{
|
{
|
||||||
rInfo("found new serialization format");
|
VLOG(1) << "found new serialization format";
|
||||||
cfg.set_revision(version);
|
cfg.set_revision(version);
|
||||||
} else if (version == 26800)
|
} else if (version == 26800)
|
||||||
{
|
{
|
||||||
rInfo("found 20080816 version");
|
VLOG(1) << "found 20080816 version";
|
||||||
cfg.set_revision(20080816);
|
cfg.set_revision(20080816);
|
||||||
} else if (version == 26797)
|
} else if (version == 26797)
|
||||||
{
|
{
|
||||||
rInfo("found 20080813");
|
VLOG(1) << "found 20080813";
|
||||||
cfg.set_revision(20080813);
|
cfg.set_revision(20080813);
|
||||||
} else if (version < V5Latest)
|
} else if (version < V5Latest)
|
||||||
{
|
{
|
||||||
rError("Invalid version %i - please fix config file", version);
|
LOG(ERROR) << "Invalid version " << version
|
||||||
|
<< " - please fix config file";
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
rInfo("Boost <= 1.41 compatibility mode");
|
LOG(INFO) << "Boost <= 1.41 compatibility mode";
|
||||||
cfg.set_revision(version);
|
cfg.set_revision(version);
|
||||||
}
|
}
|
||||||
rInfo("subVersion = %i", cfg.revision());
|
VLOG(1) << "subVersion = " << cfg.revision();
|
||||||
|
|
||||||
config->read("creator", cfg.mutable_creator());
|
config->read("creator", cfg.mutable_creator());
|
||||||
config->read("cipherAlg", cfg.mutable_cipher());
|
config->read("cipherAlg", cfg.mutable_cipher());
|
||||||
@ -390,15 +385,15 @@ bool readV5Config( const char *configFile,
|
|||||||
{
|
{
|
||||||
/* config file specifies a version outside our supported
|
/* config file specifies a version outside our supported
|
||||||
range.. */
|
range.. */
|
||||||
rWarning(_("Config subversion %i found, but this version of"
|
LOG(ERROR) << "Config subversion " << config.revision()
|
||||||
" encfs only supports up to version %i."),
|
<< " found, but this version of encfs only supports up to version "
|
||||||
config.revision(), V5Latest);
|
<< V5Latest;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if( config.revision() < V5Latest )
|
if( config.revision() < V5Latest )
|
||||||
{
|
{
|
||||||
rError(_("This version of EncFS doesn't support "
|
LOG(ERROR) << "This version of EncFS doesn't support "
|
||||||
"filesystems created before 2004-08-13"));
|
<< "filesystems created with EncFS releases before 2004-08-13";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,10 +418,10 @@ bool readV5Config( const char *configFile,
|
|||||||
config.set_block_mac_rand_bytes( cfgRdr["blockMACRandBytes"].readInt(0) );
|
config.set_block_mac_rand_bytes( cfgRdr["blockMACRandBytes"].readInt(0) );
|
||||||
|
|
||||||
ok = true;
|
ok = true;
|
||||||
} catch( rlog::Error &err)
|
} catch( Error &err)
|
||||||
{
|
{
|
||||||
err.log( _RLWarningChannel );
|
LOG(WARNING) << "Error parsing data in config file " << configFile
|
||||||
rDebug("Error parsing data in config file %s", configFile);
|
<< "; " << err.what();
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -458,10 +453,10 @@ bool readV4Config( const char *configFile,
|
|||||||
config.set_creator( "EncFS 1.0.x" );
|
config.set_creator( "EncFS 1.0.x" );
|
||||||
|
|
||||||
ok = true;
|
ok = true;
|
||||||
} catch( rlog::Error &err)
|
} catch( Error &err)
|
||||||
{
|
{
|
||||||
err.log( _RLWarningChannel );
|
LOG(WARNING) << "Error parsing config file " << configFile
|
||||||
rDebug("Error parsing config file %s", configFile);
|
<< ": " << err.what();
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -474,7 +469,7 @@ bool writeTextConfig( const char *fileName, const EncfsConfig &cfg )
|
|||||||
int fd = ::open( fileName, O_RDWR | O_CREAT, 0640 );
|
int fd = ::open( fileName, O_RDWR | O_CREAT, 0640 );
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
rError("Unable to open or create file %s", fileName);
|
LOG(ERROR) << "Unable to open or create file " << fileName;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -503,10 +498,11 @@ bool saveConfig( const string &rootDir, const EncfsConfig &config )
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
const_cast<EncfsConfig &>(config).set_writer("EncFS " VERSION );
|
||||||
ok = writeTextConfig( path.c_str(), config );
|
ok = writeTextConfig( path.c_str(), config );
|
||||||
} catch( rlog::Error &err )
|
} catch( Error &err )
|
||||||
{
|
{
|
||||||
err.log( _RLWarningChannel );
|
LOG(WARNING) << "saveConfig failed: " << err.what();
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -519,7 +515,7 @@ bool readProtoConfig( const char *fileName, EncfsConfig &config,
|
|||||||
int fd = ::open( fileName, O_RDONLY, 0640 );
|
int fd = ::open( fileName, O_RDONLY, 0640 );
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
{
|
{
|
||||||
rError("Unable to open file %s", fileName);
|
LOG(ERROR) << "Unable to open file " << fileName;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -623,7 +619,7 @@ Cipher::CipherAlgorithm selectCipherAlgorithm()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
Interface selectNameCoding()
|
Interface selectNameCoding(const Cipher::CipherAlgorithm &alg)
|
||||||
{
|
{
|
||||||
for(;;)
|
for(;;)
|
||||||
{
|
{
|
||||||
@ -634,10 +630,15 @@ Interface selectNameCoding()
|
|||||||
NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList();
|
NameIO::AlgorithmList algorithms = NameIO::GetAlgorithmList();
|
||||||
NameIO::AlgorithmList::const_iterator it;
|
NameIO::AlgorithmList::const_iterator it;
|
||||||
int optNum = 1;
|
int optNum = 1;
|
||||||
for(it = algorithms.begin(); it != algorithms.end(); ++it, ++optNum)
|
map<int, NameIO::AlgorithmList::const_iterator> algMap;
|
||||||
|
for(it = algorithms.begin(); it != algorithms.end(); ++it)
|
||||||
{
|
{
|
||||||
|
if (it->needsStreamMode && !alg.hasStreamMode)
|
||||||
|
continue;
|
||||||
|
|
||||||
cout << optNum << ". " << it->name
|
cout << optNum << ". " << it->name
|
||||||
<< " : " << gettext(it->description.c_str()) << "\n";
|
<< " : " << gettext(it->description.c_str()) << "\n";
|
||||||
|
algMap[optNum++] = it;
|
||||||
}
|
}
|
||||||
|
|
||||||
// xgroup(setup)
|
// xgroup(setup)
|
||||||
@ -647,15 +648,13 @@ Interface selectNameCoding()
|
|||||||
int algNum = (res == 0 ? 0 : atoi( answer ));
|
int algNum = (res == 0 ? 0 : atoi( answer ));
|
||||||
cout << "\n";
|
cout << "\n";
|
||||||
|
|
||||||
if( algNum < 1 || algNum > (int)algorithms.size() )
|
if( algNum < 1 || algNum >= optNum )
|
||||||
{
|
{
|
||||||
cerr << _("Invalid selection.") << "\n";
|
cerr << _("Invalid selection.") << "\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
it = algorithms.begin();
|
it = algMap[algNum];
|
||||||
while(--algNum) // numbering starts at 1
|
|
||||||
++it;
|
|
||||||
|
|
||||||
// xgroup(setup)
|
// xgroup(setup)
|
||||||
cout << autosprintf(_("Selected algorithm \"%s\""), it->name.c_str())
|
cout << autosprintf(_("Selected algorithm \"%s\""), it->name.c_str())
|
||||||
@ -940,7 +939,7 @@ RootPtr createConfig( EncFS_Context *ctx,
|
|||||||
{
|
{
|
||||||
if (reverseEncryption)
|
if (reverseEncryption)
|
||||||
{
|
{
|
||||||
rError(_("Paranoia configuration not supported for --reverse"));
|
LOG(ERROR) << "Paranoia configuration not supported for --reverse";
|
||||||
return rootInfo;
|
return rootInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -953,7 +952,7 @@ RootPtr createConfig( EncFS_Context *ctx,
|
|||||||
// Enable filename initialization vector chaning
|
// Enable filename initialization vector chaning
|
||||||
keySize = 256;
|
keySize = 256;
|
||||||
blockSize = DefaultBlockSize;
|
blockSize = DefaultBlockSize;
|
||||||
alg = findCipherAlgorithm("AES", keySize);
|
alg = findCipherAlgorithm("AES_XTS", keySize);
|
||||||
nameIOIface = BlockNameIO::CurrentInterface();
|
nameIOIface = BlockNameIO::CurrentInterface();
|
||||||
blockMACBytes = 8;
|
blockMACBytes = 8;
|
||||||
blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary
|
blockMACRandBytes = 0; // using uniqueIV, so this isn't necessary
|
||||||
@ -1004,7 +1003,7 @@ RootPtr createConfig( EncFS_Context *ctx,
|
|||||||
alg = selectCipherAlgorithm();
|
alg = selectCipherAlgorithm();
|
||||||
keySize = selectKeySize( alg );
|
keySize = selectKeySize( alg );
|
||||||
blockSize = selectBlockSize( alg );
|
blockSize = selectBlockSize( alg );
|
||||||
nameIOIface = selectNameCoding();
|
nameIOIface = selectNameCoding( alg );
|
||||||
if (reverseEncryption)
|
if (reverseEncryption)
|
||||||
{
|
{
|
||||||
cout << _("--reverse specified, not using unique/chained IV") << "\n";
|
cout << _("--reverse specified, not using unique/chained IV") << "\n";
|
||||||
@ -1030,18 +1029,22 @@ RootPtr createConfig( EncFS_Context *ctx,
|
|||||||
shared_ptr<Cipher> cipher = Cipher::New( alg.name, keySize );
|
shared_ptr<Cipher> cipher = Cipher::New( alg.name, keySize );
|
||||||
if(!cipher)
|
if(!cipher)
|
||||||
{
|
{
|
||||||
rError(_("Unable to instanciate cipher %s, key size %i, block size %i"),
|
LOG(ERROR) << "Unable to instanciate cipher " << alg.name
|
||||||
alg.name.c_str(), keySize, blockSize);
|
<< ", key size " << keySize << ", block size " << blockSize;
|
||||||
return rootInfo;
|
return rootInfo;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
rDebug("Using cipher %s, key size %i, block size %i",
|
VLOG(1) << "Using cipher " << alg.name
|
||||||
alg.name.c_str(), keySize, blockSize);
|
<< ", key size " << keySize << ", block size " << blockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
EncfsConfig config;
|
EncfsConfig config;
|
||||||
|
|
||||||
config.mutable_cipher()->MergeFrom( cipher->interface() );
|
config.mutable_cipher()->MergeFrom( cipher->interface() );
|
||||||
|
// TODO: allow user config
|
||||||
|
if (!cipher->hasStreamMode())
|
||||||
|
config.set_block_mode_only(true);
|
||||||
|
|
||||||
config.set_block_size( blockSize );
|
config.set_block_size( blockSize );
|
||||||
config.mutable_naming()->MergeFrom( nameIOIface );
|
config.mutable_naming()->MergeFrom( nameIOIface );
|
||||||
config.set_creator( "EncFS " VERSION );
|
config.set_creator( "EncFS " VERSION );
|
||||||
@ -1093,7 +1096,7 @@ RootPtr createConfig( EncFS_Context *ctx,
|
|||||||
|
|
||||||
// get user key and use it to encode volume key
|
// get user key and use it to encode volume key
|
||||||
CipherKey userKey;
|
CipherKey userKey;
|
||||||
rDebug( "useStdin: %i", useStdin );
|
VLOG(1) << "useStdin: " << useStdin;
|
||||||
if(useStdin)
|
if(useStdin)
|
||||||
{
|
{
|
||||||
if (annotate)
|
if (annotate)
|
||||||
@ -1109,8 +1112,8 @@ RootPtr createConfig( EncFS_Context *ctx,
|
|||||||
|
|
||||||
if(!volumeKey)
|
if(!volumeKey)
|
||||||
{
|
{
|
||||||
rWarning(_("Failure generating new volume key! "
|
LOG(ERROR) << "Failure generating new volume key! "
|
||||||
"Please report this error."));
|
<< "Please report this error.";
|
||||||
return rootInfo;
|
return rootInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1122,7 +1125,7 @@ RootPtr createConfig( EncFS_Context *ctx,
|
|||||||
cipher, volumeKey );
|
cipher, volumeKey );
|
||||||
if(!nameCoder)
|
if(!nameCoder)
|
||||||
{
|
{
|
||||||
rWarning(_("Name coding interface not supported"));
|
LOG(WARNING) << "Name coding interface not supported";
|
||||||
cout << _("The filename encoding interface requested is not available")
|
cout << _("The filename encoding interface requested is not available")
|
||||||
<< endl;
|
<< endl;
|
||||||
return rootInfo;
|
return rootInfo;
|
||||||
@ -1175,12 +1178,17 @@ void showFSInfo( const EncfsConfig &config )
|
|||||||
cout << "\n";
|
cout << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
|
||||||
// xgroup(diag)
|
|
||||||
cout << autosprintf(_("Filename encoding: \"%s\", version %i:%i:%i"),
|
|
||||||
config.naming().name().c_str(), config.naming().major(),
|
|
||||||
config.naming().minor(), config.naming().age());
|
|
||||||
|
|
||||||
|
// xgroup(diag)
|
||||||
|
cout << autosprintf(_("Filename encoding: \"%s\", version %i:%i:%i"),
|
||||||
|
config.naming().name().c_str(), config.naming().major(),
|
||||||
|
config.naming().minor(), config.naming().age());
|
||||||
|
|
||||||
|
if (!cipher)
|
||||||
|
{
|
||||||
|
cout << "\n";
|
||||||
|
} else
|
||||||
|
{
|
||||||
// check if we support the filename encoding interface..
|
// check if we support the filename encoding interface..
|
||||||
shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(),
|
shared_ptr<NameIO> nameCoder = NameIO::New( config.naming(),
|
||||||
cipher, CipherKey() );
|
cipher, CipherKey() );
|
||||||
@ -1312,8 +1320,9 @@ CipherKey decryptKey(const EncfsConfig &config, const char *password, int passwd
|
|||||||
iterations, key.kdf_duration(),
|
iterations, key.kdf_duration(),
|
||||||
(const unsigned char *)key.salt().data(), key.salt().size());
|
(const unsigned char *)key.salt().data(), key.salt().size());
|
||||||
|
|
||||||
if (iterations != key.kdf_iterations()) {
|
if (iterations != key.kdf_iterations())
|
||||||
rError("Error in KDF, iteration mismatch");
|
{
|
||||||
|
LOG(ERROR) << "Error in KDF, iteration mismatch";
|
||||||
return userKey;
|
return userKey;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
@ -1366,7 +1375,7 @@ SecureMem *passwordFromProgram(const std::string &passProg,
|
|||||||
perror(_("Internal error: socketpair() failed"));
|
perror(_("Internal error: socketpair() failed"));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
rDebug("getUserKey: fds = %i, %i", fds[0], fds[1]);
|
VLOG(1) << "getUserKey: fds = " << fds[0] << ", " << fds[1];
|
||||||
|
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if(pid == -1)
|
if(pid == -1)
|
||||||
@ -1570,11 +1579,10 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts )
|
|||||||
shared_ptr<Cipher> cipher = getCipher(config);
|
shared_ptr<Cipher> cipher = getCipher(config);
|
||||||
if(!cipher)
|
if(!cipher)
|
||||||
{
|
{
|
||||||
rError(_("Unable to find cipher %s, version %i:%i:%i"),
|
Interface iface = config.cipher();
|
||||||
config.cipher().name().c_str(),
|
LOG(ERROR) << "Unable to find cipher " << iface.name()
|
||||||
config.cipher().major(),
|
<< ", version " << iface.major()
|
||||||
config.cipher().minor(),
|
<< ":" << iface.minor() << ":" << iface.age();
|
||||||
config.cipher().age());
|
|
||||||
// xgroup(diag)
|
// xgroup(diag)
|
||||||
cout << _("The requested cipher interface is not available\n");
|
cout << _("The requested cipher interface is not available\n");
|
||||||
return rootInfo;
|
return rootInfo;
|
||||||
@ -1594,7 +1602,7 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts )
|
|||||||
if(!userKey)
|
if(!userKey)
|
||||||
return rootInfo;
|
return rootInfo;
|
||||||
|
|
||||||
rDebug("cipher encoded key size = %i", cipher->encodedKeySize());
|
VLOG(1) << "cipher encoded key size = " << cipher->encodedKeySize();
|
||||||
// decode volume key..
|
// decode volume key..
|
||||||
CipherKey volumeKey = cipher->readKey(
|
CipherKey volumeKey = cipher->readKey(
|
||||||
(const unsigned char *)config.key().ciphertext().data(), userKey, opts->checkKey);
|
(const unsigned char *)config.key().ciphertext().data(), userKey, opts->checkKey);
|
||||||
@ -1611,11 +1619,10 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts )
|
|||||||
cipher, volumeKey );
|
cipher, volumeKey );
|
||||||
if(!nameCoder)
|
if(!nameCoder)
|
||||||
{
|
{
|
||||||
rError(_("Unable to find nameio interface %s, version %i:%i:%i"),
|
Interface iface = config.naming();
|
||||||
config.naming().name().c_str(),
|
LOG(ERROR) << "Unable to find nameio interface " << iface.name()
|
||||||
config.naming().major(),
|
<< ", version " << iface.major()
|
||||||
config.naming().minor(),
|
<< ":" << iface.minor() << ":" << iface.age();
|
||||||
config.naming().age());
|
|
||||||
// xgroup(diag)
|
// xgroup(diag)
|
||||||
cout << _("The requested filename coding interface is "
|
cout << _("The requested filename coding interface is "
|
||||||
"not available\n");
|
"not available\n");
|
||||||
@ -1653,7 +1660,7 @@ RootPtr initFS( EncFS_Context *ctx, const shared_ptr<EncFS_Opts> &opts )
|
|||||||
|
|
||||||
int remountFS(EncFS_Context *ctx)
|
int remountFS(EncFS_Context *ctx)
|
||||||
{
|
{
|
||||||
rDebug("Attempting to reinitialize filesystem");
|
VLOG(1) << "Attempting to reinitialize filesystem";
|
||||||
|
|
||||||
RootPtr rootInfo = initFS( ctx, ctx->opts );
|
RootPtr rootInfo = initFS( ctx, ctx->opts );
|
||||||
if(rootInfo)
|
if(rootInfo)
|
||||||
@ -1662,7 +1669,7 @@ int remountFS(EncFS_Context *ctx)
|
|||||||
return 0;
|
return 0;
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
rInfo(_("Remount failed"));
|
LOG(WARNING) << "Remount failed";
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,10 +21,10 @@
|
|||||||
#ifndef _FileUtils_incl_
|
#ifndef _FileUtils_incl_
|
||||||
#define _FileUtils_incl_
|
#define _FileUtils_incl_
|
||||||
|
|
||||||
#include "encfs.h"
|
#include "base/Interface.h"
|
||||||
#include "Interface.h"
|
#include "cipher/CipherKey.h"
|
||||||
#include "CipherKey.h"
|
#include "fs/encfs.h"
|
||||||
#include "FSConfig.h"
|
#include "fs/FSConfig.h"
|
||||||
|
|
||||||
// true if the path points to an existing node (of any type)
|
// true if the path points to an existing node (of any type)
|
||||||
bool fileExists( const char *fileName );
|
bool fileExists( const char *fileName );
|
295
fs/MACFileIO.cpp
Normal file
295
fs/MACFileIO.cpp
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
/*****************************************************************************
|
||||||
|
* Author: Valient Gough <vgough@pobox.com>
|
||||||
|
*
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (c) 2004, Valient Gough
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||||
|
* for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "fs/MACFileIO.h"
|
||||||
|
|
||||||
|
#include "base/config.pb.h"
|
||||||
|
#include "base/Error.h"
|
||||||
|
#include "base/i18n.h"
|
||||||
|
#include "cipher/MemoryPool.h"
|
||||||
|
#include "fs/FileUtils.h"
|
||||||
|
|
||||||
|
#include <glog/logging.h>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Version 1.0 worked on blocks of size (blockSize + headerSize).
|
||||||
|
// That is, it took [blockSize] worth of user data and added headers.
|
||||||
|
// Version 2.0 takes [blockSize - headerSize] worth of user data and writes
|
||||||
|
// [blockSize] bytes. That way the size going into the crypto engine is
|
||||||
|
// valid from what was selected based on the crypto module allowed ranges!
|
||||||
|
// Version 2.1 allows per-block rand bytes to be used without enabling MAC.
|
||||||
|
//
|
||||||
|
// The information about MACFileIO currently does not make its way into the
|
||||||
|
// configuration file, so there is no easy way to make this backward
|
||||||
|
// compatible, except at a high level by checking a revision number for the
|
||||||
|
// filesystem...
|
||||||
|
//
|
||||||
|
static Interface MACFileIO_iface = makeInterface("FileIO/MAC", 2, 1, 0);
|
||||||
|
|
||||||
|
int dataBlockSize(const FSConfigPtr &cfg)
|
||||||
|
{
|
||||||
|
return cfg->config->block_size()
|
||||||
|
- cfg->config->block_mac_bytes()
|
||||||
|
- cfg->config->block_mac_rand_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
MACFileIO::MACFileIO( const shared_ptr<FileIO> &_base,
|
||||||
|
const FSConfigPtr &cfg )
|
||||||
|
: BlockFileIO( dataBlockSize( cfg ), cfg )
|
||||||
|
, base( _base )
|
||||||
|
, cipher( cfg->cipher )
|
||||||
|
, key( cfg->key )
|
||||||
|
, macBytes( cfg->config->block_mac_bytes() )
|
||||||
|
, randBytes( cfg->config->block_mac_rand_bytes() )
|
||||||
|
, warnOnly( cfg->opts->forceDecode )
|
||||||
|
{
|
||||||
|
rAssert( macBytes >= 0 && macBytes <= 8 );
|
||||||
|
rAssert( randBytes >= 0 );
|
||||||
|
VLOG(1) << "fs block size = " << cfg->config->block_size()
|
||||||
|
<< ", macBytes = " << cfg->config->block_mac_bytes()
|
||||||
|
<< ", randBytes = " << cfg->config->block_mac_rand_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
MACFileIO::~MACFileIO()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from a location in the raw file to a location when MAC headers are
|
||||||
|
// interleved with the data.
|
||||||
|
// So, if the filesystem stores/encrypts [blockSize] bytes per block, then
|
||||||
|
// [blockSize - headerSize] of those bytes will contain user-supplied data,
|
||||||
|
// and the rest ([headerSize]) will contain the MAC header for this block.
|
||||||
|
// Example, offset points to second block (of user-data)
|
||||||
|
// offset = blockSize - headerSize
|
||||||
|
// ... 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert from a given location in the stream containing headers, and return a
|
||||||
|
// location in the user-data stream (which doesn't contain MAC headers)..
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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..
|
||||||
|
int headerSize = macBytes + randBytes;
|
||||||
|
int bs = blockSize() + headerSize;
|
||||||
|
|
||||||
|
off_t size = base->getSize();
|
||||||
|
if(size > 0)
|
||||||
|
size = locWithoutHeader( size, bs, headerSize );
|
||||||
|
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t MACFileIO::readOneBlock( const IORequest &req ) const
|
||||||
|
{
|
||||||
|
int headerSize = macBytes + randBytes;
|
||||||
|
|
||||||
|
int bs = blockSize() + headerSize;
|
||||||
|
|
||||||
|
MemBlock mb;
|
||||||
|
mb.allocate( bs );
|
||||||
|
|
||||||
|
IORequest tmp;
|
||||||
|
tmp.offset = locWithHeader( req.offset, bs, headerSize );
|
||||||
|
tmp.data = mb.data;
|
||||||
|
tmp.dataLen = headerSize + req.dataLen;
|
||||||
|
|
||||||
|
// get the data from the base FileIO layer
|
||||||
|
ssize_t readSize = base->read( tmp );
|
||||||
|
|
||||||
|
// 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(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 );
|
||||||
|
|
||||||
|
for(int i=0; i<macBytes; ++i, mac >>= 8)
|
||||||
|
{
|
||||||
|
int test = mac & 0xff;
|
||||||
|
int stored = tmp.data[i];
|
||||||
|
if(test != stored)
|
||||||
|
{
|
||||||
|
// uh oh..
|
||||||
|
long blockNum = req.offset / bs;
|
||||||
|
LOG(WARNING) << "MAC comparison failure in block " << blockNum;
|
||||||
|
if( !warnOnly )
|
||||||
|
{
|
||||||
|
throw Error(
|
||||||
|
_("MAC comparison failure, refusing to read"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now copy the data to the output buffer
|
||||||
|
readSize -= headerSize;
|
||||||
|
memcpy( req.data, tmp.data + headerSize, readSize );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
VLOG(1) << "readSize " << readSize << " at offset " << req.offset;
|
||||||
|
if(readSize > 0)
|
||||||
|
readSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return readSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MACFileIO::writeOneBlock( const IORequest &req )
|
||||||
|
{
|
||||||
|
int headerSize = macBytes + randBytes;
|
||||||
|
|
||||||
|
int bs = blockSize() + headerSize;
|
||||||
|
|
||||||
|
// we have the unencrypted data, so we need to attach a header to it.
|
||||||
|
MemBlock mb;
|
||||||
|
mb.allocate( bs );
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now, we can let the next level have it..
|
||||||
|
bool ok = base->write( newReq );
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
int MACFileIO::truncate( off_t size )
|
||||||
|
{
|
||||||
|
int headerSize = macBytes + randBytes;
|
||||||
|
int bs = blockSize() + headerSize;
|
||||||
|
|
||||||
|
int res = blockTruncate( size, 0 );
|
||||||
|
|
||||||
|
if(res == 0)
|
||||||
|
base->truncate( locWithHeader( size, bs, headerSize ) );
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MACFileIO::isWritable() const
|
||||||
|
{
|
||||||
|
return base->isWritable();
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user