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