OSDN Git Service

restored optional log table in management db
[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   signalx(SIGHUP, sigHupHandler);
163   signalx(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   DelOldMacInfoInWorkDb();
186   DelOldSessionLogInMngDb();
187
188   /*** enter infinite loop of packet inspection ***/
189   while(1){
190
191     
192     /* if sig-hup flag is on, reload this program */
193     /* sig-hup flag is set in sigHupHandler, when HUP signal arrives */
194     if(sigHupArrived)execlp(argv[0], argv[0], NULL);
195
196     /* if sig-IO flag is on, get mac addresses from UDP port */
197     /* and remove the addresses from caches (renew info at next capture) */
198     /* sig-IO flag is on in sigIoHandler, when a packet arrives. */
199     /* the packet includes renewed mac addresses sent from management program. */ 
200     if(sigIoArrived){
201       sigIoArrived=FALSE;
202       while(GetDataFromUdpPort(macAddrInUdp, ADDRMAXLN, clientIpAddress)>0){
203         if(IsUdpClientTrusted(clientIpAddress)){
204           DelCacheItem(macAddrInUdp,"");
205           DelMacCacheItem(macAddrInUdp);
206         }
207       }
208     }      
209
210     /* get one packet from pcap */
211     ret=GetNextPacketFromPcap(macAndIpAddressRaw, &addrLen, &ttl);
212
213     /* if no packet */
214     if(ret==0){
215  
216       /* when long time passed from previous check, */
217       /* check&delete useless sessions and old macinfo */
218       if( time(NULL) - uselessCheckTime > checkInterval ){
219         uselessCheckTime = time(NULL);
220         DelUselessSessions();
221         DelOldMacInfoInWorkDb();
222         DelOldSessionLogInMngDb();
223       }
224
225       /* and return to loop top */
226       continue;
227     }
228
229     /* ignore not-ip packet */
230     if(ret==-1) continue;
231
232     /* ignore local packet */
233     if(ttl<=1) continue;
234    
235     /* ignore the packet tha is checked recently */
236     if( IsRecentlyCheckedAddress(macAndIpAddressRaw, addrLen) ) continue;
237
238     /**** no more processing for recently checked packets ****/
239     /**** only cache timeout packets proceeds to below ****/
240
241     /* convert address from network-raw form to presentation form */
242     ConvertMacFromRawToDisplay(macAndIpAddressRaw, macAddress);
243     ConvertIpFromRawToDisplay(macAndIpAddressRaw+MACADDRLN,
244                               addrLen-MACADDRLN, ipAddress);
245
246     /* if the address is included in watchlist, report the detection */
247     /* watchlist is the address list needing specific reporting */
248     /* (e.g., suspicion of illegal access). no need for normal operation */
249     if(IsFoundInWatchlistCache(macAddress)==TRUE){
250       err_msg_warn("WARN: find mac=%s ip=%s", macAddress, ipAddress);
251     }
252
253     /* check NAT/Router and save info to db */
254     /* when NAT/Router is inserted, the acquired MAC is the address of NAT/Router */
255     /* thus the MAC based control is failed */
256     /* at now, not denying the packet but reporting the detection. */
257     isNatOrRouter=IsSentViaNatOrRouter(ipAddress, macAddress, ttl);
258     if(isNatOrRouter) PutLogAtNatOrRouter(isNatOrRouter,ipAddress,macAddress,ttl);
259     PutMacInfoToWorkDb(macAddress, ttl, isNatOrRouter);
260
261     /*** get the status of the terminal from session table and DB ***/
262
263     /* search the captured address in session table */
264     sessionFound = IsMatchedSessionFound(macAddress);
265
266     /* search the captured address in cache of MAC DB */ 
267     macFound = QueryMacFromMacCache(macAddress, userId, extraId);
268
269     /* if failed, access MAC DB */
270     if(macFound==ERROR){
271       macFound = QueryMacFromMngDb(macAddress, userId, extraId);
272       
273       /* if db access is failed, set not-found and retry at next capture */
274       /* if db access is successed (found or not-found), save it to cache */ 
275       if(macFound==ERROR) macFound=FALSE;
276       else AddMacCacheItem(macAddress, userId, extraId, macFound);
277     }
278
279     /*** depending the status, add/del/renew the session ***/
280
281     /* if valid mac and no session, start session */
282     if(macFound && !sessionFound){
283         AddSession(macAddress, userId, extraId);
284
285         /* save MAC and IP address pair to work db */
286         SetMacIpPair(macAddress, ipAddress, userId, extraId);
287     }
288
289     /* if no mac and started session, stop session */
290     /* (MAC-IP pair in work db is removed in delSession) */
291     if(!macFound && sessionFound){
292       DelSession(macAddress);
293     }
294
295     /* if valid mac and started session, renew check time */
296     if(macFound && sessionFound){
297
298       /* in normal case, ipfw rule exists. */
299       if(IsMacAddressFoundInIpfw(macAddress)){
300         RenewSession(macAddress);
301       }
302       
303       /* when no ipfw rule exists, reset the session */
304       else{
305         DelSession(macAddress);
306         AddSession(macAddress, userId, extraId);        
307       }
308
309       /* save MAC-IP pair to work db (new ip is found for a mac in session).  */
310       SetMacIpPair(macAddress, ipAddress, userId, extraId);
311     }
312
313     /*  check useless sessions and old macinfo at some interval */
314     if( time(NULL) - uselessCheckTime > checkInterval ){
315       uselessCheckTime = time(NULL);
316       DelUselessSessions();
317       DelOldMacInfoInWorkDb();
318       DelOldSessionLogInMngDb();
319     }
320   }
321   /*** end of infinite loop ***/
322
323   /* clear data structures (can't reach here, but coded for debugging) */
324   FreeCache();
325   FreeMacCache();
326   FreeWatchlistCache();
327   ClosePcap();
328   CloseConfFile();
329   CloseMngDb();
330
331   terminateProg(0);
332   return 0;
333 }
334
335 /********************************
336  open and lock proc lock file 
337  to prevent overlapped daemon exec
338 ********************************/
339 int lockDaemonLockFile(void){
340   
341   static int lockFd;
342   char str[WORDMAXLN];
343  
344   /* open process lock file */
345   lockFd = open(GetConfValue("DaemonLockFile"), O_RDWR|O_CREAT, 0644);
346   
347   /* if cannot open or cannot lock, return false */
348   if(lockFd<0){
349     err_msg("ERR at %s#%d: cannot open daemon lock file:%s",__FILE__,__LINE__,
350             lockFd);
351     return FALSE;
352   }
353   if(lockf(lockFd, F_TLOCK, 0)<0) return FALSE;
354
355   /* record pid */
356   snprintf(str, WORDMAXLN, "%d", getpid());
357   write(lockFd, str, strlen(str)+1);
358
359   /* normally locked */
360   return TRUE;
361 }
362
363
364 /******************************
365 daemonize the process
366 ******************************/
367 void daemonize(void){
368
369   int pid;
370   int i;
371
372   /* detach from parent */
373   pid = fork();
374   if(pid < 0){
375    err_msg("ERR at %s#%d: cannot fork",__FILE__,__LINE__);
376     terminateProg(0);
377   }
378
379   /* parent process exits */
380   if(pid > 0) exit(0);
381
382   /* child process runs from here */
383   /* set new prosess group */
384   setsid();
385
386   /* close all file descriptors */
387   for(i=getdtablesize(); i>=0; i--) close(i);
388
389   /* connect stdin/out/err to null */
390   i=open("/dev/null", O_RDWR); dup(i); dup(i);
391
392   /* set newly created file permission */
393   /* umask(033);   */
394
395   if(debug>0) err_msg("INFO: Deamon started");
396 }
397
398 /****************************************
399 show help message
400 ****************************************/
401 void showHelp(char* procName){
402    printf("\n");
403    printf("firewall control by mac address\n");
404    printf(" see detail in Opengate Homepage\n");
405    printf("\n");
406    printf(" format: %s [arg] \n", procName);
407    printf(" arg : -c = run on console (run as daemon in default)\n");
408    printf("     : -e = close all sessions and end service\n");
409    printf("     : -r = reload deamon(not close sessions)\n");
410    printf("     : -s = stop deamon (not close sessions)\n");
411    printf("     : -v = show make dir to check version\n");
412  }
413
414 /*********************************
415 signal handler for SIGIO
416 *********************************/
417 void sigIoHandler(int sig){
418
419   sigIoArrived=TRUE;
420 }
421
422 /*********************************
423 signal handler for SIGHUP
424 *********************************/
425 void sigHupHandler(int sig){
426
427   if(debug>0)err_msg("INFO: Deamon receives HUP signal");
428   sigHupArrived=TRUE;
429 }
430
431 /*********************************
432 signal handler for SIGTERM
433 *********************************/
434 void sigTermHandler(int sig){
435
436   if(debug>0)err_msg("INFO: Deamon receives TERM signal");
437   terminateProg(0);
438 }
439
440 /*********************************
441 kill daemon process
442 *********************************/
443 void killDaemon(void){
444   FILE* file=NULL;
445   struct stat st;
446   int pid=0;
447   char* lockFileMd;
448
449   /* get lock file name */
450   lockFileMd=GetConfValue("DaemonLockFile");
451
452   /* if lock file does not exist, skip */
453   if(stat(lockFileMd, &st)!=0){
454     ;
455   }
456
457   /*else (file exists), read pid from the file */
458   else if((file=fopen(lockFileMd, "r"))==NULL){
459     err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
460             ,lockFileMd);
461   }
462   else if(fscanf(file, "%d", &pid)==0){
463     err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
464             ,lockFileMd);
465   }
466   if(file!=NULL) fclose(file);
467
468   /* send kill signal to the 'pid' process */
469   if(pid!=0){
470     seteuid(0);   /* get root privilege */
471     kill(pid, SIGKILL);
472     seteuid(getuid());   /* drop root privilege */
473   }
474
475   /* remove the lockfile */
476   remove(lockFileMd);
477 }
478
479 /*********************************
480 reload daemon process
481 *********************************/
482 void reloadDaemon(void){
483   FILE* file=NULL;
484   struct stat st;
485   int pid=0;
486   char* lockFileMd;
487
488   /* get lock file name */
489   lockFileMd=GetConfValue("DaemonLockFile");
490
491   /* if lock file does not exists, skip */
492   if(stat(lockFileMd, &st)!=0){
493     ;
494   }
495
496   /* else (file exists), read pid from the file */
497   else if((file=fopen(lockFileMd, "r"))==NULL){
498     err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
499             ,lockFileMd);
500   }
501   else if(fscanf(file, "%d", &pid)==0){
502     err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
503             ,lockFileMd);
504   }
505   if(file!=NULL)fclose(file);
506
507   /* send hup signal to the 'pid' process */
508   if(pid!=0){
509     seteuid(0);   /* get root privilege */
510     kill(pid, SIGHUP);
511     seteuid(getuid());   /* drop root privilege */
512   }
513 }
514
515 /*************************************
516 put out end message and exit 
517 *************************************/
518 void terminateProg(int ret){
519
520   /* close work db (opengatemd.db) */
521   FinalizeWorkDb();
522
523   if(debug>0) err_msg("INFO: Terminated");
524   exit(ret);
525 }
526
527
528 /************************************
529  routines for debugging output
530  ***********************************/
531 int LockDaemonLockFile(void){
532   int ret;
533   if(debug>1) err_msg("DEBUG:=>lockDaemonLockFile( )");
534   ret = lockDaemonLockFile();
535   if(debug>1) err_msg("DEBUG:(%d)<=lockDaemonLockFile( )",ret);
536   return ret;
537 }
538
539 void Daemonize(void){
540   if(debug>1) err_msg("DEBUG:=>daemonize( )");
541   daemonize();
542   if(debug>1) err_msg("DEBUG:<=daemonize( )");
543 }
544
545 void ShowHelp(char* procName){
546   if(debug>1) err_msg("DEBUG:=>showHelp(%s)", procName);
547   showHelp(procName);
548   if(debug>1) err_msg("DEBUG:<=showhelp( )");
549 }
550 void KillDaemon(void){
551   if(debug>1) err_msg("DEBUG:=>killDaemon( )");
552   killDaemon();
553   if(debug>1) err_msg("DEBUG:<=killDaemon( )");
554 }
555 void ReloadDaemon(void){
556   if(debug>1) err_msg("DEBUG:=>reloadDaemon( )");
557   reloadDaemon();
558   if(debug>1) err_msg("DEBUG:<=reloadDaemon( )");
559 }