1/*
2 * Copyright (c) 1983, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
35
36#ifndef lint
37__unused static const char copyright[] =
38"@(#) Copyright (c) 1983, 1993\n\
39	The Regents of the University of California.  All rights reserved.\n";
40#endif /* not lint */
41
42/* Mac OS X kernel core dump server, based on the BSD trivial file
43 * transfer protocol server (FreeBSD distribution), with several
44 * modifications. This server is *not* compatible with tftp, as the
45 * protocol has changed considerably.
46 */
47
48/*
49 * Based on the trivial file transfer protocol server.
50 *
51 * The original version included many modifications by Jim Guyton
52 * <guyton@rand-unix>.
53 */
54
55#include <sys/param.h>
56#include <sys/ioctl.h>
57#include <sys/stat.h>
58#include <sys/socket.h>
59#include <sys/types.h>
60#include <sys/mman.h>
61
62#include <netinet/in.h>
63#include "kdump.h"
64#include <arpa/inet.h>
65
66#include <stdint.h>
67#include <ctype.h>
68#include <errno.h>
69#include <fcntl.h>
70#include <netdb.h>
71#include <pwd.h>
72#include <setjmp.h>
73#include <signal.h>
74#include <stdio.h>
75#include <stdlib.h>
76#include <string.h>
77#include <syslog.h>
78#include <unistd.h>
79#include <libkern/OSByteOrder.h>
80
81#include "kdumpsubs.h"
82
83#define	TIMEOUT		2
84
85int	peer;
86int	rexmtval = TIMEOUT;
87int	maxtimeout = 25 * TIMEOUT;
88
89#define	PKTSIZE	SEGSIZE+6
90
91char	buf[MAXIMUM_KDP_PKTSIZE];
92char	ackbuf[MAXIMUM_KDP_PKTSIZE];
93struct	sockaddr_in from;
94socklen_t fromlen;
95
96void	kdump __P((struct kdumphdr *, int));
97
98/*
99 * Null-terminated directory prefix list for absolute pathname requests and
100 * search list for relative pathname requests.
101 *
102 * MAXDIRS should be at least as large as the number of arguments that
103 * inetd allows (currently 20).
104 */
105#define MAXDIRS	20
106static struct dirlist {
107	char	*name;
108	int	len;
109} dirs[MAXDIRS+1];
110static int	suppress_naks;
111static int	logging = 1;
112static int	ipchroot;
113
114static char *errtomsg __P((int));
115static void  nak __P((int));
116static char * __P(verifyhost(struct sockaddr_in *));
117uint32_t kdp_crashdump_pkt_size = (SEGSIZE + (sizeof(struct kdumphdr)));
118uint32_t kdp_crashdump_seg_size = SEGSIZE;
119
120#define KDP_FEATURE_MASK_STRING		"features"
121enum	{KDP_FEATURE_LARGE_CRASHDUMPS = 1, KDP_FEATURE_LARGE_PKT_SIZE = 2};
122
123uint32_t kdp_crashdump_feature_mask;
124uint32_t kdp_feature_large_crashdumps, kdp_feature_large_packets;
125
126int
127main(argc, argv)
128	int argc;
129	char *argv[];
130{
131	register struct kdumphdr *tp;
132	register int n;
133	int ch, on;
134	struct sockaddr_in sin;
135	char *chroot_dir = NULL;
136	struct passwd *nobody;
137	char *chuser = "nobody";
138
139	openlog("kdumpd", LOG_PID | LOG_NDELAY, LOG_FTP);
140	while ((ch = getopt(argc, argv, "cClns:u:")) != -1) {
141		switch (ch) {
142		case 'c':
143			ipchroot = 1;
144			break;
145		case 'C':
146			ipchroot = 2;
147			break;
148		case 'l':
149			logging = 1;
150			break;
151		case 'n':
152			suppress_naks = 1;
153			break;
154		case 's':
155			chroot_dir = optarg;
156			break;
157		case 'u':
158			chuser = optarg;
159			break;
160		default:
161			syslog(LOG_WARNING, "ignoring unknown option -%c", ch);
162		}
163	}
164
165	if (optind < argc) {
166		struct dirlist *dirp;
167
168		/* Get list of directory prefixes. Skip relative pathnames. */
169		for (dirp = dirs; optind < argc && dirp < &dirs[MAXDIRS];
170		     optind++) {
171			if (argv[optind][0] == '/') {
172				dirp->name = argv[optind];
173				dirp->len  = strlen(dirp->name);
174				dirp++;
175			}
176		}
177	}
178	else if (chroot_dir) {
179		dirs->name = "/";
180		dirs->len = 1;
181	}
182	if (ipchroot && chroot_dir == NULL) {
183		syslog(LOG_ERR, "-c requires -s");
184		exit(1);
185	}
186
187	on = 1;
188	if (ioctl(0, FIONBIO, &on) < 0) {
189		syslog(LOG_ERR, "ioctl(FIONBIO): %m");
190		exit(1);
191	}
192	fromlen = sizeof (from);
193	n = recvfrom(0, buf, sizeof (buf), 0,
194	    (struct sockaddr *)&from, &fromlen);
195	if (n < 0) {
196		syslog(LOG_ERR, "recvfrom: %m");
197		exit(1);
198	}
199	/*
200	 * Now that we have read the message out of the UDP
201	 * socket, we fork and exit.  Thus, inetd will go back
202	 * to listening to the kdump port, and the next request
203	 * to come in will start up a new instance of kdumpd.
204	 *
205	 * We do this so that inetd can run kdumpd in "wait" mode.
206	 * The problem with kdumpd running in "nowait" mode is that
207	 * inetd may get one or more successful "selects" on the
208	 * kdump port before we do our receive, so more than one
209	 * instance of kdumpd may be started up.  Worse, if kdumpd
210	 * breaks before doing the above "recvfrom", inetd would
211	 * spawn endless instances, clogging the system.
212	 */
213	{
214		int pid;
215		int i;
216		socklen_t j;
217
218		for (i = 1; i < 20; i++) {
219		    pid = fork();
220		    if (pid < 0) {
221				sleep(i);
222				/*
223				 * flush out to most recently sent request.
224				 *
225				 * This may drop some requests, but those
226				 * will be resent by the clients when
227				 * they timeout.  The positive effect of
228				 * this flush is to (try to) prevent more
229				 * than one kdumpd being started up to service
230				 * a single request from a single client.
231				 */
232				j = sizeof from;
233				i = recvfrom(0, buf, sizeof (buf), 0,
234				    (struct sockaddr *)&from, &j);
235				if (i > 0) {
236					n = i;
237					fromlen = j;
238				}
239		    } else {
240				break;
241		    }
242		}
243		if (pid < 0) {
244			syslog(LOG_ERR, "fork: %m");
245			exit(1);
246		} else if (pid != 0) {
247			exit(0);
248		}
249	}
250
251	/*
252	 * Since we exit here, we should do that only after the above
253	 * recvfrom to keep inetd from constantly forking should there
254	 * be a problem.  See the above comment about system clogging.
255	 */
256	if (chroot_dir) {
257		if (ipchroot) {
258			char tempchroot[MAXPATHLEN];
259			char *tempaddr;
260			struct stat sb;
261			int statret;
262
263			tempaddr = inet_ntoa(from.sin_addr);
264			snprintf(tempchroot, sizeof(tempchroot), "%s/%s", chroot_dir, tempaddr);
265			statret = stat(tempchroot, &sb);
266			if (((sb.st_mode & S_IFMT ) == S_IFDIR) &&
267			    (statret == 0 || (statret == -1 && ipchroot == 1)))
268				chroot_dir = tempchroot;
269		}
270		/* Must get this before chroot because /etc might go away */
271		if ((nobody = getpwnam(chuser)) == NULL) {
272			syslog(LOG_ERR, "%s: no such user", chuser);
273			exit(1);
274		}
275		if (chroot(chroot_dir)) {
276			syslog(LOG_ERR, "chroot: %s: %m", chroot_dir);
277			exit(1);
278		}
279		chdir( "/" );
280		setuid(nobody->pw_uid);
281	}
282	else
283	  if (0 !=  chdir(dirs->name))
284	    syslog(LOG_ERR, "chdir%s: %m", dirs->name);
285
286	from.sin_family = AF_INET;
287	alarm(0);
288	close(0);
289	close(1);
290	peer = socket(AF_INET, SOCK_DGRAM, 0);
291	if (peer < 0) {
292		syslog(LOG_ERR, "socket: %m");
293		exit(1);
294	}
295	memset(&sin, 0, sizeof(sin));
296	sin.sin_family = AF_INET;
297	if (bind(peer, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
298		syslog(LOG_ERR, "bind: %m");
299		exit(1);
300	}
301	if (connect(peer, (struct sockaddr *)&from, sizeof(from)) < 0) {
302		syslog(LOG_ERR, "connect: %m");
303		exit(1);
304	}
305	tp = (struct kdumphdr *)buf;
306	tp->th_opcode = ntohs(tp->th_opcode);
307	if (tp->th_opcode == WRQ)
308		kdump(tp, n);
309	exit(1);
310}
311
312struct formats;
313int	validate_access __P((char **, int));
314
315void	recvfile __P((struct formats *));
316
317struct formats {
318	char	*f_mode;
319	int	(*f_validate) __P((char **, int));
320
321	void	(*f_recv) __P((struct formats *));
322	int	f_convert;
323} formats[] = {
324  { "netascii",	validate_access, recvfile, 1 },
325  { "octet",	validate_access, recvfile, 0 },
326  { 0 }
327};
328
329/*
330 * Handle initial connection protocol.
331 */
332void
333kdump(tp, size)
334	struct kdumphdr *tp;
335	int size;
336{
337	register char *cp;
338	int first = 1, ecode;
339	register struct formats *pf;
340	char *filename, *mode = NULL;
341
342	filename = cp = tp->th_stuff;
343again:
344	while (cp < buf + size) {
345		if (*cp == '\0')
346			break;
347		cp++;
348	}
349	if (*cp != '\0') {
350		nak(EBADOP);
351		exit(1);
352	}
353	if (first) {
354		mode = ++cp;
355		first = 0;
356		goto again;
357	}
358	for (cp = mode; *cp; cp++)
359		if (isupper(*cp))
360			*cp = tolower(*cp);
361
362	cp++;
363	if (strncmp(KDP_FEATURE_MASK_STRING, cp, sizeof(KDP_FEATURE_MASK_STRING)) == 0) {
364		kdp_crashdump_feature_mask = ntohl(*(uint32_t *) (cp + sizeof(KDP_FEATURE_MASK_STRING)));
365		kdp_feature_large_crashdumps = kdp_crashdump_feature_mask & KDP_FEATURE_LARGE_CRASHDUMPS;
366		kdp_feature_large_packets = kdp_crashdump_feature_mask & KDP_FEATURE_LARGE_PKT_SIZE;
367
368		if (kdp_feature_large_packets) {
369			kdp_crashdump_pkt_size = KDP_LARGE_CRASHDUMP_PKT_SIZE;
370			kdp_crashdump_seg_size = kdp_crashdump_pkt_size - sizeof(struct kdumphdr);
371		}
372		syslog(KDUMPD_DEBUG_LEVEL, "Received feature mask %s:0x%x", cp, kdp_crashdump_feature_mask);
373	} else
374		syslog(KDUMPD_DEBUG_LEVEL, "Unable to locate feature mask, mode: %s", mode);
375
376	for (pf = formats; pf->f_mode; pf++)
377		if (strcmp(pf->f_mode, mode) == 0)
378			break;
379	if (pf->f_mode == 0) {
380		nak(EBADOP);
381		exit(1);
382	}
383	ecode = (*pf->f_validate)(&filename, tp->th_opcode);
384	if (logging) {
385		syslog(KDUMPD_DEBUG_LEVEL, "%s: %s request for %s: %s", verifyhost(&from),
386			tp->th_opcode == WRQ ? "write" : "read",
387			filename, errtomsg(ecode));
388	}
389	if (ecode) {
390		/*
391		 * Avoid storms of naks to a RRQ broadcast for a relative
392		 * bootfile pathname from a diskless Sun.
393		 */
394		if (suppress_naks && *filename != '/' && ecode == ENOTFOUND)
395			exit(0);
396		nak(ecode);
397		exit(1);
398	}
399	if (tp->th_opcode == WRQ)
400		(*pf->f_recv)(pf);
401
402	exit(0);
403}
404
405
406FILE *file;
407
408/*
409 * Validate file access. We only allow storage of files that do not already
410 * exist, and that do not include directory specifiers in their pathnames.
411 * This is because kernel coredump filenames should always be of the form
412 * "core-version-IP as dotted quad-random string" as in :
413 * core-custom-17.202.40.204-a75b4eec
414 * The file is written to the directory supplied as the first argument
415 * in inetd.conf
416 */
417
418int
419validate_access(char **filep, int mode)
420{
421  struct stat stbuf;
422  int	fd;
423  char *filename = *filep;
424  static char pathname[MAXPATHLEN];
425
426  if (strstr(filename, "/") || strstr(filename, ".."))
427    return (EACCESS);
428
429  snprintf(pathname, sizeof(pathname), "./%s", filename);
430
431  if (0 == stat(pathname, &stbuf))
432    return (EEXIST);
433
434  if (errno != ENOENT)
435    return (errno);
436
437
438  fd = open(filename, O_RDWR|O_CREAT|O_TRUNC , S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
439
440  if (fd < 0)
441    return (errno + 100);
442
443  file = fdopen(fd, (mode == RRQ)? "r":"w");
444  if (file == NULL) {
445    return errno+100;
446  }
447
448  return (0);
449}
450
451int	timeout;
452jmp_buf	timeoutbuf;
453
454void
455timer()
456{
457
458	timeout += rexmtval;
459	if (timeout >= maxtimeout)
460	  {
461	    longjmp(timeoutbuf, 2);
462	  }
463	longjmp(timeoutbuf, 1);
464}
465
466void
467justquit()
468{
469	exit(0);
470}
471
472/*
473 * Receive a file.
474 */
475void
476recvfile(pf)
477	struct formats *pf;
478{
479	struct kdumphdr *dp, *w_init();
480	register struct kdumphdr *ap;    /* ack buffer */
481	register int n, size;
482	volatile unsigned int block;
483	volatile unsigned int jmpval = 0;
484
485	signal(SIGALRM, timer);
486	dp = w_init();
487	ap = (struct kdumphdr *)ackbuf;
488	block = 0;
489	do {
490send_seek_ack:	timeout = 0;
491		if (block == 0)
492			ap->th_opcode = htons((u_short)ACK | ((kdp_feature_large_crashdumps | kdp_feature_large_packets)  << 8));
493		else
494			ap->th_opcode = htons((u_short)ACK);
495		ap->th_block = htonl((unsigned int)block);
496		block++;
497		jmpval = setjmp(timeoutbuf);
498		if (2 == jmpval)
499		  {
500		    syslog (LOG_ERR, "Timing out and flushing file to disk");
501		    goto flushfile;
502		  }
503send_ack:
504		if (send(peer, ackbuf, 6 , 0) != 6) {
505			syslog(LOG_ERR, "write: %m");
506			goto abort;
507		}
508		write_behind(file, pf->f_convert);
509		for ( ; ; ) {
510			alarm(rexmtval);
511			n = recv(peer, dp, kdp_crashdump_pkt_size, 0);
512			alarm(0);
513			if (n < 0) {            /* really? */
514				syslog(LOG_ERR, "read: %m");
515				goto abort;
516			}
517			dp->th_opcode = ntohs((u_short)dp->th_opcode);
518			dp->th_block = ntohl((unsigned int)dp->th_block);
519#if	DEBUG
520			syslog(KDUMPD_DEBUG_LEVEL, "Received packet type %u, block %u\n", (unsigned)dp->th_opcode, (unsigned)dp->th_block);
521#endif
522
523			if (dp->th_opcode == ERROR)
524				goto abort;
525
526			if (dp->th_opcode == KDP_EOF)
527			  {
528			    syslog (LOG_ERR, "Received last panic dump packet");
529			    goto final_ack;
530			  }
531			if (dp->th_opcode == KDP_SEEK)
532			  {
533			    if (dp->th_block == block)
534			      {
535				off_t crashdump_offset = 0;
536				unsigned int tempoff = 0;
537
538				if (kdp_feature_large_crashdumps) {
539					crashdump_offset = OSSwapBigToHostInt64((*(uint64_t *)dp->th_data));
540				}
541				else {
542				bcopy (dp->th_data, &tempoff, sizeof(unsigned int));
543				crashdump_offset = ntohl(tempoff);
544				}
545
546#if	DEBUG
547				syslog(KDUMPD_DEBUG_LEVEL, "Seeking to offset 0x%llx\n", crashdump_offset);
548#endif
549				errno = 0;
550				lseek(fileno (file), crashdump_offset, SEEK_SET);
551				if (errno)
552				  syslog (LOG_ERR, "lseek: %m");
553
554				goto send_seek_ack;
555			      }
556			    (void) synchnet(peer);
557			    if (dp->th_block == (block-1))
558			    {
559				    syslog (LOG_DAEMON|LOG_ERR, "Retransmitting seek ack - current block %u, received block %u", block, dp->th_block);
560				    goto send_ack;          /* rexmit */
561			    }
562			}
563
564			if (dp->th_opcode == DATA) {
565				if (dp->th_block == block) {
566					break;   /* normal */
567				}
568				/* Re-synchronize with the other side */
569				(void) synchnet(peer);
570				if (dp->th_block == (block-1))
571				  {
572				    syslog (LOG_DAEMON|LOG_ERR, "Retransmitting ack - current block %u, received block %u", block, dp->th_block);
573				    goto send_ack;          /* rexmit */
574				  }
575				else
576				  syslog (LOG_DAEMON|LOG_ERR, "Not retransmitting ack - current block %u, received block %u", block, dp->th_block);
577			}
578		}
579#if DEBUG
580		syslog(KDUMPD_DEBUG_LEVEL, "Writing block sized %u, current offset 0x%llx\n", n - 6, ftello(file));
581#endif
582		size = writeit(file, &dp, n - 6, pf->f_convert);
583		if (size != (n-6)) {                    /* ahem */
584			if (size < 0) nak(errno + 100);
585			else nak(ENOSPACE);
586			goto abort;
587		}
588	} while (dp->th_opcode != KDP_EOF);
589
590final_ack:
591	ap->th_opcode = htons((u_short)ACK);    /* send the "final" ack */
592	ap->th_block = htonl((unsigned int) (block));
593	(void) send(peer, ackbuf, 6, 0);
594flushfile:
595	write_behind(file, pf->f_convert);
596	(void) fclose(file);            /* close data file */
597	syslog (LOG_ERR, "file closed, sending final ACK\n");
598
599	signal(SIGALRM, justquit);      /* just quit on timeout */
600	alarm(rexmtval);
601	n = recv(peer, buf, sizeof (buf), 0); /* normally times out and quits */
602	alarm(0);
603	if (n >= 6 &&                   /* if read some data */
604	    dp->th_opcode == DATA &&    /* and got a data block */
605	    block == dp->th_block) {	/* then my last ack was lost */
606		(void) send(peer, ackbuf, 6, 0);     /* resend final ack */
607	}
608abort:
609	return;
610}
611
612/* update if needed, when adding new errmsgs */
613#define MAXERRMSGLEN	40
614
615struct errmsg {
616	int	e_code;
617	char	*e_msg;
618} errmsgs[] = {
619	{ EUNDEF,	"Undefined error code" },
620	{ ENOTFOUND,	"File not found" },
621	{ EACCESS,	"Access violation" },
622	{ ENOSPACE,	"Disk full or allocation exceeded" },
623	{ EBADOP,	"Illegal KDUMP operation" },
624	{ EBADID,	"Unknown transfer ID" },
625	{ EEXISTS,	"File already exists" },
626	{ ENOUSER,	"No such user" },
627	{ -1,		0 }
628};
629
630static char *
631errtomsg(error)
632	int error;
633{
634	static char buf[20];
635	register struct errmsg *pe;
636	if (error == 0)
637		return "success";
638	for (pe = errmsgs; pe->e_code >= 0; pe++)
639		if (pe->e_code == error)
640			return pe->e_msg;
641	snprintf(buf, sizeof(buf), "error %d", error);
642	return buf;
643}
644
645/*
646 * Send a nak packet (error message).
647 * Error code passed in is one of the
648 * standard KDUMP codes, or a UNIX errno
649 * offset by 100.
650 */
651static void
652nak(error)
653	int error;
654{
655	register struct kdumphdr *tp;
656	int length;
657	register struct errmsg *pe;
658
659	tp = (struct kdumphdr *)buf;
660	tp->th_opcode = htons((u_short)ERROR);
661	tp->th_code = htons((unsigned int)error);
662	for (pe = errmsgs; pe->e_code >= 0; pe++)
663		if (pe->e_code == error)
664			break;
665	if (pe->e_code < 0) {
666		pe->e_msg = strerror(error - 100);
667		tp->th_code = EUNDEF;   /* set 'undef' errorcode */
668	}
669	if (strlen(pe->e_msg) > MAXERRMSGLEN) {
670		syslog(LOG_ERR, "nak: error msg too long");
671		return;
672	}
673
674	strlcpy(tp->th_msg, pe->e_msg, MAXERRMSGLEN);
675	length = strlen(pe->e_msg);
676	tp->th_msg[length] = '\0';
677	length += 5;
678	if (send(peer, buf, length, 0) != length)
679		syslog(LOG_ERR, "nak: %m");
680
681	return;
682}
683
684static char *
685verifyhost(fromp)
686	struct sockaddr_in *fromp;
687{
688	struct hostent *hp;
689
690	hp = gethostbyaddr((char *)&fromp->sin_addr, sizeof(fromp->sin_addr),
691		fromp->sin_family);
692	if(hp)
693		return hp->h_name;
694	else
695		return inet_ntoa(fromp->sin_addr);
696}
697