rgpgfs.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. #include "src/fs.h"
  2. // http://libfuse.github.io/doxygen/globals.html
  3. #define FUSE_USE_VERSION 31
  4. #include <fuse.h>
  5. // https://www.gnupg.org/documentation/manuals/gpgme/Function-and-Data-Index.html
  6. #include <gpgme.h>
  7. // posix
  8. #include <dirent.h>
  9. #include <sys/stat.h>
  10. #include <sys/types.h>
  11. #include <unistd.h>
  12. #include <errno.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <string.h>
  16. #define FUSE_PATH_BUF_LEN 256
  17. static char cache_dir[] = "/tmp/rgpgfs-cache-XXXXXX";
  18. static const size_t CACHE_PATH_BUF_LEN = sizeof(cache_dir) + FUSE_PATH_BUF_LEN;
  19. static gpgme_ctx_t gpgme_ctx;
  20. static const char gpgme_recip_fpr[] =
  21. "1234567890ABCDEF1234567890ABCDEF12345678";
  22. static gpgme_key_t gpgme_recip_key;
  23. static int rgpgfs_gpgme_data_to_file(const char *path, gpgme_data_t data) {
  24. if (gpgme_data_seek(data, 0, SEEK_SET) != 0) {
  25. perror("rgpgfs_gpgme_data_to_file: failed to seek");
  26. return 1;
  27. }
  28. FILE *file = fopen(path, "wb");
  29. if (file == NULL) {
  30. perror("rgpgfs_gpgme_data_to_file: failed to open file");
  31. return 1;
  32. }
  33. ssize_t count;
  34. char buf[BUFSIZ];
  35. while ((count = gpgme_data_read(data, buf, BUFSIZ)) > 0) {
  36. if (fwrite(buf, 1, count, file) != count) {
  37. fprintf(stderr,
  38. "rgpgfs_gpgme_data_to_file: failed to write data to file");
  39. return 1;
  40. }
  41. }
  42. if (count != 0) {
  43. perror("rgpgfs_gpgme_data_to_file: failed to load data into buffer");
  44. return 1;
  45. }
  46. fclose(file);
  47. return 0;
  48. }
  49. static int rgpgfs_encrypt(const char *source_path, char *cache_path) {
  50. // fprintf(stderr, "rgpgfs_encrypt('%s', %p)\n", source_path, cache_path);
  51. size_t source_path_len = strnlen(source_path, FUSE_PATH_BUF_LEN);
  52. if (source_path_len >= FUSE_PATH_BUF_LEN) {
  53. errno = ENAMETOOLONG;
  54. perror("rgpgfs_encrypt");
  55. return 1;
  56. }
  57. strcpy(cache_path, cache_dir);
  58. strcat(cache_path, source_path);
  59. struct stat source_stat;
  60. if (lstat(source_path, &source_stat)) {
  61. perror("rgpgfs_encrypt: failed to stat source file");
  62. return 1;
  63. }
  64. struct stat cache_stat;
  65. if (lstat(cache_path, &cache_stat) ||
  66. source_stat.st_mtim.tv_sec > cache_stat.st_mtim.tv_sec) {
  67. if (rgpgfs_fs_mkdirs(cache_path)) {
  68. perror("rgpgfs_encrypt: failed to create dirs");
  69. return 1;
  70. }
  71. gpgme_data_t plain_data;
  72. gpgme_error_t gpgme_source_read_err =
  73. gpgme_data_new_from_file(&plain_data, source_path, 1);
  74. if (gpgme_source_read_err != GPG_ERR_NO_ERROR) {
  75. fprintf(stderr,
  76. "rgpgfs_encrypt: failed to read source file %s: %s (%d)\n",
  77. source_path, gpg_strerror(gpgme_source_read_err),
  78. gpgme_source_read_err);
  79. return 1;
  80. }
  81. gpgme_data_t cipher_data;
  82. if (gpgme_data_new(&cipher_data) != GPG_ERR_NO_ERROR) {
  83. fprintf(stderr,
  84. "rgpgfs_encrypt: failed to prepare cipher data container\n");
  85. gpgme_data_release(plain_data);
  86. return 1;
  87. }
  88. // list of recipients may implicitly include the default recipient
  89. // (GPGME_ENCRYPT_NO_ENCRYPT_TO)
  90. gpgme_key_t recip_keys[] = {gpgme_recip_key, NULL};
  91. if (gpgme_op_encrypt(gpgme_ctx, recip_keys, 0, plain_data, cipher_data) ==
  92. GPG_ERR_NO_ERROR) {
  93. if (rgpgfs_gpgme_data_to_file(cache_path, cipher_data)) {
  94. fprintf(stderr,
  95. "rgpgfs_encrypt: failed to write cipher data to disk\n");
  96. } else {
  97. printf("encrypted %s\n", source_path);
  98. }
  99. } else {
  100. fprintf(stderr, "rgpgfs_encrypt: failed to encrypt %s\n", source_path);
  101. }
  102. gpgme_data_release(cipher_data);
  103. gpgme_data_release(plain_data);
  104. }
  105. return 0;
  106. }
  107. static int rgpgfs_getattr(const char *source_path, struct stat *statbuf,
  108. struct fuse_file_info *fi) {
  109. if (lstat(source_path, statbuf))
  110. return -errno;
  111. if (!S_ISDIR(statbuf->st_mode)) {
  112. char cache_path[CACHE_PATH_BUF_LEN];
  113. if (rgpgfs_encrypt(source_path, cache_path))
  114. return -errno;
  115. if (lstat(cache_path, statbuf))
  116. return -errno;
  117. }
  118. return 0;
  119. }
  120. static int rgpgfs_access(const char *path, int mask) {
  121. int res = access(path, mask);
  122. // printf("rgpgfs_access(%s, %d) = %d\n", path, mask, res);
  123. if (res == -1)
  124. return -errno;
  125. return 0;
  126. }
  127. static int rgpgfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
  128. off_t offset, struct fuse_file_info *fi,
  129. enum fuse_readdir_flags flags) {
  130. DIR *dirp = opendir(path);
  131. if (dirp == NULL) {
  132. return -errno;
  133. }
  134. struct dirent *entp;
  135. while ((entp = readdir(dirp)) != NULL) {
  136. struct stat statbf;
  137. memset(&statbf, 0, sizeof(statbf));
  138. statbf.st_ino = entp->d_ino;
  139. statbf.st_mode = entp->d_type << 12;
  140. if (filler(buf, entp->d_name, &statbf, 0, 0))
  141. break;
  142. }
  143. closedir(dirp);
  144. return 0;
  145. }
  146. static int rgpgfs_open(const char *source_path, struct fuse_file_info *fi) {
  147. // fprintf(stderr, "rgpgfs_open('%s', %p)", source_path, fi);
  148. char cache_path[CACHE_PATH_BUF_LEN];
  149. if (rgpgfs_encrypt(source_path, cache_path))
  150. return -errno;
  151. int res = open(cache_path, fi->flags);
  152. if (res == -1)
  153. return -errno;
  154. fi->fh = res;
  155. return 0;
  156. }
  157. static int rgpgfs_read(const char *path, char *buf, size_t count, off_t offset,
  158. struct fuse_file_info *fi) {
  159. if (fi == NULL) {
  160. return ENOTSUP;
  161. }
  162. ssize_t bytes_num = pread(fi->fh, buf, count, offset);
  163. if (bytes_num == -1) {
  164. return -errno;
  165. }
  166. return bytes_num;
  167. }
  168. static int rgpgfs_release(const char *path, struct fuse_file_info *fi) {
  169. close(fi->fh);
  170. return 0;
  171. }
  172. static struct fuse_operations rgpgfs_fuse_operations = {
  173. .getattr = rgpgfs_getattr,
  174. .open = rgpgfs_open,
  175. .read = rgpgfs_read,
  176. .release = rgpgfs_release,
  177. .readdir = rgpgfs_readdir,
  178. .access = rgpgfs_access,
  179. };
  180. int main(int argc, char *argv[]) {
  181. if (mkdtemp(cache_dir) == NULL) {
  182. return 1;
  183. }
  184. printf("cache: %s\n", cache_dir);
  185. printf("gpgme version: %s\n", gpgme_check_version(NULL));
  186. gpg_error_t gpgme_init_err = gpgme_new(&gpgme_ctx);
  187. if (gpgme_init_err != GPG_ERR_NO_ERROR) {
  188. fprintf(stderr, "Failed to initialize gpgme: %s (%d)\n",
  189. gpg_strerror(gpgme_init_err), gpgme_init_err);
  190. return 1;
  191. }
  192. gpg_error_t gpgme_get_key_err =
  193. gpgme_get_key(gpgme_ctx, gpgme_recip_fpr, &gpgme_recip_key, 0);
  194. switch (gpgme_get_key_err) {
  195. case GPG_ERR_NO_ERROR:
  196. break;
  197. case GPG_ERR_EOF:
  198. fprintf(stderr, "Could not find key %s\n", gpgme_recip_fpr);
  199. return 1;
  200. case GPG_ERR_AMBIGUOUS_NAME:
  201. fprintf(stderr, "Key name '%s' is ambiguous\n", gpgme_recip_fpr);
  202. return 1;
  203. case GPG_ERR_INV_VALUE:
  204. default:
  205. fprintf(stderr, "Failed to load key %s: %s (%d)\n", gpgme_recip_fpr,
  206. gpg_strerror(gpgme_init_err), gpgme_get_key_err);
  207. return 1;
  208. }
  209. if (!gpgme_recip_key->can_encrypt) {
  210. fprintf(stderr, "Selected key %s can not be used for encryption\n",
  211. gpgme_recip_key->fpr);
  212. return 1;
  213. }
  214. gpgme_user_id_t gpgme_recip_id = gpgme_recip_key->uids;
  215. while (gpgme_recip_id != NULL) {
  216. printf("recipient: %s\n", gpgme_recip_id->uid);
  217. gpgme_recip_id = gpgme_recip_id->next;
  218. }
  219. printf("recipient fingerprint: %s\n", gpgme_recip_key->fpr);
  220. // TODO rm -r cache_dir (see man nftw)
  221. int fuse_main_err = fuse_main(argc, argv, &rgpgfs_fuse_operations, NULL);
  222. gpgme_release(gpgme_ctx);
  223. return fuse_main_err;
  224. }