rda-python-setuid 1.0.3__tar.gz → 1.0.5__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 (25) hide show
  1. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/MANIFEST.in +1 -0
  2. rda_python_setuid-1.0.5/PKG-INFO +125 -0
  3. rda_python_setuid-1.0.5/README.md +109 -0
  4. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/pyproject.toml +4 -3
  5. rda_python_setuid-1.0.5/src/rda_python_setuid/install.py +156 -0
  6. rda_python_setuid-1.0.5/src/rda_python_setuid/install.usg +132 -0
  7. rda_python_setuid-1.0.5/src/rda_python_setuid/pgstart.py +118 -0
  8. rda_python_setuid-1.0.5/src/rda_python_setuid/pywrapper.c +115 -0
  9. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/src/rda_python_setuid/pywrapper.py +29 -19
  10. rda_python_setuid-1.0.5/src/rda_python_setuid.egg-info/PKG-INFO +125 -0
  11. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/src/rda_python_setuid.egg-info/SOURCES.txt +2 -0
  12. rda_python_setuid-1.0.5/src/rda_python_setuid.egg-info/entry_points.txt +4 -0
  13. rda_python_setuid-1.0.3/PKG-INFO +0 -17
  14. rda_python_setuid-1.0.3/README.md +0 -1
  15. rda_python_setuid-1.0.3/src/rda_python_setuid/pgstart.py +0 -99
  16. rda_python_setuid-1.0.3/src/rda_python_setuid/pywrapper.c +0 -79
  17. rda_python_setuid-1.0.3/src/rda_python_setuid.egg-info/PKG-INFO +0 -17
  18. rda_python_setuid-1.0.3/src/rda_python_setuid.egg-info/entry_points.txt +0 -3
  19. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/LICENSE +0 -0
  20. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/setup.cfg +0 -0
  21. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/src/rda_python_setuid/__init__.py +0 -0
  22. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/src/rda_python_setuid.egg-info/dependency_links.txt +0 -0
  23. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/src/rda_python_setuid.egg-info/requires.txt +0 -0
  24. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/src/rda_python_setuid.egg-info/top_level.txt +0 -0
  25. {rda_python_setuid-1.0.3 → rda_python_setuid-1.0.5}/tests/test_setuid.py +0 -0
@@ -1 +1,2 @@
1
1
  include src/rda_python_setuid/pywrapper.c
2
+ include src/rda_python_setuid/install.usg
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: rda_python_setuid
3
+ Version: 1.0.5
4
+ Summary: RDA Python Package to setuid for program executions as an effective or common user
5
+ Author-email: Zaihua Ji <zji@ucar.edu>
6
+ Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: rda_python_common
15
+ Dynamic: license-file
16
+
17
+ RDA Python package, including a C code wrapper, to execute commandline applications
18
+ via setuid for effective and common user names.
19
+
20
+ ## Overview
21
+
22
+ `rda_python_setuid` provides a C binary (`pywrapper`) that acquires a setuid effective
23
+ user, then `execv`s a Python entry point script. This allows Python programs to run
24
+ as a designated common user (e.g. `gdexdata`) without requiring `sudo` access.
25
+
26
+ Two modes are supported:
27
+
28
+ - **Mode 1 (CommonUser program):** a symlink `dsarch -> pywrapper` runs `setuid_dsarch`
29
+ as the common user.
30
+ - **Mode 2 (pgstart specialist):** a copy `pgstart_zji` runs any command as specialist
31
+ `zji` via `pgstart.py`, restricted to authorized users.
32
+
33
+ ## Dependency requirement
34
+
35
+ Any Python package whose programs are to be run via the setuid mechanism must declare
36
+ `rda_python_setuid` as a dependency in its `pyproject.toml`:
37
+
38
+ ```toml
39
+ [project]
40
+ dependencies = [
41
+ "rda_python_setuid",
42
+ ...
43
+ ]
44
+ ```
45
+
46
+ It must also register each wrapped program's connector entry point with a `setuid_`
47
+ prefix:
48
+
49
+ ```toml
50
+ [project.scripts]
51
+ "setuid_dsarch" = "rda_python_dsarch.dsarch:main"
52
+ ```
53
+
54
+ `pip install` then places `setuid_dsarch` in the environment's `bin/` directory
55
+ automatically. `pywrapper-install -l/--link` locks it down to `chmod 700` so users
56
+ cannot bypass the setuid wrapper by running it directly.
57
+
58
+ ## Environment setup
59
+
60
+ ### Option A — Python venv (DECS machines)
61
+
62
+ ```bash
63
+ python3 -m venv $ENVHOME # e.g. /glade/u/home/gdexdata/gdexmsenv
64
+ source $ENVHOME/bin/activate
65
+ pip install rda_python_setuid rda_python_dsarch ...
66
+ ```
67
+
68
+ ### Option B — Conda (DAV/Casper)
69
+
70
+ ```bash
71
+ conda create -n pg-gdex python=3.10
72
+ conda activate pg-gdex
73
+ pip install rda_python_setuid rda_python_dsarch ...
74
+ ```
75
+
76
+ The conda environment is typically at `/glade/work/gdexdata/conda-envs/pg-gdex`.
77
+
78
+ ## Installation
79
+
80
+ After setting up the environment and installing packages, run `pywrapper-install`
81
+ with no arguments to display the full user guide:
82
+
83
+ ```bash
84
+ pywrapper-install
85
+ ```
86
+
87
+ ### Full setuid setup (requires sudo access to CommonUser)
88
+
89
+ ```bash
90
+ # 1. Install the target package (pulls in rda_python_setuid automatically):
91
+ pip install rda_python_dsarch
92
+
93
+ # 2. Compile pywrapper C binary (once per environment):
94
+ pywrapper-install
95
+
96
+ # 3. Wire up each program as a setuid entry:
97
+ pywrapper-install -l dsarch
98
+
99
+ # 4. Optionally, allow a specialist to run commands as themselves:
100
+ pywrapper-install -p -u zji
101
+ ```
102
+
103
+ ### Simple install (no sudo required, runs as current user)
104
+
105
+ Users who do not need the setuid mechanism can skip steps 2–4 and create a
106
+ direct symlink from `dsarch` to `setuid_dsarch`:
107
+
108
+ ```bash
109
+ pip install rda_python_dsarch
110
+ pywrapper-install -l dsarch -s
111
+ ```
112
+
113
+ ## Runtime flow
114
+
115
+ ```
116
+ user runs: dsarch [args]
117
+ | (symlink -> pywrapper, setuid bit -> EUID=gdexdata)
118
+ pywrapper.c: execv(bin/setuid_dsarch, args)
119
+ | (chmod 700, only gdexdata can exec directly)
120
+ setuid_dsarch: calls dsarch:main() as gdexdata
121
+ ```
122
+
123
+ ## Github
124
+
125
+ <https://github.com/NCAR/rda-python-setuid>
@@ -0,0 +1,109 @@
1
+ RDA Python package, including a C code wrapper, to execute commandline applications
2
+ via setuid for effective and common user names.
3
+
4
+ ## Overview
5
+
6
+ `rda_python_setuid` provides a C binary (`pywrapper`) that acquires a setuid effective
7
+ user, then `execv`s a Python entry point script. This allows Python programs to run
8
+ as a designated common user (e.g. `gdexdata`) without requiring `sudo` access.
9
+
10
+ Two modes are supported:
11
+
12
+ - **Mode 1 (CommonUser program):** a symlink `dsarch -> pywrapper` runs `setuid_dsarch`
13
+ as the common user.
14
+ - **Mode 2 (pgstart specialist):** a copy `pgstart_zji` runs any command as specialist
15
+ `zji` via `pgstart.py`, restricted to authorized users.
16
+
17
+ ## Dependency requirement
18
+
19
+ Any Python package whose programs are to be run via the setuid mechanism must declare
20
+ `rda_python_setuid` as a dependency in its `pyproject.toml`:
21
+
22
+ ```toml
23
+ [project]
24
+ dependencies = [
25
+ "rda_python_setuid",
26
+ ...
27
+ ]
28
+ ```
29
+
30
+ It must also register each wrapped program's connector entry point with a `setuid_`
31
+ prefix:
32
+
33
+ ```toml
34
+ [project.scripts]
35
+ "setuid_dsarch" = "rda_python_dsarch.dsarch:main"
36
+ ```
37
+
38
+ `pip install` then places `setuid_dsarch` in the environment's `bin/` directory
39
+ automatically. `pywrapper-install -l/--link` locks it down to `chmod 700` so users
40
+ cannot bypass the setuid wrapper by running it directly.
41
+
42
+ ## Environment setup
43
+
44
+ ### Option A — Python venv (DECS machines)
45
+
46
+ ```bash
47
+ python3 -m venv $ENVHOME # e.g. /glade/u/home/gdexdata/gdexmsenv
48
+ source $ENVHOME/bin/activate
49
+ pip install rda_python_setuid rda_python_dsarch ...
50
+ ```
51
+
52
+ ### Option B — Conda (DAV/Casper)
53
+
54
+ ```bash
55
+ conda create -n pg-gdex python=3.10
56
+ conda activate pg-gdex
57
+ pip install rda_python_setuid rda_python_dsarch ...
58
+ ```
59
+
60
+ The conda environment is typically at `/glade/work/gdexdata/conda-envs/pg-gdex`.
61
+
62
+ ## Installation
63
+
64
+ After setting up the environment and installing packages, run `pywrapper-install`
65
+ with no arguments to display the full user guide:
66
+
67
+ ```bash
68
+ pywrapper-install
69
+ ```
70
+
71
+ ### Full setuid setup (requires sudo access to CommonUser)
72
+
73
+ ```bash
74
+ # 1. Install the target package (pulls in rda_python_setuid automatically):
75
+ pip install rda_python_dsarch
76
+
77
+ # 2. Compile pywrapper C binary (once per environment):
78
+ pywrapper-install
79
+
80
+ # 3. Wire up each program as a setuid entry:
81
+ pywrapper-install -l dsarch
82
+
83
+ # 4. Optionally, allow a specialist to run commands as themselves:
84
+ pywrapper-install -p -u zji
85
+ ```
86
+
87
+ ### Simple install (no sudo required, runs as current user)
88
+
89
+ Users who do not need the setuid mechanism can skip steps 2–4 and create a
90
+ direct symlink from `dsarch` to `setuid_dsarch`:
91
+
92
+ ```bash
93
+ pip install rda_python_dsarch
94
+ pywrapper-install -l dsarch -s
95
+ ```
96
+
97
+ ## Runtime flow
98
+
99
+ ```
100
+ user runs: dsarch [args]
101
+ | (symlink -> pywrapper, setuid bit -> EUID=gdexdata)
102
+ pywrapper.c: execv(bin/setuid_dsarch, args)
103
+ | (chmod 700, only gdexdata can exec directly)
104
+ setuid_dsarch: calls dsarch:main() as gdexdata
105
+ ```
106
+
107
+ ## Github
108
+
109
+ <https://github.com/NCAR/rda-python-setuid>
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
 
7
7
  [project]
8
8
  name = "rda_python_setuid"
9
- version = "1.0.3"
9
+ version = "1.0.5"
10
10
  authors = [
11
11
  { name="Zaihua Ji", email="zji@ucar.edu" },
12
12
  ]
@@ -30,11 +30,12 @@ include-package-data = true
30
30
  where = ["src"]
31
31
 
32
32
  [tool.setuptools.package-data]
33
- "rda_python_setuid" = ["pywrapper.c"]
33
+ "rda_python_setuid" = ["pywrapper.c", "install.usg"]
34
34
 
35
35
  [project.urls]
36
36
  "Homepage" = "https://github.com/NCAR/rda-python-setuid"
37
37
 
38
38
  [project.scripts]
39
- "pywrapper.py" = "rda_python_setuid.pywrapper:main"
39
+ "setuid_pywrapper" = "rda_python_setuid.pywrapper:main"
40
40
  "pgstart.py" = "rda_python_setuid.pgstart:main"
41
+ "pywrapper-install" = "rda_python_setuid.install:main"
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ ##################################################################################
4
+ #
5
+ # Title: pywrapper-install
6
+ # Author: Zaihua Ji, zji@ucar.edu
7
+ # Date: 2025-05-11
8
+ # Purpose: Install helper for the pywrapper setuid C binary.
9
+ # Replaces the manual gcc/chmod/ln steps with a single command.
10
+ #
11
+ # Github: https://github.com/NCAR/rda-python-setuid.git
12
+ #
13
+ # Usage:
14
+ # # 1. Compile and install pywrapper (run once per environment):
15
+ # pywrapper-install [-u gdexdata] [-e $ENVHOME/bin/]
16
+ #
17
+ # # 2. Create pgstart_USER entry so USER can run commands as themselves:
18
+ # pywrapper-install -p [-u zji] [-e $ENVHOME/bin/]
19
+ #
20
+ # # 3. Create a symlink so a program runs as CommonUser via pywrapper (setuid):
21
+ # pywrapper-install -l myprog [-u gdexdata] [-e $ENVHOME/bin/]
22
+ #
23
+ # # 4. Simple install: symlink PROGRAM -> setuid_PROGRAM (no setuid, runs as current user):
24
+ # pywrapper-install -l myprog -s [-e $ENVHOME/bin/]
25
+ #
26
+ # Convention for wrapped programs:
27
+ # The target package must register its connector entry point with a setuid_ prefix:
28
+ # [project.scripts]
29
+ # "setuid_dsarch" = "rda_python_dsarch.dsarch:main"
30
+ # pip install places setuid_dsarch in the bin dir automatically.
31
+ # pywrapper-install --link dsarch will chown setuid_dsarch to CommonUser and
32
+ # chmod 700, so users cannot run it directly and must go through the setuid wrapper.
33
+ # pywrapper-install --link dsarch --simple creates dsarch -> setuid_dsarch directly,
34
+ # skipping setuid; the program runs as the current user.
35
+ #
36
+ ##################################################################################
37
+
38
+ import argparse
39
+ import os
40
+ import shutil
41
+ import subprocess
42
+ import sys
43
+
44
+
45
+ def get_bindir():
46
+ """Return the bin directory of the active Python environment."""
47
+ return os.path.dirname(os.path.abspath(sys.executable))
48
+
49
+
50
+ def get_c_source():
51
+ """Return path to pywrapper.c bundled with this package."""
52
+ return os.path.join(os.path.dirname(__file__), 'pywrapper.c')
53
+
54
+
55
+ def run(cmd):
56
+ print(" $", " ".join(cmd))
57
+ subprocess.run(cmd, check=True)
58
+
59
+
60
+ def show_usage():
61
+ usgfile = os.path.join(os.path.dirname(__file__), 'install.usg')
62
+ os.system("more " + usgfile)
63
+ sys.exit(0)
64
+
65
+
66
+ def main():
67
+
68
+ if len(sys.argv) == 1:
69
+ show_usage()
70
+
71
+ parser = argparse.ArgumentParser(
72
+ description="Compile and install the pywrapper setuid C binary."
73
+ )
74
+ parser.add_argument(
75
+ '-e', '--envhome', default=None,
76
+ help="Path to the venv bin/ directory (default: bin/ dir of the current Python executable)"
77
+ )
78
+ parser.add_argument(
79
+ '-u', '--user', default=None,
80
+ help="User name to own the setuid binary (default: current login user for -p/--pgstart, gdexdata otherwise)"
81
+ )
82
+ parser.add_argument(
83
+ '-s', '--simple', action='store_true',
84
+ help="Simple install: create symlink PROGRAM -> setuid_PROGRAM, skipping setuid (use with -l/--link)"
85
+ )
86
+ group = parser.add_mutually_exclusive_group()
87
+ group.add_argument(
88
+ '-p', '--pgstart', action='store_true',
89
+ help="Create pgstart_USER for running commands as USER (Mode 2)"
90
+ )
91
+ group.add_argument(
92
+ '-l', '--link', metavar='PROGRAM',
93
+ help="Create symlink PROGRAM -> pywrapper for running a fixed program as CommonUser (Mode 1)"
94
+ )
95
+ args = parser.parse_args()
96
+
97
+ if args.user is None and not args.simple:
98
+ import pwd
99
+ if args.pgstart:
100
+ args.user = pwd.getpwuid(os.getuid()).pw_name
101
+ else:
102
+ args.user = 'gdexdata'
103
+
104
+ bindir = args.envhome or get_bindir()
105
+ pywrapper = os.path.join(bindir, 'pywrapper')
106
+
107
+ if args.link:
108
+ target = os.path.join(bindir, args.link)
109
+ script = os.path.join(bindir, 'setuid_{}'.format(args.link))
110
+ if not os.path.exists(script):
111
+ print("Error: {} not found. Install the package that provides it first.".format(script))
112
+ sys.exit(1)
113
+ if args.simple:
114
+ # Simple install: symlink PROGRAM -> setuid_PROGRAM, no setuid, runs as current user.
115
+ if os.path.lexists(target):
116
+ print("Already exists: {}".format(target))
117
+ else:
118
+ os.symlink(script, target)
119
+ print("Created: {} -> setuid_{}".format(target, args.link))
120
+ else:
121
+ # Mode 1: symlink PROGRAM -> pywrapper, then lock down setuid_PROGRAM
122
+ # so users cannot bypass the setuid wrapper by running it directly.
123
+ if os.path.lexists(target):
124
+ print("Already exists: {}".format(target))
125
+ else:
126
+ os.symlink(pywrapper, target)
127
+ print("Created: {} -> pywrapper".format(target))
128
+ # chown and chmod 700: only CommonUser can execute setuid_PROGRAM directly.
129
+ # pywrapper (running as CommonUser via setuid) can still execv it, but any
130
+ # direct invocation by other users will get "permission denied".
131
+ run(['sudo', '-u', args.user, 'chown', args.user, script])
132
+ run(['sudo', '-u', args.user, 'chmod', '700', script])
133
+ print("Locked: {} (chmod 700, owned by {})".format(script, args.user))
134
+
135
+ elif args.pgstart:
136
+ # Mode 2: copy pywrapper to pgstart_USER with setuid owned by USER
137
+ if not os.path.exists(pywrapper):
138
+ print("Error: {} not found. Run pywrapper-install --user COMMONUSER first.".format(pywrapper))
139
+ sys.exit(1)
140
+ target = os.path.join(bindir, 'pgstart_{}'.format(args.user))
141
+ run(['sudo', '-u', args.user, 'cp', pywrapper, target])
142
+ run(['sudo', '-u', args.user, 'chmod', '4750', target])
143
+ print("Installed: {} (setuid, owned by {})".format(target, args.user))
144
+
145
+ else:
146
+ # Default: compile pywrapper.c and install pywrapper with setuid
147
+ src = get_c_source()
148
+ src_dest = os.path.join(bindir, 'pywrapper.c')
149
+ shutil.copy(src, src_dest)
150
+ print("Copied: {}".format(src_dest))
151
+ run(['sudo', '-u', args.user, 'gcc', '-o', pywrapper, src_dest])
152
+ run(['sudo', '-u', args.user, 'chmod', '4750', pywrapper])
153
+ print("Installed: {} (setuid, owned by {})".format(pywrapper, args.user))
154
+
155
+
156
+ if __name__ == '__main__': main()
@@ -0,0 +1,132 @@
1
+ Compile and install the pywrapper setuid C binary, and set up program entries
2
+ that execute Python scripts as a common or effective user via the setuid mechanism.
3
+ Must be run inside the target Python virtual environment.
4
+
5
+ Usage: pywrapper-install [-u USER] [-e BINDIR] [-p | -l PROGRAM [-s]]
6
+
7
+ - Option -u or --user USER
8
+ The user name to own the setuid binary. For Mode 1 (CommonUser program),
9
+ this is the common user (e.g. gdexdata). For Mode 2 (pgstart), this is
10
+ the specialist user (e.g. zji). Defaults to 'gdexdata' unless -p/--pgstart
11
+ is given, in which case it defaults to the current login user.
12
+
13
+ - Option -e or --envhome BINDIR
14
+ Path to the venv bin/ directory. Defaults to the bin/ directory of the
15
+ currently active Python executable. Only needed when installing into an
16
+ environment other than the one currently active.
17
+
18
+ - Option -p or --pgstart
19
+ Mode 2: copy the compiled pywrapper binary to pgstart_USER (owned by USER,
20
+ chmod 4750). Allows USER to run arbitrary commands as themselves via the
21
+ setuid wrapper. Cannot be combined with -l/--link.
22
+
23
+ - Option -l or --link PROGRAM
24
+ Mode 1: create a symlink PROGRAM -> pywrapper in the bin/ directory, then
25
+ lock down setuid_PROGRAM (chmod 700, owned by USER) so users cannot bypass
26
+ the setuid wrapper by running setuid_PROGRAM directly. Cannot be combined
27
+ with -p/--pgstart.
28
+
29
+ - Option -s or --simple (use with -l/--link)
30
+ Simple install: create a symlink PROGRAM -> setuid_PROGRAM directly,
31
+ skipping the setuid mechanism entirely. The program runs as the current
32
+ user with no privilege change. -u/--user is not required with this option.
33
+ Useful for users who do not need or cannot set up the setuid wrapper.
34
+
35
+ When run without --pgstart or --link, the default action is to compile pywrapper.c
36
+ (bundled with this package) and install it as bin/pywrapper, owned by USER with
37
+ chmod 4750 (setuid). This step must be done once per environment before using
38
+ --pgstart or --link.
39
+
40
+ Convention for wrapped programs:
41
+ Any Python package whose program is to be run via pywrapper must:
42
+
43
+ 1. Declare rda_python_setuid as a dependency in pyproject.toml:
44
+
45
+ [project]
46
+ dependencies = [
47
+ "rda_python_setuid",
48
+ ...
49
+ ]
50
+
51
+ This ensures pywrapper-install is available in the environment after
52
+ pip install.
53
+
54
+ 2. Register its connector entry point with a setuid_ prefix in pyproject.toml:
55
+
56
+ [project.scripts]
57
+ "setuid_dsarch" = "rda_python_dsarch.dsarch:main"
58
+
59
+ pip install then places setuid_dsarch in the bin/ directory automatically.
60
+ Running pywrapper-install --link will lock it down (chmod 700) so that only
61
+ pywrapper (running as CommonUser via its setuid bit) can execv it.
62
+
63
+ Environment Setup:
64
+
65
+ Option A - Python venv (recommended for DECS machines):
66
+
67
+ 1. Create and activate the virtual environment:
68
+ python3 -m venv $ENVHOME
69
+ source $ENVHOME/bin/activate
70
+
71
+ 2. Install the required packages:
72
+ pip install rda_python_setuid rda_python_dsarch ...
73
+
74
+ 3. Deactivate when done:
75
+ deactivate
76
+
77
+ $ENVHOME is typically /glade/u/home/gdexdata/gdexmsenv on DECS machines.
78
+
79
+ Option B - Conda (recommended for DAV/Casper):
80
+
81
+ 1. Create and activate the conda environment:
82
+ conda create -n pg-gdex python=3.10
83
+ conda activate pg-gdex
84
+
85
+ 2. Install the required packages:
86
+ pip install rda_python_setuid rda_python_dsarch ...
87
+
88
+ The conda environment is typically located at:
89
+ /glade/work/gdexdata/conda-envs/pg-gdex
90
+
91
+ Use 'conda activate pg-gdex' to activate it before running pywrapper-install.
92
+
93
+ Setup sequence for a new environment:
94
+
95
+ Option A - Full setuid setup (requires sudo access to CommonUser):
96
+
97
+ 1. Install the target package and its dependencies:
98
+ pip install rda_python_dsarch
99
+
100
+ 2. Compile pywrapper (once per environment):
101
+ pywrapper-install
102
+
103
+ 3. Wire up each program as a setuid entry:
104
+ pywrapper-install -l dsarch
105
+
106
+ 4. Optionally, allow a specialist to run commands as themselves:
107
+ pywrapper-install -p
108
+
109
+ Option B - Simple install (no sudo required, runs as current user):
110
+
111
+ 1. Install the target package:
112
+ pip install rda_python_dsarch
113
+
114
+ 2. Create a direct symlink to the connector script:
115
+ pywrapper-install -l dsarch -s
116
+
117
+ Examples:
118
+
119
+ 1. Compile and install pywrapper for common user gdexdata (default):
120
+ pywrapper-install
121
+
122
+ 2. Same, but targeting a specific environment:
123
+ pywrapper-install -e /glade/u/home/gdexdata/gdexmsenv/bin/
124
+
125
+ 3. Wire up dsarch to run as gdexdata via pywrapper:
126
+ pywrapper-install -l dsarch
127
+
128
+ 4. Allow specialist zji to run commands as themselves via pgstart:
129
+ pywrapper-install -p -u zji
130
+
131
+ 5. Simple install of dsarch for a user who does not need setuid:
132
+ pywrapper-install -l dsarch -s
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env python3
2
+ #
3
+ ##################################################################################
4
+ #
5
+ # Title: pgstart
6
+ # Author: Zaihua Ji, zji@ucar.edu
7
+ # Date: 10/17/2020
8
+ # 2025-03-18 transferred to package rda_python_setuid from
9
+ # https://github.com/NCAR/rda-utility-programs.git
10
+ # Purpose: python script to start up an application in background
11
+ # for individual specialist
12
+ #
13
+ # Github: https://github.com/NCAR/rda-python-setuid.git
14
+ #
15
+ ##################################################################################
16
+
17
+ import os
18
+ import sys
19
+ import re
20
+ import pwd
21
+ import subprocess
22
+ from rda_python_common.pg_log import PgLOG
23
+
24
+ def main():
25
+ """Entry point for pgstart.
26
+
27
+ Checks whether the real user is authorized (must be the effective user or
28
+ the GDEX common user), then executes the supplied command as the effective
29
+ user. Supports foreground/background execution and optional working-directory
30
+ change via command-line options.
31
+
32
+ Options (prefix with '-'):
33
+ bg -- run command in background (non-blocking)
34
+ fg -- run command in foreground (default, no-op flag)
35
+ cwd -- change to the next argument as working directory before running
36
+ env -- print environment variables and exit
37
+ inc -- print sys.path include paths and exit
38
+ plg -- print PGLOG variables and exit
39
+ """
40
+ pglog = PgLOG()
41
+ permit = False
42
+ pglog.PGLOG['LOGFILE'] = "pgstart.log"
43
+ aname = PgLOG.get_command()
44
+ bckgrd = False
45
+ workdir = None
46
+ argv = sys.argv[1:]
47
+
48
+ ruid = pglog.PGLOG['RUID']
49
+ euid = pglog.PGLOG['EUID']
50
+ ruser = pwd.getpwuid(ruid).pw_name
51
+ euser = pwd.getpwuid(euid).pw_name
52
+ if ruser == euser or ruser == pglog.PGLOG['GDEXUSER']: permit = True
53
+ pglog.set_suid(euid)
54
+
55
+ while argv:
56
+ ms = re.match(r'^-(\w+)$', argv[0])
57
+ if not ms: break
58
+ argv.pop(0)
59
+ opt = ms.group(1)
60
+ if opt == "bg":
61
+ bckgrd = True
62
+ elif opt == "cwd":
63
+ if argv: workdir = argv.pop(0)
64
+ elif opt != "fg":
65
+ display_message(pglog, opt)
66
+
67
+ if not (permit and argv):
68
+ print("********************************************************************")
69
+ print("* Your Login Name is {}({}) & Effective User Name is {}({}).".format(ruser, ruid, euser, euid))
70
+ print("* Pass a command or options -(bg|fg|cwd|env|inc|plg) to run '{}'.".format(aname))
71
+ if not permit:
72
+ print("* You must be '{}' or '{}' to execute a command as user '{}'.".format(euser, pglog.PGLOG['GDEXUSER'], euser))
73
+ print("********************************************************************")
74
+ sys.exit(0)
75
+
76
+ cmd = pglog.argv_to_string(argv)
77
+
78
+ msg = "{}-{}{}-{}".format(pglog.PGLOG['HOSTNAME'], aname, pglog.current_datetime(), pglog.PGLOG['CURUID'])
79
+ if workdir:
80
+ msg += "-" + workdir
81
+ os.chdir(workdir)
82
+
83
+ pglog.pglog("{}: {}".format(msg, cmd), PgLOG.MSGLOG)
84
+ if bckgrd:
85
+ subprocess.Popen(argv)
86
+ else:
87
+ subprocess.run(argv)
88
+ sys.exit(0)
89
+
90
+ def display_message(pglog, option):
91
+ """Print diagnostic information for the given option and exit.
92
+
93
+ Args:
94
+ pglog (PgLOG): Initialized PgLOG instance.
95
+ option (str): One of 'env' (environment variables), 'inc' (sys.path),
96
+ or 'plg' (PGLOG dict). Prints an error for unknown values.
97
+ """
98
+ if option == "env":
99
+ print("Environment Variables:")
100
+ for ename in sorted(os.environ):
101
+ print("{}: {}".format(ename, os.environ[ename]))
102
+ elif option == "inc":
103
+ print("Including Paths:")
104
+ for pname in sorted(sys.path):
105
+ print(pname)
106
+ elif option == "plg":
107
+ print("PGLOG variables:")
108
+ for vname in sorted(pglog.PGLOG):
109
+ print("{}: {}".format(vname, pglog.PGLOG[vname]))
110
+ else:
111
+ print("* {}: Unknown option".format(option))
112
+
113
+ sys.exit(0)
114
+
115
+ #
116
+ # call main() to start program
117
+ #
118
+ if __name__ == "__main__": main()
@@ -0,0 +1,115 @@
1
+ /***************************************************************************************\
2
+ *
3
+ * Title: pywrapper.c
4
+ * Author: Zaihua Ji, zji@ucar.edu
5
+ * Date: 2025-03-17
6
+ * Purpose: C wrapper to start any executable python code as an effective user
7
+ *
8
+ * Instruction:
9
+ * python -m venv $ENVHOME
10
+ * python -m pip install rda_python_setuid
11
+ *
12
+ * # Compile pywrapper and set setuid bit (owned by CommonUser):
13
+ * pywrapper-install [-u CommonUser] [-e $ENVHOME/bin]
14
+ *
15
+ * # For an existing python program, $ENVHOME/bin/setuid_CommonProgram, to execute it
16
+ * # as the common user (Mode 1 - symlink):
17
+ * pywrapper-install -l CommonProgram [-u CommonUser] [-e $ENVHOME/bin]
18
+ * CommonProgram [options]
19
+ *
20
+ * # For a specialist to run commands as themselves via pgstart (Mode 2):
21
+ * pywrapper-install -p [-u EffectUser] [-e $ENVHOME/bin]
22
+ * pgstart_EffectUser EffectProgram [options]
23
+ *
24
+ * N: python 3 release number, it is 10 for Python 3.10.12
25
+ * CommonUser: a common user login name, such as gdexdata, for GDEXMS configuration
26
+ * EffectUser: any user login name in the same group of the common user
27
+ * $ENVHOME: /glade/u/home/gdexdata/gdexmsenv (venv) on DECS machines, and
28
+ * /glade/work/gdexdata/conda-envs/pg-gdex (conda) on DAV
29
+ *
30
+ * Convention:
31
+ * Any Python package whose program is to be run via pywrapper must register
32
+ * its connector entry point with a setuid_ prefix in pyproject.toml, e.g.:
33
+ * [project.scripts]
34
+ * "setuid_dsarch" = "rda_python_dsarch.dsarch:main"
35
+ * pip install places setuid_dsarch in $ENVHOME/bin/. pywrapper locates it by
36
+ * prepending setuid_ to the invoked program name. No manual script creation needed.
37
+ * pywrapper-install -l/--link will chown setuid_dsarch to CommonUser and chmod 700,
38
+ * preventing direct execution by other users while still allowing pywrapper
39
+ * (which runs with EUID=CommonUser via the setuid bit) to execv it.
40
+ *
41
+ \***************************************************************************************/
42
+
43
+ #include <sys/types.h>
44
+ #include <sys/stat.h>
45
+ #include <unistd.h>
46
+ #include <stdio.h>
47
+ #include <string.h>
48
+ #include <stdlib.h>
49
+ #include <limits.h>
50
+ #include <libgen.h>
51
+
52
+ int is_executable(const char *filename) {
53
+ struct stat sb;
54
+
55
+ if(stat(filename, &sb) == 0 && sb.st_mode & S_IXUSR) {
56
+ return 1;
57
+ } else {
58
+ return 0;
59
+ }
60
+ }
61
+
62
+ /* main program */
63
+ int main(int argc, char *argv[]) {
64
+ char *name;
65
+ char cname[80], prog[PATH_MAX];
66
+ char exepath[PATH_MAX];
67
+ char *fpath;
68
+ ssize_t exelen;
69
+ char pgstart[] = "pgstart";
70
+ char **apntr = argv;
71
+
72
+ exelen = readlink("/proc/self/exe", exepath, sizeof(exepath) - 1);
73
+ if(exelen == -1) {
74
+ perror("readlink /proc/self/exe");
75
+ exit(1);
76
+ }
77
+ exepath[exelen] = '\0';
78
+ fpath = dirname(exepath);
79
+
80
+ name = strrchr(argv[0], '/');
81
+ strncpy(cname, (name == NULL ? argv[0] : ++name), sizeof(cname) - 1);
82
+ cname[sizeof(cname) - 1] = '\0';
83
+
84
+ if(strstr(cname, pgstart) == cname) {
85
+ if(argc == 1 || argv[1][0] == '-') {
86
+ strncpy(cname, pgstart, sizeof(cname) - 1);
87
+ } else {
88
+ argv += 1;
89
+ name = strrchr(argv[0], '/');
90
+ strncpy(cname, (name == NULL ? argv[0] : ++name), sizeof(cname) - 1);
91
+ cname[sizeof(cname) - 1] = '\0';
92
+ }
93
+ }
94
+ if(snprintf(prog, sizeof(prog), "%s/setuid_%s", fpath, cname) >= (int)sizeof(prog)) {
95
+ fprintf(stderr, "pywrapper: path too long\n");
96
+ exit(1);
97
+ }
98
+ if(is_executable(prog) == 0) {
99
+ /* fall back to cname.py for backward compatibility */
100
+ if(snprintf(prog, sizeof(prog), "%s/%s.py", fpath, cname) >= (int)sizeof(prog)) {
101
+ fprintf(stderr, "pywrapper: path too long\n");
102
+ exit(1);
103
+ }
104
+ if(is_executable(prog) == 0) {
105
+ if(snprintf(prog, sizeof(prog), "%s/pgstart.py", fpath) >= (int)sizeof(prog)) {
106
+ fprintf(stderr, "pywrapper: path too long\n");
107
+ exit(1);
108
+ }
109
+ argv = apntr;
110
+ }
111
+ }
112
+ execv(prog, argv); /* call Python script */
113
+ perror(prog); /* execv only returns on error */
114
+ exit(1);
115
+ }
@@ -17,15 +17,23 @@
17
17
  import os
18
18
  import sys
19
19
  import pwd
20
- from rda_python_common import PgLOG
20
+ from rda_python_common.pg_log import PgLOG
21
21
 
22
- #
23
- # main function to excecute this script
24
- #
25
22
  def main():
23
+ """Entry point for setuid_pywrapper (the default pywrapper.c fallback target).
24
+
25
+ When pywrapper.c cannot find a matching setuid_<program> entry point, it
26
+ falls back to executing this script. Prints UID information and setup
27
+ instructions, or dumps diagnostic information when a flag is supplied.
26
28
 
27
- PgLOG.set_suid(PgLOG.PGLOG['EUID'])
28
- inc = 1
29
+ Options:
30
+ -env -- print environment variables and exit
31
+ -inc -- print sys.path include paths and exit
32
+ -plg -- print PGLOG variables and exit
33
+ """
34
+ pglog = PgLOG()
35
+ pglog.set_suid(pglog.PGLOG['EUID'])
36
+ inc = True
29
37
  print("********************************************************************")
30
38
  argv = sys.argv[1:]
31
39
  if argv:
@@ -33,35 +41,37 @@ def main():
33
41
  print("Environment Variables:")
34
42
  for ename in sorted(os.environ):
35
43
  print("{}: {}".format(ename, os.environ[ename]))
36
- inc = 0
44
+ inc = False
37
45
  elif argv[0] == "-inc":
38
46
  print("Including Paths:")
39
47
  for pname in sorted(sys.path):
40
48
  print(pname)
41
- inc = 0
49
+ inc = False
42
50
  elif argv[0] == "-plg":
43
51
  print("PGLOG variables:")
44
- for vname in sorted(PgLOG.PGLOG):
45
- print("{}: {}".format(vname, PgLOG.PGLOG[vname]))
46
- inc = 0
52
+ for vname in sorted(pglog.PGLOG):
53
+ print("{}: {}".format(vname, pglog.PGLOG[vname]))
54
+ inc = False
47
55
  else:
48
56
  print("* {}: Unknown option".format(argv[0]))
49
57
  else:
50
- ruid = PgLOG.PGLOG['RUID']
51
- euid = PgLOG.PGLOG['EUID']
58
+ ruid = pglog.PGLOG['RUID']
59
+ euid = pglog.PGLOG['EUID']
52
60
  ruser = pwd.getpwuid(ruid).pw_name
53
61
  euser = pwd.getpwuid(euid).pw_name
54
- print("* Your Login Name is {}({}) & Effective User Name is {}({}).".format(ruser, ruid, euser,euid))
55
- print("* To wrap your python script 'yourscript.py', you install it as")
56
- print("* an executable under $ENVHOME/bin/ and create a link to 'pywrapper'")
57
- print("* under $ENVHOME/bin/ as 'ln -s pywrapper yourscript'.")
62
+ print("* Your Login Name is {}({}) & Effective User Name is {}({}).".format(ruser, ruid, euser, euid))
63
+ print("* To wrap a Python program 'yourscript', register its entry point")
64
+ print("* with a setuid_ prefix in pyproject.toml:")
65
+ print("* [project.scripts]")
66
+ print("* \"setuid_yourscript\" = \"your_package.module:main\"")
67
+ print("* Then run: pywrapper-install -l yourscript")
58
68
  if inc:
59
69
  print("* Include -env to show environment variables")
60
70
  print("* Include -inc to show included paths")
61
71
  print("* Include -plg to show PGLOG variables")
62
-
72
+
63
73
  print("********************************************************************\n")
64
-
74
+
65
75
  sys.exit(0)
66
76
 
67
77
  #
@@ -0,0 +1,125 @@
1
+ Metadata-Version: 2.4
2
+ Name: rda_python_setuid
3
+ Version: 1.0.5
4
+ Summary: RDA Python Package to setuid for program executions as an effective or common user
5
+ Author-email: Zaihua Ji <zji@ucar.edu>
6
+ Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Requires-Python: >=3.7
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: rda_python_common
15
+ Dynamic: license-file
16
+
17
+ RDA Python package, including a C code wrapper, to execute commandline applications
18
+ via setuid for effective and common user names.
19
+
20
+ ## Overview
21
+
22
+ `rda_python_setuid` provides a C binary (`pywrapper`) that acquires a setuid effective
23
+ user, then `execv`s a Python entry point script. This allows Python programs to run
24
+ as a designated common user (e.g. `gdexdata`) without requiring `sudo` access.
25
+
26
+ Two modes are supported:
27
+
28
+ - **Mode 1 (CommonUser program):** a symlink `dsarch -> pywrapper` runs `setuid_dsarch`
29
+ as the common user.
30
+ - **Mode 2 (pgstart specialist):** a copy `pgstart_zji` runs any command as specialist
31
+ `zji` via `pgstart.py`, restricted to authorized users.
32
+
33
+ ## Dependency requirement
34
+
35
+ Any Python package whose programs are to be run via the setuid mechanism must declare
36
+ `rda_python_setuid` as a dependency in its `pyproject.toml`:
37
+
38
+ ```toml
39
+ [project]
40
+ dependencies = [
41
+ "rda_python_setuid",
42
+ ...
43
+ ]
44
+ ```
45
+
46
+ It must also register each wrapped program's connector entry point with a `setuid_`
47
+ prefix:
48
+
49
+ ```toml
50
+ [project.scripts]
51
+ "setuid_dsarch" = "rda_python_dsarch.dsarch:main"
52
+ ```
53
+
54
+ `pip install` then places `setuid_dsarch` in the environment's `bin/` directory
55
+ automatically. `pywrapper-install -l/--link` locks it down to `chmod 700` so users
56
+ cannot bypass the setuid wrapper by running it directly.
57
+
58
+ ## Environment setup
59
+
60
+ ### Option A — Python venv (DECS machines)
61
+
62
+ ```bash
63
+ python3 -m venv $ENVHOME # e.g. /glade/u/home/gdexdata/gdexmsenv
64
+ source $ENVHOME/bin/activate
65
+ pip install rda_python_setuid rda_python_dsarch ...
66
+ ```
67
+
68
+ ### Option B — Conda (DAV/Casper)
69
+
70
+ ```bash
71
+ conda create -n pg-gdex python=3.10
72
+ conda activate pg-gdex
73
+ pip install rda_python_setuid rda_python_dsarch ...
74
+ ```
75
+
76
+ The conda environment is typically at `/glade/work/gdexdata/conda-envs/pg-gdex`.
77
+
78
+ ## Installation
79
+
80
+ After setting up the environment and installing packages, run `pywrapper-install`
81
+ with no arguments to display the full user guide:
82
+
83
+ ```bash
84
+ pywrapper-install
85
+ ```
86
+
87
+ ### Full setuid setup (requires sudo access to CommonUser)
88
+
89
+ ```bash
90
+ # 1. Install the target package (pulls in rda_python_setuid automatically):
91
+ pip install rda_python_dsarch
92
+
93
+ # 2. Compile pywrapper C binary (once per environment):
94
+ pywrapper-install
95
+
96
+ # 3. Wire up each program as a setuid entry:
97
+ pywrapper-install -l dsarch
98
+
99
+ # 4. Optionally, allow a specialist to run commands as themselves:
100
+ pywrapper-install -p -u zji
101
+ ```
102
+
103
+ ### Simple install (no sudo required, runs as current user)
104
+
105
+ Users who do not need the setuid mechanism can skip steps 2–4 and create a
106
+ direct symlink from `dsarch` to `setuid_dsarch`:
107
+
108
+ ```bash
109
+ pip install rda_python_dsarch
110
+ pywrapper-install -l dsarch -s
111
+ ```
112
+
113
+ ## Runtime flow
114
+
115
+ ```
116
+ user runs: dsarch [args]
117
+ | (symlink -> pywrapper, setuid bit -> EUID=gdexdata)
118
+ pywrapper.c: execv(bin/setuid_dsarch, args)
119
+ | (chmod 700, only gdexdata can exec directly)
120
+ setuid_dsarch: calls dsarch:main() as gdexdata
121
+ ```
122
+
123
+ ## Github
124
+
125
+ <https://github.com/NCAR/rda-python-setuid>
@@ -3,6 +3,8 @@ MANIFEST.in
3
3
  README.md
4
4
  pyproject.toml
5
5
  src/rda_python_setuid/__init__.py
6
+ src/rda_python_setuid/install.py
7
+ src/rda_python_setuid/install.usg
6
8
  src/rda_python_setuid/pgstart.py
7
9
  src/rda_python_setuid/pywrapper.c
8
10
  src/rda_python_setuid/pywrapper.py
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ pgstart.py = rda_python_setuid.pgstart:main
3
+ pywrapper-install = rda_python_setuid.install:main
4
+ setuid_pywrapper = rda_python_setuid.pywrapper:main
@@ -1,17 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: rda_python_setuid
3
- Version: 1.0.3
4
- Summary: RDA Python Package to setuid for program executions as an effective or common user
5
- Author-email: Zaihua Ji <zji@ucar.edu>
6
- Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
7
- Classifier: Programming Language :: Python :: 3
8
- Classifier: License :: OSI Approved :: MIT License
9
- Classifier: Operating System :: OS Independent
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Requires-Python: >=3.7
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Requires-Dist: rda_python_common
15
- Dynamic: license-file
16
-
17
- RDA python package, including a C code wrapper, to execute commandline applications via setuid for effective and common user names.
@@ -1 +0,0 @@
1
- RDA python package, including a C code wrapper, to execute commandline applications via setuid for effective and common user names.
@@ -1,99 +0,0 @@
1
- #!/usr/bin/env python3
2
- #
3
- ##################################################################################
4
- #
5
- # Title: pgstart
6
- # Author: Zaihua Ji, zji@ucar.edu
7
- # Date: 10/17/2020
8
- # 2025-03-18 transferred to package rda_python_setuid from
9
- # https://github.com/NCAR/rda-utility-programs.git
10
- # Purpose: python script to start up an application in background
11
- # for individual specialist
12
- #
13
- # Github: https://github.com/NCAR/rda-python-setuid.git
14
- #
15
- ##################################################################################
16
-
17
- import os
18
- import sys
19
- import re
20
- import pwd
21
- from rda_python_common import PgLOG
22
-
23
- #
24
- # main function to excecute this script
25
- #
26
- def main():
27
-
28
- permit = 1
29
- PgLOG.PGLOG['LOGFILE'] = "pgstart.log"
30
- aname = PgLOG.get_command()
31
- bckgrd = ""
32
- workdir = None
33
- argv = sys.argv[1:]
34
-
35
- PgLOG.set_suid(PgLOG.PGLOG['EUID'])
36
- if PgLOG.PGLOG['CURUID'] != PgLOG.PGLOG['RDAUSER'] and PgLOG.PGLOG['CURUID'] != "zji": permit = 0
37
-
38
- while argv:
39
- ms = re.match(r'^-(\w+)$', argv[0])
40
- if not ms: break
41
- argv.pop(0)
42
- opt = ms.group(1)
43
- if opt == "bg":
44
- bckgrd = " &"
45
- elif opt == "cwd":
46
- if argv: workdir = argv.pop(0)
47
- elif opt != "fg":
48
- display_message(opt)
49
-
50
- if not (permit and argv):
51
- ruid = PgLOG.PGLOG['RUID']
52
- euid = PgLOG.PGLOG['EUID']
53
- ruser = pwd.getpwuid(ruid).pw_name
54
- euser = pwd.getpwuid(euid).pw_name
55
- print("********************************************************************")
56
- print("* Your Login Name is {}({}) & Effective User Name is {}({}).".format(ruser, ruid, euser, euid))
57
- print("* Pass a command or options -(plg|env|inc) to run '{}'.".format(aname))
58
- if not permit:
59
- print("* You must be '{}' to execute a command as user '{}'.".format(PgLOG.PGLOG['RDAUSER'], euser))
60
- print("********************************************************************")
61
- sys.exit(0)
62
-
63
- cmd = PgLOG.argv_to_string(argv)
64
-
65
- msg = "{}-{}{}-{}".format(PgLOG.PGLOG['HOSTNAME'], aname, PgLOG.current_datetime(), PgLOG.PGLOG['CURUID'])
66
- if workdir:
67
- msg += "-" + workdir
68
- os.chdir(workdir)
69
-
70
- PgLOG.pglog("{}: {}".format(msg, cmd), PgLOG.MSGLOG)
71
- os.system(cmd + bckgrd)
72
- sys.exit(0)
73
-
74
- #
75
- # display message acording to the option passed in
76
- #
77
- def display_message(option):
78
-
79
- if option == "env":
80
- print("Environment Variables:")
81
- for ename in sorted(os.environ):
82
- print("{}: {}".format(ename, os.environ[ename]))
83
- elif option == "inc":
84
- print("Including Paths:")
85
- for pname in sorted(sys.path):
86
- print(pname)
87
- elif option == "plg":
88
- print("PGLOG variables:")
89
- for vname in sorted(PgLOG.PGLOG):
90
- print("{}: {}".format(vname, PgLOG.PGLOG[vname]))
91
- else:
92
- print("* {}: Unknown option".format(option))
93
-
94
- sys.exit(0)
95
-
96
- #
97
- # call main() to start program
98
- #
99
- if __name__ == "__main__": main()
@@ -1,79 +0,0 @@
1
- /***************************************************************************************\
2
- *
3
- * Title: pywrapper.c
4
- * Author: Zaihua Ji, zji@ucar.edu
5
- * Date: 2025-03-17
6
- * Purpose: C wrapper to start any executable python code as an effective user
7
- *
8
- * Instruction:
9
- * python -m venv $ENVHOME
10
- * python -m pip install rda_python_setuid
11
- * cd $ENVHOME/bin/
12
- * cp ../lib/python3.N/site-packages/rda_python_setuid/pywrapper.c ./
13
- * sudo -u CommonUser gcc -o pywrapper $ENVHOME/bin/pywrapper.c
14
- * sudo -u CommonUser chmod 4750 pywrapper
15
- *
16
- * For an existing python program, $ENVHOME/bin/CommonProgram.py, to execute it
17
- * as the common user:
18
- * sudo -u CommonUser ln -s pywrapper CommonProgram
19
- * CommonProgram [options]
20
- *
21
- * For an existing python program, $ENVHOME/bin/EffectProgram.py, to execute it
22
- * as the effective user:
23
- * sudo -u EffectUser cp pywrapper pgstart_EffectUser
24
- * sudo -u EffectUser chmod 4750 pgstart_EffectUser
25
- * pgstart_EffectUser EffectProgram [options]
26
- *
27
- * N: python 3 release number, it is 10 for Python 3.10.12
28
- * CommonUser: a common user login name, such as rdadata, for RDAMS configuration
29
- * EffectUser: any user login name in the same group of the common user
30
- * $ENVHOME: /glade/u/home/rdadata/rdamsenv (venv) on DECS machines, and
31
- * /glade/work/rdadata/conda-envs/pg-rda (conda) on DAV
32
- *
33
- \***************************************************************************************/
34
-
35
- #include <sys/types.h>
36
- #include <sys/stat.h>
37
- #include <unistd.h>
38
- #include <stdio.h>
39
- #include <string.h>
40
- #include <libgen.h>
41
-
42
- int is_executable(const char *filename) {
43
- struct stat sb;
44
-
45
- if(stat(filename, &sb) == 0 && sb.st_mode & S_IXUSR) {
46
- return 1;
47
- } else {
48
- return 0;
49
- }
50
- }
51
-
52
- /* main program */
53
- int main(int argc, char *argv[]) {
54
- char *name;
55
- char cname[80], prog[255];
56
- char file[] = __FILE__;
57
- char *fpath = dirname(file);
58
- char pgstart[] = "pgstart";
59
- char **apntr = argv;
60
-
61
- name = strrchr(argv[0], '/');
62
- strcpy(cname, (name == NULL ? argv[0] : ++name));
63
-
64
- if(strstr(cname, pgstart) == cname) {
65
- if(argc == 1 || argv[1][0] == '-') {
66
- strcpy(cname, pgstart);
67
- } else {
68
- argv += 1;
69
- name = strrchr(argv[0], '/');
70
- strcpy(cname, (name == NULL ? argv[0] : ++name));
71
- }
72
- }
73
- sprintf(prog, "%s/%s.py", fpath, cname);
74
- if(is_executable(prog) == 0) {
75
- sprintf(prog, "%s/pgstart.py", fpath);
76
- argv = apntr;
77
- }
78
- execv(prog, argv); /* call Python script */
79
- }
@@ -1,17 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: rda_python_setuid
3
- Version: 1.0.3
4
- Summary: RDA Python Package to setuid for program executions as an effective or common user
5
- Author-email: Zaihua Ji <zji@ucar.edu>
6
- Project-URL: Homepage, https://github.com/NCAR/rda-python-setuid
7
- Classifier: Programming Language :: Python :: 3
8
- Classifier: License :: OSI Approved :: MIT License
9
- Classifier: Operating System :: OS Independent
10
- Classifier: Development Status :: 5 - Production/Stable
11
- Requires-Python: >=3.7
12
- Description-Content-Type: text/markdown
13
- License-File: LICENSE
14
- Requires-Dist: rda_python_common
15
- Dynamic: license-file
16
-
17
- RDA python package, including a C code wrapper, to execute commandline applications via setuid for effective and common user names.
@@ -1,3 +0,0 @@
1
- [console_scripts]
2
- pgstart.py = rda_python_setuid.pgstart:main
3
- pywrapper.py = rda_python_setuid.pywrapper:main