From dd50e8b75cd8b57669a75ee1477e88d94acbbc44 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 14 Nov 2008 08:31:03 -0800 Subject: [PATCH 01/65] If AI_CANONNAME or AI_ADDRCONFIG don't exist, set them to zero If the AI_CANONNAME or AI_ADDRCONFIG flags are missing, just set them to zero. Signed-off-by: H. Peter Anvin --- config.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/config.h b/config.h index 76c7514..0e35438 100644 --- a/config.h +++ b/config.h @@ -321,14 +321,6 @@ int getaddrinfo(const char *, const char *, const struct addrinfo *, void freeaddrinfo(struct addrinfo *); const char *gai_strerror(int); -#ifndef AI_CANONNAME -#define AI_CANONNAME 0x0002 /* Request for canonical name. */ -#endif - -#ifndef AI_ADDRCONFIG -#define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose - returned address type.. */ -#endif #ifndef EAI_NONAME #define EAI_NONAME -2 /* NAME or SERVICE is unknown. */ @@ -344,6 +336,14 @@ const char *gai_strerror(int); #endif #endif +#ifndef AI_CANONNAME +#define AI_CANONNAME 0 +#endif + +#ifndef AI_ADDRCONFIG +#define AI_ADDRCONFIG 0 +#endif + #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif From 12996491c2f09be58e667146515af828cafca489 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 14 Nov 2008 08:32:29 -0800 Subject: [PATCH 02/65] Document getaddrinfo() workaround. --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index e183bed..9909cd3 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Changes in 5.0: + Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or + AI_CANONNAME. + + Changes in 0.49: Add IPv6 support. Patch by Karsten Keil. From b0a2a17864a589adbda6f7e30c57a57a175f622e Mon Sep 17 00:00:00 2001 From: Georg Schwarz Date: Sat, 1 Nov 2008 08:21:56 +0100 Subject: [PATCH 03/65] Cast IPv6 address from SOCKADDR_P() to struct in6_addr * We need to cast IPv6 addresses from SOCKADDR_P() to struct in6_addr * on some platforms, including at least MacOS X. --- tftpd/tftpd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index b29c49a..3c96844 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -841,9 +841,10 @@ int main(int argc, char **argv) sizeof(bindaddr4.sin_addr)); #ifdef HAVE_IPV6 } else if ((from.sa.sa_family == AF_INET6) && - IN6_IS_ADDR_UNSPECIFIED(SOCKADDR_P(&myaddr))) { - memcpy(SOCKADDR_P(&myaddr), &bindaddr6.sin6_addr, - sizeof(bindaddr6.sin6_addr)); + IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *) + SOCKADDR_P(&myaddr))) { + memcpy(SOCKADDR_P(&myaddr), &bindaddr6.sin6_addr, + sizeof(bindaddr6.sin6_addr)); #endif } } From fcdd859a7513319f68a5ba1261c0f66ef3a28723 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 2 Feb 2009 15:14:05 -0800 Subject: [PATCH 04/65] autogen.sh: script to run relevant autotools --- autogen.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 autogen.sh diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..defd3bd --- /dev/null +++ b/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh +autoheader +autoconf From 932277c9a5f90d906296a319c0c3e5f36bf2e8b1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 2 Feb 2009 15:14:27 -0800 Subject: [PATCH 05/65] tftpd: implement the "rollover" option Implement the "rollover" option, to set the rollover block number to anything other than zero. Apparently some idiots have gotten the idea that block numbers should roll over to one, rather than zero. --- tftpd/tftpd.8.in | 4 ++++ tftpd/tftpd.c | 26 ++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index a469ac8..f42d874 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -218,6 +218,10 @@ Set the time before the server retransmits a packet, in seconds. .TP \fButimeout\fP (nonstandard) Set the time before the server retransmits a packet, in microseconds. +.TP +\fBrollover\fP (nonstandard) +Set the block number to resume at after a block number rollover. The +default and recommended value is zero. .PP The .B \-\-refuse diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 3c96844..f98d52b 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -81,6 +81,7 @@ static unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ static unsigned long maxtimeout = TIMEOUT_LIMIT * TIMEOUT; static int timeout_quit = 0; static sigjmp_buf timeoutbuf; +static uint16_t rollover_val = 0; #define PKTSIZE MAX_SEGSIZE+4 static char buf[PKTSIZE]; @@ -119,6 +120,7 @@ static int set_blksize2(char *, char **); static int set_tsize(char *, char **); static int set_timeout(char *, char **); static int set_utimeout(char *, char **); +static int set_rollover(char *, char **); struct options { const char *o_opt; @@ -129,6 +131,7 @@ struct options { {"tsize", set_tsize}, {"timeout", set_timeout}, {"utimeout", set_utimeout}, + {"rollover", set_rollover}, {NULL, NULL} }; @@ -1160,6 +1163,23 @@ static int set_blksize2(char *val, char **ret) return (1); } +/* + * Set the block number rollover value + */ +static int set_rollover(char *val, char **ret) +{ + uintmax_t ro; + char *vend; + + ro = strtoumax(val, &vend, 10); + if (ro > 65535 || *vend) + return 0; + + rollover_val = (uint16_t)ro; + *ret = val; + return 1; +} + /* * Return a file size (c.f. RFC2349) * For netascii mode, we don't know the size ahead of time; @@ -1542,7 +1562,8 @@ static void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) } } - block++; + if (!++block) + block = rollover_val; } while (size == segsize); abort: (void)fclose(file); @@ -1575,7 +1596,8 @@ static void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) ap->th_block = htons((u_short) block); acksize = 4; } - block++; + if (!++block) + block = rollover_val; (void)sigsetjmp(timeoutbuf, 1); send_ack: r_timeout = timeout; From acf818880cbbbeb3709050cfcaa0e5503063df20 Mon Sep 17 00:00:00 2001 From: Florian Lohoff Date: Mon, 16 Feb 2009 14:38:48 -0800 Subject: [PATCH 06/65] tftpd: correctly disable PMTU discovery in standalone mode Use the correct file descriptors so we correctly turn off PMTU. --- tftpd/tftpd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index f98d52b..b3b49ee 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -719,7 +719,7 @@ int main(int argc, char **argv) } /* Disable path MTU discovery */ - pmtu_discovery_off(0); + pmtu_discovery_off(fd); /* This means we don't want to wait() for children */ #ifdef SA_NOCLDWAIT @@ -962,7 +962,7 @@ int main(int argc, char **argv) } /* Disable path MTU discovery */ - pmtu_discovery_off(0); + pmtu_discovery_off(peer); tp = (struct tftphdr *)buf; tp_opcode = ntohs(tp->th_opcode); From 878b024bcd1de09b092fb9106f1b8a271cdb0ff1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 16 Feb 2009 14:49:43 -0800 Subject: [PATCH 07/65] Update version for release 5.0 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index a2ff373..819e07a 100644 --- a/version +++ b/version @@ -1 +1 @@ -0.49 +5.0 From e7a5fc2d681119f663af4e420ae898f115771712 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 16 Feb 2009 14:51:22 -0800 Subject: [PATCH 08/65] Update CHANGES for 5.0 release --- CHANGES | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES b/CHANGES index 9909cd3..c74fd95 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,12 @@ Changes in 5.0: Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or AI_CANONNAME. + Implement the "rollover" option, for clients which want block + number to rollover to anything other than zero. + + Correctly disable PMTU in standalone mode. Patch by Florian + Lohoff. + Changes in 0.49: Add IPv6 support. Patch by Karsten Keil. From 2f3a775f8585f510116f341dfd256c1a494743e8 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 16 Feb 2009 14:53:50 -0800 Subject: [PATCH 09/65] autogen.sh: just do "make autoconf" We require GNU make, so we can have autoconf built from inside the Makefile. Just make autogen.sh do "make autoconf" for convenience. --- autogen.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/autogen.sh b/autogen.sh index defd3bd..728a381 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,3 +1,2 @@ #!/bin/sh -autoheader -autoconf +make autoconf From 9ba4e118d2f6806bf5b01b498812eae319fabc87 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 24 Feb 2009 16:26:16 -0800 Subject: [PATCH 10/65] tftpd.c: update copyright notice --- tftpd/tftpd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index b3b49ee..72d3e5d 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,5 +1,6 @@ /* * Copyright (c) 1983 Regents of the University of California. + * Copyright (c) 1999-2009 H. Peter Anvin * All rights reserved. * * Redistribution and use in source and binary forms, with or without From 338648870e57ff91699b8d65633213762ce1bc58 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Tue, 24 Feb 2009 16:27:27 -0800 Subject: [PATCH 11/65] tftpd.8: we're no longer limited to IPv4 --- tftpd/tftpd.8.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index f42d874..367f7ab 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -3,7 +3,7 @@ .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" -.\" Copyright 2001-2008 H. Peter Anvin - All Rights Reserved +.\" Copyright 2001-2009 H. Peter Anvin - All Rights Reserved .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -30,10 +30,10 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "30 July 2008" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "24 February 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd -\- IPv4 Trivial File Transfer Protocol server +\- Trivial File Transfer Protocol server .SH SYNOPSIS .B in.tftpd .RI [ options... ] From 44c98cf8b63a644ee07ac12a1ed3503875bb2b4d Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:41:53 +0200 Subject: [PATCH 12/65] Downcase datarootdir, so mandir et al. find their defaults mandir et all uses $(datarootdir), not $(DATAROOTDIR) Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- MCONFIG.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCONFIG.in b/MCONFIG.in index ecff393..2660f7e 100644 --- a/MCONFIG.in +++ b/MCONFIG.in @@ -32,7 +32,7 @@ MANDIR = @mandir@ SBINDIR = @sbindir@ # Data root directory -DATAROOTDIR = @datarootdir@ +datarootdir = @datarootdir@ # Binary suffixes O = @OBJEXT@ From 3f2bc9833d8b4642aac2866f7718c80d5c0b52be Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:41:59 +0200 Subject: [PATCH 13/65] Ensure that the log socket is available for the child Just in case syslog has been restarted, bounce the log socket before the chroot. Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 72d3e5d..721ebd8 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -870,6 +870,15 @@ int main(int argc, char **argv) /* Ignore SIGHUP */ set_signal(SIGHUP, SIG_IGN, 0); + /* Make sure the log socket is still connected. This has to be + done before the chroot, while /dev/log is still accessible. + When not running standalone, there is little chance that the + syslog daemon gets restarted by the time we get here. */ + if (secure && standalone) { + closelog(); + openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + } + #ifdef HAVE_TCPWRAPPERS /* Verify if this was a legal request for us. This has to be done before the chroot, while /etc is still accessible. */ From 85029077c86769bc53e17c1cfaf393a1fe6ba59d Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:42:06 +0200 Subject: [PATCH 14/65] Fix comment typo Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 721ebd8..1f5d9d6 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -696,7 +696,7 @@ int main(int argc, char **argv) } #endif /* Daemonize this process */ - /* Note: when running in secure mode (-s), we must not chroot, since + /* Note: when running in secure mode (-s), we must not chdir, since we are already in the proper directory. */ if (!nodaemon && daemon(secure, 0) < 0) { syslog(LOG_ERR, "cannot daemonize: %m"); From 5a27e30ec25b6ff49de9e5bc65d0f358dd69275b Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:42:12 +0200 Subject: [PATCH 15/65] Untabify tftpd.c Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 1f5d9d6..ff39c85 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -364,7 +364,7 @@ int main(int argc, char **argv) srand(time(NULL) ^ getpid()); while ((c = getopt_long(argc, argv, short_options, long_options, NULL)) - != -1) + != -1) switch (c) { case '4': ai_fam = AF_INET; @@ -467,9 +467,9 @@ int main(int argc, char **argv) case 'v': verbosity++; break; - case OPT_VERBOSITY: - verbosity = atoi(optarg); - break; + case OPT_VERBOSITY: + verbosity = atoi(optarg); + break; case 'V': /* Print configuration to stdout and exit */ printf("%s\n", TFTPD_CONFIG_STR); From c86f82532ec439678e4dd329b7d9c59d3f9ad6bb Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 5 Sep 2009 18:42:18 +0200 Subject: [PATCH 16/65] Implement the --pidfile option Setting the umask moved later, right before entering the select loop, so that it does not affect the permissions of the pid file. Signed-off-by: Ferenc Wagner Signed-off-by: H. Peter Anvin --- tftpd/tftpd.8.in | 5 +++++ tftpd/tftpd.c | 49 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 367f7ab..8486934 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -133,6 +133,11 @@ system-provided access controls for the user specified via the .B \-\-user option. .TP +\fB\-\-pidfile\fP \fIpidfile\fP, \fB\-P\fP \fIpidfile\fP When run in +standalone mode, write the process ID of the listening server into +\fIpidfile\fP. On normal termination (SIGTERM or SIGINT) the pid file +is automatically removed. +.TP \fB\-\-timeout\fP \fItimeout\fP, \fB\-t\fP \fItimeout\fP When run from .B inetd diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index ff39c85..69ff121 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -144,6 +144,13 @@ static void handle_sighup(int sig) caught_sighup = 1; } +/* Handle exit requests by SIGTERM and SIGINT */ +static volatile sig_atomic_t exit_signal = 0; +static void handle_exit(int sig) +{ + exit_signal = sig; +} + /* Handle timeout signal or timeout event */ void timer(int sig) { @@ -318,9 +325,10 @@ static struct option long_options[] = { { "retransmit", 1, NULL, 'T' }, { "port-range", 1, NULL, 'R' }, { "map-file", 1, NULL, 'm' }, + { "pidfile", 1, NULL, 'P' }, { NULL, 0, NULL, 0 } }; -static const char short_options[] = "46cspvVlLa:B:u:U:r:t:T:R:m:"; +static const char short_options[] = "46cspvVlLa:B:u:U:r:t:T:R:m:P:"; int main(int argc, char **argv) { @@ -352,6 +360,7 @@ int main(int argc, char **argv) #ifdef WITH_REGEX char *rewrite_file = NULL; #endif + const char *pidfile = NULL; u_short tp_opcode; /* basename() is way too much of a pain from a portability standpoint */ @@ -475,6 +484,9 @@ int main(int argc, char **argv) printf("%s\n", TFTPD_CONFIG_STR); exit(0); break; + case 'P': + pidfile = optarg; + break; default: syslog(LOG_ERR, "Unknown option: '%c'", optopt); break; @@ -507,16 +519,19 @@ int main(int argc, char **argv) exit(EX_NOUSER); } - if (spec_umask || !unixperms) - umask(my_umask); - #ifdef WITH_REGEX if (rewrite_file) rewrite_rules = read_remap_rules(rewrite_file); #endif + if (pidfile && !standalone) { + syslog(LOG_WARNING, "not in standalone mode, ignoring pid file"); + pidfile = NULL; + } + /* If we're running standalone, set up the input port */ if (standalone) { + FILE *pf; #ifdef HAVE_IPV6 if (ai_fam != AF_INET6) { #endif @@ -702,6 +717,20 @@ int main(int argc, char **argv) syslog(LOG_ERR, "cannot daemonize: %m"); exit(EX_OSERR); } + set_signal(SIGTERM, handle_exit, 0); + set_signal(SIGINT, handle_exit, 0); + if (pidfile) { + pf = fopen (pidfile, "w"); + if (!pf) { + syslog(LOG_ERR, "cannot open pid file '%s' for writing: %m", pidfile); + pidfile = NULL; + } else { + if (fprintf(pf, "%d\n", getpid()) < 0) + syslog(LOG_ERR, "error writing pid file '%s': %m", pidfile); + if (fclose(pf)) + syslog(LOG_ERR, "error closing pid file '%s': %m", pidfile); + } + } if (fd6 > fd4) fdmax = fd6; else @@ -734,11 +763,23 @@ int main(int argc, char **argv) lose packets as a result. */ set_signal(SIGHUP, handle_sighup, 0); + if (spec_umask || !unixperms) + umask(my_umask); + while (1) { fd_set readset; struct timeval tv_waittime; int rv; + if (exit_signal) { /* happens in standalone mode only */ + if (pidfile && unlink(pidfile)) { + syslog(LOG_WARNING, "error removing pid file '%s': %m", pidfile); + exit(EX_OSERR); + } else { + exit(0); + } + } + if (caught_sighup) { caught_sighup = 0; if (standalone) { From ab382980ae555fd6024c4ec24a1d5bddb527829f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 14 Sep 2009 14:44:54 -0700 Subject: [PATCH 17/65] Fix man page formatting --- tftpd/tftpd.8.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 8486934..78b4cfb 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "24 February 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "14 September 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- Trivial File Transfer Protocol server @@ -133,10 +133,10 @@ system-provided access controls for the user specified via the .B \-\-user option. .TP -\fB\-\-pidfile\fP \fIpidfile\fP, \fB\-P\fP \fIpidfile\fP When run in -standalone mode, write the process ID of the listening server into -\fIpidfile\fP. On normal termination (SIGTERM or SIGINT) the pid file -is automatically removed. +\fB\-\-pidfile\fP \fIpidfile\fP, \fB\-P\fP \fIpidfile\fP +When run in standalone mode, write the process ID of the listening +server into \fIpidfile\fP. On normal termination (SIGTERM or SIGINT) +the pid file is automatically removed. .TP \fB\-\-timeout\fP \fItimeout\fP, \fB\-t\fP \fItimeout\fP When run from From e7a7b194833a5283ac5c4cf24db370e70cf2674f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 14 Sep 2009 14:46:34 -0700 Subject: [PATCH 18/65] Update CHANGES for future 5.1 release Signed-off-by: H. Peter Anvin --- CHANGES | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGES b/CHANGES index c74fd95..cdb9a2e 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,12 @@ +Changes in 5.1: + Add -P option to write a PID file. Patch by Ferenc Wagner. + + Bounce the syslog socket in standalone mode, in case the + syslog daemon has been restarted. Patch by Ferenc Wagner. + + Build fixes. + + Changes in 5.0: Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or AI_CANONNAME. From a63534e6e6fa351d1c51b1d7cd14f916af3f835a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 11 Dec 2009 16:05:48 -0800 Subject: [PATCH 19/65] recvfrom: fix the type of the fallthrough case If we can't figure out the source address, we have the "fall on our face" version of myrecvfrom(); make sure its prototype matches. This handles building on machines where sockaddr_t != int and yet there is no way to get the source address. This apparently affects at least one version of Solaris. Reported-by: Georg Schwarz Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 389ba82..3ee5642 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -245,7 +245,8 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, int *fromlen, union sock_addr *myaddr) + struct sockaddr *from, socklen_t * fromlen, + union sock_addr *myaddr) { /* There is no way we can get the local address, fudge it */ From aeb1c31bae431ad3b9269befa221229458adb854 Mon Sep 17 00:00:00 2001 From: Tim Newsome Date: Wed, 8 Sep 2010 14:11:42 -0700 Subject: [PATCH 20/65] tftpd: Don't resend the OACK packet on block number wrap When uploading a file that is larger than 32MB (with standard block size), the block number will roll over. If it rolls over to 0, the code mistakenly resends the option ack frame instead of acknowledging the 0 data block. This change fixes that behavior. --- tftpd/tftpd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 69ff121..0a8da72 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1646,6 +1646,10 @@ static void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) ap->th_opcode = htons((u_short) ACK); ap->th_block = htons((u_short) block); acksize = 4; + /* If we're sending a regular ACK, that means we have successfully + * sent the OACK. Clear oap so that we won't try to send another + * OACK when the block number wraps back to 0. */ + oap = NULL; } if (!++block) block = rollover_val; From 915f62f5c76d7bd953b758d477b542acf6a3bf65 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 20 Sep 2010 17:49:10 -0700 Subject: [PATCH 21/65] CHANGES: document wraparound fix --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index cdb9a2e..5238089 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,9 @@ Changes in 5.1: Build fixes. + Fix handling of block number wraparound after a successful + options negotiation. + Changes in 5.0: Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or From 0b5732e263fa439906b298cdcab8f2dba3f93ce4 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 5 May 2011 10:08:53 -0700 Subject: [PATCH 22/65] remap: change the mode argument from a boolean to a character Instead of taking a boolean value for get/put, pass a character; this allows us to extend the number of possibilities in the future. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 21 ++++++++------------- tftpd/remap.h | 2 +- tftpd/tftpd.c | 3 ++- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index cdb9062..1e7abe7 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -30,14 +30,13 @@ #define RULE_EXIT 0x04 /* Exit after matching this rule */ #define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ #define RULE_ABORT 0x10 /* Terminate processing with an error */ -#define RULE_GETONLY 0x20 /* Applicable to GET only */ -#define RULE_PUTONLY 0x40 /* Applicable to PUT only */ -#define RULE_INVERSE 0x80 /* Execute if regex *doesn't* match */ +#define RULE_INVERSE 0x20 /* Execute if regex *doesn't* match */ struct rule { struct rule *next; int nrule; int rule_flags; + char rule_mode; regex_t rx; const char *pattern; }; @@ -221,15 +220,13 @@ static int parseline(char *line, struct rule *r, int lineno) case 'i': rxflags |= REG_ICASE; break; - case 'G': - r->rule_flags |= RULE_GETONLY; - break; - case 'P': - r->rule_flags |= RULE_PUTONLY; - break; case '~': r->rule_flags |= RULE_INVERSE; break; + case 'G': + case 'P': + r->rule_mode = *p; + break; default: syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", @@ -329,7 +326,7 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const char *input, const struct rule *rules, - int is_put, match_pattern_callback macrosub, + char mode, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); @@ -348,10 +345,8 @@ char *rewrite_string(const char *input, const struct rule *rules, } for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { - if (((ruleptr->rule_flags & RULE_GETONLY) && is_put) || - ((ruleptr->rule_flags & RULE_PUTONLY) && !is_put)) { + if (ruleptr->rule_mode && ruleptr->rule_mode != mode) continue; /* Rule not applicable, try next */ - } if (!deadman--) { syslog(LOG_WARNING, diff --git a/tftpd/remap.h b/tftpd/remap.h index 3830b5c..69ca08d 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -35,7 +35,7 @@ struct rule *parserulefile(FILE *); void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *, const struct rule *, int, +char *rewrite_string(const char *, const struct rule *, char, match_pattern_callback, const char **); #endif /* WITH_REGEX */ diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 69ff121..bdbea6d 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1388,7 +1388,8 @@ static char *rewrite_access(char *filename, int mode, const char **msg) { if (rewrite_rules) { char *newname = - rewrite_string(filename, rewrite_rules, mode != RRQ, + rewrite_string(filename, rewrite_rules, + mode != RRQ ? 'P' : 'G', rewrite_macros, msg); filename = newname; } From ad5aab9281b6d67b77537b4aabfe5bc070e7fe99 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 5 May 2011 10:11:59 -0700 Subject: [PATCH 23/65] tftpd: constipate struct formats struct formats should be static const; make it so and mark all users const. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index bdbea6d..1f946f6 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1023,18 +1023,19 @@ int main(int argc, char **argv) } static char *rewrite_access(char *, int, const char **); -static int validate_access(char *, int, struct formats *, const char **); -static void tftp_sendfile(struct formats *, struct tftphdr *, int); -static void tftp_recvfile(struct formats *, struct tftphdr *, int); +static int validate_access(char *, int, const struct formats *, const char **); +static void tftp_sendfile(const struct formats *, struct tftphdr *, int); +static void tftp_recvfile(const struct formats *, struct tftphdr *, int); struct formats { const char *f_mode; char *(*f_rewrite) (char *, int, const char **); - int (*f_validate) (char *, int, struct formats *, const char **); - void (*f_send) (struct formats *, struct tftphdr *, int); - void (*f_recv) (struct formats *, struct tftphdr *, int); + int (*f_validate) (char *, int, const struct formats *, const char **); + void (*f_send) (const struct formats *, struct tftphdr *, int); + void (*f_recv) (const struct formats *, struct tftphdr *, int); int f_convert; -} formats[] = { +}; +static const struct formats formats[] = { { "netascii", rewrite_access, validate_access, tftp_sendfile, tftp_recvfile, 1}, { @@ -1050,7 +1051,7 @@ int tftp(struct tftphdr *tp, int size) { char *cp, *end; int argn, ecode; - struct formats *pf = NULL; + const struct formats *pf = NULL; char *origfilename; char *filename, *mode = NULL; const char *errmsgptr; @@ -1418,7 +1419,7 @@ static FILE *file; * given as we have no login directory. */ static int validate_access(char *filename, int mode, - struct formats *pf, const char **errmsg) + const struct formats *pf, const char **errmsg) { struct stat stbuf; int i, len; @@ -1526,7 +1527,7 @@ static int validate_access(char *filename, int mode, /* * Send the requested file. */ -static void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_sendfile(const struct formats *pf, struct tftphdr *oap, int oacklen) { struct tftphdr *dp; struct tftphdr *ap; /* ack packet */ @@ -1624,7 +1625,7 @@ static void tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen) /* * Receive a file. */ -static void tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_recvfile(const struct formats *pf, struct tftphdr *oap, int oacklen) { struct tftphdr *dp; int n, size; From 2864d83feaac090865acb9c147c17d9793ef53dc Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 12 May 2011 19:16:17 -0700 Subject: [PATCH 24/65] tftpd: try to handle duplicate WRQ packets Duplicate WRQ packets can really hurt, since they end up accessing the same file. This attempts to lock the file, which should work for the case where a correctly implemented TFTP stack uses the same session ID (port number) for each retry; in any other case they look like multiple sessions to the same file and it is a crapshoot if we end up with the correct one. Signed-off-by: H. Peter Anvin --- configure.in | 6 ++++++ tftpd/tftpd.c | 48 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/configure.in b/configure.in index ca21af7..7ab7c5a 100644 --- a/configure.in +++ b/configure.in @@ -46,6 +46,7 @@ AC_CHECK_HEADERS(strings.h) AC_CHECK_HEADERS(sysexits.h) AC_CHECK_HEADERS(time.h) AC_CHECK_HEADERS(unistd.h) +AC_CHECK_HEADERS(sys/file.h) AC_CHECK_HEADERS(sys/filio.h) AC_CHECK_HEADERS(sys/stat.h) AC_CHECK_HEADERS(sys/time.h) @@ -140,6 +141,11 @@ PA_HEADER_DEFINES(fcntl.h, int, O_NONBLOCK) PA_HEADER_DEFINES(fcntl.h, int, O_BINARY) PA_HEADER_DEFINES(fcntl.h, int, O_TEXT) +PA_HEADER_DEFINES(fcntl.h, int, F_SETLK) + +PA_HEADER_DEFINES(sys/file.h, int, LOCK_SH) +PA_HEADER_DEFINES(sys/file.h, int, LOCK_EX) + AH_TEMPLATE([HAVE_SIGSETJMP], [Define if we have sigsetjmp, siglongjmp and sigjmp_buf.]) PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index c122d61..4c4d4ed 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -179,6 +179,26 @@ static struct rule *read_remap_rules(const char *file) } #endif +/* + * Rules for locking files; return 0 on success, -1 on failure + */ +static int lock_file(int fd, int lock_write) +{ +#if defined(HAVE_FCNTL) && defined(HAVE_F_SETLK_DEFINITION) + struct flock fl; + + fl.l_type = lock_write ? F_WRLCK : F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 0; /* Whole file */ + return fcntl(fd, F_SETLK, &fl); +#elif defined(HAVE_LOCK_SH_DEFINITION) + return flock(fd, lock_write ? LOCK_EX|LOCK_NB : LOCK_SH|LOCK_NB); +#else + return 0; /* Hope & pray... */ +#endif +} + static void set_socket_nonblock(int fd, int flag) { int err; @@ -1463,11 +1483,13 @@ static int validate_access(char *filename, int mode, * We use different a different permissions scheme if `cancreate' is * set. */ - wmode = O_WRONLY | - (cancreate ? O_CREAT : 0) | - (unixperms ? O_TRUNC : 0) | (pf->f_convert ? O_TEXT : O_BINARY); + wmode = O_WRONLY | (cancreate ? O_CREAT : 0) | (pf->f_convert ? O_TEXT : O_BINARY); rmode = O_RDONLY | (pf->f_convert ? O_TEXT : O_BINARY); +#ifndef HAVE_FTRUNCATE + wmode |= O_TRUNC; /* This really sucks on a dupe */ +#endif + fd = open(filename, mode == RRQ ? rmode : wmode, 0666); if (fd < 0) { switch (errno) { @@ -1486,6 +1508,10 @@ static int validate_access(char *filename, int mode, if (fstat(fd, &stbuf) < 0) exit(EX_OSERR); /* This shouldn't happen */ + /* A duplicate RRQ or (worse!) WRQ packet could really cause havoc... */ + if (lock_file(fd, mode != RRQ)) + exit(0); + if (mode == RRQ) { if (!unixperms && (stbuf.st_mode & (S_IREAD >> 6)) == 0) { *errmsg = "File must have global read permissions"; @@ -1500,15 +1526,15 @@ static int validate_access(char *filename, int mode, *errmsg = "File must have global write permissions"; return (EACCESS); } - - /* We didn't get to truncate the file at open() time */ -#ifdef HAVE_FTRUNCATE - if (ftruncate(fd, (off_t) 0)) { - *errmsg = "Cannot reset file size"; - return (EACCESS); - } -#endif } + +#ifdef HAVE_FTRUNCATE + /* We didn't get to truncate the file at open() time */ + if (ftruncate(fd, (off_t) 0)) { + *errmsg = "Cannot reset file size"; + return (EACCESS); + } +#endif tsize = 0; tsize_ok = 1; } From f3035c45bc50bb5cac87ca01e7ef6a12485184f8 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 10 Jun 2011 11:47:02 -0700 Subject: [PATCH 25/65] tftpd: simplify option parsing Simplify the option parsing to make use of the fact that all the options we support are integer options. This fixes a buffer overflow in the utimeout option. Reported-by: Timo Warns Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 154 ++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 81 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 4c4d4ed..dd3c982 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -114,18 +114,18 @@ static struct rule *rewrite_rules = NULL; int tftp(struct tftphdr *, int); static void nak(int, const char *); static void timer(int); -static void do_opt(char *, char *, char **); +static void do_opt(const char *, const char *, char **); -static int set_blksize(char *, char **); -static int set_blksize2(char *, char **); -static int set_tsize(char *, char **); -static int set_timeout(char *, char **); -static int set_utimeout(char *, char **); -static int set_rollover(char *, char **); +static int set_blksize(uintmax_t *); +static int set_blksize2(uintmax_t *); +static int set_tsize(uintmax_t *); +static int set_timeout(uintmax_t *); +static int set_utimeout(uintmax_t *); +static int set_rollover(uintmax_t *); struct options { const char *o_opt; - int (*o_fnc) (char *, char **); + int (*o_fnc)(uintmax_t *); } options[] = { {"blksize", set_blksize}, {"blksize2", set_blksize2}, @@ -1175,48 +1175,38 @@ static int blksize_set; /* * Set a non-standard block size (c.f. RFC2348) */ -static int set_blksize(char *val, char **ret) +static int set_blksize(uintmax_t *vp) { - static char b_ret[6]; - unsigned int sz; - char *vend; + uintmax_t sz = *vp; - sz = (unsigned int)strtoul(val, &vend, 10); - - if (blksize_set || *vend) + if (blksize_set) return 0; if (sz < 8) - return (0); + return 0; else if (sz > max_blksize) sz = max_blksize; - segsize = sz; - sprintf(*ret = b_ret, "%u", sz); - + *vp = segsize = sz; blksize_set = 1; - - return (1); + return 1; } /* * Set a power-of-two block size (nonstandard) */ -static int set_blksize2(char *val, char **ret) +static int set_blksize2(uintmax_t *vp) { - static char b_ret[6]; - unsigned int sz; - char *vend; + uintmax_t sz = *vp; - sz = (unsigned int)strtoul(val, &vend, 10); - - if (blksize_set || *vend) + if (blksize_set) return 0; if (sz < 8) return (0); else if (sz > max_blksize) sz = max_blksize; + else /* Convert to a power of two */ if (sz & (sz - 1)) { @@ -1227,29 +1217,23 @@ static int set_blksize2(char *val, char **ret) sz = sz1; } - segsize = sz; - sprintf(*ret = b_ret, "%u", sz); - + *vp = segsize = sz; blksize_set = 1; - - return (1); + return 1; } /* * Set the block number rollover value */ -static int set_rollover(char *val, char **ret) +static int set_rollover(uintmax_t *vp) { - uintmax_t ro; - char *vend; + uintmax_t ro = *vp; + + if (ro > 65535) + return 0; - ro = strtoumax(val, &vend, 10); - if (ro > 65535 || *vend) - return 0; - - rollover_val = (uint16_t)ro; - *ret = val; - return 1; + rollover_val = (uint16_t)ro; + return 1; } /* @@ -1257,22 +1241,18 @@ static int set_rollover(char *val, char **ret) * For netascii mode, we don't know the size ahead of time; * so reject the option. */ -static int set_tsize(char *val, char **ret) +static int set_tsize(uintmax_t *vp) { - static char b_ret[sizeof(uintmax_t) * CHAR_BIT / 3 + 2]; - uintmax_t sz; - char *vend; + uintmax_t sz = *vp; - sz = strtoumax(val, &vend, 10); - - if (!tsize_ok || *vend) + if (!tsize_ok) return 0; if (sz == 0) - sz = (uintmax_t) tsize; + sz = tsize; - sprintf(*ret = b_ret, "%" PRIuMAX, sz); - return (1); + *vp = sz; + return 1; } /* @@ -1280,74 +1260,86 @@ static int set_tsize(char *val, char **ret) * to be the (default) retransmission timeout, but being an * integer in seconds it seems a bit limited. */ -static int set_timeout(char *val, char **ret) +static int set_timeout(uintmax_t *vp) { - static char b_ret[4]; - unsigned long to; - char *vend; + uintmax_t to = *vp; - to = strtoul(val, &vend, 10); - - if (to < 1 || to > 255 || *vend) + if (to < 1 || to > 255) return 0; rexmtval = timeout = to * 1000000UL; maxtimeout = rexmtval * TIMEOUT_LIMIT; - sprintf(*ret = b_ret, "%lu", to); - return (1); + return 1; } /* Similar, but in microseconds. We allow down to 10 ms. */ -static int set_utimeout(char *val, char **ret) +static int set_utimeout(uintmax_t *vp) { - static char b_ret[4]; - unsigned long to; - char *vend; + uintmax_t to = *vp; - to = strtoul(val, &vend, 10); - - if (to < 10000UL || to > 255000000UL || *vend) + if (to < 10000UL || to > 255000000UL) return 0; rexmtval = timeout = to; maxtimeout = rexmtval * TIMEOUT_LIMIT; - sprintf(*ret = b_ret, "%lu", to); - return (1); + return 1; } /* - * Parse RFC2347 style options + * Conservative calculation for the size of a buffer which can hold an + * arbitrary integer */ -static void do_opt(char *opt, char *val, char **ap) +#define OPTBUFSIZE (sizeof(uintmax_t) * CHAR_BIT / 3 + 3) + +/* + * Parse RFC2347 style options; we limit the arguments to positive + * integers which matches all our current options. + */ +static void do_opt(const char *opt, const char *val, char **ap) { struct options *po; - char *ret; + char retbuf[OPTBUFSIZE]; + char *p = *ap; + size_t optlen, retlen; + char *vend; + uintmax_t v; /* Global option-parsing variables initialization */ blksize_set = 0; - if (!*opt) + if (!*opt || !*val) return; + errno = 0; + v = strtoumax(val, &vend, 10); + if (*vend || errno == ERANGE) + return; + for (po = options; po->o_opt; po++) if (!strcasecmp(po->o_opt, opt)) { - if (po->o_fnc(val, &ret)) { - if (*ap + strlen(opt) + strlen(ret) + 2 >= - ackbuf + sizeof(ackbuf)) { + if (po->o_fnc(&v)) { + optlen = strlen(opt); + retlen = sprintf(retbuf, "%"PRIuMAX, v); + + if (p + optlen + retlen + 2 >= ackbuf + sizeof(ackbuf)) { nak(EOPTNEG, "Insufficient space for options"); exit(0); } - *ap = strrchr(strcpy(strrchr(strcpy(*ap, opt), '\0') + 1, - ret), '\0') + 1; + + memcpy(p, opt, optlen+1); + p += optlen+1; + memcpy(p, retbuf, retlen+1); + p += retlen+1; } else { nak(EOPTNEG, "Unsupported option(s) requested"); exit(0); } break; } - return; + + *ap = p; } #ifdef WITH_REGEX From bd250a597f74408458bc7c8af02a52712d658965 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 22 Jun 2011 16:27:48 -0700 Subject: [PATCH 26/65] CHANGES: Document bug fix Signed-off-by: H. Peter Anvin --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 5238089..6df0d97 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,8 @@ Changes in 5.1: Fix handling of block number wraparound after a successful options negotiation. + Fix a buffer overflow in option parsing. + Changes in 5.0: Try to on platforms with getaddrinfo() without AI_ADDRCONFIG or From 464be3090b6ddfc9afb879887f76373dc61f6e24 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 22 Jun 2011 16:28:38 -0700 Subject: [PATCH 27/65] tftpd: add Intel copyright header Part of my Intel job now... Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index dd3c982..1873e70 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,6 +1,7 @@ /* * Copyright (c) 1983 Regents of the University of California. * Copyright (c) 1999-2009 H. Peter Anvin + * Copyright (c) 2011 Intel Corporation; author: H. Peter Anvin * All rights reserved. * * Redistribution and use in source and binary forms, with or without From f6a1282fecc1dbd4b71497ff335ceefe5b2e2541 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 22 Jun 2011 16:29:59 -0700 Subject: [PATCH 28/65] Update version for release 5.1 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index 819e07a..a75b92f 100644 --- a/version +++ b/version @@ -1 +1 @@ -5.0 +5.1 From badf05140d3c2408715a73a52c0f35887e337c04 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 22 Jun 2011 16:32:56 -0700 Subject: [PATCH 29/65] spec: BuildPreReq -> BuildRequires; need -devel package Signed-off-by: H. Peter Anvin --- tftp.spec.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftp.spec.in b/tftp.spec.in index 7a8c7b7..cd5e4cc 100644 --- a/tftp.spec.in +++ b/tftp.spec.in @@ -5,7 +5,7 @@ Release: 1 License: BSD Group: Applications/Internet Source0: http://www.kernel.org/pub/software/network/tftp/tftp-hpa-%{version}.tar.gz -BuildPreReq: tcp_wrappers +BuildRequires: tcp_wrappers-devel BuildRoot: %{_tmppath}/%{name}-root %description From c6d2c36b1a2b1986cab7aebb72d120fa118bdfeb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 8 Dec 2011 21:48:06 -0800 Subject: [PATCH 30/65] tftpd: the "is this address local" algorithm no longer works on Linux Linux no longer tries to match the local address with the remote one, so address_is_local() fails. Try instead to simply see if we can bind to the explicit address. Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 3ee5642..24abce3 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -51,52 +51,59 @@ struct in_pktinfo { #endif /* - * Check to see if this is a valid local address. If so, we should - * end up having the same local and remote address when trying to - * bind to it. + * Check to see if this is a valid local address, meaning that we can + * legally bind to it. */ static int address_is_local(const union sock_addr *addr) { - union sock_addr sa; + union sock_addr sa1, sa2; int sockfd = -1; int e; int rv = 0; socklen_t addrlen; + memcpy(&sa1, addr, sizeof sa1); + /* Multicast or universal broadcast address? */ - if (addr->sa.sa_family == AF_INET) { - if (ntohl(addr->si.sin_addr.s_addr) >= (224UL << 24)) + if (sa1.sa.sa_family == AF_INET) { + if (ntohl(sa1.si.sin_addr.s_addr) >= (224UL << 24)) return 0; + sa1.si.sin_port = 0; /* Any port */ } #ifdef HAVE_IPV6 - else if (addr->sa.sa_family == AF_INET6) { - if (IN6_IS_ADDR_MULTICAST(&addr->s6.sin6_addr)) + else if (sa1.sa.sa_family == AF_INET6) { + if (IN6_IS_ADDR_MULTICAST(&sa1.s6.sin6_addr)) return 0; + sa1.s6.sin6_port = 0; /* Any port */ } #endif else return 0; - sockfd = socket(addr->sa.sa_family, SOCK_DGRAM, 0); + sockfd = socket(sa1.sa.sa_family, SOCK_DGRAM, 0); if (sockfd < 0) goto err; - if (connect(sockfd, &addr->sa, SOCKLEN(addr))) + if (bind(sockfd, &sa1.sa, SOCKLEN(&sa1))) goto err; addrlen = SOCKLEN(addr); - if (getsockname(sockfd, (struct sockaddr *)&sa, &addrlen)) + if (getsockname(sockfd, (struct sockaddr *)&sa2, &addrlen)) goto err; - if (addr->sa.sa_family == AF_INET) - rv = sa.si.sin_addr.s_addr == addr->si.sin_addr.s_addr; + if (sa1.sa.sa_family != sa2.sa.sa_family) + goto err; + + if (sa2.sa.sa_family == AF_INET) + rv = sa1.si.sin_addr.s_addr == sa2.si.sin_addr.s_addr; #ifdef HAVE_IPV6 - else if (addr->sa.sa_family == AF_INET6) - rv = IN6_ARE_ADDR_EQUAL(&sa.s6.sin6_addr, &addr->s6.sin6_addr); + else if (sa2.sa.sa_family == AF_INET6) + rv = IN6_ARE_ADDR_EQUAL(&sa1.s6.sin6_addr, &sa2.s6.sin6_addr); #endif else rv = 0; - err: + +err: e = errno; if (sockfd >= 0) From f08a34ede181274efe845212737e352efbc6ff6d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 11 Dec 2011 14:13:13 -0800 Subject: [PATCH 31/65] CHANGES: document is address local fix Signed-off-by: H. Peter Anvin --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 6df0d97..6b7b53c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,8 @@ +Changes in 5.2: + Fix breakage on newer Linux when a single interface has + multiple IP addresses. + + Changes in 5.1: Add -P option to write a PID file. Patch by Ferenc Wagner. From b9708e22014e03c9fa35ff7ced20e46194c39dc6 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sun, 11 Dec 2011 14:13:52 -0800 Subject: [PATCH 32/65] Update version for release 5.2 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index a75b92f..ef425ca 100644 --- a/version +++ b/version @@ -1 +1 @@ -5.1 +5.2 From 128e6a3905fe12c546ff1eb7cb7d71334407872d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Thu, 1 Mar 2012 22:44:32 -0800 Subject: [PATCH 33/65] Support IPv6 on MacOS X systems Add feature test macros for MacOS X, and don't require IPV6_RECVPKTINFO to exist. Reported-by: YJZ Signed-off-by: H. Peter Anvin --- config.h | 7 +++++++ tftpd/recvfrom.c | 13 ++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/config.h b/config.h index 0e35438..b22d540 100644 --- a/config.h +++ b/config.h @@ -17,6 +17,13 @@ #ifndef CONFIG_H #define CONFIG_H 1 +/* Feature enables for specific environments */ +#ifdef __APPLE__ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1070 +#define __APPLE_USE_RFC_3542 1 +#endif +#endif + /* Must be included before we include any system headers! */ #include "aconfig.h" /* autogenerated configuration header */ diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 24abce3..d050b80 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -222,12 +222,15 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #ifdef HAVE_STRUCT_IN6_PKTINFO if (cmptr->cmsg_level == IPPROTO_IPV6 && - (cmptr->cmsg_type == IPV6_RECVPKTINFO || + ( +#ifdef IPV6_RECVPKTINFO + cmptr->cmsg_type == IPV6_RECVPKTINFO || +#endif cmptr->cmsg_type == IPV6_PKTINFO)) { - memcpy(&pktinfo6, CMSG_DATA(cmptr), - sizeof(struct in6_pktinfo)); - memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr, - sizeof(struct in6_addr)); + memcpy(&pktinfo6, CMSG_DATA(cmptr), + sizeof(struct in6_pktinfo)); + memcpy(&myaddr->s6.sin6_addr, &pktinfo6.ipi6_addr, + sizeof(struct in6_addr)); } #endif } From c89a63a441cdd176c52740bf386b78548f46d492 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 Jun 2014 12:37:33 -0700 Subject: [PATCH 34/65] tftp: convert IPv6-mapped IPv4 addresses to IPv4 If we receive IPv4 addresses mapped to IPv6, convert them back to IPv4 so that mapping scripts which use \i behave sanely. Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index d050b80..b613823 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -113,6 +113,25 @@ err: return rv; } +#ifdef HAVE_IPV6 +static void normalize_ip6_compat(union sock_addr *myaddr) +{ + static const uint8_t ip6_compat_prefix[12] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + struct sockaddr_in in; + + if (!memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix, + sizeof ip6_compat_prefix)) { + bzero(&in, sizeof in); + in.sin_family = AF_INET; + in.sin_port = myaddr->s6.sin6_port; + memcpy(&in.sin_addr, (const char *)&myaddr->s6.sin6_addr + + sizeof ip6_compat_prefix, sizeof in.sin_addr); + memcpy(&myaddr->si, &in, sizeof in); + } +} +#endif + int myrecvfrom(int s, void *buf, int len, unsigned int flags, struct sockaddr *from, socklen_t * fromlen, @@ -233,6 +252,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, sizeof(struct in6_addr)); } #endif + normalize_ip6_compat(myaddr); } #endif } From 18ee96a03f8656031b67dd13ad2fcadb5af7ffaf Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 Jun 2014 12:52:57 -0700 Subject: [PATCH 35/65] tftpd: allow IPv4/6-specific remapping rules Allow remapping rules to be conditional on IPv4 vs IPv6. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 18 ++++++++++++++++-- tftpd/remap.h | 2 +- tftpd/tftpd.c | 21 +++++++++++---------- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index 1e7abe7..6f5b409 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 2001-2007 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2014 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -31,6 +31,8 @@ #define RULE_RESTART 0x08 /* Restart at the top after matching this rule */ #define RULE_ABORT 0x10 /* Terminate processing with an error */ #define RULE_INVERSE 0x20 /* Execute if regex *doesn't* match */ +#define RULE_IPV4 0x40 /* IPv4 only */ +#define RULE_IPV6 0x80 /* IPv6 only */ struct rule { struct rule *next; @@ -223,6 +225,12 @@ static int parseline(char *line, struct rule *r, int lineno) case '~': r->rule_flags |= RULE_INVERSE; break; + case '4': + r->rule_flags |= RULE_IPV4; + break; + case '6': + r->rule_flags |= RULE_IPV6; + break; case 'G': case 'P': r->rule_mode = *p; @@ -326,7 +334,7 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const char *input, const struct rule *rules, - char mode, match_pattern_callback macrosub, + char mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); @@ -348,6 +356,12 @@ char *rewrite_string(const char *input, const struct rule *rules, if (ruleptr->rule_mode && ruleptr->rule_mode != mode) continue; /* Rule not applicable, try next */ + if ((ruleptr->rule_flags & RULE_IPV4) && (af != AF_INET)) + continue; /* Rule not applicable, try next */ + + if ((ruleptr->rule_flags & RULE_IPV6) && (af != AF_INET6)) + continue; /* Rule not applicable, try next */ + if (!deadman--) { syslog(LOG_WARNING, "remap: Breaking loop, input = %s, last = %s", input, diff --git a/tftpd/remap.h b/tftpd/remap.h index 69ca08d..7efcf6a 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -35,7 +35,7 @@ struct rule *parserulefile(FILE *); void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *, const struct rule *, char, +char *rewrite_string(const char *, const struct rule *, char, int, match_pattern_callback, const char **); #endif /* WITH_REGEX */ diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 1873e70..88d2812 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1,7 +1,7 @@ /* * Copyright (c) 1983 Regents of the University of California. * Copyright (c) 1999-2009 H. Peter Anvin - * Copyright (c) 2011 Intel Corporation; author: H. Peter Anvin + * Copyright (c) 2011-2014 Intel Corporation; author: H. Peter Anvin * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1039,18 +1039,18 @@ int main(int argc, char **argv) tp = (struct tftphdr *)buf; tp_opcode = ntohs(tp->th_opcode); if (tp_opcode == RRQ || tp_opcode == WRQ) - tftp(tp, n); + tftp(tp, n); exit(0); } -static char *rewrite_access(char *, int, const char **); +static char *rewrite_access(char *, int, int, const char **); static int validate_access(char *, int, const struct formats *, const char **); static void tftp_sendfile(const struct formats *, struct tftphdr *, int); static void tftp_recvfile(const struct formats *, struct tftphdr *, int); struct formats { const char *f_mode; - char *(*f_rewrite) (char *, int, const char **); + char *(*f_rewrite) (char *, int, int, const char **); int (*f_validate) (char *, int, const struct formats *, const char **); void (*f_send) (const struct formats *, struct tftphdr *, int); void (*f_recv) (const struct formats *, struct tftphdr *, int); @@ -1112,9 +1112,8 @@ int tftp(struct tftphdr *tp, int size) nak(EBADOP, "Unknown mode"); exit(0); } - if (!(filename = - (*pf->f_rewrite) (origfilename, tp_opcode, - &errmsgptr))) { + if (!(filename = (*pf->f_rewrite) + (origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) { nak(EACCESS, errmsgptr); /* File denied by mapping rule */ exit(0); } @@ -1398,12 +1397,13 @@ static int rewrite_macros(char macro, char *output) /* * Modify the filename, if applicable. If it returns NULL, deny the access. */ -static char *rewrite_access(char *filename, int mode, const char **msg) +static char *rewrite_access(char *filename, int mode, int af, + const char **msg) { if (rewrite_rules) { char *newname = rewrite_string(filename, rewrite_rules, - mode != RRQ ? 'P' : 'G', + mode != RRQ ? 'P' : 'G', af, rewrite_macros, msg); filename = newname; } @@ -1411,10 +1411,11 @@ static char *rewrite_access(char *filename, int mode, const char **msg) } #else -static char *rewrite_access(char *filename, int mode, const char **msg) +static char *rewrite_access(char *filename, int mode, int af, const char **msg) { (void)mode; /* Avoid warning */ (void)msg; + (void)af; return filename; } #endif From 2ac12abbc919678bc4ebeed20b1e714d08d2ddec Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 Jun 2014 12:55:26 -0700 Subject: [PATCH 36/65] tftpd.8: document IPv6 handling in remapping rules Document the "4" and "6" conditionals as well as how \i and \x handle IPv6 addresses. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.8.in | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 78b4cfb..b500d27 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -30,7 +30,7 @@ .\" SUCH DAMAGE. .\" .\"----------------------------------------------------------------------- */ -.TH TFTPD 8 "14 September 2009" "tftp-hpa @@VERSION@@" "System Manager's Manual" +.TH TFTPD 8 "7 June 2014" "tftp-hpa @@VERSION@@" "System Manager's Manual" .SH NAME .B tftpd \- Trivial File Transfer Protocol server @@ -295,6 +295,12 @@ This rule applies to GET (RRQ) requests only. .B P This rule applies to PUT (WRQ) requests only. .TP +.B 4 +This rule applies to IPv4 sessions only. +.TP +.B 6 +This rule applies to IPv6 sessions only. +.TP .B ~ Inverse the sense of this rule, i.e. execute the .I operation @@ -318,12 +324,14 @@ subexpressions, \\( ... \\), of the pattern. .TP \fB\\i\fP -The IP address of the requesting host, in dotted-quad notation -(e.g. 192.0.2.169). +The IP address of the requesting host, in dotted-quad notation for +IPv4 (e.g. 192.0.2.169) or conventional colon form for IPv6 +(e.g. 2001:db8::1). .TP \fB\\x\fP -The IP address of the requesting host, in hexadecimal notation -(e.g. C00002A9). +The IP address of the requesting host, in expanded hexadecimal +notation (e.g. C00002A9 for IPv4, or 20010DB8000000000000000000000001 +for IPv6). .TP \fB\\\\\fP Literal backslash. From 8ddf0d87d7463c21e28dd2bea6f3f42d4c92cb1d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Sat, 7 Jun 2014 13:00:46 -0700 Subject: [PATCH 37/65] tftp: drop "inline" from definition of usage() It is pointless and newer gcc say it is a lose. Signed-off-by: H. Peter Anvin --- tftp/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftp/main.c b/tftp/main.c index 1b8a881..b2f9059 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -188,7 +188,7 @@ char *xstrdup(const char *); const char *program; -static inline void usage(int errcode) +static void usage(int errcode) { fprintf(stderr, #ifdef HAVE_IPV6 From 18ac1e26f756dd47fef33f5f706b0ec8fa696216 Mon Sep 17 00:00:00 2001 From: Thorsten Glaser Date: Thu, 31 Jul 2014 16:29:41 +0930 Subject: [PATCH 38/65] __progname[] is provided by libc Rename local variable to tftpd_progname to avoid a clash with glibc global symbols and work around Debian bug #519006 (Closes: #564052). [ hpa: specifically, double-underscore symbols in C are reserved for the implementation, i.e. compiler/libc. ] Signed-off-by: Ron Lee Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 88d2812..91f5ae1 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -76,7 +76,7 @@ static int ai_fam = AF_INET; #define TRIES 6 /* Number of attempts to send each packet */ #define TIMEOUT_LIMIT ((1 << TRIES)-1) -const char *__progname; +const char *tftpd_progname; static int peer; static unsigned long timeout = TIMEOUT; /* Current timeout value */ static unsigned long rexmtval = TIMEOUT; /* Basic timeout value */ @@ -387,9 +387,9 @@ int main(int argc, char **argv) /* basename() is way too much of a pain from a portability standpoint */ p = strrchr(argv[0], '/'); - __progname = (p && p[1]) ? p + 1 : argv[0]; + tftpd_progname = (p && p[1]) ? p + 1 : argv[0]; - openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog(tftpd_progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); srand(time(NULL) ^ getpid()); @@ -938,14 +938,14 @@ int main(int argc, char **argv) syslog daemon gets restarted by the time we get here. */ if (secure && standalone) { closelog(); - openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); + openlog(tftpd_progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); } #ifdef HAVE_TCPWRAPPERS /* Verify if this was a legal request for us. This has to be done before the chroot, while /etc is still accessible. */ request_init(&wrap_request, - RQ_DAEMON, __progname, + RQ_DAEMON, tftpd_progname, RQ_FILE, fd, RQ_CLIENT_SIN, &from, RQ_SERVER_SIN, &myaddr, 0); sock_methods(&wrap_request); From 3ee2b278091ac0c60e9077096afa2135708de5e4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 31 Jul 2014 16:29:42 +0930 Subject: [PATCH 39/65] Update manpage to match source code for --map-file The manpage had --mapfile but the code had --map-file. Closes: #606267 in the Debian BTS Reported-By: Jim Paris Signed-off-by: Ron Lee Signed-off-by: H. Peter Anvin --- tftpd/tftpd.8.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index b500d27..71a712d 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -155,7 +155,7 @@ or .B utimeout option is negotiated. The default is 1000000 (1 second.) .TP -\fB\-\-mapfile\fP \fIremap-file\fP, \fB\-m\fP \fIremap-file\fP +\fB\-\-map-file\fP \fIremap-file\fP, \fB\-m\fP \fIremap-file\fP Specify the use of filename remapping. The .I remap-file is a file containing the remapping rules. See the section on filename @@ -238,7 +238,7 @@ option, but crash with an error if they actually get the option accepted by the server. .SH "FILENAME REMAPPING" The -.B \-\-mapfile +.B \-\-map-file option specifies a file which contains filename remapping rules. Each non-comment line (comments begin with hash marks, .BR # ) @@ -398,7 +398,7 @@ flag is used to set up a chroot() environment for the server to run in once a connection has been set up. .PP Finally, the filename remapping -.RB ( \-\-mapfile +.RB ( \-\-map-file flag) support can be used to provide a limited amount of additional access control. .SH "CONFORMING TO" From ff819b108a14f40612ebfb15937ae4a1b632bab9 Mon Sep 17 00:00:00 2001 From: Ron Lee Date: Thu, 31 Jul 2014 16:29:43 +0930 Subject: [PATCH 40/65] Make configure an order-only prerequisite of aconfig.h.in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On filesystems with subsecond resolution, like ext4, we can't trust the timestamp of aconfig.h.in since autoheader leaves it truncated to second resolution (apparently touch -r and cp -p can do this at the very least) while configure has full subsecond resolution, so it can look newer even when it was cleanly created first, leading to the build system looping trying to recreate all of that again ... So in the initial stage of a clean build we can get something like this: $ make spotless $ make autoconf rm -rf MCONFIG configure config.log aconfig.h *.cache autoconf rm -f aconfig.h.in aconfig.h autoheader $ stat configure aconfig.h.in File: ‘configure’ Device: 807h/2055d Inode: 9443466 Links: 1 Access: 2014-07-31 03:27:27.599293442 +0930 Modify: 2014-07-31 03:27:27.711290270 +0930 Change: 2014-07-31 03:27:27.711290270 +0930 File: ‘aconfig.h.in’ Device: 807h/2055d Inode: 9443467 Links: 1 Access: 2014-07-31 03:27:27.000000000 +0930 Modify: 2014-07-31 03:27:27.000000000 +0930 Change: 2014-07-31 03:27:27.903284841 +0930 And with a parallel build, that can then leave 'make all' racing to remove and recreate aconfig.h (and possibly more things), while it begins to build the first targets. Which then fail horribly like we see here: https://buildd.debian.org/status/fetch.php?pkg=tftp-hpa&arch=i386&ver=5.2%2B20140608-1&stamp=1406736363 Possibly we also need to move the actual build job into the rule for the 'all' target, so that the build system update prerequisites are guaranteed to be completed before it runs (as opposed to running in parallel with them), but this change might be enough for now. Signed-off-by: Ron Lee Signed-off-by: H. Peter Anvin --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9ff12d8..75a2042 100644 --- a/Makefile +++ b/Makefile @@ -58,8 +58,12 @@ aconfig.h: MCONFIG # Adding "configure" to the dependencies serializes this with running # autoconf, because there are apparently race conditions between -# autoconf and autoheader. -aconfig.h.in: configure.in configure aclocal.m4 +# autoconf and autoheader. And worse than that, even when autoconf +# cleanly returns first, autoheader will truncate the timestamp of +# aconfig.h.in to second resolution, so on a filesystem with subsecond +# resolution it can appear older than configure (which isn't truncated). +# So make it an order-only prerequisite to avoid looping regenerating it. +aconfig.h.in: configure.in aclocal.m4 | configure rm -f aconfig.h.in aconfig.h autoheader From 7678ae7f14e5aa6792c4486d4eedcb2db887872c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 7 Aug 2015 11:49:13 -0700 Subject: [PATCH 41/65] tftpd: Canonicalize all the addresses We cannot canonicalize myaddr and not the from address. We need to canonicalize both of them, or else we'll try to create an IPv4 socket and bind an IPv6-mapped IPv4 address to it, which is going to fail. Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 43 +++++++++++++++++++++++-------------------- tftpd/recvfrom.h | 3 +-- tftpd/tftpd.c | 5 +---- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index b613823..83ef29a 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -113,14 +113,15 @@ err: return rv; } -#ifdef HAVE_IPV6 static void normalize_ip6_compat(union sock_addr *myaddr) { +#ifdef HAVE_IPV6 static const uint8_t ip6_compat_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; struct sockaddr_in in; - if (!memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix, + if (myaddr->sa.sa_family == AF_INET6 && + !memcmp(&myaddr->s6.sin6_addr, ip6_compat_prefix, sizeof ip6_compat_prefix)) { bzero(&in, sizeof in); in.sin_family = AF_INET; @@ -129,13 +130,14 @@ static void normalize_ip6_compat(union sock_addr *myaddr) sizeof ip6_compat_prefix, sizeof in.sin_addr); memcpy(&myaddr->si, &in, sizeof in); } -} +#else + (void)myaddr; #endif +} int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t * fromlen, - union sock_addr *myaddr) + union sock_addr *from, union sock_addr *myaddr) { struct msghdr msg; struct iovec iov; @@ -168,16 +170,16 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, /* Try to enable getting the return address */ #ifdef IP_RECVDSTADDR - if (from->sa_family == AF_INET) + if (from->sa.sa_family == AF_INET) setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)); #endif #ifdef IP_PKTINFO - if (from->sa_family == AF_INET) + if (from->sa.sa_family == AF_INET) setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on)); #endif #ifdef HAVE_IPV6 #ifdef IPV6_RECVPKTINFO - if (from->sa_family == AF_INET6) + if (from->sa.sa_family == AF_INET6) setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)); #endif #endif @@ -186,8 +188,8 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, msg.msg_controllen = sizeof(control_un); msg.msg_flags = 0; - msg.msg_name = from; - msg.msg_namelen = *fromlen; + msg.msg_name = &from->sa; + msg.msg_namelen = sizeof(*from); iov.iov_base = buf; iov.iov_len = len; msg.msg_iov = &iov; @@ -196,11 +198,9 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, if ((n = recvmsg(s, &msg, flags)) < 0) return n; /* Error */ - *fromlen = msg.msg_namelen; - if (myaddr) { bzero(myaddr, sizeof(*myaddr)); - myaddr->sa.sa_family = from->sa_family; + myaddr->sa.sa_family = from->sa.sa_family; if (msg.msg_controllen < sizeof(struct cmsghdr) || (msg.msg_flags & MSG_CTRUNC)) @@ -209,7 +209,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; cmptr = CMSG_NXTHDR(&msg, cmptr)) { - if (from->sa_family == AF_INET) { + if (from->sa.sa_family == AF_INET) { myaddr->sa.sa_family = AF_INET; #ifdef IP_RECVDSTADDR if (cmptr->cmsg_level == IPPROTO_IP && @@ -230,7 +230,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #endif } #ifdef HAVE_IPV6 - else if (from->sa_family == AF_INET6) { + else if (from->sa.sa_family == AF_INET6) { myaddr->sa.sa_family = AF_INET6; #ifdef IP6_RECVDSTADDR if (cmptr->cmsg_level == IPPROTO_IPV6 && @@ -252,7 +252,6 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, sizeof(struct in6_addr)); } #endif - normalize_ip6_compat(myaddr); } #endif } @@ -268,6 +267,10 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, #endif } } + + normalize_ip6_compat(myaddr); + normalize_ip6_compat(from); + return n; } @@ -275,16 +278,16 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t * fromlen, - union sock_addr *myaddr) + union sock_addr *from, union sock_addr *myaddr) { /* There is no way we can get the local address, fudge it */ + socklen_t fromlen = sizeof(*from); bzero(myaddr, sizeof(*myaddr)); - myaddr->sa.sa_family = from->sa_family; + myaddr->sa.sa_family = from->sa.sa_family; sa_set_port(myaddr, htons(IPPORT_TFTP)); - return recvfrom(s, buf, len, flags, from, fromlen); + return recvfrom(s, buf, len, flags, from, &fromlen); } #endif diff --git a/tftpd/recvfrom.h b/tftpd/recvfrom.h index e3c4055..7773a0d 100644 --- a/tftpd/recvfrom.h +++ b/tftpd/recvfrom.h @@ -19,5 +19,4 @@ int myrecvfrom(int s, void *buf, int len, unsigned int flags, - struct sockaddr *from, socklen_t *fromlen, - union sock_addr *myaddr); + union sock_addr *from, union sock_addr *myaddr); diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 91f5ae1..364e7d2 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -93,7 +93,6 @@ static unsigned int max_blksize = MAX_SEGSIZE; static char tmpbuf[INET6_ADDRSTRLEN], *tmp_p; static union sock_addr from; -static socklen_t fromlen; static off_t tsize; static int tsize_ok; @@ -874,9 +873,7 @@ int main(int argc, char **argv) set_socket_nonblock(fd, 0); #endif - fromlen = sizeof(from); - n = myrecvfrom(fd, buf, sizeof(buf), 0, - (struct sockaddr *)&from, &fromlen, &myaddr); + n = myrecvfrom(fd, buf, sizeof(buf), 0, &from, &myaddr); if (n < 0) { if (E_WOULD_BLOCK(errno) || errno == EINTR) { From b2b34cecc8cbc18ff6f1fc00bda6ae6e9011e6c7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Fri, 7 Aug 2015 11:55:08 -0700 Subject: [PATCH 42/65] tftpd: Canonicalize myaddr before address_is_local() The comparisons for forbidden addresses in address_is_local() only work on canonicalized addresses. Also, work in the case myaddr is NULL (if we ever call it that way...) Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 83ef29a..d7ef500 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -255,6 +255,9 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, } #endif } + + normalize_ip6_compat(myaddr); + /* If the address is not a valid local address, * then bind to any address... */ @@ -268,7 +271,6 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, } } - normalize_ip6_compat(myaddr); normalize_ip6_compat(from); return n; From e83d71d394901e6d08605bcd7f35b2f112fe73ea Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:03:40 -0700 Subject: [PATCH 43/65] autoconf: rename configure.in to configure.ac Rename configure.in to configure.ac in accordance with current practice. Signed-off-by: H. Peter Anvin --- Makefile | 5 ++--- configure.in => configure.ac | 0 2 files changed, 2 insertions(+), 3 deletions(-) rename configure.in => configure.ac (100%) diff --git a/Makefile b/Makefile index 75a2042..8c779d3 100644 --- a/Makefile +++ b/Makefile @@ -63,11 +63,11 @@ aconfig.h: MCONFIG # aconfig.h.in to second resolution, so on a filesystem with subsecond # resolution it can appear older than configure (which isn't truncated). # So make it an order-only prerequisite to avoid looping regenerating it. -aconfig.h.in: configure.in aclocal.m4 | configure +aconfig.h.in: configure.ac aclocal.m4 | configure rm -f aconfig.h.in aconfig.h autoheader -configure: configure.in aclocal.m4 +configure: configure.ac aclocal.m4 rm -rf MCONFIG configure config.log aconfig.h *.cache autoconf @@ -76,4 +76,3 @@ version.h: version tftp.spec: tftp.spec.in version sed -e "s/@@VERSION@@/`cat version`/g" < $< > $@ || rm -f $@ - diff --git a/configure.in b/configure.ac similarity index 100% rename from configure.in rename to configure.ac From 9c0908a778d0c57226c8a4e8ac6582d35b83739c Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:09:38 -0700 Subject: [PATCH 44/65] autoconf: remove workarounds for some very old systems Remove workarounds for some ridiculously old systems; these workarounds are basically untestable. Signed-off-by: H. Peter Anvin --- config.h | 31 +++++------------------------- configure.ac | 54 ++++++++-------------------------------------------- 2 files changed, 13 insertions(+), 72 deletions(-) diff --git a/config.h b/config.h index b22d540..8c9af73 100644 --- a/config.h +++ b/config.h @@ -30,6 +30,10 @@ /* Standard includes */ #include +#include +#include +#include +#include #ifdef HAVE_SYS_TYPES_H #include @@ -39,21 +43,6 @@ #include #endif -#ifdef STDC_HEADERS -#include -#include -#else -#ifdef HAVE_STDLIB_H -#include -#endif -#endif - -#ifdef HAVE_MEMORY_H -#ifndef STDC_HEADERS -#include -#endif -#endif - #ifdef HAVE_STRING_H #include #endif @@ -80,15 +69,8 @@ #include #endif -#ifdef TIME_WITH_SYS_TIME +#ifdef HAVE_SYS_TIME_H #include -#include -#else -#if HAVE_SYS_TIME_H -#include -#else -#include -#endif #endif #ifdef HAVE_GRP_H @@ -99,9 +81,6 @@ #include #endif -#include -#include - #ifdef HAVE_SYS_SOCKET_H #include #else diff --git a/configure.ac b/configure.ac index 7ab7c5a..9a467b3 100644 --- a/configure.ac +++ b/configure.ac @@ -2,12 +2,13 @@ dnl dnl autoconf input file to generate MCONFIG dnl -AC_PREREQ(2.61) -AC_INIT(MCONFIG.in) +AC_PREREQ([2.71]) +AC_INIT +AC_CONFIG_SRCDIR([MCONFIG.in]) AC_PREFIX_DEFAULT(/usr) AC_USE_SYSTEM_EXTENSIONS -AC_ISC_POSIX +AC_SEARCH_LIBS([strerror],[cposix]) AC_PROG_CC AC_C_CONST @@ -30,21 +31,15 @@ PA_ADD_CFLAGS(-Wsign-compare) PA_ADD_CFLAGS(-pipe) PA_ADD_CFLAGS(-fno-strict-aliasing) -AC_HEADER_STDC AC_CHECK_HEADERS(inttypes.h) AC_CHECK_HEADERS(stdint.h) PA_CHECK_INTTYPES_H_SANE AC_CHECK_HEADERS(fcntl.h) AC_CHECK_HEADERS(grp.h) AC_CHECK_HEADERS(libgen.h) -AC_CHECK_HEADERS(memory.h) AC_CHECK_HEADERS(setjmp.h) -AC_CHECK_HEADERS(stddef.h) -AC_CHECK_HEADERS(stdlib.h) -AC_CHECK_HEADERS(string.h) AC_CHECK_HEADERS(strings.h) AC_CHECK_HEADERS(sysexits.h) -AC_CHECK_HEADERS(time.h) AC_CHECK_HEADERS(unistd.h) AC_CHECK_HEADERS(sys/file.h) AC_CHECK_HEADERS(sys/filio.h) @@ -53,7 +48,7 @@ AC_CHECK_HEADERS(sys/time.h) AC_CHECK_HEADERS(sys/types.h) AC_CHECK_HEADERS(arpa/inet.h) AC_CHECK_HEADERS(netdb.h) -AC_HEADER_TIME + dnl This is needed on some versions of FreeBSD... AC_CHECK_HEADERS(machine/param.h) AC_CHECK_HEADERS(sys/socket.h) @@ -78,44 +73,10 @@ dnl isn't among the list of standard headers that autoconf checks dnl but POSIX requires for socklen_t to be defined. dnl AC_CHECK_TYPES(socklen_t,,, -[ -#include +[AC_INCLUDES_DEFAULT #if HAVE_SYS_TYPES_H # include #endif -#if HAVE_SYS_STAT_H -# include -#endif -#if STDC_HEADERS -# include -# include -#else -# if HAVE_STDLIB_H -# include -# endif -#endif -#if HAVE_STRING_H -# if !STDC_HEADERS && HAVE_MEMORY_H -# include -# endif -# include -#endif -#if HAVE_STRINGS_H -# include -#endif -#if HAVE_INTTYPES_H -# include -#else -# if HAVE_STDINT_H -# include -# endif -#endif -#if HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -#endif ]) AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) @@ -300,4 +261,5 @@ AC_PROG_INSTALL fi] AC_CONFIG_HEADERS(aconfig.h) -AC_OUTPUT(MCONFIG) +AC_CONFIG_FILES([MCONFIG]) +AC_OUTPUT From 1f4b33a1f726d832742c370dc0c6d7f242424a5a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:34:57 -0700 Subject: [PATCH 45/65] Modernize: use sigaction() whenever possible. Remove uses of common. bsd_signal() may not be defined, and there is really no reason to even try to use it if sigaction() is avaiable; using sigaction() guarantees the semantics we really want. Replace uses of common variables with explicit instantiation and extern declarations in a header file. Signed-off-by: H. Peter Anvin --- common/Makefile | 2 +- common/signal.c | 42 ++++++++++++++++++++++++++++++++++++++++++ config.h | 11 ++++++++--- configure.ac | 4 +++- lib/Makefile | 2 -- lib/bsdsignal.c | 27 --------------------------- tftp/extern.h | 7 +++++-- tftp/main.c | 4 ++-- tftp/tftp.c | 7 +++---- 9 files changed, 64 insertions(+), 42 deletions(-) create mode 100644 common/signal.c delete mode 100644 lib/bsdsignal.c diff --git a/common/Makefile b/common/Makefile index a825213..2aaffb7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -4,7 +4,7 @@ VERSION = $(shell cat ../version) -include ../MCONFIG include ../MRULES -OBJS = tftpsubs.$(O) +OBJS = tftpsubs.$(O) signal.$(O) LIB = libcommon.a all: $(LIB) diff --git a/common/signal.c b/common/signal.c new file mode 100644 index 0000000..9119d57 --- /dev/null +++ b/common/signal.c @@ -0,0 +1,42 @@ +/* + * signal.c + * + * Use sigaction() to simulate BSD signal() + */ + +#include "config.h" + +#ifdef HAVE_SIGACTION + +sighandler_t tftp_signal(int signum, sighandler_t handler) +{ + struct sigaction action, oldaction; + + memset(&action, 0, sizeof action); + action.sa_handler = handler; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, signum); + action.sa_flags = SA_RESTART; + + if (sigaction(signum, &action, &oldaction)) + return SIG_ERR; + + return oldaction.sa_handler; +} + +#elif defined(HAVE_BSD_SIGNAL) + +sighandler_t tftp_signal(int signum, sighandler_t handler) +{ + return bsd_signal(signum, handler); +} + +#else + +/* This is dangerous at best. Let's hope it works. */ +sighandler_t tftp_signal(int signum, sighandler_t handler) +{ + return signal(signum, handler); +} + +#endif diff --git a/config.h b/config.h index 8c9af73..a6a402b 100644 --- a/config.h +++ b/config.h @@ -1,6 +1,6 @@ /* -*- c -*- ------------------------------------------------------------- * * - * Copyright 2001-2006 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -279,9 +279,14 @@ typedef int socklen_t; void *xmalloc(size_t); char *xstrdup(const char *); -#ifndef HAVE_BSD_SIGNAL -void (*bsd_signal(int, void (*)(int))) (int); +#ifndef HAVE_SIGHANDLER_T +typedef void (*sighandler_t)(int); #endif +#ifndef SIG_ERR +#define SIG_ERR NULL +#endif +sighandler_t tftp_signal(int, sighandler_t); + #ifndef HAVE_DUP2 int dup2(int, int); #endif diff --git a/configure.ac b/configure.ac index 9a467b3..04b6f98 100644 --- a/configure.ac +++ b/configure.ac @@ -89,6 +89,9 @@ AC_CHECK_FUNCS(setreuid) AC_CHECK_FUNCS(setregid) AC_CHECK_FUNCS(initgroups) AC_CHECK_FUNCS(setgroups) +AC_CHECK_FUNCS(sigaction) +AC_CHECK_FUNCS(bsd_signal) +AC_CHECK_TYPES(sighandler_t) dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged. AC_CHECK_FUNCS(strtoumax) @@ -120,7 +123,6 @@ OBJROOT=`pwd` XTRA=false PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) -PA_SEARCH_LIBS_AND_ADD(bsd_signal, bsd, bsdsignal) PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long) PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) if $pa_add_getaddrinfo diff --git a/lib/Makefile b/lib/Makefile index a43ce19..5dd83d0 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -25,5 +25,3 @@ libxtra.a: $(LIBOBJS) -rm -f libxtra.a $(AR) libxtra.a $(LIBOBJS) $(RANLIB) libxtra.a - - diff --git a/lib/bsdsignal.c b/lib/bsdsignal.c deleted file mode 100644 index 0aae136..0000000 --- a/lib/bsdsignal.c +++ /dev/null @@ -1,27 +0,0 @@ -/* - * bsdsignal.c - * - * Use sigaction() to simulate BSD signal() - */ - -#include "config.h" - -void (*bsd_signal(int signum, void (*handler) (int))) (int) { - struct sigaction action, oldaction; - - memset(&action, 0, sizeof action); - action.sa_handler = handler; - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, signum); - action.sa_flags = SA_RESTART; - - if (sigaction(signum, &action, &oldaction) == -1) { -#ifdef SIG_ERR - return SIG_ERR; -#else - return NULL; -#endif - } - - return oldaction.sa_handler; -} diff --git a/tftp/extern.h b/tftp/extern.h index 78474fc..9c578e6 100644 --- a/tftp/extern.h +++ b/tftp/extern.h @@ -31,10 +31,13 @@ * SUCH DAMAGE. */ -#ifndef RECVFILE_H -#define RECVFILE_H +#ifndef EXTERN_H +#define EXTERN_H + +#include "config.h" void tftp_recvfile(int, const char *, const char *); void tftp_sendfile(int, const char *, const char *); +extern sigjmp_buf toplevel; #endif diff --git a/tftp/main.c b/tftp/main.c index b2f9059..438f48a 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -305,7 +305,7 @@ int main(int argc, char *argv[]) sp->s_proto = (char *)"udp"; } - bsd_signal(SIGINT, intr); + tftp_signal(SIGINT, intr); if (peerargc) { /* Set peer */ @@ -768,7 +768,7 @@ void intr(int sig) { (void)sig; /* Quiet unused warning */ - bsd_signal(SIGALRM, SIG_IGN); + tftp_signal(SIGALRM, SIG_IGN); alarm(0); siglongjmp(toplevel, -1); } diff --git a/tftp/tftp.c b/tftp/tftp.c index d15da22..aecdeb9 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -48,8 +48,7 @@ extern int maxtimeout; #define PKTSIZE SEGSIZE+4 char ackbuf[PKTSIZE]; int timeout; -sigjmp_buf toplevel; -sigjmp_buf timeoutbuf; +static sigjmp_buf timeoutbuf; static void nak(int, const char *); static int makerequest(int, const char *, struct tftphdr *, const char *); @@ -85,7 +84,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode) is_request = 1; /* First packet is the actual WRQ */ amount = 0; - bsd_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer); do { if (is_request) { size = makerequest(WRQ, name, dp, mode) - 4; @@ -191,7 +190,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode) firsttrip = 1; amount = 0; - bsd_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer); do { if (firsttrip) { size = makerequest(RRQ, name, ap, mode); From aaaa76e8e7a8a81e71b5ee1df1d9d31c37f69a4d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:37:44 -0700 Subject: [PATCH 46/65] tftpd: explicitly declare timer() static Declare the function timer() as static, which is what it ought to be. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 364e7d2..f6d0565 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -152,7 +152,7 @@ static void handle_exit(int sig) } /* Handle timeout signal or timeout event */ -void timer(int sig) +static void timer(int sig) { (void)sig; /* Suppress unused warning */ timeout <<= 1; From 15c4f369ee741e9205dc28ce631aaf6799193b04 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 15:40:13 -0700 Subject: [PATCH 47/65] Update version for release 5.3 --- version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version b/version index ef425ca..d346e2a 100644 --- a/version +++ b/version @@ -1 +1 @@ -5.2 +5.3 From 1dc6d558115746f50e78ba8d84915a56ae66ec5f Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 16:01:54 -0700 Subject: [PATCH 48/65] tftpd: handle restricted user namespaces on Linux If the tftpd process lacks the privilege to set its uid, gid, or groups, then assume it is already restricted in the way the administrator requested, if and only if EPERM is returned. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index f6d0565..9abf009 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -374,6 +374,7 @@ int main(int argc, char **argv) int spec_umask = 0; int c; int setrv; + int die; int waittime = 900; /* Default time to wait for a connect */ const char *user = "nobody"; /* Default user */ char *p, *ep; @@ -974,21 +975,30 @@ int main(int argc, char **argv) exit(EX_IOERR); } - /* Set up the supplementary group access list if possible */ - /* /etc/group still need to be accessible at this point */ + /* Set up the supplementary group access list if possible + /etc/group still need to be accessible at this point. + If we get EPERM, this is already a restricted process, e.g. + using user namespaces on Linux. */ + setrv = -1; + die = 0; #ifdef HAVE_INITGROUPS setrv = initgroups(user, pw->pw_gid); - if (setrv) { + if (setrv && errno != EPERM) { syslog(LOG_ERR, "cannot set groups for user %s", user); - exit(EX_OSERR); + die = EX_OSERR; } -#else +#endif #ifdef HAVE_SETGROUPS - if (setgroups(0, NULL)) { - syslog(LOG_ERR, "cannot clear group list"); + if (setrv) { + setrv = setgroups(0, NULL); + if (setrv && errno != EPERM) { + syslog(LOG_ERR, "cannot clear group list"); + die = EX_OSERR; + } } #endif -#endif + if (die) + exit(die); /* Chroot and drop privileges */ if (secure) { @@ -1000,11 +1010,15 @@ int main(int argc, char **argv) chdir("/"); /* Cygwin chroot() bug workaround */ #endif } + #ifdef HAVE_SETREGID setrv = setregid(pw->pw_gid, pw->pw_gid); #else setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif + if (setrv && errno == EPERM) { + setrv = 0; /* Already restricted */ + } #ifdef HAVE_SETREUID setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); @@ -1013,6 +1027,9 @@ int main(int argc, char **argv) setrv = setrv || setuid(pw->pw_uid) || (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); #endif + if (setrv && errno == EPERM) { + setrv = 0; /* Already restricted */ + } if (setrv) { syslog(LOG_ERR, "cannot drop privileges: %m"); From 6f96fcd1b669dbf9c0a3d96d26d370117fddcadd Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:16:06 -0700 Subject: [PATCH 49/65] tftpd: allow a rewrite rule to probe for the existence of a file This adds an "E" flag to the rewrite rules, which exits rule processing if and only if the result is a valid, accessible filename. If combined with "r", the rewrite is cancelled if the rule is not applied. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 72 +++++++++++++++++++++++++++++++++++------------- tftpd/remap.h | 4 ++- tftpd/tftpd.8.in | 13 +++++++-- tftpd/tftpd.c | 52 +++++++++++++++++----------------- tftpd/tftpd.h | 9 ++++++ 5 files changed, 103 insertions(+), 47 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index 6f5b409..c7c8f85 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -1,6 +1,6 @@ /* ----------------------------------------------------------------------- * * - * Copyright 2001-2014 H. Peter Anvin - All Rights Reserved + * Copyright 2001-2024 H. Peter Anvin - All Rights Reserved * * This program is free software available under the same license * as the "OpenBSD" operating system, distributed at @@ -34,6 +34,8 @@ #define RULE_IPV4 0x40 /* IPv4 only */ #define RULE_IPV6 0x80 /* IPv6 only */ +#define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */ + struct rule { struct rule *next; int nrule; @@ -213,6 +215,9 @@ static int parseline(char *line, struct rule *r, int lineno) case 'e': r->rule_flags |= RULE_EXIT; break; + case 'E': + r->rule_flags |= RULE_HASFILE; + break; case 's': r->rule_flags |= RULE_RESTART; break; @@ -244,15 +249,21 @@ static int parseline(char *line, struct rule *r, int lineno) } } - /* RULE_GLOBAL only applies when RULE_REWRITE specified */ - if (!(r->rule_flags & RULE_REWRITE)) - r->rule_flags &= ~RULE_GLOBAL; - - if ((r->rule_flags & (RULE_INVERSE | RULE_REWRITE)) == - (RULE_INVERSE | RULE_REWRITE)) { - syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", - lineno, line); - return -1; /* Error */ + if (r->rule_flags & RULE_REWRITE) { + if (r->rule_flags & RULE_INVERSE) { + syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", + lineno, line); + return -1; /* Error */ + } + if ((r->rule_flags & (RULE_GLOBAL|RULE_HASFILE)) + == (RULE_GLOBAL|RULE_HASFILE)) { + syslog(LOG_ERR, "E rules cannot be combined with g, line %d: %s\n", + lineno, line); + return -1; /* Error */ + } + } else { + /* RULE_GLOBAL is meaningless without RULE_REWRITE */ + r->rule_flags &= ~RULE_GLOBAL; } /* Read and compile the regex */ @@ -333,12 +344,14 @@ void freerules(struct rule *r) } /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *input, const struct rule *rules, +char *rewrite_string(const struct formats *pf, + const char *input, const struct rule *rules, char mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); char *newstr; + const char *accerr; const struct rule *ruleptr = rules; regmatch_t pmatch[10]; int len; @@ -410,13 +423,34 @@ char *rewrite_string(const char *input, const struct rule *rules, newstr = tfmalloc(len + 1); genmatchstring(newstr, ruleptr->pattern, current, pmatch, macrosub); - free(current); - current = newstr; - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: rewrite: %s", - ruleptr->nrule, current); - } - } + if ((ruleptr->rule_flags & RULE_HASFILE) && + pf->f_validate(newstr, mode == 'G' ? RRQ : WRQ, + pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", + ruleptr->nrule, accerr, newstr); + } + free(newstr); + was_match = 0; + break; + } + free(current); + current = newstr; + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: rewrite: %s", + ruleptr->nrule, current); + } + } else if (ruleptr->rule_flags & RULE_HASFILE) { + if (pf->f_validate(current, mode == 'G' ? RRQ : WRQ, + pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", + ruleptr->nrule, accerr); + } + was_match = 0; + break; + } + } } else { break; /* No match, terminate unconditionally */ } @@ -426,7 +460,7 @@ char *rewrite_string(const char *input, const struct rule *rules, if (was_match) { was_match = 0; - if (ruleptr->rule_flags & RULE_EXIT) { + if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { if (verbosity >= 3) { syslog(LOG_INFO, "remap: rule %d: exit", ruleptr->nrule); diff --git a/tftpd/remap.h b/tftpd/remap.h index 7efcf6a..2290aad 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -35,7 +35,9 @@ struct rule *parserulefile(FILE *); void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ -char *rewrite_string(const char *, const struct rule *, char, int, +struct formats; +char *rewrite_string(const struct formats *, const char *, + const struct rule *, char, int, match_pattern_callback, const char **); #endif /* WITH_REGEX */ diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 71a712d..c9722cf 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -281,6 +281,15 @@ case-insensitively. By default it is case sensitive. .B e If this rule matches, end rule processing after executing the rule. .TP +.B E +If this rule matches, +\fIand the result matches a filename that can be transferred\fP, +end rule processing after executing the rule. If this is combined with +.BR r , +then if the substitution does \fInot\fP result in a valid filename, +the substitution is undone. This cannot be combined with +.BR g . +.TP .B s If this rule matches, start rule processing over from the very first rule after executing this rule. @@ -307,10 +316,10 @@ Inverse the sense of this rule, i.e. execute the only if the .I regex .I doesn't -match. Cannot used together with +match. Cannot used together with .BR r . .PP -The following escape sequences are recognized as part of the +The following escape sequences are recognized as part of a .IR "replacement pattern" : .TP \fB\\0\fP diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 9abf009..3349894 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -106,11 +106,12 @@ int portrange = 0; unsigned int portrange_from, portrange_to; int verbosity = 0; -struct formats; #ifdef WITH_REGEX static struct rule *rewrite_rules = NULL; #endif +static FILE *file; + int tftp(struct tftphdr *, int); static void nak(int, const char *); static void timer(int); @@ -162,14 +163,14 @@ static void timer(int sig) } #ifdef WITH_REGEX -static struct rule *read_remap_rules(const char *file) +static struct rule *read_remap_rules(const char *rulefile) { FILE *f; struct rule *rulep; - f = fopen(file, "rt"); + f = fopen(rulefile, "rt"); if (!f) { - syslog(LOG_ERR, "Cannot open map file: %s: %m", file); + syslog(LOG_ERR, "Cannot open map file: %s: %m", rulefile); exit(EX_NOINPUT); } rulep = parserulefile(f); @@ -1057,19 +1058,12 @@ int main(int argc, char **argv) exit(0); } -static char *rewrite_access(char *, int, int, const char **); +static char *rewrite_access(const struct formats *, + char *, int, int, const char **); static int validate_access(char *, int, const struct formats *, const char **); static void tftp_sendfile(const struct formats *, struct tftphdr *, int); static void tftp_recvfile(const struct formats *, struct tftphdr *, int); -struct formats { - const char *f_mode; - char *(*f_rewrite) (char *, int, int, const char **); - int (*f_validate) (char *, int, const struct formats *, const char **); - void (*f_send) (const struct formats *, struct tftphdr *, int); - void (*f_recv) (const struct formats *, struct tftphdr *, int); - int f_convert; -}; static const struct formats formats[] = { { "netascii", rewrite_access, validate_access, tftp_sendfile, @@ -1126,8 +1120,9 @@ int tftp(struct tftphdr *tp, int size) nak(EBADOP, "Unknown mode"); exit(0); } + file = NULL; if (!(filename = (*pf->f_rewrite) - (origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) { + (pf, origfilename, tp_opcode, from.sa.sa_family, &errmsgptr))) { nak(EACCESS, errmsgptr); /* File denied by mapping rule */ exit(0); } @@ -1150,12 +1145,18 @@ int tftp(struct tftphdr *tp, int size) tmp_p, origfilename, filename); } - ecode = - (*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr); - if (ecode) { - nak(ecode, errmsgptr); - exit(0); - } + /* + * If "file" is already set, then a file was already validated + * and opened during remap processing. + */ + if (!file) { + ecode = + (*pf->f_validate) (filename, tp_opcode, pf, &errmsgptr); + if (ecode) { + nak(ecode, errmsgptr); + exit(0); + } + } opt = ++cp; } else if (argn & 1) { val = ++cp; @@ -1411,12 +1412,12 @@ static int rewrite_macros(char macro, char *output) /* * Modify the filename, if applicable. If it returns NULL, deny the access. */ -static char *rewrite_access(char *filename, int mode, int af, - const char **msg) +static char *rewrite_access(const struct formats *pf, char *filename, + int mode, int af, const char **msg) { if (rewrite_rules) { char *newname = - rewrite_string(filename, rewrite_rules, + rewrite_string(pf, filename, rewrite_rules, mode != RRQ ? 'P' : 'G', af, rewrite_macros, msg); filename = newname; @@ -1425,8 +1426,10 @@ static char *rewrite_access(char *filename, int mode, int af, } #else -static char *rewrite_access(char *filename, int mode, int af, const char **msg) +static char *rewrite_access(const struct formats *pf, char *filename, + int mode, int af, const char **msg) { + (void)pf; (void)mode; /* Avoid warning */ (void)msg; (void)af; @@ -1434,7 +1437,6 @@ static char *rewrite_access(char *filename, int mode, int af, const char **msg) } #endif -static FILE *file; /* * Validate file access. Since we * have no uid or gid, for now require diff --git a/tftpd/tftpd.h b/tftpd/tftpd.h index e1d8bf0..277e5d2 100644 --- a/tftpd/tftpd.h +++ b/tftpd/tftpd.h @@ -23,4 +23,13 @@ char *tfstrdup(const char *); extern int verbosity; +struct formats { + const char *f_mode; + char *(*f_rewrite) (const struct formats *, char *, int, int, const char **); + int (*f_validate) (char *, int, const struct formats *, const char **); + void (*f_send) (const struct formats *, struct tftphdr *, int); + void (*f_recv) (const struct formats *, struct tftphdr *, int); + int f_convert; +}; + #endif From 351907e3f0021876abeba14ac47081cf2c21b50a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:28:53 -0700 Subject: [PATCH 50/65] tftpd: handle rule filter flags more cleanly Instead of a bunch of ad hoc tests, keep a bitmask of flags that would keep this rule from being executed. This also removes the ugly hack of converting the request mode between opcode and character encodings for really no good reason. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 38 ++++++++++++++++++++------------------ tftpd/remap.h | 2 +- tftpd/tftpd.c | 3 +-- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index c7c8f85..16801c1 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -35,12 +35,13 @@ #define RULE_IPV6 0x80 /* IPv6 only */ #define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */ +#define RULE_RRQ 0x200 /* Get (read) only */ +#define RULE_WRQ 0x400 /* Put (write) only */ struct rule { struct rule *next; int nrule; - int rule_flags; - char rule_mode; + unsigned int rule_flags; regex_t rx; const char *pattern; }; @@ -164,7 +165,7 @@ static int readescstring(char *buf, char **str) int wasbs = 0, len = 0; while (*p && isspace(*p)) - p++; + p++; if (!*p) { *buf = '\0'; @@ -237,9 +238,11 @@ static int parseline(char *line, struct rule *r, int lineno) r->rule_flags |= RULE_IPV6; break; case 'G': + r->rule_flags |= RULE_RRQ; + break; case 'P': - r->rule_mode = *p; - break; + r->rule_flags |= RULE_WRQ; + break; default: syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", @@ -346,7 +349,7 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const struct formats *pf, const char *input, const struct rule *rules, - char mode, int af, match_pattern_callback macrosub, + int mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); @@ -357,6 +360,7 @@ char *rewrite_string(const struct formats *pf, int len; int was_match = 0; int deadman = DEADMAN_MAX_STEPS; + unsigned int bad_flags; /* Default error */ *errmsg = "Remap table failure"; @@ -365,15 +369,15 @@ char *rewrite_string(const struct formats *pf, syslog(LOG_INFO, "remap: input: %s", current); } + bad_flags = 0; + if (mode != RRQ) bad_flags |= RULE_RRQ; + if (mode != WRQ) bad_flags |= RULE_WRQ; + if (af != AF_INET) bad_flags |= RULE_IPV4; + if (af != AF_INET6) bad_flags |= RULE_IPV6; + for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { - if (ruleptr->rule_mode && ruleptr->rule_mode != mode) - continue; /* Rule not applicable, try next */ - - if ((ruleptr->rule_flags & RULE_IPV4) && (af != AF_INET)) - continue; /* Rule not applicable, try next */ - - if ((ruleptr->rule_flags & RULE_IPV6) && (af != AF_INET6)) - continue; /* Rule not applicable, try next */ + if (ruleptr->rule_flags & bad_flags) + continue; /* This rule is excluded by flags */ if (!deadman--) { syslog(LOG_WARNING, @@ -424,8 +428,7 @@ char *rewrite_string(const struct formats *pf, genmatchstring(newstr, ruleptr->pattern, current, pmatch, macrosub); if ((ruleptr->rule_flags & RULE_HASFILE) && - pf->f_validate(newstr, mode == 'G' ? RRQ : WRQ, - pf, &accerr)) { + pf->f_validate(newstr, mode, pf, &accerr)) { if (verbosity >= 3) { syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", ruleptr->nrule, accerr, newstr); @@ -441,8 +444,7 @@ char *rewrite_string(const struct formats *pf, ruleptr->nrule, current); } } else if (ruleptr->rule_flags & RULE_HASFILE) { - if (pf->f_validate(current, mode == 'G' ? RRQ : WRQ, - pf, &accerr)) { + if (pf->f_validate(current, mode, pf, &accerr)) { if (verbosity >= 3) { syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", ruleptr->nrule, accerr); diff --git a/tftpd/remap.h b/tftpd/remap.h index 2290aad..a9954ab 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -37,7 +37,7 @@ void freerules(struct rule *); /* Execute a rule set on a string; returns a malloc'd new string. */ struct formats; char *rewrite_string(const struct formats *, const char *, - const struct rule *, char, int, + const struct rule *, int, int, match_pattern_callback, const char **); #endif /* WITH_REGEX */ diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 3349894..14182f3 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1417,8 +1417,7 @@ static char *rewrite_access(const struct formats *pf, char *filename, { if (rewrite_rules) { char *newname = - rewrite_string(pf, filename, rewrite_rules, - mode != RRQ ? 'P' : 'G', af, + rewrite_string(pf, filename, rewrite_rules, mode, af, rewrite_macros, msg); filename = newname; } From b9f2335e88dcb3939015843c7143f1533c755a46 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:35:14 -0700 Subject: [PATCH 51/65] tftpd: shut up one more setjmp complaint One more place where the compiler complains about setjmp. Work around it by creating yet another static variable. Ugly, but it works. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 14182f3..d9b3f46 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1660,17 +1660,21 @@ static void tftp_sendfile(const struct formats *pf, struct tftphdr *oap, int oac /* * Receive a file. */ -static void tftp_recvfile(const struct formats *pf, struct tftphdr *oap, int oacklen) +static void tftp_recvfile(const struct formats *pf, + struct tftphdr *oack, int oacklen) { struct tftphdr *dp; int n, size; /* These are "static" to avoid longjmp funnies */ + static struct tftphdr *oap; static struct tftphdr *ap; /* ack buffer */ static u_short block = 0; static int acksize; u_short dp_opcode, dp_block; unsigned long r_timeout; + oap = oack; + dp = w_init(); do { timeout = rexmtval; From 416046e2ad67508869ce472d8b8b0dd5578db0d7 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:49:21 -0700 Subject: [PATCH 52/65] tftpd: call setgroups() before initgroups() Unconditionally call setgroups() before calling initgroups(). That way if initgroups() fails for some reason (e.g. it is unable to access /etc/groups or the equivalent) then at least the group list will be empty. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index d9b3f46..2db7977 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -980,23 +980,20 @@ int main(int argc, char **argv) /etc/group still need to be accessible at this point. If we get EPERM, this is already a restricted process, e.g. using user namespaces on Linux. */ - setrv = -1; die = 0; +#ifdef HAVE_SETGROUPS + setrv = setgroups(0, NULL); + if (setrv && errno != EPERM) { + syslog(LOG_ERR, "cannot clear group list"); + die = EX_OSERR; + } +#endif #ifdef HAVE_INITGROUPS setrv = initgroups(user, pw->pw_gid); if (setrv && errno != EPERM) { syslog(LOG_ERR, "cannot set groups for user %s", user); die = EX_OSERR; } -#endif -#ifdef HAVE_SETGROUPS - if (setrv) { - setrv = setgroups(0, NULL); - if (setrv && errno != EPERM) { - syslog(LOG_ERR, "cannot clear group list"); - die = EX_OSERR; - } - } #endif if (die) exit(die); From 99112f02064359f5070c82298aa9a6f4dd58e535 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 17:52:42 -0700 Subject: [PATCH 53/65] tftpd: allow initgroups() to rescue setgroups() failure If setgroups() fails, but initgroups() succeeds (somehow) then allow the process to continue, as initgroups() ought to have set the group list properly. Signed-off-by: H. Peter Anvin --- tftpd/tftpd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index 2db7977..e410d42 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -990,7 +990,9 @@ int main(int argc, char **argv) #endif #ifdef HAVE_INITGROUPS setrv = initgroups(user, pw->pw_gid); - if (setrv && errno != EPERM) { + if (!setrv) { + die = 0; + } else if (errno != EPERM) { syslog(LOG_ERR, "cannot set groups for user %s", user); die = EX_OSERR; } From 5e8d5c24b260b2e9e25a248a93eddc11bc9260ac Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Wed, 29 May 2024 18:10:41 -0700 Subject: [PATCH 54/65] tftpd: Use setres*id() if available POSIX apparently doesn't clearly specify the behavior of the saved ID when calling setre*id(). If the system has setres*id() then use it to make absolutely sure that the ID changes cannot be undone. Signed-off-by: H. Peter Anvin --- configure.ac | 2 ++ tftpd/tftpd.c | 12 ++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 04b6f98..8770431 100644 --- a/configure.ac +++ b/configure.ac @@ -86,7 +86,9 @@ AC_CHECK_FUNCS(setsid) AC_CHECK_FUNCS(recvmsg) AC_CHECK_FUNCS(ftruncate) AC_CHECK_FUNCS(setreuid) +AC_CHECK_FUNCS(setresuid) AC_CHECK_FUNCS(setregid) +AC_CHECK_FUNCS(setresgid) AC_CHECK_FUNCS(initgroups) AC_CHECK_FUNCS(setgroups) AC_CHECK_FUNCS(sigaction) diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index e410d42..fcb29aa 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -1011,16 +1011,20 @@ int main(int argc, char **argv) #endif } -#ifdef HAVE_SETREGID +#ifdef HAVE_SETRESGID + setrv = setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid); +#elif defined(HAVE_SETREGID) setrv = setregid(pw->pw_gid, pw->pw_gid); #else setrv = setegid(pw->pw_gid) || setgid(pw->pw_gid); #endif if (setrv && errno == EPERM) { - setrv = 0; /* Already restricted */ + setrv = 0; /* Assume already restricted by system policy */ } -#ifdef HAVE_SETREUID +#ifdef HAVE_SETRESUID + setrv = setrv || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); +#elif defined(HAVE_SETREUID) setrv = setrv || setreuid(pw->pw_uid, pw->pw_uid); #else /* Important: setuid() must come first */ @@ -1028,7 +1032,7 @@ int main(int argc, char **argv) (geteuid() != pw->pw_uid && seteuid(pw->pw_uid)); #endif if (setrv && errno == EPERM) { - setrv = 0; /* Already restricted */ + setrv = 0; /* Assume already restricted by system policy */ } if (setrv) { From e52af4207a6e09a1ba21f61d6f85d8b2b1e6efc0 Mon Sep 17 00:00:00 2001 From: Yegor Yefremov Date: Fri, 31 May 2024 09:03:28 +0200 Subject: [PATCH 55/65] Add EditorConfig support Provide common indentation configuration for the source files. For more information, visit https://editorconfig.org/. Signed-off-by: Yegor Yefremov --- .editorconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0143c3c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true + +[{*.c,*.h}] +indent_style = space +indent_size = 4 +tab_width = 4 From 33051a296c4890270d75c6ed22dc074b2ab9520a Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 14:36:28 -0700 Subject: [PATCH 56/65] signals: require and always use sigaction() tftpd already requires sigaction() to compile, so there is no reason to use anything else. It also allows for nicer combination of flags. Signed-off-by: H. Peter Anvin --- common/signal.c | 39 ++++++++------------------------------- config.h | 5 +---- configure.ac | 11 ++++++----- tftp/main.c | 4 ++-- tftp/tftp.c | 4 ++-- tftpd/misc.c | 14 +++----------- 6 files changed, 22 insertions(+), 55 deletions(-) diff --git a/common/signal.c b/common/signal.c index 9119d57..ccd4af8 100644 --- a/common/signal.c +++ b/common/signal.c @@ -1,42 +1,19 @@ /* * signal.c * - * Use sigaction() to simulate BSD signal() + * User-friendly wrapper around sigaction(). */ #include "config.h" -#ifdef HAVE_SIGACTION - -sighandler_t tftp_signal(int signum, sighandler_t handler) +int tftp_signal(int signum, sighandler_t handler, int flags) { - struct sigaction action, oldaction; + struct sigaction sa; - memset(&action, 0, sizeof action); - action.sa_handler = handler; - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, signum); - action.sa_flags = SA_RESTART; + memset(&sa, 0, sizeof sa); + sa.sa_handler = handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = flags; - if (sigaction(signum, &action, &oldaction)) - return SIG_ERR; - - return oldaction.sa_handler; + return sigaction(signum, &sa, NULL); } - -#elif defined(HAVE_BSD_SIGNAL) - -sighandler_t tftp_signal(int signum, sighandler_t handler) -{ - return bsd_signal(signum, handler); -} - -#else - -/* This is dangerous at best. Let's hope it works. */ -sighandler_t tftp_signal(int signum, sighandler_t handler) -{ - return signal(signum, handler); -} - -#endif diff --git a/config.h b/config.h index a6a402b..1983058 100644 --- a/config.h +++ b/config.h @@ -282,10 +282,7 @@ char *xstrdup(const char *); #ifndef HAVE_SIGHANDLER_T typedef void (*sighandler_t)(int); #endif -#ifndef SIG_ERR -#define SIG_ERR NULL -#endif -sighandler_t tftp_signal(int, sighandler_t); +int tftp_signal(int, sighandler_t, int); #ifndef HAVE_DUP2 int dup2(int, int); diff --git a/configure.ac b/configure.ac index 8770431..2d4b93d 100644 --- a/configure.ac +++ b/configure.ac @@ -74,9 +74,12 @@ dnl but POSIX requires for socklen_t to be defined. dnl AC_CHECK_TYPES(socklen_t,,, [AC_INCLUDES_DEFAULT -#if HAVE_SYS_TYPES_H +#ifdef HAVE_SYS_TYPES_H # include #endif +#ifdef HAVE_SYS_SOCKET_H +# include +#endif ]) AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) @@ -85,14 +88,12 @@ AC_CHECK_FUNCS(fcntl) AC_CHECK_FUNCS(setsid) AC_CHECK_FUNCS(recvmsg) AC_CHECK_FUNCS(ftruncate) -AC_CHECK_FUNCS(setreuid) AC_CHECK_FUNCS(setresuid) -AC_CHECK_FUNCS(setregid) +AC_CHECK_FUNCS(setreuid) AC_CHECK_FUNCS(setresgid) +AC_CHECK_FUNCS(setregid) AC_CHECK_FUNCS(initgroups) AC_CHECK_FUNCS(setgroups) -AC_CHECK_FUNCS(sigaction) -AC_CHECK_FUNCS(bsd_signal) AC_CHECK_TYPES(sighandler_t) dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged. diff --git a/tftp/main.c b/tftp/main.c index 438f48a..ac06330 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -305,7 +305,7 @@ int main(int argc, char *argv[]) sp->s_proto = (char *)"udp"; } - tftp_signal(SIGINT, intr); + tftp_signal(SIGINT, intr, 0); if (peerargc) { /* Set peer */ @@ -768,8 +768,8 @@ void intr(int sig) { (void)sig; /* Quiet unused warning */ - tftp_signal(SIGALRM, SIG_IGN); alarm(0); + tftp_signal(SIGALRM, SIG_DFL, 0); siglongjmp(toplevel, -1); } diff --git a/tftp/tftp.c b/tftp/tftp.c index aecdeb9..33a4175 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -84,7 +84,7 @@ void tftp_sendfile(int fd, const char *name, const char *mode) is_request = 1; /* First packet is the actual WRQ */ amount = 0; - tftp_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer, 0); do { if (is_request) { size = makerequest(WRQ, name, dp, mode) - 4; @@ -190,7 +190,7 @@ void tftp_recvfile(int fd, const char *name, const char *mode) firsttrip = 1; amount = 0; - tftp_signal(SIGALRM, timer); + tftp_signal(SIGALRM, timer, 0); do { if (firsttrip) { size = makerequest(RRQ, name, ap, mode); diff --git a/tftpd/misc.c b/tftpd/misc.c index 07684dd..9070f96 100644 --- a/tftpd/misc.c +++ b/tftpd/misc.c @@ -19,19 +19,11 @@ #include "tftpd.h" /* - * Set the signal handler and flags. Basically a user-friendly - * wrapper around sigaction(). + * Set the signal handler and flags, and error out on failure. */ -void set_signal(int signum, void (*handler) (int), int flags) +void set_signal(int signum, sighandler_t handler, int flags) { - struct sigaction sa; - - memset(&sa, 0, sizeof sa); - sa.sa_handler = handler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = flags; - - if (sigaction(signum, &sa, NULL)) { + if (tftp_signal(signum, handler, flags)) { syslog(LOG_ERR, "sigaction: %m"); exit(EX_OSERR); } From fefaa2cc5c2ae1537fccbb552496787130feeeaa Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 18:24:53 -0700 Subject: [PATCH 57/65] autoconf: modernize and modularize Use my modular m4 library used for other things as well; update autoconf macros to 2.71 standard. Signed-off-by: H. Peter Anvin --- .gitignore | 11 +- Makefile | 37 ++-- aclocal.m4 | 280 ------------------------ autoconf/m4/pa_add_cflags.m4 | 9 + autoconf/m4/pa_add_flags.m4 | 39 ++++ autoconf/m4/pa_add_headers.m4 | 13 ++ autoconf/m4/pa_add_langflags.m4 | 27 +++ autoconf/m4/pa_arg_bool.m4 | 19 ++ autoconf/m4/pa_arg_disabled.m4 | 4 + autoconf/m4/pa_arg_enabled.m4 | 4 + autoconf/m4/pa_build_ifelse.m4 | 16 ++ autoconf/m4/pa_c_typeof.m4 | 32 +++ autoconf/m4/pa_check_bad_stdc_inline.m4 | 26 +++ autoconf/m4/pa_check_inttypes_h_sane.m4 | 17 ++ autoconf/m4/pa_cross_compile.m4 | 41 ++++ autoconf/m4/pa_flags_langlist.m4 | 19 ++ autoconf/m4/pa_have_tcpwrappers.m4 | 26 +++ autoconf/m4/pa_lang_foreach.m4 | 15 ++ autoconf/m4/pa_lang_seen_list.m4 | 20 ++ autoconf/m4/pa_option_debug.m4 | 13 ++ autoconf/m4/pa_option_profiling.m4 | 8 + autoconf/m4/pa_prog_cc.m4 | 13 ++ autoconf/m4/pa_search_libs_and_add.m4 | 22 ++ autoconf/m4/pa_sigsetjmp.m4 | 25 +++ autoconf/m4/pa_sym.m4 | 11 + autogen.sh | 85 ++++++- common/Makefile | 2 +- config.h | 10 +- MCONFIG.in => config/MCONFIG.in | 0 configure.ac | 278 +++++++++++------------ lib/Makefile | 2 +- tftp/Makefile | 2 +- tftpd/Makefile | 2 +- tftpd/recvfrom.c | 2 +- tftpd/tftpd.c | 11 +- 35 files changed, 680 insertions(+), 461 deletions(-) delete mode 100644 aclocal.m4 create mode 100644 autoconf/m4/pa_add_cflags.m4 create mode 100644 autoconf/m4/pa_add_flags.m4 create mode 100644 autoconf/m4/pa_add_headers.m4 create mode 100644 autoconf/m4/pa_add_langflags.m4 create mode 100644 autoconf/m4/pa_arg_bool.m4 create mode 100644 autoconf/m4/pa_arg_disabled.m4 create mode 100644 autoconf/m4/pa_arg_enabled.m4 create mode 100644 autoconf/m4/pa_build_ifelse.m4 create mode 100644 autoconf/m4/pa_c_typeof.m4 create mode 100644 autoconf/m4/pa_check_bad_stdc_inline.m4 create mode 100644 autoconf/m4/pa_check_inttypes_h_sane.m4 create mode 100644 autoconf/m4/pa_cross_compile.m4 create mode 100644 autoconf/m4/pa_flags_langlist.m4 create mode 100644 autoconf/m4/pa_have_tcpwrappers.m4 create mode 100644 autoconf/m4/pa_lang_foreach.m4 create mode 100644 autoconf/m4/pa_lang_seen_list.m4 create mode 100644 autoconf/m4/pa_option_debug.m4 create mode 100644 autoconf/m4/pa_option_profiling.m4 create mode 100644 autoconf/m4/pa_prog_cc.m4 create mode 100644 autoconf/m4/pa_search_libs_and_add.m4 create mode 100644 autoconf/m4/pa_sigsetjmp.m4 create mode 100644 autoconf/m4/pa_sym.m4 rename MCONFIG.in => config/MCONFIG.in (100%) diff --git a/.gitignore b/.gitignore index 7a98dc8..bb95251 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ -/MCONFIG -/aconfig.h -/aconfig.h.in +/config/MCONFIG +/config/config.h +/config/config.h.in +/autoconf/aclocal.m4 +/autoconf/clean.sh +/autoconf/helpers/ /autom4te.cache /config.log /config.status @@ -12,5 +15,7 @@ *.8 *.a *.o +*.i +*.s *~ \#* diff --git a/Makefile b/Makefile index 8c779d3..2e790fd 100644 --- a/Makefile +++ b/Makefile @@ -3,10 +3,10 @@ SUB = lib common tftp tftpd -%.build: MCONFIG aconfig.h version.h +%.build: config/MCONFIG config/config.h version.h $(MAKE) -C $(patsubst %.build, %, $@) -%.install: MCONFIG aconfig.h version.h +%.install: config/MCONFIG config/config.h version.h $(MAKE) -C $(patsubst %.install, %, $@) install %.clean: @@ -15,12 +15,12 @@ SUB = lib common tftp tftpd %.distclean: $(MAKE) -C $(patsubst %.distclean, %, $@) distclean -all: MCONFIG $(patsubst %, %.build, $(SUB)) +all: config/MCONFIG $(patsubst %, %.build, $(SUB)) tftp.build: lib.build common.build tftpd.build: lib.build common.build -install: MCONFIG $(patsubst %, %.install, $(SUB)) +install: config/MCONFIG $(patsubst %, %.install, $(SUB)) clean: localclean $(patsubst %, %.clean, $(SUB)) @@ -30,46 +30,37 @@ localclean: distclean: localdistclean $(patsubst %, %.distclean, $(SUB)) localdistclean: localclean - rm -f MCONFIG config.status config.log aconfig.h *~ \#* + rm -f config/config/MCONFIG config.status config.log config/config.h *~ \#* rm -rf *.cache find . -type f \( -name \*.orig -o -name \*.rej \) | xargs rm -f spotless: distclean - rm -f configure aconfig.h.in tftp.spec + rm -f configure config/config.h.in tftp.spec -autoconf: configure aconfig.h.in +autoconf: configure config/config.h.in -config: MCONFIG aconfig.h +config: config/MCONFIG config/config.h release: $(MAKE) autoconf $(MAKE) tftp.spec $(MAKE) distclean -MCONFIG: configure MCONFIG.in aconfig.h.in +config/MCONFIG: configure config/MCONFIG.in config/config.h.in if test -x config.status; then \ ./config.status --recheck && ./config.status ; \ else \ ./configure ; \ fi -aconfig.h: MCONFIG +config/config.h: config/MCONFIG : Generated by side effect -# Adding "configure" to the dependencies serializes this with running -# autoconf, because there are apparently race conditions between -# autoconf and autoheader. And worse than that, even when autoconf -# cleanly returns first, autoheader will truncate the timestamp of -# aconfig.h.in to second resolution, so on a filesystem with subsecond -# resolution it can appear older than configure (which isn't truncated). -# So make it an order-only prerequisite to avoid looping regenerating it. -aconfig.h.in: configure.ac aclocal.m4 | configure - rm -f aconfig.h.in aconfig.h - autoheader +configure: configure.ac + sh autogen.sh -configure: configure.ac aclocal.m4 - rm -rf MCONFIG configure config.log aconfig.h *.cache - autoconf +config/config.h.in: configure + : Generated by side effect version.h: version echo \#define VERSION \"tftp-hpa `cat version`\" > version.h diff --git a/aclocal.m4 b/aclocal.m4 deleted file mode 100644 index c07702c..0000000 --- a/aclocal.m4 +++ /dev/null @@ -1,280 +0,0 @@ -dnl ----------------------------------------------------------------------- -dnl -dnl Copyright 1999-2008 H. Peter Anvin - All Rights Reserved -dnl -dnl This program is free software; you can redistribute it and/or modify -dnl it under the terms of the GNU General Public License as published by -dnl the Free Software Foundation, Inc., 53 Temple Place Ste 330, -dnl Bostom MA 02111-1307, USA; either version 2 of the License, or -dnl (at your option) any later version; incorporated herein by reference. -dnl -dnl ----------------------------------------------------------------------- - -dnl -------------------------------------------------------------------------- -dnl PA_ADD_CFLAGS() -dnl -dnl Attempt to add the given option to CFLAGS, if it doesn't break compilation -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_ADD_CFLAGS, -[AC_MSG_CHECKING([if $CC accepts $1]) - pa_add_cflags__old_cflags="$CFLAGS" - CFLAGS="$CFLAGS $1" - AC_TRY_LINK([#include ], - [printf("Hello, World!\n");], - AC_MSG_RESULT([yes]), - AC_MSG_RESULT([no]) - CFLAGS="$pa_add_cflags__old_cflags")]) - -dnl -------------------------------------------------------------------------- -dnl PA_SIGSETJMP -dnl -dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work -dnl for these particular functions.) -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_SIGSETJMP, -[AC_MSG_CHECKING([for sigsetjmp]) - AC_TRY_LINK([ -#ifdef HAVE_SETJMP_H -#include -#endif], - [sigjmp_buf buf; - sigsetjmp(buf,1); - siglongjmp(buf,2);], - AC_MSG_RESULT([yes]) - $1, - AC_MSG_RESULT([no]) - $2)]) - -dnl -------------------------------------------------------------------------- -dnl PA_MSGHDR_MSG_CONTROL -dnl -dnl Does struct msghdr have the msg_control field? -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([HAVE_MSGHDR_MSG_CONTROL], -[Define if struct msghdr has the msg_control field.]) - -AC_DEFUN(PA_MSGHDR_MSG_CONTROL, - [AC_CHECK_MEMBER(struct msghdr.msg_control, - [AC_DEFINE(HAVE_MSGHDR_MSG_CONTROL)], - [], - [ -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_IN_PKTINFO -dnl -dnl Look for definition of struct in_pktinfo, which at least has an -dnl ipi_addr member. Some versions of glibc lack struct in_pktinfo; -dnl if so we need to include the definition ourselves -- but we only -dnl want to do that if absolutely necessary! -dnl ------------------------------------------------------------------------ -AH_TEMPLATE([HAVE_STRUCT_IN_PKTINFO], -[Define if struct in_pktinfo is defined.]) - -AC_DEFUN(PA_STRUCT_IN_PKTINFO, - [AC_CHECK_MEMBER(struct in_pktinfo.ipi_addr, - [AC_DEFINE(HAVE_STRUCT_IN_PKTINFO)], - [], - [ -#include -#include -#include -#include -#include -#include - ])]) - - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_SOCKADDR_IN6 -dnl -dnl Look for definition of struct sockaddr_in6, which at least has an -dnl sin6_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_SOCKADDR_IN6], -[Define if struct sockaddr_in6 is defined.]) - -AC_DEFUN(PA_STRUCT_SOCKADDR_IN6, - [AC_CHECK_MEMBER(struct sockaddr_in6.sin6_addr, - [ - AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6) - HAVE_INET6=true; - ], - [ - HAVE_INET6=false; - ], - [ -#include -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_ADDRINFO -dnl -dnl Look for definition of struct addrinfo, which at least has an -dnl ai_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_ADDRINFO], -[Define if struct addrinfo is defined.]) - -AC_DEFUN(PA_STRUCT_ADDRINFO, - [AC_CHECK_MEMBER(struct addrinfo.ai_addr, - [AC_DEFINE(HAVE_STRUCT_ADDRINFO)], - [], - [ -#include -#include -#include - ])]) - -dnl ------------------------------------------------------------------------ -dnl PA_STRUCT_IN6_PKTINFO -dnl -dnl Look for definition of struct in6_pktinfo, which at least has an -dnl ipi6_addr member -dnl -AH_TEMPLATE([HAVE_STRUCT_IN6_PKTINFO], -[Define if struct in6_pktinfo is defined.]) - -AC_DEFUN(PA_STRUCT_IN6_PKTINFO, - [AC_CHECK_MEMBER(struct in6_pktinfo.ipi6_addr, - [AC_DEFINE(HAVE_STRUCT_IN6_PKTINFO)], - [], - [ -#include -#include -#include -#include - ])]) - -dnl -------------------------------------------------------------------------- -dnl PA_HAVE_TCPWRAPPERS -dnl -dnl Do we have the tcpwrappers -lwrap? This can't be done using AC_CHECK_LIBS -dnl due to the need to provide "allow_severity" and "deny_severity" variables -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([HAVE_TCPWRAPPERS], -[Define if we have tcpwrappers (-lwrap) and .]) - -AC_DEFUN(PA_HAVE_TCPWRAPPERS, -[AC_CHECK_LIB([wrap], [main]) - AC_MSG_CHECKING([for tcpwrappers]) - AC_TRY_LINK( -[ -#include -int allow_severity = 0; -int deny_severity = 0; -], -[ - hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN); -], -[ - AC_DEFINE(HAVE_TCPWRAPPERS) - AC_MSG_RESULT([yes]) -], -[ - AC_MSG_RESULT([no]) -])]) - -dnl ------------------------------------------------------------------------ -dnl PA_CHECK_INTTYPES_H_SANE -dnl -dnl At least some versions of AIX 4 have macros which are -dnl completely broken. Try to detect those. -dnl -------------------------------------------------------------------------- -AH_TEMPLATE([INTTYPES_H_IS_SANE], -[Define if the macros in are usable]) - -AC_DEFUN(PA_CHECK_INTTYPES_H_SANE, -[AC_CHECK_HEADERS(inttypes.h, - [ - AC_MSG_CHECKING([if inttypes.h is sane]) - AC_TRY_LINK( - [ -#include -#include - ], - [uintmax_t max = UINTMAX_C(0); - printf("%"PRIuMAX"\n", max);], - AC_MSG_RESULT([yes]) - AC_DEFINE(INTTYPES_H_IS_SANE), - AC_MSG_RESULT([no (AIX, eh?)])) - ]) -]) - -dnl ------------------------------------------------------------------------ -dnl PA_WITH_BOOL -dnl -dnl PA_WITH_BOOL(option, default, help, enable, disable) -dnl -dnl Provides a more convenient way to specify --with-option and -dnl --without-option, with a default. default should be either 0 or 1. -dnl ------------------------------------------------------------------------ -AC_DEFUN(PA_WITH_BOOL, -[AC_ARG_WITH([$1], [$3], -if test ["$withval"] != no; then -[$4] -else -[$5] -fi, -if test [$2] -ne 0; then -[$4] -else -[$5] -fi)]) - -dnl -------------------------------------------------------------------------- -dnl PA_HEADER_DEFINES -dnl -dnl PA_HEADER_DEFINES(header, type, value) -dnl -------------------------------------------------------------------------- -AC_DEFUN(PA_HEADER_DEFINES, -[AC_MSG_CHECKING([if $1 defines $3]) - AH_TEMPLATE([HAVE_$3_DEFINITION], [Define if $1 defines $3]) - AC_TRY_COMPILE([ -#include <$1> -], -[ -int main() -{ - $2 dummy = $3; - return 0; -} -], -[ - pa_header_define=`echo HAVE_$3_DEFINITION | tr '[a-z]' '[A-Z]'` - AC_DEFINE_UNQUOTED($pa_header_define) - AC_MSG_RESULT(yes) -], -[ - AC_MSG_RESULT(no) -])]) - -dnl -------------------------------------------------------------------------- -dnl PA_SEARCH_LIBS_AND_ADD -dnl -dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add]) -dnl -------------------------------------------------------------------------- - -AC_DEFUN(PA_SEARCH_LIBS_AND_ADD, - [ - AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found]) - AC_SEARCH_LIBS($1, $2, - [ - AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1)) - pa_add_$1=false; - ], - [ - XTRA=true; - if test $# -eq 3; then - AC_LIBOBJ($3) - else - AC_LIBOBJ($1) - fi - pa_add_$1=true; - ])]) diff --git a/autoconf/m4/pa_add_cflags.m4 b/autoconf/m4/pa_add_cflags.m4 new file mode 100644 index 0000000..26d55c4 --- /dev/null +++ b/autoconf/m4/pa_add_cflags.m4 @@ -0,0 +1,9 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_CFLAGS(variable, flag [,actual_flag [,success [,failure]]]]) +dnl +dnl Attempt to add the given option to xFLAGS, if it doesn't break +dnl compilation. If the option to be tested is different than the +dnl option that should actually be added, add the option to be +dnl actually added as a second argument. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ADD_CFLAGS], [PA_ADD_FLAGS(CFLAGS, [$1], [$2], [$3], [$4])]) diff --git a/autoconf/m4/pa_add_flags.m4 b/autoconf/m4/pa_add_flags.m4 new file mode 100644 index 0000000..23f96f1 --- /dev/null +++ b/autoconf/m4/pa_add_flags.m4 @@ -0,0 +1,39 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_FLAGS(flagvar, flags) +dnl +dnl Add [flags] to the variable [flagvar] if and only if it is accepted +dnl by all languages affected by [flagvar], if those languages have +dnl been previously seen in the script. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ADD_FLAGS], +[ + AS_VAR_PUSHDEF([old], [_$0_$1_orig]) + AS_VAR_PUSHDEF([ok], [_$0_$1_ok]) + AS_VAR_PUSHDEF([flags], [$1]) + + AS_VAR_COPY([old], [flags]) + AS_VAR_SET([flags], ["$flags $2"]) + AS_VAR_SET([ok], [yes]) + + PA_LANG_FOREACH(PA_FLAGS_LANGLIST($1), + [AS_VAR_IF([ok], [yes], + [AC_MSG_CHECKING([if $]_AC_CC[ accepts $2]) + PA_BUILD_IFELSE([], + [AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no]) + AS_VAR_SET([ok], [no])])]) + ]) + + AS_VAR_IF([ok], [yes], + [m4_ifnblank([$3],[AS_VAR_SET([flags], ["$old $3"])]) + m4_foreach_w([_pa_add_flags_flag], [m4_ifblank([$3],[$2],[$3])], + [AC_DEFINE(PA_SYM([$1_]_pa_add_flags_flag), 1, + [Define to 1 if compiled with the ]_pa_add_flags_flag[ compiler flag])]) + $4], + [AS_VAR_SET([flags], ["$old"]) + $5]) + + AS_VAR_POPDEF([flags]) + AS_VAR_POPDEF([ok]) + AS_VAR_POPDEF([old]) +]) diff --git a/autoconf/m4/pa_add_headers.m4 b/autoconf/m4/pa_add_headers.m4 new file mode 100644 index 0000000..d3c478a --- /dev/null +++ b/autoconf/m4/pa_add_headers.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_HEADERS(headers...) +dnl +dnl Call AC_CHECK_HEADERS(), and add to ac_includes_default if found +dnl -------------------------------------------------------------------------- +AC_DEFUN([_PA_ADD_HEADER], +[AC_CHECK_HEADERS([$1],[ac_includes_default="$ac_includes_default +#include <$1>" +]) +]) + +AC_DEFUN([PA_ADD_HEADERS], +[m4_map_args_w([$1],[_PA_ADD_HEADER(],[)])]) diff --git a/autoconf/m4/pa_add_langflags.m4 b/autoconf/m4/pa_add_langflags.m4 new file mode 100644 index 0000000..05c3114 --- /dev/null +++ b/autoconf/m4/pa_add_langflags.m4 @@ -0,0 +1,27 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ADD_LANGFLAGS(flag...) +dnl +dnl Attempt to add the option in the given list to each compiler flags +dnl (CFLAGS, CXXFLAGS, ...), if it doesn't break compilation. +dnl -------------------------------------------------------------------------- +m4_defun([_PA_LANGFLAG_VAR], +[m4_case([$1], + [C], [CFLAGS], + [C++], [CXXFLAGS], + [Fortran 77], [FFLAGS], + [Fortran], [FCFLAGS], + [Erlang], [ERLCFLAGS], + [Objective C], [OBJCFLAGS], + [Objective C++], [OBJCXXFLAGS], + [Go], [GOFLAGS], + [m4_fatal([PA_ADD_LANGFLAGS: Unknown language: $1])])]) + +AC_DEFUN([PA_ADD_LANGFLAGS], +[m4_pushdef([_pa_langflags],m4_dquote($1))dnl +m4_set_foreach(_PA_LANG_SEEN_SET,[_pa_lang],dnl +[_pa_flag_found=no + m4_foreach_w([_pa_flag], _pa_langflags, + [AS_IF([test $_pa_flag_found = no], + [PA_ADD_FLAGS(_PA_LANGFLAG_VAR(_pa_lang),_pa_flag,[],[_pa_flag_found=yes])]) + ])]) +m4_popdef([_pa_langflags])]) diff --git a/autoconf/m4/pa_arg_bool.m4 b/autoconf/m4/pa_arg_bool.m4 new file mode 100644 index 0000000..5289ed4 --- /dev/null +++ b/autoconf/m4/pa_arg_bool.m4 @@ -0,0 +1,19 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_BOOL(option,helptext,default,enabled_action,disabled_action) +dnl +dnl The last three arguments are optional; default can be yes or no. +dnl +dnl Simpler-to-use versions of AC_ARG_ENABLED, that include the +dnl test for $enableval and the AS_HELP_STRING definition. This is only +dnl to be used for boolean options. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_BOOL], +[m4_pushdef([pa_default],m4_default(m4_normalize([$3]),[no])) + m4_pushdef([pa_option],m4_case(pa_default,[yes],[disable],[enable])) + AC_ARG_ENABLE([$1], + [AS_HELP_STRING([--]m4_defn([pa_option])[-$1],[$2])], + [pa_arg_bool_enableval="$enableval"], + [pa_arg_bool_enableval="]m4_defn([pa_default])["]) + m4_popdef([pa_option], [pa_default]) + AS_IF([test x"$pa_arg_bool_enableval" != xno], [$4], [$5]) +]) diff --git a/autoconf/m4/pa_arg_disabled.m4 b/autoconf/m4/pa_arg_disabled.m4 new file mode 100644 index 0000000..42f4ce8 --- /dev/null +++ b/autoconf/m4/pa_arg_disabled.m4 @@ -0,0 +1,4 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_DISABLED(option,helptext,disabled_action,enabled_action) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_DISABLED],[PA_ARG_BOOL([$1],[$2],yes,[$4],[$3])]) diff --git a/autoconf/m4/pa_arg_enabled.m4 b/autoconf/m4/pa_arg_enabled.m4 new file mode 100644 index 0000000..7d66a21 --- /dev/null +++ b/autoconf/m4/pa_arg_enabled.m4 @@ -0,0 +1,4 @@ +dnl -------------------------------------------------------------------------- +dnl PA_ARG_ENABLED(option,helptext,enabled_action,disabled_action) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_ARG_ENABLED],[PA_ARG_BOOL([$1],[$2],no,[$3],[$4])]) diff --git a/autoconf/m4/pa_build_ifelse.m4 b/autoconf/m4/pa_build_ifelse.m4 new file mode 100644 index 0000000..1aacfcd --- /dev/null +++ b/autoconf/m4/pa_build_ifelse.m4 @@ -0,0 +1,16 @@ +dnl -------------------------------------------------------------------------- +dnl PA_BUILD_IFELSE(input [,success [,failure]]) +dnl +dnl Same as AC_LINK_IFELSE for languages where linking is applicable, +dnl otherwise AC_COMPILE_IFELSE. +dnl +dnl If the first argument is empty, use _AC_LANG_IO_PROGRAM. +dnl -------------------------------------------------------------------------- +m4_defun([_PA_BUILD_IFELSE], +[m4_case(_AC_LANG, + [Erlang], [AC_COMPILE_IFELSE($@)], + [AC_LINK_IFELSE($@)])]) + +AC_DEFUN([PA_BUILD_IFELSE], +[_PA_BUILD_IFELSE([m4_ifblank([$1],[AC_LANG_SOURCE(_AC_LANG_IO_PROGRAM)], + [$1])],[$2],[$3])]) diff --git a/autoconf/m4/pa_c_typeof.m4 b/autoconf/m4/pa_c_typeof.m4 new file mode 100644 index 0000000..909b171 --- /dev/null +++ b/autoconf/m4/pa_c_typeof.m4 @@ -0,0 +1,32 @@ +dnl -------------------------------------------------------------------------- +dnl PA_C_TYPEOF +dnl +dnl Find if typeof() exists, or an equivalent (__typeof__, decltype, +dnl __decltype__) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_C_TYPEOF], +[AC_CACHE_CHECK([if $CC supports typeof], [pa_cv_typeof], + [pa_cv_typeof=no + for pa_typeof_try in typeof __typeof __typeof__ decltype __decltype __decltype__ _Decltype + do + AS_IF([test $pa_cv_typeof = no], + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +AC_INCLUDES_DEFAULT +int testme(int x); +int testme(int x) +{ + $pa_typeof_try(x) y = x*x; + return y; +} +])], + [pa_cv_typeof=$pa_typeof_try])]) + done + ]) + AS_IF([test $pa_cv_typeof = no], + [], + [AC_DEFINE([HAVE_TYPEOF], 1, + [Define to 1 if you have some version of the typeof operator.]) + AS_IF([test $pa_cv_typeof = typeof], + [], + [AC_DEFINE_UNQUOTED([typeof], [$pa_cv_typeof], + [Define if your typeof operator is not named `typeof'.])])])]) diff --git a/autoconf/m4/pa_check_bad_stdc_inline.m4 b/autoconf/m4/pa_check_bad_stdc_inline.m4 new file mode 100644 index 0000000..3fbc53a --- /dev/null +++ b/autoconf/m4/pa_check_bad_stdc_inline.m4 @@ -0,0 +1,26 @@ +dnl -------------------------------------------------------------------------- +dnl PA_CHECK_BAD_STDC_INLINE +dnl +dnl Some versions of gcc seem to apply -Wmissing-prototypes to C99 +dnl inline functions, which means we need to use GNU inline syntax +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_CHECK_BAD_STDC_INLINE], +[AC_MSG_CHECKING([if $CC supports C99 external inlines]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([ +AC_INCLUDES_DEFAULT + +/* Don't mistake GNU inlines for c99 */ +#if defined(__GNUC__) && !defined(__GNUC_STDC_INLINE__) +# error "Using gnu inline standard" +#endif + +inline int foo(int x) +{ + return x+1; +} + ])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_STDC_INLINE], 1, + [Define to 1 if your compiler supports C99 extern inline])], + [AC_MSG_RESULT([no]) + PA_ADD_CFLAGS([-fgnu89-inline])])]) diff --git a/autoconf/m4/pa_check_inttypes_h_sane.m4 b/autoconf/m4/pa_check_inttypes_h_sane.m4 new file mode 100644 index 0000000..589e0a2 --- /dev/null +++ b/autoconf/m4/pa_check_inttypes_h_sane.m4 @@ -0,0 +1,17 @@ +dnl ------------------------------------------------------------------------ +dnl PA_CHECK_INTTYPES_H_SANE +dnl +dnl At least some versions of AIX 4 have macros which are +dnl completely broken. Try to detect those. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_CHECK_INTTYPES_H_SANE], +[AC_CHECK_HEADERS_ONCE(inttypes.h) +AS_IF([test "x$ac_cv_header_inttypes_h" = xyes],[ + AC_MSG_CHECKING([if inttypes.h is sane]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], + [uintmax_t max = UINTMAX_C(0); + printf("%"PRIuMAX"\n", max);])], + [AC_MSG_RESULT([yes]) + AC_DEFINE(INTTYPES_H_IS_SANE, 1, + [Define if the macros in are usable])], + [AC_MSG_RESULT([no (AIX, eh?)])])])]) diff --git a/autoconf/m4/pa_cross_compile.m4 b/autoconf/m4/pa_cross_compile.m4 new file mode 100644 index 0000000..f082973 --- /dev/null +++ b/autoconf/m4/pa_cross_compile.m4 @@ -0,0 +1,41 @@ +dnl -------------------------------------------------------------------------- +dnl PA_CROSS_COMPILE +dnl +dnl Get the canonical name for the build and host (runtime) systems; +dnl then figure out if this is cross-compilation. Specifically, this +dnl disables invoking WINE on non-Windows systems which are configured +dnl to run WINE automatically. +dnl +dnl Use PA_CROSS_COMPILE_TOOL if the target system (output of a code- +dnl generation tool) is applicable. +dnl +dnl This doesn't explicitly print any messages as that is automatically +dnl done elsewhere. +dnl -------------------------------------------------------------------------- +AC_DEFUN_ONCE([PA_CROSS_COMPILE], +[ + AC_BEFORE([$0], [AC_LANG_COMPILER]) + AC_BEFORE([$0], [AC_LANG]) + AC_BEFORE([$0], [AC_PROG_CC]) + AC_BEFORE([$0], [AC_PROG_CPP]) + AC_BEFORE([$0], [AC_PROG_CXX]) + AC_BEFORE([$0], [AC_PROG_CXXCPP]) + AC_BEFORE([$0], [AC_PROG_OBJC]) + AC_BEFORE([$0], [AC_PROG_OBJCPP]) + AC_BEFORE([$0], [AC_PROG_OBJCXX]) + AC_BEFORE([$0], [AC_PROG_OBJCXXCPP]) + AC_BEFORE([$0], [AC_PROG_F77]) + AC_BEFORE([$0], [AC_PROG_FC]) + AC_BEFORE([$0], [AC_PROG_GO]) + + # Disable WINE + WINELOADER=/dev/null + export WINELOADER + WINESERVER=/dev/null + export WINESERVER + WINEPREFIX=/dev/null + export WINEPREFIX + + AC_CANONICAL_BUILD + AC_CANONICAL_HOST +]) diff --git a/autoconf/m4/pa_flags_langlist.m4 b/autoconf/m4/pa_flags_langlist.m4 new file mode 100644 index 0000000..2ef5ded --- /dev/null +++ b/autoconf/m4/pa_flags_langlist.m4 @@ -0,0 +1,19 @@ +dnl -------------------------------------------------------------------------- +dnl PA_FLAGS_LANGLIST(flagvar) +dnl +dnl Return a list of languages affected by the variable flagvar. +dnl If flagvar is unknown, assume it affects the current language. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_FLAGS_LANGLIST], +[m4_dquote(m4_case([$1], + [CPPFLAGS], [[C],[C++],[Objective C],[Objective C++]], + [CFLAGS], [[C]], + [CXXFLAGS], [[C++]], + [FFLAGS], [[Fortran 77]], + [FCFLAGS], [[Fortran]], + [ERLCFLAGS], [[Erlang]], + [OBJCFLAGS], [[Objective C]], + [OBJCXXFLAGS], [[Objective C++]], + [GOFLAGS], [[Go]], + [LDFLAGS], [[C],[C++],[Fortran 77],[Fortran],[Objective C],[Objective C++],[Go]], + m4_dquote(_AC_LANG)))]) diff --git a/autoconf/m4/pa_have_tcpwrappers.m4 b/autoconf/m4/pa_have_tcpwrappers.m4 new file mode 100644 index 0000000..fba87ce --- /dev/null +++ b/autoconf/m4/pa_have_tcpwrappers.m4 @@ -0,0 +1,26 @@ +dnl -------------------------------------------------------------------------- +dnl PA_HAVE_TCPWRAPPERS +dnl +dnl Do we have the tcpwrappers -lwrap? This can't be done using AC_CHECK_LIBS +dnl due to the need to provide "allow_severity" and "deny_severity" variables +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_HAVE_TCPWRAPPERS], +[AC_CHECK_LIB([wrap], [main]) + AC_MSG_CHECKING([for tcpwrappers]) + AC_LINK_IFELSE([AC_LANG_PROGRAM( + [[ +#include +int allow_severity = 0; +int deny_severity = 0; + ]], + [[ + hosts_ctl("sample_daemon", STRING_UNKNOWN, STRING_UNKNOWN, STRING_UNKNOWN); + ]])], + [ + AC_DEFINE(HAVE_TCPWRAPPERS, 1, + [Define if we have tcpwrappers (-lwrap) and .]) + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + ])]) diff --git a/autoconf/m4/pa_lang_foreach.m4 b/autoconf/m4/pa_lang_foreach.m4 new file mode 100644 index 0000000..650913d --- /dev/null +++ b/autoconf/m4/pa_lang_foreach.m4 @@ -0,0 +1,15 @@ +dnl -------------------------------------------------------------------------- +dnl PA_LANG_FOREACH(subset, body) +dnl +dnl Expand [body] for each language encountered in the configure script also +dnl present in [subset], or all if [subset] is empty +dnl -------------------------------------------------------------------------- +AC_DEFUN([_PA_LANG_DO],dnl +[AC_LANG([$2])dnl +$1]) + +AC_DEFUN([PA_LANG_FOREACH],dnl +[m4_pushdef([_pa_lang_foreach_current],[_AC_LANG])dnl +m4_map_args([m4_curry([_PA_LANG_DO],[$2])],m4_unquote(PA_LANG_SEEN_LIST($1)))dnl +AC_LANG(_pa_lang_foreach_current)dnl +m4_popdef([_pa_lang_foreach_current])]) diff --git a/autoconf/m4/pa_lang_seen_list.m4 b/autoconf/m4/pa_lang_seen_list.m4 new file mode 100644 index 0000000..d524013 --- /dev/null +++ b/autoconf/m4/pa_lang_seen_list.m4 @@ -0,0 +1,20 @@ +dnl -------------------------------------------------------------------------- +dnl PA_LANG_SEEN_LIST(subset) +dnl +dnl List of the language lang has been used in the configuration +dnl script so far, possibly subset by [subset]. +dnl +dnl This relies on overriding _AC_LANG_SET(from, to), +dnl the internal implementation of _AC_LANG. +dnl -------------------------------------------------------------------------- +m4_ifndef([_PA_LANG_SET], +[m4_rename([_AC_LANG_SET], [_PA_LANG_SET])dnl +m4_defun([_AC_LANG_SET], [m4_set_add([_PA_LANG_SEEN_SET],[$2])dnl +_PA_LANG_SET($@)])]) + +AC_DEFUN([PA_LANG_SEEN_LIST], +[m4_set_delete([_pa_lang_seen_subset])dnl +m4_pushdef([_pa_lang_seen_subset_list],m4_ifnblank([$1],[$1],m4_dquote(m4_set_list([_PA_LANG_SEEN_SET]))))dnl +m4_set_add_all([_pa_lang_seen_subset],_pa_lang_seen_subset_list)dnl +m4_cdr(m4_set_intersection([_pa_lang_seen_subset],[_PA_LANG_SEEN_SET]))dnl +m4_popdef([_pa_lang_seen_subset_list])]) diff --git a/autoconf/m4/pa_option_debug.m4 b/autoconf/m4/pa_option_debug.m4 new file mode 100644 index 0000000..ae7d9db --- /dev/null +++ b/autoconf/m4/pa_option_debug.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_OPTION_DEBUG(with_debug, without_debug) +dnl +dnl Set debug flags and optimization flags depending on if +dnl --enable-debug is set or not. Some flags are set regardless... +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_OPTION_DEBUG], +[PA_ARG_DISABLED([gdb], [disable gdb debug extensions], + [PA_ADD_LANGFLAGS([-g3])], [PA_ADD_LANGFLAGS([-ggdb3 -g3])]) + PA_ARG_ENABLED([debug], [optimize for debugging], + [PA_ADD_LANGFLAGS([-Og -O0]) + $1], + [$2])]) diff --git a/autoconf/m4/pa_option_profiling.m4 b/autoconf/m4/pa_option_profiling.m4 new file mode 100644 index 0000000..39a3f6c --- /dev/null +++ b/autoconf/m4/pa_option_profiling.m4 @@ -0,0 +1,8 @@ +dnl -------------------------------------------------------------------------- +dnl PA_OPTION_PROFILING(with_profiling, without_profiling) +dnl +dnl Try to enable profiling if --enable-profiling is set. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_OPTION_PROFILING], +[PA_ARG_ENABLED([profiling], [compile with profiling (-pg option)], +[PA_ADD_LANGFLAGS([-pg])])]) diff --git a/autoconf/m4/pa_prog_cc.m4 b/autoconf/m4/pa_prog_cc.m4 new file mode 100644 index 0000000..0debe5d --- /dev/null +++ b/autoconf/m4/pa_prog_cc.m4 @@ -0,0 +1,13 @@ +dnl -------------------------------------------------------------------------- +dnl PA_PROG_CC() +dnl +dnl Similar to AC_PROG_CC, but add a prototype for main() to +dnl AC_INCLUDES_DEFAULT to avoid -Werror from breaking compilation. +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_PROG_CC], +[AC_PROG_CC + AS_IF([test x$ac_cv_prog != xno], + [ac_includes_default="$ac_includes_default +#ifndef __cplusplus +extern int main(void); +#endif"])]) diff --git a/autoconf/m4/pa_search_libs_and_add.m4 b/autoconf/m4/pa_search_libs_and_add.m4 new file mode 100644 index 0000000..b006128 --- /dev/null +++ b/autoconf/m4/pa_search_libs_and_add.m4 @@ -0,0 +1,22 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SEARCH_LIBS_AND_ADD +dnl +dnl PA_SEARCH_LIBS_AND_ADD(function, libraries [,function to add]) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SEARCH_LIBS_AND_ADD], + [ + AH_TEMPLATE(AS_TR_CPP(HAVE_$1), [Define if $1 function was found]) + AC_SEARCH_LIBS($1, $2, + [ + AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_$1)) + pa_add_$1=false; + ], + [ + XTRA=true; + if test $# -eq 3; then + AC_LIBOBJ($3) + else + AC_LIBOBJ($1) + fi + pa_add_$1=true; + ])]) diff --git a/autoconf/m4/pa_sigsetjmp.m4 b/autoconf/m4/pa_sigsetjmp.m4 new file mode 100644 index 0000000..898b562 --- /dev/null +++ b/autoconf/m4/pa_sigsetjmp.m4 @@ -0,0 +1,25 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SIGSETJMP +dnl +dnl Do we have sigsetjmp/siglongjmp? (AC_CHECK_FUNCS doesn't seem to work +dnl for these particular functions.) +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SIGSETJMP], +[AC_MSG_CHECKING([for sigsetjmp]) + AC_LINK_IFELSE([AC_LANG_SOURCE( +[ +AC_INCLUDES_DEFAULT +#include + +int main(void) { + sigjmp_buf buf; + if (sigsetjmp(buf,1)) + return 0; + siglongjmp(buf,2); + return 1; + } +])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_SIGSETJMP], 1, + [Define to 1 if your system has sigsetjmp/siglongjmp])], + [AC_MSG_RESULT([no])])]) diff --git a/autoconf/m4/pa_sym.m4 b/autoconf/m4/pa_sym.m4 new file mode 100644 index 0000000..d3a8965 --- /dev/null +++ b/autoconf/m4/pa_sym.m4 @@ -0,0 +1,11 @@ +dnl -------------------------------------------------------------------------- +dnl PA_SYM(prefix, string) +dnl +dnl Convert a (semi-) arbitrary string to a CPP symbol +dnl Compact underscores and convert non-C characters to underscore, +dnl except + which is converted to X (so C++ -> CXX). +dnl -------------------------------------------------------------------------- +AC_DEFUN([PA_SYM], +[m4_bpatsubsts(m4_quote(m4_toupper([$*])), + [,],[],[\+],[X],[[^ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789]+],[_],dnl +[^._?\(.*\)_.$],[[\1]])]) diff --git a/autogen.sh b/autogen.sh index 728a381..0c892cc 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,2 +1,83 @@ -#!/bin/sh -make autoconf +#!/bin/sh -x +# +# Run this script to regenerate autoconf files +# +recheck=false +for arg; do + case x"$arg" in + x--recheck) + recheck=true + config=$(sh config.status --config 2>/dev/null) + ;; + x--clearenv) + unset AUTOCONF AUTOMAKE ACLOCAL AUTOHEADER ACLOCAL_PATH + ;; + *) + echo "$0: unknown option: $arg" 1>&2 + ;; + esac +done + +# This allows for overriding the default autoconf programs +AUTOCONF="${AUTOCONF:-${AUTOTOOLS_PREFIX}autoconf}" +AUTOMAKE="${AUTOMAKE:-${AUTOTOOLS_PREFIX}automake}" +ACLOCAL="${ACLOCAL:-${AUTOTOOLS_PREFIX}aclocal}" +AUTOHEADER="${AUTOHEADER:-${AUTOTOOLS_PREFIX}autoheader}" + +mkdir -p autoconf autoconf/helpers config +autolib="`"$AUTOMAKE" --print-libdir`" +if test ! x"$autolib" = x; then + for prg in install-sh compile config.guess config.sub; do + # Update autoconf helpers if and only if newer ones are available + if test -f "$autolib"/"$prg" && \ + ( set -e ; \ + test -f autoconf/helpers/"$prg" && sed -n \ + -e 's/^scriptver=/scriptversion=/' \ + -e 's/^timestamp=/scriptversion=/' \ + -e 's/^scriptversion=['\''"]?\([^'\''"]*\).*$/\1/p' \ + "$autolib"/"$prg" autoconf/helpers/"$prg" | \ + sort -c 2>/dev/null ; \ + test $? -ne 0 ) + then + cp -f "$autolib"/"$prg" autoconf/helpers + fi + done +fi +mv -f autoconf/aclocal.m4 autoconf/aclocal.m4.old +mkdir -p autoconf/m4.old autoconf/m4 +mv -f autoconf/m4/*.m4 autoconf/m4.old/ 2>/dev/null || true +ACLOCAL_PATH="${ACLOCAL_PATH}${ACLOCAL_PATH:+:}`pwd`/autoconf/m4.old" +export ACLOCAL_PATH +"$ACLOCAL" --install --output=autoconf/aclocal.m4 -I autoconf/m4 +if test ! -f autoconf/aclocal.m4; then + # aclocal failed, revert to previous files + mv -f autoconf/m4.old/*.m4 autoconf/m4/ + mv -f autoconf/aclocal.m4.old autoconf/aclocal.m4 + exit 1 +fi +rm -rf autoconf/*m4.old +"$AUTOHEADER" -B autoconf +"$AUTOCONF" -B autoconf +( + echo '#!/bin/sh' + "$AUTOCONF" -B autoconf \ + -t AC_CONFIG_HEADERS:'rm -f $*' \ + -t AC_CONFIG_FILES:'rm -f $*' + echo 'rm -f config.log config.status' + echo 'rm -rf autom4te.cache' +) > autoconf/clean.sh +chmod +x autoconf/clean.sh +sh autoconf/clean.sh + +rm -f configure~ || true + +# Try to regenerate unconfig.h if Perl is available and unconfig.pl +# is present in the autoconf directory. +if [ -n "$(which perl)" -a -f autoconf/unconfig.pl ]; then + perl autoconf/unconfig.pl . config/config.h.in config/unconfig.h +fi + +if $recheck; then + # This bizarre statement has to do with how config.status quotes its output + echo exec sh configure $config | sh - +fi diff --git a/common/Makefile b/common/Makefile index 2aaffb7..89e351d 100644 --- a/common/Makefile +++ b/common/Makefile @@ -1,7 +1,7 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES OBJS = tftpsubs.$(O) signal.$(O) diff --git a/config.h b/config.h index 1983058..a8c55ac 100644 --- a/config.h +++ b/config.h @@ -25,7 +25,7 @@ #endif /* Must be included before we include any system headers! */ -#include "aconfig.h" /* autogenerated configuration header */ +#include "config/config.h" /* autogenerated configuration header */ /* Standard includes */ @@ -115,10 +115,10 @@ /* Some broken systems care about text versus binary, but real Unix systems don't... */ -#ifndef HAVE_O_TEXT_DEFINITION +#if !HAVE_DECL_O_TEXT #define O_TEXT 0 #endif -#ifndef HAVE_O_BINARY_DEFINITION +#if !HAVE_DECL_O_BINARY #define O_BINARY 0 #endif @@ -253,11 +253,9 @@ typedef int socklen_t; #include -#ifndef HAVE_IPPORT_TFTP_DEFINITION -#ifndef IPPORT_TFTP +#if !HAVE_DECL_IPPORT_TFTP && !defined(IPPORT_TFTP) #define IPPORT_TFTP 69 #endif -#endif /* arpa/{inet,tftp}.h, and possible missing pieces */ diff --git a/MCONFIG.in b/config/MCONFIG.in similarity index 100% rename from MCONFIG.in rename to config/MCONFIG.in diff --git a/configure.ac b/configure.ac index 2d4b93d..be2a500 100644 --- a/configure.ac +++ b/configure.ac @@ -1,61 +1,89 @@ -dnl -dnl autoconf input file to generate MCONFIG -dnl - +dnl Process this file with autoconf 2.71 or later to produce +dnl a configure script. AC_PREREQ([2.71]) AC_INIT -AC_CONFIG_SRCDIR([MCONFIG.in]) -AC_PREFIX_DEFAULT(/usr) +AC_CONFIG_SRCDIR([MRULES]) +AC_PREFIX_DEFAULT([/usr]) +AC_CONFIG_AUX_DIR([autoconf/helpers]) +dnl This prevents us from running Wine and thinking we are not +dnl cross-compiling when in fact we are; running Wine here is at +dnl the best very slow and doesn't buy us a single thing at all. +PA_CROSS_COMPILE + +dnl Enable any available C extensions +PA_PROG_CC AC_USE_SYSTEM_EXTENSIONS -AC_SEARCH_LIBS([strerror],[cposix]) -AC_PROG_CC +dnl Options for debugging and profiling +PA_OPTION_DEBUG +PA_OPTION_PROFILING + +dnl LLVM doesn't error out on invalid -W options unless this option is +dnl specified first. Enable this so this script can actually discover +dnl which -W options are possible for this compiler. +PA_ADD_CFLAGS([-Werror=unknown-warning-option]) + +dnl Force gcc and gcc-compatible compilers treat signed integers +dnl as 2's complement +PA_ADD_CFLAGS([-fwrapv]) + +dnl Force clang to behave in a predictable manner, in order to make bugs +dnl possible to track down. gcc appears to have this behavior by default. +PA_ADD_CFLAGS([-ftrivial-auto-var-init=zero]) + +dnl Some environments abuse __STRICT_ANSI__ to disable some +dnl function declarations +PA_ADD_CFLAGS([-U__STRICT_ANSI__]) + +dnl Don't put things in common if we can avoid it. We don't want to +dnl assume all compilers support common, and this will help find those +dnl problems. This also works around an OSX linker problem. +PA_ADD_CFLAGS([-fno-common]) + +dnl Tests which may trigger warnings on some compilers AC_C_CONST AC_C_INLINE +AC_C_RESTRICT -PA_ADD_CFLAGS(-W) -PA_ADD_CFLAGS(-Wall) -PA_ADD_CFLAGS(-Wpointer-arith) -PA_ADD_CFLAGS(-Wbad-function-cast) -PA_ADD_CFLAGS(-Wcast-equal) -PA_ADD_CFLAGS(-Wstrict-prototypes) -PA_ADD_CFLAGS(-Wmissing-prototypes) -PA_ADD_CFLAGS(-Wmissing-declarations) -PA_ADD_CFLAGS(-Wnested-externs) -PA_ADD_CFLAGS(-Winline) -PA_ADD_CFLAGS(-Wwrite-strings) -PA_ADD_CFLAGS(-Wundef) -PA_ADD_CFLAGS(-Wshadow) -PA_ADD_CFLAGS(-Wsign-compare) -PA_ADD_CFLAGS(-pipe) -PA_ADD_CFLAGS(-fno-strict-aliasing) +dnl Checks for header files. +AC_CHECK_INCLUDES_DEFAULT -AC_CHECK_HEADERS(inttypes.h) -AC_CHECK_HEADERS(stdint.h) +dnl See if we need extra libraries +XTRA=false + +AC_SEARCH_LIBS([strerror],[cposix]) + +AC_CHECK_HEADERS_ONCE(inttypes.h) +AC_CHECK_HEADERS_ONCE(stdint.h) +AC_CHECK_HEADERS_ONCE(grp.h) +AC_CHECK_HEADERS_ONCE(libgen.h) +AC_CHECK_HEADERS_ONCE(setjmp.h) +AC_CHECK_HEADERS_ONCE(strings.h) +AC_CHECK_HEADERS_ONCE(sysexits.h) +AC_CHECK_HEADERS_ONCE(unistd.h) +AC_CHECK_HEADERS_ONCE(sys/filio.h) +AC_CHECK_HEADERS_ONCE(sys/stat.h) +AC_CHECK_HEADERS_ONCE(sys/time.h) PA_CHECK_INTTYPES_H_SANE -AC_CHECK_HEADERS(fcntl.h) -AC_CHECK_HEADERS(grp.h) -AC_CHECK_HEADERS(libgen.h) -AC_CHECK_HEADERS(setjmp.h) -AC_CHECK_HEADERS(strings.h) -AC_CHECK_HEADERS(sysexits.h) -AC_CHECK_HEADERS(unistd.h) -AC_CHECK_HEADERS(sys/file.h) -AC_CHECK_HEADERS(sys/filio.h) -AC_CHECK_HEADERS(sys/stat.h) -AC_CHECK_HEADERS(sys/time.h) -AC_CHECK_HEADERS(sys/types.h) -AC_CHECK_HEADERS(arpa/inet.h) -AC_CHECK_HEADERS(netdb.h) dnl This is needed on some versions of FreeBSD... -AC_CHECK_HEADERS(machine/param.h) -AC_CHECK_HEADERS(sys/socket.h) -AC_CHECK_HEADERS(winsock2.h) -AC_CHECK_HEADERS(winsock.h) +AC_CHECK_HEADERS_ONCE(machine/param.h) -AC_SYS_LARGEFILE +dnl Windows... +PA_ADD_HEADERS(windows.h) +PA_ADD_HEADERS(winsock2.h) +AS_IF([test "x$ac_cv_header_winsock2_h" != xyes], + [PA_ADD_HEADERS(winsock.h)]) + +PA_ADD_HEADERS(fcntl.h) +PA_ADD_HEADERS(sys/types.h) +PA_ADD_HEADERS(arpa/inet.h) +PA_ADD_HEADERS(sys/socket.h) +PA_ADD_HEADERS(sys/file.h) +PA_ADD_HEADERS(netinet/in.h) +PA_ADD_HEADERS(sys/uio.h) +PA_ADD_HEADERS(netdb.h) AC_TYPE_OFF_T AC_TYPE_PID_T @@ -68,23 +96,13 @@ AC_CHECK_TYPES(uint32_t) AC_CHECK_TYPES(u_short) AC_CHECK_TYPES(u_long) -dnl -dnl isn't among the list of standard headers that autoconf checks, -dnl but POSIX requires for socklen_t to be defined. -dnl -AC_CHECK_TYPES(socklen_t,,, -[AC_INCLUDES_DEFAULT -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_SOCKET_H -# include -#endif -]) +AC_CHECK_TYPES(socklen_t) -AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , [AC_MSG_ERROR(socket library not found)]) +AC_SEARCH_LIBS(socket, [socket ws2_32 wsock32], , + [AC_MSG_ERROR(socket library not found)]) AC_CHECK_FUNCS(fcntl) +AC_CHECK_FUNCS(flock) AC_CHECK_FUNCS(setsid) AC_CHECK_FUNCS(recvmsg) AC_CHECK_FUNCS(ftruncate) @@ -100,22 +118,15 @@ dnl Solaris 8 has [u]intmax_t but not strtoumax(). How utterly braindamaged. AC_CHECK_FUNCS(strtoumax) AC_CHECK_FUNCS(strtoull) -PA_MSGHDR_MSG_CONTROL -PA_STRUCT_IN_PKTINFO -PA_STRUCT_ADDRINFO +AC_CHECK_MEMBERS(struct msghdr.msg_control) +AC_CHECK_MEMBERS(struct in_pktinfo.ipi_addr) +AC_CHECK_MEMBERS(struct addrinfo.ai_addr) -PA_HEADER_DEFINES(fcntl.h, int, O_NONBLOCK) -PA_HEADER_DEFINES(fcntl.h, int, O_BINARY) -PA_HEADER_DEFINES(fcntl.h, int, O_TEXT) +AC_CHECK_DECLS([O_NONBLOCK, O_BINARY, O_TEXT]) +AC_CHECK_DECLS([F_SETLK]) +AC_CHECK_DECLS([LOCK_SH, LOCK_EX]) -PA_HEADER_DEFINES(fcntl.h, int, F_SETLK) - -PA_HEADER_DEFINES(sys/file.h, int, LOCK_SH) -PA_HEADER_DEFINES(sys/file.h, int, LOCK_EX) - -AH_TEMPLATE([HAVE_SIGSETJMP], -[Define if we have sigsetjmp, siglongjmp and sigjmp_buf.]) -PA_SIGSETJMP([AC_DEFINE(HAVE_SIGSETJMP)]) +PA_SIGSETJMP dnl dnl Get common paths @@ -123,38 +134,31 @@ dnl SRCROOT=`cd $srcdir && pwd` OBJROOT=`pwd` -XTRA=false PA_SEARCH_LIBS_AND_ADD(xmalloc, iberty) PA_SEARCH_LIBS_AND_ADD(xstrdup, iberty) PA_SEARCH_LIBS_AND_ADD(getopt_long, getopt, getopt_long) PA_SEARCH_LIBS_AND_ADD(getaddrinfo, [nsl resolv]) -if $pa_add_getaddrinfo -then - AC_SEARCH_LIBS(gethostbyname, [nsl resolv], +AS_IF([$pa_add_getaddrinfo], +[AC_SEARCH_LIBS(gethostbyname, [nsl resolv], [AC_SEARCH_LIBS(herror, [nsl resolv], , [AC_MSG_ERROR(herror not found)])], - [AC_MSG_ERROR(gethostbyname not found)]) -else - AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], , + [AC_MSG_ERROR(gethostbyname not found)])], +[AC_SEARCH_LIBS(freeaddrinfo, [nsl resolv], , [AC_MSG_ERROR(getaddrinfo but not freeaddrinfo found)]) AC_SEARCH_LIBS(gai_strerror, [nsl resolv], , - [AC_MSG_ERROR(getaddrinfo but not gai_strerror found)]) -fi + [AC_MSG_ERROR(getaddrinfo but not gai_strerror found)])]) PA_SEARCH_LIBS_AND_ADD(inet_ntop, [nsl resolv]) -if $pa_add_inet_ntop -then - AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], , - [AC_MSG_ERROR(inet_ntoa not found)]) -fi +AS_IF([$pa_add_inet_ntop], + [AC_SEARCH_LIBS(inet_ntoa, [nsl resolv], , + [AC_MSG_ERROR(inet_ntoa not found)])]) + AC_SEARCH_LIBS(inet_aton, [nsl resolv], ,[AC_MSG_ERROR(inet_aton not found)]) -AC_CHECK_FUNCS(daemon, , [XTRA=true; AC_LIBOBJ(daemon)]) -AC_CHECK_FUNCS(dup2, , [XTRA=true; AC_LIBOBJ(dup2)]) -if $XTRA -then - XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS" -fi +PA_SEARCH_LIBS_AND_ADD(daemon) +PA_SEARCH_LIBS_AND_ADD(dup2) + +AS_IF([$XTRA], [XTRALIBS="$OBJROOT/lib/libxtra.a $XTRALIBS"]) dnl dnl These libraries apply to the server only @@ -162,31 +166,14 @@ dnl common_libs="$LIBS" -PA_HEADER_DEFINES(netinet/in.h, int, IPPORT_TFTP) +AC_CHECK_DECLS(IPPORT_TFTP) -PA_WITH_BOOL(tcpwrappers, 1, -[ --without-tcpwrappers disable tcpwrapper permissions checking], -[ +PA_ARG_DISABLED([tcpwrappers], + [disable tcpwrapper permissions checking], [], + [ AC_SEARCH_LIBS(yp_get_default_domain, [nsl resolv]) PA_HAVE_TCPWRAPPERS -],:) - - -AH_TEMPLATE([WITH_REGEX], -[Define if we are compiling with regex filename remapping.]) - -PA_WITH_BOOL(remap, 1, -[ --without-remap disable regex-based filename remapping], -[ - AC_CHECK_HEADER(regex.h, - [ - AC_SEARCH_LIBS(regcomp, [regex rx], - [ - AC_DEFINE(WITH_REGEX) - TFTPDOBJS="remap.${OBJEXT} $TFTPDOBJS" - ]) - ]) -],:) + ]) TFTPD_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" @@ -198,10 +185,10 @@ dnl AH_TEMPLATE([WITH_READLINE], [Define if we are compiling with readline/editline command-line editing.]) -PA_WITH_BOOL(readline, 1, -[ --without-readline disable the use of readline command-line editing], -[ - AC_CHECK_HEADER(readline/readline.h, +PA_ARG_DISABLED([readline], + [disable the use of readline command-line editing], [], + [ + AC_CHECK_HEADER(readline/readline.h, [ dnl readline may need libtermcap or somesuch... AC_SEARCH_LIBS(tputs, [termcap terminfo]) @@ -226,23 +213,17 @@ LIBS="$common_libs" dnl dnl Check for IPV6 and disable-ipv6 dnl -PA_STRUCT_SOCKADDR_IN6 -AC_MSG_CHECKING([for IPv6 support]) -PA_WITH_BOOL(ipv6, 1, -[ --without-ipv6 disable the support for IPv6], -[ - if $HAVE_INET6 - then - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_IPV6, 1, [Define if IPv6 support is enabled.]) - PA_STRUCT_IN6_PKTINFO - else - AC_MSG_RESULT(no) - AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled]) - fi -], -[AC_MSG_RESULT(disabled)]) +AC_CHECK_MEMBERS(struct sockaddr_in6.sin6_addr) +AC_MSG_CHECKING([for IPv6 support]) +PA_ARG_DISABLED([ipv6], + [disable support for IPv6], + [AC_MSG_RESULT(disabled)], + [AS_IF([test x"$ac_cv_member_struct_sockaddr_in6_sin6_addr$ac_cv_member_struct_addrinfo_ai_addr" = xyesyes], + [AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_IPV6, 1, [define if IPv6 support is enabled])], + [AC_MSG_RESULT(no) + AC_MSG_WARN([*** we do not have required IPv6 structs - IPv6 will be disabled])])]) AC_SUBST(SRCROOT) AC_SUBST(OBJROOT) @@ -265,6 +246,29 @@ AC_PROG_INSTALL INSTALL='\${SRCROOT}'/"$INSTALL" fi] -AC_CONFIG_HEADERS(aconfig.h) -AC_CONFIG_FILES([MCONFIG]) +PA_ADD_CFLAGS(-W) +PA_ADD_CFLAGS(-Wall) +PA_ADD_CFLAGS(-Wpointer-arith) +PA_ADD_CFLAGS(-Wbad-function-cast) +PA_ADD_CFLAGS(-Wcast-equal) +PA_ADD_CFLAGS(-Wstrict-prototypes) +PA_ADD_CFLAGS(-Wmissing-prototypes) +PA_ADD_CFLAGS(-Wmissing-declarations) +PA_ADD_CFLAGS(-Wnested-externs) +PA_ADD_CFLAGS(-Winline) +PA_ADD_CFLAGS(-Wwrite-strings) +PA_ADD_CFLAGS(-Wundef) +PA_ADD_CFLAGS(-Wshadow) +PA_ADD_CFLAGS(-Wsign-compare) +PA_ADD_CFLAGS(-fno-strict-aliasing) + +dnl +dnl Test compiler features. On some compilers, this can be affected +dnl by -Werror options, so run this *after* those options are added. +dnl +PA_CHECK_BAD_STDC_INLINE +PA_C_TYPEOF + +AC_CONFIG_HEADERS([config/config.h]) +AC_CONFIG_FILES([config/MCONFIG]) AC_OUTPUT diff --git a/lib/Makefile b/lib/Makefile index 5dd83d0..a7fd057 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -4,7 +4,7 @@ SRCROOT = .. --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES ifeq ($(LIBOBJS),) diff --git a/tftp/Makefile b/tftp/Makefile index 20f4c18..9faa62f 100644 --- a/tftp/Makefile +++ b/tftp/Makefile @@ -1,7 +1,7 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES OBJS = tftp.$(O) main.$(O) diff --git a/tftpd/Makefile b/tftpd/Makefile index a05335d..5963581 100644 --- a/tftpd/Makefile +++ b/tftpd/Makefile @@ -1,7 +1,7 @@ SRCROOT = .. VERSION = $(shell cat ../version) --include ../MCONFIG +-include ../config/MCONFIG include ../MRULES OBJS = tftpd.$(O) recvfrom.$(O) misc.$(O) $(TFTPDOBJS) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index d7ef500..50b788b 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -289,7 +289,7 @@ myrecvfrom(int s, void *buf, int len, unsigned int flags, myaddr->sa.sa_family = from->sa.sa_family; sa_set_port(myaddr, htons(IPPORT_TFTP)); - return recvfrom(s, buf, len, flags, from, &fromlen); + return recvfrom(s, buf, len, flags, &from->sa, &fromlen); } #endif diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index fcb29aa..bec0f34 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -185,7 +185,8 @@ static struct rule *read_remap_rules(const char *rulefile) */ static int lock_file(int fd, int lock_write) { -#if defined(HAVE_FCNTL) && defined(HAVE_F_SETLK_DEFINITION) + (void)lock_write; +#if defined(HAVE_FCNTL) && HAVE_DECL_F_SETLK struct flock fl; fl.l_type = lock_write ? F_WRLCK : F_RDLCK; @@ -193,7 +194,7 @@ static int lock_file(int fd, int lock_write) fl.l_start = 0; fl.l_len = 0; /* Whole file */ return fcntl(fd, F_SETLK, &fl); -#elif defined(HAVE_LOCK_SH_DEFINITION) +#elif defined(HAVE_FLOCK) && HAVE_DECL_LOCK_SH && HAVE_DECL_LOCK_EX return flock(fd, lock_write ? LOCK_EX|LOCK_NB : LOCK_SH|LOCK_NB); #else return 0; /* Hope & pray... */ @@ -325,7 +326,7 @@ static int split_port(char **ap, char **pp) enum long_only_options { OPT_VERBOSITY = 256, }; - + static struct option long_options[] = { { "ipv4", 0, NULL, '4' }, { "ipv6", 0, NULL, '6' }, @@ -1246,7 +1247,7 @@ static int set_blksize2(uintmax_t *vp) static int set_rollover(uintmax_t *vp) { uintmax_t ro = *vp; - + if (ro > 65535) return 0; @@ -1345,7 +1346,7 @@ static void do_opt(const char *opt, const char *val, char **ap) nak(EOPTNEG, "Insufficient space for options"); exit(0); } - + memcpy(p, opt, optlen+1); p += optlen+1; memcpy(p, retbuf, retlen+1); From cb619257ed1875fcc95c31c81e43a21c0e00fbf1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 18:38:43 -0700 Subject: [PATCH 58/65] recvfrom: update config.h define for in_pktinfo.ipi_addr configure.ac now explicitly checks for struct in_pktinfo.ipi_addr; update the configure name to match. Signed-off-by: H. Peter Anvin --- tftpd/recvfrom.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tftpd/recvfrom.c b/tftpd/recvfrom.c index 50b788b..320678d 100644 --- a/tftpd/recvfrom.c +++ b/tftpd/recvfrom.c @@ -26,10 +26,12 @@ #if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL) -#include +#ifdef HAVE_SYS_UIO_H +# include +#endif #ifdef IP_PKTINFO -# ifndef HAVE_STRUCT_IN_PKTINFO +# ifndef HAVE_STRUCT_IN_PKTINFO_IPI_ADDR # ifdef __linux__ /* Assume this version of glibc simply lacks the definition */ struct in_pktinfo { From 74c5d8a02030a0e598f6dd7db0c122d1b38bd904 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 19:31:17 -0700 Subject: [PATCH 59/65] .editorconfig: tabs are 8 The size of a tab is 8, even if the indentation is 4. Signed-off-by: H. Peter Anvin --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 0143c3c..544d21f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,4 +8,4 @@ insert_final_newline = true [{*.c,*.h}] indent_style = space indent_size = 4 -tab_width = 4 +tab_width = 8 From ac7f98e4d8036d6194b41920e46e37be40b496a4 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 20:19:55 -0700 Subject: [PATCH 60/65] remap: fix timeouts for "g", add a "gg" flag to match sed s///g Make sure that when using the global option, we still bump the deadman timer. The "g" option really should only have applied to the right-hand unmatched part of the string, like in sed. Add a "gg" option which does that. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 287 ++++++++++++++++++++++++++++------------------- tftpd/tftpd.8.in | 21 +++- 2 files changed, 189 insertions(+), 119 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index 16801c1..d74b2db 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -22,7 +22,7 @@ #include "tftpd.h" #include "remap.h" -#define DEADMAN_MAX_STEPS 1024 /* Timeout after this many steps */ +#define DEADMAN_MAX_STEPS 4096 /* Timeout after this many steps */ #define MAXLINE 16384 /* Truncate a line at this many bytes */ #define RULE_REWRITE 0x01 /* This is a rewrite rule */ @@ -37,6 +37,7 @@ #define RULE_HASFILE 0x100 /* Valid if rule results in a valid filename */ #define RULE_RRQ 0x200 /* Get (read) only */ #define RULE_WRQ 0x400 /* Put (write) only */ +#define RULE_SEDG 0x800 /* sed-style global */ struct rule { struct rule *next; @@ -61,22 +62,30 @@ static int xform_tolower(int c) return tolower(c); } -/* Do \-substitution. Call with string == NULL to get length only. */ -static int genmatchstring(char *string, const char *pattern, - const char *input, const regmatch_t * pmatch, - match_pattern_callback macrosub) +/* + * Do \-substitution. Call with string == NULL to get length only. + * "start" indicates an offset into the input buffer where the pattern + * match was started. + */ +static int do_genmatchstring(char *string, const char *pattern, + const char *ibuf, + const regmatch_t * pmatch, + match_pattern_callback macrosub, + int start, int *nextp) { int (*xform) (int) = xform_null; int len = 0; int n, mlen, sublen; int endbytes; + const char *input = ibuf + start; /* Get section before match; note pmatch[0] is the whole match */ endbytes = strlen(input) - pmatch[0].rm_eo; - len = pmatch[0].rm_so + endbytes; + len = start + pmatch[0].rm_so; if (string) { - memcpy(string, input, pmatch[0].rm_so); - string += pmatch[0].rm_so; + /* Copy the prefix before "start" as well! */ + memcpy(string, ibuf, start + pmatch[0].rm_so); + string += start + pmatch[0].rm_so; } /* Transform matched section */ @@ -102,7 +111,7 @@ static int genmatchstring(char *string, const char *pattern, mlen = pmatch[n].rm_eo - pmatch[n].rm_so; len += mlen; if (string) { - const char *p = input + pmatch[n].rm_so; + const char *p = input + start + pmatch[n].rm_so; while (mlen--) *string++ = xform(*p++); } @@ -145,7 +154,12 @@ static int genmatchstring(char *string, const char *pattern, } } + /* Pointer to post-substitution tail */ + if (nextp) + *nextp = len; + /* Copy section after match */ + len += endbytes; if (string) { memcpy(string, input + pmatch[0].rm_eo, endbytes); string[endbytes] = '\0'; @@ -154,6 +168,26 @@ static int genmatchstring(char *string, const char *pattern, return len; } +/* + * Ditto, but allocate the string in a new buffer + */ + +static int genmatchstring(char **string, const char *pattern, + const char *ibuf, + const regmatch_t * pmatch, + match_pattern_callback macrosub, + int start, int *nextp) +{ + int len; + char *buf; + + len = do_genmatchstring(NULL, pattern, ibuf, pmatch, + macrosub, start, NULL); + buf = tf_malloc(len + 1); + return do_genmatchstring(buf, pattern, ibuf, pmatch, + macrosub, start, nextp); +} + /* * Extract a string terminated by non-escaped whitespace; ignoring * leading whitespace. Consider an unescaped # to be a comment marker, @@ -165,7 +199,7 @@ static int readescstring(char *buf, char **str) int wasbs = 0, len = 0; while (*p && isspace(*p)) - p++; + p++; if (!*p) { *buf = '\0'; @@ -211,14 +245,17 @@ static int parseline(char *line, struct rule *r, int lineno) r->rule_flags |= RULE_REWRITE; break; case 'g': - r->rule_flags |= RULE_GLOBAL; + if (r->rule_flags & RULE_GLOBAL) + r->rule_flags |= RULE_SEDG; + else + r->rule_flags |= RULE_GLOBAL; break; case 'e': r->rule_flags |= RULE_EXIT; break; - case 'E': - r->rule_flags |= RULE_HASFILE; - break; + case 'E': + r->rule_flags |= RULE_HASFILE; + break; case 's': r->rule_flags |= RULE_RESTART; break; @@ -231,18 +268,18 @@ static int parseline(char *line, struct rule *r, int lineno) case '~': r->rule_flags |= RULE_INVERSE; break; - case '4': - r->rule_flags |= RULE_IPV4; - break; - case '6': - r->rule_flags |= RULE_IPV6; - break; - case 'G': - r->rule_flags |= RULE_RRQ; - break; - case 'P': - r->rule_flags |= RULE_WRQ; - break; + case '4': + r->rule_flags |= RULE_IPV4; + break; + case '6': + r->rule_flags |= RULE_IPV6; + break; + case 'G': + r->rule_flags |= RULE_RRQ; + break; + case 'P': + r->rule_flags |= RULE_WRQ; + break; default: syslog(LOG_ERR, "Remap command \"%s\" on line %d contains invalid char \"%c\"", @@ -253,20 +290,20 @@ static int parseline(char *line, struct rule *r, int lineno) } if (r->rule_flags & RULE_REWRITE) { - if (r->rule_flags & RULE_INVERSE) { - syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", - lineno, line); - return -1; /* Error */ - } - if ((r->rule_flags & (RULE_GLOBAL|RULE_HASFILE)) - == (RULE_GLOBAL|RULE_HASFILE)) { - syslog(LOG_ERR, "E rules cannot be combined with g, line %d: %s\n", - lineno, line); - return -1; /* Error */ - } + if (r->rule_flags & RULE_INVERSE) { + syslog(LOG_ERR, "r rules cannot be inverted, line %d: %s\n", + lineno, line); + return -1; /* Error */ + } + if ((r->rule_flags & (RULE_GLOBAL|RULE_SEDG|RULE_HASFILE)) + == (RULE_GLOBAL|RULE_HASFILE)) { + syslog(LOG_ERR, "E rules cannot be combined with g (but gg is OK), line %d: %s\n", + lineno, line); + return -1; /* Error */ + } } else { - /* RULE_GLOBAL is meaningless without RULE_REWRITE */ - r->rule_flags &= ~RULE_GLOBAL; + /* RULE_GLOBAL and RULE_SEDG are meaningless without RULE_REWRITE */ + r->rule_flags &= ~(RULE_GLOBAL|RULE_SEDG); } /* Read and compile the regex */ @@ -348,19 +385,23 @@ void freerules(struct rule *r) /* Execute a rule set on a string; returns a malloc'd new string. */ char *rewrite_string(const struct formats *pf, - const char *input, const struct rule *rules, + const char *input, const struct rule *rules, int mode, int af, match_pattern_callback macrosub, const char **errmsg) { char *current = tfstrdup(input); - char *newstr; + char *newstr, *newerstr; const char *accerr; const struct rule *ruleptr = rules; regmatch_t pmatch[10]; - int len; + int i; + int len, newlen; int was_match = 0; int deadman = DEADMAN_MAX_STEPS; + int matchsense; + int pmatches; unsigned int bad_flags; + int ggoffset; /* Default error */ *errmsg = "Remap table failure"; @@ -376,30 +417,25 @@ char *rewrite_string(const struct formats *pf, if (af != AF_INET6) bad_flags |= RULE_IPV6; for (ruleptr = rules; ruleptr; ruleptr = ruleptr->next) { - if (ruleptr->rule_flags & bad_flags) - continue; /* This rule is excluded by flags */ + if (ruleptr->rule_flags & bad_flags) + continue; /* This rule is excluded by flags */ - if (!deadman--) { - syslog(LOG_WARNING, - "remap: Breaking loop, input = %s, last = %s", input, - current); - free(current); - return NULL; /* Did not terminate! */ - } + matchsense = ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0; + pmatches = ruleptr->rule_flags & RULE_INVERSE ? 0 : 10; + + /* Clear the pmatch[] array */ + for (i = 0; i < 10; i++) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; do { - if (regexec(&ruleptr->rx, current, 10, pmatch, 0) == - (ruleptr->rule_flags & RULE_INVERSE ? REG_NOMATCH : 0)) { + if (!deadman--) + goto dead; + + if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0) + == matchsense) { /* Match on this rule */ was_match = 1; - if (ruleptr->rule_flags & RULE_INVERSE) { - /* No actual match, so clear out the pmatch array */ - int i; - for (i = 0; i < 10; i++) - pmatch[i].rm_so = pmatch[i].rm_eo = -1; - } - if (ruleptr->rule_flags & RULE_ABORT) { if (verbosity >= 3) { syslog(LOG_INFO, "remap: rule %d: abort: %s", @@ -407,12 +443,8 @@ char *rewrite_string(const struct formats *pf, } if (ruleptr->pattern[0]) { /* Custom error message */ - len = - genmatchstring(NULL, ruleptr->pattern, current, - pmatch, macrosub); - newstr = tfmalloc(len + 1); - genmatchstring(newstr, ruleptr->pattern, current, - pmatch, macrosub); + genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, NULL); *errmsg = newstr; } else { *errmsg = NULL; @@ -422,58 +454,76 @@ char *rewrite_string(const struct formats *pf, } if (ruleptr->rule_flags & RULE_REWRITE) { - len = genmatchstring(NULL, ruleptr->pattern, current, - pmatch, macrosub); - newstr = tfmalloc(len + 1); - genmatchstring(newstr, ruleptr->pattern, current, - pmatch, macrosub); - if ((ruleptr->rule_flags & RULE_HASFILE) && - pf->f_validate(newstr, mode, pf, &accerr)) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", - ruleptr->nrule, accerr, newstr); - } - free(newstr); - was_match = 0; - break; - } - free(current); - current = newstr; - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: rewrite: %s", - ruleptr->nrule, current); - } - } else if (ruleptr->rule_flags & RULE_HASFILE) { - if (pf->f_validate(current, mode, pf, &accerr)) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", - ruleptr->nrule, accerr); - } - was_match = 0; - break; - } - } - } else { - break; /* No match, terminate unconditionally */ + len = genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, &ggoffset); + + if (ruleptr->rule_flags & RULE_SEDG) { + /* sed-style partial-matching global */ + while (ggoffset < len && + regexec(ruleptr->rx, newstr + ggoffset, + pmatches, pmatch, + ggoffset ? REG_NOTBOL : 0) + == matchsense) { + if (!deadman--) { + free(current); + current = newstr; + goto dead; + } + len = genmatchstring(&newerstr, ruleptr->pattern, + newstr, pmatch, macrosub, + ggoffset, &ggoffset); + free(newstr); + newstr = newerstr; + } + } + + if ((ruleptr->rule_flags & RULE_HASFILE) && + pf->f_validate(newstr, mode, pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", + ruleptr->nrule, accerr, newstr); + } + free(newstr); + was_match = 0; + break; + } + } + free(current); + current = newstr; + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: rewrite: %s", + ruleptr->nrule, current); + } + } else if (ruleptr->rule_flags & RULE_HASFILE) { + if (pf->f_validate(current, mode, pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: not exiting (%s)\n", + ruleptr->nrule, accerr); + } + was_match = 0; + break; + } } - /* If the rule is global, keep going until no match */ - } while (ruleptr->rule_flags & RULE_GLOBAL); + } else { + break; /* No match, terminate unconditionally */ + } + /* If the rule is (old-style) global, keep going until no match */ + } while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL); - if (was_match) { - was_match = 0; + if (was_match) { + was_match = 0; - if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: exit", - ruleptr->nrule); - } - return current; /* Exit here, we're done */ - } else if (ruleptr->rule_flags & RULE_RESTART) { - ruleptr = rules; /* Start from the top */ - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: restart", - ruleptr->nrule); - } + if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: exit", + ruleptr->nrule); + } + return current; /* Exit here, we're done */ + } else if (ruleptr->rule_flags & RULE_RESTART) { + ruleptr = rules; /* Start from the top */ + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: restart", + ruleptr->nrule); } } } @@ -482,4 +532,11 @@ char *rewrite_string(const struct formats *pf, syslog(LOG_INFO, "remap: done"); } return current; + +dead: /* Deadman expired */ + syslog(LOG_WARNING, + "remap: Breaking loop, input = %s, last = %s", input, + current); + free(current); + return NULL; /* Did not terminate! */ } diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index c9722cf..9baf7fd 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -1,5 +1,5 @@ .\" -*- nroff -*- --------------------------------------------------------- * -.\" +.\" .\" Copyright (c) 1990, 1993, 1994 .\" The Regents of the University of California. All rights reserved. .\" @@ -270,7 +270,18 @@ by the The replacement pattern may contain escape sequences; see below. .TP .B g -Repeat this rule until it no longer matches. This is always used with +Repeat this rule until it no longer matches. This is always used with +.BR r . +.TP +.B gg +Repeat this rule until it no longer matches, but only on the portion +of the string that has not yet been matched, similar to how the +.B s +command with the +.B g +option works in +.BR sed (1). +This is always used with .BR r . .TP .B i @@ -288,7 +299,9 @@ end rule processing after executing the rule. If this is combined with .BR r , then if the substitution does \fInot\fP result in a valid filename, the substitution is undone. This cannot be combined with -.BR g . +.BR g , +but \fIcan\fP be combined with +.BR gg . .TP .B s If this rule matches, start rule processing over from the very first @@ -400,7 +413,7 @@ Access to files can, and should, be restricted by invoking .B tftpd with a list of directories by including pathnames as server program arguments on the command line. In this case access is restricted to -files whole names are prefixed by one of the given directories. If +files whose names are prefixed by one of the given directories. If possible, it is recommended that the .B \-\-secure flag is used to set up a chroot() environment for the server to run in From 9a92dec1dcc618051500908a1f24761ab29abdcb Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 20:36:22 -0700 Subject: [PATCH 61/65] tftpd: make it possible to adjust the remap deadman Allow the user to tweak the remap deadman counter if it is necessary for whatever reason. Signed-off-by: H. Peter Anvin --- tftpd/remap.c | 10 ++++++---- tftpd/remap.h | 3 +++ tftpd/tftpd.8.in | 4 ++++ tftpd/tftpd.c | 14 ++++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/tftpd/remap.c b/tftpd/remap.c index d74b2db..6497cad 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -39,6 +39,8 @@ #define RULE_WRQ 0x400 /* Put (write) only */ #define RULE_SEDG 0x800 /* sed-style global */ +int deadman_max_steps = DEADMAN_MAX_STEPS; + struct rule { struct rule *next; int nrule; @@ -397,7 +399,7 @@ char *rewrite_string(const struct formats *pf, int i; int len, newlen; int was_match = 0; - int deadman = DEADMAN_MAX_STEPS; + int deadman = deadman_max_steps; int matchsense; int pmatches; unsigned int bad_flags; @@ -534,9 +536,9 @@ char *rewrite_string(const struct formats *pf, return current; dead: /* Deadman expired */ - syslog(LOG_WARNING, - "remap: Breaking loop, input = %s, last = %s", input, - current); + syslog(LOG_ERR, + "remap: Breaking loop after %d steps, input = %s, last = %s", + deadman_max_steps, input, current); free(current); return NULL; /* Did not terminate! */ } diff --git a/tftpd/remap.h b/tftpd/remap.h index a9954ab..c8d76ff 100644 --- a/tftpd/remap.h +++ b/tftpd/remap.h @@ -40,5 +40,8 @@ char *rewrite_string(const struct formats *, const char *, const struct rule *, int, int, match_pattern_callback, const char **); +/* Remapping deadman counter */ +extern int deadman_max_steps; + #endif /* WITH_REGEX */ #endif /* TFTPD_REMAP_H */ diff --git a/tftpd/tftpd.8.in b/tftpd/tftpd.8.in index 9baf7fd..3d43325 100644 --- a/tftpd/tftpd.8.in +++ b/tftpd/tftpd.8.in @@ -163,6 +163,10 @@ remapping below. This option may not be compiled in, see the output of .B "in.tftpd \-V" to verify whether or not it is available. .TP +.\fB\-\-map-steps\fP \fIsteps\fP +Specify the number of remapping rules that may be executed before the +filename mapping fails. The default is 4096. +.TP \fB\-\-verbose\fP, \fB\-v\fP Increase the logging verbosity of .BR tftpd . diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index bec0f34..fc7e54a 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -325,6 +325,7 @@ static int split_port(char **ap, char **pp) enum long_only_options { OPT_VERBOSITY = 256, + OPT_MAP_STEPS }; static struct option long_options[] = { @@ -347,6 +348,7 @@ static struct option long_options[] = { { "retransmit", 1, NULL, 'T' }, { "port-range", 1, NULL, 'R' }, { "map-file", 1, NULL, 'm' }, + { "map-steps", 1, NULL, OPT_MAP_STEPS }, { "pidfile", 1, NULL, 'P' }, { NULL, 0, NULL, 0 } }; @@ -495,6 +497,18 @@ int main(int argc, char **argv) } rewrite_file = optarg; break; + case OPT_MAP_STEPS: + { + char *ep; + unsigned long steps = strtoul(optarg, &ep, 0); + if (*optarg && *ep && steps > 0 && steps <= INT_MAX) { + deadman_max_steps = steps; + } else { + syslog(LOG_ERR, "Bad --map-steps option: %s", optarg); + exit(EX_USAGE); + } + break; + } #endif case 'v': verbosity++; From 33ec23c0dccbee8d8af207d2f8f59ee52ecfb6fa Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 20:56:27 -0700 Subject: [PATCH 62/65] remap: re-enable in autoconf; fix missing pointer assignment Need to actually output something, too. The whole section for remap had gotten dropped from autoconf, with the obvious results... Signed-off-by: H. Peter Anvin --- configure.ac | 12 ++++++++++-- tftpd/remap.c | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index be2a500..49b23f9 100644 --- a/configure.ac +++ b/configure.ac @@ -175,6 +175,14 @@ PA_ARG_DISABLED([tcpwrappers], PA_HAVE_TCPWRAPPERS ]) +AC_CHECK_HEADERS_ONCE([regex.h]) +PA_ARG_DISABLED([remap], + [disable regex-based filename remapping], [], + [AS_IF([test x"$ac_cv_header_regex_h" = xyes], + [AC_SEARCH_LIBS(regcomp, [regex rx], + [AC_DEFINE([WITH_REGEX], 1, + [Define if we are compiling with regex filename remapping.])])])]) + TFTPD_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" @@ -188,7 +196,7 @@ AH_TEMPLATE([WITH_READLINE], PA_ARG_DISABLED([readline], [disable the use of readline command-line editing], [], [ - AC_CHECK_HEADER(readline/readline.h, + AC_CHECK_HEADER([readline/readline.h], [ dnl readline may need libtermcap or somesuch... AC_SEARCH_LIBS(tputs, [termcap terminfo]) @@ -197,7 +205,7 @@ PA_ARG_DISABLED([readline], [AC_DEFINE(WITH_READLINE)]) AC_CHECK_HEADERS(readline/history.h) ], - [AC_CHECK_HEADER(editline/readline.h, + [AC_CHECK_HEADER([editline/readline.h], [ dnl editline may need libtermcap or somesuch... AC_SEARCH_LIBS(tputs, [termcap terminfo]) diff --git a/tftpd/remap.c b/tftpd/remap.c index 6497cad..3fea915 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -185,7 +185,7 @@ static int genmatchstring(char **string, const char *pattern, len = do_genmatchstring(NULL, pattern, ibuf, pmatch, macrosub, start, NULL); - buf = tf_malloc(len + 1); + *string = buf = tf_malloc(len + 1); return do_genmatchstring(buf, pattern, ibuf, pmatch, macrosub, start, nextp); } From 2c86ff58dcc003107b47f2d35aa0fdc4a3fd95e1 Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 10 Jun 2024 21:06:50 -0700 Subject: [PATCH 63/65] remap: *actually* build, and fix masked logic errors Well, now it is actually being compiled, and should hopefully work again... Signed-off-by: H. Peter Anvin --- configure.ac | 3 +- tftpd/remap.c | 115 +++++++++++++++++++++++++------------------------- tftpd/tftpd.c | 1 - 3 files changed, 60 insertions(+), 59 deletions(-) diff --git a/configure.ac b/configure.ac index 49b23f9..deaa4fc 100644 --- a/configure.ac +++ b/configure.ac @@ -181,7 +181,8 @@ PA_ARG_DISABLED([remap], [AS_IF([test x"$ac_cv_header_regex_h" = xyes], [AC_SEARCH_LIBS(regcomp, [regex rx], [AC_DEFINE([WITH_REGEX], 1, - [Define if we are compiling with regex filename remapping.])])])]) + [Define if we are compiling with regex filename remapping.]) + TFTPDOBJS="remap.\$(O) $TFTPOBJS"])])]) TFTPD_LIBS="$LIBS $XTRALIBS" LIBS="$common_libs" diff --git a/tftpd/remap.c b/tftpd/remap.c index 3fea915..413d117 100644 --- a/tftpd/remap.c +++ b/tftpd/remap.c @@ -185,7 +185,7 @@ static int genmatchstring(char **string, const char *pattern, len = do_genmatchstring(NULL, pattern, ibuf, pmatch, macrosub, start, NULL); - *string = buf = tf_malloc(len + 1); + *string = buf = tfmalloc(len + 1); return do_genmatchstring(buf, pattern, ibuf, pmatch, macrosub, start, nextp); } @@ -397,7 +397,7 @@ char *rewrite_string(const struct formats *pf, const struct rule *ruleptr = rules; regmatch_t pmatch[10]; int i; - int len, newlen; + int len; int was_match = 0; int deadman = deadman_max_steps; int matchsense; @@ -429,67 +429,71 @@ char *rewrite_string(const struct formats *pf, for (i = 0; i < 10; i++) pmatch[i].rm_so = pmatch[i].rm_eo = -1; + was_match = 0; + do { if (!deadman--) goto dead; if (regexec(&ruleptr->rx, current, pmatches, pmatch, 0) - == matchsense) { - /* Match on this rule */ - was_match = 1; + != matchsense) + break; /* No match, break out of do loop */ - if (ruleptr->rule_flags & RULE_ABORT) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: abort: %s", - ruleptr->nrule, current); - } - if (ruleptr->pattern[0]) { - /* Custom error message */ - genmatchstring(&newstr, ruleptr->pattern, current, - pmatch, macrosub, 0, NULL); - *errmsg = newstr; - } else { - *errmsg = NULL; - } - free(current); - return (NULL); + /* Match on this rule */ + was_match = 1; + + if (ruleptr->rule_flags & RULE_ABORT) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: abort: %s", + ruleptr->nrule, current); } + if (ruleptr->pattern[0]) { + /* Custom error message */ + genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, NULL); + *errmsg = newstr; + } else { + *errmsg = NULL; + } + free(current); + return (NULL); + } - if (ruleptr->rule_flags & RULE_REWRITE) { - len = genmatchstring(&newstr, ruleptr->pattern, current, - pmatch, macrosub, 0, &ggoffset); + if (ruleptr->rule_flags & RULE_REWRITE) { + len = genmatchstring(&newstr, ruleptr->pattern, current, + pmatch, macrosub, 0, &ggoffset); - if (ruleptr->rule_flags & RULE_SEDG) { - /* sed-style partial-matching global */ - while (ggoffset < len && - regexec(ruleptr->rx, newstr + ggoffset, - pmatches, pmatch, - ggoffset ? REG_NOTBOL : 0) - == matchsense) { - if (!deadman--) { - free(current); - current = newstr; - goto dead; - } - len = genmatchstring(&newerstr, ruleptr->pattern, - newstr, pmatch, macrosub, - ggoffset, &ggoffset); - free(newstr); - newstr = newerstr; - } - } - - if ((ruleptr->rule_flags & RULE_HASFILE) && - pf->f_validate(newstr, mode, pf, &accerr)) { - if (verbosity >= 3) { - syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", - ruleptr->nrule, accerr, newstr); + if (ruleptr->rule_flags & RULE_SEDG) { + /* sed-style partial-matching global */ + while (ggoffset < len && + regexec(&ruleptr->rx, newstr + ggoffset, + pmatches, pmatch, + ggoffset ? REG_NOTBOL : 0) + == matchsense) { + if (!deadman--) { + free(current); + current = newstr; + goto dead; } + len = genmatchstring(&newerstr, ruleptr->pattern, + newstr, pmatch, macrosub, + ggoffset, &ggoffset); free(newstr); - was_match = 0; - break; + newstr = newerstr; } } + + if ((ruleptr->rule_flags & RULE_HASFILE) && + pf->f_validate(newstr, mode, pf, &accerr)) { + if (verbosity >= 3) { + syslog(LOG_INFO, "remap: rule %d: ignored rewrite (%s): %s", + ruleptr->nrule, accerr, newstr); + } + free(newstr); + was_match = 0; + break; + } + free(current); current = newstr; if (verbosity >= 3) { @@ -506,14 +510,11 @@ char *rewrite_string(const struct formats *pf, break; } } - } else { - break; /* No match, terminate unconditionally */ - } - /* If the rule is (old-style) global, keep going until no match */ - } while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL); + /* If the rule is (old-style) global, keep going until no match */ + } while ((ruleptr->rule_flags & (RULE_GLOBAL|RULE_SEDG)) == RULE_GLOBAL); - if (was_match) { - was_match = 0; + if (!was_match) + continue; /* Next rule */ if (ruleptr->rule_flags & (RULE_EXIT|RULE_HASFILE)) { if (verbosity >= 3) { diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c index fc7e54a..e0041dc 100644 --- a/tftpd/tftpd.c +++ b/tftpd/tftpd.c @@ -499,7 +499,6 @@ int main(int argc, char **argv) break; case OPT_MAP_STEPS: { - char *ep; unsigned long steps = strtoul(optarg, &ep, 0); if (*optarg && *ep && steps > 0 && steps <= INT_MAX) { deadman_max_steps = steps; From 37ac880897b0d53bfe2bfbfc1e03078039ec9846 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Fri, 25 Apr 2025 01:21:57 +0300 Subject: [PATCH 64/65] Applying Arch Linux Patch https://gitlab.archlinux.org/archlinux/packaging/packages/tftp-hpa/-/blob/a70146278f2d494448a831b29cd6fa955e352b2a/tftp-hpa-0.49-fortify-strcpy-crash.patch --- tftp/tftp.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tftp/tftp.c b/tftp/tftp.c index 33a4175..9c9c5af 100644 --- a/tftp/tftp.c +++ b/tftp/tftp.c @@ -278,15 +278,16 @@ makerequest(int request, const char *name, struct tftphdr *tp, const char *mode) { char *cp; + size_t len; tp->th_opcode = htons((u_short) request); cp = (char *)&(tp->th_stuff); - strcpy(cp, name); - cp += strlen(name); - *cp++ = '\0'; - strcpy(cp, mode); - cp += strlen(mode); - *cp++ = '\0'; + len = strlen(name) + 1; + memcpy(cp, name, len); + cp += len; + len = strlen(mode) + 1; + memcpy(cp, mode, len); + cp += len; return (cp - (char *)tp); } From 1dd16bc0df85f556db194168cd6e37f2d892b927 Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Fri, 25 Apr 2025 01:23:54 +0300 Subject: [PATCH 65/65] doesn't flush by default, so the prompt is never printed https://gitlab.alpinelinux.org/alpine/aports/-/blob/3.21-stable/main/tftp-hpa/fix-prompt-flush.patch --- tftp/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tftp/main.c b/tftp/main.c index ac06330..ecdc0e0 100644 --- a/tftp/main.c +++ b/tftp/main.c @@ -806,6 +806,7 @@ static void command(void) exit(0); /* EOF */ #else fputs(prompt, stdout); + fflush(stdout); if (fgets(line, LBUFLEN, stdin) == 0) { if (feof(stdin)) { exit(0);