From 30dd916348001e4313708473d91d633d3b14d1b5 Mon Sep 17 00:00:00 2001 From: Mark Lodato Date: Wed, 27 May 2009 23:16:02 -0400 Subject: [PATCH 1/2] http.c: prompt for SSL client certificate password If an SSL client certificate is enabled (via http.sslcert or GIT_SSL_CERT), prompt for the certificate password rather than defaulting to OpenSSL's password prompt. This causes the prompt to only appear once each run. Previously, OpenSSL prompted the user *many* times, causing git to be unusable over HTTPS with client-side certificates. Note that the password is stored in memory in the clear while the program is running. This may be a security problem if git crashes and core dumps. The user is always prompted, even if the certificate is not encrypted. This should be fine; unencrypted certificates are rare and a security risk anyway. Signed-off-by: Mark Lodato Signed-off-by: Junio C Hamano --- http.c | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/http.c b/http.c index 2e3d6493ef..1c138135d7 100644 --- a/http.c +++ b/http.c @@ -27,6 +27,17 @@ static int curl_ftp_no_epsv; static const char *curl_http_proxy; static char *user_name, *user_pass; +#if LIBCURL_VERSION_NUM >= 0x071700 +/* Use CURLOPT_KEYPASSWD as is */ +#elif LIBCURL_VERSION_NUM >= 0x070903 +#define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD +#else +#define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD +#endif + +static char *ssl_cert_password; +static int ssl_cert_password_required; + static struct curl_slist *pragma_header; static struct active_request_slot *active_queue_head; @@ -167,6 +178,22 @@ static void init_curl_http_auth(CURL *result) } } +static int has_cert_password(void) +{ + if (ssl_cert_password != NULL) + return 1; + if (ssl_cert == NULL || ssl_cert_password_required != 1) + return 0; + /* Only prompt the user once. */ + ssl_cert_password_required = -1; + ssl_cert_password = getpass("Certificate Password: "); + if (ssl_cert_password != NULL) { + ssl_cert_password = xstrdup(ssl_cert_password); + return 1; + } else + return 0; +} + static CURL *get_curl_handle(void) { CURL *result = curl_easy_init(); @@ -189,6 +216,8 @@ static CURL *get_curl_handle(void) if (ssl_cert != NULL) curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert); + if (has_cert_password()) + curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password); #if LIBCURL_VERSION_NUM >= 0x070902 if (ssl_key != NULL) curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key); @@ -329,8 +358,11 @@ void http_init(struct remote *remote) if (getenv("GIT_CURL_FTP_NO_EPSV")) curl_ftp_no_epsv = 1; - if (remote && remote->url && remote->url[0]) + if (remote && remote->url && remote->url[0]) { http_auth_init(remote->url[0]); + if (!prefixcmp(remote->url[0], "https://")) + ssl_cert_password_required = 1; + } #ifndef NO_CURL_EASY_DUPHANDLE curl_default = get_curl_handle(); @@ -370,6 +402,13 @@ void http_cleanup(void) free((void *)curl_http_proxy); curl_http_proxy = NULL; } + + if (ssl_cert_password != NULL) { + memset(ssl_cert_password, 0, strlen(ssl_cert_password)); + free(ssl_cert_password); + ssl_cert_password = NULL; + } + ssl_cert_password_required = 0; } struct active_request_slot *get_active_slot(void) From 754ae192a4390baeb4d00b96e72c69023efb22ee Mon Sep 17 00:00:00 2001 From: Mark Lodato Date: Wed, 27 May 2009 23:16:03 -0400 Subject: [PATCH 2/2] http.c: add http.sslCertPasswordProtected option Add a configuration option, http.sslCertPasswordProtected, and associated environment variable, GIT_SSL_CERT_PASSWORD_PROTECTED, to enable SSL client certificate password prompt from within git. If this option is false and if the environment variable does not exist, git falls back to OpenSSL's prompts (as in earlier versions of git). The environment variable may only be used to enable, not to disable git's password prompt. This behavior mimics GIT_NO_VERIFY; the mere existence of the variable is all that is checked. Signed-off-by: Mark Lodato Signed-off-by: Junio C Hamano --- Documentation/config.txt | 6 ++++++ http.c | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 3a86d1f8f0..2649b303fa 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1043,6 +1043,12 @@ http.sslKey:: over HTTPS. Can be overridden by the 'GIT_SSL_KEY' environment variable. +http.sslCertPasswordProtected:: + Enable git's password prompt for the SSL certificate. Otherwise + OpenSSL will prompt the user, possibly many times, if the + certificate or private key is encrypted. Can be overridden by the + 'GIT_SSL_CERT_PASSWORD_PROTECTED' environment variable. + http.sslCAInfo:: File containing the certificates to verify the peer with when fetching or pushing over HTTPS. Can be overridden by the diff --git a/http.c b/http.c index 1c138135d7..1b140d381d 100644 --- a/http.c +++ b/http.c @@ -140,6 +140,11 @@ static int http_options(const char *var, const char *value, void *cb) #endif if (!strcmp("http.sslcainfo", var)) return git_config_string(&ssl_cainfo, var, value); + if (!strcmp("http.sslcertpasswordprotected", var)) { + if (git_config_bool(var, value)) + ssl_cert_password_required = 1; + return 0; + } #ifdef USE_CURL_MULTI if (!strcmp("http.maxrequests", var)) { max_requests = git_config_int(var, value); @@ -360,7 +365,9 @@ void http_init(struct remote *remote) if (remote && remote->url && remote->url[0]) { http_auth_init(remote->url[0]); - if (!prefixcmp(remote->url[0], "https://")) + if (!ssl_cert_password_required && + getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") && + !prefixcmp(remote->url[0], "https://")) ssl_cert_password_required = 1; }