Define TNC-VLAN-{Access,Isolate}, and look for them in
[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         VALUE_PAIR *vp;
118     char *vlanNumber = NULL;
119     switch(mode){
120         case VLAN_ISOLATE:
121             vlanNumber = inst->vlan_isolate;
122             vp = pairfind(handler->request->config_items,
123                           PW_TNC_VLAN_ISOLATE);
124             if (vp) vlanNumber = vp->vp_strvalue;
125             break;
126         case VLAN_ACCESS:
127             vlanNumber = inst->vlan_access;
128             vp = pairfind(handler->request->config_items,
129                           PW_TNC_VLAN_ACCESS);
130             if (vp) vlanNumber = vp->vp_strvalue;
131             break;
132
133     default:
134             DEBUG2("  rlm_eap_tnc: Internal error.  Not setting vlan number");
135             return;
136     }
137     pairadd(&handler->request->reply->vps,
138             pairmake("Tunnel-Type", "VLAN", T_OP_SET));
139     
140     pairadd(&handler->request->reply->vps,
141             pairmake("Tunnel-Medium-Type", "IEEE-802", T_OP_SET));
142     
143     pairadd(&handler->request->reply->vps,
144             pairmake("Tunnel-Private-Group-ID", vlanNumber, T_OP_SET));
145     
146 }
147
148 /*
149  *      Authenticate a previously sent challenge.
150  */
151 static int tnc_authenticate(void *type_arg, EAP_HANDLER *handler)
152 {
153     TNC_PACKET  *packet;
154     TNC_PACKET  *reply;
155     TNC_ConnectionID connId = *((TNC_ConnectionID *) (handler->opaque));
156     rlm_eap_tnc_t *inst = type_arg;
157
158     DEBUG2("HANDLER_OPAQUE: %d\n", *((TNC_ConnectionID *) (handler->opaque)));
159     DEBUG2("XXXXXXXXXXXX TNC-AUTHENTICATE is starting now for %d..........\n", connId);
160
161     /*
162          *      Get the User-Password for this user.
163          */
164     rad_assert(handler->request != NULL);
165         rad_assert(handler->stage == AUTHENTICATE);
166     
167         /*
168          *      Extract the EAP-TNC packet.
169          */
170     if (!(packet = eaptnc_extract(handler->eap_ds)))
171                 return 0;
172
173     
174     /*
175          *      Create a reply, and initialize it.
176          */
177         reply = eaptnc_alloc();
178         if (!reply) {
179                 return 0;
180         }
181     
182         reply->id = handler->eap_ds->request->id;
183         reply->length = 0;
184     TNC_UInt32 tnccsMsgLength = 0;
185     if(packet->data_length==0){
186         tnccsMsgLength = packet->length-TNC_PACKET_LENGTH_WITHOUT_DATA_LENGTH;
187     }else{
188         tnccsMsgLength = packet->length-TNC_PACKET_LENGTH;
189     }
190     TNC_BufferReference outMessage;
191     TNC_UInt32 outMessageLength = 2;
192     int isLengthIncluded = TNC_LENGTH_INCLUDED(packet->flags_ver);
193     TNC_UInt32 overallLength = packet->data_length;
194     int moreFragments = TNC_MORE_FRAGMENTS(packet->flags_ver);
195     int outIsLengthIncluded=0;
196     int outMoreFragments=0;
197     TNC_UInt32 outOverallLength=0;
198     int isAcknowledgement = 0;
199     if(isLengthIncluded == 0 
200         && moreFragments == 0 
201         && overallLength == 0 
202         && tnccsMsgLength == 0
203         && TNC_START(packet->flags_ver)==0){
204         
205         isAcknowledgement = 1;
206     }
207     
208     DEBUG("Data received: (%d)\n", tnccsMsgLength);
209 /*    int i;
210     for(i=0;i<tnccsMsgLength;i++){
211         DEBUG2("%c", (packet->data)[i]);
212     }
213     DEBUG2("\n");
214    */
215     TNC_ConnectionState state = exchangeTNCCSMessages(inst->tnc_path,
216                                                         connId,
217                                                         isAcknowledgement,
218                                                         packet->data, 
219                                                         tnccsMsgLength, 
220                                                         isLengthIncluded, 
221                                                         moreFragments, 
222                                                         overallLength, 
223                                                         &outMessage, 
224                                                         &outMessageLength,
225                                                         &outIsLengthIncluded,
226                                                         &outMoreFragments,
227                                                         &outOverallLength);
228     DEBUG("GOT State %d from TNCS", state);
229     if(state == TNC_CONNECTION_EAP_ACKNOWLEDGEMENT){ //send back acknoledgement
230         reply->code = PW_TNC_REQUEST;
231         reply->data = NULL;
232         reply->data_length = 0;
233         reply->flags_ver = 1;
234         reply->length =TNC_PACKET_LENGTH_WITHOUT_DATA_LENGTH; 
235     }else{ //send back normal message
236         DEBUG("GOT Message from TNCS (length: %d)", outMessageLength);
237         
238  /*       for(i=0;i<outMessageLength;i++){
239             DEBUG2("%c", outMessage[i]);
240         }
241         DEBUG2("\n");
242  */
243         DEBUG("outIsLengthIncluded: %d, outMoreFragments: %d, outOverallLength: %d", 
244                 outIsLengthIncluded, outMoreFragments, outOverallLength);
245         DEBUG("NEW STATE: %d", state);
246         switch(state){
247             case TNC_CONNECTION_STATE_HANDSHAKE:
248                 reply->code = PW_TNC_REQUEST;
249                 DEBUG2("Set Reply->Code to EAP-REQUEST\n");
250                 break;
251             case TNC_CONNECTION_STATE_ACCESS_ALLOWED:
252                 reply->code = PW_TNC_SUCCESS;
253                 setVlanAttribute(inst, handler,VLAN_ACCESS);
254                 break;
255             case TNC_CONNECTION_STATE_ACCESS_NONE:
256                 reply->code = PW_TNC_FAILURE;
257                 //setVlanAttribute(inst, handler, VLAN_ISOLATE);
258                 break;
259             case TNC_CONNECTION_STATE_ACCESS_ISOLATED:
260                 reply->code = PW_TNC_SUCCESS;
261                 setVlanAttribute(inst, handler, VLAN_ISOLATE);
262                 break;
263             default:
264                 reply->code= PW_TNC_FAILURE;
265                 
266         }
267         if(outMessage!=NULL && outMessageLength!=0){
268             reply->data = outMessage;
269         }
270         reply->flags_ver = 1;
271         if(outIsLengthIncluded){
272             reply->flags_ver = SET_LENGTH_INCLUDED(reply->flags_ver);
273             reply->data_length = outOverallLength;
274             reply->length = TNC_PACKET_LENGTH + outMessageLength;
275             DEBUG("SET LENGTH: %d", reply->length);
276             DEBUG("SET DATALENGTH: %d", outOverallLength);
277         }else{
278             reply->data_length = 0;
279             reply->length = TNC_PACKET_LENGTH_WITHOUT_DATA_LENGTH + outMessageLength;        
280             DEBUG("SET LENGTH: %d", reply->length);
281         }
282         if(outMoreFragments){
283             reply->flags_ver = SET_MORE_FRAGMENTS(reply->flags_ver);
284         }
285     }
286     
287         /*
288          *      Compose the EAP-MD5 packet out of the data structure,
289          *      and free it.
290          */
291         eaptnc_compose(handler->eap_ds, reply);
292     
293     handler->stage = AUTHENTICATE;
294     
295         eaptnc_free(&packet);
296         return 1;
297 }
298
299 /*
300  *      Detach the EAP-TNC module.
301  */
302 static int tnc_detach(void *arg)
303 {
304         free(arg);
305         return 0;
306 }
307
308
309 static CONF_PARSER module_config[] = {
310         { "vlan_access", PW_TYPE_STRING,
311           offsetof(rlm_eap_tnc_t, vlan_access), NULL, NULL },
312         { "vlan_isolate", PW_TYPE_STRING,
313           offsetof(rlm_eap_tnc_t, vlan_isolate), NULL, NULL },
314         { "libtns.so", PW_TYPE_STRING,
315           offsetof(rlm_eap_tnc_t, tnc_path), NULL,
316         "/usr/local/lib/libTNCS.so"},
317
318         { NULL, -1, 0, NULL, NULL }           /* end the list */
319 };
320
321 /*
322  *      Attach the EAP-TNC module.
323  */
324 static int tnc_attach(CONF_SECTION *cs, void **instance)
325 {
326         rlm_eap_tnc_t *inst;
327
328         inst = malloc(sizeof(*inst));
329         if (!inst) return -1;
330         memset(inst, 0, sizeof(*inst));
331
332         if (cf_section_parse(cs, inst, module_config) < 0) {
333                 tnc_detach(inst);
334                 return -1;
335         }
336
337         
338         if (!inst->vlan_access || !inst->vlan_isolate) {
339                 radlog(L_ERR, "rlm_eap_tnc: Must set both vlan_access and vlan_isolate");
340                 tnc_detach(inst);
341                 return -1;
342         }
343
344         *instance = inst;
345         return 0;
346 }
347
348 /*
349  *      The module name should be the only globally exported symbol.
350  *      That is, everything else should be 'static'.
351  */
352 EAP_TYPE rlm_eap_tnc = {
353         "eap_tnc",
354         tnc_attach,                     /* attach */
355         tnc_initiate,                   /* Start the initial request */
356         NULL,                           /* authorization */
357         tnc_authenticate,               /* authentication */
358         tnc_detach                      /* detach */
359 };