From 4636661a64260cb90dd6cd6ced6cac031029d9ad Mon Sep 17 00:00:00 2001 From: sunil Date: Sat, 6 Jan 2018 07:59:27 +0000 Subject: [PATCH] Import gilles@'s standalone spfwalk utility into smtpctl(8) as 'spf walk' command. Ok gilles@ --- usr.sbin/smtpd/smtpctl.8 | 10 +- usr.sbin/smtpd/smtpctl.c | 5 +- usr.sbin/smtpd/smtpctl/Makefile | 8 +- usr.sbin/smtpd/spfwalk.c | 238 ++++++++++++++++++++++++++++++++ 4 files changed, 255 insertions(+), 6 deletions(-) create mode 100644 usr.sbin/smtpd/spfwalk.c diff --git a/usr.sbin/smtpd/smtpctl.8 b/usr.sbin/smtpd/smtpctl.8 index e231fe67822..4db465cc621 100644 --- a/usr.sbin/smtpd/smtpctl.8 +++ b/usr.sbin/smtpd/smtpctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: smtpctl.8,v 1.59 2017/05/23 06:55:05 jmc Exp $ +.\" $OpenBSD: smtpctl.8,v 1.60 2018/01/06 07:59:27 sunil Exp $ .\" .\" Copyright (c) 2006 Pierre-Yves Ritschard .\" Copyright (c) 2012 Gilles Chehade @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 23 2017 $ +.Dd $Mdocdate: January 6 2018 $ .Dt SMTPCTL 8 .Os .Sh NAME @@ -245,6 +245,12 @@ Displays runtime statistics concerning .Xr smtpd 8 . .It Cm show status Shows if MTA, MDA and SMTP systems are currently running or paused. +.It Cm spf walk +Recursively lookup SPF records for the domains read from stdin. +For example: +.Bd -literal -offset indent +# smtpctl spf walk < domains.txt +.Ed .It Cm trace Ar subsystem Enables real-time tracing of .Ar subsystem . diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c index bd85863267c..ec2320b0a31 100644 --- a/usr.sbin/smtpd/smtpctl.c +++ b/usr.sbin/smtpd/smtpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpctl.c,v 1.154 2017/07/27 18:48:30 sunil Exp $ */ +/* $OpenBSD: smtpctl.c,v 1.155 2018/01/06 07:59:27 sunil Exp $ */ /* * Copyright (c) 2013 Eric Faurot @@ -69,6 +69,8 @@ static int is_gzip_buffer(const char *); static FILE *offline_file(void); static void sendmail_compat(int, char **); +extern int do_spfwalk(int, struct parameter *); + extern char *__progname; int sendmail; struct smtpd *env; @@ -1067,6 +1069,7 @@ main(int argc, char **argv) cmd_install("show routes", do_show_routes); cmd_install("show stats", do_show_stats); cmd_install("show status", do_show_status); + cmd_install("spf walk", do_spfwalk); cmd_install("trace ", do_trace); cmd_install("uncorrupt ", do_uncorrupt); cmd_install("unprofile ", do_unprofile); diff --git a/usr.sbin/smtpd/smtpctl/Makefile b/usr.sbin/smtpd/smtpctl/Makefile index 0355bb93fea..73d990fdf86 100644 --- a/usr.sbin/smtpd/smtpctl/Makefile +++ b/usr.sbin/smtpd/smtpctl/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.45 2017/07/03 22:21:47 espie Exp $ +# $OpenBSD: Makefile,v 1.46 2018/01/06 07:59:27 sunil Exp $ .PATH: ${.CURDIR}/.. @@ -46,7 +46,9 @@ SRCS+= table_static.c SRCS+= table_db.c SRCS+= table_getpwnam.c SRCS+= table_proc.c +SRCS+= unpack_dns.c +SRCS+= spfwalk.c -LDADD+= -lutil -lz -lcrypto -DPADD+= ${LIBUTIL} ${LIBZ} ${LIBCRYPTO} +LDADD+= -levent -lutil -lz -lcrypto +DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBZ} ${LIBCRYPTO} .include diff --git a/usr.sbin/smtpd/spfwalk.c b/usr.sbin/smtpd/spfwalk.c new file mode 100644 index 00000000000..833841adad0 --- /dev/null +++ b/usr.sbin/smtpd/spfwalk.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2017 Gilles Chehade + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smtpd-defines.h" +#include "unpack_dns.h" +#include "parser.h" + +int do_spfwalk(int, struct parameter *); + +static void dispatch_txt(struct dns_rr *); +static void dispatch_mx(struct dns_rr *); +static void dispatch_a(struct dns_rr *); +static void dispatch_aaaa(struct dns_rr *); +static void lookup_record(int, const char *, void (*)(struct dns_rr *)); +static void dispatch_record(struct asr_result *, void *); + +int ip_v4 = 0; +int ip_v6 = 0; +int ip_both = 1; + +int +do_spfwalk(int argc, struct parameter *argv) +{ + struct passwd *pw; + const char *ip_family = NULL; + char *line = NULL; + size_t linesize = 0; + ssize_t linelen; + + if (argv) + ip_family = argv[0].u.u_str; + + if (ip_family) { + if (strcmp(ip_family, "-4") == 0) { + ip_both = 0; + ip_v4 = 1; + } else if (strcmp(ip_family, "-6") == 0) { + ip_both = 0; + ip_v6 = 1; + } else + errx(1, "invalid ip_family"); + } + + argv += optind; + argc -= optind; + + if ((pw = getpwnam(SMTPD_USER)) == NULL) + errx(1, "unknown user " SMTPD_USER); + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + err(1, "do_spfwalk: cannot drop privileges"); + + event_init(); + + while ((linelen = getline(&line, &linesize, stdin)) != -1) { + while (linelen-- > 0 && isspace(line[linelen])) + line[linelen] = '\0'; + + if (linelen > 0) + lookup_record(T_TXT, line, dispatch_txt); + } + + free(line); + + if (pledge("dns stdio", NULL) == -1) + err(1, "pledge"); + + event_dispatch(); + + return 0; +} + +void +lookup_record(int type, const char *record, void (*cb)(struct dns_rr *)) +{ + struct asr_query *as; + + as = res_query_async(record, C_IN, type, NULL); + if (as == NULL) + err(1, "res_query_async"); + event_asr_run(as, dispatch_record, cb); +} + +void +dispatch_record(struct asr_result *ar, void *arg) +{ + void (*cb)(struct dns_rr *) = arg; + struct unpack pack; + struct dns_header h; + struct dns_query q; + struct dns_rr rr; + + /* best effort */ + if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA) + return; + + unpack_init(&pack, ar->ar_data, ar->ar_datalen); + unpack_header(&pack, &h); + unpack_query(&pack, &q); + + for (; h.ancount; h.ancount--) { + unpack_rr(&pack, &rr); + /**/ + cb(&rr); + } +} + +void +dispatch_txt(struct dns_rr *rr) +{ + char buf[512]; + char buf2[512]; + char *in = buf; + char *argv[512]; + char **ap = argv; + + print_dname(rr->rr.other.rdata, buf, sizeof(buf)); + buf[strlen(buf) - 1] = '\0'; + if (buf[strlen(buf) - 1] == '.') + buf[strlen(buf) - 1] = '\0'; + if (strncasecmp("v=spf1 ", buf, 7)) + return; + + while ((*ap = strsep(&in, " ")) != NULL) { + if (strcasecmp(*ap, "v=spf1") == 0) + continue; + + if (strncasecmp("ip4:", *ap, 4) == 0) { + if (ip_v4 == 1 || ip_both == 1) + printf("%s\n", *(ap) + 4); + continue; + } + if (strncasecmp("ip6:", *ap, 4) == 0) { + if (ip_v6 == 1 || ip_both == 1) + printf("%s\n", *(ap) + 4); + continue; + } + if (strncasecmp("+ip4:", *ap, 5) == 0) { + if (ip_v4 == 1 || ip_both == 1) + printf("%s\n", *(ap) + 5); + continue; + } + if (strncasecmp("+ip6:", *ap, 5) == 0) { + if (ip_v6 == 1 || ip_both == 1) + printf("%s\n", *(ap) + 5); + continue; + } + if (strncasecmp("include:", *ap, 8) == 0) { + lookup_record(T_TXT, *(ap) + 8, dispatch_txt); + continue; + } + if (strncasecmp("redirect=", *ap, 9) == 0) { + lookup_record(T_TXT, *(ap) + 9, dispatch_txt); + continue; + } + if (strcasecmp(*ap, "mx") == 0 || strcasecmp(*ap, "+mx") == 0) { + print_dname(rr->rr_dname, buf2, sizeof(buf2)); + buf2[strlen(buf2) - 1] = '\0'; + lookup_record(T_MX, buf2, dispatch_mx); + continue; + } + if (strcasecmp(*ap, "a") == 0 || strcasecmp(*ap, "+a") == 0) { + print_dname(rr->rr_dname, buf2, sizeof(buf2)); + buf2[strlen(buf2) - 1] = '\0'; + lookup_record(T_A, buf2, dispatch_a); + lookup_record(T_AAAA, buf2, dispatch_aaaa); + continue; + } + } + *ap = NULL; +} + +void +dispatch_mx(struct dns_rr *rr) +{ + char buf[512]; + + print_dname(rr->rr.mx.exchange, buf, sizeof(buf)); + buf[strlen(buf) - 1] = '\0'; + if (buf[strlen(buf) - 1] == '.') + buf[strlen(buf) - 1] = '\0'; + lookup_record(T_A, buf, dispatch_a); + lookup_record(T_AAAA, buf, dispatch_aaaa); +} + +void +dispatch_a(struct dns_rr *rr) +{ + char buffer[512]; + const char *ptr; + + if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr, + buffer, sizeof buffer))) + printf("%s\n", ptr); +} + +void +dispatch_aaaa(struct dns_rr *rr) +{ + char buffer[512]; + const char *ptr; + + if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6, + buffer, sizeof buffer))) + printf("%s\n", ptr); +} -- 2.20.1