]>
Commit | Line | Data |
---|---|---|
be970e4c MT |
1 | /* Copyright (C) 2005 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. | |
3 | ||
4 | The GNU C Library is free software; you can redistribute it and/or | |
5 | modify it under the terms of the GNU Lesser General Public | |
6 | License as published by the Free Software Foundation; either | |
7 | version 2.1 of the License, or (at your option) any later version. | |
8 | ||
9 | The GNU C Library is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | Lesser General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU Lesser General Public | |
15 | License along with the GNU C Library; if not, write to the Free | |
16 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
17 | 02111-1307 USA. */ | |
18 | ||
19 | /* Copyright (C) 2006-2007 Gentoo Foundation Inc. | |
20 | * License terms as above. | |
21 | * | |
22 | * Hardened Gentoo SSP handler | |
23 | * | |
24 | * An SSP failure handler that does not use functions from the rest of | |
25 | * glibc; it uses the INTERNAL_SYSCALL methods directly. This ensures | |
26 | * no possibility of recursion into the handler. | |
27 | * | |
28 | * Direct all bug reports to http://bugs.gentoo.org/ | |
29 | * | |
30 | * Re-written from the glibc-2.3 Hardened Gentoo SSP handler | |
31 | * by Kevin F. Quinn - <kevquinn[@]gentoo.org> | |
32 | * | |
33 | * The following people contributed to the glibc-2.3 Hardened | |
34 | * Gentoo SSP handler, from which this implementation draws much: | |
35 | * | |
36 | * Ned Ludd - <solar[@]gentoo.org> | |
37 | * Alexander Gabert - <pappy[@]gentoo.org> | |
38 | * The PaX Team - <pageexec[@]freemail.hu> | |
39 | * Peter S. Mazinger - <ps.m[@]gmx.net> | |
40 | * Yoann Vandoorselaere - <yoann[@]prelude-ids.org> | |
41 | * Robert Connolly - <robert[@]linuxfromscratch.org> | |
42 | * Cory Visi <cory[@]visi.name> | |
43 | * Mike Frysinger <vapier[@]gentoo.org> | |
44 | */ | |
45 | ||
46 | #include <errno.h> | |
47 | #include <stdlib.h> | |
48 | #include <unistd.h> | |
49 | #include <signal.h> | |
50 | ||
51 | #include <sys/types.h> | |
52 | ||
53 | #include <sysdep-cancel.h> | |
54 | #include <sys/syscall.h> | |
55 | #include <bp-checks.h> | |
56 | ||
57 | #include <kernel-features.h> | |
58 | ||
59 | #include <alloca.h> | |
60 | /* from sysdeps */ | |
61 | #include <socketcall.h> | |
62 | /* for the stuff in bits/socket.h */ | |
63 | #include <sys/socket.h> | |
64 | #include <sys/un.h> | |
65 | ||
66 | ||
67 | /* Sanity check on SYSCALL macro names - force compilation | |
68 | * failure if the names used here do not exist | |
69 | */ | |
70 | #if !defined __NR_socketcall && !defined __NR_socket | |
71 | # error Cannot do syscall socket or socketcall | |
72 | #endif | |
73 | #if !defined __NR_socketcall && !defined __NR_connect | |
74 | # error Cannot do syscall connect or socketcall | |
75 | #endif | |
76 | #ifndef __NR_write | |
77 | # error Cannot do syscall write | |
78 | #endif | |
79 | #ifndef __NR_close | |
80 | # error Cannot do syscall close | |
81 | #endif | |
82 | #ifndef __NR_getpid | |
83 | # error Cannot do syscall getpid | |
84 | #endif | |
85 | #ifndef __NR_kill | |
86 | # error Cannot do syscall kill | |
87 | #endif | |
88 | #ifndef __NR_exit | |
89 | # error Cannot do syscall exit | |
90 | #endif | |
91 | #ifdef SSP_SMASH_DUMPS_CORE | |
92 | # define ENABLE_SSP_SMASH_DUMPS_CORE 1 | |
93 | # if !defined _KERNEL_NSIG && !defined _NSIG | |
94 | # error No _NSIG or _KERNEL_NSIG for rt_sigaction | |
95 | # endif | |
96 | # if !defined __NR_sigaction && !defined __NR_rt_sigaction | |
97 | # error Cannot do syscall sigaction or rt_sigaction | |
98 | # endif | |
99 | /* Although rt_sigaction expects sizeof(sigset_t) - it expects the size | |
100 | * of the _kernel_ sigset_t which is not the same as the user sigset_t. | |
101 | * Most arches have this as _NSIG bits - mips has _KERNEL_NSIG bits for | |
102 | * some reason. | |
103 | */ | |
104 | # ifdef _KERNEL_NSIG | |
105 | # define _SSP_NSIG _KERNEL_NSIG | |
106 | # else | |
107 | # define _SSP_NSIG _NSIG | |
108 | # endif | |
109 | #else | |
110 | # define _SSP_NSIG 0 | |
111 | # define ENABLE_SSP_SMASH_DUMPS_CORE 0 | |
112 | #endif | |
113 | ||
114 | /* Define DO_SIGACTION - default to newer rt signal interface but | |
115 | * fallback to old as needed. | |
116 | */ | |
117 | #ifdef __NR_rt_sigaction | |
118 | # define DO_SIGACTION(signum, act, oldact) \ | |
119 | INLINE_SYSCALL(rt_sigaction, 4, signum, act, oldact, _SSP_NSIG/8) | |
120 | #else | |
121 | # define DO_SIGACTION(signum, act, oldact) \ | |
122 | INLINE_SYSCALL(sigaction, 3, signum, act, oldact) | |
123 | #endif | |
124 | ||
125 | /* Define DO_SOCKET/DO_CONNECT functions to deal with socketcall vs socket/connect */ | |
126 | #if defined(__NR_socket) && defined(__NR_connect) | |
127 | # define USE_OLD_SOCKETCALL 0 | |
128 | #else | |
129 | # define USE_OLD_SOCKETCALL 1 | |
130 | #endif | |
131 | /* stub out the __NR_'s so we can let gcc optimize away dead code */ | |
132 | #ifndef __NR_socketcall | |
133 | # define __NR_socketcall 0 | |
134 | #endif | |
135 | #ifndef __NR_socket | |
136 | # define __NR_socket 0 | |
137 | #endif | |
138 | #ifndef __NR_connect | |
139 | # define __NR_connect 0 | |
140 | #endif | |
141 | #define DO_SOCKET(result, domain, type, protocol) \ | |
142 | do { \ | |
143 | if (USE_OLD_SOCKETCALL) { \ | |
144 | socketargs[0] = domain; \ | |
145 | socketargs[1] = type; \ | |
146 | socketargs[2] = protocol; \ | |
147 | socketargs[3] = 0; \ | |
148 | result = INLINE_SYSCALL(socketcall, 2, SOCKOP_socket, socketargs); \ | |
149 | } else \ | |
150 | result = INLINE_SYSCALL(socket, 3, domain, type, protocol); \ | |
151 | } while (0) | |
152 | #define DO_CONNECT(result, sockfd, serv_addr, addrlen) \ | |
153 | do { \ | |
154 | if (USE_OLD_SOCKETCALL) { \ | |
155 | socketargs[0] = sockfd; \ | |
156 | socketargs[1] = (unsigned long int)serv_addr; \ | |
157 | socketargs[2] = addrlen; \ | |
158 | socketargs[3] = 0; \ | |
159 | result = INLINE_SYSCALL(socketcall, 2, SOCKOP_connect, socketargs); \ | |
160 | } else \ | |
161 | result = INLINE_SYSCALL(connect, 3, sockfd, serv_addr, addrlen); \ | |
162 | } while (0) | |
163 | ||
164 | #ifndef _PATH_LOG | |
165 | # define _PATH_LOG "/dev/log" | |
166 | #endif | |
167 | ||
168 | static const char path_log[] = _PATH_LOG; | |
169 | ||
170 | /* For building glibc with SSP switched on, define __progname to a | |
171 | * constant if building for the run-time loader, to avoid pulling | |
172 | * in more of libc.so into ld.so | |
173 | */ | |
174 | #ifdef IS_IN_rtld | |
175 | static char *__progname = "<rtld>"; | |
176 | #else | |
177 | extern char *__progname; | |
178 | #endif | |
179 | ||
180 | ||
181 | /* Common handler code, used by stack_chk_fail and __stack_smash_handler | |
182 | * Inlined to ensure no self-references to the handler within itself. | |
183 | * Data static to avoid putting more than necessary on the stack, | |
184 | * to aid core debugging. | |
185 | */ | |
186 | __attribute__ ((__noreturn__ , __always_inline__)) | |
187 | static inline void | |
188 | __hardened_gentoo_stack_chk_fail(char func[], int damaged) | |
189 | { | |
190 | #define MESSAGE_BUFSIZ 256 | |
191 | static pid_t pid; | |
192 | static int plen, i; | |
193 | static char message[MESSAGE_BUFSIZ]; | |
194 | static const char msg_ssa[] = ": stack smashing attack"; | |
195 | static const char msg_inf[] = " in function "; | |
196 | static const char msg_ssd[] = "*** stack smashing detected ***: "; | |
197 | static const char msg_terminated[] = " - terminated\n"; | |
198 | static const char msg_report[] = "Report to http://bugs.gentoo.org/\n"; | |
199 | static const char msg_unknown[] = "<unknown>"; | |
200 | static int log_socket, connect_result; | |
201 | static struct sockaddr_un sock; | |
202 | static unsigned long int socketargs[4]; | |
203 | ||
204 | /* Build socket address | |
205 | */ | |
206 | sock.sun_family = AF_UNIX; | |
207 | i = 0; | |
208 | while ((path_log[i] != '\0') && (i<(sizeof(sock.sun_path)-1))) { | |
209 | sock.sun_path[i] = path_log[i]; | |
210 | i++; | |
211 | } | |
212 | sock.sun_path[i] = '\0'; | |
213 | ||
214 | /* Try SOCK_DGRAM connection to syslog */ | |
215 | connect_result = -1; | |
216 | DO_SOCKET(log_socket, AF_UNIX, SOCK_DGRAM, 0); | |
217 | if (log_socket != -1) | |
218 | DO_CONNECT(connect_result, log_socket, &sock, sizeof(sock)); | |
219 | if (connect_result == -1) { | |
220 | if (log_socket != -1) | |
221 | INLINE_SYSCALL(close, 1, log_socket); | |
222 | /* Try SOCK_STREAM connection to syslog */ | |
223 | DO_SOCKET(log_socket, AF_UNIX, SOCK_STREAM, 0); | |
224 | if (log_socket != -1) | |
225 | DO_CONNECT(connect_result, log_socket, &sock, sizeof(sock)); | |
226 | } | |
227 | ||
228 | /* Build message. Messages are generated both in the old style and new style, | |
229 | * so that log watchers that are configured for the old-style message continue | |
230 | * to work. | |
231 | */ | |
232 | #define strconcat(str) \ | |
233 | {i=0; while ((str[i] != '\0') && ((i+plen)<(MESSAGE_BUFSIZ-1))) \ | |
234 | {\ | |
235 | message[plen+i]=str[i];\ | |
236 | i++;\ | |
237 | }\ | |
238 | plen+=i;} | |
239 | ||
240 | /* R.Henderson post-gcc-4 style message */ | |
241 | plen = 0; | |
242 | strconcat(msg_ssd); | |
243 | if (__progname != (char *)0) | |
244 | strconcat(__progname) | |
245 | else | |
246 | strconcat(msg_unknown); | |
247 | strconcat(msg_terminated); | |
248 | ||
249 | /* Write out error message to STDERR, to syslog if open */ | |
250 | INLINE_SYSCALL(write, 3, STDERR_FILENO, message, plen); | |
251 | if (connect_result != -1) | |
252 | INLINE_SYSCALL(write, 3, log_socket, message, plen); | |
253 | ||
254 | /* Dr. Etoh pre-gcc-4 style message */ | |
255 | plen = 0; | |
256 | if (__progname != (char *)0) | |
257 | strconcat(__progname) | |
258 | else | |
259 | strconcat(msg_unknown); | |
260 | strconcat(msg_ssa); | |
261 | strconcat(msg_inf); | |
262 | if (func != NULL) | |
263 | strconcat(func) | |
264 | else | |
265 | strconcat(msg_unknown); | |
266 | strconcat(msg_terminated); | |
267 | /* Write out error message to STDERR, to syslog if open */ | |
268 | INLINE_SYSCALL(write, 3, STDERR_FILENO, message, plen); | |
269 | if (connect_result != -1) | |
270 | INLINE_SYSCALL(write, 3, log_socket, message, plen); | |
271 | ||
272 | /* Direct reports to bugs.gentoo.org */ | |
273 | plen=0; | |
274 | strconcat(msg_report); | |
275 | message[plen++]='\0'; | |
276 | ||
277 | /* Write out error message to STDERR, to syslog if open */ | |
278 | INLINE_SYSCALL(write, 3, STDERR_FILENO, message, plen); | |
279 | if (connect_result != -1) | |
280 | INLINE_SYSCALL(write, 3, log_socket, message, plen); | |
281 | ||
282 | if (log_socket != -1) | |
283 | INLINE_SYSCALL(close, 1, log_socket); | |
284 | ||
285 | /* Suicide */ | |
286 | pid = INLINE_SYSCALL(getpid, 0); | |
287 | ||
288 | if (ENABLE_SSP_SMASH_DUMPS_CORE) { | |
289 | static struct sigaction default_abort_act; | |
290 | /* Remove any user-supplied handler for SIGABRT, before using it */ | |
291 | default_abort_act.sa_handler = SIG_DFL; | |
292 | default_abort_act.sa_sigaction = NULL; | |
293 | __sigfillset(&default_abort_act.sa_mask); | |
294 | default_abort_act.sa_flags = 0; | |
295 | if (DO_SIGACTION(SIGABRT, &default_abort_act, NULL) == 0) | |
296 | INLINE_SYSCALL(kill, 2, pid, SIGABRT); | |
297 | } | |
298 | ||
299 | /* Note; actions cannot be added to SIGKILL */ | |
300 | INLINE_SYSCALL(kill, 2, pid, SIGKILL); | |
301 | ||
302 | /* In case the kill didn't work, exit anyway | |
303 | * The loop prevents gcc thinking this routine returns | |
304 | */ | |
305 | while (1) | |
306 | INLINE_SYSCALL(exit, 0); | |
307 | } | |
308 | ||
309 | __attribute__ ((__noreturn__)) | |
310 | void __stack_chk_fail(void) | |
311 | { | |
312 | __hardened_gentoo_stack_chk_fail(NULL, 0); | |
313 | } | |
314 | ||
315 | #ifdef ENABLE_OLD_SSP_COMPAT | |
316 | __attribute__ ((__noreturn__)) | |
317 | void __stack_smash_handler(char func[], int damaged) | |
318 | { | |
319 | __hardened_gentoo_stack_chk_fail(func, damaged); | |
320 | } | |
321 | #endif |