summaryrefslogtreecommitdiff
path: root/lib/securenets.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/securenets.c')
-rw-r--r--lib/securenets.c359
1 files changed, 359 insertions, 0 deletions
diff --git a/lib/securenets.c b/lib/securenets.c
new file mode 100644
index 0000000..90f0910
--- /dev/null
+++ b/lib/securenets.c
@@ -0,0 +1,359 @@
+/* Copyright (c) 1996-2014, 2016 Thorsten Kukuk
+ Author: Thorsten Kukuk <kukuk@suse.de>
+
+ The YP Server is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The YP Server is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with the YP Server; see the file COPYING. If
+ not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+ Suite 500, Boston, MA 02110-1335, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "access.h"
+#include "log_msg.h"
+
+#ifndef SECURENETS
+#define SECURENETS YPMAPDIR "/securenets"
+#endif
+
+const char *securenetsfile = SECURENETS;
+
+typedef struct securenet
+{
+ sa_family_t family;
+ struct sockaddr_storage network;
+ struct sockaddr_storage netmask;
+ struct securenet *next;
+}
+securenet_t;
+
+static securenet_t *securenets = NULL;
+
+
+void
+dump_securenets (void)
+{
+ struct securenet *sn;
+ int i = 0;
+
+ log_msg ("--- securenets start ---");
+ sn = securenets;
+ while (sn)
+ {
+ char host[INET6_ADDRSTRLEN];
+ char mask[INET6_ADDRSTRLEN];
+
+ i++;
+
+ switch (sn->family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *network =
+ (struct sockaddr_in *)&(sn->network);
+ struct sockaddr_in *netmask =
+ (struct sockaddr_in *)&(sn->netmask);
+
+ log_msg ("entry %d: %s %s", i,
+ inet_ntop (AF_INET, &network->sin_addr,
+ host, sizeof (host)),
+ inet_ntop (AF_INET, &netmask->sin_addr,
+ mask, sizeof (mask)));
+ }
+ break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *network =
+ (struct sockaddr_in6 *)&(sn->network);
+ struct sockaddr_in6 *netmask =
+ (struct sockaddr_in6 *)&(sn->netmask);
+
+ log_msg ("entry %d: %s %s", i,
+ inet_ntop (AF_INET6, &network->sin6_addr,
+ host, sizeof (host)),
+ inet_ntop (AF_INET6, &netmask->sin6_addr,
+ mask, sizeof (mask)));
+ }
+ break;
+ default:
+ log_msg ("ERROR: Unknown family: %i", sn->family);
+ break;
+ }
+ sn = sn->next;
+ }
+ log_msg ("--- securenets end ---");
+}
+
+int
+load_securenets (void)
+{
+ char buf[2 * NI_MAXHOST + 2];
+ char col_mask[NI_MAXHOST + 1], col_host[NI_MAXHOST + 1];
+ struct addrinfo hints, *res0;
+ FILE *in;
+ securenet_t *work, *tmp;
+ int error, line = 0;
+
+
+ /* If securenets isn't NULL, we should reload the securents file. */
+ if (securenets != NULL)
+ {
+ log_msg ("Reloading securenets file\n");
+ while (securenets != NULL)
+ {
+ work = securenets;
+ securenets = securenets->next;
+ free (work);
+ }
+ }
+ securenets = NULL;
+ work = NULL;
+ tmp = NULL;
+
+ if ((in = fopen (securenetsfile, "r")) == NULL)
+ {
+ log_msg ("WARNING: no %s file found!\n", securenetsfile);
+ return 1;
+ }
+
+ while (!feof (in))
+ {
+ int nr_entries;
+
+ memset (col_mask, 0, sizeof (col_mask));
+ memset (col_host, 0, sizeof (col_host));
+ memset (buf, 0, sizeof (buf));
+ if (fgets (buf, sizeof (buf) -1, in) == NULL)
+ continue;
+ line++;
+
+ if (buf[0] == '\0' || buf[0] == '#' || buf[0] == '\n')
+ continue;
+
+ nr_entries = sscanf (buf, "%s %s", col_mask, col_host);
+
+ if (nr_entries == 2)
+ {
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if ((error = getaddrinfo (col_host, NULL, &hints, &res0)))
+ {
+ log_msg ("securenets (%d) badly formated: %s",
+ line, gai_strerror (error));
+ continue;
+ }
+
+ if ((tmp = malloc (sizeof (securenet_t))) == NULL)
+ {
+ log_msg ("ERROR: could not allocate enough memory! [%s|%d]\n",
+ __FILE__, __LINE__);
+ exit (1);
+ }
+
+ tmp->next = NULL;
+ memcpy (&tmp->network, res0->ai_addr, res0->ai_addrlen);
+ tmp->family = res0->ai_addr->sa_family;
+ freeaddrinfo(res0);
+
+ if (strcmp (col_mask, "host") == 0)
+ {
+ if (tmp->family == AF_INET)
+ strcpy (col_mask, "255.255.255.255");
+ else if (tmp->family == AF_INET6)
+ strcpy (col_mask, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ else
+ {
+ log_msg ("securenets(%d): unsupported address family: %i", line, tmp->family);
+ free (tmp);
+ continue;
+ }
+ }
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if ((error = getaddrinfo (col_mask, NULL, &hints, &res0)))
+ {
+ log_msg ("securenets (%d) badly formated: %s",
+ line, gai_strerror (error));
+ free (tmp);
+ continue;
+ }
+ memcpy (&tmp->netmask, res0->ai_addr, res0->ai_addrlen);
+ freeaddrinfo(res0);
+ }
+ else if (nr_entries == 1)
+ {
+ /* 127.0.0.1/8, 2001:0db8:85a3::8a2e:0370:7334/64 */
+ int netmask_len = 0;
+ char *p;
+
+ if ((p = strrchr (buf, '/')))
+ {
+ *p = ' ';
+ nr_entries = sscanf(buf, "%s %i", col_host, &netmask_len);
+ if (nr_entries != 2)
+ goto malformed;
+
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ if ((error = getaddrinfo (col_host, NULL, &hints, &res0)))
+ {
+ log_msg ("securenets (%d) badly formated: %s",
+ line, gai_strerror (error));
+ continue;
+ }
+ }
+ else
+ goto malformed;
+
+ if ((tmp = malloc (sizeof (securenet_t))) == NULL)
+ {
+ log_msg ("ERROR: could not allocate enough memory! [%s|%d]\n",
+ __FILE__, __LINE__);
+ exit (1);
+ }
+
+ tmp->next = NULL;
+ memcpy (&tmp->network, res0->ai_addr, res0->ai_addrlen);
+ tmp->family = res0->ai_addr->sa_family;
+ switch (tmp->family) /* prefixlen -> netmask */
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in sin;
+
+ memcpy (&sin, res0->ai_addr, res0->ai_addrlen);
+ sin.sin_addr.s_addr = (0xFFFFFFFFu >> (32 - netmask_len));
+ memcpy (&tmp->netmask, &sin, sizeof (struct sockaddr_in));
+ }
+ break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 sin6;
+ int i, j;
+
+ memcpy (&sin6, res0->ai_addr, res0->ai_addrlen);
+ for (i = netmask_len, j = 0; i > 0; i -= 8, ++j)
+ sin6.sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff
+ : (unsigned long)(( 0xffU << ( 8 - i ) ) & 0xffU );
+ memcpy (&tmp->netmask, &sin6, sizeof (struct sockaddr_in6));
+ }
+ break;
+ default:
+ log_msg ("securenets(%d): unsupported address family: %i",
+ line, tmp->family);
+ free (tmp);
+ continue;
+ break;
+ }
+ freeaddrinfo (res0);
+ }
+ else
+ {
+ malformed:
+ log_msg ("securenets(%d): malformed line, ignore it\n", line);
+ continue;
+ }
+
+
+ if (work == NULL)
+ {
+ work = tmp;
+ securenets = work;
+ }
+ else
+ {
+ work->next = tmp;
+ work = work->next;
+ }
+ }
+ fclose (in);
+
+ if (debug_flag)
+ dump_securenets ();
+
+ return 0;
+}
+
+int
+securenet_host (struct netconfig *nconf, struct netbuf *nbuf)
+{
+ securenet_t *ptr;
+ struct __rpc_sockinfo si;
+
+ if (nconf == NULL || nbuf == NULL || nbuf->len <= 0)
+ return 0;
+
+ if (!__rpc_nconf2sockinfo(nconf, &si))
+ return 0;
+
+ ptr = securenets;
+
+ if (ptr == NULL) /* this means no securenets file, grant access */
+ return 1;
+ else
+ while (ptr != NULL)
+ {
+ if (si.si_af == ptr->family)
+ switch (ptr->family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *sin1 = nbuf->buf;
+ struct sockaddr_in *sin2 = (struct sockaddr_in *)&(ptr->netmask);
+ struct sockaddr_in *sin3 = (struct sockaddr_in *)&(ptr->network);
+
+ if ((sin1->sin_addr.s_addr & sin2->sin_addr.s_addr) ==
+ sin3->sin_addr.s_addr)
+ return 1;
+ }
+ break;
+ case AF_INET6:
+ {
+ int i;
+ struct sockaddr_in6 *sin1 = nbuf->buf;
+ struct sockaddr_in6 *sin2 =
+ (struct sockaddr_in6 *)&(ptr->netmask);
+ struct sockaddr_in6 *sin3 =
+ (struct sockaddr_in6 *)&(ptr->network);
+
+ for (i = 0; i < 16; i++)
+ if ((sin1->sin6_addr.s6_addr[i] & sin2->sin6_addr.s6_addr[i])
+ != sin3->sin6_addr.s6_addr[i])
+ goto next;
+ return 1;
+ }
+ break;
+ default:
+ goto next; /* Something is wrong here, should not happen. */
+ }
+ next:
+ ptr = ptr->next;
+ }
+ return 0;
+}