fix two more memory problems
[tor.git] / src / or / dirserv.c
blob14219beb1f917523c49800ecc40f997c6977bbec
1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
3 /* $Id$ */
5 #include "or.h"
7 extern or_options_t options; /* command-line and config-file options */
9 static int the_directory_is_dirty = 1;
10 static char *the_directory = NULL;
11 static int the_directory_len = -1;
13 static int list_running_servers(char **nicknames_out);
15 /************** Fingerprint handling code ************/
17 typedef struct fingerprint_entry_t {
18 char *nickname;
19 char *fingerprint;
20 } fingerprint_entry_t;
22 static fingerprint_entry_t fingerprint_list[MAX_ROUTERS_IN_DIR];
23 static int n_fingerprints = 0;
25 static void
26 add_fingerprint_to_dir(const char *nickname, const char *fp)
28 int i;
29 for (i = 0; i < n_fingerprints; ++i) {
30 if (!strcasecmp(fingerprint_list[i].nickname,nickname)) {
31 free(fingerprint_list[i].fingerprint);
32 fingerprint_list[i].fingerprint = tor_strdup(fp);
33 return;
36 fingerprint_list[n_fingerprints].nickname = tor_strdup(nickname);
37 fingerprint_list[n_fingerprints].fingerprint = tor_strdup(fp);
38 ++n_fingerprints;
41 int
42 dirserv_add_own_fingerprint(const char *nickname, crypto_pk_env_t *pk)
44 char fp[FINGERPRINT_LEN+1];
45 if (crypto_pk_get_fingerprint(pk, fp)<0) {
46 log_fn(LOG_ERR, "Error computing fingerprint");
47 return -1;
49 add_fingerprint_to_dir(nickname, fp);
50 return 0;
53 /* return 0 on success, -1 on failure */
54 int
55 dirserv_parse_fingerprint_file(const char *fname)
57 FILE *file;
58 char line[FINGERPRINT_LEN+MAX_NICKNAME_LEN+20+1];
59 char *nickname, *fingerprint;
60 fingerprint_entry_t fingerprint_list_tmp[MAX_ROUTERS_IN_DIR];
61 int n_fingerprints_tmp = 0;
62 int i, result;
64 if(!(file = fopen(fname, "r"))) {
65 log_fn(LOG_WARN, "Cannot open fingerprint file %s", fname);
66 return -1;
68 while( (result=parse_line_from_file(line, sizeof(line),file,&nickname,&fingerprint)) > 0) {
69 if (strlen(nickname) > MAX_NICKNAME_LEN) {
70 log(LOG_WARN, "Nickname %s too long in fingerprint file. Skipping.", nickname);
71 continue;
73 if(strlen(fingerprint) != FINGERPRINT_LEN ||
74 !crypto_pk_check_fingerprint_syntax(fingerprint)) {
75 log_fn(LOG_WARN, "Invalid fingerprint (nickname %s, fingerprint %s). Skipping.",
76 nickname, fingerprint);
77 continue;
79 for (i = 0; i < n_fingerprints_tmp; ++i) {
80 if (0==strcasecmp(fingerprint_list_tmp[i].nickname, nickname)) {
81 log(LOG_WARN, "Duplicate nickname %s. Skipping.",nickname);
82 break; /* out of the for. the 'if' below means skip to the next line. */
85 if(i == n_fingerprints_tmp) { /* not a duplicate */
86 fingerprint_list_tmp[n_fingerprints_tmp].nickname = tor_strdup(nickname);
87 fingerprint_list_tmp[n_fingerprints_tmp].fingerprint = tor_strdup(fingerprint);
88 ++n_fingerprints_tmp;
91 fclose(file);
92 if(result == 0) { /* eof; replace the global fingerprints list. */
93 dirserv_free_fingerprint_list();
94 memcpy(fingerprint_list, fingerprint_list_tmp,
95 sizeof(fingerprint_entry_t)*n_fingerprints_tmp);
96 n_fingerprints = n_fingerprints_tmp;
97 return 0;
99 /* error */
100 log_fn(LOG_WARN, "Error reading from fingerprint file");
101 for (i = 0; i < n_fingerprints_tmp; ++i) {
102 free(fingerprint_list_tmp[i].nickname);
103 free(fingerprint_list_tmp[i].fingerprint);
105 return -1;
108 /* return 1 if router's identity and nickname match. */
110 dirserv_router_fingerprint_is_known(const routerinfo_t *router)
112 int i;
113 fingerprint_entry_t *ent =NULL;
114 char fp[FINGERPRINT_LEN+1];
116 log_fn(LOG_DEBUG, "%d fingerprints known.", n_fingerprints);
117 for (i=0;i<n_fingerprints;++i) {
118 log_fn(LOG_DEBUG,"%s vs %s", router->nickname, fingerprint_list[i].nickname);
119 if (!strcasecmp(router->nickname,fingerprint_list[i].nickname)) {
120 ent = &fingerprint_list[i];
121 break;
125 if (!ent) { /* No such server known */
126 log_fn(LOG_WARN,"no fingerprint found for %s",router->nickname);
127 return 0;
129 if (crypto_pk_get_fingerprint(router->identity_pkey, fp)) {
130 log_fn(LOG_WARN,"error computing fingerprint");
131 return 0;
133 if (0==strcasecmp(ent->fingerprint, fp)) {
134 log_fn(LOG_DEBUG,"good fingerprint for %s",router->nickname);
135 return 1; /* Right fingerprint. */
136 } else {
137 log_fn(LOG_WARN,"mismatched fingerprint for %s",router->nickname);
138 return 0; /* Wrong fingerprint. */
142 void
143 dirserv_free_fingerprint_list()
145 int i;
146 for (i = 0; i < n_fingerprints; ++i) {
147 free(fingerprint_list[i].nickname);
148 free(fingerprint_list[i].fingerprint);
150 n_fingerprints = 0;
154 * Descriptor list
156 typedef struct descriptor_entry_t {
157 char *nickname;
158 time_t published;
159 size_t desc_len;
160 char *descriptor;
161 } descriptor_entry_t;
163 static descriptor_entry_t *descriptor_list[MAX_ROUTERS_IN_DIR];
164 static int n_descriptors = 0;
166 static void free_descriptor_entry(descriptor_entry_t *desc)
168 if (desc->descriptor)
169 free(desc->descriptor);
170 if (desc->nickname)
171 free(desc->nickname);
172 free(desc);
175 void
176 dirserv_free_descriptors()
178 int i;
179 for (i = 0; i < n_descriptors; ++i) {
180 free_descriptor_entry(descriptor_list[i]);
182 n_descriptors = 0;
185 /* Return 0 if descriptor added; -1 if descriptor rejected. Updates *desc
186 * to point after the descriptor if the descriptor is OK.
189 dirserv_add_descriptor(const char **desc)
191 descriptor_entry_t **desc_ent_ptr;
192 routerinfo_t *ri = NULL;
193 int i;
194 char *start, *end;
195 char *desc_tmp = NULL, *cp;
196 size_t desc_len;
198 start = strstr(*desc, "router ");
199 if (!start) {
200 log(LOG_WARN, "no descriptor found.");
201 goto err;
203 if ((end = strstr(start+6, "\nrouter "))) {
204 ++end; /* Include NL. */
205 } else if ((end = strstr(start+6, "\ndirectory-signature"))) {
206 ++end;
207 } else {
208 end = start+strlen(start);
210 desc_len = end-start;
211 cp = desc_tmp = tor_malloc(desc_len+1);
212 strncpy(desc_tmp, start, desc_len);
213 desc_tmp[desc_len]='\0';
215 /* Check: is the descriptor syntactically valid? */
216 ri = router_get_entry_from_string(&cp);
217 if (!ri) {
218 log(LOG_WARN, "Couldn't parse descriptor");
219 goto err;
221 free(desc_tmp); desc_tmp = NULL;
222 /* Okay. Now check whether the fingerprint is recognized. */
223 if (!dirserv_router_fingerprint_is_known(ri)) {
224 log(LOG_WARN, "Identity is unrecognized for descriptor");
225 goto err;
227 /* Do we already have an entry for this router? */
228 desc_ent_ptr = NULL;
229 for (i = 0; i < n_descriptors; ++i) {
230 if (!strcasecmp(ri->nickname, descriptor_list[i]->nickname)) {
231 desc_ent_ptr = &descriptor_list[i];
232 break;
235 if (desc_ent_ptr) {
236 /* if so, decide whether to update it. */
237 if ((*desc_ent_ptr)->published > ri->published_on) {
238 /* We already have a newer descriptor */
239 log_fn(LOG_INFO,"We already have a newer desc for nickname %s. Not adding.",ri->nickname);
240 /* This isn't really an error; return. */
241 if (desc_tmp) free(desc_tmp);
242 if (ri) routerinfo_free(ri);
243 *desc = end;
244 return 0;
246 /* We don't have a newer one; we'll update this one. */
247 free_descriptor_entry(*desc_ent_ptr);
248 } else {
249 /* Add this at the end. */
250 desc_ent_ptr = &descriptor_list[n_descriptors++];
253 (*desc_ent_ptr) = tor_malloc(sizeof(descriptor_entry_t));
254 (*desc_ent_ptr)->nickname = strdup(ri->nickname);
255 (*desc_ent_ptr)->published = ri->published_on;
256 (*desc_ent_ptr)->desc_len = desc_len;
257 (*desc_ent_ptr)->descriptor = tor_malloc(desc_len+1);
258 strncpy((*desc_ent_ptr)->descriptor, start, desc_len);
259 (*desc_ent_ptr)->descriptor[desc_len] = '\0';
260 *desc = end;
261 the_directory_is_dirty = 1;
263 routerinfo_free(ri);
264 return 0;
265 err:
266 if (desc_tmp)
267 free(desc_tmp);
268 if (ri)
269 routerinfo_free(ri);
271 return -1;
274 void
275 directory_set_dirty()
277 the_directory_is_dirty = 1;
280 int
281 dirserv_init_from_directory_string(const char *dir)
283 const char *cp = dir;
284 while(1) {
285 cp = strstr(cp, "\nrouter ");
286 if (!cp) break;
287 ++cp;
288 if (dirserv_add_descriptor(&cp)) {
289 return -1;
291 --cp; /*Back up to newline.*/
293 return 0;
296 static int
297 list_running_servers(char **nicknames_out)
299 char *nickname_lst[MAX_ROUTERS_IN_DIR];
300 connection_t **connection_array;
301 int n_conns;
302 connection_t *conn;
303 char *cp;
304 int n = 0, i;
305 int length;
306 *nicknames_out = NULL;
307 nickname_lst[n++] = options.Nickname;
309 get_connection_array(&connection_array, &n_conns);
310 for (i = 0; i<n_conns; ++i) {
311 conn = connection_array[i];
312 if (conn->type != CONN_TYPE_OR || conn->state != OR_CONN_STATE_OPEN)
313 continue; /* only list successfully handshaked OR's. */
314 if(!conn->nickname) /* it's an OP, don't list it */
315 continue;
316 nickname_lst[n++] = conn->nickname;
318 length = n + 1; /* spaces + EOS + 1. */
319 for (i = 0; i<n; ++i) {
320 length += strlen(nickname_lst[i]);
322 *nicknames_out = tor_malloc(length);
323 cp = *nicknames_out;
324 memset(cp,0,length);
325 for (i = 0; i<n; ++i) {
326 if (i)
327 strcat(cp, " ");
328 strcat(cp, nickname_lst[i]);
329 while (*cp)
330 ++cp;
332 return 0;
337 dirserv_dump_directory_to_string(char *s, int maxlen,
338 crypto_pk_env_t *private_key)
340 char *cp, *eos;
341 char digest[20];
342 char signature[128];
343 char published[33];
344 time_t published_on;
345 int i;
346 eos = s+maxlen;
348 if (list_running_servers(&cp))
349 return -1;
350 published_on = time(NULL);
351 strftime(published, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on));
352 snprintf(s, maxlen,
353 "signed-directory\n"
354 "published %s\n"
355 "recommended-software "RECOMMENDED_SOFTWARE_VERSIONS"\n"
356 "running-routers %s\n\n", published, cp);
357 free(cp);
358 i = strlen(s);
359 cp = s+i;
361 for (i = 0; i < n_descriptors; ++i) {
362 strncat(cp, descriptor_list[i]->descriptor, descriptor_list[i]->desc_len);
363 cp += descriptor_list[i]->desc_len;
364 assert(!*cp);
366 /* These multiple strlen calls are inefficient, but dwarfed by the RSA
367 signature.
369 i = strlen(s);
370 strncat(s, "directory-signature\n", maxlen-i);
371 i = strlen(s);
372 cp = s + i;
374 if (router_get_dir_hash(s,digest)) {
375 log_fn(LOG_WARN,"couldn't compute digest");
376 return -1;
378 if (crypto_pk_private_sign(private_key, digest, 20, signature) < 0) {
379 log_fn(LOG_WARN,"couldn't sign digest");
380 return -1;
382 log(LOG_DEBUG,"generated directory digest begins with %02x:%02x:%02x:%02x",
383 ((int)digest[0])&0xff,((int)digest[1])&0xff,
384 ((int)digest[2])&0xff,((int)digest[3])&0xff);
386 strncpy(cp,
387 "-----BEGIN SIGNATURE-----\n", maxlen-i);
389 i = strlen(s);
390 cp = s+i;
391 if (base64_encode(cp, maxlen-i, signature, 128) < 0) {
392 log_fn(LOG_WARN,"couldn't base64-encode signature");
393 return -1;
396 i = strlen(s);
397 cp = s+i;
398 strncat(cp, "-----END SIGNATURE-----\n", maxlen-i);
399 i = strlen(s);
400 if (i == maxlen) {
401 log_fn(LOG_WARN,"tried to exceed string length.");
402 return -1;
405 return 0;
408 size_t dirserv_get_directory(const char **directory)
410 char *new_directory;
411 char filename[512];
412 if (the_directory_is_dirty) {
413 new_directory = tor_malloc(MAX_DIR_SIZE);
414 if (dirserv_dump_directory_to_string(new_directory, MAX_DIR_SIZE,
415 get_identity_key())) {
416 log(LOG_WARN, "Error creating directory.");
417 free(new_directory);
418 return 0;
420 if (the_directory)
421 free(the_directory);
422 the_directory = new_directory;
423 the_directory_len = strlen(the_directory);
424 log_fn(LOG_INFO,"New directory (size %d):\n%s",the_directory_len,
425 the_directory);
426 the_directory_is_dirty = 0;
427 /* Now read the directory we just made in order to update our own
428 * router lists. This does more signature checking than is strictly
429 * necessary, but safe is better than sorry. */
430 new_directory = tor_strdup(the_directory);
431 /* use a new copy of the dir, since get_dir_from_string scribbles on it */
432 if (router_get_dir_from_string(new_directory, get_identity_key())) {
433 log_fn(LOG_ERR, "We just generated a directory we can't parse. Dying.");
434 exit(0);
436 free(new_directory);
437 sprintf(filename,"%s/cached-directory", options.DataDirectory);
438 if(write_str_to_file(filename,the_directory) < 0) {
439 log_fn(LOG_WARN, "Couldn't write cached directory to disk. Ignoring.");
441 } else {
442 log(LOG_INFO,"Directory still clean, reusing.");
444 *directory = the_directory;
445 return the_directory_len;