-
Notifications
You must be signed in to change notification settings - Fork 42
/
Copy pathsignals.txt
545 lines (492 loc) · 18.1 KB
/
signals.txt
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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
----------------------------------------------------------------------------
OpenBSD Security Advisory
September 15, 1997
Vulnerability in I/O Signal Handling
----------------------------------------------------------------------------
SYNOPSIS
A vulnerability discovered in the 4.4BSD kernel allows unprivileged users
to send certain signals to arbitrary processes on the system. Depending on
the operating system and targeted program, this may allow users to kill
off processes or disrupt the operation of certain programs.
----------------------------------------------------------------------------
AFFECTED SYSTEMS
This vulnerability has been tested on all available 4.4BSD-based operating
systems, including BSDI, NetBSD, OpenBSD, and FreeBSD, in their most
recent release revisions. Additionally, this problem is known to affect
SGI IRIX, and may affect other operating systems as well.
----------------------------------------------------------------------------
DETAILS
Certain programs implemented in Unix operating systems make use of a
facility called "asynchronous I/O" to handle multiple tasks
simultaneously. Asynchronous I/O allows a process to be notified whenever
it has data that needs to be read from an input source; the kernel
notifies the process using a signal.
Asynchronous I/O is enabled on a descriptor using the fcntl() system call;
a descriptor with the O_ASYNC flag set will cause a signal to be sent
whenever there is data available to be read from it. Additionally, using
the FIOASYNC ioctl(), asynchronous notification can be enabled on a
descriptor.
In cases where multiple processes are used in an application, it becomes
useful to allow a descriptor to notify other processes as well. This is
accomplished by use of another fcntl() operation, F_SETOWN, as well as an
ioctl, FIOSETOWN (certain devices also provide an interface to this
facility with the TIOCSPGRP ioctl). These operations allow a program to
set the process or process group that will receive signal notification of
pending I/O.
A lack of checking in the code that handles these operations allows a
program to specify an arbitrary process ID when using a socket or device
file descriptor. By setting the recipient of signal notification to a
process that is not owned by the program, the kernel can be tricked into
signalling arbitrary programs.
Additionally, vulnerable kernels do not keep track of the credentials
associated with a process when determining whether to send I/O signals;
because of this, it is possible to specify the PID of a process that is
owned by an attacker, and then destroy that process and wait for it's PID
to be re-used. The new process occupying that PID can then be signalled by
the attacker, regardless of it's owner.
It's important to note that operating systems that check credentials when
a program attempts to set the PID for I/O notification (thus evading part
of this vulnerability) may still be vulnerable to the latter problem
(process ID re-use), if credentials aren't checked at signal delivery
time. We recommend that concerned parties contact their operating system
vendors or support channels to verify their vulnerability status.
----------------------------------------------------------------------------
TECHNICAL DETAILS
This vulnerability exists due to a lack of credential checking in the
kernel when setting the recipient of I/O notification. BSD-based
kernels maintain this information in descriptor-specific data structures
unrelated to the process table; the vulnerability discussed here involves
sockets, which maintain the signal recipient in the per-descriptor
"socket" structure, although certain devices provide similar facilities
and vulnerabilities.
On 4.4BSD systems, the signal normally sent to inform a process of pending
I/O is SIGIO. The default disposition of the SIGIO signal is "ignore" -
thus, most processes are unaffected by this vulnerability. However,
certain programs explicitly catch SIGIO in order to use asynchronous I/O;
these programs can be disrupted by sending stray SIGIO's.
The process notification information is also used to determine the process
that receives notification of out-of-band data on a socket. The same
vulnerability applies, this time by setting the process to notify and then
sending a message with the MSG_OOB flag set; the targetted process will
receive a SIGURG signal. Certain network daemons (ftpd, for instance) can
be disrupted by being sent a stray SIGURG signal when no data is available
for reading.
The problem is more serious on vulnerable System V operating systems; in
many cases, SIGIO is the equivalent of SIGPOLL, and the default
disposition of that signal is "terminate process". On SGI's IRIX operating
system, exploitation of this vulnerability can kill any process on the
system.
In addition to being an extremely potent denial of service attack,
surgical application of this vulnerability can be used to compromise the
system - for example, a process holding a bound address (NFS port 2049,
for instance) can be killed off and it's port stolen; this can be used to
steal NFS file handles.
In addition to sockets, some devices also provide facilities for
notification of pending I/O; examples include the "log", "tun", and "bpf".
The BPF and tunnel devices are of minimal concern, as they are not
typically accessible by arbitrary users (although BPF is interesting in
that it will allow the owner of a bpf-associated descriptor to choose an
arbitrary signal to send, including SIGSTOP).
Unfortunately, the log device is normally accessible by users, and can be
used to perform the same attack as sockets allow. It's also worth noting
that the interface that allows programs to set the process to receive
notification of I/O on the log device renders the legitimate purpose of
this facility totally unreliable; unrelated programs can seize control of
the asynchronous I/O notification on the log device, causing programs that
rely on it to fail. The provided patches do not attempt to resolve this
problem.
----------------------------------------------------------------------------
RESOLUTION
This is a kernel problem that can only be fixed by patching the
problematic system code. Patches for the OpenBSD operating system are
provided in this advisory; FreeBSD is known to be working on a similar
resolution.
The provided OpenBSD patch causes the kernel to keep track of the
credentials of the process associated with an I/O object; the credentials
are checked whenever I/O notification will occur, and therefore resolve
both the F_SETOWN and PID-reuse problems. Device drivers that present an
interface to I/O notification must be modified to check credentials when
the TIOCSPGRP (or equivalent) ioctl() is used to set notificatio PID; the
OpenBSD patch resolves all currently known occurances of this in that
operating system.
----------------------------------------------------------------------------
CREDITS
This vulnerability is believed to have been discovered originally by Alan
Peakall. Documentation and testing of this problem was conducted by Theo
de Raadt and the OpenBSD development team; SGI information was obtained
from Timothy Newsham.
The OpenBSD patch for this vulnerability was written in a caffeinated haze
by Theo de Raadt of the OpenBSD project.
The developers at OpenBSD would like to thank Perry Metzger for his
continuous support of their work.
----------------------------------------------------------------------------
OPENBSD PATCH
Index: sys/signalvar.h
===================================================================
RCS file: /cvs/src/sys/sys/signalvar.h,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- signalvar.h 1997/02/01 21:49:36 1.6
+++ signalvar.h 1997/08/31 20:42:01 1.7
@@ -156,6 +156,7 @@
int coredump __P((struct proc *p));
void execsigs __P((struct proc *p));
void gsignal __P((int pgid, int sig));
+void csignal __P((pid_t pgid, int signum, uid_t uid, uid_t euid));
int issignal __P((struct proc *p));
void pgsignal __P((struct pgrp *pgrp, int sig, int checkctty));
void postsig __P((int sig));
Index: sys/socketvar.h
===================================================================
RCS file: /cvs/src/sys/sys/socketvar.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- socketvar.h 1997/02/28 04:04:13 1.10
+++ socketvar.h 1997/08/31 20:42:02 1.11
@@ -71,6 +71,8 @@
short so_timeo; /* connection timeout */
u_short so_error; /* error affecting connection */
pid_t so_pgid; /* pgid for signals */
+ uid_t so_siguid; /* uid of process who set so_pgid */
+ uid_t so_sigeuid; /* euid of process who set so_pgid */
u_long so_oobmark; /* chars to oob mark */
/*
* Variables for socket buffering.
Index: kern/kern_descrip.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_descrip.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- kern_descrip.c 1997/08/21 05:17:37 1.13
+++ kern_descrip.c 1997/08/31 20:42:15 1.14
@@ -55,6 +55,7 @@
#include <sys/fcntl.h>
#include <sys/malloc.h>
#include <sys/syslog.h>
+#include <sys/ucred.h>
#include <sys/unistd.h>
#include <sys/resourcevar.h>
#include <sys/conf.h>
@@ -251,8 +252,11 @@
case F_SETOWN:
if (fp->f_type == DTYPE_SOCKET) {
- ((struct socket *)fp->f_data)->so_pgid =
- (long)SCARG(uap, arg);
+ struct socket *so = (struct socket *)fp->f_data;
+
+ so->so_pgid = (long)SCARG(uap, arg);
+ so->so_siguid = p->p_cred->p_ruid;
+ so->so_sigeuid = p->p_ucred->cr_uid;
return (0);
}
if ((long)SCARG(uap, arg) <= 0) {
Index: kern/kern_sig.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_sig.c,v
retrieving revision 1.16
retrieving revision 1.17
diff -u -r1.16 -r1.17
--- kern_sig.c 1997/02/01 21:49:41 1.16
+++ kern_sig.c 1997/08/31 20:42:18 1.17
@@ -481,6 +481,46 @@
}
}
return (nfound ? 0 : ESRCH);
+}
+
+#define CANDELIVER(uid, euid, p) \
+ (euid == 0 || \
+ (uid) == (p)->p_cred->p_ruid || \
+ (uid) == (p)->p_cred->p_svuid || \
+ (uid) == (p)->p_ucred->cr_uid || \
+ (euid) == (p)->p_cred->p_ruid || \
+ (euid) == (p)->p_cred->p_svuid || \
+ (euid) == (p)->p_ucred->cr_uid)
+
+/*
+ * Deliver signum to pgid, but first check uid/euid against each
+ * process and see if it is permitted.
+ */
+void
+csignal(pgid, signum, uid, euid)
+ pid_t pgid;
+ int signum;
+ uid_t uid, euid;
+{
+ struct pgrp *pgrp;
+ struct proc *p;
+
+ if (pgid == 0)
+ return;
+ if (pgid < 0) {
+ pgid = -pgid;
+ if ((pgrp = pgfind(pgid)) == NULL)
+ return;
+ for (p = pgrp->pg_members.lh_first; p;
+ p = p->p_pglist.le_next)
+ if (CANDELIVER(uid, euid, p))
+ psignal(p, signum);
+ } else {
+ if ((p = pfind(pgid)) == NULL)
+ return;
+ if (CANDELIVER(uid, euid, p))
+ psignal(p, signum);
+ }
}
/*
Index: kern/subr_log.c
===================================================================
RCS file: /cvs/src/sys/kern/subr_log.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- subr_log.c 1996/04/21 22:27:17 1.3
+++ subr_log.c 1997/08/31 20:42:20 1.4
@@ -60,6 +60,8 @@
int sc_state; /* see above for possibilities */
struct selinfo sc_selp; /* process waiting on select call */
int sc_pgid; /* process/group for async I/O */
+ uid_t sc_siguid; /* uid for process that set sc_pgid */
+ uid_t sc_sigeuid; /* euid for process that set sc_pgid */
} logsoftc;
int log_open; /* also used in log() */
@@ -179,17 +181,12 @@
void
logwakeup()
{
- struct proc *p;
-
if (!log_open)
return;
selwakeup(&logsoftc.sc_selp);
- if (logsoftc.sc_state & LOG_ASYNC) {
- if (logsoftc.sc_pgid < 0)
- gsignal(-logsoftc.sc_pgid, SIGIO);
- else if ((p = pfind(logsoftc.sc_pgid)) != NULL)
- psignal(p, SIGIO);
- }
+ if (logsoftc.sc_state & LOG_ASYNC)
+ csignal(logsoftc.sc_pgid, SIGIO,
+ logsoftc.sc_siguid, logsoftc.sc_sigeuid);
if (logsoftc.sc_state & LOG_RDWAIT) {
wakeup((caddr_t)msgbufp);
logsoftc.sc_state &= ~LOG_RDWAIT;
@@ -232,6 +229,8 @@
case TIOCSPGRP:
logsoftc.sc_pgid = *(int *)data;
+ logsoftc.sc_siguid = p->p_cred->p_ruid;
+ logsoftc.sc_sigeuid = p->p_ucred->cr_uid;
break;
case TIOCGPGRP:
Index: kern/sys_generic.c
===================================================================
RCS file: /cvs/src/sys/kern/sys_generic.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- sys_generic.c 1997/01/27 23:21:13 1.7
+++ sys_generic.c 1997/08/31 20:42:21 1.8
@@ -480,7 +480,11 @@
case FIOSETOWN:
tmp = *(int *)data;
if (fp->f_type == DTYPE_SOCKET) {
- ((struct socket *)fp->f_data)->so_pgid = tmp;
+ struct socket *so = (struct socket *)fp->f_data;
+
+ so->so_pgid = tmp;
+ so->so_siguid = p->p_cred->p_ruid;
+ so->so_sigeuid = p->p_ucred->cr_uid;
error = 0;
break;
}
Index: kern/sys_socket.c
===================================================================
RCS file: /cvs/src/sys/kern/sys_socket.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- sys_socket.c 1997/02/24 14:19:59 1.2
+++ sys_socket.c 1997/08/31 20:42:23 1.3
@@ -39,6 +39,7 @@
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/file.h>
+#include <sys/proc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
@@ -112,6 +113,8 @@
case SIOCSPGRP:
so->so_pgid = *(int *)data;
+ so->so_siguid = p->p_cred->p_ruid;
+ so->so_sigeuid = p->p_ucred->cr_uid;
return (0);
case SIOCGPGRP:
Index: kern/uipc_socket.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.15
retrieving revision 1.17
diff -u -r1.15 -r1.17
--- uipc_socket.c 1997/06/29 18:14:35 1.15
+++ uipc_socket.c 1997/08/31 20:42:24 1.17
@@ -1058,11 +1060,6 @@
sohasoutofband(so)
register struct socket *so;
{
- struct proc *p;
-
- if (so->so_pgid < 0)
- gsignal(-so->so_pgid, SIGURG);
- else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0)
- psignal(p, SIGURG);
+ csignal(so->so_pgid, SIGURG, so->so_siguid, so->so_sigeuid);
selwakeup(&so->so_rcv.sb_sel);
}
Index: kern/uipc_socket2.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_socket2.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- uipc_socket2.c 1997/02/21 08:45:00 1.5
+++ uipc_socket2.c 1997/08/31 20:42:26 1.6
@@ -315,20 +315,14 @@
register struct socket *so;
register struct sockbuf *sb;
{
- struct proc *p;
-
selwakeup(&sb->sb_sel);
sb->sb_flags &= ~SB_SEL;
if (sb->sb_flags & SB_WAIT) {
sb->sb_flags &= ~SB_WAIT;
wakeup((caddr_t)&sb->sb_cc);
}
- if (so->so_state & SS_ASYNC) {
- if (so->so_pgid < 0)
- gsignal(-so->so_pgid, SIGIO);
- else if (so->so_pgid > 0 && (p = pfind(so->so_pgid)) != 0)
- psignal(p, SIGIO);
- }
+ if (so->so_state & SS_ASYNC)
+ csignal(so->so_pgid, SIGIO, so->so_siguid, so->so_sigeuid);
}
/*
Index: net/bpf.c
===================================================================
RCS file: /cvs/src/sys/net/bpf.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- bpf.c 1997/03/17 16:29:37 1.9
+++ bpf.c 1997/08/31 20:42:29 1.10
@@ -522,14 +522,10 @@
bpf_wakeup(d)
register struct bpf_d *d;
{
- struct proc *p;
-
wakeup((caddr_t)d);
if (d->bd_async && d->bd_sig)
- if (d->bd_pgid > 0)
- gsignal (d->bd_pgid, d->bd_sig);
- else if ((p = pfind (-d->bd_pgid)) != NULL)
- psignal (p, d->bd_sig);
+ csignal(d->bd_pgid, d->bd_sig,
+ d->bd_siguid, d->bd_sigeuid);
#if BSD >= 199103
selwakeup(&d->bd_sel);
@@ -822,6 +818,8 @@
*/
case TIOCSPGRP: /* Process or group to send signals to */
d->bd_pgid = *(int *)addr;
+ d->bd_siguid = p->p_cred->p_ruid;
+ d->bd_sigeuid = p->p_ucred->cr_uid;
break;
case TIOCGPGRP:
Index: net/bpfdesc.h
===================================================================
RCS file: /cvs/src/sys/net/bpfdesc.h,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- bpfdesc.h 1997/02/24 13:33:56 1.2
+++ bpfdesc.h 1997/08/31 20:42:30 1.3
@@ -77,6 +77,8 @@
int bd_async; /* non-zero if packet reception should generate signal */
int bd_sig; /* signal to send upon packet reception */
pid_t bd_pgid; /* process or group id for signal */
+ uid_t bd_siguid; /* uid for process that set pgid */
+ uid_t bd_sigeuid; /* euid for process that set pgid */
#if BSD < 199103
u_char bd_selcoll; /* true if selects collide */
int bd_timedout;
Index: net/if_tun.c
===================================================================
RCS file: /cvs/src/sys/net/if_tun.c,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- if_tun.c 1997/07/29 07:18:20 1.19
+++ if_tun.c 1997/08/31 20:42:32 1.20
@@ -84,7 +84,9 @@
struct tun_softc {
u_short tun_flags; /* misc flags */
struct ifnet tun_if; /* the interface */
- int tun_pgrp; /* the process group - if any */
+ pid_t tun_pgid; /* the process group - if any */
+ uid_t tun_siguid; /* uid for process that set tun_pgid */
+ uid_t tun_sigeuid; /* euid for process that set tun_pgid */
struct selinfo tun_rsel; /* read select */
struct selinfo tun_wsel; /* write select (not used) */
};
@@ -228,7 +230,7 @@
}
splx(s);
}
- tp->tun_pgrp = 0;
+ tp->tun_pgid = 0;
selwakeup(&tp->tun_rsel);
TUNDEBUG(("%s: closed\n", ifp->if_xname));
@@ -331,7 +333,6 @@
{
struct tun_softc *tp = ifp->if_softc;
struct tunnel_header *th;
- struct proc *p;
int s;
TUNDEBUG(("%s: tun_output\n", ifp->if_xname));
@@ -371,12 +372,9 @@
tp->tun_flags &= ~TUN_RWAIT;
wakeup((caddr_t)tp);
}
- if (tp->tun_flags & TUN_ASYNC && tp->tun_pgrp) {
- if (tp->tun_pgrp > 0)
- gsignal(tp->tun_pgrp, SIGIO);
- else if ((p = pfind(-tp->tun_pgrp)) != NULL)
- psignal(p, SIGIO);
- }
+ if (tp->tun_flags & TUN_ASYNC && tp->tun_pgid)
+ csignal(tp->tun_pgid, SIGIO,
+ tp->tun_siguid, tp->tun_sigeuid);
selwakeup(&tp->tun_rsel);
return 0;
}
@@ -446,10 +444,12 @@
splx(s);
break;
case TIOCSPGRP:
- tp->tun_pgrp = *(int *)data;
+ tp->tun_pgid = *(int *)data;
+ tp->tun_siguid = p->p_cred->p_ruid;
+ tp->tun_sigeuid = p->p_ucred->cr_uid;
break;
case TIOCGPGRP:
- *(int *)data = tp->tun_pgrp;
+ *(int *)data = tp->tun_pgid;
break;
default:
splx(s);