Fossil SCM
Rename the "tls-config" command into "ssl-config" for consistency. The older "tls-config" command is retained as an alias. Enhance the command to support server certificate management.
Commit
f6051784c5eef7377e70eed4100584912f9a8f6db272505409749b0f913ed52c
Parent
7532ffa4e34e317…
2 files changed
+232
-17
+3
-1
+232
-17
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -697,13 +697,13 @@ | ||
| 697 | 697 | ** of disk files that hold the certificate and private-key for the |
| 698 | 698 | ** server. If zCertFile is not NULL but zKeyFile is NULL, then |
| 699 | 699 | ** zCertFile is assumed to be a concatenation of the certificate and |
| 700 | 700 | ** the private-key in the PEM format. |
| 701 | 701 | ** |
| 702 | -** If zCertFile is NULL, then "tls-server-cert" setting is consulted | |
| 702 | +** If zCertFile is NULL, then "ssl-cert" setting is consulted | |
| 703 | 703 | ** to get the certificate and private-key (concatenated together, in |
| 704 | -** the PEM format). If there is no tls-server-cert setting, then | |
| 704 | +** the PEM format). If there is no ssl-cert setting, then | |
| 705 | 705 | ** a built-in self-signed cert is used. |
| 706 | 706 | */ |
| 707 | 707 | void ssl_init_server(const char *zCertFile, const char *zKeyFile){ |
| 708 | 708 | if( sslIsInit==0 ){ |
| 709 | 709 | const char *zTlsCert; |
| @@ -724,16 +724,16 @@ | ||
| 724 | 724 | if( SSL_CTX_use_PrivateKey_file(sslCtx, zKeyFile, SSL_FILETYPE_PEM)<=0 ){ |
| 725 | 725 | ERR_print_errors_fp(stderr); |
| 726 | 726 | fossil_fatal("Error loading PRIVATE KEY from file \"%s\"", zKeyFile); |
| 727 | 727 | } |
| 728 | 728 | }else |
| 729 | - if( (zTlsCert = db_get("tls-server-cert",0))!=0 ){ | |
| 729 | + if( (zTlsCert = db_get("ssl-cert",0))!=0 ){ | |
| 730 | 730 | if( sslctx_use_cert_from_mem(sslCtx, zTlsCert, -1) |
| 731 | 731 | || sslctx_use_pkey_from_mem(sslCtx, zTlsCert, -1) |
| 732 | 732 | ){ |
| 733 | 733 | fossil_fatal("Error loading the CERT from the" |
| 734 | - " 'tls-server-cert' setting"); | |
| 734 | + " 'ssl-cert' setting"); | |
| 735 | 735 | } |
| 736 | 736 | }else if( sslctx_use_cert_from_mem(sslCtx, sslSelfCert, -1) |
| 737 | 737 | || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1) ){ |
| 738 | 738 | fossil_fatal("Error loading self-signed CERT"); |
| 739 | 739 | } |
| @@ -846,25 +846,41 @@ | ||
| 846 | 846 | |
| 847 | 847 | #endif /* FOSSIL_ENABLE_SSL */ |
| 848 | 848 | |
| 849 | 849 | /* |
| 850 | 850 | ** COMMAND: tls-config* |
| 851 | +** COMMAND: ssl-config | |
| 851 | 852 | ** |
| 852 | -** Usage: %fossil tls-config [SUBCOMMAND] [OPTIONS...] [ARGS...] | |
| 853 | +** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] | |
| 853 | 854 | ** |
| 854 | 855 | ** This command is used to view or modify the TLS (Transport Layer |
| 855 | 856 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 856 | 857 | ** encryption technology used for secure HTTPS transport. |
| 857 | 858 | ** |
| 858 | 859 | ** Sub-commands: |
| 859 | 860 | ** |
| 860 | -** show Show the TLS configuration | |
| 861 | +** clear-cert Remove information about server certificates. | |
| 862 | +** This is a subset of the "scrub" command. | |
| 863 | +** | |
| 864 | +** load-cert PEM-FILES... Identify server certificate files. These | |
| 865 | +** should be in the PEM format. There are | |
| 866 | +** normally two files, the certificate and the | |
| 867 | +** private-key. By default, the text of both | |
| 868 | +** files is concatenated and added to the | |
| 869 | +** "ssl-cert" setting. Use --filename to store | |
| 870 | +** just the filenames. | |
| 871 | +** | |
| 872 | +** remove-exception DOMAINS Remove TLS cert exceptions for the domains | |
| 873 | +** listed. Or remove them all if the --all | |
| 874 | +** option is specified. | |
| 875 | +** | |
| 876 | +** scrub ?--force? Remove all SSL configuration data from the | |
| 877 | +** repository. Use --force to omit the | |
| 878 | +** confirmation. | |
| 861 | 879 | ** |
| 862 | -** remove-exception DOMAIN... Remove TLS cert exceptions | |
| 863 | -** for the domains listed. Or if | |
| 864 | -** the --all option is specified, | |
| 865 | -** remove all TLS cert exceptions. | |
| 880 | +** show ?-v? Show the TLS configuration. Add -v to see | |
| 881 | +** additional explaination | |
| 866 | 882 | */ |
| 867 | 883 | void test_tlsconfig_info(void){ |
| 868 | 884 | #if !defined(FOSSIL_ENABLE_SSL) |
| 869 | 885 | fossil_print("TLS disabled in this build\n"); |
| 870 | 886 | #else |
| @@ -871,20 +887,159 @@ | ||
| 871 | 887 | const char *zCmd; |
| 872 | 888 | size_t nCmd; |
| 873 | 889 | int nHit = 0; |
| 874 | 890 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 875 | 891 | db_open_config(1,0); |
| 876 | - zCmd = g.argc>=3 ? g.argv[2] : "show"; | |
| 877 | - nCmd = strlen(zCmd); | |
| 892 | + if( g.argc==2 || (g.argc>=3 && g.argv[2][0]=='-') ){ | |
| 893 | + zCmd = "show"; | |
| 894 | + nCmd = 4; | |
| 895 | + }else{ | |
| 896 | + zCmd = g.argv[2]; | |
| 897 | + nCmd = strlen(zCmd); | |
| 898 | + } | |
| 899 | + if( strncmp("clear-cert",zCmd,nCmd)==0 && nCmd>=4 ){ | |
| 900 | + int bForce = find_option("force","f",0)!=0; | |
| 901 | + verify_all_options(); | |
| 902 | + if( !bForce ){ | |
| 903 | + Blob ans; | |
| 904 | + char cReply; | |
| 905 | + prompt_user( | |
| 906 | + "Confirm removing of the SSL server certificate from this repository.\n" | |
| 907 | + "The removal cannot be undone. Continue (y/N)? ", &ans); | |
| 908 | + cReply = blob_str(&ans)[0]; | |
| 909 | + if( cReply!='y' && cReply!='Y' ){ | |
| 910 | + fossil_exit(1); | |
| 911 | + } | |
| 912 | + } | |
| 913 | + db_unprotect(PROTECT_ALL); | |
| 914 | + db_multi_exec( | |
| 915 | + "PRAGMA secure_delete=ON;" | |
| 916 | + "DELETE FROM config " | |
| 917 | + " WHERE name IN ('ssl-cert','ssl-cert-file','ssl-cert-key');" | |
| 918 | + ); | |
| 919 | + db_protect_pop(); | |
| 920 | + }else | |
| 921 | + if( strncmp("load-cert",zCmd,nCmd)==0 && nCmd>=4 ){ | |
| 922 | + int bFN = find_option("filename",0,0)!=0; | |
| 923 | + int i; | |
| 924 | + Blob allText = BLOB_INITIALIZER; | |
| 925 | + int haveCert = 0; | |
| 926 | + int haveKey = 0; | |
| 927 | + verify_all_options(); | |
| 928 | + db_begin_transaction(); | |
| 929 | + db_unprotect(PROTECT_ALL); | |
| 930 | + db_multi_exec( | |
| 931 | + "PRAGMA secure_delete=ON;" | |
| 932 | + "DELETE FROM config " | |
| 933 | + " WHERE name IN ('ssl-cert','ssl-cert-file','ssl-cert-key');" | |
| 934 | + ); | |
| 935 | + nHit = 0; | |
| 936 | + for(i=3; i<g.argc; i++){ | |
| 937 | + Blob x; | |
| 938 | + int isCert; | |
| 939 | + int isKey; | |
| 940 | + if( !file_isfile(g.argv[i], ExtFILE) ){ | |
| 941 | + fossil_fatal("no such file: \"%s\"", g.argv[i]); | |
| 942 | + } | |
| 943 | + blob_read_from_file(&x, g.argv[i], ExtFILE); | |
| 944 | + isCert = strstr(blob_str(&x),"-----BEGIN CERTIFICATE-----")!=0; | |
| 945 | + isKey = strstr(blob_str(&x),"-----BEGIN PRIVATE KEY-----")!=0; | |
| 946 | + if( !isCert && !isKey ){ | |
| 947 | + fossil_fatal("not a certificate or a private key: \"%s\"", g.argv[i]); | |
| 948 | + } | |
| 949 | + if( isCert ){ | |
| 950 | + if( haveCert ){ | |
| 951 | + fossil_fatal("more than one certificate provided"); | |
| 952 | + } | |
| 953 | + haveCert = 1; | |
| 954 | + if( bFN ){ | |
| 955 | + db_set("ssl-cert-file", file_canonical_name_dup(g.argv[i]), 0); | |
| 956 | + }else{ | |
| 957 | + blob_append(&allText, blob_buffer(&x), blob_size(&x)); | |
| 958 | + } | |
| 959 | + if( isKey && !haveKey ){ | |
| 960 | + haveKey = 1; | |
| 961 | + isKey = 0; | |
| 962 | + } | |
| 963 | + } | |
| 964 | + if( isKey ){ | |
| 965 | + if( haveKey ){ | |
| 966 | + fossil_fatal("more than one private key provided"); | |
| 967 | + } | |
| 968 | + haveKey = 1; | |
| 969 | + if( bFN ){ | |
| 970 | + db_set("ssl-key-file", file_canonical_name_dup(g.argv[i]), 0); | |
| 971 | + }else{ | |
| 972 | + blob_append(&allText, blob_buffer(&x), blob_size(&x)); | |
| 973 | + } | |
| 974 | + } | |
| 975 | + } | |
| 976 | + db_protect_pop(); | |
| 977 | + if( !haveCert ){ | |
| 978 | + if( !haveKey ){ | |
| 979 | + fossil_fatal("missing certificate and private-key"); | |
| 980 | + }else{ | |
| 981 | + fossil_fatal("missing certificate"); | |
| 982 | + } | |
| 983 | + }else if( !haveKey ){ | |
| 984 | + fossil_fatal("missing private-key"); | |
| 985 | + } | |
| 986 | + if( !bFN ){ | |
| 987 | + db_set("ssl-cert", blob_str(&allText), 0); | |
| 988 | + } | |
| 989 | + db_commit_transaction(); | |
| 990 | + }else | |
| 991 | + if( strncmp("scrub",zCmd,nCmd)==0 && nCmd>4 ){ | |
| 992 | + int bForce = find_option("force","f",0)!=0; | |
| 993 | + verify_all_options(); | |
| 994 | + if( !bForce ){ | |
| 995 | + Blob ans; | |
| 996 | + char cReply; | |
| 997 | + prompt_user( | |
| 998 | + "Scrubbing the SSL configuration will permanently delete information.\n" | |
| 999 | + "Changes cannot be undone. Continue (y/N)? ", &ans); | |
| 1000 | + cReply = blob_str(&ans)[0]; | |
| 1001 | + if( cReply!='y' && cReply!='Y' ){ | |
| 1002 | + fossil_exit(1); | |
| 1003 | + } | |
| 1004 | + } | |
| 1005 | + db_unprotect(PROTECT_ALL); | |
| 1006 | + db_multi_exec( | |
| 1007 | + "PRAGMA secure_delete=ON;" | |
| 1008 | + "DELETE FROM config WHERE name GLOB 'ssl-*';" | |
| 1009 | + ); | |
| 1010 | + db_protect_pop(); | |
| 1011 | + }else | |
| 878 | 1012 | if( strncmp("show",zCmd,nCmd)==0 ){ |
| 879 | 1013 | const char *zName, *zValue; |
| 880 | 1014 | size_t nName; |
| 881 | 1015 | Stmt q; |
| 1016 | + int verbose = find_option("verbose","v",0)!=0; | |
| 1017 | + verify_all_options(); | |
| 1018 | + | |
| 882 | 1019 | fossil_print("OpenSSL-version: %s (0x%09x)\n", |
| 883 | 1020 | SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER); |
| 1021 | + if( verbose ){ | |
| 1022 | + fossil_print("\n" | |
| 1023 | + " The version of the OpenSSL library being used\n" | |
| 1024 | + " by this instance of Fossil. Version 3.0.0 or\n" | |
| 1025 | + " later is recommended.\n\n" | |
| 1026 | + ); | |
| 1027 | + } | |
| 1028 | + | |
| 884 | 1029 | fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file()); |
| 885 | 1030 | fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); |
| 1031 | + if( verbose ){ | |
| 1032 | + fossil_print("\n" | |
| 1033 | + " The default locations for the set of root certificates\n" | |
| 1034 | + " used by the \"fossil sync\" and similar commands to verify\n" | |
| 1035 | + " the identity of servers for \"https:\" URLs. These values\n" | |
| 1036 | + " come into play when Fossil is used as a TLS client. These\n" | |
| 1037 | + " values are built into your OpenSSL library.\n\n" | |
| 1038 | + ); | |
| 1039 | + } | |
| 1040 | + | |
| 886 | 1041 | zName = X509_get_default_cert_file_env(); |
| 887 | 1042 | zValue = fossil_getenv(zName); |
| 888 | 1043 | if( zValue==0 ) zValue = ""; |
| 889 | 1044 | nName = strlen(zName); |
| 890 | 1045 | fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue); |
| @@ -891,25 +1046,85 @@ | ||
| 891 | 1046 | zName = X509_get_default_cert_dir_env(); |
| 892 | 1047 | zValue = fossil_getenv(zName); |
| 893 | 1048 | if( zValue==0 ) zValue = ""; |
| 894 | 1049 | nName = strlen(zName); |
| 895 | 1050 | fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue); |
| 896 | - nHit++; | |
| 1051 | + if( verbose ){ | |
| 1052 | + fossil_print("\n" | |
| 1053 | + " Alternative locations for the root certificates used by Fossil\n" | |
| 1054 | + " when it is acting as a SSL client in order to verify the identity\n" | |
| 1055 | + " of servers. If specified, these alternative locations override\n" | |
| 1056 | + " the built-in locations.\n\n" | |
| 1057 | + ); | |
| 1058 | + } | |
| 1059 | + | |
| 897 | 1060 | fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location","")); |
| 1061 | + if( verbose ){ | |
| 1062 | + fossil_print("\n" | |
| 1063 | + " This setting is the name of a file or directory that contains\n" | |
| 1064 | + " the complete set of root certificates to used by Fossil when it\n" | |
| 1065 | + " is acting as a SSL client. If defined, this setting takes\n" | |
| 1066 | + " priority over built-in paths and environment variables\n\n" | |
| 1067 | + ); | |
| 1068 | + } | |
| 1069 | + | |
| 898 | 1070 | fossil_print("ssl-identity: %s\n", db_get("ssl-identity","")); |
| 1071 | + if( verbose ){ | |
| 1072 | + fossil_print("\n" | |
| 1073 | + " This setting is the name of a file that contains the PEM-format\n" | |
| 1074 | + " certificate and private-key used by Fossil clients to authentice\n" | |
| 1075 | + " with servers. Few servers actually require this, so this setting\n" | |
| 1076 | + " is usually blank.\n\n" | |
| 1077 | + ); | |
| 1078 | + } | |
| 1079 | + | |
| 1080 | + zValue = db_get("ssl-cert",0); | |
| 1081 | + if( zValue ){ | |
| 1082 | + fossil_print("ssl-cert: (%d-byte PEM)\n", (int)strlen(zValue)); | |
| 1083 | + }else{ | |
| 1084 | + fossil_print("ssl-cert:\n"); | |
| 1085 | + } | |
| 1086 | + if( verbose ){ | |
| 1087 | + fossil_print("\n" | |
| 1088 | + " This setting is the PEM-formatted value of the SSL server\n" | |
| 1089 | + " certificate and private-key, used by Fossil when it is acting\n" | |
| 1090 | + " as a server via the \"fossil server\" command or similar.\n\n" | |
| 1091 | + ); | |
| 1092 | + } | |
| 1093 | + | |
| 1094 | + fossil_print("ssl-cert-file: %s\n", db_get("ssl-cert-file","")); | |
| 1095 | + fossil_print("ssl-key-file: %s\n", db_get("ssl-key-file","")); | |
| 1096 | + if( verbose ){ | |
| 1097 | + fossil_print("\n" | |
| 1098 | + " This settings are the names of files that contin the certificate\n" | |
| 1099 | + " private-key used by Fossil when it is acting as a server.\n\n" | |
| 1100 | + ); | |
| 1101 | + } | |
| 1102 | + | |
| 899 | 1103 | db_prepare(&q, |
| 900 | - "SELECT name FROM global_config" | |
| 1104 | + "SELECT name, '' FROM global_config" | |
| 901 | 1105 | " WHERE name GLOB 'cert:*'" |
| 902 | 1106 | "UNION ALL " |
| 903 | - "SELECT name FROM config" | |
| 1107 | + "SELECT name, date(mtime,'unixepoch') FROM config" | |
| 904 | 1108 | " WHERE name GLOB 'cert:*'" |
| 905 | 1109 | " ORDER BY name" |
| 906 | 1110 | ); |
| 1111 | + nHit = 0; | |
| 907 | 1112 | while( db_step(&q)==SQLITE_ROW ){ |
| 908 | - fossil_print("exception: %s\n", db_column_text(&q,0)+5); | |
| 1113 | + fossil_print("exception: %-40s %s\n", | |
| 1114 | + db_column_text(&q,0)+5, db_column_text(&q,1)); | |
| 1115 | + nHit++; | |
| 909 | 1116 | } |
| 910 | 1117 | db_finalize(&q); |
| 1118 | + if( nHit && verbose ){ | |
| 1119 | + fossil_print("\n" | |
| 1120 | + " The exceptions are server certificates that the Fossil client\n" | |
| 1121 | + " is unable to verify using root certificates, but which should be\n" | |
| 1122 | + " accepted anyhow.\n\n" | |
| 1123 | + ); | |
| 1124 | + } | |
| 1125 | + | |
| 911 | 1126 | }else |
| 912 | 1127 | if( strncmp("remove-exception",zCmd,nCmd)==0 ){ |
| 913 | 1128 | int i; |
| 914 | 1129 | Blob sql; |
| 915 | 1130 | char *zSep = "("; |
| @@ -948,10 +1163,10 @@ | ||
| 948 | 1163 | db_commit_transaction(); |
| 949 | 1164 | blob_reset(&sql); |
| 950 | 1165 | }else |
| 951 | 1166 | /*default*/{ |
| 952 | 1167 | fossil_fatal("unknown sub-command \"%s\".\nshould be one of:" |
| 953 | - " remove-exception show", | |
| 1168 | + " load-certs remove-exception scrub show", | |
| 954 | 1169 | zCmd); |
| 955 | 1170 | } |
| 956 | 1171 | #endif |
| 957 | 1172 | } |
| 958 | 1173 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -697,13 +697,13 @@ | |
| 697 | ** of disk files that hold the certificate and private-key for the |
| 698 | ** server. If zCertFile is not NULL but zKeyFile is NULL, then |
| 699 | ** zCertFile is assumed to be a concatenation of the certificate and |
| 700 | ** the private-key in the PEM format. |
| 701 | ** |
| 702 | ** If zCertFile is NULL, then "tls-server-cert" setting is consulted |
| 703 | ** to get the certificate and private-key (concatenated together, in |
| 704 | ** the PEM format). If there is no tls-server-cert setting, then |
| 705 | ** a built-in self-signed cert is used. |
| 706 | */ |
| 707 | void ssl_init_server(const char *zCertFile, const char *zKeyFile){ |
| 708 | if( sslIsInit==0 ){ |
| 709 | const char *zTlsCert; |
| @@ -724,16 +724,16 @@ | |
| 724 | if( SSL_CTX_use_PrivateKey_file(sslCtx, zKeyFile, SSL_FILETYPE_PEM)<=0 ){ |
| 725 | ERR_print_errors_fp(stderr); |
| 726 | fossil_fatal("Error loading PRIVATE KEY from file \"%s\"", zKeyFile); |
| 727 | } |
| 728 | }else |
| 729 | if( (zTlsCert = db_get("tls-server-cert",0))!=0 ){ |
| 730 | if( sslctx_use_cert_from_mem(sslCtx, zTlsCert, -1) |
| 731 | || sslctx_use_pkey_from_mem(sslCtx, zTlsCert, -1) |
| 732 | ){ |
| 733 | fossil_fatal("Error loading the CERT from the" |
| 734 | " 'tls-server-cert' setting"); |
| 735 | } |
| 736 | }else if( sslctx_use_cert_from_mem(sslCtx, sslSelfCert, -1) |
| 737 | || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1) ){ |
| 738 | fossil_fatal("Error loading self-signed CERT"); |
| 739 | } |
| @@ -846,25 +846,41 @@ | |
| 846 | |
| 847 | #endif /* FOSSIL_ENABLE_SSL */ |
| 848 | |
| 849 | /* |
| 850 | ** COMMAND: tls-config* |
| 851 | ** |
| 852 | ** Usage: %fossil tls-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 853 | ** |
| 854 | ** This command is used to view or modify the TLS (Transport Layer |
| 855 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 856 | ** encryption technology used for secure HTTPS transport. |
| 857 | ** |
| 858 | ** Sub-commands: |
| 859 | ** |
| 860 | ** show Show the TLS configuration |
| 861 | ** |
| 862 | ** remove-exception DOMAIN... Remove TLS cert exceptions |
| 863 | ** for the domains listed. Or if |
| 864 | ** the --all option is specified, |
| 865 | ** remove all TLS cert exceptions. |
| 866 | */ |
| 867 | void test_tlsconfig_info(void){ |
| 868 | #if !defined(FOSSIL_ENABLE_SSL) |
| 869 | fossil_print("TLS disabled in this build\n"); |
| 870 | #else |
| @@ -871,20 +887,159 @@ | |
| 871 | const char *zCmd; |
| 872 | size_t nCmd; |
| 873 | int nHit = 0; |
| 874 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 875 | db_open_config(1,0); |
| 876 | zCmd = g.argc>=3 ? g.argv[2] : "show"; |
| 877 | nCmd = strlen(zCmd); |
| 878 | if( strncmp("show",zCmd,nCmd)==0 ){ |
| 879 | const char *zName, *zValue; |
| 880 | size_t nName; |
| 881 | Stmt q; |
| 882 | fossil_print("OpenSSL-version: %s (0x%09x)\n", |
| 883 | SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER); |
| 884 | fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file()); |
| 885 | fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); |
| 886 | zName = X509_get_default_cert_file_env(); |
| 887 | zValue = fossil_getenv(zName); |
| 888 | if( zValue==0 ) zValue = ""; |
| 889 | nName = strlen(zName); |
| 890 | fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue); |
| @@ -891,25 +1046,85 @@ | |
| 891 | zName = X509_get_default_cert_dir_env(); |
| 892 | zValue = fossil_getenv(zName); |
| 893 | if( zValue==0 ) zValue = ""; |
| 894 | nName = strlen(zName); |
| 895 | fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue); |
| 896 | nHit++; |
| 897 | fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location","")); |
| 898 | fossil_print("ssl-identity: %s\n", db_get("ssl-identity","")); |
| 899 | db_prepare(&q, |
| 900 | "SELECT name FROM global_config" |
| 901 | " WHERE name GLOB 'cert:*'" |
| 902 | "UNION ALL " |
| 903 | "SELECT name FROM config" |
| 904 | " WHERE name GLOB 'cert:*'" |
| 905 | " ORDER BY name" |
| 906 | ); |
| 907 | while( db_step(&q)==SQLITE_ROW ){ |
| 908 | fossil_print("exception: %s\n", db_column_text(&q,0)+5); |
| 909 | } |
| 910 | db_finalize(&q); |
| 911 | }else |
| 912 | if( strncmp("remove-exception",zCmd,nCmd)==0 ){ |
| 913 | int i; |
| 914 | Blob sql; |
| 915 | char *zSep = "("; |
| @@ -948,10 +1163,10 @@ | |
| 948 | db_commit_transaction(); |
| 949 | blob_reset(&sql); |
| 950 | }else |
| 951 | /*default*/{ |
| 952 | fossil_fatal("unknown sub-command \"%s\".\nshould be one of:" |
| 953 | " remove-exception show", |
| 954 | zCmd); |
| 955 | } |
| 956 | #endif |
| 957 | } |
| 958 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -697,13 +697,13 @@ | |
| 697 | ** of disk files that hold the certificate and private-key for the |
| 698 | ** server. If zCertFile is not NULL but zKeyFile is NULL, then |
| 699 | ** zCertFile is assumed to be a concatenation of the certificate and |
| 700 | ** the private-key in the PEM format. |
| 701 | ** |
| 702 | ** If zCertFile is NULL, then "ssl-cert" setting is consulted |
| 703 | ** to get the certificate and private-key (concatenated together, in |
| 704 | ** the PEM format). If there is no ssl-cert setting, then |
| 705 | ** a built-in self-signed cert is used. |
| 706 | */ |
| 707 | void ssl_init_server(const char *zCertFile, const char *zKeyFile){ |
| 708 | if( sslIsInit==0 ){ |
| 709 | const char *zTlsCert; |
| @@ -724,16 +724,16 @@ | |
| 724 | if( SSL_CTX_use_PrivateKey_file(sslCtx, zKeyFile, SSL_FILETYPE_PEM)<=0 ){ |
| 725 | ERR_print_errors_fp(stderr); |
| 726 | fossil_fatal("Error loading PRIVATE KEY from file \"%s\"", zKeyFile); |
| 727 | } |
| 728 | }else |
| 729 | if( (zTlsCert = db_get("ssl-cert",0))!=0 ){ |
| 730 | if( sslctx_use_cert_from_mem(sslCtx, zTlsCert, -1) |
| 731 | || sslctx_use_pkey_from_mem(sslCtx, zTlsCert, -1) |
| 732 | ){ |
| 733 | fossil_fatal("Error loading the CERT from the" |
| 734 | " 'ssl-cert' setting"); |
| 735 | } |
| 736 | }else if( sslctx_use_cert_from_mem(sslCtx, sslSelfCert, -1) |
| 737 | || sslctx_use_pkey_from_mem(sslCtx, sslSelfPKey, -1) ){ |
| 738 | fossil_fatal("Error loading self-signed CERT"); |
| 739 | } |
| @@ -846,25 +846,41 @@ | |
| 846 | |
| 847 | #endif /* FOSSIL_ENABLE_SSL */ |
| 848 | |
| 849 | /* |
| 850 | ** COMMAND: tls-config* |
| 851 | ** COMMAND: ssl-config |
| 852 | ** |
| 853 | ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 854 | ** |
| 855 | ** This command is used to view or modify the TLS (Transport Layer |
| 856 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 857 | ** encryption technology used for secure HTTPS transport. |
| 858 | ** |
| 859 | ** Sub-commands: |
| 860 | ** |
| 861 | ** clear-cert Remove information about server certificates. |
| 862 | ** This is a subset of the "scrub" command. |
| 863 | ** |
| 864 | ** load-cert PEM-FILES... Identify server certificate files. These |
| 865 | ** should be in the PEM format. There are |
| 866 | ** normally two files, the certificate and the |
| 867 | ** private-key. By default, the text of both |
| 868 | ** files is concatenated and added to the |
| 869 | ** "ssl-cert" setting. Use --filename to store |
| 870 | ** just the filenames. |
| 871 | ** |
| 872 | ** remove-exception DOMAINS Remove TLS cert exceptions for the domains |
| 873 | ** listed. Or remove them all if the --all |
| 874 | ** option is specified. |
| 875 | ** |
| 876 | ** scrub ?--force? Remove all SSL configuration data from the |
| 877 | ** repository. Use --force to omit the |
| 878 | ** confirmation. |
| 879 | ** |
| 880 | ** show ?-v? Show the TLS configuration. Add -v to see |
| 881 | ** additional explaination |
| 882 | */ |
| 883 | void test_tlsconfig_info(void){ |
| 884 | #if !defined(FOSSIL_ENABLE_SSL) |
| 885 | fossil_print("TLS disabled in this build\n"); |
| 886 | #else |
| @@ -871,20 +887,159 @@ | |
| 887 | const char *zCmd; |
| 888 | size_t nCmd; |
| 889 | int nHit = 0; |
| 890 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 891 | db_open_config(1,0); |
| 892 | if( g.argc==2 || (g.argc>=3 && g.argv[2][0]=='-') ){ |
| 893 | zCmd = "show"; |
| 894 | nCmd = 4; |
| 895 | }else{ |
| 896 | zCmd = g.argv[2]; |
| 897 | nCmd = strlen(zCmd); |
| 898 | } |
| 899 | if( strncmp("clear-cert",zCmd,nCmd)==0 && nCmd>=4 ){ |
| 900 | int bForce = find_option("force","f",0)!=0; |
| 901 | verify_all_options(); |
| 902 | if( !bForce ){ |
| 903 | Blob ans; |
| 904 | char cReply; |
| 905 | prompt_user( |
| 906 | "Confirm removing of the SSL server certificate from this repository.\n" |
| 907 | "The removal cannot be undone. Continue (y/N)? ", &ans); |
| 908 | cReply = blob_str(&ans)[0]; |
| 909 | if( cReply!='y' && cReply!='Y' ){ |
| 910 | fossil_exit(1); |
| 911 | } |
| 912 | } |
| 913 | db_unprotect(PROTECT_ALL); |
| 914 | db_multi_exec( |
| 915 | "PRAGMA secure_delete=ON;" |
| 916 | "DELETE FROM config " |
| 917 | " WHERE name IN ('ssl-cert','ssl-cert-file','ssl-cert-key');" |
| 918 | ); |
| 919 | db_protect_pop(); |
| 920 | }else |
| 921 | if( strncmp("load-cert",zCmd,nCmd)==0 && nCmd>=4 ){ |
| 922 | int bFN = find_option("filename",0,0)!=0; |
| 923 | int i; |
| 924 | Blob allText = BLOB_INITIALIZER; |
| 925 | int haveCert = 0; |
| 926 | int haveKey = 0; |
| 927 | verify_all_options(); |
| 928 | db_begin_transaction(); |
| 929 | db_unprotect(PROTECT_ALL); |
| 930 | db_multi_exec( |
| 931 | "PRAGMA secure_delete=ON;" |
| 932 | "DELETE FROM config " |
| 933 | " WHERE name IN ('ssl-cert','ssl-cert-file','ssl-cert-key');" |
| 934 | ); |
| 935 | nHit = 0; |
| 936 | for(i=3; i<g.argc; i++){ |
| 937 | Blob x; |
| 938 | int isCert; |
| 939 | int isKey; |
| 940 | if( !file_isfile(g.argv[i], ExtFILE) ){ |
| 941 | fossil_fatal("no such file: \"%s\"", g.argv[i]); |
| 942 | } |
| 943 | blob_read_from_file(&x, g.argv[i], ExtFILE); |
| 944 | isCert = strstr(blob_str(&x),"-----BEGIN CERTIFICATE-----")!=0; |
| 945 | isKey = strstr(blob_str(&x),"-----BEGIN PRIVATE KEY-----")!=0; |
| 946 | if( !isCert && !isKey ){ |
| 947 | fossil_fatal("not a certificate or a private key: \"%s\"", g.argv[i]); |
| 948 | } |
| 949 | if( isCert ){ |
| 950 | if( haveCert ){ |
| 951 | fossil_fatal("more than one certificate provided"); |
| 952 | } |
| 953 | haveCert = 1; |
| 954 | if( bFN ){ |
| 955 | db_set("ssl-cert-file", file_canonical_name_dup(g.argv[i]), 0); |
| 956 | }else{ |
| 957 | blob_append(&allText, blob_buffer(&x), blob_size(&x)); |
| 958 | } |
| 959 | if( isKey && !haveKey ){ |
| 960 | haveKey = 1; |
| 961 | isKey = 0; |
| 962 | } |
| 963 | } |
| 964 | if( isKey ){ |
| 965 | if( haveKey ){ |
| 966 | fossil_fatal("more than one private key provided"); |
| 967 | } |
| 968 | haveKey = 1; |
| 969 | if( bFN ){ |
| 970 | db_set("ssl-key-file", file_canonical_name_dup(g.argv[i]), 0); |
| 971 | }else{ |
| 972 | blob_append(&allText, blob_buffer(&x), blob_size(&x)); |
| 973 | } |
| 974 | } |
| 975 | } |
| 976 | db_protect_pop(); |
| 977 | if( !haveCert ){ |
| 978 | if( !haveKey ){ |
| 979 | fossil_fatal("missing certificate and private-key"); |
| 980 | }else{ |
| 981 | fossil_fatal("missing certificate"); |
| 982 | } |
| 983 | }else if( !haveKey ){ |
| 984 | fossil_fatal("missing private-key"); |
| 985 | } |
| 986 | if( !bFN ){ |
| 987 | db_set("ssl-cert", blob_str(&allText), 0); |
| 988 | } |
| 989 | db_commit_transaction(); |
| 990 | }else |
| 991 | if( strncmp("scrub",zCmd,nCmd)==0 && nCmd>4 ){ |
| 992 | int bForce = find_option("force","f",0)!=0; |
| 993 | verify_all_options(); |
| 994 | if( !bForce ){ |
| 995 | Blob ans; |
| 996 | char cReply; |
| 997 | prompt_user( |
| 998 | "Scrubbing the SSL configuration will permanently delete information.\n" |
| 999 | "Changes cannot be undone. Continue (y/N)? ", &ans); |
| 1000 | cReply = blob_str(&ans)[0]; |
| 1001 | if( cReply!='y' && cReply!='Y' ){ |
| 1002 | fossil_exit(1); |
| 1003 | } |
| 1004 | } |
| 1005 | db_unprotect(PROTECT_ALL); |
| 1006 | db_multi_exec( |
| 1007 | "PRAGMA secure_delete=ON;" |
| 1008 | "DELETE FROM config WHERE name GLOB 'ssl-*';" |
| 1009 | ); |
| 1010 | db_protect_pop(); |
| 1011 | }else |
| 1012 | if( strncmp("show",zCmd,nCmd)==0 ){ |
| 1013 | const char *zName, *zValue; |
| 1014 | size_t nName; |
| 1015 | Stmt q; |
| 1016 | int verbose = find_option("verbose","v",0)!=0; |
| 1017 | verify_all_options(); |
| 1018 | |
| 1019 | fossil_print("OpenSSL-version: %s (0x%09x)\n", |
| 1020 | SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER); |
| 1021 | if( verbose ){ |
| 1022 | fossil_print("\n" |
| 1023 | " The version of the OpenSSL library being used\n" |
| 1024 | " by this instance of Fossil. Version 3.0.0 or\n" |
| 1025 | " later is recommended.\n\n" |
| 1026 | ); |
| 1027 | } |
| 1028 | |
| 1029 | fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file()); |
| 1030 | fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir()); |
| 1031 | if( verbose ){ |
| 1032 | fossil_print("\n" |
| 1033 | " The default locations for the set of root certificates\n" |
| 1034 | " used by the \"fossil sync\" and similar commands to verify\n" |
| 1035 | " the identity of servers for \"https:\" URLs. These values\n" |
| 1036 | " come into play when Fossil is used as a TLS client. These\n" |
| 1037 | " values are built into your OpenSSL library.\n\n" |
| 1038 | ); |
| 1039 | } |
| 1040 | |
| 1041 | zName = X509_get_default_cert_file_env(); |
| 1042 | zValue = fossil_getenv(zName); |
| 1043 | if( zValue==0 ) zValue = ""; |
| 1044 | nName = strlen(zName); |
| 1045 | fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue); |
| @@ -891,25 +1046,85 @@ | |
| 1046 | zName = X509_get_default_cert_dir_env(); |
| 1047 | zValue = fossil_getenv(zName); |
| 1048 | if( zValue==0 ) zValue = ""; |
| 1049 | nName = strlen(zName); |
| 1050 | fossil_print("%s:%*s%s\n", zName, 18-nName, "", zValue); |
| 1051 | if( verbose ){ |
| 1052 | fossil_print("\n" |
| 1053 | " Alternative locations for the root certificates used by Fossil\n" |
| 1054 | " when it is acting as a SSL client in order to verify the identity\n" |
| 1055 | " of servers. If specified, these alternative locations override\n" |
| 1056 | " the built-in locations.\n\n" |
| 1057 | ); |
| 1058 | } |
| 1059 | |
| 1060 | fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location","")); |
| 1061 | if( verbose ){ |
| 1062 | fossil_print("\n" |
| 1063 | " This setting is the name of a file or directory that contains\n" |
| 1064 | " the complete set of root certificates to used by Fossil when it\n" |
| 1065 | " is acting as a SSL client. If defined, this setting takes\n" |
| 1066 | " priority over built-in paths and environment variables\n\n" |
| 1067 | ); |
| 1068 | } |
| 1069 | |
| 1070 | fossil_print("ssl-identity: %s\n", db_get("ssl-identity","")); |
| 1071 | if( verbose ){ |
| 1072 | fossil_print("\n" |
| 1073 | " This setting is the name of a file that contains the PEM-format\n" |
| 1074 | " certificate and private-key used by Fossil clients to authentice\n" |
| 1075 | " with servers. Few servers actually require this, so this setting\n" |
| 1076 | " is usually blank.\n\n" |
| 1077 | ); |
| 1078 | } |
| 1079 | |
| 1080 | zValue = db_get("ssl-cert",0); |
| 1081 | if( zValue ){ |
| 1082 | fossil_print("ssl-cert: (%d-byte PEM)\n", (int)strlen(zValue)); |
| 1083 | }else{ |
| 1084 | fossil_print("ssl-cert:\n"); |
| 1085 | } |
| 1086 | if( verbose ){ |
| 1087 | fossil_print("\n" |
| 1088 | " This setting is the PEM-formatted value of the SSL server\n" |
| 1089 | " certificate and private-key, used by Fossil when it is acting\n" |
| 1090 | " as a server via the \"fossil server\" command or similar.\n\n" |
| 1091 | ); |
| 1092 | } |
| 1093 | |
| 1094 | fossil_print("ssl-cert-file: %s\n", db_get("ssl-cert-file","")); |
| 1095 | fossil_print("ssl-key-file: %s\n", db_get("ssl-key-file","")); |
| 1096 | if( verbose ){ |
| 1097 | fossil_print("\n" |
| 1098 | " This settings are the names of files that contin the certificate\n" |
| 1099 | " private-key used by Fossil when it is acting as a server.\n\n" |
| 1100 | ); |
| 1101 | } |
| 1102 | |
| 1103 | db_prepare(&q, |
| 1104 | "SELECT name, '' FROM global_config" |
| 1105 | " WHERE name GLOB 'cert:*'" |
| 1106 | "UNION ALL " |
| 1107 | "SELECT name, date(mtime,'unixepoch') FROM config" |
| 1108 | " WHERE name GLOB 'cert:*'" |
| 1109 | " ORDER BY name" |
| 1110 | ); |
| 1111 | nHit = 0; |
| 1112 | while( db_step(&q)==SQLITE_ROW ){ |
| 1113 | fossil_print("exception: %-40s %s\n", |
| 1114 | db_column_text(&q,0)+5, db_column_text(&q,1)); |
| 1115 | nHit++; |
| 1116 | } |
| 1117 | db_finalize(&q); |
| 1118 | if( nHit && verbose ){ |
| 1119 | fossil_print("\n" |
| 1120 | " The exceptions are server certificates that the Fossil client\n" |
| 1121 | " is unable to verify using root certificates, but which should be\n" |
| 1122 | " accepted anyhow.\n\n" |
| 1123 | ); |
| 1124 | } |
| 1125 | |
| 1126 | }else |
| 1127 | if( strncmp("remove-exception",zCmd,nCmd)==0 ){ |
| 1128 | int i; |
| 1129 | Blob sql; |
| 1130 | char *zSep = "("; |
| @@ -948,10 +1163,10 @@ | |
| 1163 | db_commit_transaction(); |
| 1164 | blob_reset(&sql); |
| 1165 | }else |
| 1166 | /*default*/{ |
| 1167 | fossil_fatal("unknown sub-command \"%s\".\nshould be one of:" |
| 1168 | " load-certs remove-exception scrub show", |
| 1169 | zCmd); |
| 1170 | } |
| 1171 | #endif |
| 1172 | } |
| 1173 |
+3
-1
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -916,10 +916,11 @@ | ||
| 916 | 916 | delete_private_content(); |
| 917 | 917 | } |
| 918 | 918 | if( !privateOnly ){ |
| 919 | 919 | db_unprotect(PROTECT_ALL); |
| 920 | 920 | db_multi_exec( |
| 921 | + "PRAGMA secure_delete=ON;" | |
| 921 | 922 | "UPDATE user SET pw='';" |
| 922 | 923 | "DELETE FROM config WHERE name IN" |
| 923 | 924 | "(WITH pattern(x) AS (VALUES" |
| 924 | 925 | " ('baseurl:*')," |
| 925 | 926 | " ('cert:*')," |
| @@ -933,11 +934,12 @@ | ||
| 933 | 934 | " ('peer-*')," |
| 934 | 935 | " ('skin:*')," |
| 935 | 936 | " ('subrepo:*')," |
| 936 | 937 | " ('sync-*')," |
| 937 | 938 | " ('syncfrom:*')," |
| 938 | - " ('syncwith:*')" | |
| 939 | + " ('syncwith:*')," | |
| 940 | + " ('ssl-*')" | |
| 939 | 941 | ") SELECT name FROM config, pattern WHERE name GLOB x);" |
| 940 | 942 | ); |
| 941 | 943 | if( bVerily ){ |
| 942 | 944 | db_multi_exec( |
| 943 | 945 | "DELETE FROM concealed;\n" |
| 944 | 946 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -916,10 +916,11 @@ | |
| 916 | delete_private_content(); |
| 917 | } |
| 918 | if( !privateOnly ){ |
| 919 | db_unprotect(PROTECT_ALL); |
| 920 | db_multi_exec( |
| 921 | "UPDATE user SET pw='';" |
| 922 | "DELETE FROM config WHERE name IN" |
| 923 | "(WITH pattern(x) AS (VALUES" |
| 924 | " ('baseurl:*')," |
| 925 | " ('cert:*')," |
| @@ -933,11 +934,12 @@ | |
| 933 | " ('peer-*')," |
| 934 | " ('skin:*')," |
| 935 | " ('subrepo:*')," |
| 936 | " ('sync-*')," |
| 937 | " ('syncfrom:*')," |
| 938 | " ('syncwith:*')" |
| 939 | ") SELECT name FROM config, pattern WHERE name GLOB x);" |
| 940 | ); |
| 941 | if( bVerily ){ |
| 942 | db_multi_exec( |
| 943 | "DELETE FROM concealed;\n" |
| 944 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -916,10 +916,11 @@ | |
| 916 | delete_private_content(); |
| 917 | } |
| 918 | if( !privateOnly ){ |
| 919 | db_unprotect(PROTECT_ALL); |
| 920 | db_multi_exec( |
| 921 | "PRAGMA secure_delete=ON;" |
| 922 | "UPDATE user SET pw='';" |
| 923 | "DELETE FROM config WHERE name IN" |
| 924 | "(WITH pattern(x) AS (VALUES" |
| 925 | " ('baseurl:*')," |
| 926 | " ('cert:*')," |
| @@ -933,11 +934,12 @@ | |
| 934 | " ('peer-*')," |
| 935 | " ('skin:*')," |
| 936 | " ('subrepo:*')," |
| 937 | " ('sync-*')," |
| 938 | " ('syncfrom:*')," |
| 939 | " ('syncwith:*')," |
| 940 | " ('ssl-*')" |
| 941 | ") SELECT name FROM config, pattern WHERE name GLOB x);" |
| 942 | ); |
| 943 | if( bVerily ){ |
| 944 | db_multi_exec( |
| 945 | "DELETE FROM concealed;\n" |
| 946 |