Fossil SCM

Enhancements to SEE integration.

mistachkin 2023-05-23 20:30 trunk merge
Commit f41cf0350500fa48abac523eb722ce3f94c81adf6eaf32193c09266284e21b0a
+370 -34
--- src/db.c
+++ src/db.c
@@ -31,25 +31,57 @@
3131
*/
3232
#include "config.h"
3333
#if defined(_WIN32)
3434
# if USE_SEE
3535
# include <windows.h>
36
+# define GETPID (int)GetCurrentProcessId
3637
# endif
3738
#else
3839
# include <pwd.h>
40
+# if USE_SEE
41
+# define GETPID getpid
42
+# endif
3943
#endif
4044
#if USE_SEE && !defined(SQLITE_HAS_CODEC)
4145
# define SQLITE_HAS_CODEC
46
+#endif
47
+#if USE_SEE && defined(__linux__)
48
+# include <sys/uio.h>
4249
#endif
4350
#include <sqlite3.h>
4451
#include <sys/types.h>
4552
#include <sys/stat.h>
4653
#include <unistd.h>
4754
#include <time.h>
55
+
56
+/* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */
57
+#if USE_SEE
58
+#if defined(_WIN32)
59
+typedef DWORD PID_T;
60
+#else
61
+typedef pid_t PID_T;
62
+#endif
63
+#endif
64
+
4865
#include "db.h"
4966
5067
#if INTERFACE
68
+/*
69
+** Type definitions used for handling the saved encryption key for SEE.
70
+*/
71
+#if !defined(_WIN32)
72
+typedef void *LPVOID;
73
+typedef size_t SIZE_T;
74
+#endif
75
+
76
+/*
77
+** Operations for db_maybe_handle_saved_encryption_key_for_process, et al.
78
+*/
79
+#define SEE_KEY_READ ((int)0)
80
+#define SEE_KEY_WRITE ((int)1)
81
+#define SEE_KEY_ZERO ((int)2)
82
+
5183
/*
5284
** An single SQL statement is represented as an instance of the following
5385
** structure.
5486
*/
5587
struct Stmt {
@@ -1539,11 +1571,30 @@
15391571
static char *zSavedKey = 0;
15401572
15411573
/*
15421574
** This is the size of the saved database encryption key, in bytes.
15431575
*/
1544
-size_t savedKeySize = 0;
1576
+static size_t savedKeySize = 0;
1577
+
1578
+/*
1579
+** This function returns non-zero if there is a saved database encryption
1580
+** key available.
1581
+*/
1582
+int db_have_saved_encryption_key(){
1583
+ return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize);
1584
+}
1585
+
1586
+/*
1587
+** This function returns non-zero if the specified database encryption key
1588
+** is valid.
1589
+*/
1590
+int db_is_valid_saved_encryption_key(const char *p, size_t n){
1591
+ if( p==0 ) return 0;
1592
+ if( n==0 ) return 0;
1593
+ if( p[0]==0 ) return 0;
1594
+ return 1;
1595
+}
15451596
15461597
/*
15471598
** This function returns the saved database encryption key -OR- zero if
15481599
** no database encryption key is saved.
15491600
*/
@@ -1556,10 +1607,36 @@
15561607
** -OR- zero if no database encryption key is saved.
15571608
*/
15581609
size_t db_get_saved_encryption_key_size(){
15591610
return savedKeySize;
15601611
}
1612
+
1613
+/*
1614
+** This function arranges for the saved database encryption key buffer
1615
+** to be allocated and then sets up the environment variable to allow
1616
+** a child process to initialize it with the actual database encryption
1617
+** key.
1618
+*/
1619
+void db_setup_for_saved_encryption_key(){
1620
+ void *p = NULL;
1621
+ size_t n = 0;
1622
+ size_t pageSize = 0;
1623
+ Blob pidKey;
1624
+
1625
+ assert( !db_have_saved_encryption_key() );
1626
+ db_unsave_encryption_key();
1627
+ fossil_get_page_size(&pageSize);
1628
+ assert( pageSize>0 );
1629
+ p = fossil_secure_alloc_page(&n);
1630
+ assert( p!=NULL );
1631
+ assert( n==pageSize );
1632
+ blob_zero(&pidKey);
1633
+ blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n);
1634
+ fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey));
1635
+ zSavedKey = p;
1636
+ savedKeySize = n;
1637
+}
15611638
15621639
/*
15631640
** This function arranges for the database encryption key to be securely
15641641
** saved in non-pagable memory (on platforms where this is possible).
15651642
*/
@@ -1569,10 +1646,11 @@
15691646
void *p = NULL;
15701647
size_t n = 0;
15711648
size_t pageSize = 0;
15721649
size_t blobSize = 0;
15731650
1651
+ assert( !db_have_saved_encryption_key() );
15741652
blobSize = blob_size(pKey);
15751653
if( blobSize==0 ) return;
15761654
fossil_get_page_size(&pageSize);
15771655
assert( pageSize>0 );
15781656
if( blobSize>pageSize ){
@@ -1599,11 +1677,11 @@
15991677
16001678
/*
16011679
** This function sets the saved database encryption key to the specified
16021680
** string value, allocating or freeing the underlying memory if needed.
16031681
*/
1604
-void db_set_saved_encryption_key(
1682
+static void db_set_saved_encryption_key(
16051683
Blob *pKey
16061684
){
16071685
if( zSavedKey!=NULL ){
16081686
size_t blobSize = blob_size(pKey);
16091687
if( blobSize==0 ){
@@ -1619,25 +1697,214 @@
16191697
}else{
16201698
db_save_encryption_key(pKey);
16211699
}
16221700
}
16231701
1702
+/*
1703
+** WEBPAGE: setseekey
1704
+**
1705
+** Sets the sets the saved database encryption key to one that gets passed
1706
+** via the "key" query string parameter. If the saved database encryption
1707
+** key has already been set, does nothing. This web page does not produce
1708
+** any output on success or failure. No permissions are required and none
1709
+** are checked (partially due to lack of encrypted database access).
1710
+**
1711
+** Query parameters:
1712
+**
1713
+** key The string to set as the saved database encryption
1714
+** key.
1715
+*/
1716
+void db_set_see_key_page(void){
1717
+ Blob key;
1718
+ const char *zKey;
1719
+ if( db_have_saved_encryption_key() ){
1720
+ fossil_trace("SEE: encryption key was already set\n");
1721
+ return;
1722
+ }
1723
+ zKey = P("key");
1724
+ blob_init(&key, 0, 0);
1725
+ if( zKey!=0 ){
1726
+ PID_T processId;
1727
+ blob_set(&key, zKey);
1728
+ db_set_saved_encryption_key(&key);
1729
+ processId = db_maybe_handle_saved_encryption_key_for_process(
1730
+ SEE_KEY_WRITE
1731
+ );
1732
+ fossil_trace("SEE: set encryption key for process %lu, length %u\n",
1733
+ (unsigned long)processId, blob_size(&key));
1734
+ }else{
1735
+ fossil_trace("SEE: no encryption key specified\n");
1736
+ }
1737
+ blob_reset(&key);
1738
+}
1739
+
1740
+/*
1741
+** WEBPAGE: unsetseekey
1742
+**
1743
+** Sets the saved database encryption key to zeros in the current and parent
1744
+** Fossil processes. This web page does not produce any output on success
1745
+** or failure. Setup permission is required.
1746
+*/
1747
+void db_unset_see_key_page(void){
1748
+ PID_T processId;
1749
+ login_check_credentials();
1750
+ if( !g.perm.Setup ){ login_needed(0); return; }
1751
+ processId = db_maybe_handle_saved_encryption_key_for_process(
1752
+ SEE_KEY_ZERO
1753
+ );
1754
+ fossil_trace("SEE: unset encryption key for process %lu\n",
1755
+ (unsigned long)processId);
1756
+}
1757
+
1758
+/*
1759
+** This function reads the saved database encryption key from the
1760
+** specified Fossil parent process. This is only necessary (or
1761
+** functional) on Windows or Linux.
1762
+*/
1763
+static void db_read_saved_encryption_key_from_process(
1764
+ PID_T processId, /* Identifier for Fossil parent process. */
1765
+ LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1766
+ SIZE_T nSize /* Size of saved key buffer in the parent process. */
1767
+){
1768
+ void *p = NULL;
1769
+ size_t n = 0;
1770
+ size_t pageSize = 0;
1771
+
1772
+ fossil_get_page_size(&pageSize);
1773
+ assert( pageSize>0 );
1774
+ if( nSize>pageSize ){
1775
+ fossil_panic("key too large: %u versus %u", nSize, pageSize);
1776
+ }
1777
+ p = fossil_secure_alloc_page(&n);
1778
+ assert( p!=NULL );
1779
+ assert( n==pageSize );
1780
+ assert( n>=nSize );
1781
+ {
1782
+#if defined(_WIN32)
1783
+ HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
1784
+ if( hProcess!=NULL ){
1785
+ SIZE_T nRead = 0;
1786
+ if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
1787
+ CloseHandle(hProcess);
1788
+ if( nRead==nSize ){
1789
+ db_unsave_encryption_key();
1790
+ zSavedKey = p;
1791
+ savedKeySize = n;
1792
+ }else{
1793
+ fossil_secure_free_page(p, n);
1794
+ fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
1795
+ nRead, nSize, pAddress, processId);
1796
+ }
1797
+ }else{
1798
+ CloseHandle(hProcess);
1799
+ fossil_secure_free_page(p, n);
1800
+ fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1801
+ pAddress, processId, GetLastError());
1802
+ }
1803
+ }else{
1804
+ fossil_secure_free_page(p, n);
1805
+ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1806
+ }
1807
+#elif defined(__linux__)
1808
+ ssize_t nRead;
1809
+ struct iovec liov = {0};
1810
+ struct iovec riov = {0};
1811
+ liov.iov_base = p;
1812
+ liov.iov_len = n;
1813
+ riov.iov_base = pAddress;
1814
+ riov.iov_len = nSize;
1815
+ nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0);
1816
+ if( nRead==nSize ){
1817
+ db_unsave_encryption_key();
1818
+ zSavedKey = p;
1819
+ savedKeySize = n;
1820
+ }else{
1821
+ fossil_secure_free_page(p, n);
1822
+ fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu",
1823
+ nRead, nSize, pAddress, (unsigned long)processId);
1824
+ }
1825
+#else
1826
+ fossil_secure_free_page(p, n);
1827
+ fossil_trace("db_read_saved_encryption_key_from_process unsupported");
1828
+#endif
1829
+ }
1830
+}
1831
+
1832
+/*
1833
+** This function writes the saved database encryption key into the
1834
+** specified Fossil parent process. This is only necessary (or
1835
+** functional) on Windows or Linux.
1836
+*/
1837
+static void db_write_saved_encryption_key_to_process(
1838
+ PID_T processId, /* Identifier for Fossil parent process. */
1839
+ LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1840
+ SIZE_T nSize /* Size of saved key buffer in the parent process. */
1841
+){
1842
+ void *p = db_get_saved_encryption_key();
1843
+ size_t n = db_get_saved_encryption_key_size();
1844
+ size_t pageSize = 0;
1845
+
1846
+ fossil_get_page_size(&pageSize);
1847
+ assert( pageSize>0 );
1848
+ if( nSize>pageSize ){
1849
+ fossil_panic("key too large: %u versus %u", nSize, pageSize);
1850
+ }
1851
+ assert( p!=NULL );
1852
+ assert( n==pageSize );
1853
+ assert( n>=nSize );
1854
+ {
16241855
#if defined(_WIN32)
1856
+ HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
1857
+ FALSE, processId);
1858
+ if( hProcess!=NULL ){
1859
+ SIZE_T nWrite = 0;
1860
+ if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
1861
+ CloseHandle(hProcess);
1862
+ if( nWrite!=nSize ){
1863
+ fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu",
1864
+ nWrite, nSize, pAddress, processId);
1865
+ }
1866
+ }else{
1867
+ CloseHandle(hProcess);
1868
+ fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize,
1869
+ pAddress, processId, GetLastError());
1870
+ }
1871
+ }else{
1872
+ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1873
+ }
1874
+#elif defined(__linux__)
1875
+ ssize_t nWrite;
1876
+ struct iovec liov = {0};
1877
+ struct iovec riov = {0};
1878
+ liov.iov_base = p;
1879
+ liov.iov_len = n;
1880
+ riov.iov_base = pAddress;
1881
+ riov.iov_len = nSize;
1882
+ nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
1883
+ if( nWrite!=nSize ){
1884
+ fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu",
1885
+ nWrite, nSize, pAddress, (unsigned long)processId);
1886
+ }
1887
+#else
1888
+ fossil_trace("db_write_saved_encryption_key_to_process unsupported");
1889
+#endif
1890
+ }
1891
+}
1892
+
16251893
/*
1626
-** This function sets the saved database encryption key to one that gets
1627
-** read from the specified Fossil parent process. This is only necessary
1628
-** (or functional) on Windows.
1894
+** This function zeros the saved database encryption key in the specified
1895
+** Fossil parent process. This is only necessary (or functional) on
1896
+** Windows or Linux.
16291897
*/
1630
-void db_read_saved_encryption_key_from_process(
1631
- DWORD processId, /* Identifier for Fossil parent process. */
1898
+static void db_zero_saved_encryption_key_in_process(
1899
+ PID_T processId, /* Identifier for Fossil parent process. */
16321900
LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
16331901
SIZE_T nSize /* Size of saved key buffer in the parent process. */
16341902
){
16351903
void *p = NULL;
16361904
size_t n = 0;
16371905
size_t pageSize = 0;
1638
- HANDLE hProcess = NULL;
16391906
16401907
fossil_get_page_size(&pageSize);
16411908
assert( pageSize>0 );
16421909
if( nSize>pageSize ){
16431910
fossil_panic("key too large: %u versus %u", nSize, pageSize);
@@ -1644,42 +1911,66 @@
16441911
}
16451912
p = fossil_secure_alloc_page(&n);
16461913
assert( p!=NULL );
16471914
assert( n==pageSize );
16481915
assert( n>=nSize );
1649
- hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
1650
- if( hProcess!=NULL ){
1651
- SIZE_T nRead = 0;
1652
- if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
1653
- CloseHandle(hProcess);
1654
- if( nRead==nSize ){
1655
- db_unsave_encryption_key();
1656
- zSavedKey = p;
1657
- savedKeySize = n;
1658
- }else{
1659
- fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
1660
- nRead, nSize, pAddress, processId);
1661
- }
1662
- }else{
1663
- CloseHandle(hProcess);
1664
- fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1665
- pAddress, processId, GetLastError());
1666
- }
1667
- }else{
1668
- fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1916
+ {
1917
+#if defined(_WIN32)
1918
+ HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
1919
+ FALSE, processId);
1920
+ if( hProcess!=NULL ){
1921
+ SIZE_T nWrite = 0;
1922
+ if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
1923
+ CloseHandle(hProcess);
1924
+ fossil_secure_free_page(p, n);
1925
+ if( nWrite!=nSize ){
1926
+ fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu",
1927
+ nWrite, nSize, pAddress, processId);
1928
+ }
1929
+ }else{
1930
+ CloseHandle(hProcess);
1931
+ fossil_secure_free_page(p, n);
1932
+ fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize,
1933
+ pAddress, processId, GetLastError());
1934
+ }
1935
+ }else{
1936
+ fossil_secure_free_page(p, n);
1937
+ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1938
+ }
1939
+#elif defined(__linux__)
1940
+ ssize_t nWrite;
1941
+ struct iovec liov = {0};
1942
+ struct iovec riov = {0};
1943
+ liov.iov_base = p;
1944
+ liov.iov_len = n;
1945
+ riov.iov_base = pAddress;
1946
+ riov.iov_len = nSize;
1947
+ nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
1948
+ if( nWrite!=nSize ){
1949
+ fossil_secure_free_page(p, n);
1950
+ fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu",
1951
+ nWrite, nSize, pAddress, (unsigned long)processId);
1952
+ }
1953
+#else
1954
+ fossil_secure_free_page(p, n);
1955
+ fossil_trace("db_zero_saved_encryption_key_in_process unsupported");
1956
+#endif
16691957
}
16701958
}
16711959
16721960
/*
16731961
** This function evaluates the specified TH1 script and attempts to parse
16741962
** its result as a colon-delimited triplet containing a process identifier,
16751963
** address, and size (in bytes) of the database encryption key. This is
1676
-** only necessary (or functional) on Windows.
1964
+** only necessary (or functional) on Windows or Linux.
16771965
*/
1678
-void db_read_saved_encryption_key_from_process_via_th1(
1679
- const char *zConfig /* The TH1 script to evaluate. */
1966
+static PID_T db_handle_saved_encryption_key_for_process_via_th1(
1967
+ const char *zConfig, /* The TH1 script to evaluate. */
1968
+ int eType /* Non-zero to write key to parent process -OR-
1969
+ * zero to read it from the parent process. */
16801970
){
1971
+ PID_T processId = 0;
16811972
int rc;
16821973
char *zResult;
16831974
char *zPwd = file_getcwd(0, 0);
16841975
Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
16851976
rc = Th_Eval(g.interp, 0, zConfig, -1);
@@ -1686,20 +1977,65 @@
16861977
zResult = (char*)Th_GetResult(g.interp, 0);
16871978
if( rc!=TH_OK ){
16881979
fossil_fatal("script for pid key failed: %s", zResult);
16891980
}
16901981
if( zResult ){
1691
- DWORD processId = 0;
16921982
LPVOID pAddress = NULL;
16931983
SIZE_T nSize = 0;
16941984
parse_pid_key_value(zResult, &processId, &pAddress, &nSize);
1695
- db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
1985
+ if( eType==SEE_KEY_READ ){
1986
+ db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
1987
+ }else if( eType==SEE_KEY_WRITE ){
1988
+ db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
1989
+ }else if( eType==SEE_KEY_ZERO ){
1990
+ db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
1991
+ }else{
1992
+ fossil_panic("unsupported SEE key operation %d", eType);
1993
+ }
16961994
}
16971995
file_chdir(zPwd, 0);
16981996
fossil_free(zPwd);
1997
+ return processId;
16991998
}
1700
-#endif /* defined(_WIN32) */
1999
+
2000
+/*
2001
+** This function sets the saved database encryption key to one that gets
2002
+** read from the specified Fossil parent process, if applicable. This is
2003
+** only necessary (or functional) on Windows or Linux.
2004
+*/
2005
+PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){
2006
+ PID_T processId = 0;
2007
+ g.zPidKey = find_option("usepidkey",0,1);
2008
+ if( !g.zPidKey ){
2009
+ g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY");
2010
+ }
2011
+ if( g.zPidKey ){
2012
+ LPVOID pAddress = NULL;
2013
+ SIZE_T nSize = 0;
2014
+ parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize);
2015
+ if( eType==SEE_KEY_READ ){
2016
+ db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
2017
+ }else if( eType==SEE_KEY_WRITE ){
2018
+ db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
2019
+ }else if( eType==SEE_KEY_ZERO ){
2020
+ db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
2021
+ }else{
2022
+ fossil_panic("unsupported SEE key operation %d", eType);
2023
+ }
2024
+ }else{
2025
+ const char *zSeeDbConfig = find_option("seedbcfg",0,1);
2026
+ if( !zSeeDbConfig ){
2027
+ zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG");
2028
+ }
2029
+ if( zSeeDbConfig ){
2030
+ processId = db_handle_saved_encryption_key_for_process_via_th1(
2031
+ zSeeDbConfig, eType
2032
+ );
2033
+ }
2034
+ }
2035
+ return processId;
2036
+}
17012037
#endif /* USE_SEE */
17022038
17032039
/*
17042040
** If the database file zDbFile has a name that suggests that it is
17052041
** encrypted, then prompt for the database encryption key and return it
17062042
--- src/db.c
+++ src/db.c
@@ -31,25 +31,57 @@
31 */
32 #include "config.h"
33 #if defined(_WIN32)
34 # if USE_SEE
35 # include <windows.h>
 
36 # endif
37 #else
38 # include <pwd.h>
 
 
 
39 #endif
40 #if USE_SEE && !defined(SQLITE_HAS_CODEC)
41 # define SQLITE_HAS_CODEC
 
 
 
42 #endif
43 #include <sqlite3.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <time.h>
 
 
 
 
 
 
 
 
 
 
48 #include "db.h"
49
50 #if INTERFACE
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51 /*
52 ** An single SQL statement is represented as an instance of the following
53 ** structure.
54 */
55 struct Stmt {
@@ -1539,11 +1571,30 @@
1539 static char *zSavedKey = 0;
1540
1541 /*
1542 ** This is the size of the saved database encryption key, in bytes.
1543 */
1544 size_t savedKeySize = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1545
1546 /*
1547 ** This function returns the saved database encryption key -OR- zero if
1548 ** no database encryption key is saved.
1549 */
@@ -1556,10 +1607,36 @@
1556 ** -OR- zero if no database encryption key is saved.
1557 */
1558 size_t db_get_saved_encryption_key_size(){
1559 return savedKeySize;
1560 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1561
1562 /*
1563 ** This function arranges for the database encryption key to be securely
1564 ** saved in non-pagable memory (on platforms where this is possible).
1565 */
@@ -1569,10 +1646,11 @@
1569 void *p = NULL;
1570 size_t n = 0;
1571 size_t pageSize = 0;
1572 size_t blobSize = 0;
1573
 
1574 blobSize = blob_size(pKey);
1575 if( blobSize==0 ) return;
1576 fossil_get_page_size(&pageSize);
1577 assert( pageSize>0 );
1578 if( blobSize>pageSize ){
@@ -1599,11 +1677,11 @@
1599
1600 /*
1601 ** This function sets the saved database encryption key to the specified
1602 ** string value, allocating or freeing the underlying memory if needed.
1603 */
1604 void db_set_saved_encryption_key(
1605 Blob *pKey
1606 ){
1607 if( zSavedKey!=NULL ){
1608 size_t blobSize = blob_size(pKey);
1609 if( blobSize==0 ){
@@ -1619,25 +1697,214 @@
1619 }else{
1620 db_save_encryption_key(pKey);
1621 }
1622 }
1623
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1624 #if defined(_WIN32)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1625 /*
1626 ** This function sets the saved database encryption key to one that gets
1627 ** read from the specified Fossil parent process. This is only necessary
1628 ** (or functional) on Windows.
1629 */
1630 void db_read_saved_encryption_key_from_process(
1631 DWORD processId, /* Identifier for Fossil parent process. */
1632 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1633 SIZE_T nSize /* Size of saved key buffer in the parent process. */
1634 ){
1635 void *p = NULL;
1636 size_t n = 0;
1637 size_t pageSize = 0;
1638 HANDLE hProcess = NULL;
1639
1640 fossil_get_page_size(&pageSize);
1641 assert( pageSize>0 );
1642 if( nSize>pageSize ){
1643 fossil_panic("key too large: %u versus %u", nSize, pageSize);
@@ -1644,42 +1911,66 @@
1644 }
1645 p = fossil_secure_alloc_page(&n);
1646 assert( p!=NULL );
1647 assert( n==pageSize );
1648 assert( n>=nSize );
1649 hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
1650 if( hProcess!=NULL ){
1651 SIZE_T nRead = 0;
1652 if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
1653 CloseHandle(hProcess);
1654 if( nRead==nSize ){
1655 db_unsave_encryption_key();
1656 zSavedKey = p;
1657 savedKeySize = n;
1658 }else{
1659 fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
1660 nRead, nSize, pAddress, processId);
1661 }
1662 }else{
1663 CloseHandle(hProcess);
1664 fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1665 pAddress, processId, GetLastError());
1666 }
1667 }else{
1668 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1669 }
1670 }
1671
1672 /*
1673 ** This function evaluates the specified TH1 script and attempts to parse
1674 ** its result as a colon-delimited triplet containing a process identifier,
1675 ** address, and size (in bytes) of the database encryption key. This is
1676 ** only necessary (or functional) on Windows.
1677 */
1678 void db_read_saved_encryption_key_from_process_via_th1(
1679 const char *zConfig /* The TH1 script to evaluate. */
 
 
1680 ){
 
1681 int rc;
1682 char *zResult;
1683 char *zPwd = file_getcwd(0, 0);
1684 Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
1685 rc = Th_Eval(g.interp, 0, zConfig, -1);
@@ -1686,20 +1977,65 @@
1686 zResult = (char*)Th_GetResult(g.interp, 0);
1687 if( rc!=TH_OK ){
1688 fossil_fatal("script for pid key failed: %s", zResult);
1689 }
1690 if( zResult ){
1691 DWORD processId = 0;
1692 LPVOID pAddress = NULL;
1693 SIZE_T nSize = 0;
1694 parse_pid_key_value(zResult, &processId, &pAddress, &nSize);
1695 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
 
 
 
 
 
 
 
 
1696 }
1697 file_chdir(zPwd, 0);
1698 fossil_free(zPwd);
 
1699 }
1700 #endif /* defined(_WIN32) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1701 #endif /* USE_SEE */
1702
1703 /*
1704 ** If the database file zDbFile has a name that suggests that it is
1705 ** encrypted, then prompt for the database encryption key and return it
1706
--- src/db.c
+++ src/db.c
@@ -31,25 +31,57 @@
31 */
32 #include "config.h"
33 #if defined(_WIN32)
34 # if USE_SEE
35 # include <windows.h>
36 # define GETPID (int)GetCurrentProcessId
37 # endif
38 #else
39 # include <pwd.h>
40 # if USE_SEE
41 # define GETPID getpid
42 # endif
43 #endif
44 #if USE_SEE && !defined(SQLITE_HAS_CODEC)
45 # define SQLITE_HAS_CODEC
46 #endif
47 #if USE_SEE && defined(__linux__)
48 # include <sys/uio.h>
49 #endif
50 #include <sqlite3.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <unistd.h>
54 #include <time.h>
55
56 /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */
57 #if USE_SEE
58 #if defined(_WIN32)
59 typedef DWORD PID_T;
60 #else
61 typedef pid_t PID_T;
62 #endif
63 #endif
64
65 #include "db.h"
66
67 #if INTERFACE
68 /*
69 ** Type definitions used for handling the saved encryption key for SEE.
70 */
71 #if !defined(_WIN32)
72 typedef void *LPVOID;
73 typedef size_t SIZE_T;
74 #endif
75
76 /*
77 ** Operations for db_maybe_handle_saved_encryption_key_for_process, et al.
78 */
79 #define SEE_KEY_READ ((int)0)
80 #define SEE_KEY_WRITE ((int)1)
81 #define SEE_KEY_ZERO ((int)2)
82
83 /*
84 ** An single SQL statement is represented as an instance of the following
85 ** structure.
86 */
87 struct Stmt {
@@ -1539,11 +1571,30 @@
1571 static char *zSavedKey = 0;
1572
1573 /*
1574 ** This is the size of the saved database encryption key, in bytes.
1575 */
1576 static size_t savedKeySize = 0;
1577
1578 /*
1579 ** This function returns non-zero if there is a saved database encryption
1580 ** key available.
1581 */
1582 int db_have_saved_encryption_key(){
1583 return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize);
1584 }
1585
1586 /*
1587 ** This function returns non-zero if the specified database encryption key
1588 ** is valid.
1589 */
1590 int db_is_valid_saved_encryption_key(const char *p, size_t n){
1591 if( p==0 ) return 0;
1592 if( n==0 ) return 0;
1593 if( p[0]==0 ) return 0;
1594 return 1;
1595 }
1596
1597 /*
1598 ** This function returns the saved database encryption key -OR- zero if
1599 ** no database encryption key is saved.
1600 */
@@ -1556,10 +1607,36 @@
1607 ** -OR- zero if no database encryption key is saved.
1608 */
1609 size_t db_get_saved_encryption_key_size(){
1610 return savedKeySize;
1611 }
1612
1613 /*
1614 ** This function arranges for the saved database encryption key buffer
1615 ** to be allocated and then sets up the environment variable to allow
1616 ** a child process to initialize it with the actual database encryption
1617 ** key.
1618 */
1619 void db_setup_for_saved_encryption_key(){
1620 void *p = NULL;
1621 size_t n = 0;
1622 size_t pageSize = 0;
1623 Blob pidKey;
1624
1625 assert( !db_have_saved_encryption_key() );
1626 db_unsave_encryption_key();
1627 fossil_get_page_size(&pageSize);
1628 assert( pageSize>0 );
1629 p = fossil_secure_alloc_page(&n);
1630 assert( p!=NULL );
1631 assert( n==pageSize );
1632 blob_zero(&pidKey);
1633 blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n);
1634 fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey));
1635 zSavedKey = p;
1636 savedKeySize = n;
1637 }
1638
1639 /*
1640 ** This function arranges for the database encryption key to be securely
1641 ** saved in non-pagable memory (on platforms where this is possible).
1642 */
@@ -1569,10 +1646,11 @@
1646 void *p = NULL;
1647 size_t n = 0;
1648 size_t pageSize = 0;
1649 size_t blobSize = 0;
1650
1651 assert( !db_have_saved_encryption_key() );
1652 blobSize = blob_size(pKey);
1653 if( blobSize==0 ) return;
1654 fossil_get_page_size(&pageSize);
1655 assert( pageSize>0 );
1656 if( blobSize>pageSize ){
@@ -1599,11 +1677,11 @@
1677
1678 /*
1679 ** This function sets the saved database encryption key to the specified
1680 ** string value, allocating or freeing the underlying memory if needed.
1681 */
1682 static void db_set_saved_encryption_key(
1683 Blob *pKey
1684 ){
1685 if( zSavedKey!=NULL ){
1686 size_t blobSize = blob_size(pKey);
1687 if( blobSize==0 ){
@@ -1619,25 +1697,214 @@
1697 }else{
1698 db_save_encryption_key(pKey);
1699 }
1700 }
1701
1702 /*
1703 ** WEBPAGE: setseekey
1704 **
1705 ** Sets the sets the saved database encryption key to one that gets passed
1706 ** via the "key" query string parameter. If the saved database encryption
1707 ** key has already been set, does nothing. This web page does not produce
1708 ** any output on success or failure. No permissions are required and none
1709 ** are checked (partially due to lack of encrypted database access).
1710 **
1711 ** Query parameters:
1712 **
1713 ** key The string to set as the saved database encryption
1714 ** key.
1715 */
1716 void db_set_see_key_page(void){
1717 Blob key;
1718 const char *zKey;
1719 if( db_have_saved_encryption_key() ){
1720 fossil_trace("SEE: encryption key was already set\n");
1721 return;
1722 }
1723 zKey = P("key");
1724 blob_init(&key, 0, 0);
1725 if( zKey!=0 ){
1726 PID_T processId;
1727 blob_set(&key, zKey);
1728 db_set_saved_encryption_key(&key);
1729 processId = db_maybe_handle_saved_encryption_key_for_process(
1730 SEE_KEY_WRITE
1731 );
1732 fossil_trace("SEE: set encryption key for process %lu, length %u\n",
1733 (unsigned long)processId, blob_size(&key));
1734 }else{
1735 fossil_trace("SEE: no encryption key specified\n");
1736 }
1737 blob_reset(&key);
1738 }
1739
1740 /*
1741 ** WEBPAGE: unsetseekey
1742 **
1743 ** Sets the saved database encryption key to zeros in the current and parent
1744 ** Fossil processes. This web page does not produce any output on success
1745 ** or failure. Setup permission is required.
1746 */
1747 void db_unset_see_key_page(void){
1748 PID_T processId;
1749 login_check_credentials();
1750 if( !g.perm.Setup ){ login_needed(0); return; }
1751 processId = db_maybe_handle_saved_encryption_key_for_process(
1752 SEE_KEY_ZERO
1753 );
1754 fossil_trace("SEE: unset encryption key for process %lu\n",
1755 (unsigned long)processId);
1756 }
1757
1758 /*
1759 ** This function reads the saved database encryption key from the
1760 ** specified Fossil parent process. This is only necessary (or
1761 ** functional) on Windows or Linux.
1762 */
1763 static void db_read_saved_encryption_key_from_process(
1764 PID_T processId, /* Identifier for Fossil parent process. */
1765 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1766 SIZE_T nSize /* Size of saved key buffer in the parent process. */
1767 ){
1768 void *p = NULL;
1769 size_t n = 0;
1770 size_t pageSize = 0;
1771
1772 fossil_get_page_size(&pageSize);
1773 assert( pageSize>0 );
1774 if( nSize>pageSize ){
1775 fossil_panic("key too large: %u versus %u", nSize, pageSize);
1776 }
1777 p = fossil_secure_alloc_page(&n);
1778 assert( p!=NULL );
1779 assert( n==pageSize );
1780 assert( n>=nSize );
1781 {
1782 #if defined(_WIN32)
1783 HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
1784 if( hProcess!=NULL ){
1785 SIZE_T nRead = 0;
1786 if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
1787 CloseHandle(hProcess);
1788 if( nRead==nSize ){
1789 db_unsave_encryption_key();
1790 zSavedKey = p;
1791 savedKeySize = n;
1792 }else{
1793 fossil_secure_free_page(p, n);
1794 fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
1795 nRead, nSize, pAddress, processId);
1796 }
1797 }else{
1798 CloseHandle(hProcess);
1799 fossil_secure_free_page(p, n);
1800 fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1801 pAddress, processId, GetLastError());
1802 }
1803 }else{
1804 fossil_secure_free_page(p, n);
1805 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1806 }
1807 #elif defined(__linux__)
1808 ssize_t nRead;
1809 struct iovec liov = {0};
1810 struct iovec riov = {0};
1811 liov.iov_base = p;
1812 liov.iov_len = n;
1813 riov.iov_base = pAddress;
1814 riov.iov_len = nSize;
1815 nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0);
1816 if( nRead==nSize ){
1817 db_unsave_encryption_key();
1818 zSavedKey = p;
1819 savedKeySize = n;
1820 }else{
1821 fossil_secure_free_page(p, n);
1822 fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu",
1823 nRead, nSize, pAddress, (unsigned long)processId);
1824 }
1825 #else
1826 fossil_secure_free_page(p, n);
1827 fossil_trace("db_read_saved_encryption_key_from_process unsupported");
1828 #endif
1829 }
1830 }
1831
1832 /*
1833 ** This function writes the saved database encryption key into the
1834 ** specified Fossil parent process. This is only necessary (or
1835 ** functional) on Windows or Linux.
1836 */
1837 static void db_write_saved_encryption_key_to_process(
1838 PID_T processId, /* Identifier for Fossil parent process. */
1839 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1840 SIZE_T nSize /* Size of saved key buffer in the parent process. */
1841 ){
1842 void *p = db_get_saved_encryption_key();
1843 size_t n = db_get_saved_encryption_key_size();
1844 size_t pageSize = 0;
1845
1846 fossil_get_page_size(&pageSize);
1847 assert( pageSize>0 );
1848 if( nSize>pageSize ){
1849 fossil_panic("key too large: %u versus %u", nSize, pageSize);
1850 }
1851 assert( p!=NULL );
1852 assert( n==pageSize );
1853 assert( n>=nSize );
1854 {
1855 #if defined(_WIN32)
1856 HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
1857 FALSE, processId);
1858 if( hProcess!=NULL ){
1859 SIZE_T nWrite = 0;
1860 if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
1861 CloseHandle(hProcess);
1862 if( nWrite!=nSize ){
1863 fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu",
1864 nWrite, nSize, pAddress, processId);
1865 }
1866 }else{
1867 CloseHandle(hProcess);
1868 fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize,
1869 pAddress, processId, GetLastError());
1870 }
1871 }else{
1872 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1873 }
1874 #elif defined(__linux__)
1875 ssize_t nWrite;
1876 struct iovec liov = {0};
1877 struct iovec riov = {0};
1878 liov.iov_base = p;
1879 liov.iov_len = n;
1880 riov.iov_base = pAddress;
1881 riov.iov_len = nSize;
1882 nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
1883 if( nWrite!=nSize ){
1884 fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu",
1885 nWrite, nSize, pAddress, (unsigned long)processId);
1886 }
1887 #else
1888 fossil_trace("db_write_saved_encryption_key_to_process unsupported");
1889 #endif
1890 }
1891 }
1892
1893 /*
1894 ** This function zeros the saved database encryption key in the specified
1895 ** Fossil parent process. This is only necessary (or functional) on
1896 ** Windows or Linux.
1897 */
1898 static void db_zero_saved_encryption_key_in_process(
1899 PID_T processId, /* Identifier for Fossil parent process. */
1900 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1901 SIZE_T nSize /* Size of saved key buffer in the parent process. */
1902 ){
1903 void *p = NULL;
1904 size_t n = 0;
1905 size_t pageSize = 0;
 
1906
1907 fossil_get_page_size(&pageSize);
1908 assert( pageSize>0 );
1909 if( nSize>pageSize ){
1910 fossil_panic("key too large: %u versus %u", nSize, pageSize);
@@ -1644,42 +1911,66 @@
1911 }
1912 p = fossil_secure_alloc_page(&n);
1913 assert( p!=NULL );
1914 assert( n==pageSize );
1915 assert( n>=nSize );
1916 {
1917 #if defined(_WIN32)
1918 HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
1919 FALSE, processId);
1920 if( hProcess!=NULL ){
1921 SIZE_T nWrite = 0;
1922 if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
1923 CloseHandle(hProcess);
1924 fossil_secure_free_page(p, n);
1925 if( nWrite!=nSize ){
1926 fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu",
1927 nWrite, nSize, pAddress, processId);
1928 }
1929 }else{
1930 CloseHandle(hProcess);
1931 fossil_secure_free_page(p, n);
1932 fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize,
1933 pAddress, processId, GetLastError());
1934 }
1935 }else{
1936 fossil_secure_free_page(p, n);
1937 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1938 }
1939 #elif defined(__linux__)
1940 ssize_t nWrite;
1941 struct iovec liov = {0};
1942 struct iovec riov = {0};
1943 liov.iov_base = p;
1944 liov.iov_len = n;
1945 riov.iov_base = pAddress;
1946 riov.iov_len = nSize;
1947 nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
1948 if( nWrite!=nSize ){
1949 fossil_secure_free_page(p, n);
1950 fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu",
1951 nWrite, nSize, pAddress, (unsigned long)processId);
1952 }
1953 #else
1954 fossil_secure_free_page(p, n);
1955 fossil_trace("db_zero_saved_encryption_key_in_process unsupported");
1956 #endif
1957 }
1958 }
1959
1960 /*
1961 ** This function evaluates the specified TH1 script and attempts to parse
1962 ** its result as a colon-delimited triplet containing a process identifier,
1963 ** address, and size (in bytes) of the database encryption key. This is
1964 ** only necessary (or functional) on Windows or Linux.
1965 */
1966 static PID_T db_handle_saved_encryption_key_for_process_via_th1(
1967 const char *zConfig, /* The TH1 script to evaluate. */
1968 int eType /* Non-zero to write key to parent process -OR-
1969 * zero to read it from the parent process. */
1970 ){
1971 PID_T processId = 0;
1972 int rc;
1973 char *zResult;
1974 char *zPwd = file_getcwd(0, 0);
1975 Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
1976 rc = Th_Eval(g.interp, 0, zConfig, -1);
@@ -1686,20 +1977,65 @@
1977 zResult = (char*)Th_GetResult(g.interp, 0);
1978 if( rc!=TH_OK ){
1979 fossil_fatal("script for pid key failed: %s", zResult);
1980 }
1981 if( zResult ){
 
1982 LPVOID pAddress = NULL;
1983 SIZE_T nSize = 0;
1984 parse_pid_key_value(zResult, &processId, &pAddress, &nSize);
1985 if( eType==SEE_KEY_READ ){
1986 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
1987 }else if( eType==SEE_KEY_WRITE ){
1988 db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
1989 }else if( eType==SEE_KEY_ZERO ){
1990 db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
1991 }else{
1992 fossil_panic("unsupported SEE key operation %d", eType);
1993 }
1994 }
1995 file_chdir(zPwd, 0);
1996 fossil_free(zPwd);
1997 return processId;
1998 }
1999
2000 /*
2001 ** This function sets the saved database encryption key to one that gets
2002 ** read from the specified Fossil parent process, if applicable. This is
2003 ** only necessary (or functional) on Windows or Linux.
2004 */
2005 PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){
2006 PID_T processId = 0;
2007 g.zPidKey = find_option("usepidkey",0,1);
2008 if( !g.zPidKey ){
2009 g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY");
2010 }
2011 if( g.zPidKey ){
2012 LPVOID pAddress = NULL;
2013 SIZE_T nSize = 0;
2014 parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize);
2015 if( eType==SEE_KEY_READ ){
2016 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
2017 }else if( eType==SEE_KEY_WRITE ){
2018 db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
2019 }else if( eType==SEE_KEY_ZERO ){
2020 db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
2021 }else{
2022 fossil_panic("unsupported SEE key operation %d", eType);
2023 }
2024 }else{
2025 const char *zSeeDbConfig = find_option("seedbcfg",0,1);
2026 if( !zSeeDbConfig ){
2027 zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG");
2028 }
2029 if( zSeeDbConfig ){
2030 processId = db_handle_saved_encryption_key_for_process_via_th1(
2031 zSeeDbConfig, eType
2032 );
2033 }
2034 }
2035 return processId;
2036 }
2037 #endif /* USE_SEE */
2038
2039 /*
2040 ** If the database file zDbFile has a name that suggests that it is
2041 ** encrypted, then prompt for the database encryption key and return it
2042
+370 -34
--- src/db.c
+++ src/db.c
@@ -31,25 +31,57 @@
3131
*/
3232
#include "config.h"
3333
#if defined(_WIN32)
3434
# if USE_SEE
3535
# include <windows.h>
36
+# define GETPID (int)GetCurrentProcessId
3637
# endif
3738
#else
3839
# include <pwd.h>
40
+# if USE_SEE
41
+# define GETPID getpid
42
+# endif
3943
#endif
4044
#if USE_SEE && !defined(SQLITE_HAS_CODEC)
4145
# define SQLITE_HAS_CODEC
46
+#endif
47
+#if USE_SEE && defined(__linux__)
48
+# include <sys/uio.h>
4249
#endif
4350
#include <sqlite3.h>
4451
#include <sys/types.h>
4552
#include <sys/stat.h>
4653
#include <unistd.h>
4754
#include <time.h>
55
+
56
+/* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */
57
+#if USE_SEE
58
+#if defined(_WIN32)
59
+typedef DWORD PID_T;
60
+#else
61
+typedef pid_t PID_T;
62
+#endif
63
+#endif
64
+
4865
#include "db.h"
4966
5067
#if INTERFACE
68
+/*
69
+** Type definitions used for handling the saved encryption key for SEE.
70
+*/
71
+#if !defined(_WIN32)
72
+typedef void *LPVOID;
73
+typedef size_t SIZE_T;
74
+#endif
75
+
76
+/*
77
+** Operations for db_maybe_handle_saved_encryption_key_for_process, et al.
78
+*/
79
+#define SEE_KEY_READ ((int)0)
80
+#define SEE_KEY_WRITE ((int)1)
81
+#define SEE_KEY_ZERO ((int)2)
82
+
5183
/*
5284
** An single SQL statement is represented as an instance of the following
5385
** structure.
5486
*/
5587
struct Stmt {
@@ -1539,11 +1571,30 @@
15391571
static char *zSavedKey = 0;
15401572
15411573
/*
15421574
** This is the size of the saved database encryption key, in bytes.
15431575
*/
1544
-size_t savedKeySize = 0;
1576
+static size_t savedKeySize = 0;
1577
+
1578
+/*
1579
+** This function returns non-zero if there is a saved database encryption
1580
+** key available.
1581
+*/
1582
+int db_have_saved_encryption_key(){
1583
+ return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize);
1584
+}
1585
+
1586
+/*
1587
+** This function returns non-zero if the specified database encryption key
1588
+** is valid.
1589
+*/
1590
+int db_is_valid_saved_encryption_key(const char *p, size_t n){
1591
+ if( p==0 ) return 0;
1592
+ if( n==0 ) return 0;
1593
+ if( p[0]==0 ) return 0;
1594
+ return 1;
1595
+}
15451596
15461597
/*
15471598
** This function returns the saved database encryption key -OR- zero if
15481599
** no database encryption key is saved.
15491600
*/
@@ -1556,10 +1607,36 @@
15561607
** -OR- zero if no database encryption key is saved.
15571608
*/
15581609
size_t db_get_saved_encryption_key_size(){
15591610
return savedKeySize;
15601611
}
1612
+
1613
+/*
1614
+** This function arranges for the saved database encryption key buffer
1615
+** to be allocated and then sets up the environment variable to allow
1616
+** a child process to initialize it with the actual database encryption
1617
+** key.
1618
+*/
1619
+void db_setup_for_saved_encryption_key(){
1620
+ void *p = NULL;
1621
+ size_t n = 0;
1622
+ size_t pageSize = 0;
1623
+ Blob pidKey;
1624
+
1625
+ assert( !db_have_saved_encryption_key() );
1626
+ db_unsave_encryption_key();
1627
+ fossil_get_page_size(&pageSize);
1628
+ assert( pageSize>0 );
1629
+ p = fossil_secure_alloc_page(&n);
1630
+ assert( p!=NULL );
1631
+ assert( n==pageSize );
1632
+ blob_zero(&pidKey);
1633
+ blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n);
1634
+ fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey));
1635
+ zSavedKey = p;
1636
+ savedKeySize = n;
1637
+}
15611638
15621639
/*
15631640
** This function arranges for the database encryption key to be securely
15641641
** saved in non-pagable memory (on platforms where this is possible).
15651642
*/
@@ -1569,10 +1646,11 @@
15691646
void *p = NULL;
15701647
size_t n = 0;
15711648
size_t pageSize = 0;
15721649
size_t blobSize = 0;
15731650
1651
+ assert( !db_have_saved_encryption_key() );
15741652
blobSize = blob_size(pKey);
15751653
if( blobSize==0 ) return;
15761654
fossil_get_page_size(&pageSize);
15771655
assert( pageSize>0 );
15781656
if( blobSize>pageSize ){
@@ -1599,11 +1677,11 @@
15991677
16001678
/*
16011679
** This function sets the saved database encryption key to the specified
16021680
** string value, allocating or freeing the underlying memory if needed.
16031681
*/
1604
-void db_set_saved_encryption_key(
1682
+static void db_set_saved_encryption_key(
16051683
Blob *pKey
16061684
){
16071685
if( zSavedKey!=NULL ){
16081686
size_t blobSize = blob_size(pKey);
16091687
if( blobSize==0 ){
@@ -1619,25 +1697,214 @@
16191697
}else{
16201698
db_save_encryption_key(pKey);
16211699
}
16221700
}
16231701
1702
+/*
1703
+** WEBPAGE: setseekey
1704
+**
1705
+** Sets the sets the saved database encryption key to one that gets passed
1706
+** via the "key" query string parameter. If the saved database encryption
1707
+** key has already been set, does nothing. This web page does not produce
1708
+** any output on success or failure. No permissions are required and none
1709
+** are checked (partially due to lack of encrypted database access).
1710
+**
1711
+** Query parameters:
1712
+**
1713
+** key The string to set as the saved database encryption
1714
+** key.
1715
+*/
1716
+void db_set_see_key_page(void){
1717
+ Blob key;
1718
+ const char *zKey;
1719
+ if( db_have_saved_encryption_key() ){
1720
+ fossil_trace("SEE: encryption key was already set\n");
1721
+ return;
1722
+ }
1723
+ zKey = P("key");
1724
+ blob_init(&key, 0, 0);
1725
+ if( zKey!=0 ){
1726
+ PID_T processId;
1727
+ blob_set(&key, zKey);
1728
+ db_set_saved_encryption_key(&key);
1729
+ processId = db_maybe_handle_saved_encryption_key_for_process(
1730
+ SEE_KEY_WRITE
1731
+ );
1732
+ fossil_trace("SEE: set encryption key for process %lu, length %u\n",
1733
+ (unsigned long)processId, blob_size(&key));
1734
+ }else{
1735
+ fossil_trace("SEE: no encryption key specified\n");
1736
+ }
1737
+ blob_reset(&key);
1738
+}
1739
+
1740
+/*
1741
+** WEBPAGE: unsetseekey
1742
+**
1743
+** Sets the saved database encryption key to zeros in the current and parent
1744
+** Fossil processes. This web page does not produce any output on success
1745
+** or failure. Setup permission is required.
1746
+*/
1747
+void db_unset_see_key_page(void){
1748
+ PID_T processId;
1749
+ login_check_credentials();
1750
+ if( !g.perm.Setup ){ login_needed(0); return; }
1751
+ processId = db_maybe_handle_saved_encryption_key_for_process(
1752
+ SEE_KEY_ZERO
1753
+ );
1754
+ fossil_trace("SEE: unset encryption key for process %lu\n",
1755
+ (unsigned long)processId);
1756
+}
1757
+
1758
+/*
1759
+** This function reads the saved database encryption key from the
1760
+** specified Fossil parent process. This is only necessary (or
1761
+** functional) on Windows or Linux.
1762
+*/
1763
+static void db_read_saved_encryption_key_from_process(
1764
+ PID_T processId, /* Identifier for Fossil parent process. */
1765
+ LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1766
+ SIZE_T nSize /* Size of saved key buffer in the parent process. */
1767
+){
1768
+ void *p = NULL;
1769
+ size_t n = 0;
1770
+ size_t pageSize = 0;
1771
+
1772
+ fossil_get_page_size(&pageSize);
1773
+ assert( pageSize>0 );
1774
+ if( nSize>pageSize ){
1775
+ fossil_panic("key too large: %u versus %u", nSize, pageSize);
1776
+ }
1777
+ p = fossil_secure_alloc_page(&n);
1778
+ assert( p!=NULL );
1779
+ assert( n==pageSize );
1780
+ assert( n>=nSize );
1781
+ {
1782
+#if defined(_WIN32)
1783
+ HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
1784
+ if( hProcess!=NULL ){
1785
+ SIZE_T nRead = 0;
1786
+ if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
1787
+ CloseHandle(hProcess);
1788
+ if( nRead==nSize ){
1789
+ db_unsave_encryption_key();
1790
+ zSavedKey = p;
1791
+ savedKeySize = n;
1792
+ }else{
1793
+ fossil_secure_free_page(p, n);
1794
+ fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
1795
+ nRead, nSize, pAddress, processId);
1796
+ }
1797
+ }else{
1798
+ CloseHandle(hProcess);
1799
+ fossil_secure_free_page(p, n);
1800
+ fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1801
+ pAddress, processId, GetLastError());
1802
+ }
1803
+ }else{
1804
+ fossil_secure_free_page(p, n);
1805
+ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1806
+ }
1807
+#elif defined(__linux__)
1808
+ ssize_t nRead;
1809
+ struct iovec liov = {0};
1810
+ struct iovec riov = {0};
1811
+ liov.iov_base = p;
1812
+ liov.iov_len = n;
1813
+ riov.iov_base = pAddress;
1814
+ riov.iov_len = nSize;
1815
+ nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0);
1816
+ if( nRead==nSize ){
1817
+ db_unsave_encryption_key();
1818
+ zSavedKey = p;
1819
+ savedKeySize = n;
1820
+ }else{
1821
+ fossil_secure_free_page(p, n);
1822
+ fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu",
1823
+ nRead, nSize, pAddress, (unsigned long)processId);
1824
+ }
1825
+#else
1826
+ fossil_secure_free_page(p, n);
1827
+ fossil_trace("db_read_saved_encryption_key_from_process unsupported");
1828
+#endif
1829
+ }
1830
+}
1831
+
1832
+/*
1833
+** This function writes the saved database encryption key into the
1834
+** specified Fossil parent process. This is only necessary (or
1835
+** functional) on Windows or Linux.
1836
+*/
1837
+static void db_write_saved_encryption_key_to_process(
1838
+ PID_T processId, /* Identifier for Fossil parent process. */
1839
+ LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1840
+ SIZE_T nSize /* Size of saved key buffer in the parent process. */
1841
+){
1842
+ void *p = db_get_saved_encryption_key();
1843
+ size_t n = db_get_saved_encryption_key_size();
1844
+ size_t pageSize = 0;
1845
+
1846
+ fossil_get_page_size(&pageSize);
1847
+ assert( pageSize>0 );
1848
+ if( nSize>pageSize ){
1849
+ fossil_panic("key too large: %u versus %u", nSize, pageSize);
1850
+ }
1851
+ assert( p!=NULL );
1852
+ assert( n==pageSize );
1853
+ assert( n>=nSize );
1854
+ {
16241855
#if defined(_WIN32)
1856
+ HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
1857
+ FALSE, processId);
1858
+ if( hProcess!=NULL ){
1859
+ SIZE_T nWrite = 0;
1860
+ if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
1861
+ CloseHandle(hProcess);
1862
+ if( nWrite!=nSize ){
1863
+ fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu",
1864
+ nWrite, nSize, pAddress, processId);
1865
+ }
1866
+ }else{
1867
+ CloseHandle(hProcess);
1868
+ fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize,
1869
+ pAddress, processId, GetLastError());
1870
+ }
1871
+ }else{
1872
+ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1873
+ }
1874
+#elif defined(__linux__)
1875
+ ssize_t nWrite;
1876
+ struct iovec liov = {0};
1877
+ struct iovec riov = {0};
1878
+ liov.iov_base = p;
1879
+ liov.iov_len = n;
1880
+ riov.iov_base = pAddress;
1881
+ riov.iov_len = nSize;
1882
+ nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
1883
+ if( nWrite!=nSize ){
1884
+ fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu",
1885
+ nWrite, nSize, pAddress, (unsigned long)processId);
1886
+ }
1887
+#else
1888
+ fossil_trace("db_write_saved_encryption_key_to_process unsupported");
1889
+#endif
1890
+ }
1891
+}
1892
+
16251893
/*
1626
-** This function sets the saved database encryption key to one that gets
1627
-** read from the specified Fossil parent process. This is only necessary
1628
-** (or functional) on Windows.
1894
+** This function zeros the saved database encryption key in the specified
1895
+** Fossil parent process. This is only necessary (or functional) on
1896
+** Windows or Linux.
16291897
*/
1630
-void db_read_saved_encryption_key_from_process(
1631
- DWORD processId, /* Identifier for Fossil parent process. */
1898
+static void db_zero_saved_encryption_key_in_process(
1899
+ PID_T processId, /* Identifier for Fossil parent process. */
16321900
LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
16331901
SIZE_T nSize /* Size of saved key buffer in the parent process. */
16341902
){
16351903
void *p = NULL;
16361904
size_t n = 0;
16371905
size_t pageSize = 0;
1638
- HANDLE hProcess = NULL;
16391906
16401907
fossil_get_page_size(&pageSize);
16411908
assert( pageSize>0 );
16421909
if( nSize>pageSize ){
16431910
fossil_panic("key too large: %u versus %u", nSize, pageSize);
@@ -1644,42 +1911,66 @@
16441911
}
16451912
p = fossil_secure_alloc_page(&n);
16461913
assert( p!=NULL );
16471914
assert( n==pageSize );
16481915
assert( n>=nSize );
1649
- hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
1650
- if( hProcess!=NULL ){
1651
- SIZE_T nRead = 0;
1652
- if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
1653
- CloseHandle(hProcess);
1654
- if( nRead==nSize ){
1655
- db_unsave_encryption_key();
1656
- zSavedKey = p;
1657
- savedKeySize = n;
1658
- }else{
1659
- fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
1660
- nRead, nSize, pAddress, processId);
1661
- }
1662
- }else{
1663
- CloseHandle(hProcess);
1664
- fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1665
- pAddress, processId, GetLastError());
1666
- }
1667
- }else{
1668
- fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1916
+ {
1917
+#if defined(_WIN32)
1918
+ HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
1919
+ FALSE, processId);
1920
+ if( hProcess!=NULL ){
1921
+ SIZE_T nWrite = 0;
1922
+ if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
1923
+ CloseHandle(hProcess);
1924
+ fossil_secure_free_page(p, n);
1925
+ if( nWrite!=nSize ){
1926
+ fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu",
1927
+ nWrite, nSize, pAddress, processId);
1928
+ }
1929
+ }else{
1930
+ CloseHandle(hProcess);
1931
+ fossil_secure_free_page(p, n);
1932
+ fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize,
1933
+ pAddress, processId, GetLastError());
1934
+ }
1935
+ }else{
1936
+ fossil_secure_free_page(p, n);
1937
+ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1938
+ }
1939
+#elif defined(__linux__)
1940
+ ssize_t nWrite;
1941
+ struct iovec liov = {0};
1942
+ struct iovec riov = {0};
1943
+ liov.iov_base = p;
1944
+ liov.iov_len = n;
1945
+ riov.iov_base = pAddress;
1946
+ riov.iov_len = nSize;
1947
+ nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
1948
+ if( nWrite!=nSize ){
1949
+ fossil_secure_free_page(p, n);
1950
+ fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu",
1951
+ nWrite, nSize, pAddress, (unsigned long)processId);
1952
+ }
1953
+#else
1954
+ fossil_secure_free_page(p, n);
1955
+ fossil_trace("db_zero_saved_encryption_key_in_process unsupported");
1956
+#endif
16691957
}
16701958
}
16711959
16721960
/*
16731961
** This function evaluates the specified TH1 script and attempts to parse
16741962
** its result as a colon-delimited triplet containing a process identifier,
16751963
** address, and size (in bytes) of the database encryption key. This is
1676
-** only necessary (or functional) on Windows.
1964
+** only necessary (or functional) on Windows or Linux.
16771965
*/
1678
-void db_read_saved_encryption_key_from_process_via_th1(
1679
- const char *zConfig /* The TH1 script to evaluate. */
1966
+static PID_T db_handle_saved_encryption_key_for_process_via_th1(
1967
+ const char *zConfig, /* The TH1 script to evaluate. */
1968
+ int eType /* Non-zero to write key to parent process -OR-
1969
+ * zero to read it from the parent process. */
16801970
){
1971
+ PID_T processId = 0;
16811972
int rc;
16821973
char *zResult;
16831974
char *zPwd = file_getcwd(0, 0);
16841975
Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
16851976
rc = Th_Eval(g.interp, 0, zConfig, -1);
@@ -1686,20 +1977,65 @@
16861977
zResult = (char*)Th_GetResult(g.interp, 0);
16871978
if( rc!=TH_OK ){
16881979
fossil_fatal("script for pid key failed: %s", zResult);
16891980
}
16901981
if( zResult ){
1691
- DWORD processId = 0;
16921982
LPVOID pAddress = NULL;
16931983
SIZE_T nSize = 0;
16941984
parse_pid_key_value(zResult, &processId, &pAddress, &nSize);
1695
- db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
1985
+ if( eType==SEE_KEY_READ ){
1986
+ db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
1987
+ }else if( eType==SEE_KEY_WRITE ){
1988
+ db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
1989
+ }else if( eType==SEE_KEY_ZERO ){
1990
+ db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
1991
+ }else{
1992
+ fossil_panic("unsupported SEE key operation %d", eType);
1993
+ }
16961994
}
16971995
file_chdir(zPwd, 0);
16981996
fossil_free(zPwd);
1997
+ return processId;
16991998
}
1700
-#endif /* defined(_WIN32) */
1999
+
2000
+/*
2001
+** This function sets the saved database encryption key to one that gets
2002
+** read from the specified Fossil parent process, if applicable. This is
2003
+** only necessary (or functional) on Windows or Linux.
2004
+*/
2005
+PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){
2006
+ PID_T processId = 0;
2007
+ g.zPidKey = find_option("usepidkey",0,1);
2008
+ if( !g.zPidKey ){
2009
+ g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY");
2010
+ }
2011
+ if( g.zPidKey ){
2012
+ LPVOID pAddress = NULL;
2013
+ SIZE_T nSize = 0;
2014
+ parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize);
2015
+ if( eType==SEE_KEY_READ ){
2016
+ db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
2017
+ }else if( eType==SEE_KEY_WRITE ){
2018
+ db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
2019
+ }else if( eType==SEE_KEY_ZERO ){
2020
+ db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
2021
+ }else{
2022
+ fossil_panic("unsupported SEE key operation %d", eType);
2023
+ }
2024
+ }else{
2025
+ const char *zSeeDbConfig = find_option("seedbcfg",0,1);
2026
+ if( !zSeeDbConfig ){
2027
+ zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG");
2028
+ }
2029
+ if( zSeeDbConfig ){
2030
+ processId = db_handle_saved_encryption_key_for_process_via_th1(
2031
+ zSeeDbConfig, eType
2032
+ );
2033
+ }
2034
+ }
2035
+ return processId;
2036
+}
17012037
#endif /* USE_SEE */
17022038
17032039
/*
17042040
** If the database file zDbFile has a name that suggests that it is
17052041
** encrypted, then prompt for the database encryption key and return it
17062042
--- src/db.c
+++ src/db.c
@@ -31,25 +31,57 @@
31 */
32 #include "config.h"
33 #if defined(_WIN32)
34 # if USE_SEE
35 # include <windows.h>
 
36 # endif
37 #else
38 # include <pwd.h>
 
 
 
39 #endif
40 #if USE_SEE && !defined(SQLITE_HAS_CODEC)
41 # define SQLITE_HAS_CODEC
 
 
 
42 #endif
43 #include <sqlite3.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <unistd.h>
47 #include <time.h>
 
 
 
 
 
 
 
 
 
 
48 #include "db.h"
49
50 #if INTERFACE
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51 /*
52 ** An single SQL statement is represented as an instance of the following
53 ** structure.
54 */
55 struct Stmt {
@@ -1539,11 +1571,30 @@
1539 static char *zSavedKey = 0;
1540
1541 /*
1542 ** This is the size of the saved database encryption key, in bytes.
1543 */
1544 size_t savedKeySize = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1545
1546 /*
1547 ** This function returns the saved database encryption key -OR- zero if
1548 ** no database encryption key is saved.
1549 */
@@ -1556,10 +1607,36 @@
1556 ** -OR- zero if no database encryption key is saved.
1557 */
1558 size_t db_get_saved_encryption_key_size(){
1559 return savedKeySize;
1560 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1561
1562 /*
1563 ** This function arranges for the database encryption key to be securely
1564 ** saved in non-pagable memory (on platforms where this is possible).
1565 */
@@ -1569,10 +1646,11 @@
1569 void *p = NULL;
1570 size_t n = 0;
1571 size_t pageSize = 0;
1572 size_t blobSize = 0;
1573
 
1574 blobSize = blob_size(pKey);
1575 if( blobSize==0 ) return;
1576 fossil_get_page_size(&pageSize);
1577 assert( pageSize>0 );
1578 if( blobSize>pageSize ){
@@ -1599,11 +1677,11 @@
1599
1600 /*
1601 ** This function sets the saved database encryption key to the specified
1602 ** string value, allocating or freeing the underlying memory if needed.
1603 */
1604 void db_set_saved_encryption_key(
1605 Blob *pKey
1606 ){
1607 if( zSavedKey!=NULL ){
1608 size_t blobSize = blob_size(pKey);
1609 if( blobSize==0 ){
@@ -1619,25 +1697,214 @@
1619 }else{
1620 db_save_encryption_key(pKey);
1621 }
1622 }
1623
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1624 #if defined(_WIN32)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1625 /*
1626 ** This function sets the saved database encryption key to one that gets
1627 ** read from the specified Fossil parent process. This is only necessary
1628 ** (or functional) on Windows.
1629 */
1630 void db_read_saved_encryption_key_from_process(
1631 DWORD processId, /* Identifier for Fossil parent process. */
1632 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1633 SIZE_T nSize /* Size of saved key buffer in the parent process. */
1634 ){
1635 void *p = NULL;
1636 size_t n = 0;
1637 size_t pageSize = 0;
1638 HANDLE hProcess = NULL;
1639
1640 fossil_get_page_size(&pageSize);
1641 assert( pageSize>0 );
1642 if( nSize>pageSize ){
1643 fossil_panic("key too large: %u versus %u", nSize, pageSize);
@@ -1644,42 +1911,66 @@
1644 }
1645 p = fossil_secure_alloc_page(&n);
1646 assert( p!=NULL );
1647 assert( n==pageSize );
1648 assert( n>=nSize );
1649 hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
1650 if( hProcess!=NULL ){
1651 SIZE_T nRead = 0;
1652 if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
1653 CloseHandle(hProcess);
1654 if( nRead==nSize ){
1655 db_unsave_encryption_key();
1656 zSavedKey = p;
1657 savedKeySize = n;
1658 }else{
1659 fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
1660 nRead, nSize, pAddress, processId);
1661 }
1662 }else{
1663 CloseHandle(hProcess);
1664 fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1665 pAddress, processId, GetLastError());
1666 }
1667 }else{
1668 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1669 }
1670 }
1671
1672 /*
1673 ** This function evaluates the specified TH1 script and attempts to parse
1674 ** its result as a colon-delimited triplet containing a process identifier,
1675 ** address, and size (in bytes) of the database encryption key. This is
1676 ** only necessary (or functional) on Windows.
1677 */
1678 void db_read_saved_encryption_key_from_process_via_th1(
1679 const char *zConfig /* The TH1 script to evaluate. */
 
 
1680 ){
 
1681 int rc;
1682 char *zResult;
1683 char *zPwd = file_getcwd(0, 0);
1684 Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
1685 rc = Th_Eval(g.interp, 0, zConfig, -1);
@@ -1686,20 +1977,65 @@
1686 zResult = (char*)Th_GetResult(g.interp, 0);
1687 if( rc!=TH_OK ){
1688 fossil_fatal("script for pid key failed: %s", zResult);
1689 }
1690 if( zResult ){
1691 DWORD processId = 0;
1692 LPVOID pAddress = NULL;
1693 SIZE_T nSize = 0;
1694 parse_pid_key_value(zResult, &processId, &pAddress, &nSize);
1695 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
 
 
 
 
 
 
 
 
1696 }
1697 file_chdir(zPwd, 0);
1698 fossil_free(zPwd);
 
1699 }
1700 #endif /* defined(_WIN32) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1701 #endif /* USE_SEE */
1702
1703 /*
1704 ** If the database file zDbFile has a name that suggests that it is
1705 ** encrypted, then prompt for the database encryption key and return it
1706
--- src/db.c
+++ src/db.c
@@ -31,25 +31,57 @@
31 */
32 #include "config.h"
33 #if defined(_WIN32)
34 # if USE_SEE
35 # include <windows.h>
36 # define GETPID (int)GetCurrentProcessId
37 # endif
38 #else
39 # include <pwd.h>
40 # if USE_SEE
41 # define GETPID getpid
42 # endif
43 #endif
44 #if USE_SEE && !defined(SQLITE_HAS_CODEC)
45 # define SQLITE_HAS_CODEC
46 #endif
47 #if USE_SEE && defined(__linux__)
48 # include <sys/uio.h>
49 #endif
50 #include <sqlite3.h>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <unistd.h>
54 #include <time.h>
55
56 /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */
57 #if USE_SEE
58 #if defined(_WIN32)
59 typedef DWORD PID_T;
60 #else
61 typedef pid_t PID_T;
62 #endif
63 #endif
64
65 #include "db.h"
66
67 #if INTERFACE
68 /*
69 ** Type definitions used for handling the saved encryption key for SEE.
70 */
71 #if !defined(_WIN32)
72 typedef void *LPVOID;
73 typedef size_t SIZE_T;
74 #endif
75
76 /*
77 ** Operations for db_maybe_handle_saved_encryption_key_for_process, et al.
78 */
79 #define SEE_KEY_READ ((int)0)
80 #define SEE_KEY_WRITE ((int)1)
81 #define SEE_KEY_ZERO ((int)2)
82
83 /*
84 ** An single SQL statement is represented as an instance of the following
85 ** structure.
86 */
87 struct Stmt {
@@ -1539,11 +1571,30 @@
1571 static char *zSavedKey = 0;
1572
1573 /*
1574 ** This is the size of the saved database encryption key, in bytes.
1575 */
1576 static size_t savedKeySize = 0;
1577
1578 /*
1579 ** This function returns non-zero if there is a saved database encryption
1580 ** key available.
1581 */
1582 int db_have_saved_encryption_key(){
1583 return db_is_valid_saved_encryption_key(zSavedKey, savedKeySize);
1584 }
1585
1586 /*
1587 ** This function returns non-zero if the specified database encryption key
1588 ** is valid.
1589 */
1590 int db_is_valid_saved_encryption_key(const char *p, size_t n){
1591 if( p==0 ) return 0;
1592 if( n==0 ) return 0;
1593 if( p[0]==0 ) return 0;
1594 return 1;
1595 }
1596
1597 /*
1598 ** This function returns the saved database encryption key -OR- zero if
1599 ** no database encryption key is saved.
1600 */
@@ -1556,10 +1607,36 @@
1607 ** -OR- zero if no database encryption key is saved.
1608 */
1609 size_t db_get_saved_encryption_key_size(){
1610 return savedKeySize;
1611 }
1612
1613 /*
1614 ** This function arranges for the saved database encryption key buffer
1615 ** to be allocated and then sets up the environment variable to allow
1616 ** a child process to initialize it with the actual database encryption
1617 ** key.
1618 */
1619 void db_setup_for_saved_encryption_key(){
1620 void *p = NULL;
1621 size_t n = 0;
1622 size_t pageSize = 0;
1623 Blob pidKey;
1624
1625 assert( !db_have_saved_encryption_key() );
1626 db_unsave_encryption_key();
1627 fossil_get_page_size(&pageSize);
1628 assert( pageSize>0 );
1629 p = fossil_secure_alloc_page(&n);
1630 assert( p!=NULL );
1631 assert( n==pageSize );
1632 blob_zero(&pidKey);
1633 blob_appendf(&pidKey, "%lu:%p:%u", (unsigned long)GETPID(), p, n);
1634 fossil_setenv("FOSSIL_SEE_PID_KEY", blob_str(&pidKey));
1635 zSavedKey = p;
1636 savedKeySize = n;
1637 }
1638
1639 /*
1640 ** This function arranges for the database encryption key to be securely
1641 ** saved in non-pagable memory (on platforms where this is possible).
1642 */
@@ -1569,10 +1646,11 @@
1646 void *p = NULL;
1647 size_t n = 0;
1648 size_t pageSize = 0;
1649 size_t blobSize = 0;
1650
1651 assert( !db_have_saved_encryption_key() );
1652 blobSize = blob_size(pKey);
1653 if( blobSize==0 ) return;
1654 fossil_get_page_size(&pageSize);
1655 assert( pageSize>0 );
1656 if( blobSize>pageSize ){
@@ -1599,11 +1677,11 @@
1677
1678 /*
1679 ** This function sets the saved database encryption key to the specified
1680 ** string value, allocating or freeing the underlying memory if needed.
1681 */
1682 static void db_set_saved_encryption_key(
1683 Blob *pKey
1684 ){
1685 if( zSavedKey!=NULL ){
1686 size_t blobSize = blob_size(pKey);
1687 if( blobSize==0 ){
@@ -1619,25 +1697,214 @@
1697 }else{
1698 db_save_encryption_key(pKey);
1699 }
1700 }
1701
1702 /*
1703 ** WEBPAGE: setseekey
1704 **
1705 ** Sets the sets the saved database encryption key to one that gets passed
1706 ** via the "key" query string parameter. If the saved database encryption
1707 ** key has already been set, does nothing. This web page does not produce
1708 ** any output on success or failure. No permissions are required and none
1709 ** are checked (partially due to lack of encrypted database access).
1710 **
1711 ** Query parameters:
1712 **
1713 ** key The string to set as the saved database encryption
1714 ** key.
1715 */
1716 void db_set_see_key_page(void){
1717 Blob key;
1718 const char *zKey;
1719 if( db_have_saved_encryption_key() ){
1720 fossil_trace("SEE: encryption key was already set\n");
1721 return;
1722 }
1723 zKey = P("key");
1724 blob_init(&key, 0, 0);
1725 if( zKey!=0 ){
1726 PID_T processId;
1727 blob_set(&key, zKey);
1728 db_set_saved_encryption_key(&key);
1729 processId = db_maybe_handle_saved_encryption_key_for_process(
1730 SEE_KEY_WRITE
1731 );
1732 fossil_trace("SEE: set encryption key for process %lu, length %u\n",
1733 (unsigned long)processId, blob_size(&key));
1734 }else{
1735 fossil_trace("SEE: no encryption key specified\n");
1736 }
1737 blob_reset(&key);
1738 }
1739
1740 /*
1741 ** WEBPAGE: unsetseekey
1742 **
1743 ** Sets the saved database encryption key to zeros in the current and parent
1744 ** Fossil processes. This web page does not produce any output on success
1745 ** or failure. Setup permission is required.
1746 */
1747 void db_unset_see_key_page(void){
1748 PID_T processId;
1749 login_check_credentials();
1750 if( !g.perm.Setup ){ login_needed(0); return; }
1751 processId = db_maybe_handle_saved_encryption_key_for_process(
1752 SEE_KEY_ZERO
1753 );
1754 fossil_trace("SEE: unset encryption key for process %lu\n",
1755 (unsigned long)processId);
1756 }
1757
1758 /*
1759 ** This function reads the saved database encryption key from the
1760 ** specified Fossil parent process. This is only necessary (or
1761 ** functional) on Windows or Linux.
1762 */
1763 static void db_read_saved_encryption_key_from_process(
1764 PID_T processId, /* Identifier for Fossil parent process. */
1765 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1766 SIZE_T nSize /* Size of saved key buffer in the parent process. */
1767 ){
1768 void *p = NULL;
1769 size_t n = 0;
1770 size_t pageSize = 0;
1771
1772 fossil_get_page_size(&pageSize);
1773 assert( pageSize>0 );
1774 if( nSize>pageSize ){
1775 fossil_panic("key too large: %u versus %u", nSize, pageSize);
1776 }
1777 p = fossil_secure_alloc_page(&n);
1778 assert( p!=NULL );
1779 assert( n==pageSize );
1780 assert( n>=nSize );
1781 {
1782 #if defined(_WIN32)
1783 HANDLE hProcess = OpenProcess(PROCESS_VM_READ, FALSE, processId);
1784 if( hProcess!=NULL ){
1785 SIZE_T nRead = 0;
1786 if( ReadProcessMemory(hProcess, pAddress, p, nSize, &nRead) ){
1787 CloseHandle(hProcess);
1788 if( nRead==nSize ){
1789 db_unsave_encryption_key();
1790 zSavedKey = p;
1791 savedKeySize = n;
1792 }else{
1793 fossil_secure_free_page(p, n);
1794 fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
1795 nRead, nSize, pAddress, processId);
1796 }
1797 }else{
1798 CloseHandle(hProcess);
1799 fossil_secure_free_page(p, n);
1800 fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1801 pAddress, processId, GetLastError());
1802 }
1803 }else{
1804 fossil_secure_free_page(p, n);
1805 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1806 }
1807 #elif defined(__linux__)
1808 ssize_t nRead;
1809 struct iovec liov = {0};
1810 struct iovec riov = {0};
1811 liov.iov_base = p;
1812 liov.iov_len = n;
1813 riov.iov_base = pAddress;
1814 riov.iov_len = nSize;
1815 nRead = process_vm_readv(processId, &liov, 1, &riov, 1, 0);
1816 if( nRead==nSize ){
1817 db_unsave_encryption_key();
1818 zSavedKey = p;
1819 savedKeySize = n;
1820 }else{
1821 fossil_secure_free_page(p, n);
1822 fossil_panic("bad size read, %zd out of %zu bytes at %p from pid %lu",
1823 nRead, nSize, pAddress, (unsigned long)processId);
1824 }
1825 #else
1826 fossil_secure_free_page(p, n);
1827 fossil_trace("db_read_saved_encryption_key_from_process unsupported");
1828 #endif
1829 }
1830 }
1831
1832 /*
1833 ** This function writes the saved database encryption key into the
1834 ** specified Fossil parent process. This is only necessary (or
1835 ** functional) on Windows or Linux.
1836 */
1837 static void db_write_saved_encryption_key_to_process(
1838 PID_T processId, /* Identifier for Fossil parent process. */
1839 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1840 SIZE_T nSize /* Size of saved key buffer in the parent process. */
1841 ){
1842 void *p = db_get_saved_encryption_key();
1843 size_t n = db_get_saved_encryption_key_size();
1844 size_t pageSize = 0;
1845
1846 fossil_get_page_size(&pageSize);
1847 assert( pageSize>0 );
1848 if( nSize>pageSize ){
1849 fossil_panic("key too large: %u versus %u", nSize, pageSize);
1850 }
1851 assert( p!=NULL );
1852 assert( n==pageSize );
1853 assert( n>=nSize );
1854 {
1855 #if defined(_WIN32)
1856 HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
1857 FALSE, processId);
1858 if( hProcess!=NULL ){
1859 SIZE_T nWrite = 0;
1860 if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
1861 CloseHandle(hProcess);
1862 if( nWrite!=nSize ){
1863 fossil_panic("bad size write, %u out of %u bytes at %p from pid %lu",
1864 nWrite, nSize, pAddress, processId);
1865 }
1866 }else{
1867 CloseHandle(hProcess);
1868 fossil_panic("failed write, %u bytes at %p from pid %lu: %lu", nSize,
1869 pAddress, processId, GetLastError());
1870 }
1871 }else{
1872 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1873 }
1874 #elif defined(__linux__)
1875 ssize_t nWrite;
1876 struct iovec liov = {0};
1877 struct iovec riov = {0};
1878 liov.iov_base = p;
1879 liov.iov_len = n;
1880 riov.iov_base = pAddress;
1881 riov.iov_len = nSize;
1882 nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
1883 if( nWrite!=nSize ){
1884 fossil_panic("bad size write, %zd out of %zu bytes at %p from pid %lu",
1885 nWrite, nSize, pAddress, (unsigned long)processId);
1886 }
1887 #else
1888 fossil_trace("db_write_saved_encryption_key_to_process unsupported");
1889 #endif
1890 }
1891 }
1892
1893 /*
1894 ** This function zeros the saved database encryption key in the specified
1895 ** Fossil parent process. This is only necessary (or functional) on
1896 ** Windows or Linux.
1897 */
1898 static void db_zero_saved_encryption_key_in_process(
1899 PID_T processId, /* Identifier for Fossil parent process. */
1900 LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */
1901 SIZE_T nSize /* Size of saved key buffer in the parent process. */
1902 ){
1903 void *p = NULL;
1904 size_t n = 0;
1905 size_t pageSize = 0;
 
1906
1907 fossil_get_page_size(&pageSize);
1908 assert( pageSize>0 );
1909 if( nSize>pageSize ){
1910 fossil_panic("key too large: %u versus %u", nSize, pageSize);
@@ -1644,42 +1911,66 @@
1911 }
1912 p = fossil_secure_alloc_page(&n);
1913 assert( p!=NULL );
1914 assert( n==pageSize );
1915 assert( n>=nSize );
1916 {
1917 #if defined(_WIN32)
1918 HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
1919 FALSE, processId);
1920 if( hProcess!=NULL ){
1921 SIZE_T nWrite = 0;
1922 if( WriteProcessMemory(hProcess, pAddress, p, nSize, &nWrite) ){
1923 CloseHandle(hProcess);
1924 fossil_secure_free_page(p, n);
1925 if( nWrite!=nSize ){
1926 fossil_panic("bad size zero, %u out of %u bytes at %p from pid %lu",
1927 nWrite, nSize, pAddress, processId);
1928 }
1929 }else{
1930 CloseHandle(hProcess);
1931 fossil_secure_free_page(p, n);
1932 fossil_panic("failed zero, %u bytes at %p from pid %lu: %lu", nSize,
1933 pAddress, processId, GetLastError());
1934 }
1935 }else{
1936 fossil_secure_free_page(p, n);
1937 fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
1938 }
1939 #elif defined(__linux__)
1940 ssize_t nWrite;
1941 struct iovec liov = {0};
1942 struct iovec riov = {0};
1943 liov.iov_base = p;
1944 liov.iov_len = n;
1945 riov.iov_base = pAddress;
1946 riov.iov_len = nSize;
1947 nWrite = process_vm_writev(processId, &liov, 1, &riov, 1, 0);
1948 if( nWrite!=nSize ){
1949 fossil_secure_free_page(p, n);
1950 fossil_panic("bad size zero, %zd out of %zu bytes at %p from pid %lu",
1951 nWrite, nSize, pAddress, (unsigned long)processId);
1952 }
1953 #else
1954 fossil_secure_free_page(p, n);
1955 fossil_trace("db_zero_saved_encryption_key_in_process unsupported");
1956 #endif
1957 }
1958 }
1959
1960 /*
1961 ** This function evaluates the specified TH1 script and attempts to parse
1962 ** its result as a colon-delimited triplet containing a process identifier,
1963 ** address, and size (in bytes) of the database encryption key. This is
1964 ** only necessary (or functional) on Windows or Linux.
1965 */
1966 static PID_T db_handle_saved_encryption_key_for_process_via_th1(
1967 const char *zConfig, /* The TH1 script to evaluate. */
1968 int eType /* Non-zero to write key to parent process -OR-
1969 * zero to read it from the parent process. */
1970 ){
1971 PID_T processId = 0;
1972 int rc;
1973 char *zResult;
1974 char *zPwd = file_getcwd(0, 0);
1975 Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO);
1976 rc = Th_Eval(g.interp, 0, zConfig, -1);
@@ -1686,20 +1977,65 @@
1977 zResult = (char*)Th_GetResult(g.interp, 0);
1978 if( rc!=TH_OK ){
1979 fossil_fatal("script for pid key failed: %s", zResult);
1980 }
1981 if( zResult ){
 
1982 LPVOID pAddress = NULL;
1983 SIZE_T nSize = 0;
1984 parse_pid_key_value(zResult, &processId, &pAddress, &nSize);
1985 if( eType==SEE_KEY_READ ){
1986 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
1987 }else if( eType==SEE_KEY_WRITE ){
1988 db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
1989 }else if( eType==SEE_KEY_ZERO ){
1990 db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
1991 }else{
1992 fossil_panic("unsupported SEE key operation %d", eType);
1993 }
1994 }
1995 file_chdir(zPwd, 0);
1996 fossil_free(zPwd);
1997 return processId;
1998 }
1999
2000 /*
2001 ** This function sets the saved database encryption key to one that gets
2002 ** read from the specified Fossil parent process, if applicable. This is
2003 ** only necessary (or functional) on Windows or Linux.
2004 */
2005 PID_T db_maybe_handle_saved_encryption_key_for_process(int eType){
2006 PID_T processId = 0;
2007 g.zPidKey = find_option("usepidkey",0,1);
2008 if( !g.zPidKey ){
2009 g.zPidKey = fossil_getenv("FOSSIL_SEE_PID_KEY");
2010 }
2011 if( g.zPidKey ){
2012 LPVOID pAddress = NULL;
2013 SIZE_T nSize = 0;
2014 parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize);
2015 if( eType==SEE_KEY_READ ){
2016 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
2017 }else if( eType==SEE_KEY_WRITE ){
2018 db_write_saved_encryption_key_to_process(processId, pAddress, nSize);
2019 }else if( eType==SEE_KEY_ZERO ){
2020 db_zero_saved_encryption_key_in_process(processId, pAddress, nSize);
2021 }else{
2022 fossil_panic("unsupported SEE key operation %d", eType);
2023 }
2024 }else{
2025 const char *zSeeDbConfig = find_option("seedbcfg",0,1);
2026 if( !zSeeDbConfig ){
2027 zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG");
2028 }
2029 if( zSeeDbConfig ){
2030 processId = db_handle_saved_encryption_key_for_process_via_th1(
2031 zSeeDbConfig, eType
2032 );
2033 }
2034 }
2035 return processId;
2036 }
2037 #endif /* USE_SEE */
2038
2039 /*
2040 ** If the database file zDbFile has a name that suggests that it is
2041 ** encrypted, then prompt for the database encryption key and return it
2042
+43
--- src/file.c
+++ src/file.c
@@ -2277,10 +2277,53 @@
22772277
int i;
22782278
for(i=2; i<g.argc; i++){
22792279
fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]);
22802280
}
22812281
}
2282
+
2283
+/*
2284
+** Returns non-zero if the specified file extension belongs to a Fossil
2285
+** repository file.
2286
+*/
2287
+int file_is_repository_extension(const char *zPath){
2288
+ if( fossil_strcmp(zPath, ".fossil")==0 ) return 1;
2289
+#if USE_SEE
2290
+ if( fossil_strcmp(zPath, ".efossil")==0 ) return 1;
2291
+#endif
2292
+ return 0;
2293
+}
2294
+
2295
+/*
2296
+** Returns non-zero if the specified path appears to match a file extension
2297
+** that should belong to a Fossil repository file.
2298
+*/
2299
+int file_contains_repository_extension(const char *zPath){
2300
+ if( sqlite3_strglob("*.fossil*",zPath)==0 ) return 1;
2301
+#if USE_SEE
2302
+ if( sqlite3_strglob("*.efossil*",zPath)==0 ) return 1;
2303
+#endif
2304
+ return 0;
2305
+}
2306
+
2307
+/*
2308
+** Returns non-zero if the specified path ends with a file extension that
2309
+** should belong to a Fossil repository file.
2310
+*/
2311
+int file_ends_with_repository_extension(const char *zPath, int bQual){
2312
+ if( bQual ){
2313
+ if( sqlite3_strglob("*/*.fossil", zPath)==0 ) return 1;
2314
+#if USE_SEE
2315
+ if( sqlite3_strglob("*/*.efossil", zPath)==0 ) return 1;
2316
+#endif
2317
+ }else{
2318
+ if( sqlite3_strglob("*.fossil", zPath)==0 ) return 1;
2319
+#if USE_SEE
2320
+ if( sqlite3_strglob("*.efossil", zPath)==0 ) return 1;
2321
+#endif
2322
+ }
2323
+ return 0;
2324
+}
22822325
22832326
/*
22842327
** Remove surplus "/" characters from the beginning of a full pathname.
22852328
** Extra leading "/" characters are benign on unix. But on Windows
22862329
** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil"
22872330
--- src/file.c
+++ src/file.c
@@ -2277,10 +2277,53 @@
2277 int i;
2278 for(i=2; i<g.argc; i++){
2279 fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]);
2280 }
2281 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2282
2283 /*
2284 ** Remove surplus "/" characters from the beginning of a full pathname.
2285 ** Extra leading "/" characters are benign on unix. But on Windows
2286 ** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil"
2287
--- src/file.c
+++ src/file.c
@@ -2277,10 +2277,53 @@
2277 int i;
2278 for(i=2; i<g.argc; i++){
2279 fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]);
2280 }
2281 }
2282
2283 /*
2284 ** Returns non-zero if the specified file extension belongs to a Fossil
2285 ** repository file.
2286 */
2287 int file_is_repository_extension(const char *zPath){
2288 if( fossil_strcmp(zPath, ".fossil")==0 ) return 1;
2289 #if USE_SEE
2290 if( fossil_strcmp(zPath, ".efossil")==0 ) return 1;
2291 #endif
2292 return 0;
2293 }
2294
2295 /*
2296 ** Returns non-zero if the specified path appears to match a file extension
2297 ** that should belong to a Fossil repository file.
2298 */
2299 int file_contains_repository_extension(const char *zPath){
2300 if( sqlite3_strglob("*.fossil*",zPath)==0 ) return 1;
2301 #if USE_SEE
2302 if( sqlite3_strglob("*.efossil*",zPath)==0 ) return 1;
2303 #endif
2304 return 0;
2305 }
2306
2307 /*
2308 ** Returns non-zero if the specified path ends with a file extension that
2309 ** should belong to a Fossil repository file.
2310 */
2311 int file_ends_with_repository_extension(const char *zPath, int bQual){
2312 if( bQual ){
2313 if( sqlite3_strglob("*/*.fossil", zPath)==0 ) return 1;
2314 #if USE_SEE
2315 if( sqlite3_strglob("*/*.efossil", zPath)==0 ) return 1;
2316 #endif
2317 }else{
2318 if( sqlite3_strglob("*.fossil", zPath)==0 ) return 1;
2319 #if USE_SEE
2320 if( sqlite3_strglob("*.efossil", zPath)==0 ) return 1;
2321 #endif
2322 }
2323 return 0;
2324 }
2325
2326 /*
2327 ** Remove surplus "/" characters from the beginning of a full pathname.
2328 ** Extra leading "/" characters are benign on unix. But on Windows
2329 ** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil"
2330
+62 -37
--- src/main.c
+++ src/main.c
@@ -24,10 +24,20 @@
2424
# include <windows.h>
2525
# include <io.h>
2626
# define isatty(h) _isatty(h)
2727
# define GETPID (int)GetCurrentProcessId
2828
#endif
29
+
30
+/* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */
31
+#if USE_SEE
32
+#if defined(_WIN32)
33
+typedef DWORD PID_T;
34
+#else
35
+typedef pid_t PID_T;
36
+#endif
37
+#endif
38
+
2939
#include "main.h"
3040
#include <string.h>
3141
#include <time.h>
3242
#include <fcntl.h>
3343
#include <sys/types.h>
@@ -216,13 +226,13 @@
216226
const char *zLogin; /* Login name. NULL or "" if not logged in. */
217227
const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
218228
const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
219229
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
220230
** SSL client identity */
221
-#if defined(_WIN32) && USE_SEE
231
+#if USE_SEE
222232
const char *zPidKey; /* Saved value of the --usepidkey option. Only
223
- * applicable when using SEE on Windows. */
233
+ * applicable when using SEE on Windows or Linux. */
224234
#endif
225235
int useLocalauth; /* No login required if from 127.0.0.1 */
226236
int noPswd; /* Logged in without password (on 127.0.0.1) */
227237
int userUid; /* Integer user id */
228238
int isHuman; /* True if access by a human, not a spider or bot */
@@ -800,29 +810,12 @@
800810
if( find_option("utc",0,0) ) g.fTimeFormat = 1;
801811
if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
802812
if( zChdir && file_chdir(zChdir, 0) ){
803813
fossil_fatal("unable to change directories to %s", zChdir);
804814
}
805
-#if defined(_WIN32) && USE_SEE
806
- {
807
- g.zPidKey = find_option("usepidkey",0,1);
808
- if( g.zPidKey ){
809
- DWORD processId = 0;
810
- LPVOID pAddress = NULL;
811
- SIZE_T nSize = 0;
812
- parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize);
813
- db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
814
- }else{
815
- const char *zSeeDbConfig = find_option("seedbcfg",0,1);
816
- if( !zSeeDbConfig ){
817
- zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG");
818
- }
819
- if( zSeeDbConfig ){
820
- db_read_saved_encryption_key_from_process_via_th1(zSeeDbConfig);
821
- }
822
- }
823
- }
815
+#if USE_SEE
816
+ db_maybe_handle_saved_encryption_key_for_process(SEE_KEY_READ);
824817
#endif
825818
if( find_option("help",0,0)!=0 ){
826819
/* If --help is found anywhere on the command line, translate the command
827820
* to "fossil help cmdname" where "cmdname" is the first argument that
828821
* does not begin with a "-" character. If all arguments start with "-",
@@ -1270,11 +1263,12 @@
12701263
#endif
12711264
#if defined(USE_MMAN_H)
12721265
blob_append(pOut, "USE_MMAN_H\n", -1);
12731266
#endif
12741267
#if defined(USE_SEE)
1275
- blob_append(pOut, "USE_SEE\n", -1);
1268
+ blob_appendf(pOut, "USE_SEE (%s)\n",
1269
+ db_have_saved_encryption_key() ? "SET" : "UNSET");
12761270
#endif
12771271
#if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES)
12781272
blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n");
12791273
#endif
12801274
@@ -1716,10 +1710,12 @@
17161710
#endif
17171711
/* If the repository has not been opened already, then find the
17181712
** repository based on the first element of PATH_INFO and open it.
17191713
*/
17201714
if( !g.repositoryOpen ){
1715
+ char zBuf[24];
1716
+ const char *zRepoExt = ".fossil";
17211717
char *zRepo; /* Candidate repository name */
17221718
char *zToFree = 0; /* Malloced memory that needs to be freed */
17231719
const char *zCleanRepo; /* zRepo with surplus leading "/" removed */
17241720
const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */
17251721
char *zNewScript; /* Revised SCRIPT_NAME after processing */
@@ -1737,11 +1733,11 @@
17371733
size_t nBase = strlen(zBase);
17381734
while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
17391735
17401736
/* The candidate repository name is some prefix of the PATH_INFO
17411737
** with ".fossil" appended */
1742
- zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo);
1738
+ zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt);
17431739
if( g.fHttpTrace ){
17441740
@ <!-- Looking for repository named "%h(zRepo)" -->
17451741
fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo);
17461742
}
17471743
@@ -1795,11 +1791,10 @@
17951791
*/
17961792
zCleanRepo = file_cleanup_fullpath(zRepo);
17971793
if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
17981794
szFile = file_size(zCleanRepo, ExtFILE);
17991795
if( g.fHttpTrace ){
1800
- char zBuf[24];
18011796
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
18021797
@ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
18031798
fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
18041799
}
18051800
}
@@ -1808,11 +1803,11 @@
18081803
** and check to see if there is a file or directory with the same
18091804
** name as the raw PATH_INFO text.
18101805
*/
18111806
if( szFile<0 && i>0 ){
18121807
const char *zMimetype;
1813
- assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
1808
+ assert( file_is_repository_extension(&zRepo[j]) );
18141809
zRepo[j] = 0; /* Remove the ".fossil" suffix */
18151810
18161811
/* The PATH_INFO prefix seen so far is a valid directory.
18171812
** Continue the loop with the next element of the PATH_INFO */
18181813
if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){
@@ -1833,11 +1828,11 @@
18331828
** pages.
18341829
*/
18351830
if( pFileGlob!=0
18361831
&& file_isfile(zCleanRepo, ExtFILE)
18371832
&& glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase))
1838
- && sqlite3_strglob("*.fossil*",zRepo)!=0
1833
+ && !file_contains_repository_extension(zRepo)
18391834
&& (zMimetype = mimetype_from_name(zRepo))!=0
18401835
&& strcmp(zMimetype, "application/x-fossil-artifact")!=0
18411836
){
18421837
Blob content;
18431838
blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
@@ -1868,10 +1863,17 @@
18681863
** string is finished. Either zRepo contains the name of the
18691864
** repository to be used, or else no repository could be found and
18701865
** some kind of error response is required.
18711866
*/
18721867
if( szFile<1024 ){
1868
+#if USE_SEE
1869
+ if( strcmp(zRepoExt,".fossil")==0 ){
1870
+ fossil_free(zToFree);
1871
+ zRepoExt = ".efossil";
1872
+ continue;
1873
+ }
1874
+#endif
18731875
set_base_url(0);
18741876
if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0)
18751877
&& allowRepoList
18761878
&& repo_list_page() ){
18771879
/* Will return a list of repositories */
@@ -1893,10 +1895,26 @@
18931895
if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo);
18941896
if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo);
18951897
cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
18961898
zPathInfo += i;
18971899
cgi_replace_parameter("SCRIPT_NAME", zNewScript);
1900
+#if USE_SEE
1901
+ if( zPathInfo ){
1902
+ if( g.fHttpTrace ){
1903
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", i);
1904
+ @ <!-- see_path_info(%s(zBuf)) is %h(zPathInfo) -->
1905
+ fprintf(stderr, "# see_path_info(%d) = %s\n", i, zPathInfo);
1906
+ }
1907
+ if( strcmp(zPathInfo,"/setseekey")==0
1908
+ && strcmp(zRepoExt,".efossil")==0
1909
+ && !db_have_saved_encryption_key() ){
1910
+ db_set_see_key_page();
1911
+ cgi_reply();
1912
+ fossil_exit(0);
1913
+ }
1914
+ }
1915
+#endif
18981916
db_open_repository(file_cleanup_fullpath(zRepo));
18991917
if( g.fHttpTrace ){
19001918
@ <!-- repository: "%h(zRepo)" -->
19011919
@ <!-- translated PATH_INFO: "%h(zPathInfo)" -->
19021920
@ <!-- translated SCRIPT_NAME: "%h(zNewScript)" -->
@@ -2615,11 +2633,11 @@
26152633
}
26162634
}
26172635
}
26182636
}
26192637
2620
-#if defined(_WIN32) && USE_SEE
2638
+#if USE_SEE
26212639
/*
26222640
** This function attempts to parse a string value in the following
26232641
** format:
26242642
**
26252643
** "%lu:%p:%u"
@@ -2632,17 +2650,19 @@
26322650
**
26332651
** If the specified value cannot be parsed, for any reason, a fatal
26342652
** error will be raised and the process will be terminated.
26352653
*/
26362654
void parse_pid_key_value(
2637
- const char *zPidKey, /* The value to be parsed. */
2638
- DWORD *pProcessId, /* The extracted process identifier. */
2639
- LPVOID *ppAddress, /* The extracted pointer value. */
2640
- SIZE_T *pnSize /* The extracted size value. */
2655
+ const char *zPidKey, /* The value to be parsed. */
2656
+ PID_T *pProcessId, /* The extracted process identifier. */
2657
+ LPVOID *ppAddress, /* The extracted pointer value. */
2658
+ SIZE_T *pnSize /* The extracted size value. */
26412659
){
2660
+ unsigned long processId = 0;
26422661
unsigned int nSize = 0;
2643
- if( sscanf(zPidKey, "%lu:%p:%u", pProcessId, ppAddress, &nSize)==3 ){
2662
+ if( sscanf(zPidKey, "%lu:%p:%u", &processId, ppAddress, &nSize)==3 ){
2663
+ *pProcessId = (PID_T)processId;
26442664
*pnSize = (SIZE_T)nSize;
26452665
}else{
26462666
fossil_fatal("failed to parse pid key");
26472667
}
26482668
}
@@ -2656,25 +2676,26 @@
26562676
** Query parameters:
26572677
**
26582678
** usepidkey When present and available, also return the
26592679
** address and size, within this server process,
26602680
** of the saved database encryption key. This
2661
-** is only supported when using SEE on Windows.
2681
+** is only supported when using SEE on Windows
2682
+** or Linux.
26622683
*/
26632684
void test_pid_page(void){
26642685
login_check_credentials();
26652686
if( !g.perm.Setup ){ login_needed(0); return; }
2666
-#if defined(_WIN32) && USE_SEE
2687
+#if USE_SEE
26672688
if( P("usepidkey")!=0 ){
26682689
if( g.zPidKey ){
26692690
@ %s(g.zPidKey)
26702691
return;
26712692
}else{
26722693
const char *zSavedKey = db_get_saved_encryption_key();
26732694
size_t savedKeySize = db_get_saved_encryption_key_size();
26742695
if( zSavedKey!=0 && savedKeySize>0 ){
2675
- @ %lu(GetCurrentProcessId()):%p(zSavedKey):%u(savedKeySize)
2696
+ @ %lu(GETPID()):%p(zSavedKey):%u(savedKeySize)
26762697
return;
26772698
}
26782699
}
26792700
}
26802701
#endif
@@ -2770,11 +2791,11 @@
27702791
** --scgi Interpret input as SCGI rather than HTTP
27712792
** --skin LABEL Use override skin LABEL. Use an empty string ("")
27722793
** to force use of the current local skin config.
27732794
** --th-trace Trace TH1 execution (for debugging purposes)
27742795
** --usepidkey Use saved encryption key from parent process. This is
2775
-** only necessary when using SEE on Windows.
2796
+** only necessary when using SEE on Windows or Linux.
27762797
**
27772798
** See also: [[cgi]], [[server]], [[winsrv]]
27782799
*/
27792800
void cmd_http(void){
27802801
const char *zIpAddr = 0;
@@ -3111,11 +3132,11 @@
31113132
** --repolist If REPOSITORY is dir, URL "/" lists repos
31123133
** --scgi Accept SCGI rather than HTTP
31133134
** --skin LABEL Use override skin LABEL
31143135
** --th-trace Trace TH1 execution (for debugging purposes)
31153136
** --usepidkey Use saved encryption key from parent process. This is
3116
-** only necessary when using SEE on Windows.
3137
+** only necessary when using SEE on Windows or Linux.
31173138
**
31183139
** See also: [[cgi]], [[http]], [[winsrv]]
31193140
*/
31203141
void cmd_webserver(void){
31213142
int iPort, mxPort; /* Range of TCP ports allowed */
@@ -3140,10 +3161,14 @@
31403161
int findServerArg = 2; /* argv index for find_server_repository() */
31413162
char *zRemote = 0; /* Remote host on which to run "fossil ui" */
31423163
const char *zJsMode; /* The --jsmode parameter */
31433164
const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
31443165
3166
+
3167
+#if USE_SEE
3168
+ db_setup_for_saved_encryption_key();
3169
+#endif
31453170
31463171
#if defined(_WIN32)
31473172
const char *zStopperFile; /* Name of file used to terminate server */
31483173
zStopperFile = find_option("stopper", 0, 1);
31493174
#endif
31503175
--- src/main.c
+++ src/main.c
@@ -24,10 +24,20 @@
24 # include <windows.h>
25 # include <io.h>
26 # define isatty(h) _isatty(h)
27 # define GETPID (int)GetCurrentProcessId
28 #endif
 
 
 
 
 
 
 
 
 
 
29 #include "main.h"
30 #include <string.h>
31 #include <time.h>
32 #include <fcntl.h>
33 #include <sys/types.h>
@@ -216,13 +226,13 @@
216 const char *zLogin; /* Login name. NULL or "" if not logged in. */
217 const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
218 const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
219 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
220 ** SSL client identity */
221 #if defined(_WIN32) && USE_SEE
222 const char *zPidKey; /* Saved value of the --usepidkey option. Only
223 * applicable when using SEE on Windows. */
224 #endif
225 int useLocalauth; /* No login required if from 127.0.0.1 */
226 int noPswd; /* Logged in without password (on 127.0.0.1) */
227 int userUid; /* Integer user id */
228 int isHuman; /* True if access by a human, not a spider or bot */
@@ -800,29 +810,12 @@
800 if( find_option("utc",0,0) ) g.fTimeFormat = 1;
801 if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
802 if( zChdir && file_chdir(zChdir, 0) ){
803 fossil_fatal("unable to change directories to %s", zChdir);
804 }
805 #if defined(_WIN32) && USE_SEE
806 {
807 g.zPidKey = find_option("usepidkey",0,1);
808 if( g.zPidKey ){
809 DWORD processId = 0;
810 LPVOID pAddress = NULL;
811 SIZE_T nSize = 0;
812 parse_pid_key_value(g.zPidKey, &processId, &pAddress, &nSize);
813 db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
814 }else{
815 const char *zSeeDbConfig = find_option("seedbcfg",0,1);
816 if( !zSeeDbConfig ){
817 zSeeDbConfig = fossil_getenv("FOSSIL_SEE_DB_CONFIG");
818 }
819 if( zSeeDbConfig ){
820 db_read_saved_encryption_key_from_process_via_th1(zSeeDbConfig);
821 }
822 }
823 }
824 #endif
825 if( find_option("help",0,0)!=0 ){
826 /* If --help is found anywhere on the command line, translate the command
827 * to "fossil help cmdname" where "cmdname" is the first argument that
828 * does not begin with a "-" character. If all arguments start with "-",
@@ -1270,11 +1263,12 @@
1270 #endif
1271 #if defined(USE_MMAN_H)
1272 blob_append(pOut, "USE_MMAN_H\n", -1);
1273 #endif
1274 #if defined(USE_SEE)
1275 blob_append(pOut, "USE_SEE\n", -1);
 
1276 #endif
1277 #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES)
1278 blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n");
1279 #endif
1280
@@ -1716,10 +1710,12 @@
1716 #endif
1717 /* If the repository has not been opened already, then find the
1718 ** repository based on the first element of PATH_INFO and open it.
1719 */
1720 if( !g.repositoryOpen ){
 
 
1721 char *zRepo; /* Candidate repository name */
1722 char *zToFree = 0; /* Malloced memory that needs to be freed */
1723 const char *zCleanRepo; /* zRepo with surplus leading "/" removed */
1724 const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */
1725 char *zNewScript; /* Revised SCRIPT_NAME after processing */
@@ -1737,11 +1733,11 @@
1737 size_t nBase = strlen(zBase);
1738 while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
1739
1740 /* The candidate repository name is some prefix of the PATH_INFO
1741 ** with ".fossil" appended */
1742 zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo);
1743 if( g.fHttpTrace ){
1744 @ <!-- Looking for repository named "%h(zRepo)" -->
1745 fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo);
1746 }
1747
@@ -1795,11 +1791,10 @@
1795 */
1796 zCleanRepo = file_cleanup_fullpath(zRepo);
1797 if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
1798 szFile = file_size(zCleanRepo, ExtFILE);
1799 if( g.fHttpTrace ){
1800 char zBuf[24];
1801 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
1802 @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
1803 fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
1804 }
1805 }
@@ -1808,11 +1803,11 @@
1808 ** and check to see if there is a file or directory with the same
1809 ** name as the raw PATH_INFO text.
1810 */
1811 if( szFile<0 && i>0 ){
1812 const char *zMimetype;
1813 assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
1814 zRepo[j] = 0; /* Remove the ".fossil" suffix */
1815
1816 /* The PATH_INFO prefix seen so far is a valid directory.
1817 ** Continue the loop with the next element of the PATH_INFO */
1818 if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){
@@ -1833,11 +1828,11 @@
1833 ** pages.
1834 */
1835 if( pFileGlob!=0
1836 && file_isfile(zCleanRepo, ExtFILE)
1837 && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase))
1838 && sqlite3_strglob("*.fossil*",zRepo)!=0
1839 && (zMimetype = mimetype_from_name(zRepo))!=0
1840 && strcmp(zMimetype, "application/x-fossil-artifact")!=0
1841 ){
1842 Blob content;
1843 blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
@@ -1868,10 +1863,17 @@
1868 ** string is finished. Either zRepo contains the name of the
1869 ** repository to be used, or else no repository could be found and
1870 ** some kind of error response is required.
1871 */
1872 if( szFile<1024 ){
 
 
 
 
 
 
 
1873 set_base_url(0);
1874 if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0)
1875 && allowRepoList
1876 && repo_list_page() ){
1877 /* Will return a list of repositories */
@@ -1893,10 +1895,26 @@
1893 if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo);
1894 if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo);
1895 cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
1896 zPathInfo += i;
1897 cgi_replace_parameter("SCRIPT_NAME", zNewScript);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1898 db_open_repository(file_cleanup_fullpath(zRepo));
1899 if( g.fHttpTrace ){
1900 @ <!-- repository: "%h(zRepo)" -->
1901 @ <!-- translated PATH_INFO: "%h(zPathInfo)" -->
1902 @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" -->
@@ -2615,11 +2633,11 @@
2615 }
2616 }
2617 }
2618 }
2619
2620 #if defined(_WIN32) && USE_SEE
2621 /*
2622 ** This function attempts to parse a string value in the following
2623 ** format:
2624 **
2625 ** "%lu:%p:%u"
@@ -2632,17 +2650,19 @@
2632 **
2633 ** If the specified value cannot be parsed, for any reason, a fatal
2634 ** error will be raised and the process will be terminated.
2635 */
2636 void parse_pid_key_value(
2637 const char *zPidKey, /* The value to be parsed. */
2638 DWORD *pProcessId, /* The extracted process identifier. */
2639 LPVOID *ppAddress, /* The extracted pointer value. */
2640 SIZE_T *pnSize /* The extracted size value. */
2641 ){
 
2642 unsigned int nSize = 0;
2643 if( sscanf(zPidKey, "%lu:%p:%u", pProcessId, ppAddress, &nSize)==3 ){
 
2644 *pnSize = (SIZE_T)nSize;
2645 }else{
2646 fossil_fatal("failed to parse pid key");
2647 }
2648 }
@@ -2656,25 +2676,26 @@
2656 ** Query parameters:
2657 **
2658 ** usepidkey When present and available, also return the
2659 ** address and size, within this server process,
2660 ** of the saved database encryption key. This
2661 ** is only supported when using SEE on Windows.
 
2662 */
2663 void test_pid_page(void){
2664 login_check_credentials();
2665 if( !g.perm.Setup ){ login_needed(0); return; }
2666 #if defined(_WIN32) && USE_SEE
2667 if( P("usepidkey")!=0 ){
2668 if( g.zPidKey ){
2669 @ %s(g.zPidKey)
2670 return;
2671 }else{
2672 const char *zSavedKey = db_get_saved_encryption_key();
2673 size_t savedKeySize = db_get_saved_encryption_key_size();
2674 if( zSavedKey!=0 && savedKeySize>0 ){
2675 @ %lu(GetCurrentProcessId()):%p(zSavedKey):%u(savedKeySize)
2676 return;
2677 }
2678 }
2679 }
2680 #endif
@@ -2770,11 +2791,11 @@
2770 ** --scgi Interpret input as SCGI rather than HTTP
2771 ** --skin LABEL Use override skin LABEL. Use an empty string ("")
2772 ** to force use of the current local skin config.
2773 ** --th-trace Trace TH1 execution (for debugging purposes)
2774 ** --usepidkey Use saved encryption key from parent process. This is
2775 ** only necessary when using SEE on Windows.
2776 **
2777 ** See also: [[cgi]], [[server]], [[winsrv]]
2778 */
2779 void cmd_http(void){
2780 const char *zIpAddr = 0;
@@ -3111,11 +3132,11 @@
3111 ** --repolist If REPOSITORY is dir, URL "/" lists repos
3112 ** --scgi Accept SCGI rather than HTTP
3113 ** --skin LABEL Use override skin LABEL
3114 ** --th-trace Trace TH1 execution (for debugging purposes)
3115 ** --usepidkey Use saved encryption key from parent process. This is
3116 ** only necessary when using SEE on Windows.
3117 **
3118 ** See also: [[cgi]], [[http]], [[winsrv]]
3119 */
3120 void cmd_webserver(void){
3121 int iPort, mxPort; /* Range of TCP ports allowed */
@@ -3140,10 +3161,14 @@
3140 int findServerArg = 2; /* argv index for find_server_repository() */
3141 char *zRemote = 0; /* Remote host on which to run "fossil ui" */
3142 const char *zJsMode; /* The --jsmode parameter */
3143 const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3144
 
 
 
 
3145
3146 #if defined(_WIN32)
3147 const char *zStopperFile; /* Name of file used to terminate server */
3148 zStopperFile = find_option("stopper", 0, 1);
3149 #endif
3150
--- src/main.c
+++ src/main.c
@@ -24,10 +24,20 @@
24 # include <windows.h>
25 # include <io.h>
26 # define isatty(h) _isatty(h)
27 # define GETPID (int)GetCurrentProcessId
28 #endif
29
30 /* BUGBUG: This (PID_T) does not work inside of INTERFACE block. */
31 #if USE_SEE
32 #if defined(_WIN32)
33 typedef DWORD PID_T;
34 #else
35 typedef pid_t PID_T;
36 #endif
37 #endif
38
39 #include "main.h"
40 #include <string.h>
41 #include <time.h>
42 #include <fcntl.h>
43 #include <sys/types.h>
@@ -216,13 +226,13 @@
226 const char *zLogin; /* Login name. NULL or "" if not logged in. */
227 const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */
228 const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */
229 const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
230 ** SSL client identity */
231 #if USE_SEE
232 const char *zPidKey; /* Saved value of the --usepidkey option. Only
233 * applicable when using SEE on Windows or Linux. */
234 #endif
235 int useLocalauth; /* No login required if from 127.0.0.1 */
236 int noPswd; /* Logged in without password (on 127.0.0.1) */
237 int userUid; /* Integer user id */
238 int isHuman; /* True if access by a human, not a spider or bot */
@@ -800,29 +810,12 @@
810 if( find_option("utc",0,0) ) g.fTimeFormat = 1;
811 if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
812 if( zChdir && file_chdir(zChdir, 0) ){
813 fossil_fatal("unable to change directories to %s", zChdir);
814 }
815 #if USE_SEE
816 db_maybe_handle_saved_encryption_key_for_process(SEE_KEY_READ);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817 #endif
818 if( find_option("help",0,0)!=0 ){
819 /* If --help is found anywhere on the command line, translate the command
820 * to "fossil help cmdname" where "cmdname" is the first argument that
821 * does not begin with a "-" character. If all arguments start with "-",
@@ -1270,11 +1263,12 @@
1263 #endif
1264 #if defined(USE_MMAN_H)
1265 blob_append(pOut, "USE_MMAN_H\n", -1);
1266 #endif
1267 #if defined(USE_SEE)
1268 blob_appendf(pOut, "USE_SEE (%s)\n",
1269 db_have_saved_encryption_key() ? "SET" : "UNSET");
1270 #endif
1271 #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES)
1272 blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n");
1273 #endif
1274
@@ -1716,10 +1710,12 @@
1710 #endif
1711 /* If the repository has not been opened already, then find the
1712 ** repository based on the first element of PATH_INFO and open it.
1713 */
1714 if( !g.repositoryOpen ){
1715 char zBuf[24];
1716 const char *zRepoExt = ".fossil";
1717 char *zRepo; /* Candidate repository name */
1718 char *zToFree = 0; /* Malloced memory that needs to be freed */
1719 const char *zCleanRepo; /* zRepo with surplus leading "/" removed */
1720 const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */
1721 char *zNewScript; /* Revised SCRIPT_NAME after processing */
@@ -1737,11 +1733,11 @@
1733 size_t nBase = strlen(zBase);
1734 while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
1735
1736 /* The candidate repository name is some prefix of the PATH_INFO
1737 ** with ".fossil" appended */
1738 zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt);
1739 if( g.fHttpTrace ){
1740 @ <!-- Looking for repository named "%h(zRepo)" -->
1741 fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo);
1742 }
1743
@@ -1795,11 +1791,10 @@
1791 */
1792 zCleanRepo = file_cleanup_fullpath(zRepo);
1793 if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
1794 szFile = file_size(zCleanRepo, ExtFILE);
1795 if( g.fHttpTrace ){
 
1796 sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
1797 @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
1798 fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
1799 }
1800 }
@@ -1808,11 +1803,11 @@
1803 ** and check to see if there is a file or directory with the same
1804 ** name as the raw PATH_INFO text.
1805 */
1806 if( szFile<0 && i>0 ){
1807 const char *zMimetype;
1808 assert( file_is_repository_extension(&zRepo[j]) );
1809 zRepo[j] = 0; /* Remove the ".fossil" suffix */
1810
1811 /* The PATH_INFO prefix seen so far is a valid directory.
1812 ** Continue the loop with the next element of the PATH_INFO */
1813 if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){
@@ -1833,11 +1828,11 @@
1828 ** pages.
1829 */
1830 if( pFileGlob!=0
1831 && file_isfile(zCleanRepo, ExtFILE)
1832 && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase))
1833 && !file_contains_repository_extension(zRepo)
1834 && (zMimetype = mimetype_from_name(zRepo))!=0
1835 && strcmp(zMimetype, "application/x-fossil-artifact")!=0
1836 ){
1837 Blob content;
1838 blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
@@ -1868,10 +1863,17 @@
1863 ** string is finished. Either zRepo contains the name of the
1864 ** repository to be used, or else no repository could be found and
1865 ** some kind of error response is required.
1866 */
1867 if( szFile<1024 ){
1868 #if USE_SEE
1869 if( strcmp(zRepoExt,".fossil")==0 ){
1870 fossil_free(zToFree);
1871 zRepoExt = ".efossil";
1872 continue;
1873 }
1874 #endif
1875 set_base_url(0);
1876 if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0)
1877 && allowRepoList
1878 && repo_list_page() ){
1879 /* Will return a list of repositories */
@@ -1893,10 +1895,26 @@
1895 if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo);
1896 if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo);
1897 cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
1898 zPathInfo += i;
1899 cgi_replace_parameter("SCRIPT_NAME", zNewScript);
1900 #if USE_SEE
1901 if( zPathInfo ){
1902 if( g.fHttpTrace ){
1903 sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", i);
1904 @ <!-- see_path_info(%s(zBuf)) is %h(zPathInfo) -->
1905 fprintf(stderr, "# see_path_info(%d) = %s\n", i, zPathInfo);
1906 }
1907 if( strcmp(zPathInfo,"/setseekey")==0
1908 && strcmp(zRepoExt,".efossil")==0
1909 && !db_have_saved_encryption_key() ){
1910 db_set_see_key_page();
1911 cgi_reply();
1912 fossil_exit(0);
1913 }
1914 }
1915 #endif
1916 db_open_repository(file_cleanup_fullpath(zRepo));
1917 if( g.fHttpTrace ){
1918 @ <!-- repository: "%h(zRepo)" -->
1919 @ <!-- translated PATH_INFO: "%h(zPathInfo)" -->
1920 @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" -->
@@ -2615,11 +2633,11 @@
2633 }
2634 }
2635 }
2636 }
2637
2638 #if USE_SEE
2639 /*
2640 ** This function attempts to parse a string value in the following
2641 ** format:
2642 **
2643 ** "%lu:%p:%u"
@@ -2632,17 +2650,19 @@
2650 **
2651 ** If the specified value cannot be parsed, for any reason, a fatal
2652 ** error will be raised and the process will be terminated.
2653 */
2654 void parse_pid_key_value(
2655 const char *zPidKey, /* The value to be parsed. */
2656 PID_T *pProcessId, /* The extracted process identifier. */
2657 LPVOID *ppAddress, /* The extracted pointer value. */
2658 SIZE_T *pnSize /* The extracted size value. */
2659 ){
2660 unsigned long processId = 0;
2661 unsigned int nSize = 0;
2662 if( sscanf(zPidKey, "%lu:%p:%u", &processId, ppAddress, &nSize)==3 ){
2663 *pProcessId = (PID_T)processId;
2664 *pnSize = (SIZE_T)nSize;
2665 }else{
2666 fossil_fatal("failed to parse pid key");
2667 }
2668 }
@@ -2656,25 +2676,26 @@
2676 ** Query parameters:
2677 **
2678 ** usepidkey When present and available, also return the
2679 ** address and size, within this server process,
2680 ** of the saved database encryption key. This
2681 ** is only supported when using SEE on Windows
2682 ** or Linux.
2683 */
2684 void test_pid_page(void){
2685 login_check_credentials();
2686 if( !g.perm.Setup ){ login_needed(0); return; }
2687 #if USE_SEE
2688 if( P("usepidkey")!=0 ){
2689 if( g.zPidKey ){
2690 @ %s(g.zPidKey)
2691 return;
2692 }else{
2693 const char *zSavedKey = db_get_saved_encryption_key();
2694 size_t savedKeySize = db_get_saved_encryption_key_size();
2695 if( zSavedKey!=0 && savedKeySize>0 ){
2696 @ %lu(GETPID()):%p(zSavedKey):%u(savedKeySize)
2697 return;
2698 }
2699 }
2700 }
2701 #endif
@@ -2770,11 +2791,11 @@
2791 ** --scgi Interpret input as SCGI rather than HTTP
2792 ** --skin LABEL Use override skin LABEL. Use an empty string ("")
2793 ** to force use of the current local skin config.
2794 ** --th-trace Trace TH1 execution (for debugging purposes)
2795 ** --usepidkey Use saved encryption key from parent process. This is
2796 ** only necessary when using SEE on Windows or Linux.
2797 **
2798 ** See also: [[cgi]], [[server]], [[winsrv]]
2799 */
2800 void cmd_http(void){
2801 const char *zIpAddr = 0;
@@ -3111,11 +3132,11 @@
3132 ** --repolist If REPOSITORY is dir, URL "/" lists repos
3133 ** --scgi Accept SCGI rather than HTTP
3134 ** --skin LABEL Use override skin LABEL
3135 ** --th-trace Trace TH1 execution (for debugging purposes)
3136 ** --usepidkey Use saved encryption key from parent process. This is
3137 ** only necessary when using SEE on Windows or Linux.
3138 **
3139 ** See also: [[cgi]], [[http]], [[winsrv]]
3140 */
3141 void cmd_webserver(void){
3142 int iPort, mxPort; /* Range of TCP ports allowed */
@@ -3140,10 +3161,14 @@
3161 int findServerArg = 2; /* argv index for find_server_repository() */
3162 char *zRemote = 0; /* Remote host on which to run "fossil ui" */
3163 const char *zJsMode; /* The --jsmode parameter */
3164 const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3165
3166
3167 #if USE_SEE
3168 db_setup_for_saved_encryption_key();
3169 #endif
3170
3171 #if defined(_WIN32)
3172 const char *zStopperFile; /* Name of file used to terminate server */
3173 zStopperFile = find_option("stopper", 0, 1);
3174 #endif
3175
+28 -8
--- src/repolist.c
+++ src/repolist.c
@@ -143,11 +143,15 @@
143143
blob_init(&base, g.zRepositoryName, -1);
144144
sqlite3_open(":memory:", &g.db);
145145
db_multi_exec("CREATE TABLE sfile(pathname TEXT);");
146146
db_multi_exec("CREATE TABLE vfile(pathname);");
147147
vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE);
148
- db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'");
148
+ db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"
149
+#if USE_SEE
150
+ " AND pathname NOT GLOB '*[^/].efossil'"
151
+#endif
152
+ );
149153
allRepo = 0;
150154
}
151155
n = db_int(0, "SELECT count(*) FROM sfile");
152156
if( n==0 ){
153157
sqlite3_close(g.db);
@@ -170,17 +174,22 @@
170174
" FROM sfile ORDER BY pathname COLLATE nocase;");
171175
rNow = db_double(0, "SELECT julianday('now')");
172176
while( db_step(&q)==SQLITE_ROW ){
173177
const char *zName = db_column_text(&q, 0);
174178
int nName = (int)strlen(zName);
179
+ int nSuffix = 7; /* ".fossil" */
175180
char *zUrl;
176181
char *zAge;
177182
char *zFull;
178183
RepoInfo x;
179184
sqlite3_int64 iAge;
180
- if( nName<7 ) continue;
181
- zUrl = sqlite3_mprintf("%.*s", nName-7, zName);
185
+#if USE_SEE
186
+ int bEncrypted = sqlite3_strglob("*.efossil", zName)==0;
187
+ if( bEncrypted ) nSuffix = 8; /* ".efossil" */
188
+#endif
189
+ if( nName<nSuffix ) continue;
190
+ zUrl = sqlite3_mprintf("%.*s", nName-nSuffix, zName);
182191
if( zName[0]=='/'
183192
#ifdef _WIN32
184193
|| sqlite3_strglob("[a-zA-Z]:/*", zName)==0
185194
#endif
186195
){
@@ -197,11 +206,15 @@
197206
zSkinRepo = mprintf("%s", x.zRepoName);
198207
zSkinUrl = mprintf("%s", zUrl);
199208
}
200209
}
201210
fossil_free(zFull);
202
- if( !x.isValid ){
211
+ if( !x.isValid
212
+#if USE_SEE
213
+ && !bEncrypted
214
+#endif
215
+ ){
203216
continue;
204217
}
205218
if( x.isRepolistSkin==2 && !allRepo ){
206219
/* Repositories with repolist-skin==2 are omitted from directory
207220
** scan lists, but included in "fossil all ui" lists */
@@ -218,11 +231,11 @@
218231
/* This repository has no entry in the "event" table.
219232
** Its age will still be maximum, so data-sortkey will work. */
220233
zAge = mprintf("unknown");
221234
}
222235
blob_append_sql(&html, "<tr><td valign='top'>");
223
- if( sqlite3_strglob("*.fossil", zName)!=0 ){
236
+ if( !file_ends_with_repository_extension(zName,0) ){
224237
/* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
225238
** do not work for repositories whose names do not end in ".fossil".
226239
** So do not hyperlink those cases. */
227240
blob_append_sql(&html,"%h",zName);
228241
} else if( sqlite3_strglob("*/.*", zName)==0 ){
@@ -230,21 +243,28 @@
230243
blob_append_sql(&html, "%h (hidden)", zName);
231244
} else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
232245
blob_append_sql(&html,
233246
"<a href='%R/%T/home' target='_blank'>/%h</a>\n",
234247
zUrl, zName);
235
- }else if( sqlite3_strglob("*/*.fossil", zName)==0 ){
248
+ }else if( file_ends_with_repository_extension(zName,1) ){
236249
/* As described in
237250
** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if
238251
** foo.fossil and foo/bar.fossil both exist and we create a
239252
** link to foo/bar/... then the URI dispatcher will instead
240253
** see that as a link to foo.fossil. In such cases, do not
241254
** emit a link to foo/bar.fossil. */
242255
char * zDirPart = file_dirname(zName);
243256
if( db_exists("SELECT 1 FROM sfile "
244
- "WHERE pathname=(%Q || '.fossil') COLLATE nocase",
245
- zDirPart) ){
257
+ "WHERE pathname=(%Q || '.fossil') COLLATE nocase"
258
+#if USE_SEE
259
+ " OR pathname=(%Q || '.efossil') COLLATE nocase"
260
+#endif
261
+ , zDirPart
262
+#if USE_SEE
263
+ , zDirPart
264
+#endif
265
+ ) ){
246266
blob_append_sql(&html,
247267
"<s>%h</s> (directory/repo name collision)\n",
248268
zName);
249269
}else{
250270
blob_append_sql(&html,
251271
--- src/repolist.c
+++ src/repolist.c
@@ -143,11 +143,15 @@
143 blob_init(&base, g.zRepositoryName, -1);
144 sqlite3_open(":memory:", &g.db);
145 db_multi_exec("CREATE TABLE sfile(pathname TEXT);");
146 db_multi_exec("CREATE TABLE vfile(pathname);");
147 vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE);
148 db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'");
 
 
 
 
149 allRepo = 0;
150 }
151 n = db_int(0, "SELECT count(*) FROM sfile");
152 if( n==0 ){
153 sqlite3_close(g.db);
@@ -170,17 +174,22 @@
170 " FROM sfile ORDER BY pathname COLLATE nocase;");
171 rNow = db_double(0, "SELECT julianday('now')");
172 while( db_step(&q)==SQLITE_ROW ){
173 const char *zName = db_column_text(&q, 0);
174 int nName = (int)strlen(zName);
 
175 char *zUrl;
176 char *zAge;
177 char *zFull;
178 RepoInfo x;
179 sqlite3_int64 iAge;
180 if( nName<7 ) continue;
181 zUrl = sqlite3_mprintf("%.*s", nName-7, zName);
 
 
 
 
182 if( zName[0]=='/'
183 #ifdef _WIN32
184 || sqlite3_strglob("[a-zA-Z]:/*", zName)==0
185 #endif
186 ){
@@ -197,11 +206,15 @@
197 zSkinRepo = mprintf("%s", x.zRepoName);
198 zSkinUrl = mprintf("%s", zUrl);
199 }
200 }
201 fossil_free(zFull);
202 if( !x.isValid ){
 
 
 
 
203 continue;
204 }
205 if( x.isRepolistSkin==2 && !allRepo ){
206 /* Repositories with repolist-skin==2 are omitted from directory
207 ** scan lists, but included in "fossil all ui" lists */
@@ -218,11 +231,11 @@
218 /* This repository has no entry in the "event" table.
219 ** Its age will still be maximum, so data-sortkey will work. */
220 zAge = mprintf("unknown");
221 }
222 blob_append_sql(&html, "<tr><td valign='top'>");
223 if( sqlite3_strglob("*.fossil", zName)!=0 ){
224 /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
225 ** do not work for repositories whose names do not end in ".fossil".
226 ** So do not hyperlink those cases. */
227 blob_append_sql(&html,"%h",zName);
228 } else if( sqlite3_strglob("*/.*", zName)==0 ){
@@ -230,21 +243,28 @@
230 blob_append_sql(&html, "%h (hidden)", zName);
231 } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
232 blob_append_sql(&html,
233 "<a href='%R/%T/home' target='_blank'>/%h</a>\n",
234 zUrl, zName);
235 }else if( sqlite3_strglob("*/*.fossil", zName)==0 ){
236 /* As described in
237 ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if
238 ** foo.fossil and foo/bar.fossil both exist and we create a
239 ** link to foo/bar/... then the URI dispatcher will instead
240 ** see that as a link to foo.fossil. In such cases, do not
241 ** emit a link to foo/bar.fossil. */
242 char * zDirPart = file_dirname(zName);
243 if( db_exists("SELECT 1 FROM sfile "
244 "WHERE pathname=(%Q || '.fossil') COLLATE nocase",
245 zDirPart) ){
 
 
 
 
 
 
 
246 blob_append_sql(&html,
247 "<s>%h</s> (directory/repo name collision)\n",
248 zName);
249 }else{
250 blob_append_sql(&html,
251
--- src/repolist.c
+++ src/repolist.c
@@ -143,11 +143,15 @@
143 blob_init(&base, g.zRepositoryName, -1);
144 sqlite3_open(":memory:", &g.db);
145 db_multi_exec("CREATE TABLE sfile(pathname TEXT);");
146 db_multi_exec("CREATE TABLE vfile(pathname);");
147 vfile_scan(&base, blob_size(&base), 0, 0, 0, ExtFILE);
148 db_multi_exec("DELETE FROM sfile WHERE pathname NOT GLOB '*[^/].fossil'"
149 #if USE_SEE
150 " AND pathname NOT GLOB '*[^/].efossil'"
151 #endif
152 );
153 allRepo = 0;
154 }
155 n = db_int(0, "SELECT count(*) FROM sfile");
156 if( n==0 ){
157 sqlite3_close(g.db);
@@ -170,17 +174,22 @@
174 " FROM sfile ORDER BY pathname COLLATE nocase;");
175 rNow = db_double(0, "SELECT julianday('now')");
176 while( db_step(&q)==SQLITE_ROW ){
177 const char *zName = db_column_text(&q, 0);
178 int nName = (int)strlen(zName);
179 int nSuffix = 7; /* ".fossil" */
180 char *zUrl;
181 char *zAge;
182 char *zFull;
183 RepoInfo x;
184 sqlite3_int64 iAge;
185 #if USE_SEE
186 int bEncrypted = sqlite3_strglob("*.efossil", zName)==0;
187 if( bEncrypted ) nSuffix = 8; /* ".efossil" */
188 #endif
189 if( nName<nSuffix ) continue;
190 zUrl = sqlite3_mprintf("%.*s", nName-nSuffix, zName);
191 if( zName[0]=='/'
192 #ifdef _WIN32
193 || sqlite3_strglob("[a-zA-Z]:/*", zName)==0
194 #endif
195 ){
@@ -197,11 +206,15 @@
206 zSkinRepo = mprintf("%s", x.zRepoName);
207 zSkinUrl = mprintf("%s", zUrl);
208 }
209 }
210 fossil_free(zFull);
211 if( !x.isValid
212 #if USE_SEE
213 && !bEncrypted
214 #endif
215 ){
216 continue;
217 }
218 if( x.isRepolistSkin==2 && !allRepo ){
219 /* Repositories with repolist-skin==2 are omitted from directory
220 ** scan lists, but included in "fossil all ui" lists */
@@ -218,11 +231,11 @@
231 /* This repository has no entry in the "event" table.
232 ** Its age will still be maximum, so data-sortkey will work. */
233 zAge = mprintf("unknown");
234 }
235 blob_append_sql(&html, "<tr><td valign='top'>");
236 if( !file_ends_with_repository_extension(zName,0) ){
237 /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands
238 ** do not work for repositories whose names do not end in ".fossil".
239 ** So do not hyperlink those cases. */
240 blob_append_sql(&html,"%h",zName);
241 } else if( sqlite3_strglob("*/.*", zName)==0 ){
@@ -230,21 +243,28 @@
243 blob_append_sql(&html, "%h (hidden)", zName);
244 } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){
245 blob_append_sql(&html,
246 "<a href='%R/%T/home' target='_blank'>/%h</a>\n",
247 zUrl, zName);
248 }else if( file_ends_with_repository_extension(zName,1) ){
249 /* As described in
250 ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if
251 ** foo.fossil and foo/bar.fossil both exist and we create a
252 ** link to foo/bar/... then the URI dispatcher will instead
253 ** see that as a link to foo.fossil. In such cases, do not
254 ** emit a link to foo/bar.fossil. */
255 char * zDirPart = file_dirname(zName);
256 if( db_exists("SELECT 1 FROM sfile "
257 "WHERE pathname=(%Q || '.fossil') COLLATE nocase"
258 #if USE_SEE
259 " OR pathname=(%Q || '.efossil') COLLATE nocase"
260 #endif
261 , zDirPart
262 #if USE_SEE
263 , zDirPart
264 #endif
265 ) ){
266 blob_append_sql(&html,
267 "<s>%h</s> (directory/repo name collision)\n",
268 zName);
269 }else{
270 blob_append_sql(&html,
271
+1 -1
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -287,11 +287,11 @@
287287
void fossil_key(const char **pzKey, int *pnKey){
288288
char *zSavedKey = db_get_saved_encryption_key();
289289
char *zKey;
290290
size_t savedKeySize = db_get_saved_encryption_key_size();
291291
292
- if( zSavedKey==0 || savedKeySize==0 ) return;
292
+ if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return;
293293
zKey = (char*)malloc( savedKeySize );
294294
if( zKey ){
295295
memcpy(zKey, zSavedKey, savedKeySize);
296296
*pzKey = zKey;
297297
if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){
298298
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -287,11 +287,11 @@
287 void fossil_key(const char **pzKey, int *pnKey){
288 char *zSavedKey = db_get_saved_encryption_key();
289 char *zKey;
290 size_t savedKeySize = db_get_saved_encryption_key_size();
291
292 if( zSavedKey==0 || savedKeySize==0 ) return;
293 zKey = (char*)malloc( savedKeySize );
294 if( zKey ){
295 memcpy(zKey, zSavedKey, savedKeySize);
296 *pzKey = zKey;
297 if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){
298
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -287,11 +287,11 @@
287 void fossil_key(const char **pzKey, int *pnKey){
288 char *zSavedKey = db_get_saved_encryption_key();
289 char *zKey;
290 size_t savedKeySize = db_get_saved_encryption_key_size();
291
292 if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return;
293 zKey = (char*)malloc( savedKeySize );
294 if( zKey ){
295 memcpy(zKey, zSavedKey, savedKeySize);
296 *pzKey = zKey;
297 if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){
298
+1 -1
--- src/winhttp.c
+++ src/winhttp.c
@@ -625,11 +625,11 @@
625625
blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0);
626626
}
627627
#if USE_SEE
628628
zSavedKey = db_get_saved_encryption_key();
629629
savedKeySize = db_get_saved_encryption_key_size();
630
- if( zSavedKey!=0 && savedKeySize>0 ){
630
+ if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){
631631
blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
632632
zSavedKey, savedKeySize);
633633
}
634634
#endif
635635
if( WSAStartup(MAKEWORD(2,0), &wd) ){
636636
--- src/winhttp.c
+++ src/winhttp.c
@@ -625,11 +625,11 @@
625 blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0);
626 }
627 #if USE_SEE
628 zSavedKey = db_get_saved_encryption_key();
629 savedKeySize = db_get_saved_encryption_key_size();
630 if( zSavedKey!=0 && savedKeySize>0 ){
631 blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
632 zSavedKey, savedKeySize);
633 }
634 #endif
635 if( WSAStartup(MAKEWORD(2,0), &wd) ){
636
--- src/winhttp.c
+++ src/winhttp.c
@@ -625,11 +625,11 @@
625 blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0);
626 }
627 #if USE_SEE
628 zSavedKey = db_get_saved_encryption_key();
629 savedKeySize = db_get_saved_encryption_key_size();
630 if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){
631 blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(),
632 zSavedKey, savedKeySize);
633 }
634 #endif
635 if( WSAStartup(MAKEWORD(2,0), &wd) ){
636

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button