Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sudo-project
GitHub Repository: sudo-project/sudo
Path: blob/main/src/preserve_fds.c
1532 views
1
/*
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2013-2015 Todd C. Miller <[email protected]>
5
*
6
* Permission to use, copy, modify, and distribute this software for any
7
* purpose with or without fee is hereby granted, provided that the above
8
* copyright notice and this permission notice appear in all copies.
9
*
10
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
*/
18
19
#include <config.h>
20
21
#include <stdlib.h>
22
#include <string.h>
23
#include <unistd.h>
24
#include <errno.h>
25
#include <fcntl.h>
26
#include <limits.h>
27
28
#include <sudo.h>
29
30
/*
31
* Add an fd to preserve.
32
*/
33
int
34
add_preserved_fd(struct preserved_fd_list *pfds, int fd)
35
{
36
struct preserved_fd *pfd, *pfd_new;
37
debug_decl(add_preserved_fd, SUDO_DEBUG_UTIL);
38
39
pfd_new = malloc(sizeof(*pfd));
40
if (pfd_new == NULL)
41
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
42
pfd_new->lowfd = fd;
43
pfd_new->highfd = fd;
44
pfd_new->flags = fcntl(fd, F_GETFD);
45
if (pfd_new->flags == -1) {
46
free(pfd_new);
47
debug_return_int(-1);
48
}
49
50
TAILQ_FOREACH(pfd, pfds, entries) {
51
if (fd == pfd->highfd) {
52
/* already preserved */
53
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
54
"fd %d already preserved", fd);
55
free(pfd_new);
56
pfd_new = NULL;
57
break;
58
}
59
if (fd < pfd->highfd) {
60
TAILQ_INSERT_BEFORE(pfd, pfd_new, entries);
61
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
62
"preserving fd %d", fd);
63
pfd_new = NULL;
64
break;
65
}
66
}
67
if (pfd_new != NULL) {
68
TAILQ_INSERT_TAIL(pfds, pfd_new, entries);
69
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
70
"preserving fd %d", fd);
71
}
72
73
debug_return_int(0);
74
}
75
76
/*
77
* Close all descriptors, startfd and higher except those listed
78
* in pfds.
79
*/
80
void
81
closefrom_except(int startfd, struct preserved_fd_list *pfds)
82
{
83
int fd, lastfd = -1;
84
struct preserved_fd *pfd, *pfd_next;
85
unsigned char *fdbits;
86
debug_decl(closefrom_except, SUDO_DEBUG_UTIL);
87
88
/* First, relocate preserved fds to be as contiguous as possible. */
89
TAILQ_FOREACH_REVERSE_SAFE(pfd, pfds, preserved_fd_list, entries, pfd_next) {
90
if (pfd->highfd < startfd)
91
continue;
92
fd = dup(pfd->highfd);
93
if (fd == -1) {
94
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
95
"dup %d", pfd->highfd);
96
if (errno == EBADF) {
97
TAILQ_REMOVE(pfds, pfd, entries);
98
continue;
99
}
100
/* NOTE: still need to adjust lastfd below with unchanged lowfd. */
101
} else if (fd < pfd->highfd) {
102
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
103
"dup %d -> %d", pfd->highfd, pfd->lowfd);
104
sudo_debug_update_fd(pfd->highfd, pfd->lowfd);
105
pfd->lowfd = fd;
106
fd = pfd->highfd;
107
}
108
if (fd != -1)
109
(void) close(fd);
110
111
if (pfd->lowfd > lastfd)
112
lastfd = pfd->lowfd; /* highest (relocated) preserved fd */
113
}
114
115
if (lastfd == -1) {
116
/* No fds to preserve. */
117
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
118
"closefrom(%d)", startfd);
119
closefrom(startfd);
120
debug_return;
121
}
122
123
/* Create bitmap of preserved (relocated) fds. */
124
fdbits = calloc((size_t)(lastfd + NBBY) / NBBY, 1);
125
if (fdbits == NULL)
126
sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
127
TAILQ_FOREACH(pfd, pfds, entries) {
128
sudo_setbit(fdbits, pfd->lowfd);
129
}
130
131
/*
132
* Close any unpreserved fds [startfd,lastfd]
133
*/
134
for (fd = startfd; fd <= lastfd; fd++) {
135
if (!sudo_isset(fdbits, fd)) {
136
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
137
"closing fd %d", fd);
138
#ifdef __APPLE__
139
/* Avoid potential libdispatch crash when we close its fds. */
140
(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
141
#else
142
(void) close(fd);
143
#endif
144
}
145
}
146
free(fdbits);
147
148
/* Let closefrom() do the rest for us. */
149
if (lastfd + 1 > startfd)
150
startfd = lastfd + 1;
151
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
152
"closefrom(%d)", startfd);
153
closefrom(startfd);
154
155
/* Restore preserved fds and set flags. */
156
TAILQ_FOREACH_REVERSE(pfd, pfds, preserved_fd_list, entries) {
157
if (pfd->lowfd != pfd->highfd) {
158
if (dup2(pfd->lowfd, pfd->highfd) == -1) {
159
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
160
"dup2(%d, %d): %s", pfd->lowfd, pfd->highfd,
161
strerror(errno));
162
} else {
163
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
164
"dup2(%d, %d)", pfd->lowfd, pfd->highfd);
165
}
166
if (fcntl(pfd->highfd, F_SETFD, pfd->flags) == -1) {
167
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
168
"fcntl(%d, F_SETFD, %d): %s", pfd->highfd,
169
pfd->flags, strerror(errno));
170
} else {
171
sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
172
"fcntl(%d, F_SETFD, %d)", pfd->highfd, pfd->flags);
173
}
174
sudo_debug_update_fd(pfd->lowfd, pfd->highfd);
175
(void) close(pfd->lowfd);
176
pfd->lowfd = pfd->highfd;
177
}
178
}
179
debug_return;
180
}
181
182
/*
183
* Parse a comma-separated list of fds and add them to preserved_fds.
184
*/
185
void
186
parse_preserved_fds(struct preserved_fd_list *pfds, const char *fdstr)
187
{
188
const char *cp = fdstr;
189
long lval;
190
char *ep;
191
debug_decl(parse_preserved_fds, SUDO_DEBUG_UTIL);
192
193
do {
194
errno = 0;
195
lval = strtol(cp, &ep, 10);
196
if (ep == cp || (*ep != ',' && *ep != '\0')) {
197
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
198
"unable to parse fd string %s", cp);
199
break;
200
}
201
if ((errno == ERANGE && lval == LONG_MAX) || lval < 0 || lval > INT_MAX) {
202
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
203
"range error parsing fd string %s", cp);
204
} else {
205
add_preserved_fd(pfds, (int)lval);
206
}
207
cp = ep + 1;
208
} while (*ep != '\0');
209
210
debug_return;
211
}
212
213