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