Fossil SCM
Support for client side SSL certificates for extra authentication to https servers. Adds --ssl-identity command line option and ssl-identity setting to specify the filename of a identity file containing a PEM encoded certificate and private key.
Commit
e06ea26e97e20859a812da55b4914cade028d411
Parent
95f04bbfbf9bd8c…
4 files changed
+10
+8
+21
+2
+10
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -37,19 +37,21 @@ | ||
| 37 | 37 | ** |
| 38 | 38 | ** Options: |
| 39 | 39 | ** |
| 40 | 40 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 41 | 41 | ** --private Also clone private branches |
| 42 | +** --ssl-identity=filename Use the SSL identity if requested by the server | |
| 42 | 43 | ** |
| 43 | 44 | */ |
| 44 | 45 | void clone_cmd(void){ |
| 45 | 46 | char *zPassword; |
| 46 | 47 | const char *zDefaultUser; /* Optional name of the default user */ |
| 47 | 48 | int nErr = 0; |
| 48 | 49 | int bPrivate; /* Also clone private branches */ |
| 49 | 50 | |
| 50 | 51 | bPrivate = find_option("private",0,0)!=0; |
| 52 | + find_option("ssl-identity",0,1); | |
| 51 | 53 | url_proxy_options(); |
| 52 | 54 | if( g.argc < 4 ){ |
| 53 | 55 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 54 | 56 | } |
| 55 | 57 | db_open_config(0); |
| @@ -91,10 +93,18 @@ | ||
| 91 | 93 | db_initial_setup(0, zDefaultUser, 0); |
| 92 | 94 | user_select(); |
| 93 | 95 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 94 | 96 | db_set("aux-schema", AUX_SCHEMA, 0); |
| 95 | 97 | db_set("last-sync-url", g.argv[2], 0); |
| 98 | + if( g.zSSLIdentity!=0 ){ | |
| 99 | + /* If the --ssl-identity option was specified, store it as a setting */ | |
| 100 | + Blob fn; | |
| 101 | + blob_zero(&fn); | |
| 102 | + file_canonical_name(g.zSSLIdentity, &fn); | |
| 103 | + db_set("ssl-identity", blob_str(&fn), 0); | |
| 104 | + blob_reset(&fn); | |
| 105 | + } | |
| 96 | 106 | db_multi_exec( |
| 97 | 107 | "REPLACE INTO config(name,value,mtime)" |
| 98 | 108 | " VALUES('server-code', lower(hex(randomblob(20))), now());" |
| 99 | 109 | ); |
| 100 | 110 | url_enable_proxy(0); |
| 101 | 111 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -37,19 +37,21 @@ | |
| 37 | ** |
| 38 | ** Options: |
| 39 | ** |
| 40 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 41 | ** --private Also clone private branches |
| 42 | ** |
| 43 | */ |
| 44 | void clone_cmd(void){ |
| 45 | char *zPassword; |
| 46 | const char *zDefaultUser; /* Optional name of the default user */ |
| 47 | int nErr = 0; |
| 48 | int bPrivate; /* Also clone private branches */ |
| 49 | |
| 50 | bPrivate = find_option("private",0,0)!=0; |
| 51 | url_proxy_options(); |
| 52 | if( g.argc < 4 ){ |
| 53 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 54 | } |
| 55 | db_open_config(0); |
| @@ -91,10 +93,18 @@ | |
| 91 | db_initial_setup(0, zDefaultUser, 0); |
| 92 | user_select(); |
| 93 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 94 | db_set("aux-schema", AUX_SCHEMA, 0); |
| 95 | db_set("last-sync-url", g.argv[2], 0); |
| 96 | db_multi_exec( |
| 97 | "REPLACE INTO config(name,value,mtime)" |
| 98 | " VALUES('server-code', lower(hex(randomblob(20))), now());" |
| 99 | ); |
| 100 | url_enable_proxy(0); |
| 101 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -37,19 +37,21 @@ | |
| 37 | ** |
| 38 | ** Options: |
| 39 | ** |
| 40 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 41 | ** --private Also clone private branches |
| 42 | ** --ssl-identity=filename Use the SSL identity if requested by the server |
| 43 | ** |
| 44 | */ |
| 45 | void clone_cmd(void){ |
| 46 | char *zPassword; |
| 47 | const char *zDefaultUser; /* Optional name of the default user */ |
| 48 | int nErr = 0; |
| 49 | int bPrivate; /* Also clone private branches */ |
| 50 | |
| 51 | bPrivate = find_option("private",0,0)!=0; |
| 52 | find_option("ssl-identity",0,1); |
| 53 | url_proxy_options(); |
| 54 | if( g.argc < 4 ){ |
| 55 | usage("?OPTIONS? FILE-OR-URL NEW-REPOSITORY"); |
| 56 | } |
| 57 | db_open_config(0); |
| @@ -91,10 +93,18 @@ | |
| 93 | db_initial_setup(0, zDefaultUser, 0); |
| 94 | user_select(); |
| 95 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 96 | db_set("aux-schema", AUX_SCHEMA, 0); |
| 97 | db_set("last-sync-url", g.argv[2], 0); |
| 98 | if( g.zSSLIdentity!=0 ){ |
| 99 | /* If the --ssl-identity option was specified, store it as a setting */ |
| 100 | Blob fn; |
| 101 | blob_zero(&fn); |
| 102 | file_canonical_name(g.zSSLIdentity, &fn); |
| 103 | db_set("ssl-identity", blob_str(&fn), 0); |
| 104 | blob_reset(&fn); |
| 105 | } |
| 106 | db_multi_exec( |
| 107 | "REPLACE INTO config(name,value,mtime)" |
| 108 | " VALUES('server-code', lower(hex(randomblob(20))), now());" |
| 109 | ); |
| 110 | url_enable_proxy(0); |
| 111 |
M
src/db.c
+8
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1662,10 +1662,11 @@ | ||
| 1662 | 1662 | { "mtime-changes", 0, 0, "on" }, |
| 1663 | 1663 | { "pgp-command", 0, 32, "gpg --clearsign -o " }, |
| 1664 | 1664 | { "proxy", 0, 32, "off" }, |
| 1665 | 1665 | { "repo-cksum", 0, 0, "on" }, |
| 1666 | 1666 | { "self-register", 0, 0, "off" }, |
| 1667 | + { "ssl-identity", 0, 40, "" }, | |
| 1667 | 1668 | { "ssh-command", 0, 32, "" }, |
| 1668 | 1669 | { "web-browser", 0, 32, "" }, |
| 1669 | 1670 | { 0,0,0,0 } |
| 1670 | 1671 | }; |
| 1671 | 1672 | |
| @@ -1771,10 +1772,17 @@ | ||
| 1771 | 1772 | ** |
| 1772 | 1773 | ** self-register Allow users to register themselves through the HTTP UI. |
| 1773 | 1774 | ** This is useful if you want to see other names than |
| 1774 | 1775 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 1775 | 1776 | ** users can not be deleted. Default: off. |
| 1777 | +** | |
| 1778 | +** ssl-identity The full pathname to a file containing a certificate | |
| 1779 | +** and private key in PEM format. Create by concatenating | |
| 1780 | +** the certificate and private key files. | |
| 1781 | +** This identity will be presented to SSL servers to | |
| 1782 | +** authenticate this client, in addition to the normal | |
| 1783 | +** password authentication. | |
| 1776 | 1784 | ** |
| 1777 | 1785 | ** ssh-command Command used to talk to a remote machine with |
| 1778 | 1786 | ** the "ssh://" protocol. |
| 1779 | 1787 | ** |
| 1780 | 1788 | ** web-browser A shell command used to launch your preferred |
| 1781 | 1789 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1662,10 +1662,11 @@ | |
| 1662 | { "mtime-changes", 0, 0, "on" }, |
| 1663 | { "pgp-command", 0, 32, "gpg --clearsign -o " }, |
| 1664 | { "proxy", 0, 32, "off" }, |
| 1665 | { "repo-cksum", 0, 0, "on" }, |
| 1666 | { "self-register", 0, 0, "off" }, |
| 1667 | { "ssh-command", 0, 32, "" }, |
| 1668 | { "web-browser", 0, 32, "" }, |
| 1669 | { 0,0,0,0 } |
| 1670 | }; |
| 1671 | |
| @@ -1771,10 +1772,17 @@ | |
| 1771 | ** |
| 1772 | ** self-register Allow users to register themselves through the HTTP UI. |
| 1773 | ** This is useful if you want to see other names than |
| 1774 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 1775 | ** users can not be deleted. Default: off. |
| 1776 | ** |
| 1777 | ** ssh-command Command used to talk to a remote machine with |
| 1778 | ** the "ssh://" protocol. |
| 1779 | ** |
| 1780 | ** web-browser A shell command used to launch your preferred |
| 1781 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1662,10 +1662,11 @@ | |
| 1662 | { "mtime-changes", 0, 0, "on" }, |
| 1663 | { "pgp-command", 0, 32, "gpg --clearsign -o " }, |
| 1664 | { "proxy", 0, 32, "off" }, |
| 1665 | { "repo-cksum", 0, 0, "on" }, |
| 1666 | { "self-register", 0, 0, "off" }, |
| 1667 | { "ssl-identity", 0, 40, "" }, |
| 1668 | { "ssh-command", 0, 32, "" }, |
| 1669 | { "web-browser", 0, 32, "" }, |
| 1670 | { 0,0,0,0 } |
| 1671 | }; |
| 1672 | |
| @@ -1771,10 +1772,17 @@ | |
| 1772 | ** |
| 1773 | ** self-register Allow users to register themselves through the HTTP UI. |
| 1774 | ** This is useful if you want to see other names than |
| 1775 | ** "Anonymous" in e.g. ticketing system. On the other hand |
| 1776 | ** users can not be deleted. Default: off. |
| 1777 | ** |
| 1778 | ** ssl-identity The full pathname to a file containing a certificate |
| 1779 | ** and private key in PEM format. Create by concatenating |
| 1780 | ** the certificate and private key files. |
| 1781 | ** This identity will be presented to SSL servers to |
| 1782 | ** authenticate this client, in addition to the normal |
| 1783 | ** password authentication. |
| 1784 | ** |
| 1785 | ** ssh-command Command used to talk to a remote machine with |
| 1786 | ** the "ssh://" protocol. |
| 1787 | ** |
| 1788 | ** web-browser A shell command used to launch your preferred |
| 1789 |
+21
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -78,10 +78,19 @@ | ||
| 78 | 78 | ** Return the current SSL error message |
| 79 | 79 | */ |
| 80 | 80 | const char *ssl_errmsg(void){ |
| 81 | 81 | return sslErrMsg; |
| 82 | 82 | } |
| 83 | + | |
| 84 | +/* | |
| 85 | +** When a server requests a client certificate that hasn't been provided, | |
| 86 | +** display a warning message explaining what to do next. | |
| 87 | +*/ | |
| 88 | +static int ssl_client_cert_callback(SSL *ssl, X509 **x509, EVP_PKEY **pkey){ | |
| 89 | + fossil_warning("The remote server requested a client certificate for authentication. Specify the pathname to a file containing the PEM encoded certificate and private key with the --ssl-identity option or the ssl-identity setting."); | |
| 90 | + return 0; /* no cert available */ | |
| 91 | +} | |
| 83 | 92 | |
| 84 | 93 | /* |
| 85 | 94 | ** Call this routine once before any other use of the SSL interface. |
| 86 | 95 | ** This routine does initial configuration of the SSL module. |
| 87 | 96 | */ |
| @@ -91,10 +100,22 @@ | ||
| 91 | 100 | SSL_load_error_strings(); |
| 92 | 101 | ERR_load_BIO_strings(); |
| 93 | 102 | OpenSSL_add_all_algorithms(); |
| 94 | 103 | sslCtx = SSL_CTX_new(SSLv23_client_method()); |
| 95 | 104 | X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); |
| 105 | + | |
| 106 | + /* Load client SSL identity, preferring the filename specified on the command line */ | |
| 107 | + const char *identityFile = ( g.zSSLIdentity!= 0) ? g.zSSLIdentity : db_get("ssl-identity", 0); | |
| 108 | + if( identityFile!=0 && identityFile[0]!='\0' ){ | |
| 109 | + if( SSL_CTX_use_certificate_file(sslCtx, identityFile, SSL_FILETYPE_PEM)!= 1 | |
| 110 | + || SSL_CTX_use_PrivateKey_file(sslCtx, identityFile, SSL_FILETYPE_PEM)!=1 ){ | |
| 111 | + fossil_fatal("Could not load SSL identity from %s", identityFile); | |
| 112 | + } | |
| 113 | + } | |
| 114 | + /* Register a callback to tell the user what to do when the server asks for a cert */ | |
| 115 | + SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback); | |
| 116 | + | |
| 96 | 117 | sslIsInit = 1; |
| 97 | 118 | } |
| 98 | 119 | } |
| 99 | 120 | |
| 100 | 121 | /* |
| 101 | 122 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -78,10 +78,19 @@ | |
| 78 | ** Return the current SSL error message |
| 79 | */ |
| 80 | const char *ssl_errmsg(void){ |
| 81 | return sslErrMsg; |
| 82 | } |
| 83 | |
| 84 | /* |
| 85 | ** Call this routine once before any other use of the SSL interface. |
| 86 | ** This routine does initial configuration of the SSL module. |
| 87 | */ |
| @@ -91,10 +100,22 @@ | |
| 91 | SSL_load_error_strings(); |
| 92 | ERR_load_BIO_strings(); |
| 93 | OpenSSL_add_all_algorithms(); |
| 94 | sslCtx = SSL_CTX_new(SSLv23_client_method()); |
| 95 | X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); |
| 96 | sslIsInit = 1; |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -78,10 +78,19 @@ | |
| 78 | ** Return the current SSL error message |
| 79 | */ |
| 80 | const char *ssl_errmsg(void){ |
| 81 | return sslErrMsg; |
| 82 | } |
| 83 | |
| 84 | /* |
| 85 | ** When a server requests a client certificate that hasn't been provided, |
| 86 | ** display a warning message explaining what to do next. |
| 87 | */ |
| 88 | static int ssl_client_cert_callback(SSL *ssl, X509 **x509, EVP_PKEY **pkey){ |
| 89 | fossil_warning("The remote server requested a client certificate for authentication. Specify the pathname to a file containing the PEM encoded certificate and private key with the --ssl-identity option or the ssl-identity setting."); |
| 90 | return 0; /* no cert available */ |
| 91 | } |
| 92 | |
| 93 | /* |
| 94 | ** Call this routine once before any other use of the SSL interface. |
| 95 | ** This routine does initial configuration of the SSL module. |
| 96 | */ |
| @@ -91,10 +100,22 @@ | |
| 100 | SSL_load_error_strings(); |
| 101 | ERR_load_BIO_strings(); |
| 102 | OpenSSL_add_all_algorithms(); |
| 103 | sslCtx = SSL_CTX_new(SSLv23_client_method()); |
| 104 | X509_STORE_set_default_paths(SSL_CTX_get_cert_store(sslCtx)); |
| 105 | |
| 106 | /* Load client SSL identity, preferring the filename specified on the command line */ |
| 107 | const char *identityFile = ( g.zSSLIdentity!= 0) ? g.zSSLIdentity : db_get("ssl-identity", 0); |
| 108 | if( identityFile!=0 && identityFile[0]!='\0' ){ |
| 109 | if( SSL_CTX_use_certificate_file(sslCtx, identityFile, SSL_FILETYPE_PEM)!= 1 |
| 110 | || SSL_CTX_use_PrivateKey_file(sslCtx, identityFile, SSL_FILETYPE_PEM)!=1 ){ |
| 111 | fossil_fatal("Could not load SSL identity from %s", identityFile); |
| 112 | } |
| 113 | } |
| 114 | /* Register a callback to tell the user what to do when the server asks for a cert */ |
| 115 | SSL_CTX_set_client_cert_cb(sslCtx, ssl_client_cert_callback); |
| 116 | |
| 117 | sslIsInit = 1; |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | /* |
| 122 |
+2
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -104,10 +104,11 @@ | ||
| 104 | 104 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 105 | 105 | char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ |
| 106 | 106 | int dontKeepUrl; /* Do not persist the URL */ |
| 107 | 107 | |
| 108 | 108 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 109 | + const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ | |
| 109 | 110 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 110 | 111 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 111 | 112 | int userUid; /* Integer user id */ |
| 112 | 113 | |
| 113 | 114 | /* Information used to populate the RCVFROM table */ |
| @@ -249,10 +250,11 @@ | ||
| 249 | 250 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 250 | 251 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 251 | 252 | g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; |
| 252 | 253 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 253 | 254 | g.zLogin = find_option("user", "U", 1); |
| 255 | + g.zSSLIdentity = find_option("ssl-identity", 0, 1); | |
| 254 | 256 | if( find_option("help",0,0)!=0 ){ |
| 255 | 257 | /* --help anywhere on the command line is translated into |
| 256 | 258 | ** "fossil help argv[1] argv[2]..." */ |
| 257 | 259 | int i; |
| 258 | 260 | char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) ); |
| 259 | 261 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -104,10 +104,11 @@ | |
| 104 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 105 | char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ |
| 106 | int dontKeepUrl; /* Do not persist the URL */ |
| 107 | |
| 108 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 109 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 110 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 111 | int userUid; /* Integer user id */ |
| 112 | |
| 113 | /* Information used to populate the RCVFROM table */ |
| @@ -249,10 +250,11 @@ | |
| 249 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 250 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 251 | g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; |
| 252 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 253 | g.zLogin = find_option("user", "U", 1); |
| 254 | if( find_option("help",0,0)!=0 ){ |
| 255 | /* --help anywhere on the command line is translated into |
| 256 | ** "fossil help argv[1] argv[2]..." */ |
| 257 | int i; |
| 258 | char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) ); |
| 259 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -104,10 +104,11 @@ | |
| 104 | char *urlProxyAuth; /* Proxy-Authorizer: string */ |
| 105 | char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ |
| 106 | int dontKeepUrl; /* Do not persist the URL */ |
| 107 | |
| 108 | const char *zLogin; /* Login name. "" if not logged in. */ |
| 109 | const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ |
| 110 | int useLocalauth; /* No login required if from 127.0.0.1 */ |
| 111 | int noPswd; /* Logged in without password (on 127.0.0.1) */ |
| 112 | int userUid; /* Integer user id */ |
| 113 | |
| 114 | /* Information used to populate the RCVFROM table */ |
| @@ -249,10 +250,11 @@ | |
| 250 | g.fSqlStats = find_option("sqlstats", 0, 0)!=0; |
| 251 | if( g.fSqlTrace ) g.fSqlStats = 1; |
| 252 | g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; |
| 253 | g.fHttpTrace = find_option("httptrace", 0, 0)!=0; |
| 254 | g.zLogin = find_option("user", "U", 1); |
| 255 | g.zSSLIdentity = find_option("ssl-identity", 0, 1); |
| 256 | if( find_option("help",0,0)!=0 ){ |
| 257 | /* --help anywhere on the command line is translated into |
| 258 | ** "fossil help argv[1] argv[2]..." */ |
| 259 | int i; |
| 260 | char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) ); |
| 261 |