Mercurial > notdcc
view 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 source
/* 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; }