RADIUS_PDU *radius_pdu_unpack(Octstr *data_without_len) { RADIUS_PDU *pdu; int type, ident; long len, pos; ParseContext *context; Octstr *authenticator; len = octstr_len(data_without_len); if (len < 20) { error(0, "RADIUS: PDU was too short (%ld bytes).", octstr_len(data_without_len)); return NULL; } context = parse_context_create(data_without_len); type = parse_get_char(context); ident = parse_get_char(context); pdu = radius_pdu_create(type, NULL); if (pdu == NULL) return NULL; len = decode_integer(data_without_len, 2, 2) - 19; parse_skip(context, 2); debug("radius", 0, "RADIUS: Attributes len is %ld", len); authenticator = parse_get_octets(context, 16); octstr_dump_short(authenticator, 0, "RADIUS: Authenticator (md5) is:"); /* skipping back to context start for macro magic */ parse_context_destroy(context); context = parse_context_create(data_without_len); switch (type) { #define INTEGER(name, octets) \ pos = octstr_len(data_without_len) - parse_octets_left(context); \ p->name = decode_integer(data_without_len, pos, octets); \ parse_skip(context, octets); #define OCTETS(name, field_giving_octets) \ p->name = parse_get_octets(context, field_giving_octets); #define PDU(name, id, fields) \ case id: { struct name *p = &pdu->u.name; fields; \ radius_attr_unpack(&context, &pdu); } break; #include "radius_pdu.def" default: error(0, "Unknown RADIUS_PDU type, internal error while unpacking."); } parse_context_destroy(context); octstr_destroy(authenticator); return pdu; }
static void proxy_thread(void *arg) { int ss, cs; /* server and client sockets */ int fl; /* socket flags */ Octstr *addr = NULL; int forward; Octstr *tmp; run_thread = 1; ss = cs = -1; /* create client binding, only if we have a remote server * and make the client socet non-blocking */ if (remote_host != NULL) { cs = udp_client_socket(); fl = fcntl(cs, F_GETFL); fcntl(cs, F_SETFL, fl | O_NONBLOCK); addr = udp_create_address(remote_host, remote_port); } /* create server binding */ ss = udp_bind(our_port, octstr_get_cstr(our_host)); /* make the server socket non-blocking */ fl = fcntl(ss, F_GETFL); fcntl(ss, F_SETFL, fl | O_NONBLOCK); if (ss == -1) panic(0, "RADIUS: Couldn't set up server socket for port %ld.", our_port); while (run_thread) { RADIUS_PDU *pdu, *r; Octstr *data, *rdata; Octstr *from_nas, *from_radius; pdu = r = NULL; data = rdata = from_nas = from_radius = NULL; if (read_available(ss, 100000) < 1) continue; /* get request from NAS */ if (udp_recvfrom(ss, &data, &from_nas) == -1) { if (errno == EAGAIN) /* No datagram available, don't block. */ continue; error(0, "RADIUS: Couldn't receive request data from NAS"); continue; } tmp = udp_get_ip(from_nas); info(0, "RADIUS: Got data from NAS <%s:%d>", octstr_get_cstr(tmp), udp_get_port(from_nas)); octstr_destroy(tmp); octstr_dump(data, 0); /* unpacking the RADIUS PDU */ if ((pdu = radius_pdu_unpack(data)) == NULL) { warning(0, "RADIUS: Couldn't unpack PDU from NAS, ignoring."); goto error; } info(0, "RADIUS: from NAS: PDU type: %s", pdu->type_name); /* authenticate the Accounting-Request packet */ if (radius_authenticate_pdu(pdu, &data, secret_nas) == 0) { warning(0, "RADIUS: Authentication failed for PDU from NAS, ignoring."); goto error; } /* store to hash table if not present yet */ mutex_lock(radius_mutex); forward = update_tables(pdu); mutex_unlock(radius_mutex); /* create response PDU for NAS */ r = radius_pdu_create(0x05, pdu); /* * create response authenticator * code+identifier(req)+length+authenticator(req)+(attributes)+secret */ r->u.Accounting_Response.identifier = pdu->u.Accounting_Request.identifier; r->u.Accounting_Response.authenticator = octstr_duplicate(pdu->u.Accounting_Request.authenticator); /* pack response for NAS */ rdata = radius_pdu_pack(r); /* creates response autenticator in encoded PDU */ radius_authenticate_pdu(r, &rdata, secret_nas); /* * forward request to remote RADIUS server only if updated * and if we have a configured remote RADIUS server */ if ((remote_host != NULL) && forward) { if (udp_sendto(cs, data, addr) == -1) { error(0, "RADIUS: Couldn't send to remote RADIUS <%s:%ld>.", octstr_get_cstr(remote_host), remote_port); } else if (read_available(cs, remote_timeout) < 1) { error(0, "RADIUS: Timeout for response from remote RADIUS <%s:%ld>.", octstr_get_cstr(remote_host), remote_port); } else if (udp_recvfrom(cs, &data, &from_radius) == -1) { error(0, "RADIUS: Couldn't receive from remote RADIUS <%s:%ld>.", octstr_get_cstr(remote_host), remote_port); } else { info(0, "RADIUS: Got data from remote RADIUS <%s:%d>.", octstr_get_cstr(udp_get_ip(from_radius)), udp_get_port(from_radius)); octstr_dump(data, 0); /* XXX unpack the response PDU and check if the response * authenticator is valid */ } } /* send response to NAS */ if (udp_sendto(ss, rdata, from_nas) == -1) error(0, "RADIUS: Couldn't send response data to NAS <%s:%d>.", octstr_get_cstr(udp_get_ip(from_nas)), udp_get_port(from_nas)); error: radius_pdu_destroy(pdu); radius_pdu_destroy(r); octstr_destroy(rdata); octstr_destroy(data); octstr_destroy(from_nas); debug("radius.proxy", 0, "RADIUS: Mapping table contains %ld elements", dict_key_count(radius_table)); debug("radius.proxy", 0, "RADIUS: Session table contains %ld elements", dict_key_count(session_table)); debug("radius.proxy", 0, "RADIUS: Client table contains %ld elements", dict_key_count(client_table)); } octstr_destroy(addr); }