OSDN Git Service

589c0f546f2ef07cb0f7ccf6f621d1886147cfd6
[opengatem/opengatem.git] / mngsrc / ipfw.c
1 /**************************************************
2 OpengateM - MAC address authentication system 
3
4  module for Controling ipfw (firewall) 
5
6 Copyright (C) 2011 Opengate Project Team
7 Written by Yoshiaki Watanabe
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 Email: watanaby@is.saga-u.ac.jp
24 **************************************************/
25 #include "opengatemmng.h"
26
27 char ruleNumber[WORDMAXLN];  /* ipfw rule number in string form */
28
29 static void sigFunc(int signo);
30
31 /******************************************************************/
32 /* open gate for clientAddr                                       */
33 /*  if forced=TRUE, ignore checking for address overlapping       */
34 /*      return=ruleNumber                                         */
35 /*      return=(-1)*ruleNumber: can't open because of overlapping */
36 /******************************************************************/
37 int openClientGate(char *clientAddr, int forced, char* userId, char* extraId, char* macAddress)
38 {
39   int fd=0;
40   int retNum;
41   struct stat st;
42   char* lockFile;
43   int lockFileExist=TRUE;
44   char userIdLong[WORDMAXLN];
45
46   Sigfunc *defaultSigFunc;
47
48   /* prepare userid-long as [userid@extraid] */
49   strlcpy(userIdLong, userId, WORDMAXLN);
50   if(!isNull(extraId)){
51     strlcat(userIdLong, "@", WORDMAXLN);
52     strlcat(userIdLong, extraId, WORDMAXLN);
53   }
54
55   /* exclusive exec of ipfw to avoid overlapped rule number */
56   /**** prepare ****/
57   /* search lock file */
58   lockFile=GetConfValue("LockFile");
59   if(stat(lockFile, &st)!=0) lockFileExist=FALSE;
60   else lockFileExist=TRUE;
61
62   /* if lock file exists, exec lock (if not found, lock is ignored) */
63   if(lockFileExist){
64     
65     /* open lockfile */
66     fd=open(lockFile, O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
67     if(fd==-1){
68       err_msg("ERR at %s#%d: lockfile open error",__FILE__,__LINE__);
69       return -1;
70     } 
71
72     /* set lock timeout (at long time locking, lock is canceled by SIGALRM) */
73     if((defaultSigFunc=signal(SIGALRM, sigFunc))==SIG_ERR){
74       err_msg("ERR at %s#%d: set sig alarm error",__FILE__,__LINE__);
75       Close(fd);
76       return 1;
77     }
78     alarm(atoi(GetConfValue("LockTimeout")));
79     
80     /* lock */
81     if(Lock(fd)<0){
82       err_msg("ERR at %s#%d: lock error",__FILE__,__LINE__);
83       Close(fd);
84       return -1;
85     }
86
87     /* when other process executes here, this process waits in lock() */
88     /* returning from lock() means that exclusive execution is possible */
89     /* then reset timeout signal and go to main processing */
90     signal(SIGALRM, defaultSigFunc);
91     alarm(0);
92   }
93
94   /**** read rules ****/
95   if((retNum=GetRuleNumber(clientAddr, forced))<0){
96     
97     /* if fail, then unlock and return */
98     if(lockFileExist){
99       Unlock(fd);
100       Close(fd);
101     }
102     return retNum; /* perhaps aleady registered addr is -retNum */
103   }
104
105   /**** write rules ****/
106   /**** if perl script control flag is set in conf file, use perl script */
107   /* (not recommended) */
108   if(atoi(GetConfValue("IpfwScript/Enable"))){
109     if(Systeml(1, GetConfValue("IpfwScript/Path"),GetConfValue("IpfwPath"),
110                ruleNumber,clientAddr,userIdLong,macAddress,"-",
111                GetConfValue("IpfwTagNumber"),(char *)0) != 0){
112       err_msg("ERR at %s#%d: exec script error",__FILE__,__LINE__);
113       retNum=1;  /* abnormal */
114     }
115   }
116
117   /**** else, direct control of firewall (default) ***************/
118   else{
119     
120     /********** add outgoing ipfw rule for the client (in IP level) *************/
121     /* eg.(ipfw -q add 10000 count tag 123 ip from 192.168.1.10 to any //wata@guest) */
122     if(Systeml(1, GetConfValue("IpfwPath"),"-q","add",ruleNumber,
123                "count","tag",GetConfValue("IpfwTagNumber"),
124                "ip","from",clientAddr,"to","any",
125                "//", userIdLong, (char *)0) != 0){
126       err_msg("ERR at %s#%d: exec ipfw add error",__FILE__,__LINE__);
127       retNum=1;  /* abnormal */
128     }
129
130     /********** add incoming ipfw rule for the client *************/
131     /* eg.(ipfw -q add 10000 count tag 123 ip from any to 192.168.1.10 //wata@guest) */
132     if(Systeml(1, GetConfValue("IpfwPath"),"-q","add",ruleNumber,
133                "count","tag",GetConfValue("IpfwTagNumber"),
134                "ip","from","any","to",clientAddr,
135                "//", userIdLong, (char *)0) != 0){
136       err_msg("ERR at %s#%d: exec ipfw add error",__FILE__,__LINE__);
137       retNum=1; /* abnormal */
138     }
139   }
140
141   /* unlock */
142   if(lockFileExist){
143     Unlock(fd);
144     Close(fd);
145   }
146   
147   return retNum;
148 }
149
150
151 /*****************************************************************/
152 /* close gate for clientAddr for the rule number                 */
153 /*****************************************************************/
154 void closeClientGate(int ruleNumber)
155 {
156   int count;
157   char ruleNumberStr[WORDMAXLN];
158
159   snprintf(ruleNumberStr, WORDMAXLN, "%d", ruleNumber); /* to string */
160
161   /* count rule */
162   count=CountRuleNumber(ruleNumber);
163
164   if(count>0){
165     /* exec ipfw del */
166     /* [ipfw del rule] deletes all rule of the rule number at one call */
167     if(Systeml(1, GetConfValue("IpfwPath"),"delete",ruleNumberStr,(char *)0) != 0){
168       err_msg("ERR at %s#%d: exec ipfw del error",__FILE__,__LINE__);
169     }
170   }
171 }
172
173 /**************************************
174  get unused ipfw rule number       
175  error if addr is already in rules  
176  if forced=TRUE, ignore checking for address overlapping
177  return value ret>0: acquired rule number that can be used 
178               ret=-1: no rule number available 
179               ret=-2: some system error occured 
180               ret=-num: the ip address is already registered in rule 'num' 
181 **************************************/
182 int getRuleNumber(char *clientAddr, int forced)
183 {
184   FILE *fpipe;
185   char buf[BUFFMAXLN];
186   int num,newNum,readinNum;
187   int ipfwmin;
188   int ipfwmax;
189   int ipfwinterval;
190   int portStatus;
191   int fileStatus;
192   char* p;
193
194   enum status {NORMAL, ABNORMAL, FOUND, NOTFOUND, DUP};
195
196   /* exec ipfw list and open pipe */
197   if((fpipe=Popenl(1, "r", GetConfValue("IpfwPath"),"list",(char *)0)) == NULL){ 
198       err_msg("ERR at %s#%d: exec ipfw list error",__FILE__,__LINE__);
199   }
200   
201   /* search unused rule number in the list read from pipe */
202   /* check duplication of clientAddr to existing rules */
203
204   newNum=-1;
205   readinNum=0;
206   portStatus=NOTFOUND;
207   fileStatus=NORMAL;
208
209   /* get rule range from config */
210   ipfwmin=atoi(GetConfValue("IpfwRule/Min"));
211   ipfwmax=atoi(GetConfValue("IpfwRule/Max"));
212   ipfwinterval=atoi(GetConfValue("IpfwRule/Interval"));
213
214   /* each port is checked whether it can be used for new rule or not */
215   for(num=ipfwmin;num<=ipfwmax;num+=ipfwinterval){
216
217     /* skip rules smaller than num */
218     while(readinNum<num){
219       if(fgets(buf, BUFFMAXLN, fpipe)==NULL){
220         if(feof(fpipe)==1) fileStatus=EOF; 
221         else fileStatus=ABNORMAL;
222         break;
223       }
224       if( sscanf(buf, "%d", &readinNum) !=1 ){
225         err_msg("ERR at %s#%d: abnormal ipfw response[ %s ]",
226                 __FILE__,__LINE__, buf);
227         fileStatus=ABNORMAL; /* abnormal responsem exit internal loop */
228         break;
229       }
230     }
231
232     if(fileStatus==ABNORMAL){
233       /* abnormal file proc, exit external loop */ 
234       break;
235     }
236
237     if(fileStatus==EOF){
238       /* EOF before reading a rule that is larger or equal to num */
239       /* it means that num can be used for new client */
240       portStatus=FOUND;
241       newNum=num;
242       break;
243     }
244
245     /* at this point, readinNum is larger or equal to num */
246     /* check number duplication */
247     if(readinNum==num){
248
249       /* if clientAddr found in the existing rule, then duplicate err exit */
250       /* if in forced mode, ignore the checking */
251       if(!forced){
252         if(((p=(char*)strstr(buf+1,clientAddr))!=NULL)
253            && isspace(*(p-1))
254            && !isalnum(*(p+strlen(clientAddr)))){
255           newNum=num;
256           portStatus=DUP;
257           break;
258         } else{
259           continue;
260         }
261       }
262       continue;
263     }
264  
265     /* at this point, readNum is larger than num */
266     /* it means that num can be used for new client */
267     newNum=num;
268     portStatus=FOUND;
269
270     break;
271   }
272   
273   /* close pipe */
274   Pclose(fpipe);
275     
276   if(fileStatus==ABNORMAL){
277     err_msg("ERR at %s#%d: abnormal ipfw response ",__FILE__,__LINE__);
278     return -2;
279   }
280   if(portStatus==NOTFOUND){
281     err_msg("ERR at %s#%d: cannot get unused ipfw number",__FILE__,__LINE__);
282     return -1;
283   }
284   if(portStatus==DUP){
285     snprintf(ruleNumber, WORDMAXLN, "%d", newNum); /* to string */
286     return -newNum;
287   }
288
289   snprintf(ruleNumber, WORDMAXLN, "%d", newNum); /* to string */
290   return newNum;
291 }
292
293 /*******************************
294  get packet count from ipfw  
295 *******************************/
296 int getPacketCount(int ruleNumber)
297 {
298   FILE *fpipe;
299   char buf[BUFFMAXLN];
300   int rule;
301   int packets,packetsSum;
302   char ruleNumberStr[WORDMAXLN];
303
304   snprintf(ruleNumberStr, WORDMAXLN, "%d", ruleNumber); /* to string */
305
306   /* exec proc */
307   if((fpipe=Popenl(1, "r", GetConfValue("IpfwPath"),"-a","list",ruleNumberStr,(char *)0)) == NULL){ 
308     err_msg("ERR at %s#%d: exec ipfw -a list error",__FILE__,__LINE__);
309   }
310
311   /* search unused number in the list read from pipe */
312   packetsSum=0;
313     
314   while(fgets(buf, BUFFMAXLN, fpipe)!=NULL){
315     sscanf(buf, "%d %d", &rule, &packets);   /* get packet count */
316     packetsSum+=packets;
317   }
318
319   /* close pipe */
320   Pclose(fpipe);
321
322   return packetsSum;
323 }
324
325 /**********************************************
326  get rule count registed to a rule number   
327 **********************************************/
328 int countRuleNumber(int ruleNumber)
329 {
330   FILE *fpipe;
331   char buf[BUFFMAXLN];
332   int ruleCount;
333   char ruleNumberStr[WORDMAXLN];
334
335   snprintf(ruleNumberStr, WORDMAXLN, "%d", ruleNumber); /* to string */
336
337   /* exec proc */
338   if((fpipe=Popenl(1, "r", GetConfValue("IpfwPath"),"list",ruleNumberStr,(char *)0)) == NULL){ 
339     err_msg("ERR at %s#%d: exec ipfw list error",__FILE__,__LINE__);
340   }
341   
342   /* count line read from pipe */
343   ruleCount = 0;
344   while(fgets(buf, BUFFMAXLN, fpipe)!=0) ruleCount++;
345
346   /* close pipe */
347   Pclose(fpipe);
348
349   return ruleCount;
350 }
351
352 /**********************************************
353  function called by signal int              
354 **********************************************/
355 static void sigFunc(int signo)
356 {
357   return;
358 }
359
360
361 /**********************************************
362 get rule numbers table from ipfw rule set
363 **********************************************/
364 int getRuleTableFromIpfw(DB* ruleTable){
365
366   DBT hashKey;
367   DBT hashVal;
368   FILE *fpipe;
369   char buf[BUFFMAXLN];
370   int ruleNumber;
371   char clientAddr[ADDRMAXLN];
372   char *p;
373   int ipfwmin;
374   int ipfwmax;
375   int ipfwinterval;
376   int resultFlag=FALSE;
377   
378   /* exec ipfw list and open pipe */
379   if((fpipe=Popenl(1, "r", GetConfValue("IpfwPath"),"list",(char *)0)) == NULL){ 
380     err_msg("ERR at %s#%d: exec ipfw list error",__FILE__,__LINE__);
381   }
382   
383   /* get rule range from config */
384   ipfwmin=atoi(GetConfValue("IpfwRule/Min"));
385   ipfwmax=atoi(GetConfValue("IpfwRule/Max"));
386   ipfwinterval=atoi(GetConfValue("IpfwRule/Interval"));
387   
388   /* get ipfw rule line from pipe */
389   while(fgets(buf, BUFFMAXLN, fpipe)!=NULL){
390
391     /* get ruleNumber(=leftmost number) */
392     /* 10000 count tag 123 ip from 192.168.0.2 to any .... */
393     /* 10000 count tag 123 ip from any to 192.168.0.2 .... */
394     if( sscanf(buf, "%d", &ruleNumber) !=1 ) continue;
395
396     /* check the rule number range */
397     if(ruleNumber < ipfwmin)continue;
398     if(ruleNumber > ipfwmax)break;
399
400     /* get clientAddr(=after [from]) in the line */
401     if((p=(char*)strstr(buf, "from"))==NULL) continue;
402     if( sscanf((p+4), "%s", clientAddr) != 1 ) continue;
403     if( (strchr(clientAddr,'.')==NULL) && (strchr(clientAddr,':')==NULL) )continue;
404       
405     /* put to the hash table */
406     resultFlag=TRUE;
407     hashVal.data = &ruleNumber;
408     hashVal.size = sizeof(int);    
409     hashKey.data = clientAddr;
410     hashKey.size = strlen(clientAddr)+1;
411     if(ruleTable->put(ruleTable, &hashKey, &hashVal, 0) == -1) {
412       err_msg("ERR at %s#%d: fail to put into hash table",__FILE__,__LINE__);
413     }
414   }
415
416   /* close pipe */
417   Pclose(fpipe);
418
419   return resultFlag;
420 }
421
422
423 /*********************************************
424  routines for debugging output
425 **********************************************/
426 int GetRuleNumber(char *clientAddr, int forced)
427 {
428   int ret;
429
430   if(debug>1) err_msg("DEBUG:=>getRuleNumber(%s,%d)",clientAddr,forced);
431   ret=getRuleNumber(clientAddr, forced);
432   if(debug>1) err_msg("DEBUG:(%d)<=getRuleNumber( )",ret);
433
434   return ret;
435 }
436
437 int OpenClientGate(char *clientAddr, int forced, char* userId, char* extraId,char* macAddress)
438 {
439   int ret;
440
441   if(debug>1) err_msg("DEBUG:=>openClientGate(%s,%d,%s,%s)",clientAddr,forced,userId,extraId,macAddress);
442   ret=openClientGate(clientAddr,forced,userId,extraId,macAddress);
443   if(debug>1) err_msg("DEBUG:(%d)<=openClientGate( )",ret);
444
445   return ret;
446 }
447
448 void CloseClientGate(int ruleNumber)
449 {
450   if(debug>1) err_msg("DEBUG:=>closeClientGate(%d)",ruleNumber);
451   closeClientGate(ruleNumber);
452   if(debug>1) err_msg("DEBUG:<=closeClientGate( )");
453 }
454
455
456 int GetPacketCount(int ruleNumber)
457 {
458   int ret;
459
460   if(debug>1) err_msg("DEBUG:=>getPacketCount(%d)",ruleNumber);
461   ret=getPacketCount(ruleNumber);
462   if(debug>1) err_msg("DEBUG:(%d)<=getPacketCount( )",ret);
463
464   return ret;
465 }
466
467 int CountRuleNumber(int ruleNumber)
468 {
469   int ret;
470   
471   if(debug>1) err_msg("DEBUG:=>countRuleNumber(%d)", ruleNumber);
472   ret=countRuleNumber(ruleNumber);
473   if(debug>1) err_msg("DEBUG:(%d)<=countRuleNumber( )",ret);
474   
475   return ret;
476 }
477
478 int GetRuleTableFromIpfw(DB* ruleTable){
479   int ret;
480   if(debug>1) err_msg("DEBUG:=>getRuleTableFromIpfw()");
481   ret=getRuleTableFromIpfw(ruleTable);
482   if(debug>1) err_msg("DEBUG:<=getRuleTableFromIpfw( )", ret);
483   return ret;
484 }