This repository was archived by the owner on Feb 25, 2022. It is now read-only.
Repository navigation
Expand file tree
/
Copy pathptysecure.c
More file actions
240 lines (219 loc) · 6.61 KB
/
Copy pathptysecure.c
File metadata and controls
240 lines (219 loc) · 6.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <errno.h>
extern int errno;
#include "ptysecure.h"
#include "ptytty.h"
#include "config/ptymodes.h"
#include "config/ptygroup.h"
static struct stat storig;
/* must close fdm and fds upon -1 return */
int ptysecure(fdm,fds,ext,fnm,fns,flagxchown,allowinsecure)
int *fdm; /* master */
int *fds; /* slave */
char *ext; /* char array, not string */
char *fnm;
char *fns;
int flagxchown;
int allowinsecure;
{
/* XXX: check that the pathnames are secure? nah */
/* XXX: vhangup(): don't make me laugh */
/* XXX: revoke() under 4.4 */
/* XXX: ofiles... fstat... pff... */
/* XXX: opencount()---but what about unprivileged users? */
/* We have the master and slave open. */
/* Any number of other processes may have the slave open. */
/* Any number of other processes may have our pgrp. */
/* We depend on never passing the master side to another process. */
/* We depend on opens of the master side being mutually exclusive. */
/* We opened the master, so no other process has the master open. */
/* Through this routine we will never close the master. */
/* Hence no other process will have the master open while we work. */
char buf[20];
int fdp[2];
char psarg1[20];
int kidpid;
int numpsrets;
int pspid;
int flagchmodworked;
int parsingpid;
char *(psargs[3]);
int r;
if (fstat(*fds,&storig) == -1)
goto death;
flagchmodworked = 1;
if (storig.st_uid != geteuid())
flagchmodworked = 0;
if (!allowinsecure && !flagchmodworked)
goto death;
if (fchmod(*fds,0600) == -1)
{
flagchmodworked = 0;
/* XXX: warning? other action? */
}
if (!allowinsecure && !flagchmodworked)
goto pdeath;
/* All security guarantees are off unless the slave tty is now */
/* protected from open() by normal users. In other words, we require */
/* either (1) being the owner of the tty, so that the fchmod worked, */
/* or (2) being privileged and having the slave ttys be protected */
/* all the time anyway. (2) is a better situation. (1) is more */
/* realistic for pty installations under current systems. Anyway, */
/* we don't do the more powerful security tests if we can't chmod. */
/* Anyway, we depend on the slave tty now being unopenable by users. */
/* This situation will persist until we change the mode again. */
/* (In situation (2) it will always be true.) */
/* We depend on read() of the master side returning 0 or -1/EIO if */
/* nobody has the slave side open, and something else otherwise. */
close(*fds); r = read(*fdm,buf,1);
if (r > 0) { /* XXX: warning? */ goto pmdeath; }
if ((r == -1) && (errno != EIO) && (errno != EWOULDBLOCK))
{ /* XXX: warning? */ goto pmdeath; }
*fds = open(fns,O_RDWR);
if (*fds == -1) goto pmdeath;
/*
Now nobody but us has the slave side open.
Furthermore, as noted above, nobody but us has the master side open,
and nobody will be able to open either the master or the slave.
But we're still not done! A process can access a tty even if the tty
is completely protected, and even if it doesn't have a descriptor open
to the tty. (Does this sound stupid? It is.) All it has to do is open
/dev/tty, provided that it has the right controlling terminal. Before
pronouncing the tty secure we have to take care of processes with the
same ctty.
We depend on the fact that all methods of associating a process with a
tty depend on (1) having the actual tty (not just /dev/tty) open; or
(2) opening the tty. We know that nobody can have the actual tty open
from here on.
*/
if (flagchmodworked)
{
if (pipe(fdp) == -1)
goto pdeath;
switch(kidpid = fork())
{
case -1:
close(fdp[0]);
close(fdp[1]);
goto pdeath;
case 0:
/* XXX: WARNING! We invoke /bin/ps with our privileges! */
/* If we switched back to the real uid, we couldn't trust the results. */
close(*fdm); close(*fds);
close(0); if (dup(fdp[0]) != 0) exit(1);
close(1); if (dup(fdp[1]) != 1) exit(1);
close(2); if (dup(fdp[1]) != 2) exit(1);
close(fdp[0]);
close(fdp[1]);
close(0);
psargs[0] = "/bin/ps";
psargs[1] = psarg1;
psarg1[0] = 'c'; psarg1[1] = 'g'; psarg1[2] = 'a'; psarg1[3] = 'x';
psarg1[4] = 't'; psarg1[5] = ext[0]; psarg1[6] = ext[1]; psarg1[7] = 0;
psargs[2] = (char *) 0;
setreuid(getuid(),getuid()); /* XXX: do we really want this? */
execve(psargs[0],psargs,psargs + 2);
exit(1);
default:
close(fdp[1]);
}
numpsrets = 0;
parsingpid = 0;
while (read(fdp[0],buf,1) == 1)
{
if (parsingpid)
if ((buf[0] != ' ') && (10 > (unsigned long) (unsigned char) (buf[0] - '0')))
pspid = pspid * 10 + (buf[0] - '0');
else if (pspid)
{
parsingpid = 0;
/* XXX: tell user about pspid? */
if ((pspid == getpid()) || (pspid == kidpid))
--numpsrets; /*XXX*/
}
if (buf[0] == '\n')
{
parsingpid = 1;
pspid = 0;
++numpsrets;
}
}
close(fdp[0]);
wait((int *) 0); /*XXX*/
if (numpsrets != 1)
{
/* XXX: warning? */
goto pdeath;
}
}
/*
We depend on /bin/ps being set up so that numpsrets == 1 implies that
there were, at some point in time, no processes (except possibly us)
with that tty as controlling tty.
Now lots of processes could have /dev/tty open and somehow pointing to
this tty, but there are none (other than us) with the actual tty open,
or with the master open, or with this controlling tty. There's an easy
way to finish off: we simply repeat the first test!
*/
close(*fds); r = read(*fdm,buf,1);
if (r > 0) { /* XXX: warning? */ goto pmdeath; }
if ((r == -1) && (errno != EIO) && (errno != EWOULDBLOCK))
{ /* XXX: warning? */ goto pmdeath; }
*fds = open(fns,O_RDWR);
if (*fds == -1) goto pmdeath;
/*
Finally! The pseudo-tty is secure---or at least a hell of a lot more
secure than the ttys you get from any other program.
*/
if (fchmod(*fds,PTYMODE_USED) == -1)
{
/* XXX: warning? */
;
}
if (flagxchown)
{
if (fchown(*fds,getuid(),PTYGROUP) == -1)
{
/* XXX: warning? */
;
}
}
return 0;
pmdeath:
*fds = open(fns,O_RDWR);
pdeath:
if (*fds != -1)
fchmod(*fds,PTYMODE_UNUSED);
death:
close(*fds); /* *fds could be -1; that's okay */
mdeath:
*fds = open("/dev/tty",O_RDWR);
if (*fds != -1)
{
if (tty_dissoc(*fds) == -1)
;
close(*fds);
}
close(*fdm);
return -1;
}
int ptyunsecure(fdm,fds,ext)
int fdm;
int fds;
char *ext;
{
if (fchmod(fds,PTYMODE_UNUSED) == -1)
{
/* XXX: warning? */
;
}
if (getuid() != storig.st_uid)
if (fchown(fds,storig.st_uid,PTYGROUP) == -1)
{
/* XXX: warning? */
;
}
return 0;
}