Skip to content

Commit 2291e9f

Browse files
authored
Merge pull request #979 from kernelkit/copy-owner
Fix annoying "permission denied" when saving running-config
2 parents 56a086e + 095c9c3 commit 2291e9f

File tree

5 files changed

+107
-26
lines changed

5 files changed

+107
-26
lines changed

board/common/rootfs/usr/bin/dir

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ dir()
2121
if [ -d "$1" ]; then
2222
dir "$1"
2323
else
24-
dir "$HOME"
24+
if [ "$USER" = "root" ]; then
25+
dir "$HOME"
26+
else
27+
dir "/home/$USER"
28+
fi
2529
dir "/cfg"
2630
dir "/log"
2731
fi

doc/ChangeLog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ All notable changes to the project are documented in this file.
3131
- Fix #956: CLI `copy` command complains it cannot change owner when
3232
copying `factory-config` to `running-config`. Bogus error, the
3333
latter is not really a file
34+
- Fix #977: "Operation not permitted" when saving `running-config` to
35+
`startup-config` (harmless warning but annoying and concerning)
3436

3537
[EVK]: https://www.nxp.com/design/design-center/development-boards-and-designs/8MPLUSLPD4-EVK
3638

src/bin/copy.c

Lines changed: 95 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
#include <getopt.h>
55
#include <pwd.h>
6+
#include <grp.h>
67
#include <stdio.h>
78
#include <stdarg.h>
9+
#include <libgen.h>
810

911
#include <sys/stat.h>
1012
#include <sys/types.h>
@@ -23,11 +25,11 @@ struct infix_ds {
2325
};
2426

2527
struct infix_ds infix_config[] = {
26-
{ "startup-config", "startup", SR_DS_STARTUP, 1, "/cfg/startup-config.cfg" },
27-
{ "running-config", "running", SR_DS_RUNNING, 1, NULL },
28-
{ "candidate-config", "candidate", SR_DS_CANDIDATE, 1, NULL },
29-
{ "operational-config", "operational", SR_DS_OPERATIONAL, 1, NULL },
30-
{ "factory-config", "factory-default", SR_DS_FACTORY_DEFAULT, 0, NULL }
28+
{ "startup-config", "startup", SR_DS_STARTUP, 1, "/cfg/startup-config.cfg" },
29+
{ "running-config", "running", SR_DS_RUNNING, 1, NULL },
30+
{ "candidate-config", "candidate", SR_DS_CANDIDATE, 1, NULL },
31+
{ "operational-config", "operational", SR_DS_OPERATIONAL, 1, NULL },
32+
{ "factory-config", "factory-default", SR_DS_FACTORY_DEFAULT, 0, NULL }
3133
};
3234

3335
static const char *prognm = "copy";
@@ -64,6 +66,9 @@ static void emsg(sr_session_ctx_t *sess, const char *fmt, ...)
6466
}
6567
}
6668

69+
/*
70+
* Current system user, same as sysrepo user
71+
*/
6772
static char *getuser(void)
6873
{
6974
const struct passwd *pw;
@@ -79,18 +84,83 @@ static char *getuser(void)
7984
return pw->pw_name;
8085
}
8186

87+
/*
88+
* If UNIX user is in UNIX group of directory containing file,
89+
* return 1, otherwise 0.
90+
*
91+
* E.g., writing to /cfg/foo, where /cfg is owned by root:wheel,
92+
* should result in the file being owned by $LOGNAME:wheel with
93+
* 0660 perms for other users in same group.
94+
*/
95+
static int in_group(const char *user, const char *fn, gid_t *gid)
96+
{
97+
char path[PATH_MAX];
98+
const struct passwd *pw;
99+
int i, num = 0, rc = 0;
100+
struct stat st;
101+
gid_t *groups;
102+
char *dir;
103+
104+
pw = getpwnam(user);
105+
if (!pw)
106+
return 0;
107+
108+
strlcpy(path, fn, sizeof(path));
109+
dir = dirname(path);
110+
111+
if (stat(dir, &st))
112+
return 0;
113+
114+
num = NGROUPS_MAX;
115+
groups = malloc(num * sizeof(gid_t));
116+
if (!groups) {
117+
perror("in_group() malloc");
118+
return 0;
119+
}
120+
121+
getgrouplist(user, pw->pw_gid, groups, &num);
122+
for (i = 0; i < num; i++) {
123+
if (groups[i] == st.st_gid) {
124+
*gid = st.st_gid;
125+
rc = 1;
126+
break;
127+
}
128+
}
129+
free(groups);
130+
131+
return rc;
132+
}
133+
134+
/*
135+
* Set group owner so other users with same directory permissions can
136+
* read/write the file as well. E.g., an 'admin' level user in group
137+
* 'wheel' writing a new file to `/cfg` should be possible to read and
138+
* write to by other administrators.
139+
*
140+
* This function is called only when the file has been successfully
141+
* copied or created in a file system directory. This is why we can
142+
* safely ignore any EPERM errors to chown(), below, because if the file
143+
* already existed, created by another user, we are not allowed to chgrp
144+
* it. The sole purpose of this function is to allow other users in the
145+
* same group to access the file in the future.
146+
*/
82147
static void set_owner(const char *fn, const char *user)
83148
{
84-
struct passwd *pw;
149+
gid_t gid = 9999;
85150

86151
if (!fn)
87152
return; /* not an error, e.g., running-config is not a file */
88153

89-
pw = getpwnam(user);
90-
if (pw && !chmod(fn, 0660) && !chown(fn, pw->pw_uid, pw->pw_gid))
91-
return;
154+
if (!in_group(user, fn, &gid))
155+
return; /* user not in parent directory's group */
156+
157+
if (chown(fn, -1, gid) && errno != EPERM) {
158+
int _errno = errno;
159+
const struct group *gr = getgrgid(gid);
92160

93-
fprintf(stderr, ERRMSG "setting owner %s on %s: %s\n", user, fn, strerror(errno));
161+
fprintf(stderr, ERRMSG "setting group owner %s (%d) on %s: %s\n",
162+
gr ? gr->gr_name : "<unknown>", gid, fn, strerror(_errno));
163+
}
94164
}
95165

96166
static const char *infix_ds(const char *text, struct infix_ds **ds)
@@ -108,20 +178,21 @@ static const char *infix_ds(const char *text, struct infix_ds **ds)
108178
}
109179

110180

111-
static int copy(const char *src, const char *dst, const char *user)
181+
static int copy(const char *src, const char *dst, const char *remote_user)
112182
{
113183
struct infix_ds *srcds = NULL, *dstds = NULL;
114184
char temp_file[20] = "/tmp/copy.XXXXXX";
115185
const char *tmpfn = NULL;
116186
sr_session_ctx_t *sess;
117187
const char *fn = NULL;
118-
const char *username;
119188
sr_conn_ctx_t *conn;
189+
const char *user;
120190
char adjust[256];
121191
mode_t oldmask;
122192
int rc = 0;
123193

124-
oldmask = umask(0660);
194+
/* rw for user and group only */
195+
oldmask = umask(0006);
125196

126197
src = infix_ds(src, &srcds);
127198
if (!src)
@@ -131,11 +202,11 @@ static int copy(const char *src, const char *dst, const char *user)
131202
goto err;
132203

133204
if (!strcmp(src, dst)) {
134-
fprintf(stderr, ERRMSG "source and destination are the same, aborting.");
205+
fprintf(stderr, ERRMSG "source and destination are the same, aborting.\n");
135206
goto err;
136207
}
137208

138-
username = getuser();
209+
user = getuser();
139210

140211
/* 1. Regular ds copy */
141212
if (srcds && dstds) {
@@ -157,13 +228,13 @@ static int copy(const char *src, const char *dst, const char *user)
157228
if (sr_session_start(conn, dstds->datastore, &sess)) {
158229
fprintf(stderr, ERRMSG "unable to open transaction to %s\n", dst);
159230
} else {
160-
sr_nacm_set_user(sess, username);
231+
sr_nacm_set_user(sess, user);
161232
rc = sr_copy_config(sess, NULL, srcds->datastore, timeout * 1000);
162233
if (rc)
163234
emsg(sess, ERRMSG "unable to copy configuration, err %d: %s\n",
164235
rc, sr_strerror(rc));
165236
else
166-
set_owner(dstds->path, username);
237+
set_owner(dstds->path, user);
167238
}
168239
rc = sr_disconnect(conn);
169240

@@ -187,11 +258,11 @@ static int copy(const char *src, const char *dst, const char *user)
187258
if (rc)
188259
fprintf(stderr, ERRMSG "failed exporting %s to %s\n", src, fn);
189260
else {
190-
rc = systemf("curl %s -LT %s %s", user, fn, dst);
261+
rc = systemf("curl %s -LT %s %s", remote_user, fn, dst);
191262
if (rc)
192263
fprintf(stderr, ERRMSG "failed uploading %s to %s\n", src, dst);
193264
else
194-
set_owner(dst, username);
265+
set_owner(dst, user);
195266
}
196267
goto err;
197268
}
@@ -219,7 +290,7 @@ static int copy(const char *src, const char *dst, const char *user)
219290
if (rc)
220291
fprintf(stderr, ERRMSG "failed copy %s to %s\n", src, fn);
221292
else
222-
set_owner(fn, username);
293+
set_owner(fn, user);
223294
} else if (dstds) {
224295
if (!dstds->sysrepocfg) {
225296
fprintf(stderr, ERRMSG "not possible to import to this datastore.\n");
@@ -245,7 +316,7 @@ static int copy(const char *src, const char *dst, const char *user)
245316
}
246317

247318
if (tmpfn)
248-
rc = systemf("curl %s -Lo %s %s", user, fn, src);
319+
rc = systemf("curl %s -Lo %s %s", remote_user, fn, src);
249320
if (rc) {
250321
fprintf(stderr, ERRMSG "failed downloading %s", src);
251322
} else {
@@ -274,7 +345,7 @@ static int copy(const char *src, const char *dst, const char *user)
274345
}
275346
}
276347

277-
rc = systemf("curl %s -Lo %s %s", user, fn, src);
348+
rc = systemf("curl %s -Lo %s %s", remote_user, fn, src);
278349
} else if (strstr(dst, "://")) {
279350
fn = cfg_adjust(src, NULL, adjust, sizeof(adjust));
280351
if (!fn) {
@@ -286,7 +357,7 @@ static int copy(const char *src, const char *dst, const char *user)
286357
if (access(fn, F_OK))
287358
fprintf(stderr, ERRMSG "no such file %s, aborting.", fn);
288359
else
289-
rc = systemf("curl %s -LT %s %s", user, fn, dst);
360+
rc = systemf("curl %s -LT %s %s", remote_user, fn, dst);
290361
} else {
291362
if (!access(dst, F_OK)) {
292363
if (!yorn("Overwrite existing file %s", dst)) {
@@ -347,7 +418,7 @@ int main(int argc, char *argv[])
347418
if (timeout < 0)
348419
timeout = 120;
349420

350-
if (optind >= argc)
421+
if (argc - optind != 2)
351422
return usage(1);
352423

353424
src = argv[optind++];

src/bin/files.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ int files(const char *path, const char *stripext)
2828
if (d->d_type != DT_REG || d->d_name[0] == '.')
2929
continue;
3030

31+
/* skip startup in /cfg, listed by plugin */
32+
if (!strcmp(path, "/cfg") && !strcmp(d->d_name, "startup-config.cfg"))
33+
continue;
34+
3135
strlcpy(name, d->d_name, sizeof(name));
3236
if (stripext) {
3337
size_t pos = has_ext(name, stripext);

src/klish-plugin-infix/src/infix.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ int infix_datastore(kcontext_t *ctx)
102102
}
103103

104104
done:
105-
return systemf("files /cfg .cfg");
105+
return systemf("files /cfg");
106106
}
107107

108108
int infix_erase(kcontext_t *ctx)

0 commit comments

Comments
 (0)