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