pax_global_header00006660000000000000000000000064131045507660014521gustar00rootroot0000000000000052 comment=11a407f34c534e989b96494faa6c2cb444466267 kickpass-0.2.0/000077500000000000000000000000001310455076600133305ustar00rootroot00000000000000kickpass-0.2.0/.travis.yml000066400000000000000000000013621310455076600154430ustar00rootroot00000000000000language: c install: - sudo apt-get update -qq - sudo apt-get install -qq check libevent-dev - sudo pip install -r requirements.txt - cd ~/build - wget https://github.com/jedisct1/libsodium/releases/download/1.0.7/libsodium-1.0.7.tar.gz - tar xzf libsodium-1.0.7.tar.gz - cd libsodium-1.0.7 && ./configure --prefix=/usr && make && sudo make install - cd ~/build - wget http://libbsd.freedesktop.org/releases/libbsd-0.7.0.tar.xz - tar xJf libbsd-0.7.0.tar.xz - cd libbsd-0.7.0 && ./configure --prefix=/usr --libdir=/lib/x86_64-linux-gnu && make && sudo make install script: - cd ~/build - mkdir kickpass-build && cd kickpass-build && cmake -DCMAKE_INSTALL_PREFIX=/usr ~/build/paulfariello/kickpass && make && make test ARGS="-V" kickpass-0.2.0/CMakeLists.txt000066400000000000000000000113261310455076600160730ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # Configure cmake cmake_minimum_required(VERSION 2.8) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build) set(CTEST_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build) # Configure project project(KickPass C) include(CheckLibraryExists) include(GetGitRevisionDescription) git_describe(KickPass_VERSION "--always") if (NOT KickPass_VERSION OR "$ENV{TRAVIS}") set(KickPass_VERSION_MAJOR 0) set(KickPass_VERSION_MINOR 2) set(KickPass_VERSION_PATCH 0) else() message(STATUS "Kickpass git version: ${KickPass_VERSION}") string(REGEX REPLACE "v([0-9]+).*" "\\1" KickPass_VERSION_MAJOR ${KickPass_VERSION}) string(REGEX REPLACE "v[0-9]+\\.([0-9]+).*" "\\1" KickPass_VERSION_MINOR ${KickPass_VERSION}) string(REGEX REPLACE "v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" KickPass_VERSION_PATCH ${KickPass_VERSION}) endif() message(STATUS "Kickpass version: ${KickPass_VERSION_MAJOR}.${KickPass_VERSION_MINOR}.${KickPass_VERSION_PATCH}") include_directories("${PROJECT_BINARY_DIR}/include/") include_directories("${PROJECT_SOURCE_DIR}/include/") include_directories("${PROJECT_SOURCE_DIR}/src/") set(SRCS src/editor.c src/main.c src/prompt.c src/log.c # commands src/command/create.c src/command/delete.c src/command/edit.c src/command/init.c src/command/list.c src/command/cat.c src/command/rename.c src/command/agent.c src/command/open.c ) # Configure dependencies find_package(Sodium 1.0.0 REQUIRED) message(STATUS "Libsodium version: ${SODIUM_VERSION}") include_directories(${SODIUM_INCLUDE_DIRS}) set(LIBS ${LIBS} ${SODIUM_LIBRARIES}) check_library_exists(c readpassphrase "readpassphrase.h" HAVE_READPASSPHRASE) if (NOT HAVE_READPASSPHRASE) find_package(BSD REQUIRED) include_directories(${BSD_INCLUDE_DIRS}/bsd) set(LIBS ${LIBS} ${BSD_LIBRARIES}) # Add _GNU_SOURCE for asprintf add_definitions(-DLIBBSD_OVERLAY -D_GNU_SOURCE) endif() check_library_exists(util imsg_init "imsg.h" HAVE_IMSG) if (HAVE_IMSG) set(LIBS ${LIBS} util) else() add_library(imsg STATIC ${CMAKE_SOURCE_DIR}/compat/bsd/imsg-buffer.c ${CMAKE_SOURCE_DIR}/compat/bsd/imsg.c ${CMAKE_SOURCE_DIR}/compat/bsd/imsg.h ) include_directories(${CMAKE_SOURCE_DIR}/compat/bsd/) set(LIBS ${LIBS} imsg) endif() find_package(Event2 REQUIRED) include_directories(${EVENT2_INCLUDE_DIRS}) set(LIBS ${LIBS} ${EVENT2_LIBRARIES}) find_package(X11) if (X11_FOUND) include_directories(${X11_INCLUDE_DIRS}) set(LIBS ${LIBS} ${X11_LIBRARIES}) set(SRCS ${SRCS} src/command/copy.c) set(HAS_X11 true) else() message(WARNING "X11 not found. Skipping copy command.") endif() # Sources configure_file( "${PROJECT_SOURCE_DIR}/include/kickpass_config.h.in" "${PROJECT_BINARY_DIR}/include/kickpass_config.h" ) add_executable(kickpass ${SRCS}) # Link all dependencies target_link_libraries(kickpass ${LIBS}) # kickpass lib add_library(libkickpass SHARED lib/config.c lib/error.c lib/kickpass.c lib/password.c lib/safe.c lib/storage.c lib/kpagent.c ) set_target_properties(libkickpass PROPERTIES VERSION ${KickPass_VERSION_MAJOR}.${KickPass_VERSION_MINOR}.${KickPass_VERSION_PATCH} SOVERSION ${KickPass_VERSION_MAJOR}) set_target_properties(libkickpass PROPERTIES OUTPUT_NAME kickpass) target_link_libraries(kickpass libkickpass) # Configure build set(BUILD "Release" CACHE STRING "Selected build type") if(NOT ("$ENV{BUILD}" STREQUAL "")) set(CMAKE_BUILD_TYPE $ENV{BUILD}) else() set(CMAKE_BUILD_TYPE ${BUILD}) endif() message(STATUS "Build configuration (BUILD): ${CMAKE_BUILD_TYPE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_DEFAULT_SOURCE -D_BSD_SOURCE") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -D_FORTIFY_SOURCE=2 -fstack-protector") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Werror") # Configure install install(TARGETS kickpass libkickpass RUNTIME DESTINATION bin LIBRARY DESTINATION lib) install(FILES manual/kickpass.1 DESTINATION share/man/man1/) install(FILES extra/completion/zsh/_kickpass DESTINATION share/zsh/site-functions/) # Tests enable_testing() add_subdirectory(test) # Packaging include(Package) kickpass-0.2.0/CNAME000066400000000000000000000000141310455076600140710ustar00rootroot00000000000000kickpass.pw kickpass-0.2.0/README.md000066400000000000000000000040561310455076600146140ustar00rootroot00000000000000[![Build Status](https://travis-ci.org/paulfariello/kickpass.svg?branch=develop)](https://travis-ci.org/paulfariello/kickpass) Kickpass ======== Kickpass is a stupid simple password safe. It keep each password in a specific safe, protected with modern cryptography. Its main user interface is command line. Full documentation is available throught manual page ``kickpass(1)``: ```bash man 1 kickpass ``` Quick help looks like: ```bash usage: kickpass [-hv] [] [] options: -h, --help Print this help -v, --version Print kickpass version commands: help Print help for given command init Initialize a new password safe directory. Default to ~/.kickpass create [-hgl] Create a new password safe open Open a password safe and print its content on stdout edit [-pm] Edit a password safe with $EDIT copy Copy a password (first line of safe) into X clipboard list List available safes delete Delete a password safe after password confirmation rename Rename a password safe ``` Features -------- * One password to rule them all * One password to find them * One password to bring them all * Integrated password generator * Full text metadata with your favorite editor * Strong encryption: AEAD with chacha20 and poly1305 * Direct copy to X selection and clipboard Examples -------- ```bash $ kickpass create -g www/github.com [kickpass] master password: $ kickpass cat www/github.com url: https://www.github.com username: paulfariello $ kickpass copy www/github.com $ ``` Technical overview ------------------ Kickpass is built around a shared library named libkickpass. libkickpass leverage [libsodium](https://libsodium.org/) to create safes. Safes are created using authenticated encryption with associated data. As of now libkickpass use chacha20 along with poly1305 to encrypt and authenticate the safe. kickpass-0.2.0/_config.yml000066400000000000000000000000341310455076600154540ustar00rootroot00000000000000theme: jekyll-theme-minimal kickpass-0.2.0/build/000077500000000000000000000000001310455076600144275ustar00rootroot00000000000000kickpass-0.2.0/build/FindBSD.cmake000066400000000000000000000017751310455076600166540ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # # TODO test if we already are on BSD include(FindPackageHandleStandardArgs) find_path(BSD_INCLUDE_DIRS NAMES bsd/bsd.h) find_library(BSD_LIBRARIES NAMES bsd) find_package_handle_standard_args(BSD REQUIRED_VARS BSD_INCLUDE_DIRS BSD_LIBRARIES) kickpass-0.2.0/build/FindCheck.cmake000066400000000000000000000021721310455076600172510ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # include(FindPackageHandleStandardArgs) find_path(CHECK_INCLUDE_DIRS NAMES check.h) find_library(CHECK_LIBRARIES NAMES check) find_library(SUBUNIT_LIBRARIES NAMES subunit) if(NOT SUBUNIT_LIBRARIES MATCHES "NOTFOUND$") list(APPEND CHECK_LIBRARIES ${SUBUNIT_LIBRARIES}) endif() find_package_handle_standard_args(Check REQUIRED_VARS CHECK_INCLUDE_DIRS CHECK_LIBRARIES) kickpass-0.2.0/build/FindEvent2.cmake000066400000000000000000000017631310455076600174040ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # include(FindPackageHandleStandardArgs) find_path(EVENT2_INCLUDE_DIRS NAMES event2/event.h) find_library(EVENT2_LIBRARIES NAMES event_core) find_package_handle_standard_args(Event2 REQUIRED_VARS EVENT2_INCLUDE_DIRS EVENT2_LIBRARIES) kickpass-0.2.0/build/FindPythonModule.cmake000066400000000000000000000020111310455076600206530ustar00rootroot00000000000000# Find if a Python module is installed # Found at http://www.cmake.org/pipermail/cmake/2011-January/041666.html # To use do: find_python_module(PyQt4 REQUIRED) function(find_python_module module) string(TOUPPER ${module} module_upper) if(NOT PY_${module_upper}) if(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED") set(${module}_FIND_REQUIRED TRUE) endif() # A module's location is usually a directory, but for binary modules # it's a .so file. execute_process(COMMAND "${PYTHON_EXECUTABLE}" "-c" "import re, ${module}; print(re.compile('/__init__.py.*').sub('',${module}.__file__))" RESULT_VARIABLE _${module}_status OUTPUT_VARIABLE _${module}_location ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT _${module}_status) set(PY_${module_upper} ${_${module}_location} CACHE STRING "Location of Python module ${module}") endif(NOT _${module}_status) endif(NOT PY_${module_upper}) find_package_handle_standard_args(PY_${module} DEFAULT_MSG PY_${module_upper}) endfunction(find_python_module) kickpass-0.2.0/build/FindSodium.cmake000066400000000000000000000024021310455076600174700ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # include(FindPackageHandleStandardArgs) find_path(SODIUM_INCLUDE_DIRS NAMES sodium.h) find_library(SODIUM_LIBRARIES NAMES sodium) if (SODIUM_INCLUDE_DIRS) file(STRINGS "${SODIUM_INCLUDE_DIRS}/sodium/version.h" _SODIUM_VERSION_H_CONTENT REGEX "#define SODIUM_VERSION_STRING ") STRING (REGEX MATCH "([0-9]+\\.[0-9]+\\.[0-9]+)" SODIUM_VERSION "${_SODIUM_VERSION_H_CONTENT}") endif() find_package_handle_standard_args(Sodium REQUIRED_VARS SODIUM_INCLUDE_DIRS SODIUM_LIBRARIES VERSION_VAR SODIUM_VERSION) kickpass-0.2.0/build/FindX11.cmake000066400000000000000000000017311310455076600166050ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # include(FindPackageHandleStandardArgs) find_path(X11_INCLUDE_DIRS NAMES X11/Xlib.h) find_library(X11_LIBRARIES NAMES X11) find_package_handle_standard_args(X11 REQUIRED_VARS X11_INCLUDE_DIRS X11_LIBRARIES) kickpass-0.2.0/build/GetGitRevisionDescription.cmake000066400000000000000000000100261310455076600225360ustar00rootroot00000000000000# - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ ...]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) function(get_git_head_revision _refspecvar _hashvar) set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(GIT_DIR "${GIT_PARENT_DIR}/.git") while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) # We have reached the root directory, we are not in git set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() set(GIT_DIR "${GIT_PARENT_DIR}/.git") endwhile() # check if this is a submodule if(NOT IS_DIRECTORY ${GIT_DIR}) file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) endif() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${GIT_DIR}/HEAD") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process(COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() kickpass-0.2.0/build/GetGitRevisionDescription.cmake.in000066400000000000000000000022621310455076600231460ustar00rootroot00000000000000# # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # http://www.boost.org/LICENSE_1_0.txt) set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) set(HEAD_HASH "${HEAD_REF}") endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() kickpass-0.2.0/build/Package.cmake000066400000000000000000000026641310455076600167740ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # set(CPACK_GENERATOR "TGZ;DEB") set(CPACK_SET_DESTDIR TRUE) set(CPACK_PACKAGE_NAME ${PROJECT_NAME}) set(CPACK_PACKAGE_VENDOR "Paul Fariello") set(CPACK_PACKAGE_CONTACT "Paul Fariello ") set(CPACK_PACKAGE_VERSION_MAJOR ${KickPass_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${KickPass_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${KickPass_VERSION_PATCH}) set(CPACK_PACKAGE_VERSION "${KickPass_VERSION_MAJOR}.${KickPass_VERSION_MINOR}.${KickPass_VERSION_PATCH}") set(CPACK_PACKAGE_FULLNAME "${CPACK_PACKAGE_NAME}-${CMAKE_BUILD_TYPE}") set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FULLNAME}-${CPACK_PACKAGE_VERSION}-${CMAKE_SYSTEM_PROCESSOR}") include(CPack) kickpass-0.2.0/build/Test.cmake000066400000000000000000000057171310455076600163620ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # include(FindPythonModule) find_package(Check) find_program(VALGRIND_COMMAND valgrind) find_package(PythonInterp) if (VALGRIND_COMMAND) set(VALGRIND_OPTIONS "-q --tool=memcheck --show-reachable=yes --leak-check=full --gen-suppressions=all --track-origins=yes") set(VALGRIND_OPTIONS "${VALGRIND_OPTIONS} --suppressions=${CTEST_MODULE_PATH}/test-functional.sup") endif() if (CHECK_FOUND) macro(UNIT_TEST) set(oneValueArgs NAME FILE) set(multiValueArgs LIBS) cmake_parse_arguments(UNIT_TEST "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(TEST_NAME test-unit-${UNIT_TEST_NAME}) add_executable(${TEST_NAME} ${UNIT_TEST_FILE}) target_link_libraries(${TEST_NAME} ${CHECK_LIBRARIES}) target_link_libraries(${TEST_NAME} ${UNIT_TEST_LIBS}) set_property(TARGET ${TEST_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES ${CHECK_INCLUDE_DIRS}) add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) endmacro(UNIT_TEST) else() message(WARNING "Check not found. Skipping unit tests !") macro(UNIT_TEST) endmacro(UNIT_TEST) endif() if (PYTHON_EXECUTABLE) find_python_module(pexpect) if (PY_PEXPECT) macro(INTEGRATION_TEST) set(oneValueArgs NAME FILE) cmake_parse_arguments(INTEGRATION_TEST "" "${oneValueArgs}" "" ${ARGN}) set(TEST_NAME test-functional-${INTEGRATION_TEST_NAME}) add_test(NAME ${TEST_NAME} COMMAND ${CTEST_MODULE_PATH}/TestFunctionalDriver.sh $ "${TEST_NAME}" "${CMAKE_CURRENT_SOURCE_DIR}/${INTEGRATION_TEST_FILE}") set(TEST_ENV "") list(APPEND TEST_ENV "PYTHON=${PYTHON_EXECUTABLE}") list(APPEND TEST_ENV "HOME=${CMAKE_CURRENT_BINARY_DIR}/workspace") list(APPEND TEST_ENV "EDITOR_PATH=${CTEST_MODULE_PATH}") if (VALGRIND_COMMAND) list(APPEND TEST_ENV "VALGRIND_COMMAND=${VALGRIND_COMMAND}") list(APPEND TEST_ENV "VALGRIND_OPTIONS=${VALGRIND_OPTIONS}") endif() set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${TEST_ENV}") endmacro(INTEGRATION_TEST) else() message(WARNING "Python pexpect module not found. Skipping functional tests !") macro(INTEGRATION_TEST) endmacro(INTEGRATION_TEST) endif() else() message(WARNING "Python not found. Skipping functional tests !") macro(INTEGRATION_TEST) endmacro(INTEGRATION_TEST) endif() kickpass-0.2.0/build/TestFunctionalDriver.sh000077500000000000000000000005141310455076600211040ustar00rootroot00000000000000#!/bin/sh set -e export KP=$1 TEST_NAME=$2 TEST=$3 [ -d $HOME ] || mkdir $HOME [ ! -d $HOME/.kickpass ] || rm -r $HOME/.kickpass echo "Running test $TEST_NAME" echo "VALGRIND_COMMAND=\"$VALGRIND_COMMAND\" VALGRIND_OPTIONS=\"$VALGRIND_OPTIONS\" HOME=\"$HOME\" KP=\"$KP\" EDITOR_PATH=\"$EDITOR_PATH\" $PYTHON $TEST" $PYTHON $TEST kickpass-0.2.0/build/TestFunctionalEditorDate.sh000077500000000000000000000000311310455076600216670ustar00rootroot00000000000000#!/bin/sh date +%s > $1 kickpass-0.2.0/build/TestFunctionalEditorEnv.sh000077500000000000000000000000431310455076600215450ustar00rootroot00000000000000#!/bin/sh echo "$EDITOR_ENV" > $1 kickpass-0.2.0/build/TestFunctionalEditorSave.sh000077500000000000000000000000521310455076600217130ustar00rootroot00000000000000#!/bin/sh cat $1 > $HOME/editor-save.txt kickpass-0.2.0/build/test-functional.sup000066400000000000000000000005061310455076600203000ustar00rootroot00000000000000# Expect { Memcheck:Leak ... fun:TclpAlloc ... } { Memcheck:Leak ... fun:Tcl_NewStringObj ... } { Memcheck:Leak ... fun:Tcl_NewObj ... } { Memcheck:Leak ... fun:Tcl_CreateInterp ... } kickpass-0.2.0/compat/000077500000000000000000000000001310455076600146135ustar00rootroot00000000000000kickpass-0.2.0/compat/bsd/000077500000000000000000000000001310455076600153635ustar00rootroot00000000000000kickpass-0.2.0/compat/bsd/imsg-buffer.c000066400000000000000000000131621310455076600177400ustar00rootroot00000000000000/* $OpenBSD: imsg-buffer.c,v 1.3 2013/11/13 20:40:24 benno Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "imsg.h" int ibuf_realloc(struct ibuf *, size_t); void ibuf_enqueue(struct msgbuf *, struct ibuf *); void ibuf_dequeue(struct msgbuf *, struct ibuf *); struct ibuf * ibuf_open(size_t len) { struct ibuf *buf; if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) return (NULL); if ((buf->buf = malloc(len)) == NULL) { free(buf); return (NULL); } buf->size = buf->max = len; buf->fd = -1; return (buf); } struct ibuf * ibuf_dynamic(size_t len, size_t max) { struct ibuf *buf; if (max < len) return (NULL); if ((buf = ibuf_open(len)) == NULL) return (NULL); if (max > 0) buf->max = max; return (buf); } int ibuf_realloc(struct ibuf *buf, size_t len) { u_char *b; /* on static buffers max is eq size and so the following fails */ if (buf->wpos + len > buf->max) { errno = ENOMEM; return (-1); } b = realloc(buf->buf, buf->wpos + len); if (b == NULL) return (-1); buf->buf = b; buf->size = buf->wpos + len; return (0); } int ibuf_add(struct ibuf *buf, const void *data, size_t len) { if (buf->wpos + len > buf->size) if (ibuf_realloc(buf, len) == -1) return (-1); memcpy(buf->buf + buf->wpos, data, len); buf->wpos += len; return (0); } void * ibuf_reserve(struct ibuf *buf, size_t len) { void *b; if (buf->wpos + len > buf->size) if (ibuf_realloc(buf, len) == -1) return (NULL); b = buf->buf + buf->wpos; buf->wpos += len; return (b); } void * ibuf_seek(struct ibuf *buf, size_t pos, size_t len) { /* only allowed to seek in already written parts */ if (pos + len > buf->wpos) return (NULL); return (buf->buf + pos); } size_t ibuf_size(struct ibuf *buf) { return (buf->wpos); } size_t ibuf_left(struct ibuf *buf) { return (buf->max - buf->wpos); } void ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) { ibuf_enqueue(msgbuf, buf); } int ibuf_write(struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; bzero(&iov, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; } again: if ((n = writev(msgbuf->fd, iov, i)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } msgbuf_drain(msgbuf, n); return (1); } void ibuf_free(struct ibuf *buf) { free(buf->buf); free(buf); } void msgbuf_init(struct msgbuf *msgbuf) { msgbuf->queued = 0; msgbuf->fd = -1; TAILQ_INIT(&msgbuf->bufs); } void msgbuf_drain(struct msgbuf *msgbuf, size_t n) { struct ibuf *buf, *next; for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; buf = next) { next = TAILQ_NEXT(buf, entry); if (buf->rpos + n >= buf->wpos) { n -= buf->wpos - buf->rpos; ibuf_dequeue(msgbuf, buf); } else { buf->rpos += n; n = 0; } } } void msgbuf_clear(struct msgbuf *msgbuf) { struct ibuf *buf; while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) ibuf_dequeue(msgbuf, buf); } int msgbuf_write(struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; bzero(&iov, sizeof(iov)); bzero(&msg, sizeof(msg)); TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = buf->buf + buf->rpos; iov[i].iov_len = buf->wpos - buf->rpos; i++; if (buf->fd != -1) break; } msg.msg_iov = iov; msg.msg_iovlen = i; if (buf != NULL && buf->fd != -1) { msg.msg_control = (caddr_t)&cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(cmsg) = buf->fd; } again: if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == ENOBUFS) errno = EAGAIN; return (-1); } if (n == 0) { /* connection closed */ errno = 0; return (0); } /* * assumption: fd got sent if sendmsg sent anything * this works because fds are passed one at a time */ if (buf != NULL && buf->fd != -1) { close(buf->fd); buf->fd = -1; } msgbuf_drain(msgbuf, n); return (1); } void ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); msgbuf->queued++; } void ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) { TAILQ_REMOVE(&msgbuf->bufs, buf, entry); if (buf->fd != -1) close(buf->fd); msgbuf->queued--; ibuf_free(buf); } kickpass-0.2.0/compat/bsd/imsg.c000066400000000000000000000146341310455076600164760ustar00rootroot00000000000000/* $OpenBSD: imsg.c,v 1.5 2013/12/26 17:32:33 eric Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "imsg.h" int imsg_fd_overhead = 0; int imsg_get_fd(struct imsgbuf *); int available_fds(unsigned int n) { unsigned int i; int ret, fds[256]; if (n > (sizeof(fds)/sizeof(fds[0]))) return (1); ret = 0; for (i = 0; i < n; i++) { fds[i] = -1; if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { ret = 1; break; } } for (i = 0; i < n && fds[i] >= 0; i++) close(fds[i]); return (ret); } void imsg_init(struct imsgbuf *ibuf, int fd) { msgbuf_init(&ibuf->w); bzero(&ibuf->r, sizeof(ibuf->r)); ibuf->fd = fd; ibuf->w.fd = fd; ibuf->pid = getpid(); TAILQ_INIT(&ibuf->fds); } ssize_t imsg_read(struct imsgbuf *ibuf) { struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; ssize_t n = -1; int fd; struct imsg_fd *ifd; bzero(&msg, sizeof(msg)); iov.iov_base = ibuf->r.buf + ibuf->r.wpos; iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) return (-1); again: if (available_fds(imsg_fd_overhead + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { errno = EAGAIN; free(ifd); return (-1); } if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { if (errno == EMSGSIZE) goto fail; if (errno != EINTR && errno != EAGAIN) goto fail; goto again; } ibuf->r.wpos += n; for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { int i; int j; /* * We only accept one file descriptor. Due to C * padding rules, our control buffer might contain * more than one fd, and we must close them. */ j = ((char *)cmsg + cmsg->cmsg_len - (char *)CMSG_DATA(cmsg)) / sizeof(int); for (i = 0; i < j; i++) { fd = ((int *)CMSG_DATA(cmsg))[i]; if (ifd != NULL) { ifd->fd = fd; TAILQ_INSERT_TAIL(&ibuf->fds, ifd, entry); ifd = NULL; } else close(fd); } } /* we do not handle other ctl data level */ } fail: if (ifd) free(ifd); return (n); } ssize_t imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) { size_t av, left, datalen; av = ibuf->r.wpos; if (IMSG_HEADER_SIZE > av) return (0); memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); if (imsg->hdr.len < IMSG_HEADER_SIZE || imsg->hdr.len > MAX_IMSGSIZE) { errno = ERANGE; return (-1); } if (imsg->hdr.len > av) return (0); datalen = imsg->hdr.len - IMSG_HEADER_SIZE; ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; if ((imsg->data = malloc(datalen)) == NULL) return (-1); if (imsg->hdr.flags & IMSGF_HASFD) imsg->fd = imsg_get_fd(ibuf); else imsg->fd = -1; memcpy(imsg->data, ibuf->r.rptr, datalen); if (imsg->hdr.len < av) { left = av - imsg->hdr.len; memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); ibuf->r.wpos = left; } else ibuf->r.wpos = 0; return (datalen + IMSG_HEADER_SIZE); } int imsg_compose(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, pid_t pid, int fd, const void *data, u_int16_t datalen) { struct ibuf *wbuf; if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) return (-1); if (imsg_add(wbuf, data, datalen) == -1) return (-1); wbuf->fd = fd; imsg_close(ibuf, wbuf); return (1); } int imsg_composev(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, pid_t pid, int fd, const struct iovec *iov, int iovcnt) { struct ibuf *wbuf; int i, datalen = 0; for (i = 0; i < iovcnt; i++) datalen += iov[i].iov_len; if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) return (-1); for (i = 0; i < iovcnt; i++) if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) return (-1); wbuf->fd = fd; imsg_close(ibuf, wbuf); return (1); } /* ARGSUSED */ struct ibuf * imsg_create(struct imsgbuf *ibuf, u_int32_t type, u_int32_t peerid, pid_t pid, u_int16_t datalen) { struct ibuf *wbuf; struct imsg_hdr hdr; datalen += IMSG_HEADER_SIZE; if (datalen > MAX_IMSGSIZE) { errno = ERANGE; return (NULL); } hdr.type = type; hdr.flags = 0; hdr.peerid = peerid; if ((hdr.pid = pid) == 0) hdr.pid = ibuf->pid; if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { return (NULL); } if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) return (NULL); return (wbuf); } int imsg_add(struct ibuf *msg, const void *data, u_int16_t datalen) { if (datalen) if (ibuf_add(msg, data, datalen) == -1) { ibuf_free(msg); return (-1); } return (datalen); } void imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) { struct imsg_hdr *hdr; hdr = (struct imsg_hdr *)msg->buf; hdr->flags &= ~IMSGF_HASFD; if (msg->fd != -1) hdr->flags |= IMSGF_HASFD; hdr->len = (u_int16_t)msg->wpos; ibuf_close(&ibuf->w, msg); } void imsg_free(struct imsg *imsg) { free(imsg->data); } int imsg_get_fd(struct imsgbuf *ibuf) { int fd; struct imsg_fd *ifd; if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) return (-1); fd = ifd->fd; TAILQ_REMOVE(&ibuf->fds, ifd, entry); free(ifd); return (fd); } int imsg_flush(struct imsgbuf *ibuf) { while (ibuf->w.queued) if (msgbuf_write(&ibuf->w) <= 0) return (-1); return (0); } void imsg_clear(struct imsgbuf *ibuf) { int fd; msgbuf_clear(&ibuf->w); while ((fd = imsg_get_fd(ibuf)) != -1) close(fd); } kickpass-0.2.0/compat/bsd/imsg.h000066400000000000000000000060411310455076600164740ustar00rootroot00000000000000/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric Exp $ */ /* * Copyright (c) 2006, 2007 Pierre-Yves Ritschard * Copyright (c) 2006, 2007, 2008 Reyk Floeter * Copyright (c) 2003, 2004 Henning Brauer * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _IMSG_H_ #define _IMSG_H_ #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 struct ibuf { TAILQ_ENTRY(ibuf) entry; u_char *buf; size_t size; size_t max; size_t wpos; size_t rpos; int fd; }; struct msgbuf { TAILQ_HEAD(, ibuf) bufs; u_int32_t queued; int fd; }; struct ibuf_read { u_char buf[IBUF_READ_SIZE]; u_char *rptr; size_t wpos; }; struct imsg_fd { TAILQ_ENTRY(imsg_fd) entry; int fd; }; struct imsgbuf { TAILQ_HEAD(, imsg_fd) fds; struct ibuf_read r; struct msgbuf w; int fd; pid_t pid; }; #define IMSGF_HASFD 1 struct imsg_hdr { u_int32_t type; u_int16_t len; u_int16_t flags; u_int32_t peerid; u_int32_t pid; }; struct imsg { struct imsg_hdr hdr; int fd; void *data; }; /* buffer.c */ struct ibuf *ibuf_open(size_t); struct ibuf *ibuf_dynamic(size_t, size_t); int ibuf_add(struct ibuf *, const void *, size_t); void *ibuf_reserve(struct ibuf *, size_t); void *ibuf_seek(struct ibuf *, size_t, size_t); size_t ibuf_size(struct ibuf *); size_t ibuf_left(struct ibuf *); void ibuf_close(struct msgbuf *, struct ibuf *); int ibuf_write(struct msgbuf *); void ibuf_free(struct ibuf *); void msgbuf_init(struct msgbuf *); void msgbuf_clear(struct msgbuf *); int msgbuf_write(struct msgbuf *); void msgbuf_drain(struct msgbuf *, size_t); /* imsg.c */ int available_fds(unsigned int); void imsg_init(struct imsgbuf *, int); ssize_t imsg_read(struct imsgbuf *); ssize_t imsg_get(struct imsgbuf *, struct imsg *); int imsg_compose(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, int, const void *, u_int16_t); int imsg_composev(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, int, const struct iovec *, int); struct ibuf *imsg_create(struct imsgbuf *, u_int32_t, u_int32_t, pid_t, u_int16_t); int imsg_add(struct ibuf *, const void *, u_int16_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); int imsg_flush(struct imsgbuf *); void imsg_clear(struct imsgbuf *); #endif kickpass-0.2.0/extra/000077500000000000000000000000001310455076600144535ustar00rootroot00000000000000kickpass-0.2.0/extra/completion/000077500000000000000000000000001310455076600166245ustar00rootroot00000000000000kickpass-0.2.0/extra/completion/zsh/000077500000000000000000000000001310455076600174305ustar00rootroot00000000000000kickpass-0.2.0/extra/completion/zsh/_kickpass000066400000000000000000000074131310455076600213270ustar00rootroot00000000000000#compdef kickpass (( $+functions[_kp_safes] )) || _kp_safes() { local files expl files=(${(f)"$(_call_program files kickpass ls 2>/dev/null)"}) _wanted safe expl "Safe" _multi_parts / files } (( $+functions[_kp_path] )) || _kp_path() { local files expl paths=(${(f)"$(_call_program files kickpass ls 2>/dev/null | sed 's#\(.*/\)[^/]\+$#\1#' | sort -u)"}) _wanted path expl "Path" _multi_parts / paths } (( $+functions[_kp-help] )) || _kp-help() { _arguments \ '(-): :->command' \ && return case $state in (command) _kp_commands ;; esac } (( $+functions[_kp-init] )) || _kp-init() { # Nothing } (( $+functions[_kp-create] )) || _kp-create() { _arguments \ {-g,--generate}'[Randomly generate a password]' \ {-l,--length}='[Length of the generated passwerd]' \ *:'Safe name:' && return } (( $+functions[_kp-cat] )) || _kp-cat() { _arguments \ {-p,--password}'[Print password]' \ {-m,--metadata}='[Print metadata]' \ :'Safe to open:->safe' && return case $state in (safe) _alternative 'safe::_kp_safes' ;; esac return } (( $+functions[_kp-edit] )) || _kp-edit() { _arguments \ {-p,--password}'[Edit password]' \ {-m,--metadata}='[Edit metadata]' \ :'Safe to edit:->safe' && return case $state in (safe) _alternative 'safe::_kp_safes' ;; esac return } (( $+functions[_kp-copy] )) || _kp-copy() { _arguments \ :'Safe to copy:->safe' && return case $state in (safe) _alternative 'safe::_kp_safes' ;; esac return } (( $+functions[_kp-list] )) || _kp-list() { _arguments \ :'Path to list:->path' && return case $state in (path) _alternative 'safe::_kp_path' ;; esac return } (( $+functions[_kp-delete] )) || _kp-delete() { _arguments \ :'Safe to delete:->safe' && return case $state in (safe) _alternative 'safe::_kp_safes' ;; esac return } (( $+functions[_kp-rename] )) || _kp-rename() { _arguments \ :'Safe to edit:->safe' \ :'New safe name:' && return case $state in (safe) _alternative 'safe::_kp_safes' ;; esac return } (( $+functions[_kp_commands] )) || _kp_commands() { local -a kp_commands kp_commands=( help:'Get some help' \ init:'Initialize a new password safe directory' \ {create,new,insert}:'Create a new password safe' \ {cat,show}:'Open a password safe and print its content on stdout' \ edit:'Edit a password safe with $EDIT' \ copy:'Copy a password (first line of safe) into X clipboard' \ {list,ls}:'List available safes' \ {delete,rm,remove,destroy}:'Delete a password safe' \ {rename,mv,move}:'Rename a password safe' agent:'Start a kickpass agent in background' \ ) _tags kp-commands _describe -t kp-commands 'main kickpass commands' kp_commands return } _kickpass() { local curcontext=$curcontext state line _arguments -C : \ {-v,--version}'[Print kickpass version]' \ {-h,--help}'[Print help]'\ '(-): :->command' \ '(-)*:: :->option-or-argument' \ && return case $state in (command) _kp_commands ;; (option-or-argument) local cmd cmds typeset -A cmds # Define aliases cmds=() # help cmds[help]=help # init cmds[init]=init # create cmds[create]=create cmds[new]=create cmds[insert]=create # cat cmds[cat]=cat cmds[show]=cat # edit cmds[edit]=edit # copy cmds[copy]=copy # list cmds[list]=list cmds[ls]=list # delete cmds[delete]=delete cmds[rm]=delete cmds[remove]=delete cmds[destroy]=delete # rename cmds[rename]=rename cmds[move]=rename cmds[mv]=rename # agent cmds[agent]=agent cmd=$cmds[$words[1]] (( $+cmds[$words[1]] )) || cmd=$words[1] curcontext=${curcontext%:*:*}:kp-$words[1]: if ! _call_function ret _kp-$cmd; then _message "unknown sub-command: $cmd" fi ;; esac return } _kickpass kickpass-0.2.0/include/000077500000000000000000000000001310455076600147535ustar00rootroot00000000000000kickpass-0.2.0/include/config.h000066400000000000000000000021661310455076600163760ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_CONFIG_H #define KP_CONFIG_H #include #include "kickpass.h" kp_error_t kp_cfg_create(struct kp_ctx *, const char *); kp_error_t kp_cfg_load(struct kp_ctx *, const char *); kp_error_t kp_cfg_save(struct kp_ctx *, const char *); kp_error_t kp_cfg_find(struct kp_ctx *, const char *, char *, size_t); #endif /* KP_CONFIG_H */ kickpass-0.2.0/include/editor.h000066400000000000000000000016741310455076600164220ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_EDITOR_H #define KP_EDITOR_H #include "kickpass.h" #include "safe.h" kp_error_t kp_edit(struct kp_ctx *, struct kp_safe *); #endif /* KP_EDITOR_H */ kickpass-0.2.0/include/error.h000066400000000000000000000023751310455076600162640ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_ERROR_H #define KP_ERROR_H #include #define KP_SUCCESS 0 #define KP_NYI 1 #define KP_EINPUT 2 #define KP_EINTERNAL 3 #define KP_INVALID_STORAGE 4 #define KP_ERRNO 5 #define KP_NO_HOME 6 #define KP_EDECRYPT 7 #define KP_EENCRYPT 8 #define KP_INVALID_MSG 9 #define KP_EXIT 10 typedef int kp_error_t; const char *kp_strerror(kp_error_t errnum); #endif /* KP_ERROR_H */ kickpass-0.2.0/include/kickpass.h000066400000000000000000000032021310455076600167310ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_KICKPASS_H #define KP_KICKPASS_H #include #include #include #include #include #include #include #include #include "error.h" #include "kickpass_config.h" #define KP_PASSWORD_MAX_LEN 4096 #define KP_METADATA_MAX_LEN 4096 struct kp_agent { int sock; struct imsgbuf ibuf; struct sockaddr_un sunaddr; bool connected; }; struct kp_ctx { char ws_path[PATH_MAX]; struct kp_agent agent; kp_error_t (*password_prompt)(struct kp_ctx *, bool, char *, const char *, ...) __attribute__((format(printf, 4, 5))); char * const password; struct { long long unsigned opslimit; size_t memlimit; } cfg; }; kp_error_t kp_init(struct kp_ctx *); kp_error_t kp_fini(struct kp_ctx *); kp_error_t kp_init_workspace(struct kp_ctx *, const char *); #endif /* KP_KICKPASS_H */ kickpass-0.2.0/include/kickpass_config.h.in000066400000000000000000000021211310455076600206620ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_KICKPASS_CONFIG_H #define KP_KICKPASS_CONFIG_H #define KICKPASS_VERSION_MAJOR @KickPass_VERSION_MAJOR@ #define KICKPASS_VERSION_MINOR @KickPass_VERSION_MINOR@ #define KICKPASS_VERSION_PATCH @KickPass_VERSION_PATCH@ #define KP_PATH ".kickpass" #cmakedefine HAS_X11 #endif /* KP_KICKPASS_CONFIG_H */ kickpass-0.2.0/include/kpagent.h000066400000000000000000000050221310455076600165540ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_KPAGENT_H #define KP_KPAGENT_H #include #include "kickpass.h" #define KP_AGENT_SOCKET_ENV "KP_AGENT_SOCK" enum kp_agent_msg_type { KP_MSG_STORE, KP_MSG_SEARCH, KP_MSG_DISCARD, KP_MSG_ERROR, }; struct kp_msg_error { kp_error_t err; int err_no; }; struct kp_unsafe { time_t timeout; /* timeout of the safe */ char path[PATH_MAX]; /* name of the safe */ char password[KP_PASSWORD_MAX_LEN]; /* plain text password (null terminated) */ char metadata[KP_METADATA_MAX_LEN]; /* plain text metadata (null terminated) */ }; #define KP_UNSAFE_INIT { .timeout = ((time_t) -1), .path = "", .password = "", .metadata = "" } struct kp_agent_safe { char path[PATH_MAX]; /* name of the safe */ char * const password; /* plain text password (null terminated) */ char * const metadata; /* plain text metadata (null terminated) */ }; /* Client side */ kp_error_t kp_agent_init(struct kp_agent *, const char *); kp_error_t kp_agent_connect(struct kp_agent *); kp_error_t kp_agent_listen(struct kp_agent *); kp_error_t kp_agent_accept(struct kp_agent *, struct kp_agent *); kp_error_t kp_agent_send(struct kp_agent *, enum kp_agent_msg_type, void *, size_t); kp_error_t kp_agent_error(struct kp_agent *, kp_error_t); kp_error_t kp_agent_receive(struct kp_agent *, enum kp_agent_msg_type, void *, size_t); kp_error_t kp_agent_close(struct kp_agent *); /* Server side */ kp_error_t kp_agent_safe_create(struct kp_agent *, struct kp_agent_safe **); kp_error_t kp_agent_store(struct kp_agent *, struct kp_agent_safe *); kp_error_t kp_agent_search(struct kp_agent *, char *, struct kp_agent_safe **); kp_error_t kp_agent_discard(struct kp_agent *, char *); kp_error_t kp_agent_safe_free(struct kp_agent *, struct kp_agent_safe *); #endif /* KP_KPAGENT_H */ kickpass-0.2.0/include/password.h000066400000000000000000000016511310455076600167710ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_PASSWORD_H #define KP_PASSWORD_H #include "kickpass.h" kp_error_t kp_password_generate(char *, size_t); #endif /* KP_PASSWORD_H */ kickpass-0.2.0/include/safe.h000066400000000000000000000044611310455076600160470ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_SAFE_H #define KP_SAFE_H #include #include "kickpass.h" #ifndef KP_METADATA_TEMPLATE #define KP_METADATA_TEMPLATE "url: \n" \ "username: \n" \ "comment: \n" #endif #define KP_PLAIN_MAX_SIZE (KP_PASSWORD_MAX_LEN + KP_METADATA_MAX_LEN) /* * A safe is either open or close. * Plain data are stored in memory. * Cipher data are stored in file. */ struct kp_safe { bool open; /* whether the safe is open or not */ bool ro; /* whether the safe is read only or not */ char name[PATH_MAX]; /* name of the safe */ int cipher; /* fd of the cipher file if the safe is open */ char * const password; /* plain text password (null terminated) */ char * const metadata; /* plain text metadata (null terminated) */ }; kp_error_t kp_safe_create(struct kp_ctx *, struct kp_safe *, const char *); kp_error_t kp_safe_delete(struct kp_ctx *, struct kp_safe *); kp_error_t kp_safe_load(struct kp_ctx *, struct kp_safe *, const char *); kp_error_t kp_safe_save(struct kp_ctx *, struct kp_safe *); kp_error_t kp_safe_open(struct kp_ctx *, struct kp_safe *, bool); kp_error_t kp_safe_close(struct kp_ctx *, struct kp_safe *); kp_error_t kp_safe_get_path(struct kp_ctx *, struct kp_safe *, char *, size_t); kp_error_t kp_safe_rename(struct kp_ctx *, struct kp_safe *, const char *); kp_error_t kp_safe_store(struct kp_ctx *, struct kp_safe *, int); #endif /* KP_SAFE_H */ kickpass-0.2.0/include/storage.h000066400000000000000000000020061310455076600165660ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_STORAGE_H #define KP_STORAGE_H #include "kickpass.h" #include "safe.h" kp_error_t kp_storage_open(struct kp_ctx *, struct kp_safe *); kp_error_t kp_storage_save(struct kp_ctx *, struct kp_safe *); #endif /* KP_STORAGE_H */ kickpass-0.2.0/lib/000077500000000000000000000000001310455076600140765ustar00rootroot00000000000000kickpass-0.2.0/lib/config.c000066400000000000000000000146261310455076600155200ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "kickpass.h" #include "config.h" #include "safe.h" #define KP_CONFIG_SAFE_NAME ".config" #define KP_CONFIG_TEMPLATE \ "memlimit: %lu\n" \ "opslimit: %llu\n" \ #define CONFIG(name, type) { .key = #name, \ .offset = (size_t)((const volatile void *)&((struct kp_ctx *)0)->cfg.name), \ .getter = type ## _config_getter, \ .setter = type ## _config_setter } #define CONFIG_GET(config, ctx, type) ((type *)(&((char *)ctx)[config->offset])) #define N_CONFIG (sizeof(configs)/sizeof(configs[0])) struct config; static int config_sort(const void *, const void *); static int config_search(const void *, const void *); static kp_error_t size_t_config_getter(struct config *, struct kp_ctx *, char **); static kp_error_t size_t_config_setter(struct config *, struct kp_ctx *, char *); static kp_error_t llu_config_getter(struct config *, struct kp_ctx *, char **); static kp_error_t llu_config_setter(struct config *, struct kp_ctx *, char *); struct config { char *key; size_t offset; kp_error_t (*getter)(struct config *, struct kp_ctx *, char **); kp_error_t (*setter)(struct config *, struct kp_ctx *, char *); } configs[] = { CONFIG(memlimit, size_t), CONFIG(opslimit, llu), }; kp_error_t kp_cfg_create(struct kp_ctx *ctx, const char *sub) { kp_error_t ret; char path[PATH_MAX] = ""; struct kp_safe cfg_safe; if (strlcpy(path , sub, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(path , "/" KP_CONFIG_SAFE_NAME, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if ((ret = kp_safe_create(ctx, &cfg_safe, path)) != KP_SUCCESS) { return ret; } snprintf(cfg_safe.password, KP_PASSWORD_MAX_LEN, "%s", ""); snprintf(cfg_safe.metadata, KP_METADATA_MAX_LEN, KP_CONFIG_TEMPLATE, ctx->cfg.memlimit, ctx->cfg.opslimit); if ((ret = kp_safe_save(ctx, &cfg_safe)) != KP_SUCCESS) { return ret; } if ((ret = kp_safe_close(ctx, &cfg_safe)) != KP_SUCCESS) { return ret; } return KP_SUCCESS; } kp_error_t kp_cfg_load(struct kp_ctx *ctx, const char *sub) { kp_error_t ret; char path[PATH_MAX] = ""; struct kp_safe cfg_safe; char *line = NULL, *save_line = NULL; if (strlcpy(path , sub, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(path , "/" KP_CONFIG_SAFE_NAME, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if ((ret = kp_safe_load(ctx, &cfg_safe, path)) != KP_SUCCESS) { return ret; } if ((ret = kp_safe_open(ctx, &cfg_safe, false)) != KP_SUCCESS) { return ret; } if (ctx->agent.connected) { if ((ret = kp_safe_store(ctx, &cfg_safe, 3600)) != KP_SUCCESS) { return ret; } } qsort(configs, N_CONFIG, sizeof(struct config), config_sort); line = strtok_r(cfg_safe.metadata, "\n", &save_line); while (line != NULL) { struct config *config; char *key, *value; key = strtok(line, ":"); value = strtok(NULL, ":"); config = bsearch(key, configs, N_CONFIG, sizeof(struct config), config_search); if (config != NULL) { config->setter(config, ctx, value); } line = strtok_r(NULL, "\n", &save_line); } if ((ret = kp_safe_close(ctx, &cfg_safe)) != KP_SUCCESS) { return ret; } return KP_SUCCESS; } kp_error_t kp_cfg_save(struct kp_ctx *ctx, const char *sub) { /* Nothing to do for now */ return KP_SUCCESS; } kp_error_t kp_cfg_find(struct kp_ctx *ctx, const char *path, char *cfg_path, size_t size) { char *dir = NULL; struct stat stats; if (strlcpy(cfg_path, "/", size) >= size) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(cfg_path, path, size) >= size) { errno = ENOMEM; return KP_ERRNO; } while ((dir = strrchr(cfg_path, '/')) != NULL) { char abspath[PATH_MAX] = ""; dir[0] = '\0'; if (strlcpy(abspath, ctx->ws_path, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(abspath, "/", PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(abspath, cfg_path, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(abspath, "/" KP_CONFIG_SAFE_NAME, PATH_MAX) >= PATH_MAX) { return KP_ERRNO; } if (stat(abspath, &stats) < 0) { if (errno == ENOENT) { continue; } return KP_ERRNO; } else { break; } } if (dir == NULL) { errno = ENOENT; return KP_ERRNO; } return KP_SUCCESS; } static int config_sort(const void *a, const void *b) { return strcmp(((struct config *)a)->key, ((struct config *)b)->key); } static int config_search(const void *k, const void *e) { return strcmp(k, ((struct config *)e)->key); } static kp_error_t size_t_config_setter(struct config *config, struct kp_ctx *ctx, char *str_value) { size_t *value = NULL; value = CONFIG_GET(config, ctx, size_t); *value = atol(str_value); return KP_SUCCESS; } static kp_error_t size_t_config_getter(struct config *config, struct kp_ctx *ctx, char **str_value) { size_t *value = NULL; value = CONFIG_GET(config, ctx, size_t); if (asprintf(str_value, "%lu", *value) < 0) { errno = ENOMEM; return KP_ERRNO; } return KP_SUCCESS; } static kp_error_t llu_config_setter(struct config *config, struct kp_ctx *ctx, char *str_value) { long long unsigned *value = NULL; value = CONFIG_GET(config, ctx, long long unsigned); *value = atoll(str_value); return KP_SUCCESS; } static kp_error_t llu_config_getter(struct config *config, struct kp_ctx *ctx, char **str_value) { long long unsigned *value = NULL; value = CONFIG_GET(config, ctx, long long unsigned); if (asprintf(str_value, "%llu", *value) < 0) { errno = ENOMEM; return KP_ERRNO; } return KP_SUCCESS; } kickpass-0.2.0/lib/error.c000066400000000000000000000021041310455076600153700ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "error.h" static const char *errstr[] = { "", "not yet implemented", "invalid input", "internal error", "invalid storage", "", "invalid HOME environmnent variable", "decryption error", "encryption error", }; const char * kp_strerror(kp_error_t errnum) { return errstr[errnum]; } kickpass-0.2.0/lib/kickpass.c000066400000000000000000000050631310455076600160560ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "kickpass.h" #include "config.h" /* * Init kickpass with a working directory and its corresponding master password. */ kp_error_t kp_init(struct kp_ctx *ctx) { const char *home; char **password; password = (char **)&ctx->password; home = getenv("HOME"); if (!home) { return KP_NO_HOME; } if (strlcpy(ctx->ws_path, home, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(ctx->ws_path, "/" KP_PATH, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (sodium_init() != 0) { return KP_EINTERNAL; } *password = sodium_malloc(KP_PASSWORD_MAX_LEN); if (!ctx->password) { errno = ENOMEM; return KP_ERRNO; } ctx->password[0] = '\0'; ctx->cfg.memlimit = crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_SENSITIVE/5; ctx->cfg.opslimit = crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_SENSITIVE/5; ctx->agent.connected = false; return KP_SUCCESS; } kp_error_t kp_fini(struct kp_ctx *ctx) { sodium_free(ctx->password); return KP_SUCCESS; } kp_error_t kp_init_workspace(struct kp_ctx *ctx, const char *sub) { kp_error_t ret = KP_SUCCESS; struct stat stats; char path[PATH_MAX] = ""; if (strlcpy(path , ctx->ws_path, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(path , "/", PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(path , sub, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (stat(path, &stats) == 0) { errno = EEXIST; ret = KP_ERRNO; goto out; } else if (errno & ENOENT) { if (mkdir(path, 0700) < 0) { ret = KP_ERRNO; goto out; } } else { ret = KP_ERRNO; goto out; } out: return ret; } kickpass-0.2.0/lib/kpagent.c000066400000000000000000000157121310455076600157010ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "kickpass.h" #include "error.h" #include "kpagent.h" #define SOCKET_BACKLOG 128 /* on some system stat.h uses a variable named __unused */ #ifndef __unused #define __unused __attribute__((unused)) #endif struct kp_store { RB_ENTRY(kp_store) tree; struct kp_agent_safe *safe; }; static int store_cmp(struct kp_store *, struct kp_store *); RB_HEAD(storage, kp_store) storage = RB_INITIALIZER(&storage); RB_PROTOTYPE_STATIC(storage, kp_store, tree, store_cmp); kp_error_t kp_agent_init(struct kp_agent *agent, const char *socket_path) { assert(agent); agent->sock = -1; agent->connected = false; memset(&agent->sunaddr, 0, sizeof(struct sockaddr_un)); agent->sunaddr.sun_family = AF_UNIX; if (strlcpy(agent->sunaddr.sun_path, socket_path, sizeof(agent->sunaddr.sun_path)) >= sizeof(agent->sunaddr.sun_path)) { errno = ENAMETOOLONG; return KP_ERRNO; } if ((agent->sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { return KP_ERRNO; } imsg_init(&agent->ibuf, agent->sock); return KP_SUCCESS; } kp_error_t kp_agent_connect(struct kp_agent *agent) { assert(agent); if (connect(agent->sock, (struct sockaddr *)&agent->sunaddr, sizeof(struct sockaddr_un)) < 0) { return KP_ERRNO; } agent->connected = true; return KP_SUCCESS; } kp_error_t kp_agent_listen(struct kp_agent *agent) { assert(agent); if (bind(agent->sock, (struct sockaddr *)&agent->sunaddr, sizeof(struct sockaddr_un)) < 0) { return KP_ERRNO; } if (listen(agent->sock, SOCKET_BACKLOG) < 0) { return KP_ERRNO; } return KP_SUCCESS; } kp_error_t kp_agent_accept(struct kp_agent *agent, struct kp_agent *out) { socklen_t addrlen = sizeof(struct sockaddr_un); assert(agent); assert(out); out->sock = -1; out->connected = false; if ((out->sock = accept(agent->sock, (struct sockaddr *)&out->sunaddr, &addrlen)) < 0) { return KP_ERRNO; } imsg_init(&out->ibuf, out->sock); out->connected = true; return KP_SUCCESS; } kp_error_t kp_agent_send(struct kp_agent *agent, enum kp_agent_msg_type type, void *data, size_t size) { assert(agent); if (imsg_compose(&agent->ibuf, type, 1, 0, -1, data, size) < 0) { return KP_ERRNO; } if (imsg_flush(&agent->ibuf) < 0) { return KP_ERRNO; } return KP_SUCCESS; } kp_error_t kp_agent_error(struct kp_agent *agent, kp_error_t err) { struct kp_msg_error error; error.err = err; error.err_no = 0; if (err == KP_ERRNO) { error.err_no = errno; } return kp_agent_send(agent, KP_MSG_ERROR, &error, sizeof(struct kp_msg_error)); } kp_error_t kp_agent_receive(struct kp_agent *agent, enum kp_agent_msg_type type, void *data, size_t size) { kp_error_t ret; struct imsg imsg; assert(agent); /* Try to get one from ibuf */ if (imsg_get(&agent->ibuf, &imsg) > 0) { goto available; } /* Nothing in buf try to read from conn */ if (imsg_read(&agent->ibuf) < 0) { imsg_clear(&agent->ibuf); /* XXX clean conn */ return KP_ERRNO; } /* Try to get one from ibuf */ if (imsg_get(&agent->ibuf, &imsg) < 0) { return KP_ERRNO; } available: if (imsg.hdr.type > KP_MSG_ERROR) { /* XXX report real error */ ret = KP_INVALID_MSG; goto out; } if (imsg.hdr.type != type) { if (imsg.hdr.type == KP_MSG_ERROR) { struct kp_msg_error *error = (struct kp_msg_error *)imsg.data; if (error->err == KP_ERRNO) { errno = error->err_no; } ret = error->err; goto out; } else { ret = KP_INVALID_MSG; goto out; } } if (imsg.hdr.len - IMSG_HEADER_SIZE != size) { errno = EMSGSIZE; ret = KP_ERRNO; goto out; } ret = KP_SUCCESS; if (data) { memcpy(data, imsg.data, size); } out: imsg_free(&imsg); return ret; } kp_error_t kp_agent_close(struct kp_agent *agent) { imsg_clear(&agent->ibuf); if (agent->sock >= 0) { close(agent->sock); } return KP_SUCCESS; } kp_error_t kp_agent_safe_create(struct kp_agent *agent, struct kp_agent_safe **_safe) { struct kp_agent_safe *safe; char **password; char **metadata; if ((safe = malloc(sizeof(struct kp_agent_safe))) == NULL) { errno = ENOMEM; return KP_ERRNO; } /* Store is the only able to set password and metadata memory */ password = (char **)&safe->password; metadata = (char **)&safe->metadata; *password = sodium_malloc(KP_PASSWORD_MAX_LEN); *metadata = sodium_malloc(KP_METADATA_MAX_LEN); *_safe = safe; return KP_SUCCESS; } kp_error_t kp_agent_safe_free(struct kp_agent *agent, struct kp_agent_safe *safe) { assert(safe); sodium_free(safe->password); sodium_free(safe->metadata); return KP_SUCCESS; } kp_error_t kp_agent_store(struct kp_agent *agent, struct kp_agent_safe *safe) { struct kp_store *store, *existing; if ((store = malloc(sizeof(struct kp_store))) == NULL) { errno = ENOMEM; return KP_ERRNO; } store->safe = safe; existing = RB_INSERT(storage, &storage, store); if (existing != NULL) { kp_agent_safe_free(agent, existing->safe); existing->safe = safe; } return KP_SUCCESS; } kp_error_t kp_agent_discard(struct kp_agent *agent, char *path) { struct kp_store needle, *store; struct kp_agent_safe safe; if (strlcpy(safe.path, path, PATH_MAX) > PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } needle.safe = &safe; store = RB_FIND(storage, &storage, &needle); if (store == NULL) { errno = ENOENT; return KP_ERRNO; } kp_agent_safe_free(agent, store->safe); RB_REMOVE(storage, &storage, store); return KP_SUCCESS; } kp_error_t kp_agent_search(struct kp_agent *agent, char *path, struct kp_agent_safe **_safe) { struct kp_store needle, *store; struct kp_agent_safe safe; if (strlcpy(safe.path, path, PATH_MAX) > PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } needle.safe = &safe; store = RB_FIND(storage, &storage, &needle); if (store == NULL) { *_safe = NULL; errno = ENOENT; return KP_ERRNO; } *_safe = store->safe; return KP_SUCCESS; } static int store_cmp(struct kp_store *a, struct kp_store *b) { return strncmp(a->safe->path, b->safe->path, PATH_MAX); } RB_GENERATE_STATIC(storage, kp_store, tree, store_cmp); kickpass-0.2.0/lib/password.c000066400000000000000000000026301310455076600161050ustar00rootroot00000000000000 /* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "kickpass.h" #include "password.h" char charset[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" "{}()[].:,;!?<>#$&'@+-/*=%^_|~\\"; /** * Generate a truly random password. * * password must be pre-allocated with at least len+1 bytes * len specify the password len without the terminating null byte * * Always return a null terminated password. */ kp_error_t kp_password_generate(char *password, size_t len) { int i; for (i = 0; i < len; i++) { password[i] = charset[arc4random_uniform(sizeof(charset) - 1)]; } password[len] = '\0'; return KP_SUCCESS; } kickpass-0.2.0/lib/safe.c000066400000000000000000000243511310455076600151650ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "kickpass.h" #include "editor.h" #include "safe.h" #include "storage.h" #include "kpagent.h" static kp_error_t kp_safe_init(struct kp_safe *, const char *, bool, bool); /* * Load an existing safe. * Init the kp_safe structure and open the cipher file. * The returned safe is closed. */ kp_error_t kp_safe_load(struct kp_ctx *ctx, struct kp_safe *safe, const char *name) { kp_error_t ret; struct stat stats; char path[PATH_MAX] = ""; assert(ctx); assert(safe); assert(name); if ((ret = kp_safe_init(safe, name, false, false)) != KP_SUCCESS) { return ret; } if ((ret = kp_safe_get_path(ctx, safe, path, PATH_MAX)) != KP_SUCCESS) { return ret; } if (stat(path, &stats) != 0) { return KP_ERRNO; } safe->cipher = open(path, O_RDWR | O_NONBLOCK); if (safe->cipher < 0) { return KP_ERRNO; } return KP_SUCCESS; } static kp_error_t kp_safe_mkdir(struct kp_ctx *ctx, char *path) { struct stat stats; char *rdir; assert(ctx); assert(path); rdir = path + strlen(ctx->ws_path); rdir++; /* Skip first / as it is part of the ctx->ws */ while ((rdir = strchr(rdir, '/'))) { *rdir = '\0'; if (stat(path, &stats) != 0) { if (errno == ENOENT) { if (mkdir(path, 0700) < 0) { return errno; } } else { return errno; } } *rdir = '/'; rdir++; } return KP_SUCCESS; } /* * Create a new safe. * The returned safe is opened. */ kp_error_t kp_safe_create(struct kp_ctx *ctx, struct kp_safe *safe, const char *name) { kp_error_t ret; struct stat stats; char path[PATH_MAX] = ""; assert(ctx); assert(safe); assert(name); if ((ret = kp_safe_init(safe, name, true, false)) != KP_SUCCESS) { return ret; } if ((ret = kp_safe_get_path(ctx, safe, path, PATH_MAX)) != KP_SUCCESS) { return ret; } if ((ret = kp_safe_mkdir(ctx, path)) != KP_SUCCESS) { return ret; } if (stat(path, &stats) == 0) { errno = EEXIST; return KP_ERRNO; } else if (errno != ENOENT) { return errno; } safe->cipher = open(path, O_RDWR | O_NONBLOCK | O_CREAT, S_IRUSR | S_IWUSR); if (safe->cipher < 0) { return errno; } return KP_SUCCESS; } /* * Delete a safe */ kp_error_t kp_safe_delete(struct kp_ctx *ctx, struct kp_safe *safe) { kp_error_t ret; char path[PATH_MAX] = ""; assert(ctx); assert(safe); assert(safe->open == true); if ((ret = kp_safe_get_path(ctx, safe, path, PATH_MAX)) != KP_SUCCESS) { return ret; } if (ctx->agent.connected) { bool result; if ((ret = kp_agent_send(&ctx->agent, KP_MSG_DISCARD, path, PATH_MAX)) != KP_SUCCESS) { /* TODO log reason in verbose mode */ return ret; } if ((ret = kp_agent_receive(&ctx->agent, KP_MSG_DISCARD, &result, sizeof(bool))) != KP_SUCCESS) { /* TODO log reason in verbose mode */ return ret; } } if (unlink(path) != 0) { ret = KP_ERRNO; return ret; } return KP_SUCCESS; } /* * Open a safe. */ kp_error_t kp_safe_open(struct kp_ctx *ctx, struct kp_safe *safe, bool force) { assert(ctx); assert(safe); /* handle not connected or not found and ask password */ if (!force && ctx->agent.connected) { kp_error_t ret; struct kp_unsafe unsafe = KP_UNSAFE_INIT; char path[PATH_MAX] = ""; if ((ret = kp_safe_get_path(ctx, safe, path, PATH_MAX)) != KP_SUCCESS) { return ret; } if ((ret = kp_agent_send(&ctx->agent, KP_MSG_SEARCH, path, PATH_MAX)) != KP_SUCCESS) { /* TODO log reason in verbose mode */ goto fallback; } if ((ret = kp_agent_receive(&ctx->agent, KP_MSG_SEARCH, &unsafe, sizeof(struct kp_unsafe))) != KP_SUCCESS) { /* TODO log reason in verbose mode */ goto fallback; } if (strlcpy(safe->password, unsafe.password, KP_PASSWORD_MAX_LEN) >= KP_PASSWORD_MAX_LEN) { errno = ENOMEM; return KP_ERRNO; } if (strlcpy(safe->metadata, unsafe.metadata, KP_METADATA_MAX_LEN) >= KP_METADATA_MAX_LEN) { errno = ENOMEM; return KP_ERRNO; } safe->open = true; return KP_SUCCESS; } fallback: if (ctx->password[0] == '\0') { kp_error_t ret; if ((ret = ctx->password_prompt(ctx, false, (char *)ctx->password, "master")) != KP_SUCCESS) { return ret; } } return kp_storage_open(ctx, safe); } /* * Save a safe. */ kp_error_t kp_safe_save(struct kp_ctx *ctx, struct kp_safe *safe) { assert(ctx); assert(safe); /* XXX is it still required to test master password ? */ if (ctx->password[0] == '\0') { kp_error_t ret; if ((ret = ctx->password_prompt(ctx, false, (char *)ctx->password, "master")) != KP_SUCCESS) { return ret; } } if (ctx->agent.connected) { kp_error_t ret; struct kp_unsafe unsafe = KP_UNSAFE_INIT; char path[PATH_MAX] = ""; if ((ret = kp_safe_get_path(ctx, safe, path, PATH_MAX)) != KP_SUCCESS) { return ret; } if ((ret = kp_agent_send(&ctx->agent, KP_MSG_SEARCH, path, PATH_MAX)) != KP_SUCCESS) { /* TODO log reason in verbose mode */ goto finally; } if ((ret = kp_agent_receive(&ctx->agent, KP_MSG_SEARCH, &unsafe, sizeof(struct kp_unsafe))) != KP_SUCCESS) { if (ret != KP_ERRNO || errno != ENOENT) { /* TODO log reason in verbose mode */ } goto finally; } if (strlcpy(unsafe.password, safe->password, KP_PASSWORD_MAX_LEN) >= KP_PASSWORD_MAX_LEN) { errno = ENOMEM; goto finally; } if (strlcpy(unsafe.metadata, safe->metadata, KP_METADATA_MAX_LEN) >= KP_METADATA_MAX_LEN) { errno = ENOMEM; goto finally; } if ((ret = kp_agent_send(&ctx->agent, KP_MSG_STORE, &unsafe, sizeof(struct kp_unsafe))) != KP_SUCCESS) { /* TODO log reason in verbose mode */ goto finally; } } finally: return kp_storage_save(ctx, safe); } /* * Close a safe. * Take care of cleaning the safe plain text and closing the opened file * descriptor. */ kp_error_t kp_safe_close(struct kp_ctx *ctx, struct kp_safe *safe) { assert(ctx); assert(safe); sodium_free((char *)safe->password); sodium_free((char *)safe->metadata); if (close(safe->cipher) < 0) { return errno; } safe->cipher = 0; return KP_SUCCESS; } static kp_error_t kp_safe_init(struct kp_safe *safe, const char *name, bool open, bool ro) { char **password; char **metadata; assert(safe); assert(name); /* Init is the only able to set password and metadata memory */ password = (char **)&safe->password; metadata = (char **)&safe->metadata; if (strlcpy(safe->name, name, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } safe->open = open; *password = sodium_malloc(KP_PASSWORD_MAX_LEN); safe->password[0] = '\0'; *metadata = sodium_malloc(KP_METADATA_MAX_LEN); safe->metadata[0] = '\0'; return KP_SUCCESS; } kp_error_t kp_safe_rename(struct kp_ctx *ctx, struct kp_safe *safe, const char *name) { kp_error_t ret; char oldpath[PATH_MAX] = "", newpath[PATH_MAX] = ""; assert(ctx); assert(safe); assert(name); if ((ret = kp_safe_get_path(ctx, safe, oldpath, PATH_MAX)) != KP_SUCCESS) { return ret; } if (strlcpy(safe->name, name, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if ((ret = kp_safe_get_path(ctx, safe, newpath, PATH_MAX)) != KP_SUCCESS) { return ret; } if (ctx->agent.connected) { struct kp_unsafe unsafe = KP_UNSAFE_INIT; bool result; if ((ret = kp_agent_send(&ctx->agent, KP_MSG_DISCARD, oldpath, PATH_MAX)) != KP_SUCCESS) { /* TODO log reason in verbose mode */ goto finally; } if ((ret = kp_agent_receive(&ctx->agent, KP_MSG_DISCARD, &result, sizeof(bool))) != KP_SUCCESS) { if (ret != KP_ERRNO || errno != ENOENT) { /* TODO log reason in verbose mode */ } goto finally; } if (strlcpy(unsafe.path, newpath, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; goto finally; } if (strlcpy(unsafe.password, safe->password, KP_PASSWORD_MAX_LEN) >= KP_PASSWORD_MAX_LEN) { errno = ENOMEM; goto finally; } if (strlcpy(unsafe.metadata, safe->metadata, KP_METADATA_MAX_LEN) >= KP_METADATA_MAX_LEN) { errno = ENOMEM; goto finally; } if ((ret = kp_agent_send(&ctx->agent, KP_MSG_STORE, &unsafe, sizeof(struct kp_unsafe))) != KP_SUCCESS) { /* TODO log reason in verbose mode */ goto finally; } } finally: if ((ret = kp_safe_mkdir(ctx, newpath)) != KP_SUCCESS) { return ret; } if (rename(oldpath, newpath) != 0) { return errno; } return KP_SUCCESS; } kp_error_t kp_safe_get_path(struct kp_ctx *ctx, struct kp_safe *safe, char *path, size_t size) { assert(ctx); assert(safe); assert(path); if (strlcpy(path, ctx->ws_path, size) >= size) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(path, "/", size) >= size) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(path, safe->name, size) >= size) { errno = ENOMEM; return KP_ERRNO; } return KP_SUCCESS; } kp_error_t kp_safe_store(struct kp_ctx *ctx, struct kp_safe *safe, int timeout) { kp_error_t ret; struct kp_unsafe unsafe = KP_UNSAFE_INIT; if (!ctx->agent.connected) { ret = KP_EINPUT; return ret; } unsafe.timeout = timeout; if ((ret = kp_safe_get_path(ctx, safe, unsafe.path, PATH_MAX)) != KP_SUCCESS) { return ret; } if (strlcpy(unsafe.password, safe->password, KP_PASSWORD_MAX_LEN) >= KP_PASSWORD_MAX_LEN) { errno = ENOMEM; return KP_ERRNO; } if (strlcpy(unsafe.metadata, safe->metadata, KP_METADATA_MAX_LEN) >= KP_METADATA_MAX_LEN) { errno = ENOMEM; return KP_ERRNO; } kp_agent_send(&ctx->agent, KP_MSG_STORE, &unsafe, sizeof(struct kp_unsafe)); return KP_SUCCESS; } kickpass-0.2.0/lib/storage.c000066400000000000000000000217211310455076600157110ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #ifndef __linux__ #include #else #include #endif #include #include #include #include #include #include #include #include "kickpass.h" #include "safe.h" #include "storage.h" static uint16_t kp_storage_version = 0x0001; #ifndef betoh16 #define betoh16 be16toh #endif #ifndef betoh64 #define betoh64 be64toh #endif #define KP_STORAGE_SALT_SIZE crypto_pwhash_scryptsalsa208sha256_SALTBYTES #define KP_STORAGE_NONCE_SIZE crypto_aead_chacha20poly1305_NPUBBYTES #define KP_STORAGE_HEADER_SIZE (2+2+8+8+KP_STORAGE_SALT_SIZE+KP_STORAGE_NONCE_SIZE) struct kp_storage_header { uint16_t version; uint16_t sodium_version; uint64_t opslimit; uint64_t memlimit; unsigned char salt[KP_STORAGE_SALT_SIZE]; unsigned char nonce[KP_STORAGE_NONCE_SIZE]; }; #define KP_STORAGE_HEADER_INIT { 0, 0, 0, 0, { 0 }, { 0 } } static void kp_storage_header_pack(const struct kp_storage_header *, unsigned char *); static void kp_storage_header_unpack(struct kp_storage_header *, const unsigned char *); static kp_error_t kp_storage_encrypt(struct kp_ctx *, struct kp_storage_header *, const unsigned char *, unsigned long long, const unsigned char *, unsigned long long, unsigned char *, unsigned long long *); static kp_error_t kp_storage_decrypt(struct kp_ctx *, struct kp_storage_header *, const unsigned char *, unsigned long long, unsigned char *, unsigned long long *, const unsigned char *, unsigned long long); #define READ_HEADER(s, packed, field) do {\ memcpy(&(field), (packed), (s)/8);\ (field) = betoh##s(field);\ (packed) = (packed) + (s)/8;\ } while(0) #define WRITE_HEADER(s, packed, field) do {\ memcpy((packed), &(field), (s)/8);\ *(uint##s##_t *)(packed) = htobe##s(*(uint##s##_t *)(packed));\ (packed) = (packed) + (s)/8;\ } while(0) static void kp_storage_header_pack(const struct kp_storage_header *header, unsigned char *packed) { assert(header); assert(packed); WRITE_HEADER(16, packed, header->version); WRITE_HEADER(16, packed, header->sodium_version); WRITE_HEADER(64, packed, header->opslimit); WRITE_HEADER(64, packed, header->memlimit); memcpy(packed, header->salt, KP_STORAGE_SALT_SIZE); packed = packed + KP_STORAGE_SALT_SIZE; memcpy(packed, header->nonce, KP_STORAGE_NONCE_SIZE); } static void kp_storage_header_unpack(struct kp_storage_header *header, const unsigned char *packed) { assert(header); assert(packed); READ_HEADER(16, packed, header->version); READ_HEADER(16, packed, header->sodium_version); READ_HEADER(64, packed, header->opslimit); READ_HEADER(64, packed, header->memlimit); memcpy(header->salt, packed, KP_STORAGE_SALT_SIZE); packed = packed + KP_STORAGE_SALT_SIZE; memcpy(header->nonce, packed, KP_STORAGE_NONCE_SIZE); } static kp_error_t kp_storage_encrypt(struct kp_ctx *ctx, struct kp_storage_header *header, const unsigned char *packed_header, unsigned long long header_size, const unsigned char *plain, unsigned long long plain_size, unsigned char *cipher, unsigned long long *cipher_size) { unsigned char key[crypto_aead_chacha20poly1305_KEYBYTES]; if (crypto_pwhash_scryptsalsa208sha256(key, crypto_aead_chacha20poly1305_KEYBYTES, ctx->password, strlen(ctx->password), header->salt, header->opslimit, header->memlimit) != 0) { errno = ENOMEM; return KP_ERRNO; } if (crypto_aead_chacha20poly1305_encrypt(cipher, cipher_size, plain, plain_size, packed_header, header_size, NULL, header->nonce, key) != 0) { return KP_EENCRYPT; } return KP_SUCCESS; } static kp_error_t kp_storage_decrypt(struct kp_ctx *ctx, struct kp_storage_header *header, const unsigned char *packed_header, unsigned long long header_size, unsigned char *plain, unsigned long long *plain_size, const unsigned char *cipher, unsigned long long cipher_size) { unsigned char key[crypto_aead_chacha20poly1305_KEYBYTES]; if (crypto_pwhash_scryptsalsa208sha256(key, crypto_aead_chacha20poly1305_KEYBYTES, ctx->password, strlen(ctx->password), header->salt, header->opslimit, header->memlimit) != 0) { errno = ENOMEM; return KP_ERRNO; } if (crypto_aead_chacha20poly1305_decrypt(plain, plain_size, NULL, cipher, cipher_size, packed_header, header_size, header->nonce, key) != 0) { return KP_EDECRYPT; } return KP_SUCCESS; } kp_error_t kp_storage_save(struct kp_ctx *ctx, struct kp_safe *safe) { kp_error_t ret = KP_SUCCESS; unsigned char *cipher = NULL, *plain = NULL; unsigned long long cipher_size, plain_size; struct kp_storage_header header = KP_STORAGE_HEADER_INIT; unsigned char packed_header[KP_STORAGE_HEADER_SIZE]; char path[PATH_MAX]; size_t password_len, metadata_len; assert(ctx); assert(safe); assert(safe->open == true); password_len = strlen(safe->password); assert(password_len < KP_PASSWORD_MAX_LEN); metadata_len = strlen(safe->metadata); assert(metadata_len < KP_METADATA_MAX_LEN); /* Ensure we are at the beginning of the safe */ if (lseek(safe->cipher, 0, SEEK_SET) != 0) { return errno; } if (ftruncate(safe->cipher, 0) != 0) { return errno; } if ((ret = kp_safe_get_path(ctx, safe, path, PATH_MAX)) != KP_SUCCESS) { return ret; } /* construct full plain */ /* plain is password + '\0' + metadata + '\0' */ plain_size = password_len + metadata_len + 2; plain = sodium_malloc(plain_size); strncpy((char *)plain, (char *)safe->password, password_len); plain[password_len] = '\0'; strncpy((char *)&plain[password_len+1], (char *)safe->metadata, metadata_len); plain[plain_size-1] = '\0'; /* alloc cipher to max size */ cipher = malloc(plain_size+crypto_aead_chacha20poly1305_ABYTES); if (!cipher) { ret = ENOMEM; goto out; } header.version = kp_storage_version; header.sodium_version = SODIUM_LIBRARY_VERSION_MAJOR << 8 | SODIUM_LIBRARY_VERSION_MINOR; header.opslimit = ctx->cfg.opslimit; header.memlimit = ctx->cfg.memlimit; randombytes_buf(header.salt, KP_STORAGE_SALT_SIZE); randombytes_buf(header.nonce, KP_STORAGE_NONCE_SIZE); kp_storage_header_pack(&header, packed_header); if ((ret = kp_storage_encrypt(ctx, &header, packed_header, KP_STORAGE_HEADER_SIZE, plain, plain_size, cipher, &cipher_size)) != KP_SUCCESS) { goto out; } if (write(safe->cipher, packed_header, KP_STORAGE_HEADER_SIZE) < KP_STORAGE_HEADER_SIZE) { ret = errno; goto out; } if (write(safe->cipher, cipher, cipher_size) < cipher_size) { ret = errno; goto out; } out: sodium_free(plain); free(cipher); return ret; } kp_error_t kp_storage_open(struct kp_ctx *ctx, struct kp_safe *safe) { kp_error_t ret = KP_SUCCESS; unsigned char *cipher = NULL, *plain = NULL; unsigned long long cipher_size, plain_size; struct kp_storage_header header = KP_STORAGE_HEADER_INIT; unsigned char packed_header[KP_STORAGE_HEADER_SIZE]; size_t password_len; assert(ctx); assert(safe); assert(safe->open == false); errno = 0; if (read(safe->cipher, packed_header, KP_STORAGE_HEADER_SIZE) != KP_STORAGE_HEADER_SIZE) { if (errno != 0) { ret = errno; } else { ret = KP_INVALID_STORAGE; } goto out; } kp_storage_header_unpack(&header, packed_header); /* alloc plain to max size */ plain = sodium_malloc(KP_PLAIN_MAX_SIZE); /* alloc cipher to max size */ cipher = malloc(KP_PLAIN_MAX_SIZE+crypto_aead_chacha20poly1305_ABYTES); cipher_size = read(safe->cipher, cipher, KP_PLAIN_MAX_SIZE+crypto_aead_chacha20poly1305_ABYTES); if (cipher_size <= crypto_aead_chacha20poly1305_ABYTES) { ret = KP_INVALID_STORAGE; goto out; } if (cipher_size - crypto_aead_chacha20poly1305_ABYTES > KP_PLAIN_MAX_SIZE) { ret = ENOMEM; goto out; } if ((ret = kp_storage_decrypt(ctx, &header, packed_header, KP_STORAGE_HEADER_SIZE, plain, &plain_size, cipher, cipher_size)) != KP_SUCCESS) { goto out; } strncpy((char *)safe->password, (char *)plain, KP_PASSWORD_MAX_LEN); /* ensure null termination */ safe->password[KP_PASSWORD_MAX_LEN-1] = '\0'; password_len = strlen((char *)safe->password); strncpy((char *)safe->metadata, (char *)&plain[password_len+1], KP_METADATA_MAX_LEN); /* ensure null termination */ safe->metadata[KP_METADATA_MAX_LEN-1] = '\0'; safe->open = true; out: sodium_free(plain); free(cipher); return ret; } kickpass-0.2.0/manual/000077500000000000000000000000001310455076600146055ustar00rootroot00000000000000kickpass-0.2.0/manual/kickpass.1000066400000000000000000000117561310455076600165110ustar00rootroot00000000000000.\" .\" Copyright (c) 2015 Paul Fariello .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .Dd May 5, 2017 .Dt KICKPASS 1 .Os .Sh NAME .Nm kickpass .Nd a stupid simple password safe .Sh SYNOPSIS .Nm .Oo Fl hv Oc .Nm .Cm help Oo Ar command Oc .Nm .Cm init Oo Ar sub Oc .Nm .Cm cat Oo Fl p Oc Ar safe .Nm .Cm create Oo Fl gl Ar len Oc Ar safe .Nm .Cm open Oo Fl t Oc Ar safe .Nm .Cm edit Oo Fl pmgl Oc Ar safe .Nm .Cm copy Ar safe .Nm .Cm list Oo Ar path Oc .Nm .Cm delete Ar safe .Nm .Cm agent Oo Fl d Oc Oo Ar command Oo Ar arg ... Oc Oc .Sh DESCRIPTION .Nm is a stupid simple password safe. It keep each password in a specific safe, protected with modern cryptography. Command line interface is splited into different command. Each is described in the following subsections. .Ss WORKSPACE All safes are stored in kickpass workspace. Default workspace is .Pa $HOME/.kickpass/ \&. .Ss SAFE NAMING Safe name can contains any character allowed by the file system containing the kickpass workspace. If safe name contains .Sq / then corresponding directories will be created under kickpass workspace. For example the following command .Bd -literal -offset indent kickpass create www/my-personnal-website .Ed .Pp will create a safe in .Pa $HOME/.kickpass/www/my-personnal-website \&. While following command .Bd -literal -offset indent kickpass list www .Ed .Pp will list all safe under the directory .Pa $HOME/.kickpass/www/ \&. .Ss Nm Oo Fl hv Oc Don't do anything .Bl -tag -width flag .It Fl v Fl -version Print .Nm version .It Fl h Fl -help Print .Nm help .El .Ss Nm Cm help Oo Ar command Oc Print general help or command help. .Ss Nm Cm init Oo Ar sub Oc Initialize a .Nm workspace or a sub-workspace. .Ss Nm Cm cat Oo Fl p Oc Ar safe Open .Ar safe and print .Ar safe metadata to stdout. .Bl -tag -width flag .It Fl p Fl -password Print the password to stdout .El .Ss Nm Cm create Oo Fl gl Oc Ar safe Create a new password safe. .Bl -tag -width flag .It Fl g Fl -generate Create a new safe with a generated random password .It Fl l Fl -length Ar len Generate a random password of .Ar len length .El .Ss Nm Cm open Oo Fl t Oc Ar safe Open .Ar safe and load it in .Nm agent .Bl -tag -width flag .It Fl t Fl -timeout Sets the lifetime of the opened safe in the agent. Default in seconds (3600s). .El .Ss Nm Cm edit Oo Fl pmgl Oc Ar safe Prompt for a new password and edit metadata from .Ar safe using .Ev EDITOR environment variable .Bl -tag -width flag .It Fl p Fl -password Edit only password .It Fl m Fl -metadata Edit only metadata .It Fl g Fl -generate Create a new safe with a generated random password .It Fl l Fl -length Ar len Generate a random password of .Ar len length .El .Ss Nm Cm copy Ar safe Copy .Ar safe password into X primary and secondary clipboards. Password can be pasted only once. .Ss Nm Cm list Oo Ar path Oc List available safes starting from .Ar path relatively to .Nm workspace or from root of .Nm workspace if .Ar path is not given. .Ss Nm Cm delete Oo Fl f Oc Ar safe Delete .Ar safe \&. .Ss Nm Cm agent Oo Fl d Oc Oo Ar command Oo arg ... Oc Oc Start a .Nm agent that will store your opened safe. Agent can be used by exporting .Ev KP_AGENT_SOCK environment variable. Optionnaly starts .Ar command with the correct environment set. .Bl -tag -width flag .It Fl d Fl -version Do not daemonize agent. .El .Sh ENVIRONMENT The following variables are used by kickpass: .Bl -tag -width BLOCKSIZE .It Ev HOME The user's login directory. Used to compute .Nm workspace path. .It Ev EDITOR The user's preferred utility to edit text files. Used to edit safe. .It Ev KP_AGENT_SOCK Socket used to communicate with .Nm agent. Path to socket is printed to stdout when at agent startup. .El .Sh FILES The following files and directories are used by kickpass: .Bl -tag -width BLOCKSIZE .It Pa $HOME/.kickpass/ The .Nm working directory. .El .Sh EXAMPLES Initialize a .Nm workspace. .Bd -literal -offset indent kickpass init .Ed Create a new safe with a random password. .Bd -literal -offset indent kickpass create -g www/my_safe .Ed Display safe content on stdout. .Bd -literal -offset indent kickpass cat www/my_safe .Ed Edit a safe. .Bd -literal -offset indent kickpass edit www/my_safe .Ed Copy password contained in safe into X clipboards. .Bd -literal -offset indent kickpass copy www/my_safe .Ed .Sh AUTHORS .Nm is written by .An Paul Fariello Aq Mt paul@fariello.eu \&. kickpass-0.2.0/requirements.txt000066400000000000000000000000101310455076600166030ustar00rootroot00000000000000pexpect kickpass-0.2.0/src/000077500000000000000000000000001310455076600141175ustar00rootroot00000000000000kickpass-0.2.0/src/command.h000066400000000000000000000020261310455076600157060ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_COMMAND_H #define KP_COMMAND_H #include #include "kickpass.h" struct kp_cmd { kp_error_t (*main)(struct kp_ctx *, int, char **); void (*usage)(void); char *opts; char *desc; }; #endif /* KP_COMMAND_H */ kickpass-0.2.0/src/command/000077500000000000000000000000001310455076600155355ustar00rootroot00000000000000kickpass-0.2.0/src/command/agent.c000066400000000000000000000257301310455076600170060ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kickpass.h" #include "log.h" #include "command.h" #include "kpagent.h" #ifndef EPROTO #define EPROTO ENOPROTOOPT #endif #define TMP_TEMPLATE "/tmp/kickpass-XXXXXX" struct agent { struct event_base *evb; struct kp_agent kp_agent; }; struct conn { struct event *ev; struct agent agent; struct imsgbuf ibuf; }; struct timeout { struct agent *agent; char path[PATH_MAX]; }; static kp_error_t agent(struct kp_ctx *, int, char **); static void agent_accept(evutil_socket_t, short, void *); static void agent_kill(evutil_socket_t, short, void *); static void timeout_discard(evutil_socket_t, short, void *); static void dispatch(evutil_socket_t, short, void *); static kp_error_t parse_opt(struct kp_ctx *, int, char **); static kp_error_t store(struct agent *, struct kp_unsafe *); static kp_error_t search(struct agent *, char *); static kp_error_t discard(struct agent *, char *); static void usage(void); struct kp_cmd kp_cmd_agent = { .main = agent, .usage = usage, .opts = "agent [-d] [command [arg ...]]", .desc = "Run a kickpass agent in background", }; static bool daemonize = true; static void agent_accept(evutil_socket_t fd, short events, void *_agent) { struct agent *agent = _agent; struct conn *conn; if ((conn = malloc(sizeof(struct conn))) == NULL) { errno = ENOMEM; kp_warn(KP_ERRNO, "cannot accept client"); return; } if ((kp_agent_accept(&agent->kp_agent, &conn->agent.kp_agent)) != KP_SUCCESS) { kp_warn(KP_ERRNO, "cannot accept client"); return; } conn->agent.evb = agent->evb; imsg_init(&conn->ibuf, conn->agent.kp_agent.sock); conn->ev = event_new(agent->evb, conn->agent.kp_agent.sock, EV_READ | EV_PERSIST, dispatch, conn); event_add(conn->ev, NULL); } static void agent_kill(evutil_socket_t fd, short events, void *_agent) { struct agent *agent = _agent; event_base_loopexit(agent->evb, NULL); } static void dispatch(evutil_socket_t fd, short events, void *_conn) { struct imsg imsg; struct conn *conn = _conn; if (imsg_read(&conn->ibuf) <= 0) { imsg_clear(&conn->ibuf); event_del(conn->ev); free(conn); return; } while (imsg_get(&conn->ibuf, &imsg) > 0) { size_t data_size; data_size = imsg.hdr.len - IMSG_HEADER_SIZE; switch (imsg.hdr.type) { case KP_MSG_STORE: if (data_size != sizeof(struct kp_unsafe)) { errno = EPROTO; kp_warn(KP_ERRNO, "invalid message"); break; } store(&conn->agent, (struct kp_unsafe *)imsg.data); /* XXX handle error */ break; case KP_MSG_SEARCH: if (data_size != PATH_MAX) { errno = EPROTO; kp_warn(KP_ERRNO, "invalid message"); break; } /* ensure null termination */ ((char *)imsg.data)[PATH_MAX-1] = '\0'; search(&conn->agent, (char *)imsg.data); break; case KP_MSG_DISCARD: if (data_size != PATH_MAX) { errno = EPROTO; kp_warn(KP_ERRNO, "invalid message"); break; } /* ensure null termination */ ((char *)imsg.data)[PATH_MAX-1] = '\0'; discard(&conn->agent, (char *)imsg.data); break; } imsg_free(&imsg); } } kp_error_t agent(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret = KP_SUCCESS; pid_t agent_pid = 0, child_pid = 0, parent_pid = 0; char socket_dir[PATH_MAX]; char socket_path[PATH_MAX]; struct agent agent; struct event *ev; struct stat sb; if ((ret = parse_opt(ctx, argc, argv)) != KP_SUCCESS) { return ret; } if (daemonize) { parent_pid = getpid(); if ((agent_pid = fork()) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot daemonize"); return ret; } if (agent_pid != 0) { sigset_t set; int sig, wstatus; if (sigemptyset(&set) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot wait for child"); return ret; } if (sigaddset(&set, SIGCONT) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot wait for child"); return ret; } if (sigaddset(&set, SIGCHLD) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot wait for child"); return ret; } sigprocmask(SIG_BLOCK, &set, NULL); sigwait(&set, &sig); if (sig == SIGCHLD) { waitpid(agent_pid, &wstatus, 0); exit(wstatus); } exit(KP_SUCCESS); } } agent_pid = getpid(); if (strlcpy(socket_dir, TMP_TEMPLATE, PATH_MAX) >= PATH_MAX) { errno = ENAMETOOLONG; ret = KP_ERRNO; kp_warn(ret, "cannot create socket temp dir"); goto out; } if (mkdtemp(socket_dir) == NULL) { ret = KP_ERRNO; kp_warn(ret, "cannot create socket temp dir"); goto out; } if (snprintf(socket_path, PATH_MAX, "%s/agent.%ld", socket_dir, (long)agent_pid) >= PATH_MAX) { errno = ENAMETOOLONG; ret = KP_ERRNO; kp_warn(ret, "cannot create socket"); goto out; } if ((ret = kp_agent_init(&agent.kp_agent, socket_path)) != KP_SUCCESS) { kp_warn(ret, "cannot create socket"); goto out; } if ((ret = kp_agent_listen(&agent.kp_agent)) != KP_SUCCESS) { kp_warn(ret, "cannot create socket"); goto out; } if (setenv(KP_AGENT_SOCKET_ENV, socket_path, 1) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot set environment"); goto out; } fprintf(stdout, "%s=%s\n", KP_AGENT_SOCKET_ENV, socket_path); fflush(stdout); if (optind < argc) { child_pid = fork(); if (child_pid == 0) { if (execvp(argv[optind], argv + optind) < 0) { kp_warn(ret, "cannot exec child"); } } } if (daemonize) { int devnull; if (chdir("/") < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot chdir to /"); return ret; } if ((devnull = open("/dev/null", O_RDWR)) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot close standard inputs"); return ret; } dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); dup2(devnull, STDERR_FILENO); close(devnull); kill(parent_pid, SIGCONT); } agent.evb = event_base_new(); ev = event_new(agent.evb, agent.kp_agent.sock, EV_READ | EV_PERSIST, agent_accept, &agent); event_add(ev, NULL); if (child_pid != 0) { ev = event_new(agent.evb, SIGCHLD, EV_SIGNAL | EV_PERSIST, agent_kill, &agent); event_add(ev, NULL); } event_base_dispatch(agent.evb); out: event_base_free(agent.evb); kp_agent_close(&agent.kp_agent); if (stat(socket_path, &sb) == 0) { if (unlink(socket_path) != 0) { kp_warn(KP_ERRNO, "cannot delete agent socket %s", socket_path); } } if (stat(socket_dir, &sb) == 0) { if (rmdir(socket_dir) != 0) { kp_warn(KP_ERRNO, "cannot delete agent socket dir %s", socket_dir); } } return ret; } static kp_error_t store(struct agent *agent, struct kp_unsafe *unsafe) { kp_error_t ret; struct kp_agent *kp_agent = &agent->kp_agent; struct kp_agent_safe *safe; struct timeout *timeout; struct event *timer; struct timeval timeval; if ((ret = kp_agent_safe_create(kp_agent, &safe)) != KP_SUCCESS) { return ret; } if (strlcpy(safe->path, unsafe->path, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; ret = KP_ERRNO; goto out; } if (strlcpy(safe->password, unsafe->password, KP_PASSWORD_MAX_LEN) >= KP_PASSWORD_MAX_LEN) { errno = ENOMEM; ret = KP_ERRNO; goto out; } if (strlcpy(safe->metadata, unsafe->metadata, KP_METADATA_MAX_LEN) >= KP_METADATA_MAX_LEN) { errno = ENOMEM; ret = KP_ERRNO; goto out; } if (unsafe->timeout > 0) { if ((timeout = malloc(sizeof(struct timeout))) == NULL) { errno = ENOMEM; ret = KP_ERRNO; goto out; } timeout->agent = agent; if (strlcpy(timeout->path, unsafe->path, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; ret = KP_ERRNO; goto out; } timeval.tv_sec = unsafe->timeout; timeval.tv_usec = 0; timer = evtimer_new(agent->evb, timeout_discard, timeout); evtimer_add(timer, &timeval); } return kp_agent_store(kp_agent, safe); out: kp_agent_safe_free(kp_agent, safe); return ret; } static kp_error_t search(struct agent *agent, char *path) { kp_error_t ret; struct kp_agent *kp_agent = &agent->kp_agent; struct kp_agent_safe *safe; struct kp_unsafe unsafe = KP_UNSAFE_INIT; if ((ret = kp_agent_search(kp_agent, path, &safe)) != KP_SUCCESS) { errno = ENOENT; kp_agent_error(kp_agent, KP_ERRNO); return ret; } if (strlcpy(unsafe.path, safe->path, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; kp_agent_error(kp_agent, KP_ERRNO); return KP_ERRNO; } if (strlcpy(unsafe.password, safe->password, KP_PASSWORD_MAX_LEN) >= KP_PASSWORD_MAX_LEN) { errno = ENOMEM; kp_agent_error(kp_agent, KP_ERRNO); return KP_ERRNO; } if (strlcpy(unsafe.metadata, safe->metadata, KP_METADATA_MAX_LEN) >= KP_METADATA_MAX_LEN) { errno = ENOMEM; kp_agent_error(kp_agent, KP_ERRNO); return KP_ERRNO; } if ((ret = kp_agent_send(kp_agent, KP_MSG_SEARCH, &unsafe, sizeof(struct kp_unsafe))) != KP_SUCCESS) { kp_warn(ret, "cannot send response"); } return ret; } static kp_error_t discard(struct agent *agent, char *path) { kp_error_t ret; struct kp_agent *kp_agent = &agent->kp_agent; bool result; if ((ret = kp_agent_discard(kp_agent, path)) != KP_SUCCESS) { kp_agent_error(kp_agent, ret); return ret; } result = true; if ((ret = kp_agent_send(kp_agent, KP_MSG_DISCARD, &result, sizeof(bool))) != KP_SUCCESS) { kp_warn(ret, "cannot send response"); } return ret; } static void timeout_discard(evutil_socket_t fd, short events, void *_timeout) { struct timeout *timeout = _timeout; struct kp_agent *kp_agent = &timeout->agent->kp_agent; kp_agent_discard(kp_agent, timeout->path); free(timeout); } static kp_error_t parse_opt(struct kp_ctx *ctx, int argc, char **argv) { int opt; kp_error_t ret = KP_SUCCESS; static struct option longopts[] = { { "no-daemon", no_argument, NULL, 'd' }, { NULL, 0, NULL, 0 }, }; while ((opt = getopt_long(argc, argv, "d", longopts, NULL)) != -1) { switch (opt) { case 'd': daemonize = false; break; default: ret = KP_EINPUT; kp_warn(ret, "unknown option %c", opt); } } return ret; } void usage(void) { printf("options:\n"); printf(" -d, --no-daemon Do not daemonize\n"); } kickpass-0.2.0/src/command/agent.h000066400000000000000000000016211310455076600170040ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_AGENT_H #define KP_AGENT_H #include "command.h" extern struct kp_cmd kp_cmd_agent; #endif /* KP_AGENT_H */ kickpass-0.2.0/src/command/cat.c000066400000000000000000000061261310455076600164550ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "kickpass.h" #include "command.h" #include "cat.h" #include "editor.h" #include "prompt.h" #include "safe.h" #include "log.h" static kp_error_t cat(struct kp_ctx *ctx, int argc, char **argv); static kp_error_t parse_opt(struct kp_ctx *, int, char **); static void usage(void); struct kp_cmd kp_cmd_cat = { .main = cat, .usage = usage, .opts = "cat [-pm] ", .desc = "Open a password safe and print its content on stdout", }; static bool password = false; static bool metadata = false; kp_error_t cat(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret; struct kp_safe safe; if ((ret = parse_opt(ctx, argc, argv)) != KP_SUCCESS) { return ret; } if (argc - optind != 1) { ret = KP_EINPUT; kp_warn(ret, "missing safe name"); return ret; } if ((ret = kp_safe_load(ctx, &safe, argv[optind])) != KP_SUCCESS) { kp_warn(ret, "cannot load safe"); return ret; } if ((ret = kp_safe_open(ctx, &safe, false)) != KP_SUCCESS) { kp_warn(ret, "cannot open safe"); return ret; } if (password) { printf("%s\n", safe.password); } if (metadata) { printf("%s\n", safe.metadata); } if ((ret = kp_safe_close(ctx, &safe)) != KP_SUCCESS) { kp_warn(ret, "cannot cleanly close safe" "clear text password might have leaked"); return ret; } return KP_SUCCESS; } static kp_error_t parse_opt(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret = KP_SUCCESS; int opt; static struct option longopts[] = { { "password", no_argument, NULL, 'p' }, { NULL, 0, NULL, 0 }, }; while ((opt = getopt_long(argc, argv, "pm", longopts, NULL)) != -1) { switch (opt) { case 'p': password = true; break; case 'm': metadata = true; break; default: ret = KP_EINPUT; kp_warn(ret, "unknown option %c", opt); return ret; } } if (!password && metadata) { kp_warnx(KP_EINPUT, "Opening only metadata is default behavior." "You can ommit option."); } /* Default open only metadata */ if (!password && !metadata) { password = false; metadata = true; } return ret; } void usage(void) { printf("options:\n"); printf(" -p, --password Open password (This should be used very carefully)\n"); printf(" -m, --metadata Open metadata\n"); } kickpass-0.2.0/src/command/cat.h000066400000000000000000000016111310455076600164540ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_CAT_H #define KP_CAT_H #include "command.h" extern struct kp_cmd kp_cmd_cat; #endif /* KP_CAT_H */ kickpass-0.2.0/src/command/copy.c000066400000000000000000000101231310455076600166500ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include /* TODO remove */ #include #include "kickpass.h" #include "command.h" #include "copy.h" #include "prompt.h" #include "safe.h" #include "log.h" static kp_error_t copy(struct kp_ctx *ctx, int argc, char **argv); struct kp_cmd kp_cmd_copy = { .main = copy, .usage = NULL, .opts = "copy ", .desc = "Copy a password (first line of safe) into X clipboard", }; kp_error_t copy(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret; struct kp_safe safe; Display *display; Window window; Atom XA_CLIPBOARD, XA_COMPOUND_TEXT, XA_UTF8_STRING, XA_TARGETS; bool replied = false; size_t password_len; if (argc - optind != 1) { ret = KP_EINPUT; kp_warn(ret, "missing safe name"); return ret; } if ((ret = kp_safe_load(ctx, &safe, argv[optind])) != KP_SUCCESS) { kp_warn(ret, "cannot load safe"); return ret; } if ((ret = kp_safe_open(ctx, &safe, false)) != KP_SUCCESS) { kp_warn(ret, "cannot open safe"); goto out; } password_len = strlen(safe.password); display = XOpenDisplay(NULL); XA_CLIPBOARD = XInternAtom(display, "CLIPBOARD", True); XA_COMPOUND_TEXT = XInternAtom(display, "COMPOUND_TEXT", True); XA_UTF8_STRING = XInternAtom(display, "UTF8_STRING", True); XA_TARGETS = XInternAtom(display, "TARGETS", True); window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 1, 1, 0, 0, 0); XSelectInput(display, window, PropertyChangeMask); XSetSelectionOwner(display, XA_PRIMARY, window, CurrentTime); XSetSelectionOwner(display, XA_CLIPBOARD, window, CurrentTime); if (daemon(0, 0) != 0) { ret = KP_ERRNO; kp_warn(ret, "cannot daemonize"); goto out; } do { XEvent event; XSelectionRequestEvent *request; XSelectionEvent reply; XNextEvent(display, &event); if (event.type != SelectionRequest) { continue; } request = &event.xselectionrequest; reply.type = SelectionNotify; reply.send_event = True; reply.display = display; reply.requestor = request->requestor; reply.selection = request->selection; reply.property = request->property; reply.target = None; reply.time = request->time; if (request->target == XA_TARGETS) { Atom possibleTargets[] = { XA_STRING, XA_UTF8_STRING, XA_COMPOUND_TEXT }; XChangeProperty(display, request->requestor, request->property, XA_ATOM, 32, PropModeReplace, (unsigned char *) possibleTargets, 3); } else if (request->target == XA_STRING || request->target == XA_UTF8_STRING || request->target == XA_COMPOUND_TEXT) { XChangeProperty(display, request->requestor, request->property, request->target, 8, PropModeReplace, (unsigned char *)safe.password, password_len); replied = true; } else { kp_warn(KP_EINPUT, "don't know what to answer"); reply.property = None; replied = true; } XSendEvent(display, event.xselectionrequest.requestor, 0, 0, (XEvent *)&reply); XSync(display, False); } while (!replied); XCloseDisplay(display); out: if ((ret = kp_safe_close(ctx, &safe)) != KP_SUCCESS) { kp_warn(ret, "cannot cleanly close safe" "clear text password might have leaked"); return ret; } return KP_SUCCESS; } kickpass-0.2.0/src/command/copy.h000066400000000000000000000016151310455076600166630ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_COPY_H #define KP_COPY_H #include "command.h" extern struct kp_cmd kp_cmd_copy; #endif /* KP_COPY_H */ kickpass-0.2.0/src/command/create.c000066400000000000000000000105721310455076600171510ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "kickpass.h" #include "config.h" #include "command.h" #include "create.h" #include "editor.h" #include "password.h" #include "prompt.h" #include "safe.h" #include "log.h" #include "kpagent.h" static kp_error_t create(struct kp_ctx *, int, char **); static kp_error_t parse_opt(struct kp_ctx *, int, char **); static void usage(void); struct kp_cmd kp_cmd_create = { .main = create, .usage = usage, .opts = "create [-hgl] ", .desc = "Create a new password safe", }; static bool generate = false; static int password_len = 20; static int timeout = 3600; static bool open = false; kp_error_t create(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret; char cfg_path[PATH_MAX] = ""; struct kp_safe safe; char *password = NULL; if ((ret = parse_opt(ctx, argc, argv)) != KP_SUCCESS) { return ret; } if (argc - optind != 1) { ret = KP_EINPUT; kp_warn(ret, "missing safe name"); return ret; } if ((ret = kp_cfg_find(ctx, argv[optind], cfg_path, PATH_MAX)) != KP_SUCCESS) { kp_warn(ret, "cannot find workspace config"); return ret; } if ((ret = kp_cfg_load(ctx, cfg_path)) != KP_SUCCESS) { kp_warn(ret, "cannot load kickpass config"); return ret; } if (ctx->password[0] == '\0') { /* Ask for password, otherwise it is asked on kp_safe_save which seems * weird for user */ if ((ret = ctx->password_prompt(ctx, false, (char *)ctx->password, "master")) != KP_SUCCESS) { return ret; } } if ((ret = kp_safe_create(ctx, &safe, argv[optind])) != KP_SUCCESS) { if (ret == KP_ERRNO && errno == EEXIST) { kp_warn(ret, "use 'edit' command to edit an existing safe"); } goto out; } if (generate) { kp_password_generate(safe.password, password_len); } else { if ((ret = ctx->password_prompt(ctx, true, safe.password, "safe")) != KP_SUCCESS) { goto out; } } snprintf((char *)safe.metadata, KP_METADATA_MAX_LEN, KP_METADATA_TEMPLATE); if ((ret = kp_edit(ctx, &safe)) != KP_SUCCESS) { goto out; } if ((ret = kp_safe_save(ctx, &safe)) != KP_SUCCESS) { goto out; } if (open) { if ((ret = kp_safe_store(ctx, &safe, timeout)) != KP_SUCCESS) { kp_warn(ret, "cannot store safe in agent"); goto out; } } if ((ret = kp_safe_close(ctx, &safe)) != KP_SUCCESS) { goto out; } ret = KP_SUCCESS; out: sodium_free(password); return ret; } static kp_error_t parse_opt(struct kp_ctx *ctx, int argc, char **argv) { int opt; kp_error_t ret = KP_SUCCESS; static struct option longopts[] = { { "generate", no_argument, NULL, 'g' }, { "length", required_argument, NULL, 'l' }, { "open", no_argument, NULL, 'o' }, { "timeout", required_argument, NULL, 't' }, { NULL, 0, NULL, 0 }, }; while ((opt = getopt_long(argc, argv, "gl:ot:", longopts, NULL)) != -1) { switch (opt) { case 'g': generate = true; break; case 'l': password_len = atoi(optarg); break; case 'o': open = true; break; case 't': timeout = atoi(optarg); break; default: ret = KP_EINPUT; kp_warn(ret, "unknown option %c", opt); } } return ret; } void usage(void) { printf("options:\n"); printf(" -g, --generate Randomly generate a password\n"); printf(" -l, --length=len Length of the generated passwerd. Default to 20\n"); printf(" -o, --open Keep safe open in agent\n"); printf(" -t, --timeout Set safe timeout. Default to %d s\n", timeout); } kickpass-0.2.0/src/command/create.h000066400000000000000000000016251310455076600171550ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_CREATE_H #define KP_CREATE_H #include "command.h" extern struct kp_cmd kp_cmd_create; #endif /* KP_CREATE_H */ kickpass-0.2.0/src/command/delete.c000066400000000000000000000037461310455076600171550ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include "kickpass.h" #include "delete.h" #include "prompt.h" #include "safe.h" #include "log.h" static kp_error_t delete(struct kp_ctx *, int, char **); struct kp_cmd kp_cmd_delete = { .main = delete, .usage = NULL, .opts = "delete ", .desc = "Delete a password safe after password confirmation", }; kp_error_t delete(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret; struct kp_safe safe; char path[PATH_MAX]; if (argc - optind != 1) { ret = KP_EINPUT; kp_warn(ret, "missing safe name"); return ret; } if ((ret = kp_safe_load(ctx, &safe, argv[optind])) != KP_SUCCESS) { kp_warn(ret, "cannot load safe"); return ret; } if ((ret = kp_safe_open(ctx, &safe, true)) != KP_SUCCESS) { kp_warn(ret, "don't delete safe"); return ret; } if ((ret = kp_safe_delete(ctx, &safe)) != KP_SUCCESS) { kp_warn(ret, "cannot delete safe %s\n" "you can delete it manually with the following command:\n" "\trm %s\n" "you should also stop any running agent with the following command:\n" "\tkillall \"kickpass agent\"", argv[optind], path); return ret; } return KP_SUCCESS; } kickpass-0.2.0/src/command/delete.h000066400000000000000000000016251310455076600171540ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_DELETE_H #define KP_DELETE_H #include "command.h" extern struct kp_cmd kp_cmd_delete; #endif /* KP_DELETE_H */ kickpass-0.2.0/src/command/edit.c000066400000000000000000000126131310455076600166310ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "kickpass.h" #include "command.h" #include "config.h" #include "edit.h" #include "editor.h" #include "password.h" #include "prompt.h" #include "safe.h" #include "log.h" static kp_error_t edit(struct kp_ctx *ctx, int argc, char **argv); static kp_error_t parse_opt(struct kp_ctx *, int, char **); static kp_error_t edit_password(struct kp_ctx *, struct kp_safe *); static kp_error_t confirm_empty_password(bool *); static void usage(void); struct kp_cmd kp_cmd_edit = { .main = edit, .usage = usage, .opts = "edit [-pmgl] ", .desc = "Edit a password safe with $EDIT", }; static bool password = false; static bool metadata = false; static bool generate = false; static int password_len = 20; kp_error_t edit(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret = KP_SUCCESS; char cfg_path[PATH_MAX] = ""; struct kp_safe safe; if ((ret = parse_opt(ctx, argc, argv)) != KP_SUCCESS) { return ret; } if (argc - optind != 1) { ret = KP_EINPUT; kp_warn(ret, "missing safe name"); return ret; } if ((ret = kp_cfg_find(ctx, argv[optind], cfg_path, PATH_MAX)) != KP_SUCCESS) { kp_warn(ret, "cannot find workspace config"); return ret; } if ((ret = kp_cfg_load(ctx, cfg_path)) != KP_SUCCESS) { kp_warn(ret, "cannot load kickpass config"); return ret; } if ((ret = kp_safe_load(ctx, &safe, argv[optind])) != KP_SUCCESS) { kp_warn(ret, "cannot load safe"); return ret; } if ((ret = kp_safe_open(ctx, &safe, true)) != KP_SUCCESS) { kp_warn(ret, "cannot open safe"); return ret; } if (password) { if (generate) { kp_password_generate(safe.password, password_len); } else { if ((ret = edit_password(ctx, &safe)) != KP_SUCCESS) { return ret; } } } if (metadata) { if ((ret = kp_edit(ctx, &safe)) != KP_SUCCESS) { return ret; } } if ((ret = kp_safe_save(ctx, &safe)) != KP_SUCCESS) { return ret; } if ((ret = kp_safe_close(ctx, &safe)) != KP_SUCCESS) { return ret; } return KP_SUCCESS; } static kp_error_t edit_password(struct kp_ctx *ctx, struct kp_safe *safe) { kp_error_t ret = KP_SUCCESS; char *password; size_t password_len; bool confirm = true; password = sodium_malloc(KP_PASSWORD_MAX_LEN); if ((ret = ctx->password_prompt(ctx, true, password, "safe")) != KP_SUCCESS) { goto out; } password_len = strlen(password); if (password_len == 0) { if ((ret = confirm_empty_password(&confirm)) != KP_SUCCESS) { sodium_free(password); return ret; } } if (confirm) { memcpy(safe->password, password, password_len); safe->password[password_len] = '\0'; } out: sodium_free(password); return ret; } static kp_error_t confirm_empty_password(bool *confirm) { kp_error_t ret = KP_SUCCESS; char *prompt = "Empty password. Do you really want to update password ? (y/n) [n] "; char *response = NULL; size_t response_len = 0; FILE *tty; *confirm = false; tty = fopen("/dev/tty", "r+"); if (!tty) { ret = KP_ERRNO; kp_warn(ret, "cannot access /dev/tty"); return ret; } fprintf(tty, "%s", prompt); fflush(tty); if (getline(&response, &response_len, stdin) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot read answer"); return ret; } if (response[0] == 'y') *confirm = true; free(response); fclose(tty); return ret; } static kp_error_t parse_opt(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret = KP_SUCCESS; int opt; static struct option longopts[] = { { "password", no_argument, NULL, 'p' }, { "metadata", no_argument, NULL, 'm' }, { "generate", no_argument, NULL, 'g' }, { "length", required_argument, NULL, 'l' }, { NULL, 0, NULL, 0 }, }; while ((opt = getopt_long(argc, argv, "pmgl:", longopts, NULL)) != -1) { switch (opt) { case 'p': password = true; break; case 'm': metadata = true; break; case 'g': generate = true; break; case 'l': password_len = atoi(optarg); break; default: ret = KP_EINPUT; kp_warn(ret, "unknown option %c", opt); return ret; } } if (password && metadata) { kp_warn(KP_EINPUT, "Editing both password and metadata" " is default behavior. You can ommit options."); } /* Default edit all */ if (!password && !metadata) { password = true; metadata = true; } return KP_SUCCESS; } void usage(void) { printf("options:\n"); printf(" -p, --password Edit only password\n"); printf(" -g, --generate Randomly generate a password\n"); printf(" -l, --length=len Length of the generated passwerd. Default to 20\n"); printf(" -m, --metadata Edit only metadata\n"); } kickpass-0.2.0/src/command/edit.h000066400000000000000000000016151310455076600166360ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_EDIT_H #define KP_EDIT_H #include "command.h" extern struct kp_cmd kp_cmd_edit; #endif /* KP_EDIT_H */ kickpass-0.2.0/src/command/init.c000066400000000000000000000050731310455076600166510ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "kickpass.h" #include "command.h" #include "init.h" #include "prompt.h" #include "config.h" #include "log.h" static kp_error_t init(struct kp_ctx *ctx, int argc, char **argv); static kp_error_t parse_opt(struct kp_ctx *, int, char **); struct kp_cmd kp_cmd_init = { .main = init, .usage = NULL, .opts = "init", .desc = "Initialize a new password safe directory. " "Default to ~/" KP_PATH, }; kp_error_t init(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret = KP_SUCCESS; char sub[PATH_MAX] = ""; if ((ret = parse_opt(ctx, argc, argv)) != KP_SUCCESS) { return ret; } if (optind < argc) { if (strlcpy(sub, argv[optind++], PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } } ctx->password_prompt(ctx, true, (char *)ctx->password, "master"); if ((ret = kp_init_workspace(ctx, sub)) != KP_SUCCESS) { kp_warn(ret, "cannot init workspace"); return ret; } if ((ret = kp_cfg_create(ctx, sub)) != KP_SUCCESS) { kp_warn(ret, "cannot create configuration"); return ret; } return ret; } static kp_error_t parse_opt(struct kp_ctx *ctx, int argc, char **argv) { int opt; kp_error_t ret = KP_SUCCESS; static struct option longopts[] = { { "memlimit", required_argument, NULL, 'm' }, /* hidden option */ { "opslimit", required_argument, NULL, 'o' }, /* hidden option */ { NULL, 0, NULL, 0 }, }; while ((opt = getopt_long(argc, argv, "", longopts, NULL)) != -1) { switch (opt) { case 'm': ctx->cfg.memlimit = atol(optarg); break; case 'o': ctx->cfg.opslimit = atoll(optarg); break; default: ret = KP_EINPUT; kp_warn(ret, "unknown option %c", opt); } } return ret; } kickpass-0.2.0/src/command/init.h000066400000000000000000000016151310455076600166540ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_INIT_H #define KP_INIT_H #include "command.h" extern struct kp_cmd kp_cmd_init; #endif /* KP_INIT_H */ kickpass-0.2.0/src/command/list.c000066400000000000000000000077451310455076600166710ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "kickpass.h" #include "command.h" #include "list.h" #include "log.h" static kp_error_t list(struct kp_ctx *, int, char **); static kp_error_t list_dir(struct kp_ctx *, char *, char *, bool); static kp_error_t list_dir_r(char ***, int *, char *); static int path_sort(const void *, const void *); struct kp_cmd kp_cmd_list = { .main = list, .usage = NULL, .opts = "list", .desc = "List available safes", }; static kp_error_t list(struct kp_ctx *ctx, int argc, char **argv) { int i; if (argc == optind) { list_dir(ctx, ctx->ws_path, "", false); } for (i = optind; i < argc; i++) { char path[PATH_MAX]; if (strlcpy(path, ctx->ws_path, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(path, "/", PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } if (strlcat(path, argv[i], PATH_MAX) >= PATH_MAX) { errno = ENOMEM; return KP_ERRNO; } list_dir(ctx, path, " ", true); } return KP_SUCCESS; } static kp_error_t list_dir_r(char ***safes, int *nsafes, char *root) { kp_error_t ret = KP_SUCCESS; DIR *dirp; struct dirent *dirent; if ((dirp = opendir(root)) == NULL) { ret = KP_ERRNO; kp_warn(ret, "cannot open dir %s", root); return ret; } while ((dirent = readdir(dirp)) != NULL) { char path[PATH_MAX]; if (dirent->d_name[0] == '.' || (dirent->d_type != DT_REG && dirent->d_type != DT_DIR)) { continue; } if (strlcpy(path, root, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "memory error"); goto out; } if (strlcat(path, "/", PATH_MAX) >= PATH_MAX) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "memory error"); goto out; } if (strlcat(path, dirent->d_name, PATH_MAX) >= PATH_MAX) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "memory error"); goto out; } switch (dirent->d_type) { case DT_REG: if ((*safes = reallocarray(*safes, *nsafes + 1, sizeof(char *))) == NULL) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "memory error"); goto out; } (*safes)[*nsafes] = strndup(path, PATH_MAX); if ((*safes)[*nsafes] == NULL) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "memory error"); goto out; } (*nsafes)++; break; case DT_DIR: ret = list_dir_r(safes, nsafes, path); } } out: closedir(dirp); return ret; } static kp_error_t list_dir(struct kp_ctx *ctx, char *root, char *indent, bool print_path) { kp_error_t ret; char **safes = NULL; int nsafes = 0, i = 0; size_t ignore; safes = calloc(nsafes, sizeof(char *)); if ((ret = list_dir_r(&safes, &nsafes, root)) != KP_SUCCESS) { goto out; } qsort(safes, nsafes, sizeof(char *), path_sort); if (print_path) { printf("%s/\n", root + strlen(ctx->ws_path) + 1); } ignore = strlen(root) + 1; for (i = 0; i < nsafes; i++) { printf("%s%s\n", indent, safes[i] + ignore); } out: for (i = 0; i < nsafes; i++) { free(safes[i]); } free(safes); return ret; } static int path_sort(const void *a, const void *b) { return strncmp(*(const char **)a, *(const char **)b, PATH_MAX); } kickpass-0.2.0/src/command/list.h000066400000000000000000000016151310455076600166640ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_LIST_H #define KP_LIST_H #include "command.h" extern struct kp_cmd kp_cmd_list; #endif /* KP_LIST_H */ kickpass-0.2.0/src/command/open.c000066400000000000000000000057011310455076600166450ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include "kickpass.h" #include "command.h" #include "open.h" #include "prompt.h" #include "safe.h" #include "log.h" #include "kpagent.h" static kp_error_t open_safe(struct kp_ctx *ctx, int argc, char **argv); static kp_error_t parse_opt(struct kp_ctx *, int, char **); static void usage(void); struct kp_cmd kp_cmd_open = { .main = open_safe, .usage = usage, .opts = "open [-t] ", .desc = "Open a password safe and load it in kickpass agent", }; static int timeout = 3600; kp_error_t open_safe(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret; struct kp_safe safe; if ((ret = parse_opt(ctx, argc, argv)) != KP_SUCCESS) { return ret; } if (argc - optind != 1) { ret = KP_EINPUT; kp_warn(ret, "missing safe name"); return ret; } if (!ctx->agent.connected) { ret = KP_EINPUT; kp_warn(ret, "not connected to any agent"); return ret; } if ((ret = kp_safe_load(ctx, &safe, argv[optind])) != KP_SUCCESS) { kp_warn(ret, "cannot load safe"); return ret; } if ((ret = kp_safe_open(ctx, &safe, false)) != KP_SUCCESS) { kp_warn(ret, "cannot open safe"); return ret; } if ((ret = kp_safe_store(ctx, &safe, timeout)) != KP_SUCCESS) { kp_warn(ret, "cannot store safe in agent"); return ret; } if ((ret = kp_safe_close(ctx, &safe)) != KP_SUCCESS) { kp_warn(ret, "cannot cleanly close safe" "clear text password might have leaked"); return ret; } return KP_SUCCESS; } static kp_error_t parse_opt(struct kp_ctx *ctx, int argc, char **argv) { int opt; kp_error_t ret = KP_SUCCESS; static struct option longopts[] = { { "timeout", no_argument, NULL, 't' }, { NULL, 0, NULL, 0 }, }; while ((opt = getopt_long(argc, argv, "t:", longopts, NULL)) != -1) { switch (opt) { case 't': timeout = atoi(optarg); break; default: ret = KP_EINPUT; kp_warn(ret, "unknown option %c", opt); } } return ret; } void usage(void) { printf("options:\n"); printf(" -t, --timeout Set safe timeout. Default to %d s\n", timeout); } kickpass-0.2.0/src/command/open.h000066400000000000000000000016151310455076600166520ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_OPEN_H #define KP_OPEN_H #include "command.h" extern struct kp_cmd kp_cmd_open; #endif /* KP_OPEN_H */ kickpass-0.2.0/src/command/rename.c000066400000000000000000000042441310455076600171540ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include "kickpass.h" #include "command.h" #include "rename.h" #include "prompt.h" #include "safe.h" #include "log.h" static kp_error_t do_rename(struct kp_ctx *ctx, int argc, char **argv); struct kp_cmd kp_cmd_rename = { .main = do_rename, .usage = NULL, .opts = "rename ", .desc = "Rename a password safe", }; kp_error_t do_rename(struct kp_ctx *ctx, int argc, char **argv) { kp_error_t ret; struct kp_safe safe; if (argc - optind != 2) { ret = KP_EINPUT; kp_warn(ret, "missing safe name"); return ret; } if ((ret = kp_safe_load(ctx, &safe, argv[optind])) != KP_SUCCESS) { kp_warn(ret, "cannot load safe"); return ret; } optind++; if ((ret = kp_safe_open(ctx, &safe, true)) != KP_SUCCESS) { kp_warn(ret, "cannot open safe"); goto fail; } if ((ret = kp_safe_rename(ctx, &safe, argv[optind])) != KP_SUCCESS) { kp_warn(ret, "cannot change safe name"); return ret; } if ((ret = kp_safe_close(ctx, &safe)) != KP_SUCCESS) { kp_warn(ret, "cannot cleanly close safe" "clear text password might have leaked"); return ret; } return KP_SUCCESS; fail: if ((ret = kp_safe_close(ctx, &safe)) != KP_SUCCESS) { kp_warn(ret, "cannot cleanly close safe" "clear text password might have leaked"); return ret; } return ret; } kickpass-0.2.0/src/command/rename.h000066400000000000000000000016251310455076600171610ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_RENAME_H #define KP_RENAME_H #include "command.h" extern struct kp_cmd kp_cmd_rename; #endif /* KP_RENAME_H */ kickpass-0.2.0/src/editor.c000066400000000000000000000066331310455076600155610ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "editor.h" #include "error.h" #include "log.h" static kp_error_t kp_editor_get_tmp(struct kp_ctx *, struct kp_safe *, char *, size_t); /* * Open editor on the given safe. * Fork, exec $EDITOR and wait for it to return. * The safe must be open. */ kp_error_t kp_edit(struct kp_ctx *ctx, struct kp_safe *safe) { kp_error_t ret; FILE *fd = NULL; const char *editor; char path[PATH_MAX]; pid_t pid; size_t metadata_len; assert(safe->open); kp_editor_get_tmp(ctx, safe, path, PATH_MAX); editor = getenv("EDITOR"); if (editor == NULL) editor = "vi"; pid = fork(); if (pid == 0) { if (execlp(editor, editor, path, NULL) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot run editor"); goto clean; } } wait(NULL); fd = fopen(path, "r"); if (fd == NULL) { ret = KP_ERRNO; kp_warn(ret, "cannot open temporary clear text file %s", path); goto clean; } clearerr(fd); metadata_len = fread(safe->metadata, 1, KP_METADATA_MAX_LEN, fd); safe->metadata[metadata_len] = '\0'; if ((errno = ferror(fd)) != 0) { ret = KP_ERRNO; kp_warn(ret, "error while reading temporary clear text file %s", path); goto clean; } if (!feof(fd)) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "safe too long, storing only %lu bytes", metadata_len); goto clean; } ret = KP_SUCCESS; clean: fclose(fd); if (unlink(path) < 0) { kp_warn(KP_ERRNO, "cannot delete temporary clear text file %s" "ensure to delete it manually to avoid metadata leak", path); } return ret; } /* * Create and open a temporary file in current workspace for later edition. */ static kp_error_t kp_editor_get_tmp(struct kp_ctx *ctx, struct kp_safe *safe, char *path, size_t size) { int fd; size_t metadata_len; kp_error_t ret; if (strlcpy(path, ctx->ws_path, size) >= size) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "memory error"); return ret; } if (strlcat(path, "/.kpXXXXXX", size) >= size) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "memory error"); return ret; } if ((fd = mkstemp(path)) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot create temporary file %s", path); return ret; } metadata_len = strlen(safe->metadata); if (write(fd, safe->metadata, metadata_len) != metadata_len) { ret = KP_ERRNO; kp_warn(ret, "cannot dump safe on temp file %s for edition", path); return ret; } if (close(fd) < 0) { ret = KP_ERRNO; kp_warn(ret, "cannot close temp file %s with plain safe", path); return ret; } return KP_SUCCESS; } kickpass-0.2.0/src/log.c000066400000000000000000000036541310455076600150540ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include "error.h" char * kp_log_decorate(kp_error_t err, const char *fmt) { char *_fmt; if (asprintf(&_fmt, "%s: %s", fmt, kp_strerror(err)) < 0) { errx(ENOMEM, "memory error"); } return _fmt; } __attribute__((format(printf, 2, 3))) void kp_err(kp_error_t err, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (err == KP_ERRNO) { verr(err, fmt, ap); } else { char *_fmt; _fmt = kp_log_decorate(err, fmt); verrx(err, _fmt, ap); free(_fmt); } va_end(ap); } __attribute__((format(printf, 2, 3))) void kp_warn(kp_error_t err, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (err == KP_ERRNO) { vwarn(fmt, ap); } else { char *_fmt; _fmt = kp_log_decorate(err, fmt); vwarnx(_fmt, ap); free(_fmt); } va_end(ap); } __attribute__((format(printf, 2, 3))) void kp_errx(kp_error_t err, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrx(err, fmt, ap); va_end(ap); } __attribute__((format(printf, 2, 3))) void kp_warnx(kp_error_t err, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } kickpass-0.2.0/src/log.h000066400000000000000000000020061310455076600150470ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_LOG_H #define KP_LOG_H void kp_err(kp_error_t, const char *, ...); void kp_warn(kp_error_t, const char *, ...); void kp_errx(kp_error_t, const char *, ...); void kp_warnx(kp_error_t, const char *, ...); #endif /* KP_LOG_H */ kickpass-0.2.0/src/main.c000066400000000000000000000153671310455076600152230ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include "kickpass.h" #include "command.h" #include "kpagent.h" #include "log.h" #include "prompt.h" #include "safe.h" /* commands */ #ifdef HAS_X11 #include "command/copy.h" #endif /* HAS_X11 */ #include "command/create.h" #include "command/delete.h" #include "command/edit.h" #include "command/init.h" #include "command/list.h" #include "command/cat.h" #include "command/rename.h" #include "command/agent.h" #include "command/open.h" static int cmd_search(const void *, const void *); static int cmd_sort(const void *, const void *); static kp_error_t command(struct kp_ctx *, int, char **); static kp_error_t help(struct kp_ctx *, int, char **); static kp_error_t parse_opt(struct kp_ctx *, int, char **); static kp_error_t setup_prompt(struct kp_ctx *); static kp_error_t show_version(struct kp_ctx *); static kp_error_t usage(void); struct cmd { const char *name; struct kp_cmd *cmd; }; #define CMD_COUNT (sizeof(cmds)/sizeof(cmds[0])) static struct kp_cmd kp_cmd_help = { .main = help, .usage = NULL, .opts = "help ", .desc = "Print help for given command", }; static struct cmd cmds[] = { /* kp_cmd_help */ { "help", &kp_cmd_help }, /* kp_cmd_init */ { "init", &kp_cmd_init }, /* kp_cmd_create */ { "create", &kp_cmd_create }, { "new", &kp_cmd_create }, { "insert", &kp_cmd_create }, /* kp_cmd_cat */ { "cat", &kp_cmd_cat }, { "show", &kp_cmd_cat }, /* kp_cmd_edit */ { "edit", &kp_cmd_edit }, #ifdef HAS_X11 /* kp_cmd_copy */ { "copy", &kp_cmd_copy }, #endif /* HAS_X11 */ /* kp_cmd_list */ { "ls", &kp_cmd_list }, { "list", &kp_cmd_list }, /* kp_cmd_delete */ { "delete", &kp_cmd_delete }, { "rm", &kp_cmd_delete }, { "remove", &kp_cmd_delete }, { "destroy", &kp_cmd_delete }, /* kp_cmd_rename */ { "rename", &kp_cmd_rename }, { "mv", &kp_cmd_rename }, { "move", &kp_cmd_rename }, /* kp_cmd_agent */ { "agent", &kp_cmd_agent }, /* kp_cmd_open */ { "open", &kp_cmd_open }, }; /* * Parse command line and call matching command. * Most command are aliased and parse their own arguments. */ int main(int argc, char **argv) { int ret; struct kp_ctx ctx; char *socket_path = NULL; kp_init(&ctx); if ((ret = parse_opt(&ctx, argc, argv)) != KP_SUCCESS) { goto out; } if ((ret = setup_prompt(&ctx)) != KP_SUCCESS) { goto out; } /* Try to connect to agent */ if ((socket_path = getenv(KP_AGENT_SOCKET_ENV)) != NULL) { if ((ret = kp_agent_init(&ctx.agent, socket_path)) != KP_SUCCESS) { kp_warn(ret, "cannot connect to agent socket %s", socket_path); return ret; } if ((ret = kp_agent_connect(&ctx.agent)) != KP_SUCCESS) { kp_warn(ret, "cannot connect to agent socket %s", socket_path); return ret; } } ret = command(&ctx, argc, argv); out: kp_fini(&ctx); return ret; } /* * Setup best prompt for password. */ static kp_error_t setup_prompt(struct kp_ctx *ctx) { int fd; const char *tty; tty = ctermid(NULL); fd = open(tty, O_RDWR); if (isatty(fd)) { ctx->password_prompt = kp_readpass; } else { ctx->password_prompt = kp_askpass; } close(fd); return KP_SUCCESS; } /* * Parse global argument */ static kp_error_t parse_opt(struct kp_ctx *ctx, int argc, char **argv) { int opt; static struct option longopts[] = { { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { NULL, 0, NULL, 0 }, }; while ((opt = getopt_long(argc, argv, "+vh", longopts, NULL)) != -1) { switch (opt) { case 'v': return show_version(ctx); case 'h': return usage(); default: kp_warnx(KP_EINPUT, "unknown option %c", opt); return KP_EINPUT; } } return KP_SUCCESS; } static int cmd_search(const void *k, const void *e) { return strcmp(k, ((struct cmd *)e)->name); } static int cmd_sort(const void *a, const void *b) { return strcmp(((struct cmd *)a)->name, ((struct cmd *)b)->name); } /* * Find command */ static struct kp_cmd * find_command(const char *command) { const struct cmd *cmd; qsort(cmds, CMD_COUNT, sizeof(struct cmd), cmd_sort); cmd = bsearch(command, cmds, CMD_COUNT, sizeof(struct cmd), cmd_search); if (!cmd) kp_errx(KP_EINPUT, "unknown command %s", command); return cmd->cmd; } /* * Call given command and let it parse its own arguments. */ static kp_error_t command(struct kp_ctx *ctx, int argc, char **argv) { struct kp_cmd *cmd; if (optind >= argc) kp_errx(KP_EINPUT, "missing command"); /* Test for help first so we don't mess with cmds */ if (strncmp(argv[optind], "help", 4) == 0) { cmd = &kp_cmd_help; } else { cmd = find_command(argv[optind]); } optind++; return cmd->main(ctx, argc, argv); } static kp_error_t show_version(struct kp_ctx *ctx) { printf("KickPass version %d.%d.%d\n", KICKPASS_VERSION_MAJOR, KICKPASS_VERSION_MINOR, KICKPASS_VERSION_PATCH); return KP_EXIT; } static kp_error_t help(struct kp_ctx *ctx, int argc, char **argv) { extern char *__progname; struct kp_cmd *cmd; if (optind >= argc) { usage(); return KP_EINPUT; } cmd = find_command(argv[optind]); printf("usage: %s %s\n" "\n", __progname, cmd->opts); if (cmd->usage) cmd->usage(); return KP_SUCCESS; } /* * Print global usage by calling each command own usage function. */ static kp_error_t usage(void) { int i, opts_max_len = 0; extern char *__progname; char usage[] = "usage: %s [-hv] [] []\n" "\n" "options:\n" " -h, --help Print this help\n" " -v, --version Print %s version\n" "\n" "commands:\n"; printf(usage, __progname, __progname); for (i = 0; i < CMD_COUNT; i++) { size_t opts_len = strlen(cmds[i].cmd->opts); if (opts_len > opts_max_len) opts_max_len = opts_len; } opts_max_len++; for (i = 0; i < CMD_COUNT; i++) { if (cmds[i-1].cmd == cmds[i].cmd) continue; printf(" %*s%s\n", -opts_max_len, cmds[i].cmd->opts, cmds[i].cmd->desc); } return KP_EXIT; } kickpass-0.2.0/src/prompt.c000066400000000000000000000103101310455076600155770ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include "kickpass.h" #include "safe.h" #include "log.h" #define PASSWORD_PROMPT "[kickpass] %s password: " #define PASSWORD_CONFIRM_PROMPT "[kickpass] confirm: " kp_error_t kp_askpass(struct kp_ctx *ctx, bool confirm, char *password, const char *fmt, ...) { kp_error_t ret = KP_SUCCESS; char *prompt_fmt = NULL, *prompt = NULL; va_list ap; char *askpass = NULL; char *output = NULL; size_t len = 0; pid_t pid; int pipefd[2], status; FILE *fout = NULL; void (*sigchld_handler)(int); if ((askpass = getenv("KP_ASKPASS")) == NULL) { askpass = "ssh-askpass"; } if (fflush(stdout) != 0) { return KP_ERRNO; } if (pipe(pipefd) < 0) { return KP_ERRNO; } sigchld_handler = signal(SIGCHLD, SIG_DFL); if ((pid = fork()) < 0) { ret = KP_ERRNO; goto out; } if (pid == 0) { close(pipefd[0]); if (dup2(pipefd[1], STDOUT_FILENO) < 0) { kp_err(KP_ERRNO, "read stdout of %s", askpass); } if (asprintf(&prompt_fmt, PASSWORD_PROMPT, fmt) < 0) { kp_err(KP_ERRNO, "cannot build prompt"); } va_start(ap, fmt); if (vasprintf(&prompt, prompt_fmt, ap) < 0) { kp_err(KP_ERRNO, "cannot build prompt"); } va_end(ap); execlp(askpass, askpass, prompt, (char *)NULL); free(prompt); close(pipefd[1]); kp_err(KP_ERRNO, "cannot execute %s", askpass); } close(pipefd[1]); if ((fout = fdopen(pipefd[0], "r")) == NULL) { ret = KP_ERRNO; kp_warn(ret, "cannot read password"); goto out; } if (getline(&output, &len, fout) < 0) { ret = KP_ERRNO; goto out; } while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) goto out; } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { goto out; } output[strcspn(output, "\r\n")] = '\0'; if (strlcpy(password, output, KP_PASSWORD_MAX_LEN) >= KP_PASSWORD_MAX_LEN) { errno = ENAMETOOLONG; ret = KP_ERRNO; kp_warn(ret, "cannot read password"); goto out; } out: fclose(fout); free(output); signal(SIGCHLD, sigchld_handler); return ret; } kp_error_t kp_readpass(struct kp_ctx *ctx, bool confirm, char *password, const char *fmt, ...) { kp_error_t ret = KP_SUCCESS; char *prompt_fmt = NULL, *prompt = NULL; va_list ap; char *confirmation = NULL; assert(fmt); assert(password); if (asprintf(&prompt_fmt, PASSWORD_PROMPT, fmt) < 0) { kp_err(KP_ERRNO, "cannot build prompt"); } va_start(ap, fmt); if (vasprintf(&prompt, prompt_fmt, ap) < 0) { kp_err(KP_ERRNO, "cannot build prompt"); } va_end(ap); if (readpassphrase(prompt, password, KP_PASSWORD_MAX_LEN, RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { ret = KP_ERRNO; kp_warn(ret, "cannot read password"); goto out; } if (confirm) { confirmation = sodium_malloc(KP_PASSWORD_MAX_LEN); if (!confirmation) { errno = ENOMEM; ret = KP_ERRNO; kp_warn(ret, "memory error"); goto out; } if (readpassphrase(PASSWORD_CONFIRM_PROMPT, confirmation, KP_PASSWORD_MAX_LEN, RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL) { ret = KP_ERRNO; kp_warn(ret, "cannot read password"); goto out; } if (strncmp(password, confirmation, KP_PASSWORD_MAX_LEN) != 0) { ret = KP_EINPUT; kp_warn(ret, "mismatching password"); goto out; } } out: free(prompt_fmt); free(prompt); sodium_free(confirmation); return ret; } kickpass-0.2.0/src/prompt.h000066400000000000000000000021111310455076600156040ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef KP_PROMPT_H #define KP_PROMPT_H #include #include #include "error.h" #include "kickpass_config.h" kp_error_t kp_readpass(struct kp_ctx *, bool, char *, const char *, ...); kp_error_t kp_askpass(struct kp_ctx *, bool, char *, const char *, ...); #endif /* KP_PROMPT_H */ kickpass-0.2.0/test/000077500000000000000000000000001310455076600143075ustar00rootroot00000000000000kickpass-0.2.0/test/CMakeLists.txt000066400000000000000000000033351310455076600170530ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # include(Test) include(CheckLibraryExists) find_library(M_LIB m) set(TEST_LIBS ${LIBS} ${M_LIB}) check_library_exists(c timer_create "" HAVE_TIMER_CREATE) if (NOT HAVE_TIMER_CREATE) check_library_exists(rt timer_create "time.h" HAVE_TIMER_CREATE_IN_RT) if (HAVE_TIMER_CREATE_IN_RT) find_library(RT_LIB rt) set(TEST_LIBS ${TEST_LIBS} ${RT_LIB}) else() message(FATAL_ERROR "can't find timer_create function") endif() endif() find_library(PTHREAD_LIB pthread) set(TEST_LIBS ${TEST_LIBS} ${PTHREAD_LIB}) UNIT_TEST(NAME storage FILE storage.c LIBS libkickpass ${TEST_LIBS}) UNIT_TEST(NAME safe FILE safe.c LIBS libkickpass ${TEST_LIBS}) INTEGRATION_TEST(NAME init FILE init.py) INTEGRATION_TEST(NAME create FILE create.py) INTEGRATION_TEST(NAME edit FILE edit.py) INTEGRATION_TEST(NAME list FILE list.py) INTEGRATION_TEST(NAME cat FILE cat.py) INTEGRATION_TEST(NAME open FILE open.py) INTEGRATION_TEST(NAME delete FILE delete.py) INTEGRATION_TEST(NAME rename FILE rename.py) kickpass-0.2.0/test/cat.py000066400000000000000000000034761310455076600154420ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import unittest import kptest class TestCatCommand(kptest.KPTestCase): def test_cat_is_successful(self): # Given self.editor('env', env="Watch out for turtles. They'll bite you if you put your fingers in their mouths.") self.create("test", password="42") # When self.cat("test", options=["-pm"]) # Then self.assertStdoutEquals("42", "Watch out for turtles. They'll bite you if you put your fingers in their mouths.") @kptest.with_agent def test_cat_is_successful_with_agent(self): # Given self.editor('env', env="Watch out for turtles. They'll bite you if you put your fingers in their mouths.") self.create("test", password="42") self.open("test") # When self.cat("test", master=None, options=["-pm"]) # Then self.assertStdoutEquals("42", "Watch out for turtles. They'll bite you if you put your fingers in their mouths.") if __name__ == '__main__': unittest.main() kickpass-0.2.0/test/check_compat.h000066400000000000000000000036021310455076600171010ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _ck_assert_ptr /* Pointer comparsion macros with improved output compared to fail_unless(). */ /* OP may be any comparion operator. */ #define _ck_assert_ptr(X, OP, Y) do { \ void *_ck_x = (X); \ void *_ck_y = (Y); \ ck_assert_msg(_ck_x OP _ck_y, "Assertion '"#X#OP#Y"' failed: "#X"==%p, "#Y"==%p", _ck_x, _ck_y); \ } while (0) #endif #ifndef ck_assert_ptr_eq #define ck_assert_ptr_eq(X, Y) _ck_assert_ptr(X, ==, Y) #endif /* ck_assert_ptr_eq */ #ifndef ck_assert_ptr_ne #define ck_assert_ptr_ne(X, Y) _ck_assert_ptr(X, !=, Y) #endif /* ck_assert_ptr_ne */ #ifndef ck_assert_ptr_lt #define ck_assert_ptr_lt(X, Y) _ck_assert_ptr(X, <, Y) #endif /* ck_assert_ptr_lt */ #ifndef ck_assert_ptr_le #define ck_assert_ptr_le(X, Y) _ck_assert_ptr(X, <=, Y) #endif /* ck_assert_ptr_le */ #ifndef ck_assert_ptr_gt #define ck_assert_ptr_gt(X, Y) _ck_assert_ptr(X, >, Y) #endif /* ck_assert_ptr_gt */ #ifndef ck_assert_ptr_ge #define ck_assert_ptr_ge(X, Y) _ck_assert_ptr(X, >=, Y) #endif /* ck_assert_ptr_ge */ #ifndef ck_assert_int_gt #define ck_assert_int_gt(X, Y) _ck_assert_int(X, >, Y) #endif /* ck_assert_int_gt */ kickpass-0.2.0/test/create.py000066400000000000000000000061611310455076600161300ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import unittest import kptest class TestCreateCommand(kptest.KPTestCase): def test_create_is_successful(self): # Given self.editor('date') # When self.create("test") # Then self.assertSafeExists("test") self.assertSafeIsBigEnough("test") def test_create_with_subdir_is_successful(self): # Given self.editor('date') # When self.create("subdir/test") # Then self.assertSafeExists("subdir/test") self.assertSafeIsBigEnough("subdir/test") def test_create_with_password_generation_is_successful(self): # Given self.editor('save') # When self.create("test", options=["-g", "-l", "42"], password=None) # Then self.cat("test", options=["-p"]) passwd = self.stdout.splitlines()[1] self.assertEqual(len(passwd), 42) @kptest.with_agent def test_create_with_agent_is_successful(self): # Given self.editor('env', env="Watch out for turtles. They'll bite you if you put your fingers in their mouths.") self.create("test") # When # cat should ask for password, thus master param is not set to None self.cat("test") # Then self.assertStdoutEquals("Watch out for turtles. They'll bite you if you put your fingers in their mouths.") @kptest.with_agent def test_create_open_with_agent_is_successful(self): # Given self.editor('env', env="Watch out for turtles. They'll bite you if you put your fingers in their mouths.") self.create("test", options=["-o"]) # When self.cat("test", master=None) # Then self.assertStdoutEquals("Watch out for turtles. They'll bite you if you put your fingers in their mouths.") def test_create_in_sub_workspace_is_successful(self): # Given self.init("sub", master="sub master password") self.editor('env', env="Watch out for turtles. They'll bite you if you put your fingers in their mouths.") self.create("sub/subdir/test", master="sub master password") # When self.cat("sub/subdir/test", master="sub master password") # Then self.assertStdoutEquals("Watch out for turtles. They'll bite you if you put your fingers in their mouths.") if __name__ == '__main__': unittest.main() kickpass-0.2.0/test/delete.py000066400000000000000000000044251310455076600161300ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import unittest import kptest class TestDeleteCommand(kptest.KPTestCase): def test_delete_is_successful(self): # Given self.editor('date') self.create("test") # When self.delete("test") # Then self.assertSafeDoesntExists("test") def test_delete_refuse_to_delete_without_valid_password(self): # Given self.editor('date') self.create("test") # When self.delete("test", master="Il0v3ST20", rc=7) # Then self.assertSafeExists("test") @kptest.with_agent def test_delete_with_agent_is_successful(self): # Given self.editor('date') self.create("test") self.open("test") # When self.delete("test") # Then self.assertSafeDoesntExists("test") @kptest.with_agent def test_delete_with_agent_remove_safe_from_agent(self): # Given self.editor('date') self.create("test") self.open("test") self.delete("test") # When # Recreate test to ensure older test isn't in agent self.editor('env', env="Watch out for turtles. They'll bite you if you put your fingers in their mouths.") self.create("test") # Then # cat should ask for password, thus master param is not set to None self.cat("test") self.assertStdoutEquals("Watch out for turtles. They'll bite you if you put your fingers in their mouths.") if __name__ == '__main__': unittest.main() kickpass-0.2.0/test/edit.py000066400000000000000000000112731310455076600156120ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import unittest import kptest class TestEditCommand(kptest.KPTestCase): def test_edit_is_successful(self): # Given self.editor('env', env="But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") self.create("test") self.editor('save') # When self.edit("test") # Then self.assertSafeExists("test") self.assertSafeIsBigEnough("test") self.assertClearTextExists() self.assertClearTextEquals("But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") def test_edit_only_password_is_successful(self): # Given self.editor('date') self.create("test") # When self.edit("test", password="RocknRolla", options=["-p"]) # Then self.cat("test", options=["-p"]) self.assertStdoutEquals("RocknRolla") def test_edit_only_metadata_is_successful(self): # Given self.editor('date') self.create("test") self.editor('env', env="Oh, you are something special, Mr. Johnny Quid.") # When self.edit("test", password=None, options=["-m"]) # Then self.cat("test") self.assertStdoutEquals("Oh, you are something special, Mr. Johnny Quid.") def test_edit_with_empty_password(self): # Given self.editor('date') self.create("test", password="RocknRolla") # When self.edit("test", password="", options=["-p"], yesno="n") # Then self.cat("test", options=["-p"]) self.assertStdoutEquals("RocknRolla") def test_edit_with_empty_password_erased(self): # Given self.editor('date') self.create("test", password="RocknRolla") # When self.edit("test", password="", options=["-p"], yesno="y") # Then self.cat("test", options=["-p"]) self.assertStdoutEquals() def test_edit_with_password_generation_is_successful(self): # Given self.editor('save') self.create("test", password="RocknRolla") # When self.edit("test", options=["-g", "-l", "42"], password=None) # Then self.cat("test", options=["-p"]) passwd = self.stdout.splitlines()[1] self.assertNotEqual(passwd, "RocknRolla") self.assertEqual(len(passwd), 42) def test_edit_opened_safe_is_successful(self): # Given self.start_agent() self.editor('date') self.create("test", password="RocknRolla", options=["-o"]) self.editor('env', env="But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") # When self.edit("test", password="42") # Then self.cat("test", master=None, options=["-pm"]) self.assertStdoutEquals("42", "But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") # When self.stop_agent() self.cat("test", options=["-pm"]) self.assertStdoutEquals("42", "But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") def test_edit_in_sub_workspace_is_successful(self): # Given self.init("sub", master="sub master password") self.editor('env', env="But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") self.create("sub/test", master="sub master password") self.editor('save') # When self.edit("sub/test", master="sub master password") # Then self.assertSafeExists("sub/test") self.assertSafeIsBigEnough("sub/test") self.assertClearTextExists() self.assertClearTextEquals("But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") if __name__ == '__main__': unittest.main() kickpass-0.2.0/test/init.py000066400000000000000000000024021310455076600156220ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import unittest import kptest class TestInitCommand(kptest.KPTestCase): def test_init_is_successful(self): # Given # Init is done in setUp() # When # Then self.assertWsExists() def test_init_sub_workspace_is_successful(self): # Given # Init is done in setUp() # When self.init("work/") # Then self.assertWsExists() self.assertWsExists("work/") if __name__ == '__main__': unittest.main() kickpass-0.2.0/test/init.sh000066400000000000000000000002021310455076600156000ustar00rootroot00000000000000#!/bin/sh set -e # Given # When do_test $KP init # Then [ -d $HOME/.kickpass ] || abort "missing kickpass workspace directory" kickpass-0.2.0/test/kptest.py000066400000000000000000000167421310455076600162050ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import collections import os import pexpect import shlex import shutil import subprocess import logging import sys import unittest class KPAgent(subprocess.Popen): def __init__(self, kp): super(KPAgent, self).__init__([kp, 'agent', '-d'], stdout=subprocess.PIPE, universal_newlines=True) env, value = self.stdout.readline().strip().split('=') self.env = {env: value} os.environ.update(self.env) def with_agent(f): def wrapper(self): self.start_agent() f(self) self.stop_agent() return wrapper class KPTestCase(unittest.TestCase): EDITORS = { 'env': 'TestFunctionalEditorEnv.sh', 'date': 'TestFunctionalEditorDate.sh', 'save': 'TestFunctionalEditorSave.sh', } def __init__(self, *args, **kwargs): super(KPTestCase, self).__init__(*args, **kwargs) # Get env vars self.kp = os.environ['KP'] self.editor_path = os.environ['EDITOR_PATH'] self.kp_ws = os.path.join(os.environ['HOME'], '.kickpass') self.clear_text = os.path.join(os.environ['HOME'], 'editor-save.txt') self.agent = None @classmethod def setUpClass(cls): logging.getLogger().setLevel(logging.INFO) def setUp(self): shutil.rmtree(self.kp_ws, ignore_errors=True) self.init() def tearDown(self): if self.agent is not None: self.agent.terminate() for env in self.agent.env: del os.environ[env] self.agent = None # Env def editor(self, editor, env=None): os.environ['EDITOR'] = os.path.join(self.editor_path, KPTestCase.EDITORS[editor]) if env: os.environ['EDITOR_ENV'] = env # Assertions def assertStdoutEquals(self, *refs): lines = [l for l in self.stdout.splitlines() if len(l.strip()) > 0] self.assertEqual(lines, list(refs)) def assertStdoutContains(self, *refs): lines = [l for l in self.stdout.splitlines() if len(l.strip()) > 0] self.assertEqual(collections.Counter(lines), collections.Counter(list(refs))) def assertSafeExists(self, safe): self.assertTrue(os.path.isfile(os.path.join(self.kp_ws, safe))) def assertSafeDoesntExists(self, safe): self.assertFalse(os.path.isfile(os.path.join(self.kp_ws, safe))) def assertSafeIsBigEnough(self, safe, size=20): self.assertGreater(os.path.getsize(os.path.join(self.kp_ws, safe)), size) def assertClearTextExists(self): self.assertTrue(os.path.isfile(self.clear_text)) def assertClearTextEquals(self, *refs): with open(self.clear_text) as f: self.assertEqual(f.read().splitlines(), list(refs)) def assertWsExists(self, sub=None): path = self.kp_ws if sub is not None: path = os.path.join(path, sub) self.assertTrue(os.path.isdir(path)) # Run commands def cmd(self, args, master=None, confirm_master=False, password=None, confirm_password=False, yesno=None, rc=0): options = {"master":master, "confirm_master":confirm_master, "password":password, "confirm_password":confirm_password, "yesno":yesno} logging.info(" ".join([self.kp]+args) + " [" + ", ".join(["{}={}".format(k, v) for k, v in options.items()]) + "]") self.stdout = "" cmd = [self.kp] + args if "VALGRIND_COMMAND" in os.environ: for i in range(1, 256): if i != rc: valgrind_rc = i break cmd = [os.environ["VALGRIND_COMMAND"], "--log-file=valgrind-"+self.id()+".log", "--error-exitcode={}".format(valgrind_rc)] \ + shlex.split(os.environ.get("VALGRIND_OPTIONS", "")) + cmd self.child = pexpect.spawn(cmd[0], cmd[1:]) if master is not None: self.child.expect('password:') self.child.sendline(master) if confirm_master: self.child.expect('confirm:') self.child.sendline(master) if password is not None: self.child.expect('password:') self.child.sendline(password) if confirm_password: self.child.expect('confirm:') self.child.sendline(password) if yesno is not None: self.child.expect('(y/n)') self.child.sendline(yesno) for line in self.child: self.stdout = self.stdout + (line.decode(sys.stdin.encoding) if sys.stdin.encoding is not None else line) self.child.wait() self.assertEqual(self.child.exitstatus, rc) def start_agent(self): self.agent = KPAgent(self.kp) def stop_agent(self): res = self.agent.poll() self.assertIsNone(res) self.agent.terminate() res = self.agent.wait() self.agent.stdout.close() self.assertIsNotNone(res) for env in self.agent.env: del os.environ[env] self.agent = None def init(self, path=None, master="test master password", rc=0): cmd = ['init', '--memlimit', '16777216', '--opslimit', '32768'] if path is not None: cmd.append(path) self.cmd(cmd, master=master, confirm_master=True, rc=rc) def create(self, name, options=None, master="test master password", password="test password", rc=0): cmd = ['create'] if options: cmd = cmd + options self.cmd(cmd + [name], master=master, confirm_master=False, password=password, confirm_password=True, rc=rc) def edit(self, name, options=None, master="test master password", password="test password", yesno=None, rc=0): cmd = ['edit'] if options: cmd = cmd + options self.cmd(cmd + [name], master=master, confirm_master=False, password=password, confirm_password=True, yesno=yesno, rc=rc) def rename(self, old, new, options=None, master="test master password", rc=0): cmd = ['rename'] if options: cmd = cmd + options self.cmd(cmd + [old, new], master=master, confirm_master=False, rc=rc) def cat(self, name, options=None, master="test master password", rc=0): cmd = ['cat'] if options: cmd = cmd + options self.cmd(cmd + [name], master=master, confirm_master=False, rc=rc) def delete(self, name, options=None, master="test master password", rc=0): cmd = ['delete'] if options: cmd = cmd + options self.cmd(cmd + [name], master=master, confirm_master=False, rc=rc) def open(self, name, options=None, master="test master password", rc=0): cmd = ['open'] if options: cmd = cmd + options self.cmd(cmd + [name], master=master, confirm_master=False, rc=rc) kickpass-0.2.0/test/list.py000066400000000000000000000041161310455076600156360ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import unittest import kptest class TestListCommand(kptest.KPTestCase): def test_list_is_complete(self): # Given self.editor('date') self.create("subdir/test") self.create("subdir/other") # When self.cmd(["ls"]) # Then self.assertStdoutContains("subdir/other", "subdir/test") def test_list_is_sorted(self): # Given self.editor('date') self.create("subdir/test") self.create("subdir/other") # When self.cmd(["ls"]) # Then self.assertStdoutEquals("subdir/other", "subdir/test") def test_list_subpath(self): # Given self.editor('date') self.create("withoutdir") self.create("subdir/test") self.create("subdir/other") self.create("important/doh") self.create("important/lastone") self.create("boringdir/stuff") self.create("boringdir/things") # When self.cmd(["ls", "subdir", "important"]) # Then self.assertStdoutEquals("subdir/", " other", " test", "important/", " doh", " lastone") if __name__ == '__main__': unittest.main() kickpass-0.2.0/test/open.py000066400000000000000000000027011310455076600156220ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import unittest import kptest import time class TestOpenCommand(kptest.KPTestCase): @kptest.with_agent def test_open_timeout_remove_password_from_agent(self): # Given self.editor('env', env="Watch out for turtles. They'll bite you if you put your fingers in their mouths.") self.create("test") self.open("test", options=['-t', '1']) # When time.sleep(2) # cat should ask for password, thus master param is not set to None self.cat("test") # Then self.assertStdoutEquals("Watch out for turtles. They'll bite you if you put your fingers in their mouths.") if __name__ == '__main__': unittest.main() kickpass-0.2.0/test/password-confirm.expect000066400000000000000000000002331310455076600210140ustar00rootroot00000000000000spawn $::env(KP) {*}$argv expect { password: { send "test password\n" } } expect { confirm: { send "test password\n" } } expect { eof { puts [wait] } } kickpass-0.2.0/test/password.expect000066400000000000000000000001531310455076600173620ustar00rootroot00000000000000spawn $::env(KP) {*}$argv expect { password: { send "test password\n" } } expect { eof { puts [wait] } } kickpass-0.2.0/test/rename.py000066400000000000000000000045121310455076600161320ustar00rootroot00000000000000# # Copyright (c) 2015 Paul Fariello # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # import unittest import kptest class TestRenameCommand(kptest.KPTestCase): def test_rename_is_successful(self): # Given self.editor('date') self.create("old") # When self.rename("old", "new") # Then self.assertSafeDoesntExists("old") self.assertSafeExists("new") self.assertSafeIsBigEnough("new") def test_rename_on_nonexistent_directory_is_successful(self): # Given self.editor('date') self.create("old") # When self.rename("old", "nonexistent/new") # Then self.assertSafeDoesntExists("old") self.assertSafeExists("nonexistent/new") self.assertSafeIsBigEnough("nonexistent/new") def test_rename_with_agent_is_successful(self): # Given self.start_agent() self.editor('env', env="But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") self.create("old", password="42", options=["-o"]) # When self.rename("old", "new") # Then self.cat("new", master=None, options=["-pm"]) self.assertStdoutEquals("42", "But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") # When self.stop_agent() self.cat("new", options=["-pm"]) self.assertStdoutEquals("42", "But a RocknRolla, oh, he's different. Why? Because a real RocknRolla wants the fucking lot.") if __name__ == '__main__': unittest.main() kickpass-0.2.0/test/safe.c000066400000000000000000000032321310455076600153710ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "check_compat.h" #include "../lib/storage.c" #include "../lib/safe.c" START_TEST(test_safe_get_path) { /* Given */ struct kp_ctx ctx; struct kp_safe safe; char path[PATH_MAX]; strlcpy(ctx.ws_path, "/home/user/.kickpass", PATH_MAX); strlcpy(safe.name, "dir/safe", PATH_MAX); /* When */ kp_safe_get_path(&ctx, &safe, path, PATH_MAX); /* Then */ ck_assert_str_eq(path, "/home/user/.kickpass/dir/safe"); } END_TEST int main(int argc, char **argv) { int number_failed; Suite *suite = suite_create("safe_test_suite"); TCase *tcase = tcase_create("case"); tcase_add_test(tcase, test_safe_get_path); suite_add_tcase(suite, tcase); SRunner *runner = srunner_create(suite); srunner_set_fork_status(runner, CK_NOFORK); srunner_run_all(runner, CK_VERBOSE); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return number_failed; } kickpass-0.2.0/test/storage.c000066400000000000000000000160731310455076600161260ustar00rootroot00000000000000/* * Copyright (c) 2015 Paul Fariello * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include "check_compat.h" #include "../lib/safe.c" #include "../lib/storage.c" START_TEST(test_storage_header_pack_should_be_successful) { /* Given */ struct kp_storage_header header = KP_STORAGE_HEADER_INIT; unsigned char packed_header[KP_STORAGE_HEADER_SIZE] = { 0 }; unsigned char salt[] = { 0x12, 0x10, 0xcb, 0x68, 0x45, 0xeb, 0xc7, 0x6a, 0x7b, 0x91, 0x00, 0xcf, 0xed, 0x42, 0xc8, 0xcf, 0xcb, 0x66, 0x50, 0xd1, 0x04, 0x2e, 0xe8, 0x81, 0xcb, 0x5f, 0x96, 0x4c, 0xe8, 0x65, 0x1e, 0x2c, }; unsigned char nonce[] = { 0xe6, 0x59, 0x12, 0x7a, 0xf5, 0x7d, 0xfc, 0xf8, }; header.version = 0xdead; header.sodium_version = 0xbaad; header.opslimit = 0x71f97b79931b97d8LL; header.memlimit = 0x50b77cc354846208LL; memcpy(header.salt, salt, KP_STORAGE_SALT_SIZE); memcpy(header.nonce, nonce, KP_STORAGE_NONCE_SIZE); /* When */ kp_storage_header_pack(&header, packed_header); /* Then */ unsigned char ref[KP_STORAGE_HEADER_SIZE] = { 0xde, 0xad, 0xba, 0xad, 0x71, 0xf9, 0x7b, 0x79, 0x93, 0x1b, 0x97, 0xd8, 0x50, 0xb7, 0x7c, 0xc3, 0x54, 0x84, 0x62, 0x08, 0x12, 0x10, 0xcb, 0x68, 0x45, 0xeb, 0xc7, 0x6a, 0x7b, 0x91, 0x00, 0xcf, 0xed, 0x42, 0xc8, 0xcf, 0xcb, 0x66, 0x50, 0xd1, 0x04, 0x2e, 0xe8, 0x81, 0xcb, 0x5f, 0x96, 0x4c, 0xe8, 0x65, 0x1e, 0x2c, 0xe6, 0x59, 0x12, 0x7a, 0xf5, 0x7d, 0xfc, 0xf8, }; ck_assert_int_eq(memcmp(packed_header, ref, KP_STORAGE_HEADER_SIZE), 0); } END_TEST START_TEST(test_storage_header_unpack_should_be_successful) { /* Given */ struct kp_storage_header header = KP_STORAGE_HEADER_INIT; unsigned char packed_header[KP_STORAGE_HEADER_SIZE] = { 0xaa, 0xd0, 0xe5, 0x23, 0x3a, 0xcf, 0xd7, 0xa6, 0xd0, 0x54, 0x21, 0xc0, 0x6a, 0x26, 0xf8, 0x1b, 0x96, 0x7f, 0x6d, 0x9b, 0x52, 0x21, 0x1e, 0x1c, 0x1d, 0x89, 0x49, 0x60, 0xc2, 0x42, 0x3a, 0x0d, 0xc2, 0x5f, 0xe8, 0x2c, 0xd0, 0xb6, 0x07, 0xcd, 0x33, 0xd1, 0xbc, 0x2d, 0x2b, 0x4a, 0x5a, 0x84, 0x69, 0x02, 0x12, 0xa3, 0x6e, 0x22, 0xa3, 0x28, 0x93, 0x0a, 0xb6, 0xb6, }; /* When */ kp_storage_header_unpack(&header, packed_header); /* Then */ unsigned char salt[] = { 0x52, 0x21, 0x1e, 0x1c, 0x1d, 0x89, 0x49, 0x60, 0xc2, 0x42, 0x3a, 0x0d, 0xc2, 0x5f, 0xe8, 0x2c, 0xd0, 0xb6, 0x07, 0xcd, 0x33, 0xd1, 0xbc, 0x2d, 0x2b, 0x4a, 0x5a, 0x84, 0x69, 0x02, 0x12, 0xa3, }; unsigned char nonce[] = { 0x6e, 0x22, 0xa3, 0x28, 0x93, 0x0a, 0xb6, 0xb6, }; ck_assert_int_eq(header.version, 0xaad0); ck_assert_int_eq(header.sodium_version, 0xe523); ck_assert(header.opslimit == 0x3acfd7a6d05421c0LL); ck_assert(header.memlimit == 0x6a26f81b967f6d9bLL); ck_assert_int_eq(memcmp(header.salt, salt, KP_STORAGE_SALT_SIZE), 0); ck_assert_int_eq(memcmp(header.nonce, nonce, KP_STORAGE_NONCE_SIZE), 0); } END_TEST START_TEST(test_storage_encrypt_should_be_successful) { /* Given */ int ret = KP_SUCCESS; struct kp_ctx ctx; char **password; struct kp_storage_header header = KP_STORAGE_HEADER_INIT; unsigned char packed_header[KP_STORAGE_HEADER_SIZE] = { 0 }; unsigned char plain[] = "the quick brown fox jumps over the lobster dog"; unsigned char cipher[sizeof(plain)+crypto_aead_chacha20poly1305_ABYTES] = { 0 }; unsigned long long cipher_size; header.version = 0xde; header.sodium_version = 0xad; header.opslimit = crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE; header.memlimit = crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE; /* keep header.salt = { 0 } */ /* keep header.nonce = { 0 } */ password = (char **)&ctx.password; *password = "test"; /* When */ ret |= kp_storage_encrypt(&ctx, &header, packed_header, KP_STORAGE_HEADER_SIZE, plain, sizeof(plain), cipher, &cipher_size); /* Then */ unsigned char ref[] = { 0x01, 0x82, 0xbf, 0x1f, 0x22, 0x57, 0x3a, 0x63, 0xa3, 0x22, 0x03, 0x3e, 0x85, 0x73, 0xcd, 0x18, 0x2d, 0x2a, 0x36, 0xf7, 0x94, 0x95, 0x53, 0x47, 0x8a, 0x2f, 0x3f, 0xfc, 0xda, 0x88, 0x0e, 0xf1, 0x04, 0x64, 0xe3, 0xb0, 0xf2, 0xbf, 0xee, 0x20, 0x3a, 0x79, 0x88, 0x6e, 0x1a, 0x8c, 0xa7, 0xd8, 0x04, 0x8c, 0x66, 0x60, 0x32, 0xec, 0xc8, 0xa0, 0xe9, 0x7c, 0x21, 0xd5, 0xf3, 0xfe, 0x44, }; ck_assert_int_eq(ret, KP_SUCCESS); ck_assert_int_eq(cipher_size, sizeof(ref)); ck_assert_int_eq(memcmp(cipher, ref, sizeof(ref)), 0); } END_TEST START_TEST(test_storage_decrypt_should_be_successful) { /* Given */ int ret = KP_SUCCESS; struct kp_ctx ctx; char **password; struct kp_storage_header header = KP_STORAGE_HEADER_INIT; unsigned char packed_header[KP_STORAGE_HEADER_SIZE] = { 0 }; unsigned char cipher[] = { 0x01, 0x82, 0xbf, 0x1f, 0x22, 0x57, 0x3a, 0x63, 0xa3, 0x22, 0x03, 0x3e, 0x85, 0x73, 0xcd, 0x18, 0x2d, 0x2a, 0x36, 0xf7, 0x94, 0x95, 0x53, 0x47, 0x8a, 0x2f, 0x3f, 0xfc, 0xda, 0x88, 0x0e, 0xf1, 0x04, 0x64, 0xe3, 0xb0, 0xf2, 0xbf, 0xee, 0x20, 0x3a, 0x79, 0x88, 0x6e, 0x1a, 0x8c, 0xa7, 0xd8, 0x04, 0x8c, 0x66, 0x60, 0x32, 0xec, 0xc8, 0xa0, 0xe9, 0x7c, 0x21, 0xd5, 0xf3, 0xfe, 0x44, }; unsigned char plain[sizeof(cipher)-crypto_aead_chacha20poly1305_ABYTES] = { 0 }; unsigned long long plain_size; header.version = 0xde; header.sodium_version = 0xad; header.opslimit = crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE; header.memlimit = crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE; /* keep header.salt = { 0 } */ /* keep header.nonce = { 0 } */ password = (char **)&ctx.password; *password = "test"; /* When */ ret |= kp_storage_decrypt(&ctx, &header, packed_header, KP_STORAGE_HEADER_SIZE, plain, &plain_size, cipher, sizeof(cipher)); /* Then */ unsigned char ref[] = "the quick brown fox jumps over the lobster dog"; ck_assert_int_eq(ret, KP_SUCCESS); ck_assert_int_eq(plain_size, sizeof(ref)); ck_assert_str_eq((char *)plain, (char *)ref); } END_TEST int main(int argc, char **argv) { int number_failed; Suite *suite = suite_create("storage_test_suite"); TCase *tcase = tcase_create("case"); tcase_add_test(tcase, test_storage_header_pack_should_be_successful); tcase_add_test(tcase, test_storage_header_unpack_should_be_successful); tcase_add_test(tcase, test_storage_encrypt_should_be_successful); tcase_add_test(tcase, test_storage_decrypt_should_be_successful); suite_add_tcase(suite, tcase); SRunner *runner = srunner_create(suite); srunner_set_fork_status(runner, CK_NOFORK); srunner_run_all(runner, CK_VERBOSE); number_failed = srunner_ntests_failed(runner); srunner_free(runner); return number_failed; }