credential: disallow Carriage Returns in the protocol by default
While Git has documented that the credential protocol is line-based, with newlines as terminators, the exact shape of a newline has not been documented. From Git's perspective, which is firmly rooted in the Linux ecosystem, it is clear that "a newline" means a Line Feed character. However, even Git's credential protocol respects Windows line endings (a Carriage Return character followed by a Line Feed character, "CR/LF") by virtue of using `strbuf_getline()`. There is a third category of line endings that has been used originally by MacOS, and that is respected by the default line readers of .NET and node.js: bare Carriage Returns. Git cannot handle those, and what is worse: Git's remedy against CVE-2020-5260 does not catch when credential helpers are used that interpret bare Carriage Returns as newlines. Git Credential Manager addressed this as CVE-2024-50338, but other credential helpers may still be vulnerable. So let's not only disallow Line Feed characters as part of the values in the credential protocol, but also disallow Carriage Return characters. In the unlikely event that a credential helper relies on Carriage Returns in the protocol, introduce an escape hatch via the `credential.protectProtocol` config setting. This addresses CVE-2024-52006. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
21
credential.c
21
credential.c
@ -69,6 +69,8 @@ static int credential_config_callback(const char *var, const char *value,
|
||||
c->use_http_path = git_config_bool(var, value);
|
||||
else if (!strcmp(key, "sanitizeprompt"))
|
||||
c->sanitize_prompt = git_config_bool(var, value);
|
||||
else if (!strcmp(key, "protectprotocol"))
|
||||
c->protect_protocol = git_config_bool(var, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -262,7 +264,8 @@ int credential_read(struct credential *c, FILE *fp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void credential_write_item(FILE *fp, const char *key, const char *value,
|
||||
static void credential_write_item(const struct credential *c,
|
||||
FILE *fp, const char *key, const char *value,
|
||||
int required)
|
||||
{
|
||||
if (!value && required)
|
||||
@ -271,19 +274,23 @@ static void credential_write_item(FILE *fp, const char *key, const char *value,
|
||||
return;
|
||||
if (strchr(value, '\n'))
|
||||
die("credential value for %s contains newline", key);
|
||||
if (c->protect_protocol && strchr(value, '\r'))
|
||||
die("credential value for %s contains carriage return\n"
|
||||
"If this is intended, set `credential.protectProtocol=false`",
|
||||
key);
|
||||
fprintf(fp, "%s=%s\n", key, value);
|
||||
}
|
||||
|
||||
void credential_write(const struct credential *c, FILE *fp)
|
||||
{
|
||||
credential_write_item(fp, "protocol", c->protocol, 1);
|
||||
credential_write_item(fp, "host", c->host, 1);
|
||||
credential_write_item(fp, "path", c->path, 0);
|
||||
credential_write_item(fp, "username", c->username, 0);
|
||||
credential_write_item(fp, "password", c->password, 0);
|
||||
credential_write_item(c, fp, "protocol", c->protocol, 1);
|
||||
credential_write_item(c, fp, "host", c->host, 1);
|
||||
credential_write_item(c, fp, "path", c->path, 0);
|
||||
credential_write_item(c, fp, "username", c->username, 0);
|
||||
credential_write_item(c, fp, "password", c->password, 0);
|
||||
if (c->password_expiry_utc != TIME_MAX) {
|
||||
char *s = xstrfmt("%"PRItime, c->password_expiry_utc);
|
||||
credential_write_item(fp, "password_expiry_utc", s, 0);
|
||||
credential_write_item(c, fp, "password_expiry_utc", s, 0);
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user