diff dcclib/load_ids.c @ 0:c7f6b056b673

First import of vendor version
author Peter Gervai <grin@grin.hu>
date Tue, 10 Mar 2009 13:49:58 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dcclib/load_ids.c	Tue Mar 10 13:49:58 2009 +0100
@@ -0,0 +1,457 @@
+/* Distributed Checksum Clearinghouse
+ *
+ * client-ID and password parsing
+ *
+ * Copyright (c) 2008 by Rhyolite Software, LLC
+ *
+ * This agreement is not applicable to any entity which sells anti-spam
+ * solutions to others or provides an anti-spam solution as part of a
+ * security solution sold to other entities, or to a private network
+ * which employs the DCC or uses data provided by operation of the DCC
+ * but does not provide corresponding data to other users.
+ *
+ * Permission to use, copy, modify, and distribute this software without
+ * changes for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies and any distributed versions or copies are either unchanged
+ * or not called anything similar to "DCC" or "Distributed Checksum
+ * Clearinghouse".
+ *
+ * Parties not eligible to receive a license under this agreement can
+ * obtain a commercial license to use DCC by contacting Rhyolite Software
+ * at sales@rhyolite.com.
+ *
+ * A commercial license would be for Distributed Checksum and Reputation
+ * Clearinghouse software.  That software includes additional features.  This
+ * free license for Distributed ChecksumClearinghouse Software does not in any
+ * way grant permision to use Distributed Checksum and Reputation Clearinghouse
+ * software
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
+ * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
+ * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+ * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ *
+ * Rhyolite Software DCC 1.3.103-1.50 $Revision$
+ */
+
+#include "dcc_clnt.h"
+#include "dcc_ids.h"
+#include "dcc_heap_debug.h"
+
+/* must be in dcclib for WIN32 */
+
+DCC_PATH ids_path;
+
+time_t ids_mtime;
+
+
+/* authenticated client database
+ * assume there will be at most 500 clients and servers known to each server */
+#define ID_TBL_LEN 509			/* must be prime */
+#define ID_TBL_MAX (ID_TBL_LEN*4)
+#define ID_HASH(id) (id % ID_TBL_LEN)
+#define ID_HASH_ENTRY(id) id_tbl_hash[ID_HASH(id)]
+static ID_TBL *id_tbl_hash[ID_TBL_LEN];
+static int id_tbl_len = 0;
+static ID_TBL *id_tbl_free;
+
+
+/* find an ID_TBL entry	*/
+ID_TBL *
+find_id_tbl(DCC_CLNT_ID id)
+{
+	ID_TBL *tp;
+
+	for (tp = ID_HASH_ENTRY(id); tp != 0; tp = tp->fwd) {
+		if (tp->id == id)
+			return tp;
+	}
+	return 0;
+}
+
+
+
+/* add an ID_TBL entry that is known to be absent */
+ID_TBL *
+add_id_tbl(DCC_CLNT_ID id)
+{
+	ID_TBL *tp, **tpp;
+	int i;
+
+	/* make more entries if necessary */
+	if (!id_tbl_free) {
+		i = 16;
+		if (id_tbl_len <= ID_TBL_MAX
+		    && id_tbl_len+i > ID_TBL_MAX)
+			dcc_error_msg("ID table overflow");
+		id_tbl_len += i;
+		tp = dcc_malloc(i*sizeof(*tp));
+		do {
+			tp->fwd = id_tbl_free;
+			id_tbl_free = tp;
+		} while (++tp, --i > 0);
+	}
+
+	tp = id_tbl_free;
+	id_tbl_free = tp->fwd;
+
+	memset(tp, 0, sizeof(*tp));
+	tp->id = id;
+	tpp = &ID_HASH_ENTRY(id);
+	tp->fwd = *tpp;
+	*tpp = tp;
+
+	return tp;
+}
+
+
+
+ID_TBL *
+enum_ids(ID_TBL *tp)
+{
+	ID_TBL **tpp;
+
+	if (!tp) {
+		tpp = id_tbl_hash;
+	} else {
+		if (tp->fwd)
+			return tp->fwd;
+		tpp = &ID_HASH_ENTRY(tp->id)+1;
+	}
+	while (tpp <= LAST(id_tbl_hash)) {
+		tp = *tpp;
+		if (tp)
+			return tp;
+		++tpp;
+	}
+	return 0;
+}
+
+
+
+u_char					/* 0=bad 1=ok 2=forever */
+parse_dccd_delay(DCC_EMSG emsg, time_t *delay_usp, u_int *delay_inflatep,
+		 const char *val,
+		 const char *fnm, int lno)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	time_t delay_ms;
+	u_long l;
+	char *p1, *p2;
+
+	*delay_inflatep = DCC_ANON_INFLATE_OFF;
+	*delay_usp = DCC_ANON_DELAY_US_BLACKLIST;
+	if (!strcasecmp(val, "forever"))
+		return 2;
+
+	delay_ms = strtoul(val, &p1, 10);
+
+	if (delay_ms > DCC_ANON_DELAY_US_BLACKLIST/1000) {
+		dcc_pemsg(EX_DATAERR, emsg, "invalid delay \"%s\"%s > %d", val,
+			  fnm_lno(&fnm_buf, fnm, lno),
+			  DCC_ANON_DELAY_US_BLACKLIST/1000);
+		return 0;
+	} else if (*p1 == '\0') {
+		*delay_inflatep = DCC_ANON_INFLATE_OFF;
+	} else if (*p1 != ',' && *p1 != '*') {
+		dcc_pemsg(EX_DATAERR, emsg, "unrecognized delay \"%s\"%s", val,
+			  fnm_lno(&fnm_buf, fnm, lno));
+		return 0;
+	} else {
+		l = strtoul(++p1, &p2, 10);
+		if (*p2 != '\0') {
+			dcc_pemsg(EX_DATAERR, emsg,
+				  "unrecognized delay inflation \"%s\"%s",
+				  p1,
+				  fnm_lno(&fnm_buf, fnm, lno));
+			return 0;
+		}
+		if (l == 0)
+			l = DCC_ANON_INFLATE_OFF;
+		*delay_inflatep = l;
+	}
+
+	*delay_usp = delay_ms*1000;
+	return 1;
+}
+
+
+
+u_char
+set_ids_path(DCC_EMSG emsg, const char *ids_nm)
+{
+	if (!ids_nm || ids_nm[0] == '\0')
+		ids_nm = IDS_NM_DEF;
+	if (!fnm2rel(ids_path, ids_nm, 0)) {
+		dcc_pemsg(EX_DATAERR, emsg, "\"%s\" is a bad ids file name",
+			  ids_nm);
+		return 0;
+	}
+	return 1;
+}
+
+
+
+/* (re)load the client-ID and password database
+ * -1=failed to find target,  0=sick file,  1=ok,  2=file unchanged */
+int
+load_ids(DCC_EMSG emsg,
+	 DCC_CLNT_ID tgt_id,		/* DCC_ID_ANON or needed ID */
+	 const ID_TBL **tgt_tbl,
+	 u_char force)
+{
+	DCC_FNM_LNO_BUF fnm_buf;
+	ID_TBL t, *tp, **tpp;
+	FILE *f;
+	int lno, passwords;
+	u_char found_it;
+	int result;
+	char buf[sizeof(ID_TBL)*2+1];
+	const char *bufp;
+	char id_buf[30];
+	struct stat sb;
+	char *p, *p1;
+
+	if (tgt_tbl)
+		*tgt_tbl = 0;
+
+	if (!set_ids_path(emsg, 0))
+		return -1;
+
+	if (!force) {
+		if (!dcc_ck_private(emsg, &sb, ids_path, -1)) {
+			ids_mtime = 0;
+			return -1;
+		}
+
+		if (ids_mtime == sb.st_mtime)
+			return 2;
+	}
+
+	f = fopen(ids_path, "r");
+	if (!f) {
+		dcc_pemsg(EX_NOINPUT, emsg, "fopen(%s): %s",
+			  fnm2abs_err(0, ids_path), ERROR_STR());
+		return -1;
+	}
+
+	/* the file contains passwords, so refuse to use it if anyone else
+	 * can read it */
+	if (!dcc_ck_private(emsg, &sb, ids_path, fileno(f))) {
+		fclose(f);
+		ids_mtime = 0;
+		return -1;
+	}
+
+	/* empty the table */
+	for (tpp = id_tbl_hash; tpp <= LAST(id_tbl_hash); ++tpp) {
+		while ((tp = *tpp) != 0) {
+			*tpp = tp->fwd;
+			memset(tp, 0, sizeof(*tp));
+			tp->fwd = id_tbl_free;
+			id_tbl_free = tp;
+		}
+	}
+
+	ids_mtime = sb.st_mtime;
+
+	passwords = 0;
+	lno = 0;
+	result = 1;
+	found_it = (tgt_id == DCC_ID_ANON);
+	for (;;) {
+		/* read and parse a line contain a client-ID and key(s) */
+		bufp = fgets(buf, sizeof(buf), f);
+		if (!bufp) {
+			if (ferror(f))
+				dcc_pemsg(EX_IOERR, emsg, "fgets(%s): %s",
+					  fnm2abs_err(0, ids_path),
+					  ERROR_STR());
+			break;
+		}
+		++lno;
+
+		/* Ignore blank lines and lines starting with '#'.
+		 * Note that '#' flags a comment only at the start of
+		 * the line to avoid dealing with the escaping hassles
+		 * of allowing '#' in passwords. */
+		bufp += strspn(bufp, DCC_WHITESPACE);
+		if (*bufp == '\0' || *bufp == '#')
+			continue;
+
+		memset(&t, 0, sizeof(t));
+		t.delay_inflate = DCC_ANON_INFLATE_OFF;
+
+		/* Each substantive line has the form:
+		 *
+		 *	ID[,rpt-ok][,delay=ms[*inflate]] password1 password2
+		 *	ID,delay=forever
+		 *
+		 *  Both passwords are always accepted.  They are intended
+		 *  to be the previous and current or the current and
+		 *  next to allow the password to be changed at both the
+		 *  client and the server without loss of service. */
+
+		bufp = dcc_parse_word(emsg, id_buf, sizeof(id_buf),
+				      bufp, "ID", ids_path, lno);
+		if (!bufp) {
+			result = 0;
+			continue;
+		}
+
+		if (*bufp)
+			t.flags |= ID_FLG_SETTINGS;
+
+		p = strchr(id_buf, ',');
+		if (p) {
+			*p++ = '\0';
+			t.flags |= ID_FLG_SETTINGS;
+		}
+		t.id = dcc_get_id(emsg, id_buf, ids_path, lno);
+		if (t.id == DCC_ID_INVALID) {
+			result = 0;
+			continue;
+		}
+		if (t.id == DCC_ID_ANON) {
+			if (result) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "invalid ID \"%s\"%s",
+					  id_buf,
+					  fnm_lno(&fnm_buf, ids_path, lno));
+				result = 0;
+			}
+			continue;
+		}
+
+		for (; p; p = p1) {
+			p1 = strchr(p, ',');
+			if (p1)
+				*p1++ = '\0';
+
+			if (t.id >= DCC_CLNT_ID_MIN
+			    && t.id <= DCC_CLNT_ID_MAX) {
+				if (!strcasecmp(p, "rpt-ok")
+				    || !strcasecmp(p, "rpt_ok")) {
+					t.flags |= ID_FLG_RPT_OK;
+					continue;
+				}
+
+				if (!CLITCMP(p, "delay=")) {
+					if (!parse_dccd_delay(emsg, &t.delay_us,
+							&t.delay_inflate,
+							p+LITZ("delay="),
+							ids_path, lno)) {
+					    result = 0;
+					}
+					continue;
+				}
+			}
+			if (t.id >= DCC_SRVR_ID_MIN
+			    && t.id <= DCC_SRVR_ID_MAX
+			    && t.srvr_type == 0) {
+				if (!strcasecmp(p, "simple")) {
+					continue;
+				}
+				if (!strcasecmp(p, "ignore")) {
+					continue;
+				}
+				if (!strcasecmp(p, "rogue")) {
+					continue;
+				}
+				if (!strcasecmp(p, "commercial")) {
+					continue;
+				}
+			}
+			if (result) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "invalid option \"%s\"%s", p,
+					  fnm_lno(&fnm_buf, ids_path, lno));
+				result = 0;
+			}
+		}
+
+		bufp = parse_passwd(emsg, t.cur_passwd,
+				    bufp, "current password", ids_path, lno);
+		if (!bufp) {
+			result = 0;
+			continue;
+		}
+		bufp = parse_passwd(emsg, t.next_passwd,
+				    bufp, "next password", ids_path, lno);
+		if (!bufp) {
+			result = 0;
+			continue;
+		}
+		if (*bufp != '\0') {
+			if (result) {
+				dcc_pemsg(EX_DATAERR, emsg,
+					  "invalid next password for ID %d%s",
+					  t.id,
+					  fnm_lno(&fnm_buf, ids_path, lno));
+				result = 0;
+			}
+			continue;
+		}
+
+		/* put the entry into the hash table if not already present */
+		tp = find_id_tbl(t.id);
+		if (!tp)
+			tp = add_id_tbl(t.id);
+
+		/* If the ID is already present, the file is bad unless
+		 * the previous or current line is
+		 * only a placeholder showing that the ID exists. */
+		if ((tp->flags & ID_FLG_SETTINGS)
+		    && (t.flags & ID_FLG_SETTINGS)) {
+			if (result) {
+				dcc_pemsg(EX_DATAERR, emsg, "duplicate ID %d%s",
+					  t.id, fnm_lno(&fnm_buf,
+							ids_path, lno));
+				result = 0;
+			}
+		}
+
+		/* copy settings to the hash table */
+		if (t.flags & ID_FLG_SETTINGS) {
+			tp->flags = t.flags;
+			tp->srvr_type = t.srvr_type;
+			tp->delay_us = t.delay_us;
+			tp->delay_inflate = t.delay_inflate;
+
+			if (t.cur_passwd[0] != '\0') {
+				++passwords;
+				memcpy(tp->cur_passwd, t.cur_passwd,
+				       sizeof(tp->cur_passwd));
+				memcpy(tp->next_passwd, t.next_passwd,
+				       sizeof(tp->next_passwd));
+
+				/* remember target password */
+				if (t.id == tgt_id) {
+					found_it = 1;
+					if (tgt_tbl)
+					    *tgt_tbl = tp;
+				}
+			}
+		}
+	}
+	fclose(f);
+
+	if (result && !passwords) {
+		dcc_pemsg(EX_DATAERR, emsg, "%s contains no passwords",
+			  fnm2abs_err(0, ids_path));
+		result = -1;
+	}
+	if (!found_it && result >= 0) {
+		dcc_pemsg(EX_DATAERR, emsg,
+			  "%s does not contain the password for ID %d",
+			  fnm2abs_err(0, ids_path), tgt_id);
+		result = -1;
+	}
+
+	return result;
+}