Added infrastructure to listen to RADIUS Dynamic Authorization Extension requests
[strongswan.git] / src / libcharon / plugins / eap_radius / eap_radius_dae.c
1 /*
2 * Copyright (C) 2012 Martin Willi
3 * Copyright (C) 2012 revosec AG
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include "eap_radius_dae.h"
17
18 #include "radius_message.h"
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <unistd.h>
24 #include <errno.h>
25
26 #include <daemon.h>
27 #include <threading/thread.h>
28 #include <processing/jobs/callback_job.h>
29
30 #define RADIUS_DAE_PORT 3799
31
32 typedef struct private_eap_radius_dae_t private_eap_radius_dae_t;
33
34 /**
35 * Private data of an eap_radius_dae_t object.
36 */
37 struct private_eap_radius_dae_t {
38
39 /**
40 * Public eap_radius_dae_t interface.
41 */
42 eap_radius_dae_t public;
43
44 /**
45 * RADIUS session state
46 */
47 eap_radius_accounting_t *accounting;
48
49 /**
50 * Socket to listen on authorization extension port
51 */
52 int fd;
53
54 /**
55 * Listen job
56 */
57 callback_job_t *job;
58 };
59
60 /**
61 * Receive RADIUS DAE requests
62 */
63 static job_requeue_t receive(private_eap_radius_dae_t *this)
64 {
65 struct sockaddr_storage addr;
66 socklen_t addr_len = sizeof(addr);
67 radius_message_t *request;
68 char buf[2048];
69 ssize_t len;
70 bool oldstate;
71
72 oldstate = thread_cancelability(TRUE);
73 len = recvfrom(this->fd, buf, sizeof(buf), 0,
74 (struct sockaddr*)&addr, &addr_len);
75 thread_cancelability(oldstate);
76
77 if (len > 0)
78 {
79 request = radius_message_parse_response(chunk_create(buf, len));
80 if (request)
81 {
82 switch (request->get_code(request))
83 {
84 case RMC_DISCONNECT_REQUEST:
85 /* TODO */
86 case RMC_COA_REQUEST:
87 /* TODO */
88 default:
89 DBG1(DBG_CFG, "ignoring unsupported RADIUS DAE %N message",
90 radius_message_code_names, request->get_code(request));
91 break;
92 }
93 request->destroy(request);
94 }
95 else
96 {
97 DBG1(DBG_NET, "ignoring invalid RADIUS DAE request");
98 }
99 }
100 else
101 {
102 DBG1(DBG_NET, "receving RADIUS DAE request failed: %s", strerror(errno));
103 }
104 return JOB_REQUEUE_DIRECT;
105 }
106
107 /**
108 * Open DAE socket
109 */
110 static bool open_socket(private_eap_radius_dae_t *this)
111 {
112 host_t *host;
113
114 this->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
115 if (this->fd == -1)
116 {
117 DBG1(DBG_CFG, "unable to open RADIUS DAE socket: %s", strerror(errno));
118 return FALSE;
119 }
120
121 host = host_create_from_string(
122 lib->settings->get_str(lib->settings,
123 "charon.plugins.eap-radius.dae.listen", "0.0.0.0"),
124 lib->settings->get_int(lib->settings,
125 "charon.plugins.eap-radius.dae.port", RADIUS_DAE_PORT));
126 if (!host)
127 {
128 DBG1(DBG_CFG, "invalid RADIUS DAE listen address");
129 return FALSE;
130 }
131
132 if (bind(this->fd, host->get_sockaddr(host),
133 *host->get_sockaddr_len(host)) == -1)
134 {
135 DBG1(DBG_CFG, "unable to bind RADIUS DAE socket: %s", strerror(errno));
136 host->destroy(host);
137 return FALSE;
138 }
139 host->destroy(host);
140 return TRUE;
141 }
142
143 METHOD(eap_radius_dae_t, destroy, void,
144 private_eap_radius_dae_t *this)
145 {
146 if (this->job)
147 {
148 this->job->cancel(this->job);
149 }
150 if (this->fd != -1)
151 {
152 close(this->fd);
153 }
154 free(this);
155 }
156
157 /**
158 * See header
159 */
160 eap_radius_dae_t *eap_radius_dae_create(eap_radius_accounting_t *accounting)
161 {
162 private_eap_radius_dae_t *this;
163
164 INIT(this,
165 .public = {
166 .destroy = _destroy,
167 },
168 .accounting = accounting,
169 .fd = -1,
170 );
171
172 if (!open_socket(this))
173 {
174 destroy(this);
175 return NULL;
176 }
177
178 this->job = callback_job_create_with_prio((callback_job_cb_t)receive,
179 this, NULL, NULL, JOB_PRIO_CRITICAL);
180 lib->processor->queue_job(lib->processor, (job_t*)this->job);
181
182 return &this->public;
183 }