root/diary/diary.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. usage
  2. main

   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     }

/* [previous][next][first][last][top][bottom][index][help] */