OSDN Git Service

fee23615473428bf1132e03fc9a0977a21ca63d5
[opengatem/opengatem.git] / mdsrc / opengatemd.c
1 /**************************************************
2 OpengateM - a MAC address authentication system
3 daemon main module 
4
5 Copyright (C) 2011 Opengate Project Team
6 Written by Yoshiaki Watanabe
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
22 Email: watanaby@is.saga-u.ac.jp
23 **************************************************/
24
25 /*************************************
26 This program uses following data structures to maintain the state of each terminal.
27  * MAC address is used as the main key for recognizing the user terminal.
28  * IP address is kept only for information.
29  * The packet cache uses the MAC-IP pair, as to record all IPs. 
30
31    1. Packet Check Cache
32        To speed up the packet check process, the address checked once 
33          is ignored for a while.
34        The cache (key: MAC&IP address pair) is used to decide 
35          the necessity of checking.
36        The cache maintains the information of arrived packets 
37          by using hash-table and queue in the memory of the local machine.
38    2. Session Table
39        The table (key: MAC address) maintains temporal information 
40          of terminals allowing the use of network at now. 
41        The data are stored in the work DB(SQLite3) on the local machine
42        (table:sessionmd in db:opengatemd.db).
43    3. MAC-IP pair Table
44        The table maintains the MAC-IP pairs where an active session has the MAC
45        (One MAC corresponds to plural IPs(ipv4 and ipv6))
46        The data are stored in the work DB on the local machine.
47        (table:macippair in db:opengatemd.db)
48        The mac-ip pair table in management db is used only for logging.
49    4. MAC address registration Table
50        The table (key: MAC address) maintains MAC addresses of terminals
51          and the owners' information.
52        The data are stored in the management DB(MySQL) on a central machine
53          and accessed via network.
54        (table: macaddrs in db:opengatem)
55    5. Cache of MAC address registration Table
56        As the network DB access is time-consuming, the access cache 
57          (key: MAC address) is maintained in the memory of the local machine.
58
59
60 *************************************/
61
62 #include        "opengatemd.h"
63
64 void sigHupHandler(int sig);
65 void sigIoHandler(int sig);
66 void sigTermHandler(int sig);
67
68 int sigHupArrived=FALSE;
69 int sigIoArrived=FALSE;
70
71 /*********************/
72 /*  main routine     */
73 /*********************/
74 int  main(int argc, char **argv)
75 {
76   char ipAddress[ADDRMAXLN];  /* packet source ip address */
77   char macAddress[ADDRMAXLN]; /* packet source mac address */
78     unsigned char macAndIpAddressRaw[ADDRMAXLN];/* mac&ip addr in raw form */
79   /* above is network raw binary concatenating MAC(6bytes) and IP(4or16Bytes) */
80   int addrLen;               /* ip address byte length 6+4 or 6+16 */         
81
82   char userId[USERMAXLN];     /* user id related to the mac address */
83   char extraId[USERMAXLN];    /* optional id for the user */
84   int macFound;               /* flag: mac address is resistered in db */
85   int sessionFound;           /* flag: session for the address exists */
86   int consoleMode=FALSE;      /* flag: start with console mode option */
87   int endServiceMode=FALSE;   /* flag: start with end service option */
88   int reloadServiceMode=FALSE;/* flag: start with reload option */ 
89   int stopServiceMode=FALSE;  /* flag: start with stop service option */ 
90   int showVersionMode=FALSE;  /* flag: show version */
91   int helpMode=FALSE;         /* flag: start with help mode */
92
93   int ttl;                    /* packet ttl(time to live) or hlim(hop limit) */
94   int i;                      /* for loop control */
95   int uselessCheckTime=0;     /* the last time for useless check */
96   int checkInterval;          /* useless session check interval */
97   int isNatOrRouter=FALSE;    /* the packet is sent from nat/router */
98   char macAddrInUdp[ADDRMAXLN]; /* mac address sent from udp port */
99   int ret;                         /* return code */
100   char clientIpAddress[ADDRMAXLN]; /* udp client ip address */
101
102   /* analyze arguments */
103   for(i=1; i<argc; i++){
104     if(*argv[i]=='-' && *(argv[i]+1)=='c') consoleMode=TRUE;
105     else if(*argv[i]=='-' && *(argv[i]+1)=='e') endServiceMode=TRUE;
106     else if(*argv[i]=='-' && *(argv[i]+1)=='r') reloadServiceMode=TRUE;
107     else if(*argv[i]=='-' && *(argv[i]+1)=='s') stopServiceMode=TRUE;
108     else if(*argv[i]=='-' && *(argv[i]+1)=='v') showVersionMode=TRUE;
109     else helpMode=TRUE;
110   }
111
112   /* if unknown args, show help and exit */
113   if(helpMode){
114     ShowHelp(argv[0]);
115     terminateProg(0);
116   }
117
118   /* if '-v' option, show 'makedir' and exit */
119   /* makedir is the directory where the make command is executed */
120   /* the directory can include the string such as 'opengatem0.9.9-trial'. */
121   if(showVersionMode){
122     printf("makedir: %s\n", MAKEDIR);
123     terminateProg(0);
124   }
125
126   /* drop root privilege */
127   seteuid(getuid());
128
129   /* prepare config file */
130   if(OpenConfFile()==-1) terminateProg(0);
131  
132   /* start log */
133   errToSyslog(atoi(GetConfValue("Syslog/Enable")));
134   openlog(GetConfValue("MacCheckDaemon"), LOG_PID, atoi(GetConfValue("Syslog/Facility")));
135
136   /* initialize config */
137   InitConf();
138
139   /* if reloadServiceMode, send HUP to resident daemon */
140   if(reloadServiceMode) ReloadDaemon();
141
142   /* if stopServiceMode, stop resident deamon and this process */
143   if(stopServiceMode){
144     KillDaemon();
145     terminateProg(0);
146   }
147
148   /* if endServiceMode, stop deamon */
149   /*  and close sessions(about 30-lines below in this source) */
150   if(endServiceMode) KillDaemon();
151
152   /* if not console mode, run as daemon */
153   if(consoleMode)  errToSyslog(FALSE);    /* console mode (no syslog) */
154   else   Daemonize();                     /* daemon mode (fork&exit) */
155
156   /* set lock file to prevent overlapped execution */
157   if(!LockDaemonLockFile()){
158     terminateProg(0);
159   }
160
161   /* set signal functions */
162   signal(SIGHUP, sigHupHandler);
163   signal(SIGTERM, sigTermHandler);
164
165   /* initializing */
166   InitPcap();
167   InitCache();
168   InitMacCache();
169   InitWorkDb();
170   if(!InitMngDb()) terminateProg(0);
171   InitTtlCheck();
172   InitWatchlistCache();
173   PrepareUdpPort(sigIoHandler); /* UDP port runs in asynchronous mode */
174
175   /* if endServiceMode is indicated, close all sessions, and exit */
176   if(endServiceMode){
177     DelAllSessions();
178     terminateProg(0);
179   }
180   
181   /* set check interval and remove useless residue sessions */
182   checkInterval=atoi(GetConfValue("UselessCheckInterval"));
183   uselessCheckTime=time(NULL);
184   DelUselessSessions();
185
186   /*** enter infinite loop of packet inspection ***/
187   while(1){
188
189     /* if sig-hup flag is on, reload this program */
190     /* sig-hup flag is set in sigHupHandler, when HUP signal arrives */
191     if(sigHupArrived)execlp(argv[0], argv[0], NULL);
192
193     /* if sig-IO flag is on, get mac addresses from UDP port */
194     /* and remove the addresses from caches (renew info at next capture) */
195     /* sig-IO flag is on in sigIoHandler, when a packet arrives. */
196     /* the packet includes renewed mac addresses sent from management program. */ 
197     if(sigIoArrived){
198       sigIoArrived=FALSE;
199       while(GetDataFromUdpPort(macAddrInUdp, ADDRMAXLN, clientIpAddress)>0){
200         if(IsUdpClientTrusted(clientIpAddress)){
201           DelCacheItem(macAddrInUdp,"");
202           DelMacCacheItem(macAddrInUdp);
203         }
204       }
205     }      
206
207     /* get one packet from pcap */
208     ret=GetNextPacketFromPcap(macAndIpAddressRaw, &addrLen, &ttl);
209
210     /* if no packet */
211     if(ret==0){
212  
213       /* when long time passed from previous check, check&delete useless sessions */
214       if( time(NULL) - uselessCheckTime > checkInterval ){
215         uselessCheckTime = time(NULL);
216         DelUselessSessions();
217       }
218
219       /* and return to loop top */
220       continue;
221     }
222
223     /* ignore not-ip packet */
224     if(ret==-1) continue;
225
226     /* ignore local packet */
227     if(ttl<=1) continue;
228    
229     /* ignore the packet tha is checked recently */
230     if( IsRecentlyCheckedAddress(macAndIpAddressRaw, addrLen) ) continue;
231
232     /**** no more processing for recently checked packets ****/
233     /**** only cache timeout packets proceeds to below ****/
234
235     /* convert address from network-raw form to presentation form */
236     ConvertMacFromRawToDisplay(macAndIpAddressRaw, macAddress);
237     ConvertIpFromRawToDisplay(macAndIpAddressRaw+MACADDRLN,
238                               addrLen-MACADDRLN, ipAddress);
239
240     /* if the address is included in watchlist, report the detection */
241     /* watchlist is the address list needing specific reporting */
242     /* (e.g., suspicion of illegal access). no need for normal operation */
243     if(IsFoundInWatchlistCache(macAddress)==TRUE){
244       err_msg_warn("WARN: find mac=%s ip=%s", macAddress, ipAddress);
245     }
246
247     /* check NAT/Router and save info to db */
248     /* when NAT/Router is inserted, the acquired MAC is the address of NAT/Router */
249     /* thus the MAC based control is failed */
250     /* at now, not denying the packet but reporting the detection. */
251     isNatOrRouter=IsSentViaNatOrRouter(ipAddress, macAddress, ttl);
252     if(isNatOrRouter) PutLogAtNatOrRouter(isNatOrRouter,ipAddress,macAddress,ttl);
253     PutMacInfoToWorkDb(macAddress, ttl, isNatOrRouter);
254
255     /*** get the status of the terminal from session table and DB ***/
256
257     /* search the captured address in session table */
258     sessionFound = IsMatchedSessionFound(macAddress);
259
260     /* search the captured address in cache of MAC DB */ 
261     macFound = QueryMacFromMacCache(macAddress, userId, extraId);
262
263     /* if failed, access MAC DB */
264     if(macFound==ERROR){
265       macFound = QueryMacFromMngDb(macAddress, userId, extraId);
266       
267       /* if db access is failed, set not-found and retry at next capture */
268       /* if db access is successed (found or not-found), save it to cache */ 
269       if(macFound==ERROR) macFound=FALSE;
270       else AddMacCacheItem(macAddress, userId, extraId, macFound);
271     }
272
273     /*** depending the status, add/del/renew the session ***/
274
275     /* if valid mac and no session, start session */
276     if(macFound && !sessionFound){
277         AddSession(macAddress, userId, extraId);
278
279         /* save MAC and IP address pair to work db */
280         SetMacIpPair(macAddress, ipAddress, userId, extraId);
281     }
282
283     /* if no mac and started session, stop session */
284     /* (MAC-IP pair in work db is removed in delSession) */
285     if(!macFound && sessionFound){
286       DelSession(macAddress);
287     }
288
289     /* if valid mac and started session, renew check time */
290     if(macFound && sessionFound){
291
292       /* in normal case, ipfw rule exists. */
293       if(IsMacAddressFoundInIpfw(macAddress)){
294         RenewSession(macAddress);
295       }
296       
297       /* when no ipfw rule exists, reset the session */
298       else{
299         DelSession(macAddress);
300         AddSession(macAddress, userId, extraId);        
301       }
302
303       /* save MAC-IP pair to work db (new ip is found for a mac in session).  */
304       SetMacIpPair(macAddress, ipAddress, userId, extraId);
305     }
306
307     /*  check useless sessions at some interval */
308     /* (MAC and IP pairs are removed in stop session) */
309     if( time(NULL) - uselessCheckTime > checkInterval ){
310       uselessCheckTime = time(NULL);
311       DelUselessSessions();
312     }
313   }
314   /*** end of infinite loop ***/
315
316   /* clear data structures (can't reach here, but coded for debugging) */
317   FreeCache();
318   FreeMacCache();
319   FreeWatchlistCache();
320   ClosePcap();
321   CloseConfFile();
322   CloseMngDb();
323
324   terminateProg(0);
325   return 0;
326 }
327
328 /********************************
329  open and lock proc lock file 
330  to prevent overlapped daemon exec
331 ********************************/
332 int lockDaemonLockFile(void){
333   
334   static int lockFd;
335   char str[WORDMAXLN];
336  
337   /* open process lock file */
338   lockFd = open(GetConfValue("DaemonLockFile"), O_RDWR|O_CREAT, 0644);
339   
340   /* if cannot open or cannot lock, return false */
341   if(lockFd<0){
342     err_msg("ERR at %s#%d: cannot open daemon lock file:%s",__FILE__,__LINE__,
343             lockFd);
344     return FALSE;
345   }
346   if(lockf(lockFd, F_TLOCK, 0)<0) return FALSE;
347
348   /* record pid */
349   snprintf(str, WORDMAXLN, "%d", getpid());
350   write(lockFd, str, strlen(str)+1);
351
352   /* normally locked */
353   return TRUE;
354 }
355
356
357 /******************************
358 daemonize the process
359 ******************************/
360 void daemonize(void){
361
362   int pid;
363   int i;
364
365   /* detach from parent */
366   pid = fork();
367   if(pid < 0){
368    err_msg("ERR at %s#%d: cannot fork",__FILE__,__LINE__);
369     terminateProg(0);
370   }
371
372   /* parent process exits */
373   if(pid > 0) exit(0);
374
375   /* child process runs from here */
376   /* set new prosess group */
377   setsid();
378
379   /* close all file descriptors */
380   for(i=getdtablesize(); i>=0; i--) close(i);
381
382   /* connect stdin/out/err to null */
383   i=open("/dev/null", O_RDWR); dup(i); dup(i);
384
385   /* set newly created file permission */
386   /* umask(033);   */
387
388   if(debug>0) err_msg("INFO: Deamon started");
389 }
390
391 /****************************************
392 show help message
393 ****************************************/
394 void showHelp(char* procName){
395    printf("\n");
396    printf("firewall control by mac address\n");
397    printf(" see detail in Opengate Homepage\n");
398    printf("\n");
399    printf(" format: %s [arg] \n", procName);
400    printf(" arg : -c = run on console (run as daemon in default)\n");
401    printf("     : -e = close all sessions and end service\n");
402    printf("     : -r = reload deamon(not close sessions)\n");
403    printf("     : -s = stop deamon (not close sessions)\n");
404    printf("     : -v = show make dir to check version\n");
405  }
406
407 /*********************************
408 signal handler for SIGIO
409 *********************************/
410 void sigIoHandler(int sig){
411
412   sigIoArrived=TRUE;
413 }
414
415 /*********************************
416 signal handler for SIGHUP
417 *********************************/
418 void sigHupHandler(int sig){
419
420   if(debug>0)err_msg("INFO: Deamon receives HUP signal");
421   sigHupArrived=TRUE;
422 }
423
424 /*********************************
425 signal handler for SIGTERM
426 *********************************/
427 void sigTermHandler(int sig){
428
429   if(debug>0)err_msg("INFO: Deamon receives TERM signal");
430   terminateProg(0);
431 }
432
433 /*********************************
434 kill daemon process
435 *********************************/
436 void killDaemon(void){
437   FILE* file=NULL;
438   struct stat st;
439   int pid=0;
440   char* lockFileMd;
441
442   /* get lock file name */
443   lockFileMd=GetConfValue("DaemonLockFile");
444
445   /* if lock file does not exist, skip */
446   if(stat(lockFileMd, &st)!=0){
447     ;
448   }
449
450   /*else (file exists), read pid from the file */
451   else if((file=fopen(lockFileMd, "r"))==NULL){
452     err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
453             ,lockFileMd);
454   }
455   else if(fscanf(file, "%d", &pid)==0){
456     err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
457             ,lockFileMd);
458   }
459   if(file!=NULL) fclose(file);
460
461   /* send kill signal to the 'pid' process */
462   if(pid!=0){
463     seteuid(0);   /* get root privilege */
464     kill(pid, SIGKILL);
465     seteuid(getuid());   /* drop root privilege */
466   }
467
468   /* remove the lockfile */
469   remove(lockFileMd);
470 }
471
472 /*********************************
473 reload daemon process
474 *********************************/
475 void reloadDaemon(void){
476   FILE* file=NULL;
477   struct stat st;
478   int pid=0;
479   char* lockFileMd;
480
481   /* get lock file name */
482   lockFileMd=GetConfValue("DaemonLockFile");
483
484   /* if lock file does not exists, skip */
485   if(stat(lockFileMd, &st)!=0){
486     ;
487   }
488
489   /* else (file exists), read pid from the file */
490   else if((file=fopen(lockFileMd, "r"))==NULL){
491     err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
492             ,lockFileMd);
493   }
494   else if(fscanf(file, "%d", &pid)==0){
495     err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
496             ,lockFileMd);
497   }
498   if(file!=NULL)fclose(file);
499
500   /* send hup signal to the 'pid' process */
501   if(pid!=0){
502     seteuid(0);   /* get root privilege */
503     kill(pid, SIGHUP);
504     seteuid(getuid());   /* drop root privilege */
505   }
506 }
507
508 /*************************************
509 put out end message and exit 
510 *************************************/
511 void terminateProg(int ret){
512
513   /* close work db (opengatemd.db) */
514   FinalizeWorkDb();
515
516   if(debug>0) err_msg("INFO: Terminated");
517   exit(ret);
518 }
519
520
521 /************************************
522  routines for debugging output
523  ***********************************/
524 int LockDaemonLockFile(void){
525   int ret;
526   if(debug>1) err_msg("DEBUG:=>lockDaemonLockFile( )");
527   ret = lockDaemonLockFile();
528   if(debug>1) err_msg("DEBUG:(%d)<=lockDaemonLockFile( )",ret);
529   return ret;
530 }
531
532 void Daemonize(void){
533   if(debug>1) err_msg("DEBUG:=>daemonize( )");
534   daemonize();
535   if(debug>1) err_msg("DEBUG:<=daemonize( )");
536 }
537
538 void ShowHelp(char* procName){
539   if(debug>1) err_msg("DEBUG:=>showHelp(%s)", procName);
540   showHelp(procName);
541   if(debug>1) err_msg("DEBUG:<=showhelp( )");
542 }
543 void KillDaemon(void){
544   if(debug>1) err_msg("DEBUG:=>killDaemon( )");
545   killDaemon();
546   if(debug>1) err_msg("DEBUG:<=killDaemon( )");
547 }
548 void ReloadDaemon(void){
549   if(debug>1) err_msg("DEBUG:=>reloadDaemon( )");
550   reloadDaemon();
551   if(debug>1) err_msg("DEBUG:<=reloadDaemon( )");
552 }