3
3
4
4
#include <getopt.h>
5
5
#include <pwd.h>
6
+ #include <grp.h>
6
7
#include <stdio.h>
7
8
#include <stdarg.h>
9
+ #include <libgen.h>
8
10
9
11
#include <sys/stat.h>
10
12
#include <sys/types.h>
@@ -23,11 +25,11 @@ struct infix_ds {
23
25
};
24
26
25
27
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 }
31
33
};
32
34
33
35
static const char * prognm = "copy" ;
@@ -64,6 +66,9 @@ static void emsg(sr_session_ctx_t *sess, const char *fmt, ...)
64
66
}
65
67
}
66
68
69
+ /*
70
+ * Current system user, same as sysrepo user
71
+ */
67
72
static char * getuser (void )
68
73
{
69
74
const struct passwd * pw ;
@@ -79,18 +84,83 @@ static char *getuser(void)
79
84
return pw -> pw_name ;
80
85
}
81
86
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
+ */
82
147
static void set_owner (const char * fn , const char * user )
83
148
{
84
- struct passwd * pw ;
149
+ gid_t gid = 9999 ;
85
150
86
151
if (!fn )
87
152
return ; /* not an error, e.g., running-config is not a file */
88
153
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 );
92
160
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
+ }
94
164
}
95
165
96
166
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)
108
178
}
109
179
110
180
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 )
112
182
{
113
183
struct infix_ds * srcds = NULL , * dstds = NULL ;
114
184
char temp_file [20 ] = "/tmp/copy.XXXXXX" ;
115
185
const char * tmpfn = NULL ;
116
186
sr_session_ctx_t * sess ;
117
187
const char * fn = NULL ;
118
- const char * username ;
119
188
sr_conn_ctx_t * conn ;
189
+ const char * user ;
120
190
char adjust [256 ];
121
191
mode_t oldmask ;
122
192
int rc = 0 ;
123
193
124
- oldmask = umask (0660 );
194
+ /* rw for user and group only */
195
+ oldmask = umask (0006 );
125
196
126
197
src = infix_ds (src , & srcds );
127
198
if (!src )
@@ -131,11 +202,11 @@ static int copy(const char *src, const char *dst, const char *user)
131
202
goto err ;
132
203
133
204
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 " );
135
206
goto err ;
136
207
}
137
208
138
- username = getuser ();
209
+ user = getuser ();
139
210
140
211
/* 1. Regular ds copy */
141
212
if (srcds && dstds ) {
@@ -157,13 +228,13 @@ static int copy(const char *src, const char *dst, const char *user)
157
228
if (sr_session_start (conn , dstds -> datastore , & sess )) {
158
229
fprintf (stderr , ERRMSG "unable to open transaction to %s\n" , dst );
159
230
} else {
160
- sr_nacm_set_user (sess , username );
231
+ sr_nacm_set_user (sess , user );
161
232
rc = sr_copy_config (sess , NULL , srcds -> datastore , timeout * 1000 );
162
233
if (rc )
163
234
emsg (sess , ERRMSG "unable to copy configuration, err %d: %s\n" ,
164
235
rc , sr_strerror (rc ));
165
236
else
166
- set_owner (dstds -> path , username );
237
+ set_owner (dstds -> path , user );
167
238
}
168
239
rc = sr_disconnect (conn );
169
240
@@ -187,11 +258,11 @@ static int copy(const char *src, const char *dst, const char *user)
187
258
if (rc )
188
259
fprintf (stderr , ERRMSG "failed exporting %s to %s\n" , src , fn );
189
260
else {
190
- rc = systemf ("curl %s -LT %s %s" , user , fn , dst );
261
+ rc = systemf ("curl %s -LT %s %s" , remote_user , fn , dst );
191
262
if (rc )
192
263
fprintf (stderr , ERRMSG "failed uploading %s to %s\n" , src , dst );
193
264
else
194
- set_owner (dst , username );
265
+ set_owner (dst , user );
195
266
}
196
267
goto err ;
197
268
}
@@ -219,7 +290,7 @@ static int copy(const char *src, const char *dst, const char *user)
219
290
if (rc )
220
291
fprintf (stderr , ERRMSG "failed copy %s to %s\n" , src , fn );
221
292
else
222
- set_owner (fn , username );
293
+ set_owner (fn , user );
223
294
} else if (dstds ) {
224
295
if (!dstds -> sysrepocfg ) {
225
296
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)
245
316
}
246
317
247
318
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 );
249
320
if (rc ) {
250
321
fprintf (stderr , ERRMSG "failed downloading %s" , src );
251
322
} else {
@@ -274,7 +345,7 @@ static int copy(const char *src, const char *dst, const char *user)
274
345
}
275
346
}
276
347
277
- rc = systemf ("curl %s -Lo %s %s" , user , fn , src );
348
+ rc = systemf ("curl %s -Lo %s %s" , remote_user , fn , src );
278
349
} else if (strstr (dst , "://" )) {
279
350
fn = cfg_adjust (src , NULL , adjust , sizeof (adjust ));
280
351
if (!fn ) {
@@ -286,7 +357,7 @@ static int copy(const char *src, const char *dst, const char *user)
286
357
if (access (fn , F_OK ))
287
358
fprintf (stderr , ERRMSG "no such file %s, aborting." , fn );
288
359
else
289
- rc = systemf ("curl %s -LT %s %s" , user , fn , dst );
360
+ rc = systemf ("curl %s -LT %s %s" , remote_user , fn , dst );
290
361
} else {
291
362
if (!access (dst , F_OK )) {
292
363
if (!yorn ("Overwrite existing file %s" , dst )) {
@@ -347,7 +418,7 @@ int main(int argc, char *argv[])
347
418
if (timeout < 0 )
348
419
timeout = 120 ;
349
420
350
- if (optind >= argc )
421
+ if (argc - optind != 2 )
351
422
return usage (1 );
352
423
353
424
src = argv [optind ++ ];
0 commit comments