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