comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:c7f6b056b673
1 /* Distributed Checksum Clearinghouse
2 *
3 * client-ID and password parsing
4 *
5 * Copyright (c) 2008 by Rhyolite Software, LLC
6 *
7 * This agreement is not applicable to any entity which sells anti-spam
8 * solutions to others or provides an anti-spam solution as part of a
9 * security solution sold to other entities, or to a private network
10 * which employs the DCC or uses data provided by operation of the DCC
11 * but does not provide corresponding data to other users.
12 *
13 * Permission to use, copy, modify, and distribute this software without
14 * changes for any purpose with or without fee is hereby granted, provided
15 * that the above copyright notice and this permission notice appear in all
16 * copies and any distributed versions or copies are either unchanged
17 * or not called anything similar to "DCC" or "Distributed Checksum
18 * Clearinghouse".
19 *
20 * Parties not eligible to receive a license under this agreement can
21 * obtain a commercial license to use DCC by contacting Rhyolite Software
22 * at sales@rhyolite.com.
23 *
24 * A commercial license would be for Distributed Checksum and Reputation
25 * Clearinghouse software. That software includes additional features. This
26 * free license for Distributed ChecksumClearinghouse Software does not in any
27 * way grant permision to use Distributed Checksum and Reputation Clearinghouse
28 * software
29 *
30 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE, LLC DISCLAIMS ALL
31 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE, LLC
33 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
34 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
35 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
36 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
37 * SOFTWARE.
38 *
39 * Rhyolite Software DCC 1.3.103-1.50 $Revision$
40 */
41
42 #include "dcc_clnt.h"
43 #include "dcc_ids.h"
44 #include "dcc_heap_debug.h"
45
46 /* must be in dcclib for WIN32 */
47
48 DCC_PATH ids_path;
49
50 time_t ids_mtime;
51
52
53 /* authenticated client database
54 * assume there will be at most 500 clients and servers known to each server */
55 #define ID_TBL_LEN 509 /* must be prime */
56 #define ID_TBL_MAX (ID_TBL_LEN*4)
57 #define ID_HASH(id) (id % ID_TBL_LEN)
58 #define ID_HASH_ENTRY(id) id_tbl_hash[ID_HASH(id)]
59 static ID_TBL *id_tbl_hash[ID_TBL_LEN];
60 static int id_tbl_len = 0;
61 static ID_TBL *id_tbl_free;
62
63
64 /* find an ID_TBL entry */
65 ID_TBL *
66 find_id_tbl(DCC_CLNT_ID id)
67 {
68 ID_TBL *tp;
69
70 for (tp = ID_HASH_ENTRY(id); tp != 0; tp = tp->fwd) {
71 if (tp->id == id)
72 return tp;
73 }
74 return 0;
75 }
76
77
78
79 /* add an ID_TBL entry that is known to be absent */
80 ID_TBL *
81 add_id_tbl(DCC_CLNT_ID id)
82 {
83 ID_TBL *tp, **tpp;
84 int i;
85
86 /* make more entries if necessary */
87 if (!id_tbl_free) {
88 i = 16;
89 if (id_tbl_len <= ID_TBL_MAX
90 && id_tbl_len+i > ID_TBL_MAX)
91 dcc_error_msg("ID table overflow");
92 id_tbl_len += i;
93 tp = dcc_malloc(i*sizeof(*tp));
94 do {
95 tp->fwd = id_tbl_free;
96 id_tbl_free = tp;
97 } while (++tp, --i > 0);
98 }
99
100 tp = id_tbl_free;
101 id_tbl_free = tp->fwd;
102
103 memset(tp, 0, sizeof(*tp));
104 tp->id = id;
105 tpp = &ID_HASH_ENTRY(id);
106 tp->fwd = *tpp;
107 *tpp = tp;
108
109 return tp;
110 }
111
112
113
114 ID_TBL *
115 enum_ids(ID_TBL *tp)
116 {
117 ID_TBL **tpp;
118
119 if (!tp) {
120 tpp = id_tbl_hash;
121 } else {
122 if (tp->fwd)
123 return tp->fwd;
124 tpp = &ID_HASH_ENTRY(tp->id)+1;
125 }
126 while (tpp <= LAST(id_tbl_hash)) {
127 tp = *tpp;
128 if (tp)
129 return tp;
130 ++tpp;
131 }
132 return 0;
133 }
134
135
136
137 u_char /* 0=bad 1=ok 2=forever */
138 parse_dccd_delay(DCC_EMSG emsg, time_t *delay_usp, u_int *delay_inflatep,
139 const char *val,
140 const char *fnm, int lno)
141 {
142 DCC_FNM_LNO_BUF fnm_buf;
143 time_t delay_ms;
144 u_long l;
145 char *p1, *p2;
146
147 *delay_inflatep = DCC_ANON_INFLATE_OFF;
148 *delay_usp = DCC_ANON_DELAY_US_BLACKLIST;
149 if (!strcasecmp(val, "forever"))
150 return 2;
151
152 delay_ms = strtoul(val, &p1, 10);
153
154 if (delay_ms > DCC_ANON_DELAY_US_BLACKLIST/1000) {
155 dcc_pemsg(EX_DATAERR, emsg, "invalid delay \"%s\"%s > %d", val,
156 fnm_lno(&fnm_buf, fnm, lno),
157 DCC_ANON_DELAY_US_BLACKLIST/1000);
158 return 0;
159 } else if (*p1 == '\0') {
160 *delay_inflatep = DCC_ANON_INFLATE_OFF;
161 } else if (*p1 != ',' && *p1 != '*') {
162 dcc_pemsg(EX_DATAERR, emsg, "unrecognized delay \"%s\"%s", val,
163 fnm_lno(&fnm_buf, fnm, lno));
164 return 0;
165 } else {
166 l = strtoul(++p1, &p2, 10);
167 if (*p2 != '\0') {
168 dcc_pemsg(EX_DATAERR, emsg,
169 "unrecognized delay inflation \"%s\"%s",
170 p1,
171 fnm_lno(&fnm_buf, fnm, lno));
172 return 0;
173 }
174 if (l == 0)
175 l = DCC_ANON_INFLATE_OFF;
176 *delay_inflatep = l;
177 }
178
179 *delay_usp = delay_ms*1000;
180 return 1;
181 }
182
183
184
185 u_char
186 set_ids_path(DCC_EMSG emsg, const char *ids_nm)
187 {
188 if (!ids_nm || ids_nm[0] == '\0')
189 ids_nm = IDS_NM_DEF;
190 if (!fnm2rel(ids_path, ids_nm, 0)) {
191 dcc_pemsg(EX_DATAERR, emsg, "\"%s\" is a bad ids file name",
192 ids_nm);
193 return 0;
194 }
195 return 1;
196 }
197
198
199
200 /* (re)load the client-ID and password database
201 * -1=failed to find target, 0=sick file, 1=ok, 2=file unchanged */
202 int
203 load_ids(DCC_EMSG emsg,
204 DCC_CLNT_ID tgt_id, /* DCC_ID_ANON or needed ID */
205 const ID_TBL **tgt_tbl,
206 u_char force)
207 {
208 DCC_FNM_LNO_BUF fnm_buf;
209 ID_TBL t, *tp, **tpp;
210 FILE *f;
211 int lno, passwords;
212 u_char found_it;
213 int result;
214 char buf[sizeof(ID_TBL)*2+1];
215 const char *bufp;
216 char id_buf[30];
217 struct stat sb;
218 char *p, *p1;
219
220 if (tgt_tbl)
221 *tgt_tbl = 0;
222
223 if (!set_ids_path(emsg, 0))
224 return -1;
225
226 if (!force) {
227 if (!dcc_ck_private(emsg, &sb, ids_path, -1)) {
228 ids_mtime = 0;
229 return -1;
230 }
231
232 if (ids_mtime == sb.st_mtime)
233 return 2;
234 }
235
236 f = fopen(ids_path, "r");
237 if (!f) {
238 dcc_pemsg(EX_NOINPUT, emsg, "fopen(%s): %s",
239 fnm2abs_err(0, ids_path), ERROR_STR());
240 return -1;
241 }
242
243 /* the file contains passwords, so refuse to use it if anyone else
244 * can read it */
245 if (!dcc_ck_private(emsg, &sb, ids_path, fileno(f))) {
246 fclose(f);
247 ids_mtime = 0;
248 return -1;
249 }
250
251 /* empty the table */
252 for (tpp = id_tbl_hash; tpp <= LAST(id_tbl_hash); ++tpp) {
253 while ((tp = *tpp) != 0) {
254 *tpp = tp->fwd;
255 memset(tp, 0, sizeof(*tp));
256 tp->fwd = id_tbl_free;
257 id_tbl_free = tp;
258 }
259 }
260
261 ids_mtime = sb.st_mtime;
262
263 passwords = 0;
264 lno = 0;
265 result = 1;
266 found_it = (tgt_id == DCC_ID_ANON);
267 for (;;) {
268 /* read and parse a line contain a client-ID and key(s) */
269 bufp = fgets(buf, sizeof(buf), f);
270 if (!bufp) {
271 if (ferror(f))
272 dcc_pemsg(EX_IOERR, emsg, "fgets(%s): %s",
273 fnm2abs_err(0, ids_path),
274 ERROR_STR());
275 break;
276 }
277 ++lno;
278
279 /* Ignore blank lines and lines starting with '#'.
280 * Note that '#' flags a comment only at the start of
281 * the line to avoid dealing with the escaping hassles
282 * of allowing '#' in passwords. */
283 bufp += strspn(bufp, DCC_WHITESPACE);
284 if (*bufp == '\0' || *bufp == '#')
285 continue;
286
287 memset(&t, 0, sizeof(t));
288 t.delay_inflate = DCC_ANON_INFLATE_OFF;
289
290 /* Each substantive line has the form:
291 *
292 * ID[,rpt-ok][,delay=ms[*inflate]] password1 password2
293 * ID,delay=forever
294 *
295 * Both passwords are always accepted. They are intended
296 * to be the previous and current or the current and
297 * next to allow the password to be changed at both the
298 * client and the server without loss of service. */
299
300 bufp = dcc_parse_word(emsg, id_buf, sizeof(id_buf),
301 bufp, "ID", ids_path, lno);
302 if (!bufp) {
303 result = 0;
304 continue;
305 }
306
307 if (*bufp)
308 t.flags |= ID_FLG_SETTINGS;
309
310 p = strchr(id_buf, ',');
311 if (p) {
312 *p++ = '\0';
313 t.flags |= ID_FLG_SETTINGS;
314 }
315 t.id = dcc_get_id(emsg, id_buf, ids_path, lno);
316 if (t.id == DCC_ID_INVALID) {
317 result = 0;
318 continue;
319 }
320 if (t.id == DCC_ID_ANON) {
321 if (result) {
322 dcc_pemsg(EX_DATAERR, emsg,
323 "invalid ID \"%s\"%s",
324 id_buf,
325 fnm_lno(&fnm_buf, ids_path, lno));
326 result = 0;
327 }
328 continue;
329 }
330
331 for (; p; p = p1) {
332 p1 = strchr(p, ',');
333 if (p1)
334 *p1++ = '\0';
335
336 if (t.id >= DCC_CLNT_ID_MIN
337 && t.id <= DCC_CLNT_ID_MAX) {
338 if (!strcasecmp(p, "rpt-ok")
339 || !strcasecmp(p, "rpt_ok")) {
340 t.flags |= ID_FLG_RPT_OK;
341 continue;
342 }
343
344 if (!CLITCMP(p, "delay=")) {
345 if (!parse_dccd_delay(emsg, &t.delay_us,
346 &t.delay_inflate,
347 p+LITZ("delay="),
348 ids_path, lno)) {
349 result = 0;
350 }
351 continue;
352 }
353 }
354 if (t.id >= DCC_SRVR_ID_MIN
355 && t.id <= DCC_SRVR_ID_MAX
356 && t.srvr_type == 0) {
357 if (!strcasecmp(p, "simple")) {
358 continue;
359 }
360 if (!strcasecmp(p, "ignore")) {
361 continue;
362 }
363 if (!strcasecmp(p, "rogue")) {
364 continue;
365 }
366 if (!strcasecmp(p, "commercial")) {
367 continue;
368 }
369 }
370 if (result) {
371 dcc_pemsg(EX_DATAERR, emsg,
372 "invalid option \"%s\"%s", p,
373 fnm_lno(&fnm_buf, ids_path, lno));
374 result = 0;
375 }
376 }
377
378 bufp = parse_passwd(emsg, t.cur_passwd,
379 bufp, "current password", ids_path, lno);
380 if (!bufp) {
381 result = 0;
382 continue;
383 }
384 bufp = parse_passwd(emsg, t.next_passwd,
385 bufp, "next password", ids_path, lno);
386 if (!bufp) {
387 result = 0;
388 continue;
389 }
390 if (*bufp != '\0') {
391 if (result) {
392 dcc_pemsg(EX_DATAERR, emsg,
393 "invalid next password for ID %d%s",
394 t.id,
395 fnm_lno(&fnm_buf, ids_path, lno));
396 result = 0;
397 }
398 continue;
399 }
400
401 /* put the entry into the hash table if not already present */
402 tp = find_id_tbl(t.id);
403 if (!tp)
404 tp = add_id_tbl(t.id);
405
406 /* If the ID is already present, the file is bad unless
407 * the previous or current line is
408 * only a placeholder showing that the ID exists. */
409 if ((tp->flags & ID_FLG_SETTINGS)
410 && (t.flags & ID_FLG_SETTINGS)) {
411 if (result) {
412 dcc_pemsg(EX_DATAERR, emsg, "duplicate ID %d%s",
413 t.id, fnm_lno(&fnm_buf,
414 ids_path, lno));
415 result = 0;
416 }
417 }
418
419 /* copy settings to the hash table */
420 if (t.flags & ID_FLG_SETTINGS) {
421 tp->flags = t.flags;
422 tp->srvr_type = t.srvr_type;
423 tp->delay_us = t.delay_us;
424 tp->delay_inflate = t.delay_inflate;
425
426 if (t.cur_passwd[0] != '\0') {
427 ++passwords;
428 memcpy(tp->cur_passwd, t.cur_passwd,
429 sizeof(tp->cur_passwd));
430 memcpy(tp->next_passwd, t.next_passwd,
431 sizeof(tp->next_passwd));
432
433 /* remember target password */
434 if (t.id == tgt_id) {
435 found_it = 1;
436 if (tgt_tbl)
437 *tgt_tbl = tp;
438 }
439 }
440 }
441 }
442 fclose(f);
443
444 if (result && !passwords) {
445 dcc_pemsg(EX_DATAERR, emsg, "%s contains no passwords",
446 fnm2abs_err(0, ids_path));
447 result = -1;
448 }
449 if (!found_it && result >= 0) {
450 dcc_pemsg(EX_DATAERR, emsg,
451 "%s does not contain the password for ID %d",
452 fnm2abs_err(0, ids_path), tgt_id);
453 result = -1;
454 }
455
456 return result;
457 }