From e87d72d9220cd18651195c00995e87a7ac7bc8db Mon Sep 17 00:00:00 2001 From: "Sungjun, Lee" Date: Wed, 23 Nov 2016 15:09:01 +0900 Subject: [PATCH] Add eCryptfs engine Change-Id: I5cb65125d41f92413d68e79d011b243b65653a51 Signed-off-by: Sungjun, Lee --- server/engine/ecryptfs-engine.cpp | 983 +++++++++++++++++++++++++++++- server/engine/ecryptfs-engine.h | 195 +++++- 2 files changed, 1161 insertions(+), 17 deletions(-) diff --git a/server/engine/ecryptfs-engine.cpp b/server/engine/ecryptfs-engine.cpp index c7f97ff..328fed4 100644 --- a/server/engine/ecryptfs-engine.cpp +++ b/server/engine/ecryptfs-engine.cpp @@ -20,8 +20,8 @@ namespace ode { -EcryptfsEngine::EcryptfsEngine(const std::string& src, const std::string& dest) : - source(src), destination(dest) +EcryptfsEngine::EcryptfsEngine(const std::string &src, const std::string &dest) : + mSource(src), mDestination(dest) { } @@ -29,28 +29,991 @@ EcryptfsEngine::~EcryptfsEngine() { } -void EcryptfsEngine::mount(const EcryptfsEngine::data& key) +key_serial_t EcryptfsEngine::add_key(const char *type, + const char *description, + const void *payload, + size_t plen, + key_serial_t ringid) { - //TODO + return ::syscall(__NR_add_key, type, description, payload, plen, ringid); } -void EcryptfsEngine::umount() +long EcryptfsEngine::keyctl_search(key_serial_t ringid, + const char *type, + const char *description, + key_serial_t destringid) { - //TODO + return ::syscall(__NR_keyctl, KEYCTL_SEARCH, ringid, type, description, destringid); } -void EcryptfsEngine::encrypt(const EcryptfsEngine::data& key) +void EcryptfsEngine::hexConvert(char *dest, unsigned char *src, int srcLen) { - //TODO + for (int i = 0; i < srcLen; i++) + ::sprintf(&dest[i * 2], "%.2x", (unsigned char)src[i]); + dest[srcLen * 2] = '\0'; } +int EcryptfsEngine::add_user_auth_token_to_keyring(ecryptfs_payload *payload) +{ + int result = ERR_NO; + + if (keyctl_search(KEY_SPEC_USER_KEYRING, AUTH_TOKEN_TYPE, + (const char *)payload->token.password.signature, 0) <= 0) { + if (add_key(AUTH_TOKEN_TYPE, (const char *)payload->token.password.signature, + (void *)payload, sizeof(ecryptfs_payload), KEY_SPEC_USER_KEYRING) <= 0) { + result = ERR_FAILED_AUTH; + } else { + std::cout << "Token added = " << payload->token.password.signature << std::endl; + } + } + + return result; +} + +int EcryptfsEngine::smack_check(void) +{ + struct statfs sfs; + int ret; + int checksmack = 0; + + do { + ret = ::statfs(SMACKFS_MNT, &sfs); + } while (ret < 0 && errno == EINTR); + + if (ret == 0 && sfs.f_type == SMACKFS_MAGIC) + checksmack = 1; + return checksmack; +} + +void EcryptfsEngine::build_ecryptfs_options(char *ecryptfs_opts, char *sig, int excludeMediaTypes) +{ + ::sprintf(ecryptfs_opts, ECRYPTFS_PLAINTEXT_PASSTHOUGH"," + ECRYPTFS_SIGNATURE"%s," + ECRYPTFS_CIPHERA"%s," + ECRYPTFS_KEY_BYTES"%d", sig, ECRYPTFS_FEK_CIPHER, MAX_KEY_BYTES); + + if (excludeMediaTypes == EXCL_MEDIA_ON) { + std::cout << "building options with media files filtering." << std::endl; + ::strcat(ecryptfs_opts, ","); + ::strcat(ecryptfs_opts, ECRYPTFS_ENABLE_FILTERING); + ::strcat(ecryptfs_opts, ECRYPTFS_MEDIA_EXCLUSION_LIST); + } else if (excludeMediaTypes == EXCL_ALL_NEW_ON) { + std::cout << "building options with all new files filtering." << std::endl; + ::strcat(ecryptfs_opts, ","); + ::strcat(ecryptfs_opts, ECRYPTFS_ENABLE_FILTERING); + ::strcat(ecryptfs_opts, ECRYPTFS_GLOBAL_EXCLUSION_LIST); + } else { + std::cout << "building options without file encryption filtering." << std::endl; + } + if (smack_check()) { + std::cout << "smack fs was enabled, add smack labeling" << std::endl; + ::strcat(ecryptfs_opts, ","); + ::strcat(ecryptfs_opts, SMACKFS_MOUNT_OPT); + } +} + +int EcryptfsEngine::mount(const data &key) +{ + int result = ERR_NO; + unsigned char master_key[MAX_KEY_BYTES] = {0}; + char ecryptfs_opts[ECRYPTFS_MAX_OPTIONS] = {0}; + std::cout << "Source: " << mSource << std::endl; + std::cout << "Destination: " << mDestination << std::endl; + std::cout << "EcryptfsMount: "; + if (key.size() == 0) + return -1; + + for (unsigned int iter = 0; iter < key.size(); iter++) { + master_key[iter] = key[iter]; + } + + if (isEcryptfsMountpointMounted(mDestination) == 0) { + std::cout << "Already Mounted" << std::endl; + return 0; + } + + //MOUNT_ECRYPTFS_DRIVE + memset(&(sde_payload), 0, sizeof(ecryptfs_payload)); + + sde_payload.version = ECRYPTFS_VERSION; + sde_payload.token_type = ECRYPTFS_PWD_PAYLOAD_TYPE; + sde_payload.token.password.session_key_encryption_key_bytes = MAX_KEY_BYTES; + sde_payload.token.password.flags = ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET; + memcpy(sde_payload.token.password.session_key_encryption_key, master_key, MAX_KEY_BYTES); + + hexConvert((char *)sde_payload.token.password.signature, master_key, SIG_SIZE); + + result = add_user_auth_token_to_keyring(&sde_payload); + if (result == ERR_NO) { + build_ecryptfs_options(ecryptfs_opts, (char *)sde_payload.token.password.signature, mExcludeMedia); + std::cout << "encrypted fs option : " << ecryptfs_opts << ", source : " << mSource << ", dest : " << mDestination << std::endl; + if (::mount(mSource.c_str(), mDestination.c_str(), ECRYPTFS_MOUNT_DEVICE, MS_NODEV, ecryptfs_opts) != 0) { + std::cout << "Unable to mount drive: " << strerror(errno) << std::endl; + result = ERR_MOUNT; + } else { + std::cout << "Mount is completed." << std::endl; + } + } else { + std::cout << "Unable to add token to keyring" << std::endl; + } + + return result; +} + +int EcryptfsEngine::umount() +{ + int result = ERR_NO; + + std::cout << "EcryptfsUmount" << std::endl; + if (isEcryptfsMountpointMounted(mDestination) == 0) { + if (::umount(mDestination.c_str()) != 0) { + if (::umount2(mDestination.c_str(), MNT_EXPIRE) != 0) { + std::cout << "Unmount failed for drive " << mDestination.c_str() << "err(" << errno << strerror(errno) << ")" << std::endl; + if (errno == EAGAIN) { + std::cout << "Trying Unmount again" << std::endl; + if (::umount2(mDestination.c_str(), MNT_EXPIRE) != 0) { + result = ERR_UNMOUNT; + std::cout << "Unmount failed for drive" << mDestination.c_str() << "err(" << errno << strerror(errno) << ")" << std::endl; + } + } else { + std::cout << "Drive " << mDestination.c_str() << "unmounted failed " << std::endl; + result = ERR_UNMOUNT; + } + } + } + + if (result == ERR_NO) + std::cout << "Drive " << mDestination.c_str() << " unmounted successfully" << std::endl; + else + std::cout << "Drive " << mDestination.c_str() << " unmounted failed" << std::endl; + } + return result; +} + +int EcryptfsEngine::encrypt(const data &key) +{ + int result = ERR_NO; + + std::cout << "EcryptfsEncrypt" << std::endl; + if (!isMountpointMounted(mSource)) { + std::cout << "SD Card not inserted!" << std::endl; + return -1; + } + + result = DoCrypt(key, mDestination.c_str(), 1, EXCL_MEDIA_ON); + + return result; +} + +int EcryptfsEngine::decrypt(const data &key) +{ + int result = ERR_NO; + + std::cout << "EcryptfsDecrypt" << std::endl; + if (!isMountpointMounted(mSource)) { + std::cout << "SD Card not inserted!" << std::endl; + return -1; + } + + result = DoCrypt(key, mDestination.c_str(), 0, EXCL_ALL_NEW_ON); + return result; +} + +int EcryptfsEngine::DoCrypt(const data &key, const char *path, int reqEnc, int excludeMedia) +{ + const char *cryptTempFile = CRYPT_META_FILE; + std::cout << "DoCrypt: reqEnc: " << reqEnc << ", excludeMedia: " << excludeMedia << std::endl; + + mPath = path; + mReqEnc = reqEnc; + mExcludeMedia = excludeMedia; + mMetaDataFile = new char[::strlen(mPath) +::strlen(cryptTempFile) + 2]; + if (mMetaDataFile) { + ::sprintf(mMetaDataFile, "%s%s%s", mPath, "/", cryptTempFile); + } + + if (reqEnc) + DoEncrypt(key); + else + DoDecrypt(key); + + return 0; +} + +int EcryptfsEngine::DoEncrypt(const data &key) +{ + std::cout << "DoEncrypt()" << std::endl; + + if (mount(key) != ERR_NO) { + std::cout << "EcryptfsEngine: Error mounting " << mPath << std::endl; + goto error; + } + + if (createEncryptMetaData(mMetaDataFile) != 0) { + goto error; + } + + if (preScanForEncrypt(mPath) != 0) { + goto error; + } + + if (mTotalFileCt) { + mTotalCopied = 0; + std::cout << "calling the recursive function EncryptFile (" << mPath << ")" << std::endl; + if (cryptInplace(mPath, "", false) != 0) { + std::cout << "Ecryptfs: Full Encryption couldn't complete" << std::endl; + goto error; + } + } + + return 0; +error: + deleteEncryptMetaData(mMetaDataFile); + umount(); + return -1; +} + +int EcryptfsEngine::DoDecrypt(const data &key) +{ + std::cout << "DoDecrypt()" << std::endl; + + if (checkEncryptMetaData(ORIG_META_FILE_PATH) != 0) { + goto success; + } + + if (mount(key) != ERR_NO) { + std::cout << "EcryptfsEngine: Error mounting " << mPath << std::endl; + goto error; + } + + if (preScanForDecrypt(mPath) != 0) { + goto error; + } + + if (mTotalFileCt) { + mTotalCopied = 0; + std::cout << "calling the recursive function EncryptFile (" << mPath << ")" << std::endl; + if (cryptInplace(mPath, "", true) != 0) { + std::cout << "Ecryptfs: Full Decryption couldn't complete" << std::endl; + goto error; + } + } + + deleteEncryptMetaData(mMetaDataFile); +success: + std::cout << "Decryption Completed !!!" << std::endl; + umount(); + return 0; +error: + umount(); + return -1; +} + +long long EcryptfsEngine::CopyImpl(int sfd, int dfd, long long fullsz, bool enctype) +{ + long long ret = -1; + long long total = 0; + + char buffer [ECRYPTFS_BUFFER_SIZE]; + + std::cout << "CopyImpl" << std::endl; + while (1) { + ssize_t rdsz = 0; + + rdsz = FullRead(sfd, buffer, ECRYPTFS_BUFFER_SIZE); + if (!rdsz) { + ret = 0; + break; + } + + if (rdsz < 0) { + std::cout << "Error reading src file" << std::endl; + break; + } + + if (dfd >= 0) { + ssize_t wrsz = FullWrite(dfd, buffer, rdsz); + if (wrsz < rdsz) { + std::cout << "Write Error" << std::endl; + break; + } + } + total += rdsz; + mTotalCopied += rdsz; + } + + return ret ? -1 : total; +} + +ssize_t EcryptfsEngine::FullRead(int fd, void *buf, size_t count) +{ + ssize_t n; + + do { + n = ::read(fd, buf, count); + } while (n < 0 && errno == EINTR); + + return n; +} +ssize_t EcryptfsEngine::FullWrite(int fd, const void *buf, size_t len) +{ + ssize_t total = 0; + + while (len) { + ssize_t n; + + do { + n = ::write(fd, buf, len); + } while (n < 0 && errno == EINTR); + + if (n < 0) { + if (total) + return total; + return n; + } + + total += n; + buf = ((const char *)buf) + n; + len -= n; + } + + return total; +} + +int EcryptfsEngine::CopyFile(const char *src, const char *dest, struct stat *src_stat, bool enctype) +{ + int sfd, dfd; + struct utimbuf times; + long long retval = 0; + + std::cout << "Copy start " << src << " => " << dest << std::endl; + + sfd = ::open(src, O_RDONLY, 0666); + + if (sfd < 0) { + std::cout << "Cann't open " << src << std::endl; + return -1; + } + + dfd = ::open(dest, O_WRONLY | O_CREAT | O_TRUNC, src_stat->st_mode); + if (dfd < 0) { + std::cout << "Error opening the destination file " << dest << std::endl; + ::close(sfd); + return -1; + } + + retval = CopyImpl(sfd, dfd, src_stat->st_size, enctype); + + if (retval < 0) { + std::cout << "Encryption Error CopyData returned <" << retval << "> : <" << src << "> errno " << errno << " " << strerror(errno) << std::endl; + ::close(sfd); + ::close(dfd); + return retval; + } + + times.actime = src_stat->st_atime; + times.modtime = src_stat->st_mtime; + + if (::utime(dest, ×) < 0) + std::cout << "can't preserve times of '" << src << "'" << std::endl; + + std::cout << "Copy Completed" << std::endl; + + if (::fsync(dfd) != 0) + std::cout << "can't fsync of '" << src << "'" << std::endl; + if (::posix_fadvise(dfd, 0, src_stat->st_size, POSIX_FADV_DONTNEED) < 0) + std::cout << "can't fadvise of '" << dest << "'" << std::endl; + + ::close(sfd); + ::close(dfd); + + return 0; +} + +void EcryptfsEngine::syncdatafile(const char *src) +{ + int sfd; + sfd = ::open(src, O_WRONLY); + if (sfd < 0) { + std::cout << "Cann't open " << src << std::endl; + return; + } + if (::fsync(sfd) != 0) + std::cout << "can't fsync of '" << src << "'" << std::endl; + /* just open and close for flushing file to ecryptfs */ + ::close(sfd); +} +int EcryptfsEngine::cryptInplace(const char *src, const char *tmpdest, bool dec_fl) +{ + struct stat src_stat; + int retval = 0; + int enc_fl = 0; + + if (::lstat(src, & src_stat) < 0) + std::cout << "source : < " << src << "> is a dangling link" << std::endl; + + if (S_ISDIR(src_stat.st_mode)) { + /* dir call recursively */ + DIR *dp; + struct dirent *d; + + dp = ::opendir(src); + if (NULL == dp) { + retval = -1; + return retval; + } + + while ((d = ::readdir(dp)) != NULL) { + char *nsrc = NULL; + char *ntmpdest = NULL; + + nsrc = catSubpathFile(src, d->d_name, &ntmpdest); + if (nsrc == NULL) { + if (ntmpdest != NULL) delete [] ntmpdest; + continue; + } + + if (cryptInplace(nsrc, ntmpdest, dec_fl) < 0) { + std::cout << "Encryptfile returned error for nsrc <" << nsrc << ">" << std::endl; + retval = -1; + delete [] ntmpdest; + delete [] nsrc; + break; + } + delete [] ntmpdest; + delete [] nsrc; + } + ::closedir(dp); + } else if (S_ISREG(src_stat.st_mode)) { + enc_fl = fn_was_encrypted(src); + + if (enc_fl == WAS_ECRYPTED_ERROR) + return enc_fl; + if ((!dec_fl && enc_fl == WAS_NOT_ENCRYPTED) || + (dec_fl && enc_fl == WAS_ENCRYPTED)) { + std::cout << "ENC/DEC " << src << std::endl; -void EcryptfsEngine::decrypt(const EcryptfsEngine::data& key) + retval = CopyFile(src, tmpdest, &src_stat, dec_fl); + if (retval) { + std::cout << "CopyFileImpl returned error <" << retval << "> errno " << errno << strerror(errno) << std::endl; + return retval; + } + ::unlink(src); + retval = ::rename(tmpdest, src); + syncdatafile(src); + if (retval) { + std::cout << "rename returned error <" << retval << "> errno " << errno << strerror(errno) << std::endl; + return retval; + } + } else + std::cout << "File already encrpyted/Decrypted : <" << src << ">" << std::endl; + } + + return retval; +} + +int EcryptfsEngine::preScanForEncrypt(const char *src) +{ + sizeinfo_type szinfo; + struct statfs statbuf; + int result = ERR_NO; + + std::cout << "preScanForEncrypt" << std::endl; + do { + ::memset(&szinfo, 0, sizeof(sizeinfo_type)); + if (!::statfs(src, &statbuf)) { + szinfo.availsz = (long long)statbuf.f_bfree * statbuf.f_bsize; + szinfo.blocksz = (int)statbuf.f_bsize; + std::cout << "Free size available from statfs : <" << szinfo.availsz << ">" << std::endl; + } else { + std::cout << "can't access " << src << std::endl; + return PRESCAN_ERR; + } + + mPreScanEncryptErr = 0; + result = getEncryptedSize(src, &szinfo); + + std::cout << "Prescan Free <" << szinfo.availsz << ">, source size <" << szinfo.cursz << ">, Source Encrypted size <" << szinfo.encsz << "> largeFileSz <" + << szinfo.largesz << "> filecount <" << szinfo.filecount << "> for <" << src << ">" << std::endl; + std::cout << "retval <" << result << "> PrescanencryptErr <" << mPreScanEncryptErr << ">" << std::endl; + + mTotalFileCt = szinfo.filecount; + mTotalStSz = szinfo.totalstsz; + + if (mPreScanEncryptErr == PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR) { + result = PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR; + } else if ((mPreScanEncryptErr == PRESCAN_ERR) || (result == PRESCAN_ERR)) { + int total = (szinfo.encsz + szinfo.largesz - szinfo.cursz + 12 * 1024) / 1024; + int needed = total - szinfo.availsz / 1024; + + if (isEcryptfsMountpointMounted(mPath)) { + umount(); + std::cout << "Unmounting mpath <" << mPath << ">" << std::endl; + } + + std::cout << "Prescan failed with Too full error, need more space, " << needed << " extra, total " << total << std::endl; + result = PRESCAN_ERR; + } + + if (result == PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR) { + std::cout << "EcryptfsEngine: Need to run Prescan Again" << std::endl; + continue; + } + + if (result == 0) { + std::cout << "EcryptfsEngine: Prescan completed successfully" << std::endl; + return result; + } + + std::cout << "EcryptfsEngine: Cannot encrypt, Disk Space is not enough" << std::endl; + return PRESCAN_ERR; + } while (1); +} + +int EcryptfsEngine::preScanForDecrypt(const char *src) +{ + sizeinfo_type szinfo; + struct statfs statbuf; + int result = ERR_NO; + + std::cout << "preScanForDecrypt" << std::endl; + do { + ::memset(&szinfo, 0, sizeof(EcryptfsEngine::sizeinfo_type)); + if (!::statfs(src, &statbuf)) { + szinfo.availsz = (long long)statbuf.f_bfree * statbuf.f_bsize; + szinfo.blocksz = (int)statbuf.f_bsize; + std::cout << "Free size available from statfs : <" << szinfo.availsz << ">" << std::endl; + } else { + std::cout << "can't access " << src << std::endl; + return PRESCAN_ERR; + } + + mPreScanEncryptErr = 0; + result = getDecryptedSize(src, &szinfo); + + std::cout << "Prescan Decrypt Free <" << szinfo.availsz << ">, source size <" << szinfo.cursz << ">, Source Decrypted size <" << szinfo.decsz << "> largeFileSz <" + << szinfo.largesz << "> filecount <" << szinfo.filecount << "> for <" << src << ">" << std::endl; + std::cout << "retval <" << result << "> PrescanencryptErr <" << mPreScanEncryptErr << ">" << std::endl; + + mTotalFileCt = szinfo.filecount; + mTotalStSz = szinfo.totalstsz; + + if (mPreScanEncryptErr == PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR) { + result = PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR; + } else if ((mPreScanEncryptErr == PRESCAN_ERR) || (result == PRESCAN_ERR)) { + int total = (szinfo.encsz + szinfo.largesz - szinfo.cursz + 12 * 1024) / 1024; + int needed = total - szinfo.availsz / 1024; + + if (isEcryptfsMountpointMounted(mPath)) { + umount(); + std::cout << "Unmounting mpath <" << mPath << ">" << std::endl; + } + + std::cout << "Prescan failed with Too full error, need more space, " << needed << " extra, total " << total << std::endl; + result = PRESCAN_ERR; + } + + if (result == PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR) { + std::cout << "EcryptfsEngine: Need to run Prescan Again" << std::endl; + continue; + } + + if (result == 0) { + std::cout << "EcryptfsEngine: Prescan completed successfully" << std::endl; + return 0; + } + + std::cout << "EcryptfsEngine: Cannot encrypt, Disk Space is not enough" << std::endl; + return PRESCAN_ERR; + } while (1); +} + +int EcryptfsEngine::getEncryptedSize(const char *src, EcryptfsEngine::sizeinfo_type *szinfo) +{ + struct stat src_stat; + int result = ERR_NO; + std::cout << "getEncryptedSize: " << src << std::endl; + if (::lstat(src, &src_stat) < 0) + std::cout << "getEncryptedSize: source : <" << src << "> is a dangling link" << std::endl; + if (S_ISDIR(src_stat.st_mode)) { + /* dir call recursively */ + DIR *dp; + struct dirent *d; + + /* TODO check for recursion + * no need to Create dir + * */ + dp = ::opendir(src); + if (NULL == dp) { + result = -1; + std::cout << "getEncryptedSize: opendir return null <" << src << ">" << std::endl; + return result; + } + + while ((d = ::readdir(dp)) != NULL) { + char *nsrc = NULL; + char *ntmpdest = NULL; + + nsrc = catSubpathFile(src, d->d_name, &ntmpdest); + + if (nsrc == NULL) { + if (ntmpdest) delete [] ntmpdest; + continue; + } + + if ((result = getEncryptedSize(nsrc, szinfo)) < 0) { + std::cout << "getEncryptedSize() reutnred error for nsrc <" << nsrc << "> result < " << result << ">" << std::endl; + if (result == PRESCAN_ERR) + mPreScanEncryptErr = result; + } + delete [] ntmpdest; + delete [] nsrc; + } + ::closedir(dp); + } else if (S_ISREG(src_stat.st_mode)) { + long long padded_sz = src_stat.st_size; + long long new_enc_sz = 0; + int enc_fl = fn_was_encrypted(src); + + std::cout << "fn_was_encrypted returned: " << enc_fl << std::endl; + + if (padded_sz % 4096) + padded_sz = (1 + padded_sz / 4096) * 4096; + + if (enc_fl != WAS_NOT_ENCRYPTED) { + std::cout << "Skipping " << src << std::endl; + new_enc_sz = padded_sz; + } else { + std::cout << "PRE ENC " << src << std::endl; + std::cout << "fn_was_encrypted returned: " << enc_fl << std::endl; + new_enc_sz = padded_sz + 2 * 4096; + + if (szinfo->largesz < src_stat.st_size) + szinfo->largesz = src_stat.st_size; + + szinfo->filecount += 1; + szinfo->totalstsz += src_stat.st_size; + } + + // Pad it to block size + if (new_enc_sz % szinfo->blocksz) + new_enc_sz = (1 + new_enc_sz / szinfo->blocksz) * szinfo->blocksz; + + szinfo->encsz += new_enc_sz; + + if ((enc_fl == WAS_NOT_ENCRYPTED) && ((szinfo->encsz - szinfo->cursz) > szinfo->availsz)) { + if (mPreScanEncryptErr == PRESCAN_TEMP_FILE_EXIST_ERR) { + result = PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR; + } else + result = PRESCAN_ERR; + } + + { + long long padded_cursz = src_stat.st_size; + + if (padded_cursz % szinfo->blocksz) + padded_cursz = (1 + padded_cursz / szinfo->blocksz) * szinfo->blocksz; + + szinfo->cursz += padded_cursz; + } + } + + return result; +} + +int EcryptfsEngine::getDecryptedSize(const char *src, EcryptfsEngine::sizeinfo_type *szinfo) +{ + struct stat src_stat; + int result = ERR_NO; + std::cout << "getDecryptedSize: " << src << std::endl; + if (::lstat(src, & src_stat) < 0) + std::cout << "getDecryptedSize: source : <" << src << "> is a dangling link" << std::endl; + + if (S_ISDIR(src_stat.st_mode)) { + /* dir call recursively */ + DIR *dp; + struct dirent *d; + + /* TODO check for recursion + * no need to Create dir + * */ + dp = ::opendir(src); + if (NULL == dp) { + result = -1; + std::cout << "getDecryptedSize: opendir return null <" << src << ">" << std::endl; + return result; + } + + while ((d = ::readdir(dp)) != NULL) { + char *nsrc = NULL; + char *ntmpdest = NULL; + + nsrc = catSubpathFile(src, d->d_name, &ntmpdest); + + if (nsrc == NULL) { + if (ntmpdest != NULL) delete [] ntmpdest; + continue; + } + + if ((result = getDecryptedSize(nsrc, szinfo)) < 0) { + std::cout << "getDecryptedSize() reutnred error for nsrc <" << nsrc << "> result < " << result << ">" << std::endl; + if (result == PRESCAN_ERR) { + mPreScanDecryptErr = result; + } + } + delete [] ntmpdest; + delete [] nsrc; + } + ::closedir(dp); + } else if (S_ISREG(src_stat.st_mode)) { + long long decsize = 0; + long long paddedsize = 0; + long long paddedstsz = 0; + int enc_fl = fn_was_encrypted(src); + + std::cout << "fn_was_encrypted returned: " << enc_fl << std::endl; + + if (src_stat.st_size > 2 * 4096) + decsize = src_stat.st_size - 2 * 4096; + + paddedsize = decsize; + if (decsize % szinfo->blocksz) + paddedsize = (1 + decsize / szinfo->blocksz) * szinfo->blocksz; + + paddedstsz = src_stat.st_size; + if (paddedstsz % szinfo->blocksz) + paddedstsz = (1 + paddedstsz / szinfo->blocksz) * szinfo->blocksz; + + if (enc_fl == WAS_ENCRYPTED) { + std::cout << "PRE ENC " << src << std::endl; + szinfo->filecount += 1; + szinfo->totalstsz += src_stat.st_size; + + if (szinfo->largesz < src_stat.st_size) + szinfo->largesz = src_stat.st_size; + + if ((decsize > szinfo->availsz) || + (!decsize && (paddedstsz > szinfo->availsz))) { + int tmpneeded; + std::cout << "Runtime Error source: " << src << ", decsz <" << szinfo->decsz << "> cursz <" << szinfo->cursz << ">, largesz <" << + szinfo->largesz << "> st_size <" << src_stat.st_size << "> availsz <" << szinfo->availsz << ">" << std::endl; + if (mPreScanDecryptErr == PRESCAN_TEMP_FILE_EXIST_ERR) + result = PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR; + else + result = PRESCAN_ERR; + + if (decsize) + tmpneeded = decsize - szinfo->availsz; + else + tmpneeded = paddedstsz - szinfo->availsz; + + if (szinfo->neededsz < tmpneeded) { + szinfo->neededsz = tmpneeded; + if (decsize) + szinfo->totalneedsz = decsize; + else + szinfo->totalneedsz = paddedstsz; + } + } else { + if (decsize) { + long long delta = paddedsize - decsize - 2 * 4096; + + szinfo->decsz += paddedsize; + if (delta > 0) + szinfo->availsz += delta; + } else + szinfo->decsz += paddedstsz; + + szinfo->cursz += paddedstsz; + } + } else { + std::cout << "in decrypting case fn_was_encrypted returned: " << enc_fl << std::endl; + std::cout << "Skipping " << src << std::endl; + szinfo->decsz += paddedstsz; + szinfo->cursz += paddedstsz; + } + } + + return result; +} + +char *EcryptfsEngine::catSubpathFile(const char *src, const char *filename, char **prefixtmp) +{ + if (src == NULL || filename == NULL || prefixtmp == NULL) + return NULL; + + char *catstr = new char[::strlen(src) + ::strlen(filename) + 2]; + int len = ::strlen(src); + const char *prefix = ".tmp_eCfs"; + + if (filename && check_dots(filename)) { + delete [] catstr; + return NULL; + } + + *prefixtmp = new char[::strlen(src) + ::strlen(filename) + ::strlen(prefix) + 2]; + + while (*filename == '/') + filename++; + + if (src [len - 1] == '/') { + ::sprintf(catstr, "%s%s", src, filename); + ::sprintf(*prefixtmp, "%s%s%s", src, prefix, filename); + } else { + ::sprintf(catstr, "%s%s%s", src, "/", filename); + ::sprintf(*prefixtmp, "%s%s%s%s", src, "/", prefix, filename); + } + + if (!::strncmp(filename, prefix, ::strlen(prefix))) { + std::cout << "Deleting Existing Temp file " << filename << std::endl; + if (!mPreScanEncryptErr) + mPreScanEncryptErr = PRESCAN_TEMP_FILE_EXIST_ERR; + else + mPreScanEncryptErr = PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR; + if (!mPreScanDecryptErr) + mPreScanDecryptErr = PRESCAN_TEMP_FILE_EXIST_ERR; + else + mPreScanDecryptErr = PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR; + ::unlink(catstr); + delete [] * prefixtmp; + delete [] catstr; + *prefixtmp = NULL; + return NULL; + } + + return catstr; +} + +int EcryptfsEngine::fn_was_encrypted(const char *filePath) +{ + int fd = 0; + int ret = WAS_NOT_ENCRYPTED; + __u32 attrs = 0; + + if (filePath == NULL) { + std::cout << "Ecryptfs: fn_was_encrypted: source file path is null" << std::endl; + return WAS_ECRYPTED_ERROR; + } + + fd = ::open(filePath, O_RDWR); + if (fd < 0) { + std::cout << "Ecryptfs: fn_was_encrypted: cannot open file " << strerror(errno) << std::endl; + return WAS_ECRYPTED_ERROR; + } + + if (::ioctl(fd, ECRYPTFS_IOCTL_GET_ATTRIBUTES, &attrs)) { + std::cout << "Ecryptfs: fn_was_encrypted: ioctl fail " << strerror(errno) << std::endl; + ::close(fd); + return WAS_ECRYPTED_ERROR; + } + + std::cout << "Ecryptfs: IOCT_GET_ATTRIBUTE = " << attrs << std::endl; + if ((attrs & ECRYPTFS_WAS_ENCRYPTED) == ECRYPTFS_WAS_ENCRYPTED) + ret = WAS_ENCRYPTED; + else if ((attrs & ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE) == ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE) + ret = WAS_ENCRYPTED_OTHER_DEVICE; + + ::close(fd); + + return ret; +} + +int EcryptfsEngine::createEncryptMetaData(const char *filename) { - //TODO + int fd = -1; + std::cout << "createEncryptMetaData" << std::endl; + + fd = ::open(filename, O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + std::cout << "Cann't open " << filename << std::endl; + return -1; + } + if (::fsync(fd) != 0) + std::cout << "Cann't fsync of '" << filename << "'" << std::endl; + ::close(fd); + return 0; } +int EcryptfsEngine::checkEncryptMetaData(const char *path) +{ + struct stat src_stat; + int retval = 0; + + if (path == NULL) { + if (mMetaDataFile) { + if (::lstat(mMetaDataFile, &src_stat) < 0) + if (errno == ENOENT) + retval = -1; + } + } else { + std::cout << path << std::endl; + if (::stat(path, &src_stat) < 0) + if (errno == ENOENT) + retval = -1; + } + return retval; +} + +void EcryptfsEngine::deleteEncryptMetaData(const char *filename) +{ + if (checkEncryptMetaData(filename) == 0) + ::unlink(filename); +} + +int EcryptfsEngine::isMountpointMounted(const std::string &path) +{ + char device[256]; + char mount_path[256]; + char rest[256]; + FILE *fp; + char line[1024]; + + if (!(fp = ::fopen("/proc/mounts", "r"))) { + std::cout << "Error opening /proc/mounts (" << strerror(errno) << ")" << std::endl; + return 0; + } + + while (::fgets(line, sizeof(line), fp)) { + line[::strlen(line) - 1] = '\0'; + ::sscanf(line, "%255s %255s %255s\n", device, mount_path, rest); + if (!::strcmp(mount_path, path.c_str())) { + ::fclose(fp); + return 1; + } + } + + ::fclose(fp); + return 0; +} + +int EcryptfsEngine::isEcryptfsMountpointMounted(const std::string &path) +{ + char device[256]; + char mount_path[256]; + char dev_path[256]; + char rest[256]; + FILE *fp; + char line[1024]; + + if (!(fp = ::fopen("/proc/mounts", "r"))) { + std::cout << "Error opening /proc/mounts (" << strerror(errno) << ")" << std::endl; + return -1; + } + + while (::fgets(line, sizeof(line), fp)) { + line[strlen(line) - 1] = '\0'; + ::sscanf(line, "%255s %255s %255s %255s\n", dev_path, mount_path, device, rest); + if (!::strcmp(mount_path, path.c_str()) && !strcmp(device, "ecryptfs")) { + ::fclose(fp); + std::cout << "Returning True mount_path<" << mount_path << ">,path <" << path << ">, device <" << device << ">" << std::endl; + return 0; + } + } + + fclose(fp); + return -1; +} } // namespace ode diff --git a/server/engine/ecryptfs-engine.h b/server/engine/ecryptfs-engine.h index cc55c1f..bef3ac1 100644 --- a/server/engine/ecryptfs-engine.h +++ b/server/engine/ecryptfs-engine.h @@ -19,6 +19,84 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define __u32 unsigned int + +#define ECRYPTFS_SIG_SIZE 8 +#define ECRYPTFS_SIG_SIZE_HEX (ECRYPTFS_SIG_SIZE*2) +#define ECRYPTFS_PASSWORD_SIG_SIZE ECRYPTFS_SIG_SIZE_HEX +#define ECRYPTFS_MAX_KEY_BYTES 64 +#define ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES 512 +#define ECRYPTFS_MAX_PKI_NAME_BYTES 16 +#define ECRYPTFS_SALT_SIZE 8 +#define ECRYPTFS_BUFFER_SIZE 4096 + +#define ECRYPTFS_MAJOR_VERSION 0x00 +#define ECRYPTFS_MINOR_VERSION 0x04 +#define ECRYPTFS_VERSION ((ECRYPTFS_MAJOR_VERSION << 8) | ECRYPTFS_MINOR_VERSION) +#define ECRYPTFS_PWD_PAYLOAD_TYPE 0 +#define ECRYPTFS_SESSION_KEY_ENCRYPTION_KEY_SET 0x02 + +#define MAX_KEY_BYTES 32 +#define SIG_SIZE 8 +#define ECRYPTFS_MAX_OPTIONS 1024 + +#define ECRYPTFS_PLAINTEXT_PASSTHOUGH "ecryptfs_passthrough" +#define ECRYPTFS_SIGNATURE "ecryptfs_sig=" +#define ECRYPTFS_CIPHERA "ecryptfs_cipher=" +#define ECRYPTFS_KEY_BYTES "ecryptfs_key_bytes=" +#define ECRYPTFS_UNLINK_SIGNATURE "ecryptfs_unlink_sig" +#define ECRYPTFS_FEK_CIPHER "aes" +#define ECRYPTFS_ENABLE_FILTERING "ecryptfs_enable_filtering=" +#define ECRYPTFS_MEDIA_EXCLUSION_LIST "temp_video/Camera/DCIM:mp3|mpga|m4a|mp4|wav|amr|awb|wma|ogg|oga|aac|mka|flac|3gp|3ga|mid|midi|xmf|rtttl|rtx|ota|smf|spm|imy|mpeg|m4v|3gp|3gpp|3g2|3gpp2|wmv|asf|mkv|webm|ts|avi|jpg|jpeg|gif|png|bmp|wbmp|divx|flv|ac3|mov|tiff|f4v|mpeg3|voice" +#define ECRYPTFS_GLOBAL_EXCLUSION_LIST "*" + +#define SMACKFS_MAGIC 0x43415d53 +#define SMACKFS_MNT "/smack" +#define SMACKFS_MOUNT_OPT "smackfsroot=*,smackfsdef=*" + +#define EXCL_ALL_NEW_ON 2 +#define EXCL_MEDIA_ON 1 +#define EXCL_MEDIA_OFF 0 + +typedef int32_t key_serial_t; + +#define KEY_SPEC_USER_KEYRING -4 /* - key ID for UID-specific keyring */ +#define KEYCTL_SEARCH 10 /* search for a key in a keyring */ +#define AUTH_TOKEN_TYPE "user" +#define ECRYPTFS_MOUNT_DEVICE "ecryptfs" + +#define CRYPT_META_FILE ".MetaEcfsFile" +#define ORIG_META_FILE_PATH "/opt/media/SDCardA1/.MetaEcfsFile" + +#define ERR_NO 0 +#define ERR_FAILED_AUTH 1 +#define ERR_MOUNT 5 +#define ERR_UNMOUNT 6 + +#define ECRYPTFS_IOCTL_GET_ATTRIBUTES _IOR('l', 0x10, __u32) +#define ECRYPTFS_WAS_ENCRYPTED 0x0080 +#define ECRYPTFS_WAS_ENCRYPTED_OTHER_DEVICE 0x0100 + +#define PRESCAN_ERR (-2) +#define PRESCAN_TEMP_FILE_EXIST_ERR (-3) +#define PRESCAN_TEMP_FILE_EXIST_AND_SIZE_ERR (-4) + +#define WAS_ECRYPTED_ERROR (-2) +#define WAS_NOT_ENCRYPTED (-1) +#define WAS_ENCRYPTED 0 +#define WAS_ENCRYPTED_OTHER_DEVICE 1 + +#define check_dots(p) ((p)[0] == '.' && (!(p)[1] || ((p)[1] =='.'&& !(p)[2]))) namespace ode { @@ -34,25 +112,128 @@ public: const std::string& getSource() { - return source; + return mSource; } const std::string& getDestination() { - return destination; + return mDestination; + } + + void setSource(const std::string& src) + { + mSource = src; + } + + void setDestination(const std::string& dest) + { + mDestination = dest; } typedef std::vector data; - void mount(const data& key); - void umount(); + typedef struct { + int blocksz; + int filecount; + long long availsz; + long long decsz; + long long encsz; + long long cursz; + long long largesz; + long long neededsz; + long long totalneedsz; + long long totalstsz; + } sizeinfo_type; + + struct ecryptfs_session_key { + u_int32_t flags; + u_int32_t encrypted_key_size; + u_int32_t decrypted_key_size; + u_int8_t encrypted_key[ECRYPTFS_MAX_ENCRYPTED_KEY_BYTES]; + u_int8_t decrypted_key[ECRYPTFS_MAX_KEY_BYTES]; + }; + + struct ecryptfs_password { + u_int32_t password_bytes; + int32_t hash_algo; + u_int32_t hash_iterations; + u_int32_t session_key_encryption_key_bytes; + u_int32_t flags; + u_int8_t session_key_encryption_key[ECRYPTFS_MAX_KEY_BYTES]; + u_int8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; + u_int8_t salt[ECRYPTFS_SALT_SIZE]; + }; - void encrypt(const data& key); - void decrypt(const data& key); + struct ecryptfs_private_key { + u_int32_t key_size; + u_int32_t data_len; + u_int8_t signature[ECRYPTFS_PASSWORD_SIG_SIZE + 1]; + char pki_type[ECRYPTFS_MAX_PKI_NAME_BYTES + 1]; + u_int8_t data[]; + }; + + struct ecryptfs_auth_tok { + u_int16_t version; + u_int16_t token_type; + u_int32_t flags; + struct ecryptfs_session_key session_key; + u_int8_t reserved[32]; + union { + struct ecryptfs_password password; + struct ecryptfs_private_key private_key; + } token; + } __attribute__ ((packed)); + + typedef struct ecryptfs_auth_tok ecryptfs_payload; + + key_serial_t add_key(const char *type, const char *description, const void *payload, size_t plen, key_serial_t ringid); + long keyctl_search(key_serial_t ringid, const char *type, const char *description, key_serial_t destringid); + void hexConvert(char *dest, unsigned char *src, int srcLen); + int add_user_auth_token_to_keyring(ecryptfs_payload *payload) ; + int smack_check(void); + void build_ecryptfs_options(char *ecryptfs_opts, char *sig, int excludeMediaTypes); + + int mount(const data& key); + int umount(); + + int encrypt(const data& key); + int decrypt(const data& key); + + int DoCrypt(const data& key, const char *path, int reqEnc, int excludeMedia); + int DoEncrypt(const data& key); + int DoDecrypt(const data& key); + long long CopyImpl(int sfd, int dfd, long long fullsz, bool enctype); + ssize_t FullRead (int fd, void * buf, size_t count); + ssize_t FullWrite (int fd, const void * buf, size_t len); + int CopyFile (const char * src, const char * dest, struct stat * src_stat, bool enctype); + void syncdatafile (const char * src); + int cryptInplace(const char * src, const char * tmpdest, bool dec_fl); + int preScanForEncrypt(const char *src); + int preScanForDecrypt(const char * src); + int getEncryptedSize(const char * src, sizeinfo_type * szinfo); + int getDecryptedSize(const char * src, sizeinfo_type * szinfo); + char * catSubpathFile(const char * src, const char * filename, char ** prefixtmp); + int fn_was_encrypted(const char *filePath); + int createEncryptMetaData(const char *filename); + int checkEncryptMetaData(const char *path); + void deleteEncryptMetaData (const char *filename); + int isMountpointMounted(const std::string& path); + int isEcryptfsMountpointMounted(const std::string& path); private: - std::string source, destination; + std::string mSource, mDestination; + ecryptfs_payload sde_payload; + const char *mPath; + char *mMetaDataFile; + int mReqEnc; + int mExcludeMedia; + int mPreScanEncryptErr; + int mPreScanDecryptErr; + int mTotalFileCt; + long long mTotalStSz; + long long mTotalCopied; }; } // namespace ode #endif // __ECRYPTFS_ENGINE_H__ + -- 2.34.1