Removed extra calls to HMAC_CTX_init()
[freeradius.git] / src / main / dhcpd.c
1 /*
2  * dhcp.c       DHCP processing.  Done poorly for now.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2008 The FreeRADIUS server project
21  * Copyright 2008 Alan DeKok <aland@deployingradius.com>
22  */
23
24 #ifdef WITH_DHCP
25
26 /*
27  *      Same contents as listen_socket_t.
28  */
29 typedef struct dhcp_socket_t {
30         /*
31          *      For normal sockets.
32          */
33         fr_ipaddr_t     ipaddr;
34         int             port;
35         const char              *interface;
36         RADCLIENT_LIST  *clients;
37
38         /*
39          *      DHCP-specific additions.
40          */  
41         int             suppress_responses;
42         RADCLIENT       dhcp_client;
43 } dhcp_socket_t;
44
45 static int dhcp_process(REQUEST *request)
46 {
47         int rcode;
48         VALUE_PAIR *vp;
49
50         vp = pairfind(request->packet->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */
51         if (vp) {
52                 DICT_VALUE *dv = dict_valbyattr(DHCP2ATTR(53), vp->vp_integer);
53                 DEBUG("Trying sub-section dhcp %s {...}",
54                       dv->name ? dv->name : "<unknown>");
55                 rcode = module_post_auth(vp->vp_integer, request);
56         } else {
57                 DEBUG("DHCP: Failed to find DHCP-Message-Type in packet!");
58                 rcode = RLM_MODULE_FAIL;
59         }
60
61         /*
62          *      Look for Relay attribute, and forward it if necessary.
63          */
64         vp = pairfind(request->config_items, DHCP2ATTR(270));
65         if (vp) {
66                 VALUE_PAIR *giaddr;
67                 RADIUS_PACKET relayed;
68                 
69                 request->reply->code = 0; /* don't reply to the client */
70
71                 /*
72                  *      Find the original giaddr.
73                  *      FIXME: Maybe look in the original packet?
74                  *
75                  *      It's invalid to have giaddr=0 AND a relay option
76                  */
77                 giaddr = pairfind(request->packet->vps, 266);
78                 if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY))) {
79                         if (pairfind(request->packet->vps, DHCP2ATTR(82))) {
80                                 RDEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet");
81                                 return 1;
82                         }
83
84                         /*
85                          *      FIXME: Add cache by XID.
86                          */
87                         RDEBUG("DHCP: Cannot yet relay packets with giaddr = 0");
88                         return 1;
89                 }
90
91                 if (request->packet->data[3] > 10) {
92                         RDEBUG("DHCP: Number of hops is greater than 10: not relaying");
93                         return 1;
94                 }
95
96                 /*
97                  *      Forward it VERBATIM to the next server, rather
98                  *      than to the client.
99                  */
100                 memcpy(&relayed, request->packet, sizeof(relayed));
101
102                 relayed.dst_ipaddr.af = AF_INET;
103                 relayed.dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
104                 relayed.dst_port = request->packet->dst_port;
105
106                 relayed.src_ipaddr = request->packet->dst_ipaddr;
107                 relayed.src_port = request->packet->dst_port;
108
109                 relayed.data = rad_malloc(relayed.data_len);
110                 memcpy(relayed.data, request->packet->data, request->packet->data_len);
111                 relayed.vps = NULL;
112
113                 /*
114                  *      The only field that changes is the number of hops
115                  */
116                 relayed.data[3]++; /* number of hops */
117                 
118                 /*
119                  *      Forward the relayed packet VERBATIM, don't
120                  *      respond to the client, and forget completely
121                  *      about this request.
122                  */
123                 fr_dhcp_send(&relayed);
124                 free(relayed.data);
125                 return 1;
126         }
127
128         vp = pairfind(request->reply->vps, DHCP2ATTR(53)); /* DHCP-Message-Type */
129         if (vp) {
130                 request->reply->code = vp->vp_integer;
131                 if ((request->reply->code != 0) &&
132                     (request->reply->code < PW_DHCP_OFFSET)) {
133                         request->reply->code += PW_DHCP_OFFSET;
134                 }
135         }
136         else switch (rcode) {
137         case RLM_MODULE_OK:
138         case RLM_MODULE_UPDATED:
139                 if (request->packet->code == PW_DHCP_DISCOVER) {
140                         request->reply->code = PW_DHCP_OFFER;
141                         break;
142
143                 } else if (request->packet->code == PW_DHCP_REQUEST) {
144                         request->reply->code = PW_DHCP_ACK;
145                         break;
146                 }
147                 request->reply->code = PW_DHCP_NAK;
148                 break;
149
150         default:
151         case RLM_MODULE_REJECT:
152         case RLM_MODULE_FAIL:
153         case RLM_MODULE_INVALID:
154         case RLM_MODULE_NOOP:
155         case RLM_MODULE_NOTFOUND:       
156                 if (request->packet->code == PW_DHCP_DISCOVER) {
157                         request->reply->code = 0; /* ignore the packet */
158                 } else {
159                         request->reply->code = PW_DHCP_NAK;
160                 }
161                 break;
162
163         case RLM_MODULE_HANDLED:
164                 break;
165         }
166
167         return 1;
168 }
169
170 static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
171 {
172         int rcode, broadcast = 1;
173         int on = 1;
174         dhcp_socket_t *sock;
175         RADCLIENT *client;
176         CONF_PAIR *cp;
177
178         rcode = common_socket_parse(cs, this);
179         if (rcode != 0) return rcode;
180
181         if (check_config) return 0;
182
183         sock = this->data;
184
185         /*
186          *      See whether or not we enable broadcast packets.
187          */
188         cp = cf_pair_find(cs, "broadcast");
189         if (cp) {
190                 const char *value = cf_pair_value(cp);
191                 if (value && (strcmp(value, "no") == 0)) {
192                         broadcast = 0;
193                 }
194         }
195
196         if (broadcast) {
197                 if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
198                         radlog(L_ERR, "Can't set broadcast option: %s\n",
199                                strerror(errno));
200                         return -1;
201                 }
202         }
203
204         if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
205                 radlog(L_ERR, "Can't set re-use addres option: %s\n",
206                        strerror(errno));
207                 return -1;
208         }
209
210         /*
211          *      Undocumented extension for testing without
212          *      destroying your network!
213          */
214         sock->suppress_responses = FALSE;
215         cp = cf_pair_find(cs, "suppress_responses");
216         if (cp) {
217                 const char *value;
218
219                 value = cf_pair_value(cp);
220
221                 if (value && (strcmp(value, "yes") == 0)) {
222                         sock->suppress_responses = TRUE;
223                 }
224         }
225
226         /*
227          *      Initialize the fake client.
228          */
229         client = &sock->dhcp_client;
230         memset(client, 0, sizeof(*client));
231         client->ipaddr.af = AF_INET;
232         client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
233         client->prefix = 0;
234         client->longname = client->shortname = "dhcp";
235         client->secret = client->shortname;
236         client->nastype = strdup("none");
237
238         return 0;
239 }
240
241
242 /*
243  *      Check if an incoming request is "ok"
244  *
245  *      It takes packets, not requests.  It sees if the packet looks
246  *      OK.  If so, it does a number of sanity checks on it.
247  */
248 static int dhcp_socket_recv(rad_listen_t *listener,
249                             RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
250 {
251         RADIUS_PACKET   *packet;
252         dhcp_socket_t   *sock;
253
254         packet = fr_dhcp_recv(listener->fd);
255         if (!packet) {
256                 radlog(L_ERR, "%s", fr_strerror());
257                 return 0;
258         }
259
260         sock = listener->data;
261         if (!received_request(listener, packet, prequest, &sock->dhcp_client)) {
262                 rad_free(&packet);
263                 return 0;
264         }
265
266         *pfun = dhcp_process;
267
268         return 1;
269 }
270
271
272 /*
273  *      Send an authentication response packet
274  */
275 static int dhcp_socket_send(rad_listen_t *listener, REQUEST *request)
276 {
277         dhcp_socket_t   *sock;
278
279         rad_assert(request->listener == listener);
280         rad_assert(listener->send == dhcp_socket_send);
281
282         if (request->reply->code == 0) return 0; /* don't reply */
283
284         if (fr_dhcp_encode(request->reply, request->packet) < 0) {
285                 return -1;
286         }
287
288         sock = listener->data;
289         if (sock->suppress_responses) return 0;
290
291         /*
292          *      Don't send anything
293          */
294         return fr_dhcp_send(request->reply);
295 }
296
297
298 static int dhcp_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
299 {
300         DEBUG2("NO ENCODE!");
301         return 0;
302         return fr_dhcp_encode(request->reply, request->packet);
303 }
304
305
306 static int dhcp_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
307 {
308         return fr_dhcp_decode(request->packet);
309 }
310 #endif /* WITH_DCHP */
311