X-Git-Url: http://www.project-moonshot.org/gitweb/?a=blobdiff_plain;f=lib%2Fpacket.c;h=8e6f22dc9124de2faae8ba13fad56deb45a16dc8;hb=19b5ce7ac3f838e689ae6e6b1e4a0c114b44ce67;hp=9a27e8c4674db4a595b68030edfbd0f7a7bc5927;hpb=cd0913e7b18d5942c12bc987b89d6b6246557569;p=radsecproxy.git diff --git a/lib/packet.c b/lib/packet.c index 9a27e8c..8e6f22d 100644 --- a/lib/packet.c +++ b/lib/packet.c @@ -1,417 +1,197 @@ -/* See the file COPYING for licensing information. */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. + See the file COPYING for licensing information. */ + +#if defined HAVE_CONFIG_H +#include +#endif -#include #include -#include -#include #include #include #include -#if defined DEBUG +#include "conn.h" +#include "debug.h" +#include "packet.h" + +#if defined (DEBUG) #include #include -#include "debug.h" +#include #endif -static int -_packet_create (struct rs_connection *conn, struct rs_packet **pkt_out) +int +packet_verify_response (struct rs_connection *conn, + struct rs_packet *response, + struct rs_packet *request) { - struct rs_packet *p; - RADIUS_PACKET *rpkt; - - *pkt_out = NULL; - - rpkt = rad_alloc (1); - if (!rpkt) - return rs_conn_err_push (conn, RSE_NOMEM, __func__); - rpkt->id = -1; + assert (conn); + assert (conn->active_peer); + assert (conn->active_peer->secret); + assert (response); + assert (response->rpkt); + assert (request); + assert (request->rpkt); + + /* Verify header and message authenticator. */ + if (rad_verify (response->rpkt, request->rpkt, conn->active_peer->secret)) + { + conn_close (&conn); + return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__, + "rad_verify: %s", fr_strerror ()); + } - p = (struct rs_packet *) malloc (sizeof (struct rs_packet)); - if (!p) + /* Decode and decrypt. */ + if (rad_decode (response->rpkt, request->rpkt, conn->active_peer->secret)) { - rad_free (&rpkt); - return rs_conn_err_push (conn, RSE_NOMEM, __func__); + conn_close (&conn); + return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__, + "rad_decode: %s", fr_strerror ()); } - memset (p, 0, sizeof (struct rs_packet)); - p->conn = conn; - p->rpkt = rpkt; - *pkt_out = p; return RSE_OK; } -static void -_do_send (struct rs_packet *pkt) + +/* Badly named function for preparing a RADIUS message and queue it. + FIXME: Rename. */ +int +packet_do_send (struct rs_packet *pkt) { - int err; + VALUE_PAIR *vp = NULL; - rad_encode (pkt->rpkt, NULL, pkt->conn->active_peer->secret); + assert (pkt); + assert (pkt->conn); + assert (pkt->conn->active_peer); + assert (pkt->conn->active_peer->secret); assert (pkt->rpkt); + + /* Add a Message-Authenticator, RFC 2869, if not already present. */ + /* FIXME: Make Message-Authenticator optional? */ + vp = paircreate (PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS); + if (!vp) + return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, + "paircreate: %s", fr_strerror ()); + pairreplace (&pkt->rpkt->vps, vp); + + /* Encode message. */ + if (rad_encode (pkt->rpkt, NULL, pkt->conn->active_peer->secret)) + return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, + "rad_encode: %s", fr_strerror ()); + /* Sign message. */ + if (rad_sign (pkt->rpkt, NULL, pkt->conn->active_peer->secret)) + return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, + "rad_sign: %s", fr_strerror ()); #if defined (DEBUG) { char host[80], serv[80]; - getnameinfo (pkt->conn->active_peer->addr->ai_addr, - pkt->conn->active_peer->addr->ai_addrlen, + getnameinfo (pkt->conn->active_peer->addr_cache->ai_addr, + pkt->conn->active_peer->addr_cache->ai_addrlen, host, sizeof(host), serv, sizeof(serv), 0 /* NI_NUMERICHOST|NI_NUMERICSERV*/); - fprintf (stderr, "%s: about to send this to %s:%s:\n", __func__, host, - serv); + rs_debug (("%s: about to send this to %s:%s:\n", __func__, host, serv)); rs_dump_packet (pkt); } #endif - err = bufferevent_write (pkt->conn->bev, pkt->rpkt->data, - pkt->rpkt->data_len); - if (err < 0) - rs_conn_err_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, - "bufferevent_write: %s", evutil_gai_strerror(err)); -} - -static void -_event_cb (struct bufferevent *bev, short events, void *ctx) -{ - struct rs_packet *pkt = (struct rs_packet *) ctx; - struct rs_connection *conn; - struct rs_peer *p; - - assert (pkt); - assert (pkt->conn); - assert (pkt->conn->active_peer); - conn = pkt->conn; - p = conn->active_peer; - - p->is_connecting = 0; - if (events & BEV_EVENT_CONNECTED) - { - p->is_connected = 1; -#if defined (DEBUG) - fprintf (stderr, "%s: connected\n", __func__); -#endif - _do_send (pkt); - /* Packet will be freed in write callback. */ - } - else if (events & BEV_EVENT_ERROR) - rs_conn_err_push_fl (pkt->conn, RSE_CONNERR, __FILE__, __LINE__, NULL); -} - -static void -_write_cb (struct bufferevent *bev, void *ctx) -{ - struct rs_packet *pkt = (struct rs_packet *) ctx; - - assert (pkt); - assert (pkt->conn); -#if defined (DEBUG) - fprintf (stderr, "%s: packet written, breaking event loop\n", __func__); -#endif - if (event_base_loopbreak (pkt->conn->evb) < 0) - abort (); /* FIXME */ - rs_packet_destroy (pkt); -} - -static void -_read_cb (struct bufferevent *bev, void *ctx) -{ - struct rs_packet *pkt = (struct rs_packet *) ctx; - size_t n; - - assert (pkt); - assert (pkt->conn); - if (!pkt->hdr_read_flag) - { - n = bufferevent_read (pkt->conn->bev, pkt->hdr, RS_HEADER_LEN); - if (n == RS_HEADER_LEN) - { - pkt->hdr_read_flag = 1; - pkt->rpkt->data_len = (pkt->hdr[2] << 8) + pkt->hdr[3]; - if (pkt->rpkt->data_len < 20 /* || len > 4096 */) - abort (); /* FIXME: Read and discard packet. */ - pkt->rpkt->data = rs_malloc (pkt->conn->ctx, pkt->rpkt->data_len); - if (!pkt->rpkt->data) - { - rs_conn_err_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__, - NULL); - abort (); /* FIXME: Read and discard packet. */ - } - memcpy (pkt->rpkt->data, pkt->hdr, RS_HEADER_LEN); - bufferevent_setwatermark (pkt->conn->bev, EV_READ, - pkt->rpkt->data_len - RS_HEADER_LEN, 0); -#if defined (DEBUG) - fprintf (stderr, "%s: packet header read, total pkt len=%d\n", - __func__, pkt->rpkt->data_len); -#endif - } - else if (n < 0) - return; /* Buffer frozen. */ - else - assert (!"short header"); - } - -#if defined (DEBUG) - printf ("%s: trying to read %d octets of packet data\n", __func__, pkt->rpkt->data_len - RS_HEADER_LEN); -#endif - n = bufferevent_read (pkt->conn->bev, pkt->rpkt->data + RS_HEADER_LEN, pkt->rpkt->data_len - RS_HEADER_LEN); -#if defined (DEBUG) - printf ("%s: read %d octets of packet data\n", __func__, n); -#endif - if (n == pkt->rpkt->data_len - RS_HEADER_LEN) - { - bufferevent_disable (pkt->conn->bev, EV_READ); - pkt->hdr_read_flag = 0; - memset (pkt->hdr, 0, sizeof(*pkt->hdr)); -#if defined (DEBUG) - fprintf (stderr, "%s: complete packet read\n", __func__); -#endif - rad_decode (pkt->rpkt, NULL, pkt->conn->active_peer->secret); - if (event_base_loopbreak (pkt->conn->evb) < 0) - abort (); /* FIXME */ - } - else if (n < 0) - return; /* Buffer frozen. */ - else - assert (!"short packet"); -} - -#if defined (DEBUG) -static void -_evlog_cb (int severity, const char *msg) -{ - const char *sevstr; - switch (severity) - { - case _EVENT_LOG_DEBUG: - sevstr = "debug"; - break; - case _EVENT_LOG_MSG: - sevstr = "msg"; - break; - case _EVENT_LOG_WARN: - sevstr = "warn"; - break; - case _EVENT_LOG_ERR: - sevstr = "err"; - break; - default: - sevstr = "???"; - break; - } - fprintf (stderr, "libevent: [%s] %s\n", sevstr, msg); -} -#endif /* DEBUG */ -static int -_init_evb (struct rs_connection *conn) -{ - if (!conn->evb) + /* Put message in output buffer. */ + if (pkt->conn->bev) /* TCP. */ { -#if defined (DEBUG) - event_enable_debug_mode (); - event_set_log_callback (_evlog_cb); -#endif - conn->evb = event_base_new (); - if (!conn->evb) - return rs_conn_err_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, - "event_base_new"); + int err = bufferevent_write (pkt->conn->bev, pkt->rpkt->data, + pkt->rpkt->data_len); + if (err < 0) + return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, + "bufferevent_write: %s", + evutil_gai_strerror (err)); } - return RSE_OK; -} - -static int -_init_socket (struct rs_connection *conn, struct rs_peer *p) -{ - if (p->s < 0) + else /* UDP. */ { - assert (p->addr); - p->s = socket (p->addr->ai_family, p->addr->ai_socktype, - p->addr->ai_protocol); - if (p->s < 0) - return rs_conn_err_push_fl (conn, RSE_SOME_ERROR, __FILE__, __LINE__, - strerror (errno)); - } - return RSE_OK; -} - -static struct rs_peer * -_pick_peer (struct rs_connection *conn) -{ - if (!conn->active_peer) - conn->active_peer = conn->peers; - return conn->active_peer; -} + struct rs_packet **pp = &pkt->conn->out_queue; -static int -_init_bev (struct rs_connection *conn, struct rs_peer *peer) -{ - if (!conn->bev) - { - conn->bev = bufferevent_socket_new (conn->evb, peer->s, 0); - if (!conn->bev) - return rs_conn_err_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, - "bufferevent_socket_new"); + while (*pp && (*pp)->next) + *pp = (*pp)->next; + *pp = pkt; } - return RSE_OK; -} - -static void -_do_connect (struct rs_peer *p) -{ - int err; - - err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr, - p->addr->ai_addrlen); - if (err < 0) - rs_conn_err_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__, - "bufferevent_socket_connect: %s", - evutil_gai_strerror(err)); - else - p->is_connecting = 1; -} - -static int -_conn_open(struct rs_connection *conn, struct rs_packet *pkt) -{ - struct rs_peer *p; - - if (_init_evb (conn)) - return -1; - - p = _pick_peer (conn); - if (!p) - return rs_conn_err_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL); - - if (_init_socket (conn, p)) - return -1; - - if (_init_bev (conn, p)) - return -1; - - if (!p->is_connected) - if (!p->is_connecting) - _do_connect (p); return RSE_OK; } -static int -_conn_is_open_p (struct rs_connection *conn) -{ - return conn->active_peer && conn->active_peer->is_connected; -} - /* Public functions. */ int -rs_packet_create_acc_request (struct rs_connection *conn, - struct rs_packet **pkt_out, - const char *user_name, const char *user_pw) -{ - struct rs_packet *pkt; - struct rs_attr *attr; - - if (_packet_create (conn, pkt_out)) - return -1; - pkt = *pkt_out; - pkt->rpkt->code = PW_AUTHENTICATION_REQUEST; - - if (rs_attr_create (conn, &attr, "User-Name", user_name)) - return -1; - rs_packet_add_attr (pkt, attr); - - if (rs_attr_create (conn, &attr, "User-Password", user_pw)) - return -1; - /* FIXME: need this too? rad_pwencode(user_pw, &pwlen, SECRET, reqauth) */ - rs_packet_add_attr (pkt, attr); - - return RSE_OK; -} - -int -rs_packet_send (struct rs_packet *pkt, void *user_data) +rs_packet_create (struct rs_connection *conn, struct rs_packet **pkt_out) { - struct rs_connection *conn; - assert (pkt); - conn = pkt->conn; + struct rs_packet *p; + RADIUS_PACKET *rpkt; - if (_conn_is_open_p (conn)) - _do_send (pkt); - else - if (_conn_open (conn, pkt)) - return -1; + *pkt_out = NULL; - assert (conn->evb); - assert (conn->bev); - assert (conn->active_peer); - assert (conn->active_peer->s >= 0); + rpkt = rad_alloc (1); + if (!rpkt) + return rs_err_conn_push (conn, RSE_NOMEM, __func__); + rpkt->id = conn->nextid++; - if (conn->callbacks.connected_cb || conn->callbacks.disconnected_cb - || conn->callbacks.received_cb || conn->callbacks.sent_cb) - ; /* FIXME: install event callbacks, other than below */ - else + p = (struct rs_packet *) malloc (sizeof (struct rs_packet)); + if (!p) { - bufferevent_setcb (conn->bev, _read_cb, _write_cb, _event_cb, pkt); - event_base_dispatch (conn->evb); + rad_free (&rpkt); + return rs_err_conn_push (conn, RSE_NOMEM, __func__); } + memset (p, 0, sizeof (struct rs_packet)); + p->conn = conn; + p->rpkt = rpkt; -#if defined (DEBUG) - fprintf (stderr, "%s: event loop done\n", __func__); - assert (event_base_got_break(conn->evb)); -#endif - + *pkt_out = p; return RSE_OK; } int -rs_conn_receive_packet (struct rs_connection *conn, struct rs_packet **pkt_out) +rs_packet_create_authn_request (struct rs_connection *conn, + struct rs_packet **pkt_out, + const char *user_name, const char *user_pw) { struct rs_packet *pkt; + VALUE_PAIR *vp = NULL; - assert (conn); - - if (_packet_create (conn, pkt_out)) + if (rs_packet_create (conn, pkt_out)) return -1; pkt = *pkt_out; - pkt->conn = conn; + pkt->rpkt->code = PW_AUTHENTICATION_REQUEST; - if (_conn_open (conn, pkt)) - return -1; - assert (conn->evb); - assert (conn->bev); - assert (conn->active_peer); - assert (conn->active_peer->s >= 0); + if (user_name) + { + vp = pairmake ("User-Name", user_name, T_OP_EQ); + if (vp == NULL) + return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__, + "pairmake: %s", fr_strerror ()); + pairadd (&pkt->rpkt->vps, vp); + } - bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0); - bufferevent_enable (conn->bev, EV_READ); - event_base_dispatch (conn->evb); -#if defined (DEBUG) - fprintf (stderr, "%s: event loop done", __func__); - if (event_base_got_break(conn->evb)) + if (user_pw) { - fprintf (stderr, ", got this:\n"); - rs_dump_packet (pkt); + vp = pairmake ("User-Password", user_pw, T_OP_EQ); + if (vp == NULL) + return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__, + "pairmake: %s", fr_strerror ()); + pairadd (&pkt->rpkt->vps, vp); } - else - fprintf (stderr, ", no reply\n"); -#endif return RSE_OK; } -void -rs_packet_add_attr(struct rs_packet *pkt, struct rs_attr *attr) -{ - pairadd (&pkt->rpkt->vps, attr->vp); - attr->pkt = pkt; -} - struct radius_packet * -rs_packet_frpkt(struct rs_packet *pkt) +rs_packet_frpkt (struct rs_packet *pkt) { assert (pkt); return pkt->rpkt; } void -rs_packet_destroy(struct rs_packet *pkt) +rs_packet_destroy (struct rs_packet *pkt) { - rad_free (&pkt->rpkt); + assert (pkt); + assert (pkt->conn); + assert (pkt->conn->ctx); + + rad_free (&pkt->rpkt); /* Note: This frees the VALUE_PAIR's too. */ rs_free (pkt->conn->ctx, pkt); }