Fossil SCM
Enhancements to SEE integration.
Commit
f41cf0350500fa48abac523eb722ce3f94c81adf6eaf32193c09266284e21b0a
Parent
0b33be99a22d4fe…
7 files changed
+370
-34
+370
-34
+43
+62
-37
+28
-8
+1
-1
+1
-1
M
src/db.c
+370
-34
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -31,25 +31,57 @@ | ||
| 31 | 31 | */ |
| 32 | 32 | #include "config.h" |
| 33 | 33 | #if defined(_WIN32) |
| 34 | 34 | # if USE_SEE |
| 35 | 35 | # include <windows.h> |
| 36 | +# define GETPID (int)GetCurrentProcessId | |
| 36 | 37 | # endif |
| 37 | 38 | #else |
| 38 | 39 | # include <pwd.h> |
| 40 | +# if USE_SEE | |
| 41 | +# define GETPID getpid | |
| 42 | +# endif | |
| 39 | 43 | #endif |
| 40 | 44 | #if USE_SEE && !defined(SQLITE_HAS_CODEC) |
| 41 | 45 | # define SQLITE_HAS_CODEC |
| 46 | +#endif | |
| 47 | +#if USE_SEE && defined(__linux__) | |
| 48 | +# include <sys/uio.h> | |
| 42 | 49 | #endif |
| 43 | 50 | #include <sqlite3.h> |
| 44 | 51 | #include <sys/types.h> |
| 45 | 52 | #include <sys/stat.h> |
| 46 | 53 | #include <unistd.h> |
| 47 | 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 | + | |
| 48 | 65 | #include "db.h" |
| 49 | 66 | |
| 50 | 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 | + | |
| 51 | 83 | /* |
| 52 | 84 | ** An single SQL statement is represented as an instance of the following |
| 53 | 85 | ** structure. |
| 54 | 86 | */ |
| 55 | 87 | struct Stmt { |
| @@ -1539,11 +1571,30 @@ | ||
| 1539 | 1571 | static char *zSavedKey = 0; |
| 1540 | 1572 | |
| 1541 | 1573 | /* |
| 1542 | 1574 | ** This is the size of the saved database encryption key, in bytes. |
| 1543 | 1575 | */ |
| 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 | +} | |
| 1545 | 1596 | |
| 1546 | 1597 | /* |
| 1547 | 1598 | ** This function returns the saved database encryption key -OR- zero if |
| 1548 | 1599 | ** no database encryption key is saved. |
| 1549 | 1600 | */ |
| @@ -1556,10 +1607,36 @@ | ||
| 1556 | 1607 | ** -OR- zero if no database encryption key is saved. |
| 1557 | 1608 | */ |
| 1558 | 1609 | size_t db_get_saved_encryption_key_size(){ |
| 1559 | 1610 | return savedKeySize; |
| 1560 | 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 | +} | |
| 1561 | 1638 | |
| 1562 | 1639 | /* |
| 1563 | 1640 | ** This function arranges for the database encryption key to be securely |
| 1564 | 1641 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1565 | 1642 | */ |
| @@ -1569,10 +1646,11 @@ | ||
| 1569 | 1646 | void *p = NULL; |
| 1570 | 1647 | size_t n = 0; |
| 1571 | 1648 | size_t pageSize = 0; |
| 1572 | 1649 | size_t blobSize = 0; |
| 1573 | 1650 | |
| 1651 | + assert( !db_have_saved_encryption_key() ); | |
| 1574 | 1652 | blobSize = blob_size(pKey); |
| 1575 | 1653 | if( blobSize==0 ) return; |
| 1576 | 1654 | fossil_get_page_size(&pageSize); |
| 1577 | 1655 | assert( pageSize>0 ); |
| 1578 | 1656 | if( blobSize>pageSize ){ |
| @@ -1599,11 +1677,11 @@ | ||
| 1599 | 1677 | |
| 1600 | 1678 | /* |
| 1601 | 1679 | ** This function sets the saved database encryption key to the specified |
| 1602 | 1680 | ** string value, allocating or freeing the underlying memory if needed. |
| 1603 | 1681 | */ |
| 1604 | -void db_set_saved_encryption_key( | |
| 1682 | +static void db_set_saved_encryption_key( | |
| 1605 | 1683 | Blob *pKey |
| 1606 | 1684 | ){ |
| 1607 | 1685 | if( zSavedKey!=NULL ){ |
| 1608 | 1686 | size_t blobSize = blob_size(pKey); |
| 1609 | 1687 | if( blobSize==0 ){ |
| @@ -1619,25 +1697,214 @@ | ||
| 1619 | 1697 | }else{ |
| 1620 | 1698 | db_save_encryption_key(pKey); |
| 1621 | 1699 | } |
| 1622 | 1700 | } |
| 1623 | 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 | + { | |
| 1624 | 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 | + | |
| 1625 | 1893 | /* |
| 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. | |
| 1629 | 1897 | */ |
| 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. */ | |
| 1632 | 1900 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1633 | 1901 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1634 | 1902 | ){ |
| 1635 | 1903 | void *p = NULL; |
| 1636 | 1904 | size_t n = 0; |
| 1637 | 1905 | size_t pageSize = 0; |
| 1638 | - HANDLE hProcess = NULL; | |
| 1639 | 1906 | |
| 1640 | 1907 | fossil_get_page_size(&pageSize); |
| 1641 | 1908 | assert( pageSize>0 ); |
| 1642 | 1909 | if( nSize>pageSize ){ |
| 1643 | 1910 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| @@ -1644,42 +1911,66 @@ | ||
| 1644 | 1911 | } |
| 1645 | 1912 | p = fossil_secure_alloc_page(&n); |
| 1646 | 1913 | assert( p!=NULL ); |
| 1647 | 1914 | assert( n==pageSize ); |
| 1648 | 1915 | 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 | |
| 1669 | 1957 | } |
| 1670 | 1958 | } |
| 1671 | 1959 | |
| 1672 | 1960 | /* |
| 1673 | 1961 | ** This function evaluates the specified TH1 script and attempts to parse |
| 1674 | 1962 | ** its result as a colon-delimited triplet containing a process identifier, |
| 1675 | 1963 | ** 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. | |
| 1677 | 1965 | */ |
| 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. */ | |
| 1680 | 1970 | ){ |
| 1971 | + PID_T processId = 0; | |
| 1681 | 1972 | int rc; |
| 1682 | 1973 | char *zResult; |
| 1683 | 1974 | char *zPwd = file_getcwd(0, 0); |
| 1684 | 1975 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); |
| 1685 | 1976 | rc = Th_Eval(g.interp, 0, zConfig, -1); |
| @@ -1686,20 +1977,65 @@ | ||
| 1686 | 1977 | zResult = (char*)Th_GetResult(g.interp, 0); |
| 1687 | 1978 | if( rc!=TH_OK ){ |
| 1688 | 1979 | fossil_fatal("script for pid key failed: %s", zResult); |
| 1689 | 1980 | } |
| 1690 | 1981 | if( zResult ){ |
| 1691 | - DWORD processId = 0; | |
| 1692 | 1982 | LPVOID pAddress = NULL; |
| 1693 | 1983 | SIZE_T nSize = 0; |
| 1694 | 1984 | 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 | + } | |
| 1696 | 1994 | } |
| 1697 | 1995 | file_chdir(zPwd, 0); |
| 1698 | 1996 | fossil_free(zPwd); |
| 1997 | + return processId; | |
| 1699 | 1998 | } |
| 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 | +} | |
| 1701 | 2037 | #endif /* USE_SEE */ |
| 1702 | 2038 | |
| 1703 | 2039 | /* |
| 1704 | 2040 | ** If the database file zDbFile has a name that suggests that it is |
| 1705 | 2041 | ** encrypted, then prompt for the database encryption key and return it |
| 1706 | 2042 |
| --- 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 |
M
src/db.c
+370
-34
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -31,25 +31,57 @@ | ||
| 31 | 31 | */ |
| 32 | 32 | #include "config.h" |
| 33 | 33 | #if defined(_WIN32) |
| 34 | 34 | # if USE_SEE |
| 35 | 35 | # include <windows.h> |
| 36 | +# define GETPID (int)GetCurrentProcessId | |
| 36 | 37 | # endif |
| 37 | 38 | #else |
| 38 | 39 | # include <pwd.h> |
| 40 | +# if USE_SEE | |
| 41 | +# define GETPID getpid | |
| 42 | +# endif | |
| 39 | 43 | #endif |
| 40 | 44 | #if USE_SEE && !defined(SQLITE_HAS_CODEC) |
| 41 | 45 | # define SQLITE_HAS_CODEC |
| 46 | +#endif | |
| 47 | +#if USE_SEE && defined(__linux__) | |
| 48 | +# include <sys/uio.h> | |
| 42 | 49 | #endif |
| 43 | 50 | #include <sqlite3.h> |
| 44 | 51 | #include <sys/types.h> |
| 45 | 52 | #include <sys/stat.h> |
| 46 | 53 | #include <unistd.h> |
| 47 | 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 | + | |
| 48 | 65 | #include "db.h" |
| 49 | 66 | |
| 50 | 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 | + | |
| 51 | 83 | /* |
| 52 | 84 | ** An single SQL statement is represented as an instance of the following |
| 53 | 85 | ** structure. |
| 54 | 86 | */ |
| 55 | 87 | struct Stmt { |
| @@ -1539,11 +1571,30 @@ | ||
| 1539 | 1571 | static char *zSavedKey = 0; |
| 1540 | 1572 | |
| 1541 | 1573 | /* |
| 1542 | 1574 | ** This is the size of the saved database encryption key, in bytes. |
| 1543 | 1575 | */ |
| 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 | +} | |
| 1545 | 1596 | |
| 1546 | 1597 | /* |
| 1547 | 1598 | ** This function returns the saved database encryption key -OR- zero if |
| 1548 | 1599 | ** no database encryption key is saved. |
| 1549 | 1600 | */ |
| @@ -1556,10 +1607,36 @@ | ||
| 1556 | 1607 | ** -OR- zero if no database encryption key is saved. |
| 1557 | 1608 | */ |
| 1558 | 1609 | size_t db_get_saved_encryption_key_size(){ |
| 1559 | 1610 | return savedKeySize; |
| 1560 | 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 | +} | |
| 1561 | 1638 | |
| 1562 | 1639 | /* |
| 1563 | 1640 | ** This function arranges for the database encryption key to be securely |
| 1564 | 1641 | ** saved in non-pagable memory (on platforms where this is possible). |
| 1565 | 1642 | */ |
| @@ -1569,10 +1646,11 @@ | ||
| 1569 | 1646 | void *p = NULL; |
| 1570 | 1647 | size_t n = 0; |
| 1571 | 1648 | size_t pageSize = 0; |
| 1572 | 1649 | size_t blobSize = 0; |
| 1573 | 1650 | |
| 1651 | + assert( !db_have_saved_encryption_key() ); | |
| 1574 | 1652 | blobSize = blob_size(pKey); |
| 1575 | 1653 | if( blobSize==0 ) return; |
| 1576 | 1654 | fossil_get_page_size(&pageSize); |
| 1577 | 1655 | assert( pageSize>0 ); |
| 1578 | 1656 | if( blobSize>pageSize ){ |
| @@ -1599,11 +1677,11 @@ | ||
| 1599 | 1677 | |
| 1600 | 1678 | /* |
| 1601 | 1679 | ** This function sets the saved database encryption key to the specified |
| 1602 | 1680 | ** string value, allocating or freeing the underlying memory if needed. |
| 1603 | 1681 | */ |
| 1604 | -void db_set_saved_encryption_key( | |
| 1682 | +static void db_set_saved_encryption_key( | |
| 1605 | 1683 | Blob *pKey |
| 1606 | 1684 | ){ |
| 1607 | 1685 | if( zSavedKey!=NULL ){ |
| 1608 | 1686 | size_t blobSize = blob_size(pKey); |
| 1609 | 1687 | if( blobSize==0 ){ |
| @@ -1619,25 +1697,214 @@ | ||
| 1619 | 1697 | }else{ |
| 1620 | 1698 | db_save_encryption_key(pKey); |
| 1621 | 1699 | } |
| 1622 | 1700 | } |
| 1623 | 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 | + { | |
| 1624 | 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 | + | |
| 1625 | 1893 | /* |
| 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. | |
| 1629 | 1897 | */ |
| 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. */ | |
| 1632 | 1900 | LPVOID pAddress, /* Pointer to saved key buffer in the parent process. */ |
| 1633 | 1901 | SIZE_T nSize /* Size of saved key buffer in the parent process. */ |
| 1634 | 1902 | ){ |
| 1635 | 1903 | void *p = NULL; |
| 1636 | 1904 | size_t n = 0; |
| 1637 | 1905 | size_t pageSize = 0; |
| 1638 | - HANDLE hProcess = NULL; | |
| 1639 | 1906 | |
| 1640 | 1907 | fossil_get_page_size(&pageSize); |
| 1641 | 1908 | assert( pageSize>0 ); |
| 1642 | 1909 | if( nSize>pageSize ){ |
| 1643 | 1910 | fossil_panic("key too large: %u versus %u", nSize, pageSize); |
| @@ -1644,42 +1911,66 @@ | ||
| 1644 | 1911 | } |
| 1645 | 1912 | p = fossil_secure_alloc_page(&n); |
| 1646 | 1913 | assert( p!=NULL ); |
| 1647 | 1914 | assert( n==pageSize ); |
| 1648 | 1915 | 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 | |
| 1669 | 1957 | } |
| 1670 | 1958 | } |
| 1671 | 1959 | |
| 1672 | 1960 | /* |
| 1673 | 1961 | ** This function evaluates the specified TH1 script and attempts to parse |
| 1674 | 1962 | ** its result as a colon-delimited triplet containing a process identifier, |
| 1675 | 1963 | ** 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. | |
| 1677 | 1965 | */ |
| 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. */ | |
| 1680 | 1970 | ){ |
| 1971 | + PID_T processId = 0; | |
| 1681 | 1972 | int rc; |
| 1682 | 1973 | char *zResult; |
| 1683 | 1974 | char *zPwd = file_getcwd(0, 0); |
| 1684 | 1975 | Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_NEED_CONFIG | TH_INIT_NO_REPO); |
| 1685 | 1976 | rc = Th_Eval(g.interp, 0, zConfig, -1); |
| @@ -1686,20 +1977,65 @@ | ||
| 1686 | 1977 | zResult = (char*)Th_GetResult(g.interp, 0); |
| 1687 | 1978 | if( rc!=TH_OK ){ |
| 1688 | 1979 | fossil_fatal("script for pid key failed: %s", zResult); |
| 1689 | 1980 | } |
| 1690 | 1981 | if( zResult ){ |
| 1691 | - DWORD processId = 0; | |
| 1692 | 1982 | LPVOID pAddress = NULL; |
| 1693 | 1983 | SIZE_T nSize = 0; |
| 1694 | 1984 | 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 | + } | |
| 1696 | 1994 | } |
| 1697 | 1995 | file_chdir(zPwd, 0); |
| 1698 | 1996 | fossil_free(zPwd); |
| 1997 | + return processId; | |
| 1699 | 1998 | } |
| 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 | +} | |
| 1701 | 2037 | #endif /* USE_SEE */ |
| 1702 | 2038 | |
| 1703 | 2039 | /* |
| 1704 | 2040 | ** If the database file zDbFile has a name that suggests that it is |
| 1705 | 2041 | ** encrypted, then prompt for the database encryption key and return it |
| 1706 | 2042 |
| --- 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 @@ | ||
| 2277 | 2277 | int i; |
| 2278 | 2278 | for(i=2; i<g.argc; i++){ |
| 2279 | 2279 | fossil_print("%s %s\n", file_is_win_reserved(g.argv[i]), g.argv[i]); |
| 2280 | 2280 | } |
| 2281 | 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 | +} | |
| 2282 | 2325 | |
| 2283 | 2326 | /* |
| 2284 | 2327 | ** Remove surplus "/" characters from the beginning of a full pathname. |
| 2285 | 2328 | ** Extra leading "/" characters are benign on unix. But on Windows |
| 2286 | 2329 | ** machines, they must be removed. Example: Convert "/C:/fossil/xyx.fossil" |
| 2287 | 2330 |
| --- 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 @@ | ||
| 24 | 24 | # include <windows.h> |
| 25 | 25 | # include <io.h> |
| 26 | 26 | # define isatty(h) _isatty(h) |
| 27 | 27 | # define GETPID (int)GetCurrentProcessId |
| 28 | 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 | + | |
| 29 | 39 | #include "main.h" |
| 30 | 40 | #include <string.h> |
| 31 | 41 | #include <time.h> |
| 32 | 42 | #include <fcntl.h> |
| 33 | 43 | #include <sys/types.h> |
| @@ -216,13 +226,13 @@ | ||
| 216 | 226 | const char *zLogin; /* Login name. NULL or "" if not logged in. */ |
| 217 | 227 | const char *zCkoutAlias; /* doc/ uses this branch as an alias for "ckout" */ |
| 218 | 228 | const char *zMainMenuFile; /* --mainmenu FILE from server/ui/cgi */ |
| 219 | 229 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of |
| 220 | 230 | ** SSL client identity */ |
| 221 | -#if defined(_WIN32) && USE_SEE | |
| 231 | +#if USE_SEE | |
| 222 | 232 | 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. */ | |
| 224 | 234 | #endif |
| 225 | 235 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 226 | 236 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 227 | 237 | int userUid; /* Integer user id */ |
| 228 | 238 | int isHuman; /* True if access by a human, not a spider or bot */ |
| @@ -800,29 +810,12 @@ | ||
| 800 | 810 | if( find_option("utc",0,0) ) g.fTimeFormat = 1; |
| 801 | 811 | if( find_option("localtime",0,0) ) g.fTimeFormat = 2; |
| 802 | 812 | if( zChdir && file_chdir(zChdir, 0) ){ |
| 803 | 813 | fossil_fatal("unable to change directories to %s", zChdir); |
| 804 | 814 | } |
| 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); | |
| 824 | 817 | #endif |
| 825 | 818 | if( find_option("help",0,0)!=0 ){ |
| 826 | 819 | /* If --help is found anywhere on the command line, translate the command |
| 827 | 820 | * to "fossil help cmdname" where "cmdname" is the first argument that |
| 828 | 821 | * does not begin with a "-" character. If all arguments start with "-", |
| @@ -1270,11 +1263,12 @@ | ||
| 1270 | 1263 | #endif |
| 1271 | 1264 | #if defined(USE_MMAN_H) |
| 1272 | 1265 | blob_append(pOut, "USE_MMAN_H\n", -1); |
| 1273 | 1266 | #endif |
| 1274 | 1267 | #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"); | |
| 1276 | 1270 | #endif |
| 1277 | 1271 | #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES) |
| 1278 | 1272 | blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n"); |
| 1279 | 1273 | #endif |
| 1280 | 1274 | |
| @@ -1716,10 +1710,12 @@ | ||
| 1716 | 1710 | #endif |
| 1717 | 1711 | /* If the repository has not been opened already, then find the |
| 1718 | 1712 | ** repository based on the first element of PATH_INFO and open it. |
| 1719 | 1713 | */ |
| 1720 | 1714 | if( !g.repositoryOpen ){ |
| 1715 | + char zBuf[24]; | |
| 1716 | + const char *zRepoExt = ".fossil"; | |
| 1721 | 1717 | char *zRepo; /* Candidate repository name */ |
| 1722 | 1718 | char *zToFree = 0; /* Malloced memory that needs to be freed */ |
| 1723 | 1719 | const char *zCleanRepo; /* zRepo with surplus leading "/" removed */ |
| 1724 | 1720 | const char *zOldScript = PD("SCRIPT_NAME", ""); /* Original SCRIPT_NAME */ |
| 1725 | 1721 | char *zNewScript; /* Revised SCRIPT_NAME after processing */ |
| @@ -1737,11 +1733,11 @@ | ||
| 1737 | 1733 | size_t nBase = strlen(zBase); |
| 1738 | 1734 | while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; } |
| 1739 | 1735 | |
| 1740 | 1736 | /* The candidate repository name is some prefix of the PATH_INFO |
| 1741 | 1737 | ** with ".fossil" appended */ |
| 1742 | - zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo); | |
| 1738 | + zRepo = zToFree = mprintf("%s%.*s%s",zBase,i,zPathInfo,zRepoExt); | |
| 1743 | 1739 | if( g.fHttpTrace ){ |
| 1744 | 1740 | @ <!-- Looking for repository named "%h(zRepo)" --> |
| 1745 | 1741 | fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo); |
| 1746 | 1742 | } |
| 1747 | 1743 | |
| @@ -1795,11 +1791,10 @@ | ||
| 1795 | 1791 | */ |
| 1796 | 1792 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1797 | 1793 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1798 | 1794 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1799 | 1795 | if( g.fHttpTrace ){ |
| 1800 | - char zBuf[24]; | |
| 1801 | 1796 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1802 | 1797 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1803 | 1798 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1804 | 1799 | } |
| 1805 | 1800 | } |
| @@ -1808,11 +1803,11 @@ | ||
| 1808 | 1803 | ** and check to see if there is a file or directory with the same |
| 1809 | 1804 | ** name as the raw PATH_INFO text. |
| 1810 | 1805 | */ |
| 1811 | 1806 | if( szFile<0 && i>0 ){ |
| 1812 | 1807 | const char *zMimetype; |
| 1813 | - assert( fossil_strcmp(&zRepo[j], ".fossil")==0 ); | |
| 1808 | + assert( file_is_repository_extension(&zRepo[j]) ); | |
| 1814 | 1809 | zRepo[j] = 0; /* Remove the ".fossil" suffix */ |
| 1815 | 1810 | |
| 1816 | 1811 | /* The PATH_INFO prefix seen so far is a valid directory. |
| 1817 | 1812 | ** Continue the loop with the next element of the PATH_INFO */ |
| 1818 | 1813 | if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){ |
| @@ -1833,11 +1828,11 @@ | ||
| 1833 | 1828 | ** pages. |
| 1834 | 1829 | */ |
| 1835 | 1830 | if( pFileGlob!=0 |
| 1836 | 1831 | && file_isfile(zCleanRepo, ExtFILE) |
| 1837 | 1832 | && glob_match(pFileGlob, file_cleanup_fullpath(zRepo+nBase)) |
| 1838 | - && sqlite3_strglob("*.fossil*",zRepo)!=0 | |
| 1833 | + && !file_contains_repository_extension(zRepo) | |
| 1839 | 1834 | && (zMimetype = mimetype_from_name(zRepo))!=0 |
| 1840 | 1835 | && strcmp(zMimetype, "application/x-fossil-artifact")!=0 |
| 1841 | 1836 | ){ |
| 1842 | 1837 | Blob content; |
| 1843 | 1838 | blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE); |
| @@ -1868,10 +1863,17 @@ | ||
| 1868 | 1863 | ** string is finished. Either zRepo contains the name of the |
| 1869 | 1864 | ** repository to be used, or else no repository could be found and |
| 1870 | 1865 | ** some kind of error response is required. |
| 1871 | 1866 | */ |
| 1872 | 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 | |
| 1873 | 1875 | set_base_url(0); |
| 1874 | 1876 | if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0) |
| 1875 | 1877 | && allowRepoList |
| 1876 | 1878 | && repo_list_page() ){ |
| 1877 | 1879 | /* Will return a list of repositories */ |
| @@ -1893,10 +1895,26 @@ | ||
| 1893 | 1895 | if( g.zTop ) g.zTop = mprintf("%R%.*s", i, zPathInfo); |
| 1894 | 1896 | if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo); |
| 1895 | 1897 | cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); |
| 1896 | 1898 | zPathInfo += i; |
| 1897 | 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 | |
| 1898 | 1916 | db_open_repository(file_cleanup_fullpath(zRepo)); |
| 1899 | 1917 | if( g.fHttpTrace ){ |
| 1900 | 1918 | @ <!-- repository: "%h(zRepo)" --> |
| 1901 | 1919 | @ <!-- translated PATH_INFO: "%h(zPathInfo)" --> |
| 1902 | 1920 | @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" --> |
| @@ -2615,11 +2633,11 @@ | ||
| 2615 | 2633 | } |
| 2616 | 2634 | } |
| 2617 | 2635 | } |
| 2618 | 2636 | } |
| 2619 | 2637 | |
| 2620 | -#if defined(_WIN32) && USE_SEE | |
| 2638 | +#if USE_SEE | |
| 2621 | 2639 | /* |
| 2622 | 2640 | ** This function attempts to parse a string value in the following |
| 2623 | 2641 | ** format: |
| 2624 | 2642 | ** |
| 2625 | 2643 | ** "%lu:%p:%u" |
| @@ -2632,17 +2650,19 @@ | ||
| 2632 | 2650 | ** |
| 2633 | 2651 | ** If the specified value cannot be parsed, for any reason, a fatal |
| 2634 | 2652 | ** error will be raised and the process will be terminated. |
| 2635 | 2653 | */ |
| 2636 | 2654 | 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. */ | |
| 2641 | 2659 | ){ |
| 2660 | + unsigned long processId = 0; | |
| 2642 | 2661 | 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; | |
| 2644 | 2664 | *pnSize = (SIZE_T)nSize; |
| 2645 | 2665 | }else{ |
| 2646 | 2666 | fossil_fatal("failed to parse pid key"); |
| 2647 | 2667 | } |
| 2648 | 2668 | } |
| @@ -2656,25 +2676,26 @@ | ||
| 2656 | 2676 | ** Query parameters: |
| 2657 | 2677 | ** |
| 2658 | 2678 | ** usepidkey When present and available, also return the |
| 2659 | 2679 | ** address and size, within this server process, |
| 2660 | 2680 | ** 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. | |
| 2662 | 2683 | */ |
| 2663 | 2684 | void test_pid_page(void){ |
| 2664 | 2685 | login_check_credentials(); |
| 2665 | 2686 | if( !g.perm.Setup ){ login_needed(0); return; } |
| 2666 | -#if defined(_WIN32) && USE_SEE | |
| 2687 | +#if USE_SEE | |
| 2667 | 2688 | if( P("usepidkey")!=0 ){ |
| 2668 | 2689 | if( g.zPidKey ){ |
| 2669 | 2690 | @ %s(g.zPidKey) |
| 2670 | 2691 | return; |
| 2671 | 2692 | }else{ |
| 2672 | 2693 | const char *zSavedKey = db_get_saved_encryption_key(); |
| 2673 | 2694 | size_t savedKeySize = db_get_saved_encryption_key_size(); |
| 2674 | 2695 | if( zSavedKey!=0 && savedKeySize>0 ){ |
| 2675 | - @ %lu(GetCurrentProcessId()):%p(zSavedKey):%u(savedKeySize) | |
| 2696 | + @ %lu(GETPID()):%p(zSavedKey):%u(savedKeySize) | |
| 2676 | 2697 | return; |
| 2677 | 2698 | } |
| 2678 | 2699 | } |
| 2679 | 2700 | } |
| 2680 | 2701 | #endif |
| @@ -2770,11 +2791,11 @@ | ||
| 2770 | 2791 | ** --scgi Interpret input as SCGI rather than HTTP |
| 2771 | 2792 | ** --skin LABEL Use override skin LABEL. Use an empty string ("") |
| 2772 | 2793 | ** to force use of the current local skin config. |
| 2773 | 2794 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2774 | 2795 | ** --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. | |
| 2776 | 2797 | ** |
| 2777 | 2798 | ** See also: [[cgi]], [[server]], [[winsrv]] |
| 2778 | 2799 | */ |
| 2779 | 2800 | void cmd_http(void){ |
| 2780 | 2801 | const char *zIpAddr = 0; |
| @@ -3111,11 +3132,11 @@ | ||
| 3111 | 3132 | ** --repolist If REPOSITORY is dir, URL "/" lists repos |
| 3112 | 3133 | ** --scgi Accept SCGI rather than HTTP |
| 3113 | 3134 | ** --skin LABEL Use override skin LABEL |
| 3114 | 3135 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3115 | 3136 | ** --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. | |
| 3117 | 3138 | ** |
| 3118 | 3139 | ** See also: [[cgi]], [[http]], [[winsrv]] |
| 3119 | 3140 | */ |
| 3120 | 3141 | void cmd_webserver(void){ |
| 3121 | 3142 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| @@ -3140,10 +3161,14 @@ | ||
| 3140 | 3161 | int findServerArg = 2; /* argv index for find_server_repository() */ |
| 3141 | 3162 | char *zRemote = 0; /* Remote host on which to run "fossil ui" */ |
| 3142 | 3163 | const char *zJsMode; /* The --jsmode parameter */ |
| 3143 | 3164 | const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ |
| 3144 | 3165 | |
| 3166 | + | |
| 3167 | +#if USE_SEE | |
| 3168 | + db_setup_for_saved_encryption_key(); | |
| 3169 | +#endif | |
| 3145 | 3170 | |
| 3146 | 3171 | #if defined(_WIN32) |
| 3147 | 3172 | const char *zStopperFile; /* Name of file used to terminate server */ |
| 3148 | 3173 | zStopperFile = find_option("stopper", 0, 1); |
| 3149 | 3174 | #endif |
| 3150 | 3175 |
| --- 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 @@ | ||
| 143 | 143 | blob_init(&base, g.zRepositoryName, -1); |
| 144 | 144 | sqlite3_open(":memory:", &g.db); |
| 145 | 145 | db_multi_exec("CREATE TABLE sfile(pathname TEXT);"); |
| 146 | 146 | db_multi_exec("CREATE TABLE vfile(pathname);"); |
| 147 | 147 | 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 | + ); | |
| 149 | 153 | allRepo = 0; |
| 150 | 154 | } |
| 151 | 155 | n = db_int(0, "SELECT count(*) FROM sfile"); |
| 152 | 156 | if( n==0 ){ |
| 153 | 157 | sqlite3_close(g.db); |
| @@ -170,17 +174,22 @@ | ||
| 170 | 174 | " FROM sfile ORDER BY pathname COLLATE nocase;"); |
| 171 | 175 | rNow = db_double(0, "SELECT julianday('now')"); |
| 172 | 176 | while( db_step(&q)==SQLITE_ROW ){ |
| 173 | 177 | const char *zName = db_column_text(&q, 0); |
| 174 | 178 | int nName = (int)strlen(zName); |
| 179 | + int nSuffix = 7; /* ".fossil" */ | |
| 175 | 180 | char *zUrl; |
| 176 | 181 | char *zAge; |
| 177 | 182 | char *zFull; |
| 178 | 183 | RepoInfo x; |
| 179 | 184 | 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); | |
| 182 | 191 | if( zName[0]=='/' |
| 183 | 192 | #ifdef _WIN32 |
| 184 | 193 | || sqlite3_strglob("[a-zA-Z]:/*", zName)==0 |
| 185 | 194 | #endif |
| 186 | 195 | ){ |
| @@ -197,11 +206,15 @@ | ||
| 197 | 206 | zSkinRepo = mprintf("%s", x.zRepoName); |
| 198 | 207 | zSkinUrl = mprintf("%s", zUrl); |
| 199 | 208 | } |
| 200 | 209 | } |
| 201 | 210 | fossil_free(zFull); |
| 202 | - if( !x.isValid ){ | |
| 211 | + if( !x.isValid | |
| 212 | +#if USE_SEE | |
| 213 | + && !bEncrypted | |
| 214 | +#endif | |
| 215 | + ){ | |
| 203 | 216 | continue; |
| 204 | 217 | } |
| 205 | 218 | if( x.isRepolistSkin==2 && !allRepo ){ |
| 206 | 219 | /* Repositories with repolist-skin==2 are omitted from directory |
| 207 | 220 | ** scan lists, but included in "fossil all ui" lists */ |
| @@ -218,11 +231,11 @@ | ||
| 218 | 231 | /* This repository has no entry in the "event" table. |
| 219 | 232 | ** Its age will still be maximum, so data-sortkey will work. */ |
| 220 | 233 | zAge = mprintf("unknown"); |
| 221 | 234 | } |
| 222 | 235 | blob_append_sql(&html, "<tr><td valign='top'>"); |
| 223 | - if( sqlite3_strglob("*.fossil", zName)!=0 ){ | |
| 236 | + if( !file_ends_with_repository_extension(zName,0) ){ | |
| 224 | 237 | /* The "fossil server DIRECTORY" and "fossil ui DIRECTORY" commands |
| 225 | 238 | ** do not work for repositories whose names do not end in ".fossil". |
| 226 | 239 | ** So do not hyperlink those cases. */ |
| 227 | 240 | blob_append_sql(&html,"%h",zName); |
| 228 | 241 | } else if( sqlite3_strglob("*/.*", zName)==0 ){ |
| @@ -230,21 +243,28 @@ | ||
| 230 | 243 | blob_append_sql(&html, "%h (hidden)", zName); |
| 231 | 244 | } else if( allRepo && sqlite3_strglob("[a-zA-Z]:/?*", zName)!=0 ){ |
| 232 | 245 | blob_append_sql(&html, |
| 233 | 246 | "<a href='%R/%T/home' target='_blank'>/%h</a>\n", |
| 234 | 247 | zUrl, zName); |
| 235 | - }else if( sqlite3_strglob("*/*.fossil", zName)==0 ){ | |
| 248 | + }else if( file_ends_with_repository_extension(zName,1) ){ | |
| 236 | 249 | /* As described in |
| 237 | 250 | ** https://fossil-scm.org/forum/info/f50f647c97c72fc1: if |
| 238 | 251 | ** foo.fossil and foo/bar.fossil both exist and we create a |
| 239 | 252 | ** link to foo/bar/... then the URI dispatcher will instead |
| 240 | 253 | ** see that as a link to foo.fossil. In such cases, do not |
| 241 | 254 | ** emit a link to foo/bar.fossil. */ |
| 242 | 255 | char * zDirPart = file_dirname(zName); |
| 243 | 256 | 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 | + ) ){ | |
| 246 | 266 | blob_append_sql(&html, |
| 247 | 267 | "<s>%h</s> (directory/repo name collision)\n", |
| 248 | 268 | zName); |
| 249 | 269 | }else{ |
| 250 | 270 | blob_append_sql(&html, |
| 251 | 271 |
| --- 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 @@ | ||
| 287 | 287 | void fossil_key(const char **pzKey, int *pnKey){ |
| 288 | 288 | char *zSavedKey = db_get_saved_encryption_key(); |
| 289 | 289 | char *zKey; |
| 290 | 290 | size_t savedKeySize = db_get_saved_encryption_key_size(); |
| 291 | 291 | |
| 292 | - if( zSavedKey==0 || savedKeySize==0 ) return; | |
| 292 | + if( !db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ) return; | |
| 293 | 293 | zKey = (char*)malloc( savedKeySize ); |
| 294 | 294 | if( zKey ){ |
| 295 | 295 | memcpy(zKey, zSavedKey, savedKeySize); |
| 296 | 296 | *pzKey = zKey; |
| 297 | 297 | if( fossil_getenv("FOSSIL_USE_SEE_TEXTKEY")==0 ){ |
| 298 | 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( 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 @@ | ||
| 625 | 625 | blob_append_escaped_arg(&options, builtin_get_js_delivery_mode_name(), 0); |
| 626 | 626 | } |
| 627 | 627 | #if USE_SEE |
| 628 | 628 | zSavedKey = db_get_saved_encryption_key(); |
| 629 | 629 | savedKeySize = db_get_saved_encryption_key_size(); |
| 630 | - if( zSavedKey!=0 && savedKeySize>0 ){ | |
| 630 | + if( db_is_valid_saved_encryption_key(zSavedKey, savedKeySize) ){ | |
| 631 | 631 | blob_appendf(&options, " --usepidkey %lu:%p:%u", GetCurrentProcessId(), |
| 632 | 632 | zSavedKey, savedKeySize); |
| 633 | 633 | } |
| 634 | 634 | #endif |
| 635 | 635 | if( WSAStartup(MAKEWORD(2,0), &wd) ){ |
| 636 | 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( 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 |