c81fb936bfebb075d642dae8081e1190e8997bf5
[fwknop.git] / server / access.c
1 /*
2  ******************************************************************************
3  *
4  * File:    access.c
5  *
6  * Author:  Damien Stuart
7  *
8  * Purpose: Access.conf file processing for fwknop server.
9  *
10  * Copyright 2010 Damien Stuart (dstuart@dstuart.org)
11  *
12  *  License (GNU Public License):
13  *
14  *  This program is free software; you can redistribute it and/or
15  *  modify it under the terms of the GNU General Public License
16  *  as published by the Free Software Foundation; either version 2
17  *  of the License, or (at your option) any later version.
18  *
19  *  This program is distributed in the hope that it will be useful,
20  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *  GNU General Public License for more details.
23  *
24  *  You should have received a copy of the GNU General Public License
25  *  along with this program; if not, write to the Free Software
26  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
27  *  USA
28  *
29  ******************************************************************************
30 */
31 #include <sys/stat.h>
32
33 #if HAVE_SYS_SOCKET_H
34   #include <sys/socket.h>
35 #endif
36
37 #include "fwknopd_common.h"
38 #include <arpa/inet.h>
39 #include "pwd.h"
40 #include "access.h"
41 #include "utils.h"
42 #include "log_msg.h"
43
44 /* Add an access string entry
45 */
46 static void
47 add_acc_string(char **var, const char *val)
48 {
49     if(*var != NULL)
50         free(*var);
51
52     if((*var = strdup(val)) == NULL)
53     {
54         log_msg(LOG_ERR,
55             "Fatal memory allocation error adding access list entry: %s", var
56         );
57         exit(EXIT_FAILURE);
58     }
59 }
60
61 /* Add an access int entry
62 */
63 static int
64 add_acc_int(int *var, const char *val)
65 {
66     return(*var = atoi(val));
67 }
68
69 /* Add an access bool entry (unsigned char of 1 or 0)
70 */
71 static unsigned char
72 add_acc_bool(unsigned char *var, const char *val)
73 {
74     return(*var = (strncasecmp(val, "Y", 1) == 0) ? 1 : 0);
75 }
76
77 /* Add expiration time - convert date to epoch seconds
78 */
79 static void
80 add_acc_expire_time(fko_srv_options_t *opts, time_t *access_expire_time, const char *val)
81 {
82     struct tm tm;
83
84     memset(&tm, 0, sizeof(struct tm));
85
86     if (sscanf(val, "%2d/%2d/%4d", &tm.tm_mon, &tm.tm_mday, &tm.tm_year) != 3)
87     {
88
89         log_msg(LOG_ERR,
90             "Fatal: invalid date value '%s' (need MM/DD/YYYY) for access stanza expiration time",
91             val
92         );
93         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
94     }
95
96     if(tm.tm_mon > 0)
97         tm.tm_mon -= 1;  /* 0-11 */
98
99     /* number of years since 1900
100     */
101     if(tm.tm_year > 1900)
102         tm.tm_year -= 1900;
103     else
104         if(tm.tm_year < 100)
105             tm.tm_year += 100;
106
107     *access_expire_time = mktime(&tm);
108
109     return;
110 }
111
112 /* Add expiration time via epoch seconds defined in access.conf
113 */
114 static void
115 add_acc_expire_time_epoch(fko_srv_options_t *opts, time_t *access_expire_time, const char *val)
116 {
117     char *endptr;
118     unsigned long expire_time = 0;
119
120     errno = 0;
121
122     expire_time = (time_t) strtoul(val, &endptr, 10);
123
124     if (errno == ERANGE || (errno != 0 && expire_time == 0))
125     {
126         log_msg(LOG_ERR,
127             "Fatal: invalid epoch seconds value '%s' for access stanza expiration time",
128             val
129         );
130         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
131     }
132
133     *access_expire_time = (time_t) expire_time;
134
135     return;
136 }
137
138 #if FIREWALL_IPTABLES
139 static void
140 add_acc_force_nat(fko_srv_options_t *opts, acc_stanza_t *curr_acc, const char *val)
141 {
142     char      ip_str[MAX_IPV4_STR_LEN] = {0};
143
144     if (sscanf(val, "%15s %5u", ip_str, &curr_acc->force_nat_port) != 2)
145     {
146
147         log_msg(LOG_ERR,
148             "Fatal: invalid FORCE_NAT arg '%s', need <IP> <PORT>",
149             val
150         );
151         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
152     }
153
154     if (curr_acc->force_nat_port > MAX_PORT)
155     {
156         log_msg(LOG_ERR,
157             "Fatal: invalid FORCE_NAT port '%d'", curr_acc->force_nat_port);
158         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
159     }
160
161     curr_acc->force_nat = 1;
162     add_acc_string(&(curr_acc->force_nat_ip), ip_str);
163
164     return;
165 }
166 #endif
167
168 /* Take an IP or Subnet/Mask and convert it to mask for later
169  * comparisons of incoming source IPs against this mask.
170 */
171 static void
172 add_source_mask(acc_stanza_t *acc, const char *ip)
173 {
174     char                *ndx;
175     char                ip_str[MAX_IPV4_STR_LEN] = {0};
176     uint32_t            mask;
177
178     struct in_addr      in;
179
180     acc_int_list_t      *last_sle, *new_sle, *tmp_sle;
181
182     if((new_sle = calloc(1, sizeof(acc_int_list_t))) == NULL)
183     {
184         log_msg(LOG_ERR,
185             "Fatal memory allocation error adding stanza source_list entry"
186         );
187         exit(EXIT_FAILURE);
188     }
189
190     /* If this is not the first entry, we walk our pointer to the
191      * end of the list.
192     */
193     if(acc->source_list == NULL)
194     {
195         acc->source_list = new_sle;
196     }
197     else
198     {
199         tmp_sle = acc->source_list;
200
201         do {
202             last_sle = tmp_sle;
203         } while((tmp_sle = tmp_sle->next));
204
205         last_sle->next = new_sle;
206     }
207
208     /* Convert the IP data into the appropriate mask
209     */
210     if(strcasecmp(ip, "ANY") == 0)
211     {
212         new_sle->maddr = 0x0;
213         new_sle->mask = 0x0;
214     }
215     else
216     {
217         /* See if we have a subnet component.  If so pull out the IP and
218          * mask values, then create the final mask value.
219         */
220         if((ndx = strchr(ip, '/')) != NULL)
221         {
222             mask = atoi(ndx+1);
223             strlcpy(ip_str, ip, (ndx-ip)+1);
224         }
225         else
226         {
227             mask = 32;
228             strlcpy(ip_str, ip, strlen(ip)+1);
229         }
230
231         if(inet_aton(ip_str, &in) == 0)
232         {
233             log_msg(LOG_ERR,
234                 "Error parsing IP to int for: %s", ip_str
235             );
236
237             free(new_sle);
238             new_sle = NULL;
239
240             return;
241         }
242
243         /* Store our mask converted from CIDR to a 32-bit value.
244         */
245         new_sle->mask  = (0xFFFFFFFF << (32 - mask));
246
247         /* Store our masked address for comparisons with future incoming
248          * packets.
249         */
250         new_sle->maddr = ntohl(in.s_addr) & new_sle->mask;
251     }
252 }
253
254 /* Expand the access SOURCE string to a list of masks.
255 */
256 void
257 expand_acc_source(acc_stanza_t *acc)
258 {
259     char           *ndx, *start;
260     char            buf[32];
261
262     start = acc->source;
263
264     for(ndx = start; *ndx; ndx++)
265     {
266         if(*ndx == ',')
267         {
268             /* Skip over any leading whitespace.
269             */
270             while(isspace(*start))
271                 start++;
272
273             strlcpy(buf, start, (ndx-start)+1);
274             add_source_mask(acc, buf);
275             start = ndx+1;
276         }
277     }
278
279     /* Skip over any leading whitespace (once again for the last in the list).
280     */
281     while(isspace(*start))
282         start++;
283
284     strlcpy(buf, start, (ndx-start)+1);
285     add_source_mask(acc, buf);
286 }
287
288 static int
289 parse_proto_and_port(char *pstr, int *proto, int *port)
290 {
291     char    *ndx;
292     char    proto_str[32];
293
294     /* Parse the string into its components.
295     */
296     if((ndx = strchr(pstr, '/')) == NULL)
297     {
298         log_msg(LOG_ERR,
299             "Parse error on access port entry: %s", pstr);
300
301         return(-1);
302     }
303
304     strlcpy(proto_str, pstr,  (ndx - pstr)+1);
305
306     *port = atoi(ndx+1);
307
308     if(strcasecmp(proto_str, "tcp") == 0)
309         *proto = PROTO_TCP;
310     else if(strcasecmp(proto_str, "udp") == 0)
311         *proto = PROTO_UDP;
312     else
313     {
314         log_msg(LOG_ERR,
315             "Invalid protocol in access port entry: %s", pstr);
316
317         return(-1);
318     }
319
320     return(0);
321 }
322
323 /* Take a proto/port string and convert it to appropriate integer values
324  * for comparisons of incoming SPA requests.
325 */
326 static void
327 add_port_list_ent(acc_port_list_t **plist, char *port_str)
328 {
329     int                 proto_int, port;
330
331     acc_port_list_t     *last_plist, *new_plist, *tmp_plist;
332
333     /* Parse the string into its components and continue only if there
334      * are no problems with the incoming string.
335     */
336     if(parse_proto_and_port(port_str, &proto_int, &port) != 0)
337         return;
338
339     if((new_plist = calloc(1, sizeof(acc_port_list_t))) == NULL)
340     {
341         log_msg(LOG_ERR,
342             "Fatal memory allocation error adding stanza source_list entry"
343         );
344         exit(EXIT_FAILURE);
345     }
346
347     /* If this is not the first entry, we walk our pointer to the
348      * end of the list.
349     */
350     if(*plist == NULL)
351     {
352         *plist = new_plist;
353     }
354     else
355     {
356         tmp_plist = *plist;
357
358         do {
359             last_plist = tmp_plist;
360         } while((tmp_plist = tmp_plist->next));
361
362         last_plist->next = new_plist;
363     }
364
365     new_plist->proto = proto_int;
366     new_plist->port  = port;
367 }
368
369 /* Add a string list entry to the given acc_string_list.
370 */
371 static void
372 add_string_list_ent(acc_string_list_t **stlist, const char *str_str)
373 {
374     acc_string_list_t   *last_stlist, *new_stlist, *tmp_stlist;
375
376     if((new_stlist = calloc(1, sizeof(acc_string_list_t))) == NULL)
377     {
378         log_msg(LOG_ERR,
379             "Fatal memory allocation error creating string list entry"
380         );
381         exit(EXIT_FAILURE);
382     }
383
384     /* If this is not the first entry, we walk our pointer to the
385      * end of the list.
386     */
387     if(*stlist == NULL)
388     {
389         *stlist = new_stlist;
390     }
391     else
392     {
393         tmp_stlist = *stlist;
394
395         do {
396             last_stlist = tmp_stlist;
397         } while((tmp_stlist = tmp_stlist->next));
398
399         last_stlist->next = new_stlist;
400     }
401
402     if(new_stlist->str != NULL)
403         free(new_stlist->str);
404
405     new_stlist->str = strdup(str_str);
406
407     if(new_stlist->str == NULL)
408     {
409         log_msg(LOG_ERR,
410             "Fatal memory allocation error adding string list entry item"
411         );
412         exit(EXIT_FAILURE);
413     }
414
415 }
416
417 /* Expand a proto/port access string to a list of access proto-port struct.
418 */
419 void
420 expand_acc_port_list(acc_port_list_t **plist, char *plist_str)
421 {
422     char           *ndx, *start;
423     char            buf[32];
424
425     start = plist_str;
426
427     for(ndx = start; *ndx; ndx++)
428     {
429         if(*ndx == ',')
430         {
431             /* Skip over any leading whitespace.
432             */
433             while(isspace(*start))
434                 start++;
435
436             strlcpy(buf, start, (ndx-start)+1);
437             add_port_list_ent(plist, buf);
438             start = ndx+1;
439         }
440     }
441
442     /* Skip over any leading whitespace (once again for the last in the list).
443     */
444     while(isspace(*start))
445         start++;
446
447     strlcpy(buf, start, (ndx-start)+1);
448
449     add_port_list_ent(plist, buf);
450 }
451
452 /* Expand a comma-separated string into a simple acc_string_list.
453 */
454 static void
455 expand_acc_string_list(acc_string_list_t **stlist, char *stlist_str)
456 {
457     char           *ndx, *start;
458     char            buf[1024];
459
460     start = stlist_str;
461
462     for(ndx = start; *ndx; ndx++)
463     {
464         if(*ndx == ',')
465         {
466             /* Skip over any leading whitespace.
467             */
468             while(isspace(*start))
469                 start++;
470
471             strlcpy(buf, start, (ndx-start)+1);
472             add_string_list_ent(stlist, buf);
473             start = ndx+1;
474         }
475     }
476
477     /* Skip over any leading whitespace (once again for the last in the list).
478     */
479     while(isspace(*start))
480         start++;
481
482     strlcpy(buf, start, (ndx-start)+1);
483
484     add_string_list_ent(stlist, buf);
485 }
486
487 /* Free the acc source_list
488 */
489 static void
490 free_acc_source_list(acc_int_list_t *sle)
491 {
492     acc_int_list_t    *last_sle;
493
494     while(sle != NULL)
495     {
496         last_sle = sle;
497         sle = last_sle->next;
498
499         free(last_sle);
500     }
501 }
502
503 /* Free a port_list
504 */
505 void
506 free_acc_port_list(acc_port_list_t *ple)
507 {
508     acc_port_list_t    *last_ple;
509
510     while(ple != NULL)
511     {
512         last_ple = ple;
513         ple = last_ple->next;
514
515         free(last_ple);
516     }
517 }
518
519 /* Free a string_list
520 */
521 static void
522 free_acc_string_list(acc_string_list_t *stl)
523 {
524     acc_string_list_t    *last_stl;
525
526     while(stl != NULL)
527     {
528         last_stl = stl;
529         stl = last_stl->next;
530
531         free(last_stl->str);
532         free(last_stl);
533     }
534 }
535
536 /* Free any allocated content of an access stanza.
537  *
538  * NOTE: If a new access.conf parameter is created, and it is a string
539  *       value, it also needs to be added to the list of items to check
540  *       and free below.
541 */
542 static void
543 free_acc_stanza_data(acc_stanza_t *acc)
544 {
545
546     if(acc->source != NULL)
547     {
548         free(acc->source);
549         free_acc_source_list(acc->source_list);
550     }
551
552     if(acc->open_ports != NULL)
553     {
554         free(acc->open_ports);
555         free_acc_port_list(acc->oport_list);
556     }
557
558     if(acc->restrict_ports != NULL)
559     {
560         free(acc->restrict_ports);
561         free_acc_port_list(acc->rport_list);
562     }
563
564     if(acc->force_nat_ip != NULL)
565         free(acc->force_nat_ip);
566
567     if(acc->key != NULL)
568         free(acc->key);
569
570     if(acc->cmd_exec_user != NULL)
571         free(acc->cmd_exec_user);
572
573     if(acc->require_username != NULL)
574         free(acc->require_username);
575
576     if(acc->gpg_home_dir != NULL)
577         free(acc->gpg_home_dir);
578
579     if(acc->gpg_decrypt_id != NULL)
580         free(acc->gpg_decrypt_id);
581
582     if(acc->gpg_decrypt_pw != NULL)
583         free(acc->gpg_decrypt_pw);
584
585     if(acc->gpg_remote_id != NULL)
586     {
587         free(acc->gpg_remote_id);
588         free_acc_string_list(acc->gpg_remote_id_list);
589     }
590 }
591
592 /* Expand any access entries that may be multi-value.
593 */
594 static void
595 expand_acc_ent_lists(fko_srv_options_t *opts)
596 {
597     acc_stanza_t   *acc = opts->acc_stanzas;
598
599     /* We need to do this for each stanza.
600     */
601     while(acc)
602     {
603         /* Expand the source string to 32-bit integer masks foreach entry.
604         */
605         expand_acc_source(acc);
606
607         /* Now expand the open_ports string.
608         */
609         if(acc->open_ports != NULL && strlen(acc->open_ports))
610             expand_acc_port_list(&(acc->oport_list), acc->open_ports);
611
612         if(acc->restrict_ports != NULL && strlen(acc->restrict_ports))
613             expand_acc_port_list(&(acc->rport_list), acc->restrict_ports);
614
615         /* Expand the GPG_REMOTE_ID string.
616         */
617         if(acc->gpg_remote_id != NULL && strlen(acc->gpg_remote_id))
618             expand_acc_string_list(&(acc->gpg_remote_id_list), acc->gpg_remote_id);
619
620         acc = acc->next;
621     }
622 }
623
624 void
625 free_acc_stanzas(fko_srv_options_t *opts)
626 {
627     acc_stanza_t    *acc, *last_acc;
628
629     /* Free any resources first (in case of reconfig). Assume non-NULL
630      * entry needs to be freed.
631     */
632     acc = opts->acc_stanzas;
633
634     while(acc != NULL)
635     {
636         last_acc = acc;
637         acc = last_acc->next;
638
639         free_acc_stanza_data(last_acc);
640         free(last_acc);
641     }
642
643     return;
644 }
645
646 /* Wrapper for free_acc_stanzas(), we may put additional initialization
647  * code here.
648 */
649 static void
650 acc_stanza_init(fko_srv_options_t *opts)
651 {
652     /* Free any resources first (in case of reconfig). Assume non-NULL
653      * entry needs to be freed.
654     */
655     free_acc_stanzas(opts);
656
657     return;
658 }
659
660 /* Add a new stanza bay allocating the required memory at the required
661  * location, yada-yada-yada.
662 */
663 static acc_stanza_t*
664 acc_stanza_add(fko_srv_options_t *opts)
665 {
666     acc_stanza_t    *acc     = opts->acc_stanzas;
667     acc_stanza_t    *new_acc = calloc(1, sizeof(acc_stanza_t));
668     acc_stanza_t    *last_acc;
669
670     if(new_acc == NULL)
671     {
672         log_msg(LOG_ERR,
673             "Fatal memory allocation error adding access stanza"
674         );
675         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
676     }
677
678     /* If this is not the first acc entry, we walk our acc pointer to the
679      * end of the existing list.
680     */
681     if(acc == NULL)
682     {
683         opts->acc_stanzas = new_acc;
684     }
685     else
686     {
687         do {
688             last_acc = acc;
689         } while((acc = acc->next));
690
691         last_acc->next = new_acc;
692     }
693
694     return(new_acc);
695 }
696
697 /* Scan the access options for entries that have not bees set, but need
698  * a default value.
699 */
700 static void
701 set_acc_defaults(fko_srv_options_t *opts)
702 {
703     acc_stanza_t    *acc = opts->acc_stanzas;
704
705     if(!acc)
706         return;
707
708     while(acc)
709     {
710         /* set default fw_access_timeout if necessary
711         */
712         if(acc->fw_access_timeout < 1)
713             acc->fw_access_timeout = DEF_FW_ACCESS_TIMEOUT;
714
715         /* set default gpg keyring path if necessary
716         */
717         if(acc->gpg_decrypt_pw != NULL && acc->gpg_home_dir == NULL)
718             add_acc_string(&(acc->gpg_home_dir), opts->config[CONF_GPG_HOME_DIR]);
719
720         acc = acc->next;
721     }
722 }
723
724 /* Perform some sanity checks on an acc stanza data.
725 */
726 static int
727 acc_data_is_valid(const acc_stanza_t *acc)
728 {
729     if((acc->key == NULL || !strlen(acc->key))
730       && (acc->gpg_decrypt_pw == NULL || !strlen(acc->gpg_decrypt_pw)))
731     {
732         fprintf(stderr,
733             "[*] No keys found for access stanza source: '%s'\n", acc->source
734         );
735         return(0);
736     }
737
738     return(1);
739 }
740
741 /* Read and parse the access file, popluating the access data as we go.
742 */
743 void
744 parse_access_file(fko_srv_options_t *opts)
745 {
746     FILE           *file_ptr;
747     char           *ndx;
748     int             got_source = 0;
749     unsigned int    num_lines = 0;
750
751     char            access_line_buf[MAX_LINE_LEN] = {0};
752     char            var[MAX_LINE_LEN]  = {0};
753     char            val[MAX_LINE_LEN]  = {0};
754
755     struct passwd  *pw;
756     struct stat     st;
757
758     acc_stanza_t   *curr_acc = NULL;
759
760     /* First see if the access file exists.  If it doesn't, complain
761      * and bail.
762     */
763     if(stat(opts->config[CONF_ACCESS_FILE], &st) != 0)
764     {
765         fprintf(stderr, "[*] Access file: '%s' was not found.\n",
766             opts->config[CONF_ACCESS_FILE]);
767
768         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
769     }
770
771     if ((file_ptr = fopen(opts->config[CONF_ACCESS_FILE], "r")) == NULL)
772     {
773         fprintf(stderr, "[*] Could not open access file: %s\n",
774             opts->config[CONF_ACCESS_FILE]);
775         perror(NULL);
776
777         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
778     }
779
780     /* Initialize the access list.
781     */
782     acc_stanza_init(opts);
783
784     /* Now walk through access file pulling the access entries into the
785      * current stanza.
786     */
787     while ((fgets(access_line_buf, MAX_LINE_LEN, file_ptr)) != NULL)
788     {
789         num_lines++;
790         access_line_buf[MAX_LINE_LEN-1] = '\0';
791
792         /* Get past comments and empty lines (note: we only look at the
793          * first character.
794         */
795         if(IS_EMPTY_LINE(access_line_buf[0]))
796             continue;
797
798         if(sscanf(access_line_buf, "%s %[^;\n\r]", var, val) != 2)
799         {
800             fprintf(stderr,
801                 "*Invalid access file entry in %s at line %i.\n - '%s'",
802                 opts->config[CONF_ACCESS_FILE], num_lines, access_line_buf
803             );
804             continue;
805         }
806
807         /* Remove any colon that may be on the end of the var
808         */
809         if((ndx = strrchr(var, ':')) != NULL)
810             *ndx = '\0';
811
812         /*
813         */
814         if(opts->verbose > 3)
815             fprintf(stderr,
816                 "ACCESS FILE: %s, LINE: %s\tVar: %s, Val: '%s'\n",
817                 opts->config[CONF_ACCESS_FILE], access_line_buf, var, val
818             );
819
820         /* Process the entry.
821          *
822          * NOTE: If a new access.conf parameter is created.  It also needs
823          *       to be accounted for in the following if/if else construct.
824         */
825
826         if(CONF_VAR_IS(var, "SOURCE"))
827         {
828             /* If this is not the first stanza, sanity check the previous
829              * stanza for the minimum required data.
830             */
831             if(curr_acc != NULL) {
832                 if(!acc_data_is_valid(curr_acc))
833                 {
834                     fprintf(stderr,
835                         "[*] Data error in access file: '%s'\n",
836                         opts->config[CONF_ACCESS_FILE]);
837                     clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
838                 }
839             }
840
841             /* Start new stanza.
842             */
843             curr_acc = acc_stanza_add(opts);
844
845             add_acc_string(&(curr_acc->source), val);
846
847             got_source++;
848         }
849         else if (curr_acc == NULL)
850         {
851             /* The stanza must start with the "SOURCE" variable
852             */
853             continue;
854         }
855         else if(CONF_VAR_IS(var, "OPEN_PORTS"))
856         {
857             add_acc_string(&(curr_acc->open_ports), val);
858         }
859         else if(CONF_VAR_IS(var, "RESTRICT_PORTS"))
860         {
861             add_acc_string(&(curr_acc->restrict_ports), val);
862         }
863         else if(CONF_VAR_IS(var, "KEY"))
864         {
865             if(strcasecmp(val, "__CHANGEME__") == 0)
866             {
867                 fprintf(stderr,
868                     "[*] KEY value is not properly set in stanza source '%s' in access file: '%s'\n",
869                     curr_acc->source, opts->config[CONF_ACCESS_FILE]);
870                 clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
871             }
872             add_acc_string(&(curr_acc->key), val);
873         }
874         else if(CONF_VAR_IS(var, "FW_ACCESS_TIMEOUT"))
875         {
876             add_acc_int(&(curr_acc->fw_access_timeout), val);
877         }
878         else if(CONF_VAR_IS(var, "ENABLE_CMD_EXEC"))
879         {
880             add_acc_bool(&(curr_acc->enable_cmd_exec), val);
881         }
882         else if(CONF_VAR_IS(var, "CMD_EXEC_USER"))
883         {
884             add_acc_string(&(curr_acc->cmd_exec_user), val);
885
886             errno = 0;
887             pw = getpwnam(val);
888
889             if(pw == NULL)
890             {
891                 fprintf(stderr, "Unable to determine UID for CMD_EXEC_USER: %s.\n",
892                     errno ? strerror(errno) : "Not a user on this system");
893                 clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
894             }
895
896             curr_acc->cmd_exec_uid = pw->pw_uid;
897         }
898         else if(CONF_VAR_IS(var, "REQUIRE_USERNAME"))
899         {
900             add_acc_string(&(curr_acc->require_username), val);
901         }
902         else if(CONF_VAR_IS(var, "REQUIRE_SOURCE_ADDRESS"))
903         {
904             add_acc_bool(&(curr_acc->require_source_address), val);
905         }
906         else if(CONF_VAR_IS(var, "REQUIRE_SOURCE"))  /* synonym for REQUIRE_SOURCE_ADDRESS */
907         {
908             add_acc_bool(&(curr_acc->require_source_address), val);
909         }
910         else if(CONF_VAR_IS(var, "GPG_HOME_DIR"))
911         {
912             if (is_valid_dir(val))
913             {
914                 add_acc_string(&(curr_acc->gpg_home_dir), val);
915             }
916             else
917             {
918                 fprintf(stderr,
919                     "[*] GPG_HOME_DIR directory '%s' stat()/existence problem in stanza source '%s' in access file: '%s'\n",
920                     val, curr_acc->source, opts->config[CONF_ACCESS_FILE]);
921                 clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
922             }
923         }
924         else if(CONF_VAR_IS(var, "GPG_DECRYPT_ID"))
925         {
926             add_acc_string(&(curr_acc->gpg_decrypt_id), val);
927         }
928         else if(CONF_VAR_IS(var, "GPG_DECRYPT_PW"))
929         {
930             if(strcasecmp(val, "__CHANGEME__") == 0)
931             {
932                 fprintf(stderr,
933                     "[*] GPG_DECRYPT_PW value is not properly set in stanza source '%s' in access file: '%s'\n",
934                     curr_acc->source, opts->config[CONF_ACCESS_FILE]);
935                 clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
936             }
937             add_acc_string(&(curr_acc->gpg_decrypt_pw), val);
938         }
939         else if(CONF_VAR_IS(var, "GPG_ALLOW_NO_PW"))
940         {
941             if(curr_acc->gpg_decrypt_pw != NULL && curr_acc->gpg_decrypt_pw[0] != '\0')
942                 free(curr_acc->gpg_decrypt_pw);
943
944             add_acc_string(&(curr_acc->gpg_decrypt_pw), "");
945         }
946         else if(CONF_VAR_IS(var, "GPG_REQUIRE_SIG"))
947         {
948             add_acc_bool(&(curr_acc->gpg_require_sig), val);
949         }
950         else if(CONF_VAR_IS(var, "GPG_IGNORE_SIG_VERIFY_ERROR"))
951         {
952             add_acc_bool(&(curr_acc->gpg_ignore_sig_error), val);
953         }
954         else if(CONF_VAR_IS(var, "GPG_REMOTE_ID"))
955         {
956             add_acc_string(&(curr_acc->gpg_remote_id), val);
957         }
958         else if(CONF_VAR_IS(var, "ACCESS_EXPIRE"))
959         {
960             add_acc_expire_time(opts, &(curr_acc->access_expire_time), val);
961         }
962         else if(CONF_VAR_IS(var, "ACCESS_EXPIRE_EPOCH"))
963         {
964             add_acc_expire_time_epoch(opts, &(curr_acc->access_expire_time), val);
965         }
966         else if(CONF_VAR_IS(var, "FORCE_NAT"))
967         {
968 #if FIREWALL_IPTABLES
969             if(strncasecmp(opts->config[CONF_ENABLE_IPT_FORWARDING], "Y", 1) !=0 )
970             {
971                 fprintf(stderr,
972                     "[*] FORCE_NAT requires ENABLE_IPT_FORWARDING to be enabled in fwknopd.conf\n");
973                 clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
974             }
975             add_acc_force_nat(opts, curr_acc, val);
976 #else
977             fprintf(stderr,
978                 "[*] FORCE_NAT not supported.\n");
979             clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
980 #endif
981         }
982         else
983         {
984             fprintf(stderr,
985                 "*Ignoring unknown access parameter: '%s' in %s\n",
986                 var, opts->config[CONF_ACCESS_FILE]
987             );
988         }
989     }
990
991     fclose(file_ptr);
992
993     /* Basic check to ensure that we got at least one SOURCE stanza with
994      * a valid KEY defined (valid meaning it has a value that is not
995      * "__CHANGEME__".
996     */
997     if (got_source == 0)
998     {
999         fprintf(stderr,
1000             "[*] Could not find valid SOURCE stanza in access file: '%s'\n",
1001             opts->config[CONF_ACCESS_FILE]);
1002         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1003     }
1004
1005     /* Sanity check the last stanza
1006     */
1007     if(!acc_data_is_valid(curr_acc))
1008     {
1009         fprintf(stderr,
1010             "[*] Data error in access file: '%s'\n",
1011             opts->config[CONF_ACCESS_FILE]);
1012         clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
1013     }
1014
1015     /* Expand our the expandable fields into their respective data buckets.
1016     */
1017
1018     expand_acc_ent_lists(opts);
1019
1020     /* Make sure default values are set where needed.
1021      * a default value.
1022     */
1023     set_acc_defaults(opts);
1024
1025     return;
1026 }
1027
1028 int
1029 compare_addr_list(acc_int_list_t *source_list, const uint32_t ip)
1030 {
1031     int match = 0;
1032
1033     while(source_list)
1034     {
1035         if((ip & source_list->mask) == (source_list->maddr & source_list->mask))
1036         {
1037             match = 1;
1038             break;
1039         }
1040
1041         source_list = source_list->next;
1042     }
1043
1044     return(match);
1045 }
1046
1047 /* Compare the contents of 2 port lists.  Return true on a match.
1048  * Match depends on the match_any flag.  if match_any is 1 then any
1049  * entry in the incoming data need only match one item to return true.
1050  * Otherwise all entries in the incoming data must have a corresponding
1051  * match in the access port_list.
1052 */
1053 static int
1054 compare_port_list(acc_port_list_t *in, acc_port_list_t *ac, const int match_any)
1055 {
1056     int a_cnt = 0;
1057     int i_cnt = 0;
1058
1059     acc_port_list_t *tlist;
1060     while(in)
1061     {
1062         i_cnt++;
1063
1064         tlist = ac;
1065         while(tlist)
1066         {
1067             if(in->proto == tlist->proto && in->port == tlist->port)
1068             {
1069                 a_cnt++;
1070                 if(match_any == 1)
1071                     return(1);
1072             }
1073             tlist = tlist->next;
1074         }
1075         in = in->next;
1076     }
1077
1078     return(i_cnt == a_cnt);
1079 }
1080
1081 /* Take a proto/port string (or mulitple comma-separated strings) and check
1082  * them against the list for the given access stanza.
1083  *
1084  * Return 1 if we are allowed
1085 */
1086 int
1087 acc_check_port_access(acc_stanza_t *acc, char *port_str)
1088 {
1089     int             res     = 1;
1090
1091     char            buf[32];
1092     char           *ndx, *start;
1093
1094     acc_port_list_t *o_pl   = acc->oport_list;
1095     acc_port_list_t *r_pl   = acc->rport_list;
1096
1097     acc_port_list_t *in_pl  = NULL;
1098
1099     start = port_str;
1100
1101     /* Create our own internal port_list from the incoming SPA data
1102      * for comparison.
1103     */
1104     for(ndx = start; *ndx; ndx++)
1105     {
1106         if(*ndx == ',')
1107         {
1108             strlcpy(buf, start, (ndx-start)+1);
1109             add_port_list_ent(&in_pl, buf);
1110             start = ndx+1;
1111         }
1112     }
1113     strlcpy(buf, start, (ndx-start)+1);
1114     add_port_list_ent(&in_pl, buf);
1115
1116     if(in_pl == NULL)
1117     {
1118         log_msg(LOG_ERR,
1119             "Unable to create acc_port_list from incoming data: %s", port_str
1120         );
1121         return(0);
1122     }
1123
1124     /* Start with restricted ports (if any).  Any match (even if only one
1125      * entry) means not allowed.
1126     */
1127     if((acc->rport_list != NULL) && (compare_port_list(in_pl, r_pl, 1)))
1128     {
1129         res = 0;
1130         goto cleanup_and_bail;
1131     }
1132
1133     /* For open port list, all must match.
1134     */
1135     if((acc->oport_list != NULL) && (!compare_port_list(in_pl, o_pl, 0)))
1136             res = 0;
1137
1138 cleanup_and_bail:
1139     free_acc_port_list(in_pl);
1140     return(res);
1141 }
1142
1143 /* Take a GPG ID string and check it against the list of allowed
1144  * GPG_REMOTE_ID's.
1145  *
1146  * Return 1 if we are allowed
1147 */
1148 int
1149 acc_check_gpg_remote_id(acc_stanza_t *acc, const char *gpg_id)
1150 {
1151     acc_string_list_t *ndx;
1152
1153     for(ndx = acc->gpg_remote_id_list; ndx != NULL; ndx=ndx->next)
1154         if(strcasecmp(ndx->str, gpg_id) == 0)
1155             return(1);
1156
1157     return(0);
1158 }
1159
1160 /* Dump the configuration
1161 */
1162 void
1163 dump_access_list(const fko_srv_options_t *opts)
1164 {
1165     int             i = 0;
1166
1167     acc_stanza_t    *acc = opts->acc_stanzas;
1168
1169     fprintf(stdout, "Current fwknopd access settings:\n");
1170
1171     if(!acc)
1172     {
1173         fprintf(stderr, "\n    ** No Access Settings Defined **\n\n");
1174         return;
1175     }
1176
1177     while(acc)
1178     {
1179         fprintf(stdout,
1180             "SOURCE (%i):  %s\n"
1181             "==============================================================\n"
1182             "                 OPEN_PORTS:  %s\n"
1183             "             RESTRICT_PORTS:  %s\n"
1184             "                        KEY:  <see the access.conf file>\n"
1185             "          FW_ACCESS_TIMEOUT:  %i\n"
1186             "            ENABLE_CMD_EXEC:  %s\n"
1187             "              CMD_EXEC_USER:  %s\n"
1188             "           REQUIRE_USERNAME:  %s\n"
1189             "     REQUIRE_SOURCE_ADDRESS:  %s\n"
1190             "             FORCE_NAT (ip):  %s\n"
1191             "          FORCE_NAT (proto):  %s\n"
1192             "           FORCE_NAT (port):  %d\n"
1193             "              ACCESS_EXPIRE:  %s"  /* asctime() adds a newline */
1194             "               GPG_HOME_DIR:  %s\n"
1195             "             GPG_DECRYPT_ID:  %s\n"
1196             "             GPG_DECRYPT_PW:  <see the access.conf file>\n"
1197             "            GPG_REQUIRE_SIG:  %s\n"
1198             "GPG_IGNORE_SIG_VERIFY_ERROR:  %s\n"
1199             "              GPG_REMOTE_ID:  %s\n",
1200             ++i,
1201             acc->source,
1202             (acc->open_ports == NULL) ? "<not set>" : acc->open_ports,
1203             (acc->restrict_ports == NULL) ? "<not set>" : acc->restrict_ports,
1204             //(acc->key == NULL) ? "<not set>" : acc->key,
1205             acc->fw_access_timeout,
1206             acc->enable_cmd_exec ? "Yes" : "No",
1207             (acc->cmd_exec_user == NULL) ? "<not set>" : acc->cmd_exec_user,
1208             (acc->require_username == NULL) ? "<not set>" : acc->require_username,
1209             acc->require_source_address ? "Yes" : "No",
1210             acc->force_nat ? acc->force_nat_ip : "<not set>",
1211             acc->force_nat && acc->force_nat_proto != NULL ? acc->force_nat_proto : "<not set>",
1212             acc->force_nat ? acc->force_nat_port : 0,
1213             (acc->access_expire_time > 0) ? asctime(localtime(&acc->access_expire_time)) : "<not set>\n",
1214             (acc->gpg_home_dir == NULL) ? "<not set>" : acc->gpg_home_dir,
1215             (acc->gpg_decrypt_id == NULL) ? "<not set>" : acc->gpg_decrypt_id,
1216             //(acc->gpg_decrypt_pw == NULL) ? "<not set>" : acc->gpg_decrypt_pw,
1217             acc->gpg_require_sig ? "Yes" : "No",
1218             acc->gpg_ignore_sig_error  ? "Yes" : "No",
1219             (acc->gpg_remote_id == NULL) ? "<not set>" : acc->gpg_remote_id
1220         );
1221
1222         fprintf(stdout, "\n");
1223
1224         acc = acc->next;
1225     }
1226
1227     fprintf(stdout, "\n");
1228     fflush(stdout);
1229 }
1230
1231 /***EOF***/