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