C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
rsh.c
Go to the documentation of this file.
1/** @file rsh.c
2 @brief restricted shell to run bash as root
3 @author Bill Waller
4 Copyright (c) 2025
5 MIT License
6 billxwaller@gmail.com
7 @date 2026-02-09
8 */
9
10#define _GNU_SOURCE
11#ifdef RSH_PAM
12#include <security/pam_appl.h>
13#include <security/pam_misc.h>
14#endif
15#include <limits.h>
16#include <stdbool.h>
17#include <stddef.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/resource.h>
22#include <sys/types.h>
23#include <sys/wait.h>
24#include <syslog.h>
25#include <termios.h>
26#include <unistd.h>
27
28#ifdef __USE_GNU_GETTEXT
29#include <libintl.h>
30#define _(String) gettext(String)
31#define gettext_noop(String) String
32#define N_(String) gettext_noop(String)
33#else
34#define _(String) (String)
35#define N_(String) String
36#define textdomain(Domain)
37#define bindtextdomain(Package, Directory)
38#endif
39
40#define HOST "localhost"
41#ifndef MAXLEN
42#define MAXLEN 256
43#endif
44
45#ifdef VERBOSE
46bool F_VERBOSE = true;
47#else
48bool F_VERBOSE = false;
49#endif
50
51#ifdef RSH_PAM
52static struct pam_conv conv = {
53 misc_conv,
54 NULL};
55#endif
56
57/** @brief Abnormal termination - print an error message and exit
58 @param e Error code
59 @param s Error message
60 @details This function prints the provided error message along with the
61 error code and its corresponding string representation, then exits the
62 program with a failure status.
63 */
64void ABEND(int e, char const *);
65
66/** @brief Main function for rsh
67 @param argc Argument count
68 @param argv Argument vector
69 @details If executed as 'rsh', this program sets the user ID and group ID to
70 0 (root) and then executes the user's default shell (or /usr/bin/bash if
71 SHELL is not set) with the provided arguments. If no arguments are given, it
72 runs the shell in interactive mode. To work properly, this program must be
73 compiled and set with the setuid bit:
74 @code
75 $ sudo -s
76 cc rsh.c -o rsh
77 sudo chown root:root rsh
78 sudo chmod 4755 rsh
79 exit
80 @endcode
81 to verify proper operation:
82 @code
83 $ rsh
84 $ whoami
85 root
86 @endcode
87 */
88int main(int argc, char **argv) {
89 char *cargv[30];
90 char exec_cmd[MAXLEN] = "/usr/bin/bash";
91 char *p;
92 int c;
93 bool ssh_login = false;
94 int status;
95 pid_t pid;
96
97#ifdef RSH_PAM
98 pam_handle_t *pamh = NULL;
99 int retval;
100 char *username = getenv("USER");
101 if (username == NULL) {
102 syslog(LOG_ERR, "USER environment variable not set");
103 fprintf(stderr, "Error: USER environment variable not set\n");
104 return 1;
105 }
106 retval = pam_start("rsh-auth", username, &conv, &pamh);
107 if (retval != PAM_SUCCESS) {
108 syslog(LOG_ERR, _("PAM start failed: %s"), pam_strerror(pamh, retval));
109 fprintf(stderr, _("PAM start failed: %s\n"), pam_strerror(pamh, retval));
110 return 1;
111 }
112
113 // Authenticate the user using PAM. The configuration for this PAM service should
114 // be set to allow passwordless authentication, for example by using pam_permit.so.
115 // You can create a PAM configuration file named /etc/pam.d/pam_nopass with the
116 // following content:
117 //
118 // /etc/pam.d/pam_nopass:
119 // auth required pam_permit.so
120 //
121 //
122 retval = pam_authenticate(pamh, 0);
123 if (retval == PAM_SUCCESS) {
124 syslog(LOG_AUTH, _("Authentication succeeded for user '%s': %s"), username, pam_strerror(pamh, retval));
125 } else {
126 syslog(LOG_AUTH, _("Authentication failed for user '%s': %s"), username, pam_strerror(pamh, retval));
127 fprintf(stderr, _("Failure: Authentication failed: %s\n"), pam_strerror(pamh, retval));
128 }
129
130 // It's a wrap
131 pam_end(pamh, retval);
132
133 if (retval != PAM_SUCCESS) {
134 fprintf(stderr, _("PAM authentication failed: %s\n"), pam_strerror(pamh, retval));
135 exit(EXIT_FAILURE);
136 }
137
138#endif
139 if ((p = getenv("SHELL")))
140 strncpy(exec_cmd, p, MAXLEN - 1);
141 cargv[0] = strdup(exec_cmd);
142 c = 1;
143 if (argc > 0) {
144 for (int i = 1; i < argc; i++) {
145 if (strcmp(argv[i], "-i") == 0) {
146 cargv[c++] = strdup("-i");
147 } else if (strcmp(argv[i], "-D1") == 0 && ssh_login) {
148 fprintf(stderr, _("SSH authentication succeeded\n"));
149 } else {
150 cargv[c++] = strdup(argv[i]);
151 }
152 }
153 }
154 cargv[c] = nullptr;
155 pid = fork();
156 switch (pid) {
157 case -1:
158 ABEND(EXIT_FAILURE, _("fork() fatal error"));
159 break;
160 case 0: // Child
161 if (argv[0] && strstr(argv[0], "rsh")) {
162 if (setuid(0) || setgid(0))
163 ABEND(EXIT_FAILURE, _("setuid(0) fatal error"));
164 struct rlimit rl;
165 getrlimit(RLIMIT_FSIZE, &rl);
166 rl.rlim_cur = RLIM_INFINITY;
167 rl.rlim_max = RLIM_INFINITY;
168 setrlimit(RLIMIT_FSIZE, &rl);
169 }
170 execvp(exec_cmd, cargv);
171 ABEND(EXIT_FAILURE, _("execvp() fatal error"));
172 break;
173 default: // Parent
174 waitpid(pid, &status, 0);
175 for (int i = 0; i < c; i++)
176 free(cargv[i]);
177#ifdef F_VERBOSE
178 if (WIFEXITED(status)) {
179 rc = WEXITSTATUS(status);
180 if (rc != 0)
181 ABEND(rc, _("Child process exited"));
182 } else {
183 if (WIFSIGNALED(status)) {
184 rc = WTERMSIG(status);
185 ABEND(rc, _("Child process terminated by signal"));
186 } else
187 ABEND(EXIT_FAILURE, _("Child process terminated abnormally"));
188 }
189#endif
190 break;
191 }
192 exit(EXIT_SUCCESS);
193}
194
195void ABEND(int e, char const *s) {
196 fprintf(stderr, "%s: %d %s\n", s, e, strerror(e));
197 exit(EXIT_FAILURE);
198}
void ABEND(int)
The ABEND function is a signal handler that is called when the program receives certain signals (e....
Definition iloan.c:429
bool F_VERBOSE
Definition rsh.c:48
#define MAXLEN
Definition curskeys.c:15
#define _(String)
Definition amort.c:23
int main(int argc, char **argv)
Capture the current terminal settings for later restoration.
Definition amort.c:41