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