From: Alan T. DeKok Date: Sat, 28 Feb 2009 08:30:28 +0000 (+0100) Subject: Allow injection of packets via radmin X-Git-Tag: release_2_1_4~36 X-Git-Url: http://www.project-moonshot.org/gitweb/?a=commitdiff_plain;h=73783247ae9beac884fe6a41399c5b58cb3bd717;p=freeradius.git Allow injection of packets via radmin inject to auth 127.0.0.1 1812 inject from 127.0.0.1 inject file input output Allows you to test policies by injecting packets as if they came from a particular client. This should ONLY be used in debugging mode. --- diff --git a/src/main/command.c b/src/main/command.c index aeda28a..7637015 100644 --- a/src/main/command.c +++ b/src/main/command.c @@ -73,6 +73,20 @@ typedef struct fr_command_socket_t { char *gid_name; char *mode_name; char user[256]; + + /* + * The next few entries handle fake packets injected by + * the control socket. + */ + fr_ipaddr_t src_ipaddr; /* src_port is always 0 */ + fr_ipaddr_t dst_ipaddr; + int dst_port; + rad_listen_t *inject_listener; + RADCLIENT *inject_client; + + /* + * The next few entries do buffer management. + */ ssize_t offset; ssize_t next; char buffer[COMMAND_BUFFER_SIZE]; @@ -952,6 +966,285 @@ static int command_show_home_server_state(rad_listen_t *listener, int argc, char } #endif +/* + * For encode/decode stuff + */ +static int null_socket_dencode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request) +{ + return 0; +} + +static int null_socket_send(UNUSED rad_listen_t *listener, REQUEST *request) +{ + char *output_file; + FILE *fp; + VALUE_PAIR *vp; + + output_file = request_data_reference(request, null_socket_send, 0); + if (!output_file) { + radlog(L_ERR, "WARNING: No output file for injected packet %d", + request->number); + return 0; + } + + fp = fopen(output_file, "w"); + if (!fp) { + radlog(L_ERR, "Failed to send injected file to %s: %s", + output_file, strerror(errno)); + return 0; + } + + if (request->reply->code != 0) { + const char *what = "reply"; + char buffer[1024]; + + if (request->reply->code < FR_MAX_PACKET_CODE) { + what = fr_packet_codes[request->reply->code]; + } + + fprintf(fp, "%s\n", what); + + if (debug_flag) { + request->radlog(L_DBG, 0, request, + "Injected %s packet to host %s port 0 code=%d, id=%d", + what, + inet_ntop(request->reply->src_ipaddr.af, + &request->reply->src_ipaddr.ipaddr, + buffer, sizeof(buffer)), + request->reply->code, request->reply->id); + } + + for (vp = request->reply->vps; vp != NULL; vp = vp->next) { + vp_prints(buffer, sizeof(buffer), vp); + fprintf(fp, "%s\n", buffer); + if (debug_flag) { + request->radlog(L_DBG, 0, request, "\t%s", + buffer); + } + } + } + fclose(fp); + + return 0; +} + +static int command_inject_to(rad_listen_t *listener, int argc, char *argv[]) +{ + int port; + RAD_LISTEN_TYPE type; + fr_command_socket_t *sock = listener->data; + fr_ipaddr_t ipaddr; + rad_listen_t *found = NULL; + + if (argc < 1) { + cprintf(listener, "ERROR: Must specify [auth/acct]\n"); + return 0; + } + + if (strcmp(argv[0], "auth") == 0) { + type = RAD_LISTEN_AUTH; + + } else if (strcmp(argv[0], "acct") == 0) { +#ifdef WITH_ACCOUNTING + type = RAD_LISTEN_ACCT; +#else + cprintf(listener, "ERROR: This server was built without accounting support.\n"); + return 0; +#endif + + } else { + cprintf(listener, "ERROR: Unknown socket type\n"); + return 0; + } + + if (argc < 3) { + cprintf(listener, "ERROR: No was given\n"); + return 0; + } + + /* + * FIXME: Look for optional arg 4, and bind interface. + */ + + if (ip_hton(argv[1], AF_UNSPEC, &ipaddr) < 0) { + cprintf(listener, "ERROR: Failed parsing IP address; %s\n", + fr_strerror()); + return 0; + } + port = atoi(argv[2]); + + found = listener_find_byipaddr(&ipaddr, port); + if (!found) { + cprintf(listener, "ERROR: Could not find matching listener\n"); + return 0; + } + + sock->inject_listener = found; + sock->dst_ipaddr = ipaddr; + sock->dst_port = port; + + return 1; +} + +static int command_inject_from(rad_listen_t *listener, int argc, char *argv[]) +{ + RADCLIENT *client; + fr_command_socket_t *sock = listener->data; + + if (argc < 1) { + cprintf(listener, "ERROR: No was given\n"); + return 0; + } + + if (!sock->inject_listener) { + cprintf(listener, "ERROR: You must specify \"inject to\" before using \"inject from\"\n"); + return 0; + } + + sock->src_ipaddr.af = AF_UNSPEC; + if (ip_hton(argv[0], AF_UNSPEC, &sock->src_ipaddr) < 0) { + cprintf(listener, "ERROR: Failed parsing IP address; %s\n", + fr_strerror()); + return 0; + } + + client = client_listener_find(sock->inject_listener, &sock->src_ipaddr, + 0); + if (!client) { + cprintf(listener, "ERROR: No such client %s\n", argv[0]); + return 0; + } + sock->inject_client = client; + + return 1; +} + +static int command_inject_file(rad_listen_t *listener, int argc, char *argv[]) +{ + static int inject_id = 0; + int filedone; + fr_command_socket_t *sock = listener->data; + rad_listen_t *fake; + REQUEST *request = NULL; + RADIUS_PACKET *packet; + VALUE_PAIR *vp; + FILE *fp; + RAD_REQUEST_FUNP fun = NULL; + char buffer[2048]; + + if (argc < 2) { + cprintf(listener, "ERROR: You must specify \n"); + return 0; + } + + /* + * Output files always go to the logging directory. + */ + snprintf(buffer, sizeof(buffer), "%s/%s", radlog_dir, argv[1]); + + fp = fopen(argv[0], "r"); + if (!fp ) { + cprintf(listener, "ERROR: Failed opening %s: %s\n", + argv[0], strerror(errno)); + return 0; + } + + vp = readvp2(fp, &filedone, ""); + fclose(fp); + if (!vp) { + cprintf(listener, "ERROR: Failed reading attributes from %s: %s\n", + argv[0], fr_strerror()); + return 0; + } + + fake = rad_malloc(sizeof(*fake)); + memcpy(fake, sock->inject_listener, sizeof(*fake)); + + /* + * Re-write the IO for the listener. + */ + fake->encode = null_socket_dencode; + fake->decode = null_socket_dencode; + fake->send = null_socket_send; + + packet = rad_alloc(0); + packet->src_ipaddr = sock->src_ipaddr; + packet->src_port = 0; + + packet->dst_ipaddr = sock->dst_ipaddr; + packet->dst_port = sock->dst_port; + packet->vps = vp; + packet->id = inject_id++; + + if (fake->type == RAD_LISTEN_AUTH) { + packet->code = PW_AUTHENTICATION_REQUEST; + fun = rad_authenticate; + + } else { +#ifdef WITH_ACCOUNTING + packet->code = PW_ACCOUNTING_REQUEST; + fun = rad_accounting; +#else + cprintf(listener, "ERROR: This server was built without accounting support.\n"); + rad_free(&packet); + free(fake); + return 0; +#endif + } + + if (!received_request(fake, packet, &request, sock->inject_client)) { + cprintf(listener, "ERROR: Failed to inject request. See log file for details\n"); + rad_free(&packet); + free(fake); + return 0; + } + + /* + * Remember what the output file is, and remember to + * delete the fake listener when done. + */ + request_data_add(request, null_socket_send, 0, strdup(buffer), free); + request_data_add(request, null_socket_send, 1, fake, free); + + if (debug_flag) { + request->radlog(L_DBG, 0, request, + "Injected %s packet from host %s port 0 code=%d, id=%d", + fr_packet_codes[packet->code], + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + buffer, sizeof(buffer)), + packet->code, packet->id); + + for (vp = packet->vps; vp != NULL; vp = vp->next) { + vp_prints(buffer, sizeof(buffer), vp); + request->radlog(L_DBG, 0, request, "\t%s", buffer); + } + } + + /* + * And go process it. + */ + thread_pool_addrequest(request, fun); + + return 1; +} + + +static fr_command_table_t command_table_inject[] = { + { "to", FR_WRITE, + "inject to - Inject packets to the destination IP and port.", + command_inject_to, NULL }, + + { "from", FR_WRITE, + "inject from - Inject packets as if they came from ", + command_inject_from, NULL }, + + { "file", FR_WRITE, + "inject file - Inject packet from input-file>, with results sent to ", + command_inject_file, NULL }, + + { NULL, 0, NULL, NULL, NULL } +}; static fr_command_table_t command_table_debug[] = { { "condition", FR_WRITE, @@ -1364,6 +1657,9 @@ static fr_command_table_t command_table[] = { { "hup", FR_WRITE, "hup [module] - sends a HUP signal to the server, or optionally to one module", command_hup, NULL }, + { "inject", FR_WRITE, + "inject - commands to inject packets into a running server", + NULL, command_table_inject }, { "reconnect", FR_READ, "reconnect - reconnect to a running server", NULL, NULL }, /* just here for "help" */ diff --git a/src/main/listen.c b/src/main/listen.c index e08ad87..835c526 100644 --- a/src/main/listen.c +++ b/src/main/listen.c @@ -146,7 +146,7 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener, /* * It's a dynamically generated client, check it. */ - if (client->dynamic) { + if (client->dynamic && (src_port != 0)) { /* * Lives forever. Return it. */ @@ -1902,6 +1902,7 @@ RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr, return NULL; } +#endif rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port) { @@ -1910,6 +1911,10 @@ rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port) for (this = mainconfig.listen; this != NULL; this = this->next) { listen_socket_t *sock; + /* + * FIXME: For TCP, ignore the *secondary* + * listeners associated with the main socket. + */ if ((this->type != RAD_LISTEN_AUTH) && (this->type != RAD_LISTEN_ACCT)) continue; @@ -1919,8 +1924,21 @@ rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port) (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) { return this; } + + if ((sock->port == port) && + ((sock->ipaddr.af == AF_INET) && + (sock->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) { + return this; + } + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 + if ((sock->port == port) && + (sock->ipaddr.af == AF_INET6) && + (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr))) { + return this; + } +#endif } return NULL; } -#endif