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