root/diary/diary.c
/*DEFINITIONS
This source file includes following definitions.1 /* 2 ** Copyright (c) 2024 Amelia Campbell 3 ** 4 ** Redistribution and use in source and binary forms, with or without 5 ** modification, are permitted provided that the following conditions are 6 ** met: 7 ** 8 ** 1. Redistributions of source code must retain the above copyright 9 ** notice, 10 ** this list of conditions and the following disclaimer. 11 ** 2. Redistributions in binary form must reproduce the above copyright 12 ** notice, this list of conditions and the following disclaimer in the 13 ** documentation and/or other materials provided with the 14 ** distribution. 15 ** 3. Neither the name of the copyright holder nor the names of its 16 ** contributors may be used to endorse or promote products derived 17 ** from this software without specific prior written permission. 18 ** 19 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 ** IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 ** PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 ** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <stdio.h> 33 #include <string.h> 34 #include <stdlib.h> 35 #include <errno.h> 36 #include <time.h> 37 #include <unistd.h> 38 #include <openssl/evp.h> 39 #include <sys/stat.h> 40 41 #include <shared/macro.h> 42 #include <shared/hash.h> 43 #include <shared/term.h> 44 #include <shared/session.h> 45 46 #include "bufop.h" 47 #include "editor.h" 48 49 #define MAXPASS 128 50 51 52 enum mode { 53 MODE_NONE, 54 MODE_ENCRYPT, 55 MODE_DECRYPT, 56 MODE_APPEND, 57 MODE_MODIFY 58 }; 59 60 61 static void usage(char *name) 62 { 63 fatal("syntax: %s [-headyx] " 64 "[-k <FILE>] [-m <FILE>] [-i <FILE>] [-o <FILE>]\n" 65 "modes: e[ncrypt] d[ecrypt] a[ppend] m[odify file]\n" 66 "k[ey] specifies keyfile instead of key prompt\n" 67 "e[ncrypt]/d[ecrypt] operate on stdin->stdout\n" 68 "m[odify] edits or creates an encrypted file with EDITOR\n" 69 "y[€es] suppresses non-critical prompts\n" 70 "i[nput]/o[utput] overrides stdin/stdout with a file\n" 71 "a[append] is like m, but appends arg i file to arg o content\n" 72 "x[...] makes append use encrypted input\n", 73 name); 74 } 75 76 77 int main(int argc, char **argv) 78 { 79 struct stat s; 80 FILE *keyfp, *modfp, *tmpfp; 81 FILE *ifp = stdin, *ofp = stdout; 82 char *keyfile = NULL, *modfile = NULL; 83 char *ifile = NULL, *ofile = NULL; 84 char tmpfile[64]; 85 int mode = 0, create = 0, ccprompt = 1, exa = 0, us = 0; 86 hash_t h1, h2; 87 88 tmpfile[0] = '\0'; 89 90 ARG_LOOP 91 { 92 ARG_TYPC; 93 case 'k': ARG_STR(keyfile); break; 94 case 'i': ARG_STR(ifile); break; 95 case 'o': ARG_STR(ofile); break; 96 case 'y': ARG_FLG(ccprompt, 0); break; 97 case 'x': ARG_FLG(exa, 1); break; 98 case 'e': ARG_FLG(mode, MODE_ENCRYPT); break; 99 case 'd': ARG_FLG(mode, MODE_DECRYPT); break; 100 case 'a': ARG_FLG(mode, MODE_APPEND ); break; 101 case 'm': ARG_FLG(mode, MODE_MODIFY ); ARG_STR(modfile); break; 102 } 103 ARG_END; 104 105 #ifdef USE_AGENT 106 if (session_open() == 0) 107 { 108 key_override = &session; 109 us = 1; 110 } 111 #endif 112 if (!us && keyfile) 113 { 114 if (!(keyfp = fopen(keyfile, "r"))) 115 fatal("unable to access keyfile\n"); 116 if (!fread(password, 1, MAXPASS, keyfp)) 117 fatal("keyfile seems to be empty\n"); 118 fclose(keyfp); 119 } 120 121 #define MAPPEND (mode == MODE_APPEND) 122 #define KSIFOF do { \ 123 if (ifile && !(ifp = fopen(ifile, "r"))) \ 124 fatal("unable to open the input file for reading\n"); \ 125 if (ofile) \ 126 { \ 127 if (ccprompt && stat(ofile, &s) == 0) \ 128 if (fcprompt("output file exists")) \ 129 fatal("user cancelled operations\n"); \ 130 if (!(ofp = fopen(ofile, MAPPEND ? "a+" : "w"))) \ 131 fatal("unable to open the output file for writing\n"); \ 132 } \ 133 } while (0) 134 #define KGETPASS(n) do { if (!us && !keyfile) dgetpass(n); } while (0) 135 switch (mode) 136 { 137 default: fatal("no mode specified (see -h)\n"); break; 138 case MODE_ENCRYPT: KGETPASS(1);KSIFOF; encryptds(ifp, ofp); break; 139 case MODE_DECRYPT: KGETPASS(0);KSIFOF; decryptds(ifp, ofp); break; 140 case MODE_APPEND: KGETPASS(0);KSIFOF; 141 if (!ofile) 142 fatal("no output file specified in append mode\n"); 143 if (modfile) 144 fatal("modify mode file arg present in append mode (how?)\n"); 145 modfile = ofile; 146 modfp = ofp; /* fallthrough */ 147 case MODE_MODIFY: 148 if (!MAPPEND) 149 { 150 if (ifile || ofile) 151 fatal("-i/-o are redunant in modify mode, check -h\n"); 152 if (stat(modfile, &s) != 0) 153 { 154 if (errno == ENOENT) 155 create = 1; 156 else 157 fatal("unable to access target file\n"); 158 } 159 KGETPASS(create ? 1 : 0); 160 if (!create && !S_ISREG(s.st_mode)) 161 fatal("target file is not a regular file\n"); 162 if (!(modfp = fopen(modfile, create ? "w" : "r"))) 163 fatal("unable to open target file\n"); 164 } 165 166 /* time + pid should be unique enough */ 167 snprintf(tmpfile, 64, "/tmp/dx%fp%u", 168 (double)time(NULL), 169 (unsigned int)getpid()); 170 /* need umask that prevents other users snooping (0077) */ 171 umask(S_IRWXG|S_IRWXO); 172 173 /* decrypt into tmpfile */ 174 if (!(tmpfp = fopen(tmpfile, "w+"))) 175 fatal("unable to open temporary file\n"); 176 if (!create && !(MAPPEND && ftell(modfp) == 0)) 177 { 178 /* XXX: opened in append mode to benefit from ftell and auto 179 ** file creation, but now it must seek to 0 to read it 180 */ 181 if (MAPPEND) fseek(modfp, 0, SEEK_SET); 182 decryptds(modfp, tmpfp); 183 } 184 hashfd(tmpfp, &h1); 185 fclose(tmpfp); 186 187 if (!MAPPEND) 188 { 189 /* edit the file with the user's editor of choice */ 190 exedit(tmpfile); 191 } 192 else 193 { 194 if (!(tmpfp = fopen(tmpfile, "a"))) 195 fatal("unable to reopen temporary file\n"); 196 /* in append mode, just copy ifile to tmpfile */ 197 if (!exa) 198 nopds(ifp, tmpfp); 199 else 200 decryptds(ifp, tmpfp); 201 fclose(tmpfp); 202 } 203 204 /* reopen it and encrypt it again */ 205 if (!(tmpfp = fopen(tmpfile, "r"))) 206 fatal("unable to reopen temporary file\n"); 207 208 hashfd(tmpfp, &h2); 209 if (hashcmp(&h1, &h2)) 210 { 211 fprintf(stderr, "no changes to edited file, rolling back\n"); 212 fclose(modfp); 213 if (create && remove(modfile) != 0) 214 fatal("failed to remove newly created file\n"); 215 } 216 else 217 { 218 /* now overwrite the whole original file (re-encrypt all) */ 219 if (!(modfp = freopen(modfile, "w", modfp))) 220 fatal("unable to reopen target file\n"); 221 encryptds(tmpfp, modfp); 222 fclose(modfp); 223 } 224 fclose(tmpfp); 225 if (remove(tmpfile) != 0) 226 fatal("failed to remove temporary file!\n" 227 "please check it. your secrets may be exposed.\n"); 228 break; 229 } 230 231 #ifdef USE_AGENT 232 if (session) session_close(); 233 #endif 234 if (ifile) fclose(ifp); 235 /* XXX: in MAPPEND, ofile closed earlier as modfp */ 236 if (!MAPPEND && ofile) fclose(ofp); 237 return 0; 238 }
/*