From 8ed85ef3de9cdeee86e281a8b46be1bd49a36e7a Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 9 Sep 2020 20:33:06 +0200 Subject: [PATCH] agent: Keep some permissions of private-keys-v1.d. * common/sysutils.c (modestr_to_mode): Re-implement. (gnupg_chmod): Support keeping of permissions. -- GnuPG-bug-id: 2312 --- agent/gpg-agent.c | 16 +++++++++--- common/sysutils.c | 64 ++++++++++++++++++++++++++++++----------------- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index bbad80074..f167c96db 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -2273,10 +2273,20 @@ create_private_keys_directory (const char *home) fname, strerror (errno) ); else if (!opt.quiet) log_info (_("directory '%s' created\n"), fname); + + if (gnupg_chmod (fname, "-rwx")) + log_error (_("can't set permissions of '%s': %s\n"), + fname, strerror (errno)); + } + else + { + /* The file exists or another error. Make sure we have sensible + * permissions. We enforce rwx for user but keep existing group + * permissions. Permissions for other are always cleared. */ + if (gnupg_chmod (fname, "-rwx...---")) + log_error (_("can't set permissions of '%s': %s\n"), + fname, strerror (errno)); } - if (gnupg_chmod (fname, "-rwx")) - log_error (_("can't set permissions of '%s': %s\n"), - fname, strerror (errno)); xfree (fname); } diff --git a/common/sysutils.c b/common/sysutils.c index efc2dfe9b..140d1d7be 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -749,33 +749,38 @@ gnupg_rename_file (const char *oldname, const char *newname, int *block_signals) #ifndef HAVE_W32_SYSTEM static mode_t -modestr_to_mode (const char *modestr) +modestr_to_mode (const char *modestr, mode_t oldmode) { + static struct { + char letter; + mode_t value; + } table[] = { { '-', 0 }, + { 'r', S_IRUSR }, { 'w', S_IWUSR }, { 'x', S_IXUSR }, + { 'r', S_IRGRP }, { 'w', S_IWGRP }, { 'x', S_IXGRP }, + { 'r', S_IROTH }, { 'w', S_IWOTH }, { 'x', S_IXOTH } }; + int idx; mode_t mode = 0; - if (modestr && *modestr) + /* For now we only support a string as used by ls(1) and no octal + * numbers. The first character must be a dash. */ + for (idx=0; idx < 10 && *modestr; idx++, modestr++) { - modestr++; - if (*modestr && *modestr++ == 'r') - mode |= S_IRUSR; - if (*modestr && *modestr++ == 'w') - mode |= S_IWUSR; - if (*modestr && *modestr++ == 'x') - mode |= S_IXUSR; - if (*modestr && *modestr++ == 'r') - mode |= S_IRGRP; - if (*modestr && *modestr++ == 'w') - mode |= S_IWGRP; - if (*modestr && *modestr++ == 'x') - mode |= S_IXGRP; - if (*modestr && *modestr++ == 'r') - mode |= S_IROTH; - if (*modestr && *modestr++ == 'w') - mode |= S_IWOTH; - if (*modestr && *modestr++ == 'x') - mode |= S_IXOTH; + if (*modestr == table[idx].letter) + mode |= table[idx].value; + else if (*modestr == '.') + { + if (!idx) + ; /* Skip the dummy. */ + else if ((oldmode & table[idx].value)) + mode |= (oldmode & table[idx].value); + else + mode &= ~(oldmode & table[idx].value); + } + else if (*modestr != '-') + break; } + return mode; } #endif @@ -815,7 +820,8 @@ gnupg_chdir (const char *name) /* A wrapper around chmod which takes a string for the mode argument. This makes it easier to handle the mode argument which is not defined on all systems. The format of the modestring is the same - as for gnupg_mkdir. */ + as for gnupg_mkdir with extra feature that a '.' keeps the original + mode bit. */ int gnupg_chmod (const char *name, const char *modestr) { @@ -824,7 +830,19 @@ gnupg_chmod (const char *name, const char *modestr) (void)modestr; return 0; #else - return chmod (name, modestr_to_mode (modestr)); + mode_t oldmode; + if (strchr (modestr, '.')) + { + /* Get the old mode so that a '.' can copy that bit. */ + struct stat st; + + if (stat (name, &st)) + return -1; + oldmode = st.st_mode; + } + else + oldmode = 0; + return chmod (name, modestr_to_mode (modestr, oldmode)); #endif }