SQLite Archiver
Check-in [06c26c027e]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add support for encrypted SQL archives using the SQLite Encryption Extension.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 06c26c027e211b985a733da2edac57f09d5132ec
User & Date: drh 2016-07-11 19:21:59
Context
2016-07-11
19:22
Update to the version 3.13.0 of SQLite. check-in: 52199b0fb4 user: drh tags: trunk
19:21
Add support for encrypted SQL archives using the SQLite Encryption Extension. check-in: 06c26c027e user: drh tags: trunk
2014-12-10
22:06
Add a missing "closedir()". check-in: 15adeb2f9a user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Makefile.

     1      1   #!/bin/make
     2      2   
     3      3   CC = gcc -g -I. -D_FILE_OFFSET_BITS=64 -Wall -Werror
     4      4   ZLIB = -lz
     5      5   FUSELIB = -lfuse
     6         -SQLITE_OPT = -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
            6  +SQLITE_OPT = $(OPT) -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION
     7      7   
     8      8   all: sqlar
     9      9   	
    10     10   
    11     11   sqlar:	sqlar.c sqlite3.o
    12         -	$(CC) -o sqlar sqlar.c sqlite3.o $(ZLIB)
           12  +	$(CC) -o sqlar $(OPT) sqlar.c sqlite3.o $(ZLIB)
    13     13   
    14     14   sqlarfs:	sqlarfs.c sqlite3.o
    15         -	$(CC) -o sqlarfs sqlarfs.c sqlite3.o $(ZLIB) $(FUSELIB)
           15  +	$(CC) -o sqlarfs $(OPT) sqlarfs.c sqlite3.o $(ZLIB) $(FUSELIB)
    16     16   
    17     17   sqlite3.o:	sqlite3.c sqlite3.h
    18     18   	$(CC) -c sqlite3.c $(SQLITE_OPT) sqlite3.c
    19     19   
    20     20   clean:	
    21     21   	rm -f sqlar sqlarfs sqlite3.o

Changes to sqlar.c.

    18     18   #include <stdlib.h>
    19     19   #include <zlib.h>
    20     20   #include <sys/types.h>
    21     21   #include <sys/stat.h>
    22     22   #include <unistd.h>
    23     23   #include <dirent.h>
    24     24   #include <string.h>
           25  +#include <assert.h>
           26  +#include <ctype.h>
           27  +
           28  +/* Maximum length of a pass-phrase */
           29  +#define MX_PASSPHRASE  40
    25     30   
    26     31   /*
    27     32   ** Show a help message and quit.
    28     33   */
    29     34   static void showHelp(const char *argv0){
    30     35     fprintf(stderr, "Usage: %s [options] archive [files...]\n", argv0);
    31         -  fprintf(stderr, "Options:\n"
    32         -                  "   -l      List files in archive\n"
    33         -                  "   -n      Do not compress files\n"
    34         -                  "   -x      Extract files from archive\n"
    35         -                  "   -v      Verbose output\n"
           36  +  fprintf(stderr,
           37  +     "Options:\n"
           38  +     "   -e      Prompt for passphrase.  -ee to scramble the prompt\n"
           39  +     "   -l      List files in archive\n"
           40  +     "   -n      Do not compress files\n"
           41  +     "   -x      Extract files from archive\n"
           42  +     "   -v      Verbose output\n"
    36     43     );
    37     44     exit(1);
    38     45   }
    39     46   
    40     47   /*
    41     48   ** The database schema:
    42     49   */
................................................................................
    87     94     va_list ap;
    88     95     va_start(ap, zFormat);
    89     96     vfprintf(stderr, zFormat, ap);
    90     97     va_end(ap);
    91     98     db_close(0);
    92     99     exit(1);
    93    100   }
          101  +
          102  +/*
          103  +** Scramble substitution matrix:
          104  +*/
          105  +static char aSubst[256];
          106  +
          107  +/*
          108  +** Descramble the password
          109  +*/
          110  +static void descramble(char *z){
          111  +  int i;
          112  +  for(i=0; z[i]; i++) z[i] = aSubst[(unsigned char)z[i]];
          113  +}
          114  +
          115  +/* Print a string in 5-letter groups */
          116  +static void printFive(const unsigned char *z){
          117  +  int i;
          118  +  for(i=0; z[i]; i++){
          119  +    if( i>0 && (i%5)==0 ) putchar(' ');
          120  +    putchar(z[i]);
          121  +  }
          122  +  putchar('\n');
          123  +}
          124  +
          125  +/* Return a pseudo-random integer between 0 and N-1 */
          126  +static int randint(int N){
          127  +  unsigned char x;
          128  +  assert( N<256 );
          129  +  sqlite3_randomness(1, &x);
          130  +  return x % N;
          131  +}
          132  +
          133  +/*
          134  +** Generate and print a random scrambling of letters a through z (omitting x)
          135  +** and set up the aSubst[] matrix to descramble.
          136  +*/
          137  +static void generateScrambleCode(void){
          138  +  unsigned char zOrig[30];
          139  +  unsigned char zA[30];
          140  +  unsigned char zB[30];
          141  +  int nA = 25;
          142  +  int nB = 0;
          143  +  int i;
          144  +  memcpy(zOrig, "abcdefghijklmnopqrstuvwyz", nA+1);
          145  +  memcpy(zA, zOrig, nA+1);
          146  +  assert( nA==(int)strlen((char*)zA) );
          147  +  for(i=0; i<sizeof(aSubst); i++) aSubst[i] = i;
          148  +  printFive(zA);
          149  +  while( nA>0 ){
          150  +    int x = randint(nA);
          151  +    zB[nB++] = zA[x];
          152  +    zA[x] = zA[--nA];
          153  +  }
          154  +  assert( nB==25 );
          155  +  zB[nB] = 0;
          156  +  printFive(zB);
          157  +  for(i=0; i<nB; i++) aSubst[zB[i]] = zOrig[i];
          158  +}
          159  +
          160  +/*
          161  +** Do a single prompt for a passphrase.  Store the results in the blob.
          162  +**
          163  +** If the FOSSIL_PWREADER environment variable is set, then it will
          164  +** be the name of a program that prompts the user for their password/
          165  +** passphrase in a secure manner.  The program should take one or more
          166  +** arguments which are the prompts and should output the acquired
          167  +** passphrase as a single line on stdout.  This function will read the
          168  +** output using popen().
          169  +**
          170  +** If FOSSIL_PWREADER is not set, or if it is not the name of an
          171  +** executable, then use the C-library getpass() routine.
          172  +**
          173  +** The return value is a pointer to a static buffer that is overwritten
          174  +** on subsequent calls to this same routine.
          175  +*/
          176  +static void prompt_for_passphrase(
          177  +  const char *zPrompt,    /* Passphrase prompt */
          178  +  int doScramble,         /* Scramble the input if true */
          179  +  char *zPassphrase       /* Write result here */
          180  +){
          181  +  char *z;
          182  +  int i;
          183  +  if( doScramble ){
          184  +    generateScrambleCode();
          185  +    z = getpass(zPrompt);
          186  +    if( z ) descramble(z);
          187  +    printf("\033[3A\033[J");  /* Erase previous three lines */
          188  +    fflush(stdout);
          189  +  }else{
          190  +    z = getpass(zPrompt);
          191  +  }
          192  +  while( isspace(z[0]) ) z++;
          193  +  for(i=0; i<MX_PASSPHRASE-1; i++){
          194  +    zPassphrase[i] = z[i];
          195  +  }
          196  +  while( i>0 && isspace(z[i-1]) ){ i--; }
          197  +  zPassphrase[i] = 0;
          198  +}
    94    199   
    95    200   /*
    96    201   ** Open the database.
    97    202   */
    98         -static void db_open(const char *zArchive, int writeFlag){
          203  +static void db_open(const char *zArchive, int writeFlag, int seeFlag){
    99    204     int rc;
   100    205     int fg;
   101    206     if( writeFlag ){
   102    207       fg = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
   103    208     }else{
   104    209       fg = SQLITE_OPEN_READONLY;
   105    210     }
   106    211     rc = sqlite3_open_v2(zArchive, &db, fg, 0);
   107    212     if( rc ) errorMsg("Cannot open archive [%s]: %s\n", zArchive,
   108    213                       sqlite3_errmsg(db));
          214  +  if( seeFlag ){
          215  +    char *zSql;
          216  +    char zPassPhrase[MX_PASSPHRASE+1];
          217  +#ifndef SQLITE_HAS_CODEC
          218  +    printf("WARNING:  The passphrase is a no-op because this build of\n"
          219  +           "sqlar is compiled without encryption capabilities.\n");
          220  +#endif
          221  +    memset(zPassPhrase, 0, sizeof(zPassPhrase));
          222  +    prompt_for_passphrase("passphrase: ", seeFlag>1, zPassPhrase);
          223  +    zSql = sqlite3_mprintf("PRAGMA key(%Q)", zPassPhrase);
          224  +    sqlite3_exec(db, zSql, 0, 0, 0);
          225  +    sqlite3_free(zSql);
          226  +  }
   109    227     sqlite3_exec(db, "BEGIN", 0, 0, 0);
   110    228     sqlite3_exec(db, zSchema, 0, 0, 0);
   111    229   }
   112    230   
   113    231   /*
   114    232   ** Prepare the pStmt statement.
   115    233   */
................................................................................
   367    485     const char *zArchive = 0;
   368    486     char **azFiles = 0;
   369    487     int nFiles = 0;
   370    488     int listFlag = 0;
   371    489     int extractFlag = 0;
   372    490     int verboseFlag = 0;
   373    491     int noCompress = 0;
          492  +  int seeFlag = 0;
   374    493     int i, j;
   375    494   
   376    495     if( sqlite3_strglob("*/unsqlar", argv[0])==0 ){
   377    496       extractFlag = 1;
   378    497     }
   379    498     for(i=1; i<argc; i++){
   380    499       if( argv[i][0]=='-' ){
   381    500         for(j=1; argv[i][j]; j++){
   382    501           switch( argv[i][j] ){
   383    502             case 'l':   listFlag = 1;    break;
   384    503             case 'n':   noCompress = 1;  break;
   385    504             case 'v':   verboseFlag = 1; break;
   386    505             case 'x':   extractFlag = 1; break;
          506  +          case 'e':   seeFlag++;       break;
   387    507             case '-':   break;
   388    508             default:    showHelp(argv[0]);
   389    509           }
   390    510         }
   391    511       }else if( zArchive==0 ){
   392    512         zArchive = argv[i];
   393    513       }else{
................................................................................
   394    514         azFiles = &argv[i];
   395    515         nFiles = argc - i;
   396    516         break;
   397    517       }
   398    518     }
   399    519     if( zArchive==0 ) showHelp(argv[0]);
   400    520     if( listFlag ){
   401         -    db_open(zArchive, 0);
          521  +    db_open(zArchive, 0, seeFlag);
   402    522       if( verboseFlag ){
   403    523         db_prepare(
   404    524             "SELECT name, sz, length(data), mode, datetime(mtime,'unixepoch')"
   405    525             " FROM sqlar ORDER BY name"
   406    526         );
   407    527         while( sqlite3_step(pStmt)==SQLITE_ROW ){
   408    528           printf("%10d %10d %03o %s %s\n", 
................................................................................
   419    539         while( sqlite3_step(pStmt)==SQLITE_ROW ){
   420    540           printf("%s\n", sqlite3_column_text(pStmt,0));
   421    541         }
   422    542       }
   423    543       db_close(1);
   424    544     }else if( extractFlag ){
   425    545       const char *zSql;
   426         -    db_open(zArchive, 0);
          546  +    db_open(zArchive, 0, seeFlag);
   427    547       if( nFiles ){
   428    548         NameList x;
   429    549         x.azName = azFiles;
   430    550         x.nName = nFiles;
   431    551         sqlite3_create_function(db, "name_on_list", 1, SQLITE_UTF8,
   432    552                                 (char*)&x, name_on_list, 0, 0);
   433    553         zSql = "SELECT name, mode, mtime, sz, data FROM sqlar"
................................................................................
   451    571                    sqlite3_column_int(pStmt,3),
   452    572                    sqlite3_column_blob(pStmt,4),
   453    573                    sqlite3_column_bytes(pStmt,4));
   454    574       }
   455    575       db_close(1);
   456    576     }else{
   457    577       if( azFiles==0 ) showHelp(argv[0]);
   458         -    db_open(zArchive, 1);
          578  +    db_open(zArchive, 1, seeFlag);
   459    579       for(i=0; i<nFiles; i++){
   460    580         add_file(azFiles[i], verboseFlag, noCompress);
   461    581       }
   462    582       db_close(1);
   463    583     }
   464    584     return 0;
   465    585   }

Changes to sqlarfs.c.

    13     13   #include <errno.h>
    14     14   #include <fcntl.h>
    15     15   #include <zlib.h>
    16     16   #include "sqlite3.h"
    17     17   #include <stdlib.h>
    18     18   #include <unistd.h>
    19     19   #include <sys/types.h>
           20  +#include <assert.h>
           21  +#include <ctype.h>
    20     22   
    21     23   /*
    22     24   ** Global state information about the archive
    23     25   */
    24     26   struct sGlobal {
    25     27     sqlite3 *db;           /* Open database connection */
    26     28     sqlite3_stmt *pStat;   /* Prepared statement to read stat info */
................................................................................
   213    215   
   214    216   static struct fuse_operations sqlarfs_methods = {
   215    217     .getattr = sqlarfs_getattr,
   216    218     .readdir = sqlarfs_readdir,
   217    219     .open   	= sqlarfs_open,
   218    220     .read    = sqlarfs_read,
   219    221   };
          222  +
          223  +/*
          224  +** Show a help message and quit.
          225  +*/
          226  +static void showHelp(const char *argv0){
          227  +  fprintf(stderr, "Usage: %s [options] archive mount-point\n", argv0);
          228  +  fprintf(stderr,
          229  +     "Options:\n"
          230  +     "   -e      Prompt for passphrase.  -ee to scramble the prompt\n"
          231  +  );
          232  +  exit(1);
          233  +}
          234  +
          235  +/* Maximum length of a pass-phrase */
          236  +#define MX_PASSPHRASE  40
          237  +
          238  +/*
          239  +** Scramble substitution matrix:
          240  +*/
          241  +static char aSubst[256];
          242  +
          243  +/*
          244  +** Descramble the password
          245  +*/
          246  +static void descramble(char *z){
          247  +  int i;
          248  +  for(i=0; z[i]; i++) z[i] = aSubst[(unsigned char)z[i]];
          249  +}
          250  +
          251  +/* Print a string in 5-letter groups */
          252  +static void printFive(const unsigned char *z){
          253  +  int i;
          254  +  for(i=0; z[i]; i++){
          255  +    if( i>0 && (i%5)==0 ) putchar(' ');
          256  +    putchar(z[i]);
          257  +  }
          258  +  putchar('\n');
          259  +}
          260  +
          261  +/* Return a pseudo-random integer between 0 and N-1 */
          262  +static int randint(int N){
          263  +  unsigned char x;
          264  +  assert( N<256 );
          265  +  sqlite3_randomness(1, &x);
          266  +  return x % N;
          267  +}
          268  +
          269  +/*
          270  +** Generate and print a random scrambling of letters a through z (omitting x)
          271  +** and set up the aSubst[] matrix to descramble.
          272  +*/
          273  +static void generateScrambleCode(void){
          274  +  unsigned char zOrig[30];
          275  +  unsigned char zA[30];
          276  +  unsigned char zB[30];
          277  +  int nA = 25;
          278  +  int nB = 0;
          279  +  int i;
          280  +  memcpy(zOrig, "abcdefghijklmnopqrstuvwyz", nA+1);
          281  +  memcpy(zA, zOrig, nA+1);
          282  +  assert( nA==(int)strlen((char*)zA) );
          283  +  for(i=0; i<sizeof(aSubst); i++) aSubst[i] = i;
          284  +  printFive(zA);
          285  +  while( nA>0 ){
          286  +    int x = randint(nA);
          287  +    zB[nB++] = zA[x];
          288  +    zA[x] = zA[--nA];
          289  +  }
          290  +  assert( nB==25 );
          291  +  zB[nB] = 0;
          292  +  printFive(zB);
          293  +  for(i=0; i<nB; i++) aSubst[zB[i]] = zOrig[i];
          294  +}
          295  +
          296  +/*
          297  +** Do a single prompt for a passphrase.  Store the results in the blob.
          298  +**
          299  +** If the FOSSIL_PWREADER environment variable is set, then it will
          300  +** be the name of a program that prompts the user for their password/
          301  +** passphrase in a secure manner.  The program should take one or more
          302  +** arguments which are the prompts and should output the acquired
          303  +** passphrase as a single line on stdout.  This function will read the
          304  +** output using popen().
          305  +**
          306  +** If FOSSIL_PWREADER is not set, or if it is not the name of an
          307  +** executable, then use the C-library getpass() routine.
          308  +**
          309  +** The return value is a pointer to a static buffer that is overwritten
          310  +** on subsequent calls to this same routine.
          311  +*/
          312  +static void prompt_for_passphrase(
          313  +  const char *zPrompt,    /* Passphrase prompt */
          314  +  int doScramble,         /* Scramble the input if true */
          315  +  char *zPassphrase       /* Write result here */
          316  +){
          317  +  char *z;
          318  +  int i;
          319  +  if( doScramble ){
          320  +    generateScrambleCode();
          321  +    z = getpass(zPrompt);
          322  +    if( z ) descramble(z);
          323  +    printf("\033[3A\033[J");  /* Erase previous three lines */
          324  +    fflush(stdout);
          325  +  }else{
          326  +    z = getpass(zPrompt);
          327  +  }
          328  +  while( isspace(z[0]) ) z++;
          329  +  for(i=0; i<MX_PASSPHRASE-1; i++){
          330  +    zPassphrase[i] = z[i];
          331  +  }
          332  +  while( i>0 && isspace(z[i-1]) ){ i--; }
          333  +  zPassphrase[i] = 0;
          334  +}
          335  +
          336  +
   220    337   int main(int argc, char **argv){
   221    338     int rc;
          339  +  int i, j;
          340  +  int seeFlag = 0;
          341  +  char *zArchive = 0;
          342  +  char *zMountPoint = 0;
   222    343     char *azNewArg[5];
   223         -  if( argc!=3 ){
   224         -    fprintf(stderr, "Usage: %s SQLAR-ARCHIVE MOUNT-POINT\n",
   225         -            argv[0]);
   226         -    exit(1);
          344  +  for(i=1; i<argc; i++){
          345  +    if( argv[i][0]=='-' ){
          346  +      for(j=1; argv[i][j]; j++){
          347  +        switch( argv[i][j] ){
          348  +          case 'e':   seeFlag++;       break;
          349  +          case '-':   break;
          350  +          default:    showHelp(argv[0]);
          351  +        }
          352  +      }
          353  +    }else if( zArchive==0 ){
          354  +      zArchive = argv[i];
          355  +    }else if( zMountPoint==0 ){
          356  +      zMountPoint = argv[i];
          357  +    }else{
          358  +      showHelp(argv[0]);
          359  +    }
   227    360     }
   228         -  rc = sqlite3_open(argv[1], &g.db);
          361  +  if( zMountPoint==0 ) showHelp(argv[0]);
          362  +  rc = sqlite3_open(zArchive, &g.db);
   229    363     if( rc!=SQLITE_OK ){
   230    364       fprintf(stderr, "Cannot open sqlar file [%s]\n", argv[1]);
   231    365       exit(1);
   232    366     }
          367  +  if( seeFlag ){
          368  +    char *zSql;
          369  +    char zPassPhrase[MX_PASSPHRASE+1];
          370  +#ifndef SQLITE_HAS_CODEC
          371  +    printf("WARNING:  The passphrase is a no-op because this build of\n"
          372  +           "sqlar is compiled without encryption capabilities.\n");
          373  +#endif
          374  +    memset(zPassPhrase, 0, sizeof(zPassPhrase));
          375  +    prompt_for_passphrase("passphrase: ", seeFlag>1, zPassPhrase);
          376  +    zSql = sqlite3_mprintf("PRAGMA key(%Q)", zPassPhrase);
          377  +    sqlite3_exec(g.db, zSql, 0, 0, 0);
          378  +    sqlite3_free(zSql);
          379  +  }
   233    380     rc = sqlite3_exec(g.db, "SELECT 1 FROM sqlar LIMIT 1", 0, 0, 0);
   234    381     if( rc!=SQLITE_OK ){
   235    382       fprintf(stderr, "File [%s] is not an SQLite archive\n", argv[1]);
   236    383       exit(1);
   237    384     }
   238    385     g.uid = getuid();
   239    386     g.gid = getgid();
   240    387     azNewArg[0] = argv[0];
   241    388     azNewArg[1] = "-f";
   242    389     azNewArg[2] = "-s";
   243         -  azNewArg[3] = argv[2];
          390  +  azNewArg[3] = zMountPoint;
   244    391     azNewArg[4] = 0;
   245    392     rc = fuse_main(4, azNewArg, &sqlarfs_methods, NULL);
   246    393     sqlite3_finalize(g.pStat);
   247    394     sqlite3_finalize(g.pFList);
   248    395     sqlite3_finalize(g.pExists);
   249    396     sqlite3_finalize(g.pRead);
   250    397     sqlite3_free(g.zCacheName);
   251    398     sqlite3_free(g.zCacheData);
   252    399     sqlite3_close(g.db);
   253    400     return rc;
   254    401   }