diff options
Diffstat (limited to 'usr.sbin/radiusctl')
-rw-r--r-- | usr.sbin/radiusctl/Makefile | 6 | ||||
-rw-r--r-- | usr.sbin/radiusctl/parser.c | 98 | ||||
-rw-r--r-- | usr.sbin/radiusctl/parser.h | 21 | ||||
-rw-r--r-- | usr.sbin/radiusctl/radiusctl.8 | 17 | ||||
-rw-r--r-- | usr.sbin/radiusctl/radiusctl.c | 141 |
5 files changed, 253 insertions, 30 deletions
diff --git a/usr.sbin/radiusctl/Makefile b/usr.sbin/radiusctl/Makefile index 73704e55083..48cec71affc 100644 --- a/usr.sbin/radiusctl/Makefile +++ b/usr.sbin/radiusctl/Makefile @@ -1,9 +1,9 @@ -# $OpenBSD: Makefile,v 1.2 2015/08/03 04:10:21 yasuoka Exp $ +# $OpenBSD: Makefile,v 1.3 2020/02/24 07:07:11 dlg Exp $ PROG= radiusctl SRCS= radiusctl.c parser.c chap_ms.c MAN= radiusctl.8 CFLAGS+= -Wall -Wextra -Wno-unused-parameter -LDADD+= -lradius -lcrypto -DPADD+= ${LIBRADIUS} ${LIBCRYPTO} +LDADD+= -lradius -lcrypto -levent +DPADD+= ${LIBRADIUS} ${LIBCRYPTO} ${LIBEVENT} .include <bsd.prog.mk> diff --git a/usr.sbin/radiusctl/parser.c b/usr.sbin/radiusctl/parser.c index b666206f616..3b97790c530 100644 --- a/usr.sbin/radiusctl/parser.c +++ b/usr.sbin/radiusctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.1 2015/07/21 04:06:04 yasuoka Exp $ */ +/* $OpenBSD: parser.c,v 1.2 2020/02/24 07:07:11 dlg Exp $ */ /* * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net> @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sys/time.h> + #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -35,6 +37,9 @@ enum token_type { PORT, METHOD, NAS_PORT, + TRIES, + INTERVAL, + MAXWAIT, ENDTOKEN }; @@ -45,7 +50,11 @@ struct token { const struct token *next; }; -static struct parse_result res; +static struct parse_result res = { + .tries = TEST_TRIES_DEFAULT, + .interval = { TEST_INTERVAL_DEFAULT, 0 }, + .maxwait = { TEST_MAXWAIT_DEFAULT, 0 }, +}; static const struct token t_test[]; static const struct token t_secret[]; @@ -55,6 +64,9 @@ static const struct token t_password[]; static const struct token t_port[]; static const struct token t_method[]; static const struct token t_nas_port[]; +static const struct token t_tries[]; +static const struct token t_interval[]; +static const struct token t_maxwait[]; static const struct token t_main[] = { { KEYWORD, "test", TEST, t_test }, @@ -82,6 +94,9 @@ static const struct token t_test_opts[] = { { KEYWORD, "port", NONE, t_port }, { KEYWORD, "method", NONE, t_method }, { KEYWORD, "nas-port", NONE, t_nas_port }, + { KEYWORD, "interval", NONE, t_interval }, + { KEYWORD, "tries", NONE, t_tries }, + { KEYWORD, "maxwait", NONE, t_maxwait }, { ENDTOKEN, "", NONE, NULL } }; @@ -105,6 +120,21 @@ static const struct token t_nas_port[] = { { ENDTOKEN, "", NONE, NULL } }; +static const struct token t_tries[] = { + { TRIES, "", NONE, t_test_opts }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_interval[] = { + { INTERVAL, "", NONE, t_test_opts }, + { ENDTOKEN, "", NONE, NULL } +}; + +static const struct token t_maxwait[] = { + { MAXWAIT, "", NONE, t_test_opts }, + { ENDTOKEN, "", NONE, NULL } +}; + static const struct token *match_token(char *, const struct token []); static void show_valid_args(const struct token []); @@ -115,8 +145,6 @@ parse(int argc, char *argv[]) const struct token *table = t_main; const struct token *match; - bzero(&res, sizeof(res)); - while (argc >= 0) { if ((match = match_token(argv[0], table)) == NULL) { fprintf(stderr, "valid commands/args:\n"); @@ -138,6 +166,12 @@ parse(int argc, char *argv[]) return (NULL); } + if (res.tries * res.interval.tv_sec > res.maxwait.tv_sec) { + fprintf(stderr, "tries %u by interval %lld > maxwait %lld", + res.tries, res.interval.tv_sec, res.maxwait.tv_sec); + return (NULL); + } + return (&res); } @@ -240,6 +274,50 @@ match_token(char *word, const struct token table[]) res.nas_port = num; t = &table[i]; break; + + case TRIES: + if (word == NULL) + break; + num = strtonum(word, + TEST_TRIES_MIN, TEST_TRIES_MAX, &errstr); + if (errstr != NULL) { + printf("invalid argument: %s is %s" + " for \"tries\"\n", word, errstr); + return (NULL); + } + match++; + res.tries = num; + t = &table[i]; + break; + case INTERVAL: + if (word == NULL) + break; + num = strtonum(word, + TEST_INTERVAL_MIN, TEST_INTERVAL_MAX, &errstr); + if (errstr != NULL) { + printf("invalid argument: %s is %s" + " for \"interval\"\n", word, errstr); + return (NULL); + } + match++; + res.interval.tv_sec = num; + t = &table[i]; + break; + case MAXWAIT: + if (word == NULL) + break; + num = strtonum(word, + TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX, &errstr); + if (errstr != NULL) { + printf("invalid argument: %s is %s" + " for \"maxwait\"\n", word, errstr); + return (NULL); + } + match++; + res.maxwait.tv_sec = num; + t = &table[i]; + break; + case ENDTOKEN: break; } @@ -293,6 +371,18 @@ show_valid_args(const struct token table[]) case NAS_PORT: fprintf(stderr, " <nas-port (0-65535)>\n"); break; + case TRIES: + fprintf(stderr, " <tries (%u-%u)>\n", + TEST_TRIES_MIN, TEST_TRIES_MAX); + break; + case INTERVAL: + fprintf(stderr, " <interval (%u-%u)>\n", + TEST_INTERVAL_MIN, TEST_INTERVAL_MAX); + break; + case MAXWAIT: + fprintf(stderr, " <maxwait (%u-%u)>\n", + TEST_MAXWAIT_MIN, TEST_MAXWAIT_MAX); + break; case ENDTOKEN: break; } diff --git a/usr.sbin/radiusctl/parser.h b/usr.sbin/radiusctl/parser.h index b84b4b4d29d..6fd0a6c30f7 100644 --- a/usr.sbin/radiusctl/parser.h +++ b/usr.sbin/radiusctl/parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.h,v 1.1 2015/07/21 04:06:04 yasuoka Exp $ */ +/* $OpenBSD: parser.h,v 1.2 2020/02/24 07:07:11 dlg Exp $ */ /* This file is derived from OpenBSD:src/usr.sbin/ikectl/parser.h 1.9 */ /* @@ -31,6 +31,18 @@ enum auth_method { MSCHAPV2 }; +#define TEST_TRIES_MIN 1 +#define TEST_TRIES_MAX 32 +#define TEST_TRIES_DEFAULT 3 + +#define TEST_INTERVAL_MIN 1 +#define TEST_INTERVAL_MAX 10 +#define TEST_INTERVAL_DEFAULT 2 + +#define TEST_MAXWAIT_MIN 3 +#define TEST_MAXWAIT_MAX 60 +#define TEST_MAXWAIT_DEFAULT 8 + struct parse_result { enum actions action; const char *hostname; @@ -40,6 +52,13 @@ struct parse_result { u_short port; int nas_port; enum auth_method auth_method; + + /* number of packets to try sending */ + unsigned int tries; + /* how long between packet sends */ + struct timeval interval; + /* overall process wait time for a reply */ + struct timeval maxwait; }; struct parse_result *parse(int, char *[]); diff --git a/usr.sbin/radiusctl/radiusctl.8 b/usr.sbin/radiusctl/radiusctl.8 index 02595580692..6fb1dc8b7d6 100644 --- a/usr.sbin/radiusctl/radiusctl.8 +++ b/usr.sbin/radiusctl/radiusctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: radiusctl.8,v 1.3 2015/08/25 01:21:57 yasuoka Exp $ +.\" $OpenBSD: radiusctl.8,v 1.4 2020/02/24 07:07:11 dlg Exp $ .\" .\" Copyright (c) YASUOKA Masahiko <yasuoka@yasuoka.net> .\" @@ -15,7 +15,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" -.Dd $Mdocdate: August 25 2015 $ +.Dd $Mdocdate: February 24 2020 $ .Dt RADIUSCTL 8 .Os .Sh NAME @@ -77,6 +77,19 @@ when sending a packet to .Ar hostname . If the port is omitted, the default port number 1812 is used. +.It Cm tries Ar number +Specifies the number of packets to try sending. +By default +.Nm +will send 3 packets before giving up. +.It Cm interval Ar seconds +Specifies how many seconds to wait before resending a packet. +The default interval between packet retries is 2 seconds. +.It Cm maxwait Ar seconds +Specifies the maximum amount of time to wait for a valid reply packet. +By default +.Nm +will wait 8 seconds for a valid reply. .El .El .Sh SEE ALSO diff --git a/usr.sbin/radiusctl/radiusctl.c b/usr.sbin/radiusctl/radiusctl.c index 6b1d03e1637..c374884eb01 100644 --- a/usr.sbin/radiusctl/radiusctl.c +++ b/usr.sbin/radiusctl/radiusctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: radiusctl.c,v 1.7 2019/04/01 09:51:56 yasuoka Exp $ */ +/* $OpenBSD: radiusctl.c,v 1.8 2020/02/24 07:07:11 dlg Exp $ */ /* * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> * @@ -19,6 +19,7 @@ #include <netinet/in.h> #include <arpa/inet.h> +#include <errno.h> #include <err.h> #include <md5.h> #include <netdb.h> @@ -30,11 +31,13 @@ #include <radius.h> +#include <event.h> + #include "parser.h" #include "chap_ms.h" -static void radius_test (struct parse_result *); +static int radius_test (struct parse_result *); static void radius_dump (FILE *, RADIUS_PACKET *, bool, const char *); static const char *radius_code_str (int code); @@ -53,6 +56,7 @@ main(int argc, char *argv[]) { int ch; struct parse_result *result; + int ecode = EXIT_SUCCESS; while ((ch = getopt(argc, argv, "")) != -1) switch (ch) { @@ -72,21 +76,38 @@ main(int argc, char *argv[]) case TEST: if (pledge("stdio dns inet", NULL) == -1) err(EXIT_FAILURE, "pledge"); - radius_test(result); + ecode = radius_test(result); break; } - return (EXIT_SUCCESS); + return (ecode); } -static void +struct radius_test { + const struct parse_result *res; + int ecode; + + RADIUS_PACKET *reqpkt; + int sock; + unsigned int tries; + struct event ev_send; + struct event ev_recv; + struct event ev_timedout; +}; + +static void radius_test_send(int, short, void *); +static void radius_test_recv(int, short, void *); +static void radius_test_timedout(int, short, void *); + +static int radius_test(struct parse_result *res) { + struct radius_test test = { .res = res }; + RADIUS_PACKET *reqpkt; struct addrinfo hints, *ai; int sock, retval; struct sockaddr_storage sockaddr; socklen_t sockaddrlen; - RADIUS_PACKET *reqpkt, *respkt; struct sockaddr_in *sin4; struct sockaddr_in6 *sin6; uint32_t u32val; @@ -110,7 +131,8 @@ radius_test(struct parse_result *res) ((struct sockaddr_in *)ai->ai_addr)->sin_port = htons(res->port); - sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + sock = socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, + ai->ai_protocol); if (sock == -1) err(1, "socket"); @@ -211,24 +233,31 @@ radius_test(struct parse_result *res) radius_put_message_authenticator(reqpkt, res->secret); + event_init(); + + test.ecode = EXIT_FAILURE; + test.res = res; + test.sock = sock; + test.reqpkt = reqpkt; + + event_set(&test.ev_recv, sock, EV_READ|EV_PERSIST, + radius_test_recv, &test); + + evtimer_set(&test.ev_send, radius_test_send, &test); + evtimer_set(&test.ev_timedout, radius_test_timedout, &test); + + event_add(&test.ev_recv, NULL); + evtimer_add(&test.ev_timedout, &res->maxwait); + /* Send! */ fprintf(stderr, "Sending:\n"); radius_dump(stdout, reqpkt, false, res->secret); - if (send(sock, radius_get_data(reqpkt), radius_get_length(reqpkt), 0) - == -1) - warn("send"); - if ((respkt = radius_recv(sock, 0)) == NULL) - warn("recv"); - else { - radius_set_request_packet(respkt, reqpkt); - fprintf(stderr, "\nReceived:\n"); - radius_dump(stdout, respkt, true, res->secret); - } + radius_test_send(0, EV_TIMEOUT, &test); + + event_dispatch(); /* Release the resources */ radius_delete_packet(reqpkt); - if (respkt) - radius_delete_packet(respkt); close(sock); freeaddrinfo(ai); @@ -236,7 +265,79 @@ radius_test(struct parse_result *res) if (res->password) explicit_bzero((char *)res->password, strlen(res->password)); - return; + return (test.ecode); +} + +static void +radius_test_send(int thing, short revents, void *arg) +{ + struct radius_test *test = arg; + RADIUS_PACKET *reqpkt = test->reqpkt; + ssize_t rv; + +retry: + rv = send(test->sock, + radius_get_data(reqpkt), radius_get_length(reqpkt), 0); + if (rv == -1) { + switch (errno) { + case EINTR: + case EAGAIN: + goto retry; + default: + break; + } + + warn("send"); + } + + if (++test->tries >= test->res->tries) + return; + + evtimer_add(&test->ev_send, &test->res->interval); +} + +static void +radius_test_recv(int sock, short revents, void *arg) +{ + struct radius_test *test = arg; + RADIUS_PACKET *respkt; + RADIUS_PACKET *reqpkt = test->reqpkt; + +retry: + respkt = radius_recv(sock, 0); + if (respkt == NULL) { + switch (errno) { + case EINTR: + case EAGAIN: + goto retry; + default: + break; + } + + warn("recv"); + return; + } + + radius_set_request_packet(respkt, reqpkt); + if (radius_get_id(respkt) == radius_get_id(reqpkt)) { + fprintf(stderr, "\nReceived:\n"); + radius_dump(stdout, respkt, true, test->res->secret); + + event_del(&test->ev_recv); + evtimer_del(&test->ev_send); + evtimer_del(&test->ev_timedout); + test->ecode = EXIT_SUCCESS; + } + + radius_delete_packet(respkt); +} + +static void +radius_test_timedout(int thing, short revents, void *arg) +{ + struct radius_test *test = arg; + + event_del(&test->ev_recv); } static void |