Add eCryptfs engine 05/99505/3
authorSungjun, Lee <sjun221.lee@samsung.com>
Wed, 23 Nov 2016 06:09:01 +0000 (15:09 +0900)
committerSungbae Yoo <sungbae.yoo@samsung.com>
Thu, 24 Nov 2016 06:06:08 +0000 (22:06 -0800)
Change-Id: I5cb65125d41f92413d68e79d011b243b65653a51
Signed-off-by: Sungjun, Lee <sjun221.lee@samsung.com>
server/engine/ecryptfs-engine.cpp
server/engine/ecryptfs-engine.h

index c7f97ff..328fed4 100644 (file)
@@ -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, &times) < 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
index cc55c1f..bef3ac1 100644 (file)
 
 #include <vector>
 #include <string>
+#include <sys/mount.h>
+#include <sys/syscall.h>
+#include <sys/vfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <utime.h>
+
+#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<unsigned char> 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__
+