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