/* * Receive one packet, maybe. */ static int recv_one_packet(int wait_time) { fd_set set; struct timeval tv; rc_request_t *request; RADIUS_PACKET *reply, **packet_p; volatile int max_fd; /* And wait for reply, timing out as necessary */ FD_ZERO(&set); max_fd = fr_packet_list_fd_set(pl, &set); if (max_fd < 0) exit(1); /* no sockets to listen on! */ tv.tv_sec = (wait_time <= 0) ? 0 : wait_time; tv.tv_usec = 0; /* * No packet was received. */ if (select(max_fd, &set, NULL, NULL, &tv) <= 0) return 0; /* * Look for the packet. */ reply = fr_packet_list_recv(pl, &set); if (!reply) { ERROR("Received bad packet"); #ifdef WITH_TCP /* * If the packet is bad, we close the socket. * I'm not sure how to do that now, so we just * die... */ if (proto) exit(1); #endif return -1; /* bad packet */ } /* * We don't use udpfromto. So if we bind to "*", we want * to find replies sent to 192.0.2.4. Therefore, we * force all replies to have the one address we know * about, no matter what real address they were sent to. * * This only works if were not using any of the * Packet-* attributes, or running with 'auto'. */ reply->dst_ipaddr = client_ipaddr; reply->dst_port = client_port; #ifdef WITH_TCP /* * TCP sockets don't use recvmsg(), and thus don't get * the source IP/port. However, since they're TCP, we * know what the source IP/port is, because that's where * we connected to. */ if (ipproto == IPPROTO_TCP) { reply->src_ipaddr = server_ipaddr; reply->src_port = server_port; } #endif packet_p = fr_packet_list_find_byreply(pl, reply); if (!packet_p) { ERROR("Received reply to request we did not send. (id=%d socket %d)", reply->id, reply->sockfd); fr_radius_free(&reply); return -1; /* got reply to packet we didn't send */ } request = fr_packet2myptr(rc_request_t, packet, packet_p); /* * Fails the signature validation: not a real reply. * FIXME: Silently drop it and listen for another packet. */ if (fr_radius_verify(reply, request->packet, secret) < 0) { REDEBUG("Reply verification failed"); stats.lost++; goto packet_done; /* shared secret is incorrect */ } if (print_filename) { RDEBUG("%s response code %d", request->files->packets, reply->code); } deallocate_id(request); request->reply = reply; reply = NULL; /* * If this fails, we're out of memory. */ if (fr_radius_decode(request->reply, request->packet, secret) != 0) { REDEBUG("Reply decode failed"); stats.lost++; goto packet_done; } fr_packet_header_print(fr_log_fp, request->reply, true); if (fr_debug_lvl > 0) fr_pair_list_fprint(fr_log_fp, request->reply->vps); /* * Increment counters... */ switch (request->reply->code) { case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCOUNTING_RESPONSE: case PW_CODE_COA_ACK: case PW_CODE_DISCONNECT_ACK: stats.accepted++; break; case PW_CODE_ACCESS_CHALLENGE: break; default: stats.rejected++; } /* * If we had an expected response code, check to see if the * packet matched that. */ if ((request->filter_code != PW_CODE_UNDEFINED) && (request->reply->code != request->filter_code)) { if (is_radius_code(request->reply->code)) { REDEBUG("%s: Expected %s got %s", request->name, fr_packet_codes[request->filter_code], fr_packet_codes[request->reply->code]); } else { REDEBUG("%s: Expected %u got %i", request->name, request->filter_code, request->reply->code); } stats.failed++; /* * Check if the contents of the packet matched the filter */ } else if (!request->filter) { stats.passed++; } else { VALUE_PAIR const *failed[2]; fr_pair_list_sort(&request->reply->vps, fr_pair_cmp_by_da_tag); if (fr_pair_validate(failed, request->filter, request->reply->vps)) { RDEBUG("%s: Response passed filter", request->name); stats.passed++; } else { fr_pair_validate_debug(request, failed); REDEBUG("%s: Response for failed filter", request->name); stats.failed++; } } if (request->resend == resend_count) { request->done = true; } packet_done: fr_radius_free(&request->reply); fr_radius_free(&reply); /* may be NULL */ return 0; }
static int radsnmp_send_recv(radsnmp_conf_t *conf, int fd) { fr_strerror(); #define NEXT_LINE(_line, _buffer) \ { \ size_t _len; \ if (stop) return 0; \ errno = 0;\ _line = fgets(_buffer, sizeof(_buffer), stdin); \ if (_line) { \ _len = strlen(_line); \ if ((_len > 0) && (_line[_len - 1] == '\n')) _line[_len - 1] = '\0'; \ DEBUG2("read: %s", _line); \ } \ } /* * Read commands from pass_persist */ while (!stop) { radsnmp_command_t command; char buffer[256]; char *line; ssize_t slen; fr_cursor_t cursor; VALUE_PAIR *vp; RADIUS_PACKET *request; /* * Alloc a new request so we can start adding * new pairs to it. */ request = radsnmp_alloc(conf, fd); if (!request) { ERROR("Failed allocating request"); return EXIT_FAILURE; } fr_cursor_init(&cursor, &request->vps); NEXT_LINE(line, buffer); /* * Determine the type of SNMP operation */ command = fr_str2int(radsnmp_command_str, line, RADSNMP_UNKNOWN); switch (command) { case RADSNMP_EXIT: DEBUG("Empty command, exiting"); return 0; case RADSNMP_PING: RESPOND_STATIC("PONG"); continue; case RADSNMP_SET: { char value_buff[254]; /* RADIUS attribute length + 1 */ char *value; char type_str[64]; char *p; fr_dict_enum_t *type; NEXT_LINE(line, buffer); /* Should be the OID */ NEXT_LINE(value, value_buff); /* Should be the value */ p = strchr(value, ' '); if (!p) { ERROR("No SNMP type specified (or type/value string was malformed)"); RESPOND_STATIC("NONE"); continue; } if ((size_t)(p - value) >= sizeof(type_str)) { ERROR("SNMP Type string too long"); RESPOND_STATIC("NONE"); continue; } strlcpy(type_str, value, (p - value) + 1); type = fr_dict_enum_by_alias(attr_freeradius_snmp_type, type_str, -1); if (!type) { ERROR("Unknown type \"%s\"", type_str); RESPOND_STATIC("NONE"); continue; } slen = radsnmp_pair_from_oid(conf, conf, &cursor, line, type->value->vb_uint32, p + 1); } break; case RADSNMP_GET: case RADSNMP_GETNEXT: NEXT_LINE(line, buffer); /* Should be the OID */ slen = radsnmp_pair_from_oid(conf, conf, &cursor, line, 0, NULL); break; default: ERROR("Unknown command \"%s\"", line); RESPOND_STATIC("NONE"); talloc_free(request); continue; } /* * Deal with any errors from the GET/GETNEXT/SET command */ if (slen <= 0) { char *spaces, *text; fr_canonicalize_error(conf, &spaces, &text, slen, line); ERROR("Failed evaluating OID:"); ERROR("%s", text); ERROR("%s^ %s", spaces, fr_strerror()); talloc_free(spaces); talloc_free(text); talloc_free(request); RESPOND_STATIC("NONE"); continue; } /* * Now add an attribute indicating what the * SNMP operation was */ vp = fr_pair_afrom_da(request, attr_freeradius_snmp_operation); if (!vp) { ERROR("Failed allocating SNMP operation attribute"); return EXIT_FAILURE; } vp->vp_uint32 = (unsigned int)command; /* Commands must match dictionary */ fr_cursor_append(&cursor, vp); /* * Add message authenticator or the stats * request will be rejected. */ MEM(vp = fr_pair_afrom_da(request, attr_message_authenticator)); fr_pair_value_memcpy(vp, (uint8_t const *)"\0", 1, true); fr_cursor_append(&cursor, vp); /* * Send the packet */ { RADIUS_PACKET *reply = NULL; ssize_t rcode; fd_set set; unsigned int ret; unsigned int i; if (fr_radius_packet_encode(request, NULL, conf->secret) < 0) { ERROR("Failed encoding request: %s", fr_strerror()); return EXIT_FAILURE; } if (fr_radius_packet_sign(request, NULL, conf->secret) < 0) { ERROR("Failed signing request: %s", fr_strerror()); return EXIT_FAILURE; } /* * Print the attributes we're about to send */ if (fr_log_fp) fr_packet_header_print(fr_log_fp, request, false); if (fr_debug_lvl > 0) fr_pair_list_fprint(fr_log_fp, request->vps); #ifndef NDEBUG if (fr_log_fp && (fr_debug_lvl > 3)) fr_radius_packet_print_hex(request); #endif FD_ZERO(&set); /* clear the set */ FD_SET(fd, &set); /* * Any connection issues cause us to exit, so * the connection can be re-initialised on the * next call. */ for (i = 0; i < conf->retries; i++) { rcode = write(request->sockfd, request->data, request->data_len); if (rcode < 0) { ERROR("Failed sending: %s", fr_syserror(errno)); return EXIT_FAILURE; } rcode = select(fd + 1, &set, NULL, NULL, &conf->timeout); switch (rcode) { case -1: ERROR("Select failed: %s", fr_syserror(errno)); return EXIT_FAILURE; case 0: DEBUG("Response timeout. Retrying %d/%u...", i + 1, conf->retries); continue; /* Timeout */ case 1: reply = fr_radius_packet_recv(request, request->sockfd, UDP_FLAGS_NONE, RADIUS_MAX_ATTRIBUTES, false); if (!reply) { ERROR("Failed receiving reply: %s", fr_strerror()); recv_error: RESPOND_STATIC("NONE"); talloc_free(request); continue; } if (fr_radius_packet_decode(reply, request, RADIUS_MAX_ATTRIBUTES, false, conf->secret) < 0) { ERROR("Failed decoding reply: %s", fr_strerror()); goto recv_error; } break; default: DEBUG("Invalid select() return value %zd", rcode); return EXIT_FAILURE; } break; } if (!reply) { ERROR("Server didn't respond"); return EXIT_FAILURE; } /* * Print the attributes we received in response */ if (fr_log_fp) fr_packet_header_print(fr_log_fp, reply, true); if (fr_debug_lvl > 0) fr_pair_list_fprint(fr_log_fp, reply->vps); #ifndef NDEBUG if (fr_log_fp && (fr_debug_lvl > 3)) fr_radius_packet_print_hex(reply); #endif switch (command) { case RADSNMP_GET: case RADSNMP_GETNEXT: ret = radsnmp_get_response(STDOUT_FILENO, conf->snmp_oid_root, attr_freeradius_snmp_type, reply->vps); switch (ret) { case -1: ERROR("Failed converting pairs to varbind response: %s", fr_strerror()); return EXIT_FAILURE; case 0: DEBUG("Empty response"); break; default: DEBUG("Returned %u varbind responses", ret); break; } break; case RADSNMP_SET: if (radsnmp_set_response(STDOUT_FILENO, attr_freeradius_snmp_failure, reply->vps) < 0) { ERROR("Failed writing SET response: %s", fr_strerror()); return EXIT_FAILURE; } break; default: assert(0); return EXIT_FAILURE; } talloc_free(request); } } return EXIT_SUCCESS; }
/* * Send one packet. */ static int send_one_packet(rc_request_t *request) { assert(request->done == false); /* * Remember when we have to wake up, to re-send the * request, of we didn't receive a reply. */ if ((sleep_time == -1) || (sleep_time > (int) timeout)) sleep_time = (int) timeout; /* * Haven't sent the packet yet. Initialize it. */ if (request->packet->id == -1) { int i; bool rcode; assert(request->reply == NULL); /* * Didn't find a free packet ID, we're not done, * we don't sleep, and we stop trying to process * this packet. */ retry: request->packet->src_ipaddr.af = server_ipaddr.af; rcode = fr_packet_list_id_alloc(pl, ipproto, &request->packet, NULL); if (!rcode) { int mysockfd; #ifdef WITH_TCP if (proto) { mysockfd = fr_socket_client_tcp(NULL, &request->packet->dst_ipaddr, request->packet->dst_port, false); } else #endif mysockfd = fr_socket(&client_ipaddr, 0); if (mysockfd < 0) { ERROR("Failed opening socket"); exit(1); } if (!fr_packet_list_socket_add(pl, mysockfd, ipproto, &request->packet->dst_ipaddr, request->packet->dst_port, NULL)) { ERROR("Can't add new socket"); exit(1); } goto retry; } assert(request->packet->id != -1); assert(request->packet->data == NULL); for (i = 0; i < 4; i++) { ((uint32_t *) request->packet->vector)[i] = fr_rand(); } /* * Update the password, so it can be encrypted with the * new authentication vector. */ if (request->password) { VALUE_PAIR *vp; if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_USER_PASSWORD, TAG_ANY)) != NULL) { fr_pair_value_strcpy(vp, request->password->vp_strvalue); } else if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_CHAP_PASSWORD, TAG_ANY)) != NULL) { uint8_t buffer[17]; fr_radius_encode_chap_password(buffer, request->packet, fr_rand() & 0xff, request->password); fr_pair_value_memcpy(vp, buffer, 17); } else if (fr_pair_find_by_num(request->packet->vps, 0, PW_MS_CHAP_PASSWORD, TAG_ANY) != NULL) { mschapv1_encode(request->packet, &request->packet->vps, request->password->vp_strvalue); } else { DEBUG("WARNING: No password in the request"); } } request->timestamp = time(NULL); request->tries = 1; request->resend++; } else { /* request->packet->id >= 0 */ time_t now = time(NULL); /* * FIXME: Accounting packets are never retried! * The Acct-Delay-Time attribute is updated to * reflect the delay, and the packet is re-sent * from scratch! */ /* * Not time for a retry, do so. */ if ((now - request->timestamp) < timeout) { /* * When we walk over the tree sending * packets, we update the minimum time * required to sleep. */ if ((sleep_time == -1) || (sleep_time > (now - request->timestamp))) { sleep_time = now - request->timestamp; } return 0; } /* * We're not trying later, maybe the packet is done. */ if (request->tries == retries) { assert(request->packet->id >= 0); /* * Delete the request from the tree of * outstanding requests. */ fr_packet_list_yank(pl, request->packet); REDEBUG("No reply from server for ID %d socket %d", request->packet->id, request->packet->sockfd); deallocate_id(request); /* * Normally we mark it "done" when we've received * the reply, but this is a special case. */ if (request->resend == resend_count) { request->done = true; } stats.lost++; return -1; } /* * We are trying later. */ request->timestamp = now; request->tries++; } /* * Send the packet. */ if (fr_radius_send(request->packet, NULL, secret) < 0) { REDEBUG("Failed to send packet for ID %d", request->packet->id); deallocate_id(request); request->done = true; return -1; } fr_packet_header_print(fr_log_fp, request->packet, false); if (fr_debug_lvl > 0) fr_pair_list_fprint(fr_log_fp, request->packet->vps); return 0; }