summaryrefslogtreecommitdiffstats
path: root/lib/clplumbing/coredumps.c
blob: 79da7372b21cd1cbeba18ff1057cce1dc51ab26a (plain)
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
/*
 * Basic Core dump control functions.
 *
 * Author:	Alan Robertson
 *
 * Copyright (C) 2004 IBM Corporation
 *
 * This software licensed under the GNU LGPL.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <lha_internal.h>

#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <fcntl.h>
#include <pwd.h>
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_SYS_PRCTL_H
#	include <sys/prctl.h>
#endif
#include <clplumbing/coredumps.h>
#include <clplumbing/cl_log.h>
#include <clplumbing/uids.h>
#include <clplumbing/cl_signal.h>

static char *	coreroot = NULL;

/* Set the root directory of our core directory hierarchy */
int
cl_set_corerootdir(const char * dir)
{
	if (dir == NULL || *dir != '/') {
		cl_perror("Invalid dir in cl_set_corerootdir() [%s]"
		,	dir ? dir : "<NULL>");
		errno = EINVAL;
		return -1;
	}
	if (coreroot != NULL) {
		free(coreroot);
		coreroot = NULL;
	}
	coreroot = strdup(dir);
	if (coreroot == NULL) {
		return -1;
	}
	return 0;
}

/*
 * Change directory to the directory our core file needs to go in
 * Call after you establish the userid you're running under.
 */
int
cl_cdtocoredir(void)
{
	const char *	dir = coreroot;
	int		rc;
	struct passwd*	pwent;
	
	if (dir == NULL) {
		dir = HA_COREDIR;
	}
	if ((rc=chdir(dir)) < 0) {
		int errsave = errno;
		cl_perror("Cannot chdir to [%s]", dir);
		errno = errsave;
		return rc;
	}
	pwent = getpwuid(getuid());
	if (pwent == NULL) {
		int errsave = errno;
		cl_perror("Cannot get name for uid [%d]", getuid());
		errno = errsave;
		return -1;
	}
	if ((rc=chdir(pwent->pw_name)) < 0) {
		int errsave = errno;
		cl_perror("Cannot chdir to [%s/%s]", dir, pwent->pw_name);
		errno = errsave;
	}
	return rc;
}

#define	CHECKED_KERNEL_CORE_ENV		"_PROC_SYS_CORE_CHECKED_"
#define	PROC_SYS_KERNEL_CORE_PID	"/proc/sys/kernel/core_uses_pid"
#define	PROC_SYS_KERNEL_CORE_PAT	"/proc/sys/kernel/core_pattern"

static void cl_coredump_signal_handler(int nsig);

/*
 *	core_uses_pid():
 *
 *	returns {-1, 0, 1}
 *		-1:	not supported
 *		 0:	supported and disabled
 *		 1:	supported and enabled
 */
#define BUF_MAX 256
static int
core_uses_pid(void)
{
	const char *	uses_pid_pathnames[] = {PROC_SYS_KERNEL_CORE_PID};
	const char *	corepats_pathnames[] = {PROC_SYS_KERNEL_CORE_PAT};
	const char *	goodpats [] = {"%t", "%p"};
	int		j;


	for (j=0; j < DIMOF(corepats_pathnames); ++j) {
		int	fd;
		char	buf[BUF_MAX];
		int	rc;
		int	k;

		if ((fd = open(corepats_pathnames[j], O_RDONLY)) < 0) {
			continue;
		}
		
		memset(buf, 0, BUF_MAX);
		rc = read(fd, buf, BUF_MAX - 1); /* Ensure it is always NULL terminated */
		close(fd);
		
		for (k=0; rc > 0 && k < DIMOF(goodpats); ++k) {
			if (strstr(buf, goodpats[k]) != NULL) {
				return 1;
			}
		}

		break;
	}
	for (j=0; j < DIMOF(uses_pid_pathnames); ++j) {
		int	fd;
		char	buf[2];
		int	rc;
		if ((fd = open(uses_pid_pathnames[j], O_RDONLY)) < 0) {
			continue;
		}
		rc = read(fd, buf, sizeof(buf));
		close(fd);
		if (rc < 1) {
			continue;
		}
		return (buf[0] == '1');
	}
	setenv(CHECKED_KERNEL_CORE_ENV, "1", TRUE);
	return -1;
}

/* Enable/disable core dumps for ourselves and our child processes */
int
cl_enable_coredumps(int doenable)
{
	int		rc;
	struct rlimit	rlim;

	if ((rc = getrlimit(RLIMIT_CORE, &rlim)) < 0) {
		int errsave = errno;
		cl_perror("Cannot get current core limit value.");
		errno = errsave;
		return rc;
	}
	if (rlim.rlim_max == 0 && geteuid() == 0) {
		rlim.rlim_max = RLIM_INFINITY;
	}

	rlim.rlim_cur = (doenable ? rlim.rlim_max : 0);

	if (doenable && rlim.rlim_max == 0) {
		cl_log(LOG_WARNING
		,	"Not possible to enable core dumps (rlim_max is 0)");
	}

	if ((rc = setrlimit(RLIMIT_CORE, &rlim)) < 0) {
		int errsave = errno;
		cl_perror("Unable to %s core dumps"
		,	 doenable ? "enable" : "disable");
		errno = errsave;
		return rc;
	}
	if (getenv(CHECKED_KERNEL_CORE_ENV) == NULL
	&&	core_uses_pid() == 0) {
		cl_log(LOG_WARNING
		,	"Core dumps could be lost if multiple dumps occur.");
		cl_log(LOG_WARNING
		,	"Consider setting non-default value in %s"
		" (or equivalent) for maximum supportability", PROC_SYS_KERNEL_CORE_PAT);
		cl_log(LOG_WARNING
		,	"Consider setting %s (or equivalent) to"
		" 1 for maximum supportability", PROC_SYS_KERNEL_CORE_PID);
	}
	return 0;
}

/*
 *   SIGQUIT      3        Core    Quit from keyboard
 *   SIGILL       4        Core    Illegal Instruction
 *   SIGABRT      6        Core    Abort signal from abort(3)
 *   SIGFPE       8        Core    Floating point exception
 *   SIGSEGV      11       Core    Invalid memory reference
 *   SIGBUS    10,7,10     Core    Bus error (bad memory access)
 *   SIGSYS     2,-,12     Core    Bad argument to routine (SVID)
 *   SIGTRAP      5        Core    Trace/breakpoint trap
 *   SIGXCPU   24,24,30    Core    CPU time limit exceeded (4.2 BSD)
 *   SIGXFSZ   25,25,31    Core    File size limit exceeded (4.2 BSD)
 */

/*
 * This function exists to allow security-sensitive programs
 * to safely take core dumps.  Such programs can't can't call
 * cl_untaint_coredumps() alone - because it might cause a
 * leak of confidential information - as information which should
 * only be known by the "high-privilege" user id will be written
 * into a core dump which is readable by the "low-privilege" user id.
 * This is a bad thing.
 *
 * This function causes this program to call a special signal handler
 * on receipt of any core dumping signal.  This handler then does
 * the following four things on receipt of a core dumping signal:
 *
 *  1)	Set privileges to "maximum" on receipt of a signal
 *  2)	"untaint" themselves with regard to core dumping
 *  3)	set SIG_DFLT for the received signal
 *  4)	Kill themselves with the received core-dumping signal
 *
 * Any process *could* do this to get core dumps, but if your stack
 * is screwed up, then the signal handler might not work.
 * If you're core dumping because of a stack overflow, it certainly won't work.
 *
 * On the other hand, this function may work on some OSes that don't support
 * prctl(2).  This is an untested theory at this time...
 */
void
cl_set_all_coredump_signal_handlers(void)
{
	static const int coresigs [] = {SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV
#ifdef SIGBUS
,	SIGBUS
#endif
#ifdef SIGSYS
,	SIGSYS
#endif
#ifdef SIGTRAP
,	SIGTRAP
#endif
#ifdef SIGXCPU
,	SIGXCPU
#endif
#ifdef SIGXFSZ
,	SIGXFSZ
#endif
};
	int	j;

	for (j=0; j < DIMOF(coresigs); ++j) {
		cl_set_coredump_signal_handler(coresigs[j]);
	}
}

/*
 * See note above about why using this function directly is sometimes
 * a bad idea, and you might need to use cl_set_all_coredump_signal_handlers()
 * instead.
 */
void
cl_untaint_coredumps(void)
{
#if defined(PR_SET_DUMPABLE)
	prctl(PR_SET_DUMPABLE, (unsigned long)TRUE, 0UL, 0UL, 0UL);
#endif
}
static void
cl_coredump_signal_handler(int nsig)
{
	return_to_orig_privs();
	if (geteuid() == 0) {
		/* Put ALL privileges back to root... */
		if (setuid(0) < 0) {
			cl_perror("cl_coredump_signal_handler: unable to setuid(0)");
		}
	}
	cl_untaint_coredumps();	/* Do the best we know how to do... */
	CL_SIGNAL(nsig, SIG_DFL);
	kill(getpid(), nsig);
}

void
cl_set_coredump_signal_handler(int nsig)
{
	CL_SIGNAL(nsig, cl_coredump_signal_handler);
}