config: preserve config file permissions on edits

Users may already store sensitive data such as imap.pass in .git/config; making the file world-readable when "git config" is called to edit means their password would be compromised on a shared system. [v2: updated for section renames, as noted by Junio] Signed-off-by: Eric Wong <normalperson@yhbt.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Eric Wong committed May 6, 2014 at 00:17 UTC daa22c6f8da466bd7a438f1bc27375fd737ffcf3
2 files changed +26
config.c
+16
@@ -1634,6 +1634,13 @@ int git_config_set_multivar_in_file(const char *config_filename,
1634 MAP_PRIVATE, in_fd, 0);
1635 close(in_fd);
1636
1637 + if (fchmod(fd, st.st_mode & 07777) < 0) {
1638 + error("fchmod on %s failed: %s",
1639 + lock->filename, strerror(errno));
1640 + ret = CONFIG_NO_WRITE;
1641 + goto out_free;
1642 + }
1643 +
1644 if (store.seen == 0)
1645 store.seen = 1;
1646
@@ -1782,6 +1789,7 @@ int git_config_rename_section_in_file(const char *config_filename,
1789 int out_fd;
1790 char buf[1024];
1791 FILE *config_file;
1792 + struct stat st;
1793
1794 if (new_name && !section_name_is_ok(new_name)) {
1795 ret = error("invalid section name: %s", new_name);
@@ -1803,6 +1811,14 @@ int git_config_rename_section_in_file(const char *config_filename,
1811 goto unlock_and_out;
1812 }
1813
1814 + fstat(fileno(config_file), &st);
1815 +
1816 + if (fchmod(out_fd, st.st_mode & 07777) < 0) {
1817 + ret = error("fchmod on %s failed: %s",
1818 + lock->filename, strerror(errno));
1819 + goto out;
1820 + }
1821 +
1822 while (fgets(buf, sizeof(buf), config_file)) {
1823 int i;
1824 int length;
t/t1300-repo-config.sh
+10
@@ -1154,4 +1154,14 @@ test_expect_failure 'adding a key into an empty section reuses header' '
1154 test_cmp expect .git/config
1155 '
1156
1157 +test_expect_success POSIXPERM,PERL 'preserves existing permissions' '
1158 + chmod 0600 .git/config &&
1159 + git config imap.pass Hunter2 &&
1160 + perl -e \
1161 + "die q(badset) if ((stat(q(.git/config)))[2] & 07777) != 0600" &&
1162 + git config --rename-section imap pop &&
1163 + perl -e \
1164 + "die q(badrename) if ((stat(q(.git/config)))[2] & 07777) != 0600"
1165 +'
1166 +
1167 test_done