1 /* Copyright (C) 2002 GFRN systems
2 
3    This program is free software; you can redistribute it and/or
4    modify it under the terms of the GNU General Public License as
5    published by the Free Software Foundation; either version 2 of the
6    License, or (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful, but
9    WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11    See the GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16    02111-1307, USA.
17 
18    The latest version of this program may be found at
19    http://CQiNet.sourceforge.net
20 
21    $Log: users.c,v $
22    Revision 1.8  2008/02/26 17:34:44  wb6ymh
23    Added dmalloc support.
24 
25    Revision 1.7  2007/12/01 01:02:21  wb6ymh
26    Deleted STRCMP macro, it's now in common.h.
27 
28    Revision 1.6  2007/06/27 19:21:15  wb6ymh
29    Added code to DeleteUser to free the QTH string.
30 
31    Revision 1.5  2004/05/29 17:32:49  wb6ymh
32    1. Eliminated unused and unneeded fields from the UserInfo structure to reduce
33       memory requirements.
34    2. Added delete of the user structure itself in DeleteUser() (!).
35 
36    Revision 1.4  2003/08/31 23:24:07  wb6ymh
37    Corrected portability problem caused by the assumption that u_long was the
38    same size as an IP address.
39 
40    Revision 1.3  2003/04/30 22:01:25  wb6ymh
41    Modified LoadACL() and SaveACL() to use the AppName configuration variable's
42    value as the application name when creating pid files rather than a
43    hardcoded "tbd".
44 
45    Revision 1.2  2002/11/02 18:31:10  wb6ymh
46    Added routines to load, save, modify and refresh the access control list.
47 
48    Revision 1.1.1.1  2002/08/10 20:33:41  wb6ymh
49    initial import
50 
51 */
52 
53 #include "common.h"
54 
55 #ifndef _WIN32
56    // FreeBSD, Linux, etc..
57    #include <stdio.h>
58    #include <stdlib.h>
59    #ifdef TIME_WITH_SYS_TIME
60       #include <sys/time.h>
61       #include <time.h>
62    #else
63       #ifdef HAVE_SYS_TIME_H
64          #include <sys/time.h>
65       #else
66          #include <time.h>
67       #endif
68    #endif
69    #include <sys/socket.h>
70    #include <netinet/in.h>
71    #include <arpa/inet.h>
72    #include <string.h>
73    #include <ctype.h>
74    #include <netdb.h>
75    #include <sys/stat.h>
76 #else
77    // Windoze
78    #include <stdio.h>
79    #include <stdlib.h>
80    #include <string.h>
81    #include <time.h>
82    #include <winsock2.h>
83    #include <ctype.h>
84    #include <sys/stat.h>
85 #endif
86 
87 #include "avl.h"
88 #include "main.h"
89 #include "users.h"
90 #include "configvars.h"
91 #include "hostfile.h"
92 
93 #ifdef USE_DMALLOC
94 #include "dmalloc.h"
95 #endif
96 
97 #define ACL_FILENAME_EXT   "acl"
98 
99 struct avl_table *UserTree;
100 struct avl_table *UserIPAdrTree;
101 struct avl_table *ACL_Call_Tree;
102 struct avl_table *ACL_IP_Tree;
103 
104 UserInfo *pUserDebug = NULL;
105 
106 int Id = 1001;
107 
DeleteUser(UserInfo * pUser,int Virgin)108 void DeleteUser(UserInfo *pUser,int Virgin)
109 {
110    D2PRINTF(("Deleting user \"%s\".\n",pUser->Callsign));
111    if(!Virgin && avl_find(UserIPAdrTree,pUser) != NULL) {
112       avl_delete(UserIPAdrTree,pUser);
113    }
114 
115    if(!Virgin) {
116       if(avl_delete(UserTree,pUser) == NULL) {
117          LOG_ERROR(("DeleteUser(): avl_delete() failed to find user \"%s\".\n",
118                     pUser->Callsign));
119       }
120    }
121 
122    if(pUser->Callsign != NULL) {
123       free(pUser->Callsign);
124    }
125 
126    if(pUser->Qth != NULL) {
127        free(pUser->Qth);
128    }
129 
130    free(pUser);
131 }
132 
133 // User compares by callsign
UserCompare(const void * avl_a,const void * avl_b,void * avl_param)134 int UserCompare(const void *avl_a,const void *avl_b,void *avl_param)
135 {
136    UserInfo *p_a = (UserInfo *) avl_a;
137    UserInfo *p_b = (UserInfo *) avl_b;
138 
139    return STRCMP(p_a->Callsign,p_b->Callsign);
140 }
141 
142 // User compares by callsign
UserIPCompare(const void * avl_a,const void * avl_b,void * avl_param)143 int UserIPCompare(const void *avl_a,const void *avl_b,void *avl_param)
144 {
145    UserInfo *p_a = (UserInfo *) avl_a;
146    UserInfo *p_b = (UserInfo *) avl_b;
147 
148    if(p_a->HisAdr.ADDR == p_b->HisAdr.ADDR) {
149       return 0;
150    }
151 
152    if(p_a->HisAdr.ADDR > p_b->HisAdr.ADDR) {
153       return 1;
154    }
155    else {
156       return -1;
157    }
158 }
159 
ACL_CallCompare(const void * avl_a,const void * avl_b,void * avl_param)160 int ACL_CallCompare(const void *avl_a,const void *avl_b,void *avl_param)
161 {
162    ACL_User *p_a = (ACL_User *) avl_a;
163    ACL_User *p_b = (ACL_User *) avl_b;
164 
165    return STRCMP(p_a->Callsign,p_b->Callsign);
166 }
167 
168 // User compares by callsign
ACL_IPCompare(const void * avl_a,const void * avl_b,void * avl_param)169 int ACL_IPCompare(const void *avl_a,const void *avl_b,void *avl_param)
170 {
171    ACL_User *p_a = (ACL_User *) avl_a;
172    ACL_User *p_b = (ACL_User *) avl_b;
173 
174    if(p_a->HisAdr.ADDR == p_b->HisAdr.ADDR) {
175       return 0;
176    }
177 
178    if(p_a->HisAdr.ADDR > p_b->HisAdr.ADDR) {
179       return 1;
180    }
181    else {
182       return -1;
183    }
184 }
185 
186 
CreateNewUser(char * Callsign)187 UserInfo *CreateNewUser(char *Callsign)
188 {
189    UserInfo *pUser = malloc(sizeof(UserInfo));
190 
191    if(pUser != NULL) {
192       memset(pUser,0,sizeof(UserInfo));
193       pUser->Callsign = strdup(Callsign);
194       pUser->NodeID = Id++;
195    }
196    return pUser;
197 }
198 
DeleteACLUser(ACL_User * pACL)199 void DeleteACLUser(ACL_User *pACL)
200 {
201    avl_delete(ACL_IP_Tree,pACL);
202    avl_delete(ACL_Call_Tree,pACL);
203 
204    if(pACL->Callsign != NULL) {
205       free(pACL->Callsign);
206    }
207 
208    if(pACL->CallPlus != NULL) {
209       free(pACL->CallPlus);
210    }
211 
212    if(pACL->HostName != NULL) {
213       free(pACL->HostName);
214    }
215 
216    if(pACL->Password != NULL) {
217       free(pACL->Password);
218    }
219 
220    free(pACL);
221 }
222 
223 // The following fields must be filled in:
224 //    Callsign
225 //    HostName ("-" for no hostname checking)
226 //    Password ("-" for no password checking)
227 //    bAuthorized
228 //    Optionally:
229 //    CallPlus - User's name (appended to Callsign to create CallPlus)
ACLAddUser(ACL_User * pACL)230 int ACLAddUser(ACL_User *pACL)
231 {
232    int Ret = TRUE;
233    ACL_User *pOldACL = NULL;
234    ACL_User *pNewACL;
235    struct hostent *pTemp;
236    char *cp;
237    int StrLen;
238 
239    pACL->HisAdr.ADDR = INADDR_NONE;
240 
241    if(*pACL->HostName != '-') {
242    // A host name was specified, make sure it's valid
243       pTemp = GetHostByName(pACL->HostName);
244       if(pTemp == NULL) {
245          Ret = FALSE;
246       }
247       else {
248          pACL->HisAdr.ADDR = IP_FROM_HOSTENT(pTemp,0);
249       }
250    }
251 
252    if(Ret) {
253    // The ACL Callsign field does not contain a "-L" or "-R", but the
254    // callplus field does...
255       if((cp = strchr(pACL->Callsign,'-')) != NULL) {
256       // Remove -R or -L from the callsign
257          *cp = 0;
258       }
259 
260       if((pOldACL = avl_find(ACL_Call_Tree,pACL)) != NULL) {
261       // delete old entry
262          DeleteACLUser(pOldACL);
263       }
264 
265       pNewACL = (ACL_User *) malloc(sizeof(ACL_User));
266       if(pNewACL != NULL) {
267          memset(pNewACL,0,sizeof(ACL_User));
268          pNewACL->bRefreshed = pACL->bRefreshed;
269          pNewACL->HostName = strdup(pACL->HostName);
270          pNewACL->Callsign = strdup(pACL->Callsign);
271          if(cp != NULL) {
272             *cp = '-';
273          }
274 
275          if(pACL->CallPlus != NULL) {
276             StrLen = strlen(pACL->Callsign) + strlen(pACL->CallPlus) + 2;
277             pNewACL->CallPlus = malloc(StrLen);
278             if(pNewACL->CallPlus != NULL) {
279                strcpy(pNewACL->CallPlus,pACL->Callsign);
280                if(*pACL->CallPlus != '-') {
281                   strcat(pNewACL->CallPlus," ");
282                }
283                strcat(pNewACL->CallPlus,pACL->CallPlus);
284             }
285          }
286          else {
287             pNewACL->CallPlus = strdup(pACL->Callsign);
288          }
289 
290          if(pACL->Password != NULL) {
291             pNewACL->Password = strdup(pACL->Password);
292          }
293          pNewACL->HisAdr.ADDR = pACL->HisAdr.ADDR;
294          pNewACL->bAuthorized = pACL->bAuthorized;
295          pNewACL->LastResolved = pACL->LastResolved;
296          avl_insert(ACL_Call_Tree,pNewACL);
297          if(*pNewACL->HostName != '-') {
298             avl_insert(ACL_IP_Tree,pNewACL);
299          }
300       }
301    }
302 
303    return Ret;
304 }
305 
RefreshACL()306 void RefreshACL()
307 {
308    ACL_User *pACL = NULL;
309    struct avl_traverser avl_trans;
310    struct hostent *pTemp;
311 
312    pACL = (ACL_User *) avl_t_first(&avl_trans,ACL_Call_Tree);
313    while(pACL != NULL) {
314       if(*pACL->HostName != '-') {
315          pTemp = GetHostByName(pACL->HostName);
316          if(pTemp != NULL) {
317             pACL->HisAdr.ADDR = IP_FROM_HOSTENT(pTemp,0);
318          }
319       }
320       pACL = (ACL_User *) avl_t_next(&avl_trans);
321    }
322 }
323 
LoadACL()324 void LoadACL()
325 {
326    char Line[80];
327    char *Allow;
328    ACL_User *pACL = NULL;
329    ACL_User *pNextACL = NULL;
330    struct avl_traverser avl_trans;
331    char  *WhiteSpace = "\t\n ";
332    int LineNum = 0;
333    FILE *fp = NULL;
334    ACL_User ACL_New;
335    struct stat FileStats;
336    char  AclFilename[80];
337 
338    snprintf(AclFilename,sizeof(AclFilename),"%s." ACL_FILENAME_EXT,AppName);
339 
340    if((fp = fopen(AclFilename,"r")) == NULL) {
341       if(stat(AclFilename,&FileStats) == 0) {
342       // The ACL file exists but we couldn't open it, complain
343          LOG_ERROR(("LoadACL(): Unable to open ACL file.\n"));
344       }
345       return;
346    }
347 
348    memset(&ACL_New,0,sizeof(ACL_New));
349    ACL_New.bRefreshed = TRUE;
350    while(fgets(Line,sizeof(Line),fp) != NULL) {
351       LineNum++;
352 
353       Allow = strtok(Line,WhiteSpace);
354       if(Allow == NULL || *Allow == '#' || *Allow == ';') {
355       // Empty line or comment
356          continue;
357       }
358 
359       if(STRCMP(Allow,"allow") == 0) {
360          ACL_New.bAuthorized = TRUE;
361       }
362       else if(STRCMP(Allow,"deny") == 0) {
363          ACL_New.bAuthorized = FALSE;
364       }
365       else {
366       // Invalid line
367          LOG_WARN(("LoadACL(): Ignoring line %d, invalid action \"%s\".\n",
368                    LineNum,Allow));
369          continue;
370       }
371 
372       ACL_New.Callsign = strtok(NULL,WhiteSpace);
373       if(ACL_New.Callsign == NULL) {
374       // Invalid line
375          LOG_WARN(("LoadACL(): Ignoring line %d, callsign missing.\n",LineNum));
376          continue;
377       }
378 
379       if((ACL_New.HostName = strtok(NULL,WhiteSpace)) == NULL) {
380          LOG_WARN(("LoadACL(): Ignoring line %d, hostname missing.\n",LineNum));
381          continue;
382       }
383 
384       if((ACL_New.Password = strtok(NULL,WhiteSpace)) == NULL) {
385          LOG_WARN(("LoadACL(): Ignoring line %d, password missing.\n",LineNum));
386          continue;
387       }
388 
389       ACL_New.CallPlus = strtok(NULL,"\n");
390 
391    // Skip any leading whitespace
392 
393       if(ACL_New.CallPlus != NULL) {
394          while(*ACL_New.CallPlus && isspace(*ACL_New.CallPlus)) {
395             ACL_New.CallPlus++;
396          }
397       }
398 
399       if(!ACLAddUser(&ACL_New)) {
400          LOG_WARN(("LoadACL(): Ignoring line %d, unable to resolve "
401                    "hostname \"%s\".\n",LineNum,ACL_New.HostName));
402       }
403    }
404    fclose(fp);
405 
406 // Remove any old entries from tree
407 
408    pNextACL = (ACL_User *) avl_t_first(&avl_trans,ACL_Call_Tree);
409    while((pACL = pNextACL) != NULL) {
410    // NB: Get the next user now in case we deleted the current user
411       pNextACL = (ACL_User *) avl_t_next(&avl_trans);
412       if(!pACL->bRefreshed) {
413          DeleteACLUser(pACL);
414       }
415       pACL->bRefreshed = FALSE;
416    }
417 }
418 
419 // ACL format:
420 // <"allow"|"deny>"<tab><callsign><tab><hostname/IP Adr><tab><password><tab>
421 // <Name/Location>
422 //
423 // The callsign field is required and should be entered in upper case without
424 // any suffix (-R or -L).  Stations are allowed/denied based on the base
425 // callsign.
426 //
427 // hostname can be a numeric IP address, actual resolvable hostname or "-" for
428 // no hostname
429 //
430 // The password can be "-" for no password or the actual password. The password
431 // may not contain spaces or tabs.
432 //
433 // The Name/location field is free text and extends to the end of the line.
434 // If it is desired to display <callsign>-R or <callsign>-L on the Echolink
435 // station list start the name/location field with "-R" or "-L".
436 //
SaveACL()437 void SaveACL()
438 {
439    struct avl_traverser avl_trans;
440    ACL_User *pACL;
441    char *cp;
442    FILE *fp = NULL;
443    char  AclFilename[80];
444 
445    snprintf(AclFilename,sizeof(AclFilename),"%s." ACL_FILENAME_EXT,AppName);
446    if((fp = fopen(AclFilename,"w")) == NULL) {
447       LOG_ERROR(("SaveACL(): Unable to open ACL file for write.\n"));
448       return;
449    }
450 
451    pACL = (ACL_User *) avl_t_first(&avl_trans,ACL_Call_Tree);
452    while(pACL != NULL) {
453       if(pACL->CallPlus == NULL) {
454          cp = NULL;
455       }
456       else if((cp = strchr(pACL->CallPlus,'-')) == NULL) {
457          cp = strchr(pACL->CallPlus,' ');
458          if(cp != NULL) {
459          // Skip the blank
460             cp++;
461          }
462       }
463 
464       fprintf(fp,"%s\t%s\t%s\t%s\t%s\n",
465               pACL->bAuthorized ? "allow" : "deny",
466               pACL->Callsign,
467               pACL->HostName,
468               pACL->Password,
469               cp == NULL ? "" : cp);
470 
471       pACL = (ACL_User *) avl_t_next(&avl_trans);
472    }
473    fclose(fp);
474 }
475 
476 
477