rda-python-setuid 3.0.0__py3-none-any.whl → 3.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rda_python_setuid/cmwrapper.c +120 -0
- rda_python_setuid/install.py +65 -5
- rda_python_setuid/install.usg +53 -7
- {rda_python_setuid-3.0.0.dist-info → rda_python_setuid-3.0.2.dist-info}/METADATA +75 -2
- rda_python_setuid-3.0.2.dist-info/RECORD +15 -0
- {rda_python_setuid-3.0.0.dist-info → rda_python_setuid-3.0.2.dist-info}/WHEEL +1 -1
- rda_python_setuid-3.0.0.dist-info/RECORD +0 -14
- {rda_python_setuid-3.0.0.dist-info → rda_python_setuid-3.0.2.dist-info}/entry_points.txt +0 -0
- {rda_python_setuid-3.0.0.dist-info → rda_python_setuid-3.0.2.dist-info}/licenses/LICENSE +0 -0
- {rda_python_setuid-3.0.0.dist-info → rda_python_setuid-3.0.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/***************************************************************************************\
|
|
2
|
+
*
|
|
3
|
+
* Title: cmwrapper.c
|
|
4
|
+
* Author: Zaihua Ji, zji@ucar.edu
|
|
5
|
+
* Date: 2026-09-23
|
|
6
|
+
* Purpose: C wrapper to start ONE fixed python program as an effective user, for
|
|
7
|
+
* callers who are NOT in the common user's group (Mode 4755).
|
|
8
|
+
*
|
|
9
|
+
* Difference from pywrapper.c:
|
|
10
|
+
* pywrapper is installed 4750, so only members of the common user's group can
|
|
11
|
+
* execute it, and it picks the program to exec from basename(argv[0]) with the
|
|
12
|
+
* directory taken from /proc/self/exe. That is safe only because of the 4750
|
|
13
|
+
* group restriction: anyone who can run it can already run every setuid_* entry
|
|
14
|
+
* in the bin directory just by naming the symlink differently.
|
|
15
|
+
*
|
|
16
|
+
* cmwrapper is installed 4755, i.e. executable by EVERY user on the machine, so
|
|
17
|
+
* neither the program nor its directory may come from the caller:
|
|
18
|
+
* - the absolute path of the program to exec is baked in at compile time as
|
|
19
|
+
* CMEXEC, and the program name reported to the program is baked in as CMPROG;
|
|
20
|
+
* - the environment is replaced with a fixed whitelist, so PYTHONPATH,
|
|
21
|
+
* PYTHONHOME, LD_PRELOAD, LD_LIBRARY_PATH and the PGLOG path variables
|
|
22
|
+
* (DSDHOME, DSSHOME, LOGPATH, COMMONUSER, ...) cannot be used to run
|
|
23
|
+
* arbitrary code, or redirect where files are written, as the common user.
|
|
24
|
+
*
|
|
25
|
+
* One cmwrapper binary is therefore compiled per wrapped program; it can NOT be
|
|
26
|
+
* symlinked under another name to reach a different program.
|
|
27
|
+
*
|
|
28
|
+
* IMPORTANT: a program wrapped by cmwrapper is callable by anybody, so it must do
|
|
29
|
+
* its own authorization (as gdexdrop does with its access list) and must confine
|
|
30
|
+
* where it writes. Do NOT wrap a general purpose program such as gdexcp.
|
|
31
|
+
*
|
|
32
|
+
* Instruction:
|
|
33
|
+
* # Compile and install bin/PROGRAM as a 4755 setuid binary owned by CommonUser:
|
|
34
|
+
* pywrapper-install -m|--cmlink PROGRAM [-n|--username CommonUser] [-e|--envhome $ENVHOME]
|
|
35
|
+
*
|
|
36
|
+
* # Compile it 755 with no setuid, to publish a program of an environment to
|
|
37
|
+
* # users who do not have that environment (-DCMSIMPLE):
|
|
38
|
+
* pywrapper-install -m|--cmlink PROGRAM -s|--simple [-d|--destdir DIR]
|
|
39
|
+
*
|
|
40
|
+
* With -DCMSIMPLE there is no privilege change, so the caller's environment is
|
|
41
|
+
* kept except for PYTHONPATH, PYTHONHOME and PYTHONSTARTUP, which would send the
|
|
42
|
+
* program to another environment's modules. Sanitizing the rest would buy no
|
|
43
|
+
* security and would only break the caller's own settings.
|
|
44
|
+
*
|
|
45
|
+
\***************************************************************************************/
|
|
46
|
+
|
|
47
|
+
#include <unistd.h>
|
|
48
|
+
#include <stdio.h>
|
|
49
|
+
#include <string.h>
|
|
50
|
+
#include <stdlib.h>
|
|
51
|
+
|
|
52
|
+
#ifndef CMPROG
|
|
53
|
+
#error "CMPROG must be defined at compile time, e.g. -DCMPROG=\"gdexdrop\""
|
|
54
|
+
#endif
|
|
55
|
+
#ifndef CMEXEC
|
|
56
|
+
#error "CMEXEC must be defined at compile time, e.g. -DCMEXEC=\"/env/bin/setuid_gdexdrop\""
|
|
57
|
+
#endif
|
|
58
|
+
|
|
59
|
+
#ifdef CMSIMPLE
|
|
60
|
+
|
|
61
|
+
/* Python variables of the caller that would make the wrapped program load modules
|
|
62
|
+
from somewhere other than its own environment. */
|
|
63
|
+
static const char *dropvars[] = {"PYTHONPATH", "PYTHONHOME", "PYTHONSTARTUP", NULL};
|
|
64
|
+
|
|
65
|
+
#else
|
|
66
|
+
|
|
67
|
+
#define CMPATH "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
|
|
68
|
+
|
|
69
|
+
/* Environment variables passed through from the caller. Everything else is
|
|
70
|
+
dropped; in particular every PYTHON*, LD_* and PGLOG path variable. */
|
|
71
|
+
static const char *keepvars[] = {"HOME", "USER", "LOGNAME", "TERM", "LANG", "TZ", NULL};
|
|
72
|
+
|
|
73
|
+
#endif
|
|
74
|
+
|
|
75
|
+
/* main program */
|
|
76
|
+
int main(int argc, char *argv[]) {
|
|
77
|
+
(void)argc;
|
|
78
|
+
|
|
79
|
+
#ifdef CMSIMPLE
|
|
80
|
+
|
|
81
|
+
/* No privilege change, so sanitizing the environment buys no security and can
|
|
82
|
+
only break the caller's own settings. Only the Python variables that would
|
|
83
|
+
send the program to another environment's modules are dropped. */
|
|
84
|
+
int i;
|
|
85
|
+
|
|
86
|
+
for(i = 0; dropvars[i] != NULL; i++) unsetenv(dropvars[i]);
|
|
87
|
+
argv[0] = (char *)CMPROG;
|
|
88
|
+
execv(CMEXEC, argv);
|
|
89
|
+
|
|
90
|
+
#else
|
|
91
|
+
|
|
92
|
+
char *envp[16];
|
|
93
|
+
char *value, *entry;
|
|
94
|
+
size_t len;
|
|
95
|
+
int i, n = 0;
|
|
96
|
+
|
|
97
|
+
for(i = 0; keepvars[i] != NULL; i++) {
|
|
98
|
+
value = getenv(keepvars[i]);
|
|
99
|
+
if(value == NULL) continue;
|
|
100
|
+
len = strlen(keepvars[i]) + strlen(value) + 2;
|
|
101
|
+
entry = malloc(len);
|
|
102
|
+
if(entry == NULL) {
|
|
103
|
+
perror(CMPROG ": malloc");
|
|
104
|
+
exit(1);
|
|
105
|
+
}
|
|
106
|
+
snprintf(entry, len, "%s=%s", keepvars[i], value);
|
|
107
|
+
envp[n++] = entry;
|
|
108
|
+
}
|
|
109
|
+
envp[n++] = (char *)"PATH=" CMPATH;
|
|
110
|
+
envp[n++] = (char *)"PYTHONNOUSERSITE=1"; /* ignore ~/.local site-packages */
|
|
111
|
+
envp[n] = NULL;
|
|
112
|
+
|
|
113
|
+
argv[0] = (char *)CMPROG; /* the program identifies itself by this name */
|
|
114
|
+
execve(CMEXEC, argv, envp);
|
|
115
|
+
|
|
116
|
+
#endif
|
|
117
|
+
|
|
118
|
+
perror(CMEXEC); /* exec only returns on error */
|
|
119
|
+
exit(1);
|
|
120
|
+
}
|
rda_python_setuid/install.py
CHANGED
|
@@ -32,6 +32,14 @@
|
|
|
32
32
|
# # 5. Update existing installation (recompile and reinstall all setuid binaries):
|
|
33
33
|
# pywrapper-install -u [-n gdexdata] [-e $ENVHOME]
|
|
34
34
|
#
|
|
35
|
+
# # 6. Compile a dedicated 4755 cmwrapper binary, so users outside the CommonUser
|
|
36
|
+
# # group can run one fixed program as CommonUser:
|
|
37
|
+
# pywrapper-install -m myprog [-t SCRIPT] [-d DESTDIR] [-n gdexdata] [-e $ENVHOME]
|
|
38
|
+
#
|
|
39
|
+
# # 6b. Compile it 755 with no setuid, to publish a program of this environment to
|
|
40
|
+
# # users who do not have the environment at all:
|
|
41
|
+
# pywrapper-install -m myprog -s [-t SCRIPT] [-d DESTDIR] [-e $ENVHOME]
|
|
42
|
+
#
|
|
35
43
|
# Convention for wrapped programs:
|
|
36
44
|
# The target package must register its connector entry point with a setuid_ prefix:
|
|
37
45
|
# [project.scripts]
|
|
@@ -47,6 +55,7 @@
|
|
|
47
55
|
|
|
48
56
|
import argparse
|
|
49
57
|
import os
|
|
58
|
+
import re
|
|
50
59
|
import shutil
|
|
51
60
|
import subprocess
|
|
52
61
|
import sys
|
|
@@ -57,9 +66,9 @@ def get_bindir():
|
|
|
57
66
|
return os.path.dirname(os.path.abspath(sys.executable))
|
|
58
67
|
|
|
59
68
|
|
|
60
|
-
def get_c_source():
|
|
61
|
-
"""Return path to
|
|
62
|
-
return os.path.join(os.path.dirname(__file__),
|
|
69
|
+
def get_c_source(name='pywrapper.c'):
|
|
70
|
+
"""Return path to a C source file bundled with this package."""
|
|
71
|
+
return os.path.join(os.path.dirname(__file__), name)
|
|
63
72
|
|
|
64
73
|
|
|
65
74
|
def run(cmd):
|
|
@@ -88,7 +97,7 @@ def main():
|
|
|
88
97
|
)
|
|
89
98
|
parser.add_argument(
|
|
90
99
|
'-s', '--simple', action='store_true',
|
|
91
|
-
help="Simple install:
|
|
100
|
+
help="Simple install, skipping setuid: symlink PROGRAM -> setuid_PROGRAM with -l/--link, or a 755 cmwrapper binary with -m/--cmlink"
|
|
92
101
|
)
|
|
93
102
|
group = parser.add_mutually_exclusive_group()
|
|
94
103
|
group.add_argument(
|
|
@@ -103,13 +112,25 @@ def main():
|
|
|
103
112
|
'-l', '--link', metavar='PROGRAM',
|
|
104
113
|
help="Create symlink PROGRAM -> pywrapper for running a fixed program as CommonUser (Mode 1); use 'all' to auto-link every setuid_* entry not yet linked"
|
|
105
114
|
)
|
|
115
|
+
group.add_argument(
|
|
116
|
+
'-m', '--cmlink', metavar='PROGRAM',
|
|
117
|
+
help="Compile a dedicated 4755 cmwrapper binary for PROGRAM, so users outside the CommonUser group can run it (Mode 3); add -s/--simple for a 755 binary with no setuid"
|
|
118
|
+
)
|
|
106
119
|
group.add_argument(
|
|
107
120
|
'-u', '--update', action='store_true',
|
|
108
121
|
help="Update an existing installation: recompile pywrapper and reinstall all pgstart_USER setuid binaries"
|
|
109
122
|
)
|
|
123
|
+
parser.add_argument(
|
|
124
|
+
'-t', '--target', default=None,
|
|
125
|
+
help="Absolute path of the python script a cmwrapper binary execs (default: BINDIR/setuid_PROGRAM, or BINDIR/PROGRAM with -s/--simple; use with -m/--cmlink)"
|
|
126
|
+
)
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
'-d', '--destdir', default=None,
|
|
129
|
+
help="Directory to install a cmwrapper binary into, such as a common area on everyone's PATH (default: BINDIR; use with -m/--cmlink)"
|
|
130
|
+
)
|
|
110
131
|
args = parser.parse_args()
|
|
111
132
|
|
|
112
|
-
if not (args.compile or args.pgstart or args.link or args.update):
|
|
133
|
+
if not (args.compile or args.pgstart or args.link or args.cmlink or args.update):
|
|
113
134
|
show_usage()
|
|
114
135
|
|
|
115
136
|
if args.username is None and not args.simple:
|
|
@@ -211,6 +232,45 @@ def main():
|
|
|
211
232
|
run(['sudo', '-u', args.username, 'chmod', '4750', pywrapper])
|
|
212
233
|
print("Installed: {} (setuid, owned by {})".format(pywrapper, args.username))
|
|
213
234
|
|
|
235
|
+
elif args.cmlink:
|
|
236
|
+
# Mode 3: compile a dedicated cmwrapper binary for one program and install it
|
|
237
|
+
# 4755, so users outside the CommonUser group can run it. The program path is
|
|
238
|
+
# baked in at compile time and the environment is sanitized by cmwrapper.c, so
|
|
239
|
+
# no symlink under another name can reach a different program.
|
|
240
|
+
# With -s/--simple the binary is 755 with no setuid, which publishes a program
|
|
241
|
+
# of this environment to users who do not have the environment at all.
|
|
242
|
+
if not re.match(r'^\w+$', args.cmlink):
|
|
243
|
+
print("Error: invalid program name '{}', expecting word characters only.".format(args.cmlink))
|
|
244
|
+
sys.exit(1)
|
|
245
|
+
if args.target:
|
|
246
|
+
script = args.target
|
|
247
|
+
else:
|
|
248
|
+
script = os.path.join(bindir, args.cmlink if args.simple else 'setuid_' + args.cmlink)
|
|
249
|
+
if not (os.path.isabs(script) and re.match(r'^[\w/.-]+$', script)):
|
|
250
|
+
print("Error: {} of -t/--target must be an absolute path of word characters, '/', '.' and '-'.".format(script))
|
|
251
|
+
sys.exit(1)
|
|
252
|
+
if not os.path.exists(script):
|
|
253
|
+
print("Error: {} not found. Install the package that provides it first.".format(script))
|
|
254
|
+
sys.exit(1)
|
|
255
|
+
destdir = args.destdir if args.destdir else bindir
|
|
256
|
+
target = os.path.join(destdir, args.cmlink)
|
|
257
|
+
if os.path.abspath(target) == os.path.abspath(script):
|
|
258
|
+
print("Error: the binary {} would overwrite the script it execs; give -d/--destdir or -t/--target.".format(target))
|
|
259
|
+
sys.exit(1)
|
|
260
|
+
src = get_c_source('cmwrapper.c')
|
|
261
|
+
src_dest = os.path.join(bindir, 'cmwrapper.c')
|
|
262
|
+
shutil.copy(src, src_dest)
|
|
263
|
+
print("Copied: {}".format(src_dest))
|
|
264
|
+
cmd = ['gcc', '-DCMPROG="{}"'.format(args.cmlink), '-DCMEXEC="{}"'.format(script)]
|
|
265
|
+
if args.simple: cmd.append('-DCMSIMPLE')
|
|
266
|
+
prefix = [] if args.simple else ['sudo', '-u', args.username]
|
|
267
|
+
run(prefix + cmd + ['-o', target, src_dest])
|
|
268
|
+
run(prefix + ['chmod', '755' if args.simple else '4755', target])
|
|
269
|
+
if args.simple:
|
|
270
|
+
print("Installed: {} (no setuid, execs {})".format(target, script))
|
|
271
|
+
else:
|
|
272
|
+
print("Installed: {} (setuid, owned by {}, execs {})".format(target, args.username, script))
|
|
273
|
+
|
|
214
274
|
elif args.update:
|
|
215
275
|
# Update an existing installation: recompile pywrapper and reinstall all setuid binaries
|
|
216
276
|
pgstart_files = sorted(f for f in os.listdir(bindir) if f.startswith('pgstart_'))
|
rda_python_setuid/install.usg
CHANGED
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
( -c|--compile
|
|
7
7
|
| -p|--pgstart
|
|
8
8
|
| -l|--link PROGRAM|all [-s|--simple]
|
|
9
|
+
| -m|--cmlink PROGRAM [-s|--simple] [-t|--target SCRIPT]
|
|
10
|
+
[-d|--destdir DIR]
|
|
9
11
|
| -u|--update )
|
|
10
12
|
|
|
11
13
|
Run pywrapper-install with no arguments to display this user guide. Exactly
|
|
12
|
-
one of -c/--compile, -p/--pgstart, -l/--link, or -u/--update
|
|
13
|
-
to perform an action.
|
|
14
|
+
one of -c/--compile, -p/--pgstart, -l/--link, -m/--cmlink, or -u/--update
|
|
15
|
+
must be given to perform an action.
|
|
14
16
|
|
|
15
17
|
- Option -n or --username USERNAME
|
|
16
18
|
The user name to own the setuid binary. For Mode 1 (CommonUser program),
|
|
@@ -49,6 +51,40 @@
|
|
|
49
51
|
Use 'all' instead of a program name to scan bin/ for every setuid_*
|
|
50
52
|
entry and add any missing PROGRAM -> pywrapper symlinks in one pass.
|
|
51
53
|
|
|
54
|
+
- Option -m or --cmlink PROGRAM
|
|
55
|
+
Mode 3: compile cmwrapper.c (bundled with this package) into a dedicated
|
|
56
|
+
binary named PROGRAM, owned by USER with chmod 4755 (setuid), so that
|
|
57
|
+
users who are NOT in the common user's group can run PROGRAM as
|
|
58
|
+
CommonUser. Unlike pywrapper, the script to exec is baked into the
|
|
59
|
+
binary at compile time and the environment is replaced with a fixed
|
|
60
|
+
whitelist, so the binary cannot be symlinked under another name to reach
|
|
61
|
+
a different program, and PYTHONPATH, LD_PRELOAD or the PGLOG path
|
|
62
|
+
variables cannot be used to run code as CommonUser. One binary is
|
|
63
|
+
compiled per program. Cannot be combined with the other actions.
|
|
64
|
+
|
|
65
|
+
Only wrap a program that does its own authorization and confines where
|
|
66
|
+
it writes, such as gdexdrop; never wrap a general purpose program such
|
|
67
|
+
as gdexcp, which would let any user on the machine read or overwrite
|
|
68
|
+
any file of the common user.
|
|
69
|
+
|
|
70
|
+
Add -s/--simple to compile the binary 755 with no setuid, which is how
|
|
71
|
+
a program of this environment is published to users who do not have the
|
|
72
|
+
environment at all, such as gdexls. There is no privilege change, so
|
|
73
|
+
the caller's environment is kept except for PYTHONPATH, PYTHONHOME and
|
|
74
|
+
PYTHONSTARTUP, which would send the program to another environment's
|
|
75
|
+
modules. The default -t/--target is then bin/PROGRAM, and no sudo
|
|
76
|
+
access to USER is needed.
|
|
77
|
+
|
|
78
|
+
- Option -t or --target SCRIPT (use with -m/--cmlink)
|
|
79
|
+
Absolute path of the python script the cmwrapper binary execs.
|
|
80
|
+
Defaults to bin/setuid_PROGRAM of the environment, or to bin/PROGRAM
|
|
81
|
+
with -s/--simple.
|
|
82
|
+
|
|
83
|
+
- Option -d or --destdir DIR (use with -m/--cmlink)
|
|
84
|
+
Directory to install the cmwrapper binary into, typically a common area
|
|
85
|
+
on everyone's PATH, such as /glade/u/home/gdexdata/bin. Defaults to the
|
|
86
|
+
bin/ directory of the environment.
|
|
87
|
+
|
|
52
88
|
- Option -u or --update
|
|
53
89
|
Update an existing installation. Discovers all pgstart_* and pywrapper
|
|
54
90
|
binaries already present in bin/, saves them to a temporary update_tmp/
|
|
@@ -56,11 +92,13 @@
|
|
|
56
92
|
reinstalls every pgstart_* binary from update_tmp. Use -n/--username to
|
|
57
93
|
specify the gdex common user (default: gdexdata).
|
|
58
94
|
|
|
59
|
-
- Option -s or --simple (use with -l/--link)
|
|
60
|
-
Simple install:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
95
|
+
- Option -s or --simple (use with -l/--link or -m/--cmlink)
|
|
96
|
+
Simple install: skip the setuid mechanism entirely, so that the program
|
|
97
|
+
runs as the current user with no privilege change. -n/--username is not
|
|
98
|
+
required with this option. With -l/--link, a symlink PROGRAM ->
|
|
99
|
+
setuid_PROGRAM is created for users who do not need or cannot set up the
|
|
100
|
+
setuid wrapper; with -m/--cmlink, a 755 cmwrapper binary is compiled for
|
|
101
|
+
users who do not have this environment at all.
|
|
64
102
|
|
|
65
103
|
Convention for wrapped programs:
|
|
66
104
|
Any Python package whose program is to be run via pywrapper must:
|
|
@@ -170,3 +208,11 @@
|
|
|
170
208
|
|
|
171
209
|
8. Simple install of all setuid_* entries at once:
|
|
172
210
|
pywrapper-install -l all -s
|
|
211
|
+
|
|
212
|
+
9. Let users outside the gdexdata group run gdexdrop as gdexdata, installing
|
|
213
|
+
the 4755 binary into a common area on everyone's PATH:
|
|
214
|
+
pywrapper-install -m gdexdrop -d /glade/u/home/gdexdata/bin
|
|
215
|
+
|
|
216
|
+
10. Publish gdexls of this environment to users who have no environment of
|
|
217
|
+
their own, with no setuid and no privilege change:
|
|
218
|
+
pywrapper-install -m gdexls -s -d /glade/u/apps/contrib
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rda_python_setuid
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: RDA Python Package to setuid for program executions as an effective or common user
|
|
5
5
|
Author-email: Zaihua Ji <zji@ucar.edu>
|
|
6
6
|
Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
|
|
@@ -23,7 +23,7 @@ via setuid for effective and common user names.
|
|
|
23
23
|
user, then `execv`s a Python entry point script. This allows Python programs to run
|
|
24
24
|
as a designated common user (e.g. `gdexdata`) without requiring `sudo` access.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
Three modes are supported:
|
|
27
27
|
|
|
28
28
|
- **Mode 1 (CommonUser program):** a symlink `dsarch -> pywrapper` runs `setuid_dsarch`
|
|
29
29
|
as the common user.
|
|
@@ -31,6 +31,12 @@ Two modes are supported:
|
|
|
31
31
|
runs any command as `<loginname>` via `pgstart.py`. `<loginname>` can be any
|
|
32
32
|
user that belongs to the same group as `PGLOG['COMMONUSER']`. Execution is
|
|
33
33
|
restricted to authorized callers (see `pgstart.py` below).
|
|
34
|
+
- **Mode 3 (cmwrapper, callers outside the group):** a dedicated binary compiled
|
|
35
|
+
from `cmwrapper.c` and installed `4755` runs ONE fixed program as the common
|
|
36
|
+
user for any user on the machine, including users outside the common user's
|
|
37
|
+
group (see "cmwrapper" below). With `-s|--simple` the same binary is installed
|
|
38
|
+
`755` with no setuid, which publishes ONE program of this environment to users
|
|
39
|
+
who have no environment of their own.
|
|
34
40
|
|
|
35
41
|
Two Python entry points are packaged alongside the C wrapper:
|
|
36
42
|
|
|
@@ -213,6 +219,63 @@ pywrapper-install -l|--link dsarch -s|--simple
|
|
|
213
219
|
pywrapper-install -l|--link all -s|--simple # or link all setuid_* entries at once
|
|
214
220
|
```
|
|
215
221
|
|
|
222
|
+
### cmwrapper (Mode 3, for callers outside the CommonUser group)
|
|
223
|
+
|
|
224
|
+
`pywrapper` is installed `4750`, so only members of the common user's group can
|
|
225
|
+
execute it. That group restriction is what makes it safe for `pywrapper` to pick
|
|
226
|
+
the program to run from `basename(argv[0])`: anyone who can execute it can already
|
|
227
|
+
reach every `setuid_*` entry in `bin/` just by naming the symlink differently.
|
|
228
|
+
|
|
229
|
+
`cmwrapper` is for the opposite case — letting users who are **not** in the common
|
|
230
|
+
user's group run one specific program as the common user. It is installed `4755`,
|
|
231
|
+
i.e. executable by everyone, so nothing about what it runs may come from the caller:
|
|
232
|
+
|
|
233
|
+
- the absolute path of the script to exec, and the program name, are baked into the
|
|
234
|
+
binary at compile time, so it cannot be symlinked under another name to reach a
|
|
235
|
+
different program;
|
|
236
|
+
- the environment is replaced with a fixed whitelist (`HOME`, `USER`, `LOGNAME`,
|
|
237
|
+
`TERM`, `LANG`, `TZ`, a fixed `PATH` and `PYTHONNOUSERSITE=1`), so `PYTHONPATH`,
|
|
238
|
+
`PYTHONHOME`, `LD_PRELOAD`, `LD_LIBRARY_PATH` and the `PGLOG` path variables
|
|
239
|
+
(`DSDHOME`, `DSSHOME`, `LOGPATH`, `COMMONUSER`, ...) cannot be used to run
|
|
240
|
+
arbitrary code, or redirect where files are written, as the common user.
|
|
241
|
+
|
|
242
|
+
One binary is compiled per wrapped program:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Install bin/gdexdrop as a 4755 binary that execs bin/setuid_gdexdrop:
|
|
246
|
+
pywrapper-install -m|--cmlink gdexdrop
|
|
247
|
+
|
|
248
|
+
# Same, but installed into a common area already on everyone's PATH:
|
|
249
|
+
pywrapper-install -m|--cmlink gdexdrop -d|--destdir /glade/u/home/gdexdata/bin
|
|
250
|
+
|
|
251
|
+
# Point it at a script somewhere other than bin/setuid_gdexdrop:
|
|
252
|
+
pywrapper-install -m|--cmlink gdexdrop -t|--target /path/to/setuid_gdexdrop
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Only wrap a program that does its own authorization and confines where it writes,
|
|
256
|
+
such as `gdexdrop`, which checks its caller against an access list and copies only
|
|
257
|
+
into the requested dataset directory. Never wrap a general purpose program such as
|
|
258
|
+
`gdexcp`: at `4755` that would let any user on the machine read or overwrite any
|
|
259
|
+
file of the common user.
|
|
260
|
+
|
|
261
|
+
### cmwrapper with no setuid, to publish a program of this environment
|
|
262
|
+
|
|
263
|
+
Add `-s|--simple` to compile the binary `755` with no setuid bit. Nothing runs as
|
|
264
|
+
another user; the binary exists only so that users who do not have this environment,
|
|
265
|
+
and cannot activate a venv or a conda environment, can still run one of its programs
|
|
266
|
+
by name — `gdexls`, for example:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Install /glade/u/apps/contrib/gdexls, a 755 binary that execs bin/gdexls:
|
|
270
|
+
pywrapper-install -m|--cmlink gdexls -s|--simple -d|--destdir /glade/u/apps/contrib
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Since there is no privilege change, sanitizing the environment would buy no security
|
|
274
|
+
and would only break the caller's own settings, so the caller's environment is passed
|
|
275
|
+
through except for `PYTHONPATH`, `PYTHONHOME` and `PYTHONSTARTUP`, which would send
|
|
276
|
+
the program to another environment's modules. The default `-t|--target` is
|
|
277
|
+
`bin/PROGRAM` rather than `bin/setuid_PROGRAM`, and no `sudo` access is required.
|
|
278
|
+
|
|
216
279
|
## Runtime flow
|
|
217
280
|
|
|
218
281
|
```
|
|
@@ -220,6 +283,16 @@ user runs: dsarch [args]
|
|
|
220
283
|
| (symlink -> pywrapper, setuid bit -> EUID=gdexdata)
|
|
221
284
|
pywrapper.c: execv(bin/setuid_dsarch, args)
|
|
222
285
|
setuid_dsarch: calls dsarch:main() as gdexdata
|
|
286
|
+
|
|
287
|
+
user runs: gdexdrop [args]
|
|
288
|
+
| (dedicated 4755 binary, setuid bit -> EUID=gdexdata)
|
|
289
|
+
cmwrapper.c: execve(bin/setuid_gdexdrop, args, sanitized env)
|
|
290
|
+
setuid_gdexdrop: calls gdexdrop:main() as gdexdata
|
|
291
|
+
|
|
292
|
+
user runs: gdexls [args]
|
|
293
|
+
| (dedicated 755 binary, no setuid, -DCMSIMPLE)
|
|
294
|
+
cmwrapper.c: execv(bin/gdexls, args)
|
|
295
|
+
gdexls: calls gdexls:main() as the calling user
|
|
223
296
|
```
|
|
224
297
|
|
|
225
298
|
## Github
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
rda_python_setuid/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
|
+
rda_python_setuid/cmwrapper.c,sha256=reXWGJhGBSegHw9mFXHv9L8CQa-2O8jAl_FyaxPqQJU,4721
|
|
3
|
+
rda_python_setuid/install.py,sha256=Y86vSRmxkRNLcBFM8Upz1atH9RCLGZBerWthUNQvCMc,15853
|
|
4
|
+
rda_python_setuid/install.usg,sha256=C8o3dNwAMHZo-zaJtPF-33wlnltnPG56qvKyrNR1GXs,10170
|
|
5
|
+
rda_python_setuid/pgstart.py,sha256=tYCOTJedqE5Lc6hUD2tyQparsOK9VGJ2alHHLNWqF20,3968
|
|
6
|
+
rda_python_setuid/pywrapper.c,sha256=Dp9EOFU9IShduQ4WMeFRiiIJLQvgZHsiQlc89vJgtII,4190
|
|
7
|
+
rda_python_setuid/pywrapper.py,sha256=7RHpWeyHGLP_7Vx3KJ_yy-3qYObYg_ar6meuworA0MA,3023
|
|
8
|
+
rda_python_setuid/setuid_setup.usg,sha256=nJaS08GtlCQmXiy-udp6cxCFgZZJuBaZ36DicUn5Q7I,2915
|
|
9
|
+
rda_python_setuid/setup_guide.py,sha256=thdTlYJwhID63G7G9NWte0qeXwH6ytQPLFbjesWodNQ,2158
|
|
10
|
+
rda_python_setuid-3.0.2.dist-info/licenses/LICENSE,sha256=1dck4EAQwv8QweDWCXDx-4Or0S8YwiCstaso_H57Pno,1097
|
|
11
|
+
rda_python_setuid-3.0.2.dist-info/METADATA,sha256=FsQOVUbq-3urP0OJf-dZwbjA4kIed7GbwgBTq7GNI2o,12014
|
|
12
|
+
rda_python_setuid-3.0.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
13
|
+
rda_python_setuid-3.0.2.dist-info/entry_points.txt,sha256=mWZUCa2KYzGsYVa33M7W03CY9SMsJYsywlIDF-4iMaQ,165
|
|
14
|
+
rda_python_setuid-3.0.2.dist-info/top_level.txt,sha256=ONMhKLagyTBktuz5dTyipSnRC0YhomTMs8eFRjM9kHQ,18
|
|
15
|
+
rda_python_setuid-3.0.2.dist-info/RECORD,,
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
rda_python_setuid/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
|
-
rda_python_setuid/install.py,sha256=IguehVEMgY08-mNZcDPL4sMX5_XF5sb71qwkzE-wTqU,12422
|
|
3
|
-
rda_python_setuid/install.usg,sha256=CEeRuUO4_3dzwlIk0uEbDl-6HtYNGm16pltBvNfJqkw,7397
|
|
4
|
-
rda_python_setuid/pgstart.py,sha256=tYCOTJedqE5Lc6hUD2tyQparsOK9VGJ2alHHLNWqF20,3968
|
|
5
|
-
rda_python_setuid/pywrapper.c,sha256=Dp9EOFU9IShduQ4WMeFRiiIJLQvgZHsiQlc89vJgtII,4190
|
|
6
|
-
rda_python_setuid/pywrapper.py,sha256=7RHpWeyHGLP_7Vx3KJ_yy-3qYObYg_ar6meuworA0MA,3023
|
|
7
|
-
rda_python_setuid/setuid_setup.usg,sha256=nJaS08GtlCQmXiy-udp6cxCFgZZJuBaZ36DicUn5Q7I,2915
|
|
8
|
-
rda_python_setuid/setup_guide.py,sha256=thdTlYJwhID63G7G9NWte0qeXwH6ytQPLFbjesWodNQ,2158
|
|
9
|
-
rda_python_setuid-3.0.0.dist-info/licenses/LICENSE,sha256=1dck4EAQwv8QweDWCXDx-4Or0S8YwiCstaso_H57Pno,1097
|
|
10
|
-
rda_python_setuid-3.0.0.dist-info/METADATA,sha256=JTvNrLDE7D3GZV1-Rr2ElyksAB8Bv0FmSVgsazKwnR0,8184
|
|
11
|
-
rda_python_setuid-3.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
12
|
-
rda_python_setuid-3.0.0.dist-info/entry_points.txt,sha256=mWZUCa2KYzGsYVa33M7W03CY9SMsJYsywlIDF-4iMaQ,165
|
|
13
|
-
rda_python_setuid-3.0.0.dist-info/top_level.txt,sha256=ONMhKLagyTBktuz5dTyipSnRC0YhomTMs8eFRjM9kHQ,18
|
|
14
|
-
rda_python_setuid-3.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|