f060bcbfde0342314588d5e6ec34bf6d220cd848
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_tnc / rlm_eap_tnc.c
1 /*
2  * rlm_eap_tnc.c    Handles that are called from eap
3  *
4  *   This software is Copyright (C) 2006,2007 FH Hannover
5  *
6  *   Portions of this code unrelated to FreeRADIUS are available
7  *   separately under a commercial license.  If you require an
8  *   implementation of EAP-TNC that is not under the GPLv2, please
9  *   contact tnc@inform.fh-hannover.de for details.
10  *
11  *   This program is free software; you can redistribute it and/or modify
12  *   it under the terms of the GNU General Public License as published by
13  *   the Free Software Foundation; either version 2 of the License, or
14  *   (at your option) any later version.
15  *
16  *   This program is distributed in the hope that it will be useful,
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *   GNU General Public License for more details.
20  *
21  *   You should have received a copy of the GNU General Public License
22  *   along with this program; if not, write to the Free Software
23  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  *
25  *   Modifications to integrate with FreeRADIUS configuration
26  *   Copyright (C) 2007 Alan DeKok <aland@deployingradius.com>
27  */
28
29 #include <freeradius-devel/autoconf.h>
30
31 #include <stdio.h>
32 #include <stdlib.h>
33
34 #include "tncs_connect.h"
35 #include "eap_tnc.h"
36 #include "tncs.h"
37 #include <freeradius-devel/rad_assert.h>
38
39 typedef struct rlm_eap_tnc_t {
40         char    *vlan_access;
41         char    *vlan_isolate;
42         char    *tnc_path;
43 } rlm_eap_tnc_t;
44
45 static int sessionCounter=0;
46
47 /*
48  *      Initiate the EAP-MD5 session by sending a challenge to the peer.
49  *  Initiate the EAP-TNC session by sending a EAP Request witch Start Bit set 
50  *  and with no data
51  */
52 static int tnc_initiate(void *type_data, EAP_HANDLER *handler)
53 {
54         rlm_eap_tnc_t *inst = type_data;
55
56         if (!handler->request || !handler->request->parent) {
57                 DEBUG2("rlm_eap_tnc: Must be run inside of a TLS method");
58                 return 0;
59         }
60
61         DEBUG("tnc_initiate: %ld", handler->timestamp);
62
63         TNC_PACKET      *reply;
64         
65         if(connectToTncs(inst->tnc_path)==-1){
66                 DEBUG("Could not connect to TNCS");
67         }else{
68                 
69         }
70
71         type_data = type_data;  /* -Wunused */
72         /*
73          *      Allocate an EAP-MD5 packet.
74          */
75         reply = eaptnc_alloc();
76         if (reply == NULL)  {
77                 radlog(L_ERR, "rlm_eap_tnc: out of memory");
78                 return 0;
79         }
80
81         /*
82          *      Fill it with data.
83          */
84         reply->code = PW_TNC_REQUEST;
85         uint8_t flags_ver = 1; //set version to 1
86         flags_ver = SET_START(flags_ver); //set start-flag
87         DEBUG("$$$$$$$$$$$$$$$$Flags: %d", flags_ver);
88         reply->flags_ver = flags_ver;
89         reply->length = 1+1; /* one byte of flags_ver */
90
91
92         /*
93          *      Compose the EAP-TNC packet out of the data structure,
94          *      and free it.
95          */
96         eaptnc_compose(handler->eap_ds, reply);
97
98     //put sessionAttribute to Handler and increase sessionCounter
99     handler->opaque = calloc(sizeof(TNC_ConnectionID), 1);
100     memcpy(handler->opaque, &sessionCounter, sizeof(int));
101     sessionCounter++;
102     
103         /*
104          *      We don't need to authorize the user at this point.
105          *
106          *      We also don't need to keep the challenge, as it's
107          *      stored in 'handler->eap_ds', which will be given back
108          *      to us...
109          */
110         handler->stage = AUTHENTICATE;
111     
112         return 1;
113 }
114
115 static void setVlanAttribute(rlm_eap_tnc_t *inst, EAP_HANDLER *handler,
116                              VlanAccessMode mode){
117     char *vlanNumber = NULL;
118     switch(mode){
119         case VLAN_ISOLATE:
120             vlanNumber = inst->vlan_isolate;
121             break;
122         case VLAN_ACCESS:
123             vlanNumber = inst->vlan_access;
124             break;
125
126     default:
127             DEBUG2("  rlm_eap_tnc: Internal error.  Not setting vlan number");
128             return;
129     }
130     pairadd(&handler->request->reply->vps,
131             pairmake("Tunnel-Type", "VLAN", T_OP_SET));
132     
133     pairadd(&handler->request->reply->vps,
134             pairmake("Tunnel-Medium-Type", "IEEE-802", T_OP_SET));
135     
136     pairadd(&handler->request->reply->vps,
137             pairmake("Tunnel-Private-Group-ID", vlanNumber, T_OP_SET));
138     
139 }
140
141 /*
142  *      Authenticate a previously sent challenge.
143  */
144 static int tnc_authenticate(void *type_arg, EAP_HANDLER *handler)
145 {
146     TNC_PACKET  *packet;
147     TNC_PACKET  *reply;
148     TNC_ConnectionID connId = *((TNC_ConnectionID *) (handler->opaque));
149     rlm_eap_tnc_t *inst = type_arg;
150
151     DEBUG2("HANDLER_OPAQUE: %d\n", *((TNC_ConnectionID *) (handler->opaque)));
152     DEBUG2("XXXXXXXXXXXX TNC-AUTHENTICATE is starting now for %d..........\n", connId);
153
154     /*
155          *      Get the User-Password for this user.
156          */
157     rad_assert(handler->request != NULL);
158         rad_assert(handler->stage == AUTHENTICATE);
159     
160         /*
161          *      Extract the EAP-TNC packet.
162          */
163     if (!(packet = eaptnc_extract(handler->eap_ds)))
164                 return 0;
165
166     
167     /*
168          *      Create a reply, and initialize it.
169          */
170         reply = eaptnc_alloc();
171         if (!reply) {
172                 return 0;
173         }
174     
175         reply->id = handler->eap_ds->request->id;
176         reply->length = 0;
177     TNC_UInt32 tnccsMsgLength = 0;
178     if(packet->data_length==0){
179         tnccsMsgLength = packet->length-TNC_PACKET_LENGTH_WITHOUT_DATA_LENGTH;
180     }else{
181         tnccsMsgLength = packet->length-TNC_PACKET_LENGTH;
182     }
183     TNC_BufferReference outMessage;
184     TNC_UInt32 outMessageLength = 2;
185     int isLengthIncluded = TNC_LENGTH_INCLUDED(packet->flags_ver);
186     TNC_UInt32 overallLength = packet->data_length;
187     int moreFragments = TNC_MORE_FRAGMENTS(packet->flags_ver);
188     int outIsLengthIncluded=0;
189     int outMoreFragments=0;
190     TNC_UInt32 outOverallLength=0;
191     int isAcknowledgement = 0;
192     if(isLengthIncluded == 0 
193         && moreFragments == 0 
194         && overallLength == 0 
195         && tnccsMsgLength == 0
196         && TNC_START(packet->flags_ver)==0){
197         
198         isAcknowledgement = 1;
199     }
200     
201     DEBUG("Data received: (%d)\n", tnccsMsgLength);
202 /*    int i;
203     for(i=0;i<tnccsMsgLength;i++){
204         DEBUG2("%c", (packet->data)[i]);
205     }
206     DEBUG2("\n");
207    */
208     TNC_ConnectionState state = exchangeTNCCSMessages(inst->tnc_path,
209                                                         connId,
210                                                         isAcknowledgement,
211                                                         packet->data, 
212                                                         tnccsMsgLength, 
213                                                         isLengthIncluded, 
214                                                         moreFragments, 
215                                                         overallLength, 
216                                                         &outMessage, 
217                                                         &outMessageLength,
218                                                         &outIsLengthIncluded,
219                                                         &outMoreFragments,
220                                                         &outOverallLength);
221     DEBUG("GOT State %d from TNCS", state);
222     if(state == TNC_CONNECTION_EAP_ACKNOWLEDGEMENT){ //send back acknoledgement
223         reply->code = PW_TNC_REQUEST;
224         reply->data = NULL;
225         reply->data_length = 0;
226         reply->flags_ver = 1;
227         reply->length =TNC_PACKET_LENGTH_WITHOUT_DATA_LENGTH; 
228     }else{ //send back normal message
229         DEBUG("GOT Message from TNCS (length: %d)", outMessageLength);
230         
231  /*       for(i=0;i<outMessageLength;i++){
232             DEBUG2("%c", outMessage[i]);
233         }
234         DEBUG2("\n");
235  */
236         DEBUG("outIsLengthIncluded: %d, outMoreFragments: %d, outOverallLength: %d", 
237                 outIsLengthIncluded, outMoreFragments, outOverallLength);
238         DEBUG("NEW STATE: %d", state);
239         switch(state){
240             case TNC_CONNECTION_STATE_HANDSHAKE:
241                 reply->code = PW_TNC_REQUEST;
242                 DEBUG2("Set Reply->Code to EAP-REQUEST\n");
243                 break;
244             case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
245                 reply->code = PW_TNC_SUCCESS;
246                 setVlanAttribute(inst, handler,VLAN_ACCESS);
247                 break;
248             case TNC_CONNECTION_STATE_ACCESS_NONE:
249                 reply->code = PW_TNC_FAILURE;
250                 //setVlanAttribute(inst, handler, VLAN_ISOLATE);
251                 break;
252             case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
253                 reply->code = PW_TNC_SUCCESS;
254                 setVlanAttribute(inst, handler, VLAN_ISOLATE);
255                 break;
256             default:
257                 reply->code= PW_TNC_FAILURE;
258                 
259         }
260         if(outMessage!=NULL && outMessageLength!=0){
261             reply->data = outMessage;
262         }
263         reply->flags_ver = 1;
264         if(outIsLengthIncluded){
265             reply->flags_ver = SET_LENGTH_INCLUDED(reply->flags_ver);
266             reply->data_length = outOverallLength;
267             reply->length = TNC_PACKET_LENGTH + outMessageLength;
268             DEBUG("SET LENGTH: %d", reply->length);
269             DEBUG("SET DATALENGTH: %d", outOverallLength);
270         }else{
271             reply->data_length = 0;
272             reply->length = TNC_PACKET_LENGTH_WITHOUT_DATA_LENGTH + outMessageLength;        
273             DEBUG("SET LENGTH: %d", reply->length);
274         }
275         if(outMoreFragments){
276             reply->flags_ver = SET_MORE_FRAGMENTS(reply->flags_ver);
277         }
278     }
279     
280         /*
281          *      Compose the EAP-MD5 packet out of the data structure,
282          *      and free it.
283          */
284         eaptnc_compose(handler->eap_ds, reply);
285     
286     handler->stage = AUTHENTICATE;
287     
288         eaptnc_free(&packet);
289         return 1;
290 }
291
292 /*
293  *      Detach the EAP-TNC module.
294  */
295 static int tnc_detach(void *arg)
296 {
297         free(arg);
298         return 0;
299 }
300
301
302 static CONF_PARSER module_config[] = {
303         { "vlan_access", PW_TYPE_STRING,
304           offsetof(rlm_eap_tnc_t, vlan_access), NULL, NULL },
305         { "vlan_isolate", PW_TYPE_STRING,
306           offsetof(rlm_eap_tnc_t, vlan_isolate), NULL, NULL },
307         { "libtns.so", PW_TYPE_STRING,
308           offsetof(rlm_eap_tnc_t, tnc_path), NULL,
309         "/usr/local/lib/libTNCS.so"},
310
311         { NULL, -1, 0, NULL, NULL }           /* end the list */
312 };
313
314 /*
315  *      Attach the EAP-TNC module.
316  */
317 static int tnc_attach(CONF_SECTION *cs, void **instance)
318 {
319         rlm_eap_tnc_t *inst;
320
321         inst = malloc(sizeof(*inst));
322         if (!inst) return -1;
323         memset(inst, 0, sizeof(*inst));
324
325         if (cf_section_parse(cs, inst, module_config) < 0) {
326                 tnc_detach(inst);
327                 return -1;
328         }
329
330         
331         if (!inst->vlan_access || !inst->vlan_isolate) {
332                 radlog(L_ERR, "rlm_eap_tnc: Must set both vlan_access and vlan_isolate");
333                 tnc_detach(inst);
334                 return -1;
335         }
336
337         *instance = inst;
338         return 0;
339 }
340
341 /*
342  *      The module name should be the only globally exported symbol.
343  *      That is, everything else should be 'static'.
344  */
345 EAP_TYPE rlm_eap_tnc = {
346         "eap_tnc",
347         tnc_attach,                     /* attach */
348         tnc_initiate,                   /* Start the initial request */
349         NULL,                           /* authorization */
350         tnc_authenticate,               /* authentication */
351         tnc_detach                      /* detach */
352 };