summaryrefslogtreecommitdiff
path: root/lib/access.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/access.c')
-rw-r--r--lib/access.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/lib/access.c b/lib/access.c
new file mode 100644
index 0000000..646d798
--- /dev/null
+++ b/lib/access.c
@@ -0,0 +1,312 @@
+/* Copyright (C) 1997-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
+ version 2 as published by the Free Software Foundation.
+
+ 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 <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#if USE_SD_NOTIFY
+#include <systemd/sd-daemon.h>
+#endif
+
+#include "log_msg.h"
+#include "ypserv_conf.h"
+#include "access.h"
+#include "yp_db.h"
+#include "yp.h"
+
+static conffile_t *conf = NULL;
+const char *confdir = CONFDIR;
+
+void
+load_config (void)
+{
+ conffile_t *tmp;
+
+ if (conf != NULL)
+ {
+ log_msg ("Reloading %s/ypserv.conf", confdir);
+ while (conf)
+ {
+ tmp = conf;
+ conf = conf->next;
+
+ free (tmp->map);
+ free (tmp);
+ }
+ }
+
+ conf = load_ypserv_conf (confdir);
+}
+
+/* Give a string with the DEFINE description back */
+static char *
+ypproc_name (int proc)
+{
+ switch (proc)
+ {
+ case YPPROC_NULL:
+ return "ypproc_null";
+ case YPPROC_DOMAIN:
+ return "ypproc_domain";
+ case YPPROC_DOMAIN_NONACK:
+ return "ypproc_domain_nonack";
+ case YPPROC_MATCH:
+ return "ypproc_match";
+ case YPPROC_FIRST:
+ return "ypproc_first";
+ case YPPROC_NEXT:
+ return "ypproc_next";
+ case YPPROC_XFR:
+ return "ypproc_xfr";
+ case YPPROC_CLEAR:
+ return "ypproc_clear";
+ case YPPROC_ALL:
+ return "ypproc_all";
+ case YPPROC_MASTER:
+ return "ypproc_master";
+ case YPPROC_ORDER:
+ return "ypproc_order";
+ case YPPROC_MAPLIST:
+ return "ypproc_maplist";
+ case YPPROC_NEWXFR:
+ return "ypproc_newxfr";
+ default:
+ log_msg ("Unknown procedure '%i'", proc);
+ return "unknown ?";
+ }
+}
+
+/* The is_valid_domain function checks the domain specified bye the
+ caller to make sure it's actually served by this server.
+
+ Return 1 if the name is a valid domain name served by us, else 0. */
+int
+is_valid_domain (const char *domain)
+{
+ struct stat sbuf;
+
+ if (domain == NULL || domain[0] == '\0' ||
+ strcmp (domain, "binding") == 0 ||
+ strcmp (domain, "..") == 0 ||
+ strcmp (domain, ".") == 0 ||
+ strchr (domain, '/'))
+ return 0;
+
+ if (stat (domain, &sbuf) < 0 || !S_ISDIR (sbuf.st_mode))
+ return 0;
+
+ return 1;
+}
+
+static struct netbuf *
+copy_netbuf (struct netbuf *src)
+{
+ struct netbuf *dst;
+
+ if (src == NULL)
+ return NULL;
+
+ dst = calloc (1, sizeof (struct netbuf));
+ if (dst == NULL)
+ return NULL;
+
+ dst->buf = malloc (src->len);
+ if (dst->buf == NULL)
+ {
+ free (dst);
+ return NULL;
+ }
+
+ dst->len = src->len;
+ dst->maxlen = src->len;
+ memcpy (dst->buf, src->buf, src->len);
+
+ return dst;
+}
+
+/* return values:
+ 0: both IP addresses are identical
+ 1: the IP addresses are not identical */
+static int
+cmp_netbuf (struct netbuf *nbuf1, struct netbuf *nbuf2)
+{
+ if (nbuf1 == NULL || nbuf2 == NULL)
+ return 1;
+
+ if (nbuf1->len != nbuf2->len)
+ return 1;
+
+ if (memcmp (nbuf1->buf, nbuf2->buf, nbuf1->len) == 0)
+ return 0;
+
+ return 1;
+}
+
+/* By default, we use the securenet list, to check if the client
+ is secure.
+
+ return 1, if request comes from an authorized host
+ return 0, if securenets does not allow access from this host
+ return -1, if request comes from an unauthorized host
+ return -2, if the map name is not valid
+ return -3, if the domain is not valid
+ return -4, if the map does not exist */
+int
+is_valid (struct svc_req *rqstp, const char *map, const char *domain)
+{
+ static struct netbuf *oldaddr = NULL; /* so we dont log multiple times */
+ static int oldstatus = -1;
+ struct netconfig *nconf;
+ struct netbuf *rqhost;
+ struct __rpc_sockinfo si;
+ int status;
+
+ if (domain && is_valid_domain (domain) == 0)
+ return -3;
+
+ if (map && (map[0] == '\0' || strchr (map ,'/')))
+ return -2;
+
+ rqhost = svc_getrpccaller (rqstp->rq_xprt);
+ nconf = getnetconfigent (rqstp->rq_xprt->xp_netid);
+
+ status = securenet_host (nconf, rqhost);
+
+ if (!__rpc_nconf2sockinfo(nconf, &si))
+ return -1;
+
+ if ((map != NULL) && status && si.si_af == AF_INET)
+ {
+ struct sockaddr_in *sin = rqhost->buf;
+ conffile_t *work;
+
+ work = conf;
+ while (work)
+ {
+ if ((sin->sin_addr.s_addr & work->netmask.s_addr) == work->network.s_addr)
+ if (strcmp (work->domain, domain) == 0 ||
+ strcmp (work->domain, "*") == 0)
+ if (strcmp (work->map, map) == 0 || strcmp (work->map, "*") == 0)
+ break;
+ work = work->next;
+ }
+
+ if (work != NULL)
+ switch (work->security)
+ {
+ case SEC_NONE:
+ break;
+ case SEC_DENY:
+ status = -1;
+ break;
+ case SEC_PORT:
+ if (taddr2port (nconf, rqhost) >= IPPORT_RESERVED)
+ status = -1;
+ break;
+ }
+ else if (domain != NULL)
+ {
+ /* The map is not in the access list, maybe it
+ has a YP_SECURE key ? */
+ DB_FILE dbp = ypdb_open (domain, map);
+ if (dbp != NULL)
+ {
+ datum key;
+
+ key.dsize = sizeof ("YP_SECURE") - 1;
+ key.dptr = "YP_SECURE";
+ if (ypdb_exists (dbp, key))
+ if (taddr2port (nconf, rqhost) >= IPPORT_RESERVED)
+ status = -1;
+ ypdb_close (dbp);
+ }
+ else
+ status = -4;
+ }
+ }
+
+ if (debug_flag)
+ {
+ char host[INET6_ADDRSTRLEN];
+
+ log_msg ("%sconnect from %s port %d to procedure %s (%s,%s;%d)",
+ status ? "" : "refused ",
+ taddr2ipstr (nconf, rqhost, host, sizeof (host)),
+ taddr2port (nconf, rqhost), ypproc_name (rqstp->rq_proc),
+ domain ? domain : "", map ? map : "", status);
+ }
+ else
+ {
+ if ((status < 1 && status != -4) &&
+ (cmp_netbuf (oldaddr, rqhost) || (status != oldstatus)))
+ {
+ char host[INET6_ADDRSTRLEN];
+
+ syslog (LOG_WARNING,
+ "refused connect from %s port %d to procedure %s (%s,%s;%d)",
+ taddr2ipstr (nconf, rqhost, host, sizeof (host)),
+ taddr2port (nconf, rqhost), ypproc_name (rqstp->rq_proc),
+ domain ? domain : "", map ? map : "", status);
+ }
+ }
+
+ /* Create a copy of the netbuf and free the old one */
+ if (oldaddr)
+ {
+ if (oldaddr->buf)
+ free (oldaddr->buf);
+ free (oldaddr);
+ }
+ oldaddr = copy_netbuf (rqhost);
+ oldstatus = status;
+
+ freenetconfigent (nconf);
+
+ return status;
+}
+
+/* Send a messages to systemd daemon, that inicialization of daemon
+ is finished and daemon is ready to accept connections.
+ It is a nop if we don't use systemd. */
+void
+announce_ready()
+{
+#if USE_SD_NOTIFY
+ int result;
+
+ result = sd_notifyf(0, "READY=1\n"
+ "STATUS=Processing requests...\n"
+ "MAINPID=%lu", (unsigned long) getpid());
+
+ /* Return code from sd_notifyf can be ignored, as per sd_notifyf(3).
+ However, if we use systemd's native unit file, we need to send
+ this message to let systemd know that daemon is ready.
+ Thus, we want to know that the call had some issues. */
+ if (result < 0)
+ log_msg ("sd_notifyf failed: %s\n", strerror(-result));
+#endif
+}