rda-python-setuid 3.0.1__tar.gz → 3.0.3__tar.gz

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.
Files changed (22) hide show
  1. {rda_python_setuid-3.0.1/src/rda_python_setuid.egg-info → rda_python_setuid-3.0.3}/PKG-INFO +27 -2
  2. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/README.md +26 -1
  3. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/pyproject.toml +1 -1
  4. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/cmwrapper.c +36 -1
  5. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/install.py +39 -9
  6. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/install.usg +23 -7
  7. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3/src/rda_python_setuid.egg-info}/PKG-INFO +27 -2
  8. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/LICENSE +0 -0
  9. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/MANIFEST.in +0 -0
  10. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/setup.cfg +0 -0
  11. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/__init__.py +0 -0
  12. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/pgstart.py +0 -0
  13. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/pywrapper.c +0 -0
  14. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/pywrapper.py +0 -0
  15. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/setuid_setup.usg +0 -0
  16. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid/setup_guide.py +0 -0
  17. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid.egg-info/SOURCES.txt +0 -0
  18. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid.egg-info/dependency_links.txt +0 -0
  19. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid.egg-info/entry_points.txt +0 -0
  20. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid.egg-info/requires.txt +0 -0
  21. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/src/rda_python_setuid.egg-info/top_level.txt +0 -0
  22. {rda_python_setuid-3.0.1 → rda_python_setuid-3.0.3}/tests/test_setuid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rda_python_setuid
3
- Version: 3.0.1
3
+ Version: 3.0.3
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
@@ -34,7 +34,9 @@ Three modes are supported:
34
34
  - **Mode 3 (cmwrapper, callers outside the group):** a dedicated binary compiled
35
35
  from `cmwrapper.c` and installed `4755` runs ONE fixed program as the common
36
36
  user for any user on the machine, including users outside the common user's
37
- group (see "cmwrapper" below).
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.
38
40
 
39
41
  Two Python entry points are packaged alongside the C wrapper:
40
42
 
@@ -256,6 +258,24 @@ into the requested dataset directory. Never wrap a general purpose program such
256
258
  `gdexcp`: at `4755` that would let any user on the machine read or overwrite any
257
259
  file of the common user.
258
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
+
259
279
  ## Runtime flow
260
280
 
261
281
  ```
@@ -268,6 +288,11 @@ user runs: gdexdrop [args]
268
288
  | (dedicated 4755 binary, setuid bit -> EUID=gdexdata)
269
289
  cmwrapper.c: execve(bin/setuid_gdexdrop, args, sanitized env)
270
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
271
296
  ```
272
297
 
273
298
  ## Github
@@ -18,7 +18,9 @@ Three modes are supported:
18
18
  - **Mode 3 (cmwrapper, callers outside the group):** a dedicated binary compiled
19
19
  from `cmwrapper.c` and installed `4755` runs ONE fixed program as the common
20
20
  user for any user on the machine, including users outside the common user's
21
- group (see "cmwrapper" below).
21
+ group (see "cmwrapper" below). With `-s|--simple` the same binary is installed
22
+ `755` with no setuid, which publishes ONE program of this environment to users
23
+ who have no environment of their own.
22
24
 
23
25
  Two Python entry points are packaged alongside the C wrapper:
24
26
 
@@ -240,6 +242,24 @@ into the requested dataset directory. Never wrap a general purpose program such
240
242
  `gdexcp`: at `4755` that would let any user on the machine read or overwrite any
241
243
  file of the common user.
242
244
 
245
+ ### cmwrapper with no setuid, to publish a program of this environment
246
+
247
+ Add `-s|--simple` to compile the binary `755` with no setuid bit. Nothing runs as
248
+ another user; the binary exists only so that users who do not have this environment,
249
+ and cannot activate a venv or a conda environment, can still run one of its programs
250
+ by name — `gdexls`, for example:
251
+
252
+ ```bash
253
+ # Install /glade/u/apps/contrib/gdexls, a 755 binary that execs bin/gdexls:
254
+ pywrapper-install -m|--cmlink gdexls -s|--simple -d|--destdir /glade/u/apps/contrib
255
+ ```
256
+
257
+ Since there is no privilege change, sanitizing the environment would buy no security
258
+ and would only break the caller's own settings, so the caller's environment is passed
259
+ through except for `PYTHONPATH`, `PYTHONHOME` and `PYTHONSTARTUP`, which would send
260
+ the program to another environment's modules. The default `-t|--target` is
261
+ `bin/PROGRAM` rather than `bin/setuid_PROGRAM`, and no `sudo` access is required.
262
+
243
263
  ## Runtime flow
244
264
 
245
265
  ```
@@ -252,6 +272,11 @@ user runs: gdexdrop [args]
252
272
  | (dedicated 4755 binary, setuid bit -> EUID=gdexdata)
253
273
  cmwrapper.c: execve(bin/setuid_gdexdrop, args, sanitized env)
254
274
  setuid_gdexdrop: calls gdexdrop:main() as gdexdata
275
+
276
+ user runs: gdexls [args]
277
+ | (dedicated 755 binary, no setuid, -DCMSIMPLE)
278
+ cmwrapper.c: execv(bin/gdexls, args)
279
+ gdexls: calls gdexls:main() as the calling user
255
280
  ```
256
281
 
257
282
  ## Github
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "rda_python_setuid"
9
- version = "3.0.1"
9
+ version = "3.0.3"
10
10
  authors = [
11
11
  { name="Zaihua Ji", email="zji@ucar.edu" },
12
12
  ]
@@ -33,6 +33,15 @@
33
33
  * # Compile and install bin/PROGRAM as a 4755 setuid binary owned by CommonUser:
34
34
  * pywrapper-install -m|--cmlink PROGRAM [-n|--username CommonUser] [-e|--envhome $ENVHOME]
35
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
+ *
36
45
  \***************************************************************************************/
37
46
 
38
47
  #include <unistd.h>
@@ -47,16 +56,39 @@
47
56
  #error "CMEXEC must be defined at compile time, e.g. -DCMEXEC=\"/env/bin/setuid_gdexdrop\""
48
57
  #endif
49
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
+
50
67
  #define CMPATH "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
51
68
 
52
69
  /* Environment variables passed through from the caller. Everything else is
53
70
  dropped; in particular every PYTHON*, LD_* and PGLOG path variable. */
54
71
  static const char *keepvars[] = {"HOME", "USER", "LOGNAME", "TERM", "LANG", "TZ", NULL};
55
72
 
73
+ #endif
74
+
56
75
  /* main program */
57
76
  int main(int argc, char *argv[]) {
58
77
  (void)argc;
59
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
+
60
92
  char *envp[16];
61
93
  char *value, *entry;
62
94
  size_t len;
@@ -80,6 +112,9 @@ int main(int argc, char *argv[]) {
80
112
 
81
113
  argv[0] = (char *)CMPROG; /* the program identifies itself by this name */
82
114
  execve(CMEXEC, argv, envp);
83
- perror(CMEXEC); /* execve only returns on error */
115
+
116
+ #endif
117
+
118
+ perror(CMEXEC); /* exec only returns on error */
84
119
  exit(1);
85
120
  }
@@ -36,6 +36,10 @@
36
36
  # # group can run one fixed program as CommonUser:
37
37
  # pywrapper-install -m myprog [-t SCRIPT] [-d DESTDIR] [-n gdexdata] [-e $ENVHOME]
38
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
+ #
39
43
  # Convention for wrapped programs:
40
44
  # The target package must register its connector entry point with a setuid_ prefix:
41
45
  # [project.scripts]
@@ -93,7 +97,7 @@ def main():
93
97
  )
94
98
  parser.add_argument(
95
99
  '-s', '--simple', action='store_true',
96
- help="Simple install: create symlink PROGRAM -> setuid_PROGRAM, skipping setuid (use with -l/--link)"
100
+ help="Simple install, skipping setuid: symlink PROGRAM -> setuid_PROGRAM with -l/--link, or a 755 cmwrapper binary with -m/--cmlink"
97
101
  )
98
102
  group = parser.add_mutually_exclusive_group()
99
103
  group.add_argument(
@@ -110,7 +114,7 @@ def main():
110
114
  )
111
115
  group.add_argument(
112
116
  '-m', '--cmlink', metavar='PROGRAM',
113
- help="Compile a dedicated 4755 cmwrapper binary for PROGRAM, so users outside the CommonUser group can run it (Mode 3)"
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"
114
118
  )
115
119
  group.add_argument(
116
120
  '-u', '--update', action='store_true',
@@ -118,7 +122,7 @@ def main():
118
122
  )
119
123
  parser.add_argument(
120
124
  '-t', '--target', default=None,
121
- help="Absolute path of the python script a cmwrapper binary execs (default: BINDIR/setuid_PROGRAM; use with -m/--cmlink)"
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)"
122
126
  )
123
127
  parser.add_argument(
124
128
  '-d', '--destdir', default=None,
@@ -233,10 +237,15 @@ def main():
233
237
  # 4755, so users outside the CommonUser group can run it. The program path is
234
238
  # baked in at compile time and the environment is sanitized by cmwrapper.c, so
235
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.
236
242
  if not re.match(r'^\w+$', args.cmlink):
237
243
  print("Error: invalid program name '{}', expecting word characters only.".format(args.cmlink))
238
244
  sys.exit(1)
239
- script = args.target if args.target else os.path.join(bindir, 'setuid_' + args.cmlink)
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)
240
249
  if not (os.path.isabs(script) and re.match(r'^[\w/.-]+$', script)):
241
250
  print("Error: {} of -t/--target must be an absolute path of word characters, '/', '.' and '-'.".format(script))
242
251
  sys.exit(1)
@@ -245,15 +254,36 @@ def main():
245
254
  sys.exit(1)
246
255
  destdir = args.destdir if args.destdir else bindir
247
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)
248
260
  src = get_c_source('cmwrapper.c')
249
261
  src_dest = os.path.join(bindir, 'cmwrapper.c')
250
262
  shutil.copy(src, src_dest)
251
263
  print("Copied: {}".format(src_dest))
252
- run(['sudo', '-u', args.username, 'gcc',
253
- '-DCMPROG="{}"'.format(args.cmlink), '-DCMEXEC="{}"'.format(script),
254
- '-o', target, src_dest])
255
- run(['sudo', '-u', args.username, 'chmod', '4755', target])
256
- print("Installed: {} (setuid, owned by {}, execs {})".format(target, args.username, script))
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
+ mode = '755' if args.simple else '4755'
268
+ # Compile to a temporary file and copy it onto the target, rather than letting
269
+ # gcc write the target directly: gcc recreates the file, which hands ownership
270
+ # of a binary in a shared common area to whoever recompiled it last. 'cp'
271
+ # writes through the file already there, so its owner, group and mode survive,
272
+ # and the mode is only refreshed when it is ours to change.
273
+ tmpout = target + '.new'
274
+ run(prefix + cmd + ['-o', tmpout, src_dest])
275
+ run(prefix + ['chmod', mode, tmpout])
276
+ if os.path.exists(target):
277
+ owned = not args.simple or os.stat(target).st_uid == os.geteuid()
278
+ run(prefix + ['cp', tmpout, target])
279
+ run(prefix + ['rm', '-f', tmpout])
280
+ if owned: run(prefix + ['chmod', mode, target])
281
+ else:
282
+ run(prefix + ['mv', tmpout, target])
283
+ if args.simple:
284
+ print("Installed: {} (no setuid, execs {})".format(target, script))
285
+ else:
286
+ print("Installed: {} (setuid, owned by {}, execs {})".format(target, args.username, script))
257
287
 
258
288
  elif args.update:
259
289
  # Update an existing installation: recompile pywrapper and reinstall all setuid binaries
@@ -6,7 +6,8 @@
6
6
  ( -c|--compile
7
7
  | -p|--pgstart
8
8
  | -l|--link PROGRAM|all [-s|--simple]
9
- | -m|--cmlink PROGRAM [-t|--target SCRIPT] [-d|--destdir DIR]
9
+ | -m|--cmlink PROGRAM [-s|--simple] [-t|--target SCRIPT]
10
+ [-d|--destdir DIR]
10
11
  | -u|--update )
11
12
 
12
13
  Run pywrapper-install with no arguments to display this user guide. Exactly
@@ -66,9 +67,18 @@
66
67
  as gdexcp, which would let any user on the machine read or overwrite
67
68
  any file of the common user.
68
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
+
69
78
  - Option -t or --target SCRIPT (use with -m/--cmlink)
70
79
  Absolute path of the python script the cmwrapper binary execs.
71
- Defaults to bin/setuid_PROGRAM of the environment.
80
+ Defaults to bin/setuid_PROGRAM of the environment, or to bin/PROGRAM
81
+ with -s/--simple.
72
82
 
73
83
  - Option -d or --destdir DIR (use with -m/--cmlink)
74
84
  Directory to install the cmwrapper binary into, typically a common area
@@ -82,11 +92,13 @@
82
92
  reinstalls every pgstart_* binary from update_tmp. Use -n/--username to
83
93
  specify the gdex common user (default: gdexdata).
84
94
 
85
- - Option -s or --simple (use with -l/--link)
86
- Simple install: create a symlink PROGRAM -> setuid_PROGRAM directly,
87
- skipping the setuid mechanism entirely. The program runs as the current
88
- user with no privilege change. -n/--username is not required with this option.
89
- Useful for users who do not need or cannot set up the setuid wrapper.
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.
90
102
 
91
103
  Convention for wrapped programs:
92
104
  Any Python package whose program is to be run via pywrapper must:
@@ -200,3 +212,7 @@
200
212
  9. Let users outside the gdexdata group run gdexdrop as gdexdata, installing
201
213
  the 4755 binary into a common area on everyone's PATH:
202
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.1
3
+ Version: 3.0.3
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
@@ -34,7 +34,9 @@ Three modes are supported:
34
34
  - **Mode 3 (cmwrapper, callers outside the group):** a dedicated binary compiled
35
35
  from `cmwrapper.c` and installed `4755` runs ONE fixed program as the common
36
36
  user for any user on the machine, including users outside the common user's
37
- group (see "cmwrapper" below).
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.
38
40
 
39
41
  Two Python entry points are packaged alongside the C wrapper:
40
42
 
@@ -256,6 +258,24 @@ into the requested dataset directory. Never wrap a general purpose program such
256
258
  `gdexcp`: at `4755` that would let any user on the machine read or overwrite any
257
259
  file of the common user.
258
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
+
259
279
  ## Runtime flow
260
280
 
261
281
  ```
@@ -268,6 +288,11 @@ user runs: gdexdrop [args]
268
288
  | (dedicated 4755 binary, setuid bit -> EUID=gdexdata)
269
289
  cmwrapper.c: execve(bin/setuid_gdexdrop, args, sanitized env)
270
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
271
296
  ```
272
297
 
273
298
  ## Github