Mercurial > notdcc
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; +}