1 /* Copyright 2001,2002,2003 Roger Dingledine, Matej Pfajfar. */
2 /* See LICENSE for licensing information */
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 /************** Fingerprint handling code ************/
15 typedef struct fingerprint_entry_t
{
18 } fingerprint_entry_t
;
20 static fingerprint_entry_t fingerprint_list
[MAX_ROUTERS_IN_DIR
];
21 static int n_fingerprints
= 0;
24 add_fingerprint_to_dir(const char *nickname
, const char *fp
)
27 for (i
= 0; i
< n_fingerprints
; ++i
) {
28 if (!strcasecmp(fingerprint_list
[i
].nickname
,nickname
)) {
29 free(fingerprint_list
[i
].fingerprint
);
30 fingerprint_list
[i
].fingerprint
= strdup(fp
);
34 fingerprint_list
[n_fingerprints
].nickname
= strdup(nickname
);
35 fingerprint_list
[n_fingerprints
].fingerprint
= strdup(fp
);
40 dirserv_add_own_fingerprint(const char *nickname
, crypto_pk_env_t
*pk
)
42 char fp
[FINGERPRINT_LEN
+1];
43 if (crypto_pk_get_fingerprint(pk
, fp
)<0) {
44 log_fn(LOG_ERR
, "Error computing fingerprint");
47 add_fingerprint_to_dir(nickname
, fp
);
51 /* return 0 on success, -1 on failure */
53 dirserv_parse_fingerprint_file(const char *fname
)
56 char line
[FINGERPRINT_LEN
+MAX_NICKNAME_LEN
+20+1];
57 char *nickname
, *fingerprint
;
58 fingerprint_entry_t fingerprint_list_tmp
[MAX_ROUTERS_IN_DIR
];
59 int n_fingerprints_tmp
= 0;
62 if(!(file
= fopen(fname
, "r"))) {
63 log_fn(LOG_WARNING
, "Cannot open fingerprint file %s", fname
);
66 while( (result
=parse_line_from_file(line
, sizeof(line
),file
,&nickname
,&fingerprint
)) > 0) {
67 if (strlen(nickname
) > MAX_NICKNAME_LEN
) {
68 log(LOG_WARNING
, "Nickname %s too long in fingerprint file. Skipping.", nickname
);
71 if(strlen(fingerprint
) != FINGERPRINT_LEN
||
72 !crypto_pk_check_fingerprint_syntax(fingerprint
)) {
73 log_fn(LOG_WARNING
, "Invalid fingerprint (nickname %s, fingerprint %s). Skipping.",
74 nickname
, fingerprint
);
77 for (i
= 0; i
< n_fingerprints_tmp
; ++i
) {
78 if (0==strcasecmp(fingerprint_list_tmp
[i
].nickname
, nickname
)) {
79 log(LOG_WARNING
, "Duplicate nickname %s. Skipping.",nickname
);
80 break; /* out of the for. the 'if' below means skip to the next line. */
83 if(i
== n_fingerprints_tmp
) { /* not a duplicate */
84 fingerprint_list_tmp
[n_fingerprints_tmp
].nickname
= strdup(nickname
);
85 fingerprint_list_tmp
[n_fingerprints_tmp
].fingerprint
= strdup(fingerprint
);
90 if(result
== 0) { /* eof; replace the global fingerprints list. */
91 dirserv_free_fingerprint_list();
92 memcpy(fingerprint_list
, fingerprint_list_tmp
,
93 sizeof(fingerprint_entry_t
)*n_fingerprints_tmp
);
94 n_fingerprints
= n_fingerprints_tmp
;
98 log_fn(LOG_WARNING
, "Error reading from fingerprint file");
99 for (i
= 0; i
< n_fingerprints_tmp
; ++i
) {
100 free(fingerprint_list_tmp
[i
].nickname
);
101 free(fingerprint_list_tmp
[i
].fingerprint
);
106 /* return 1 if router's identity and nickname match. */
108 dirserv_router_fingerprint_is_known(const routerinfo_t
*router
)
111 fingerprint_entry_t
*ent
=NULL
;
112 char fp
[FINGERPRINT_LEN
+1];
114 log_fn(LOG_DEBUG
, "%d fingerprints known.", n_fingerprints
);
115 for (i
=0;i
<n_fingerprints
;++i
) {
116 log_fn(LOG_DEBUG
,"%s vs %s", router
->nickname
, fingerprint_list
[i
].nickname
);
117 if (!strcasecmp(router
->nickname
,fingerprint_list
[i
].nickname
)) {
118 ent
= &fingerprint_list
[i
];
123 if (!ent
) { /* No such server known */
124 log_fn(LOG_WARNING
,"no fingerprint found for %s",router
->nickname
);
127 if (crypto_pk_get_fingerprint(router
->identity_pkey
, fp
)) {
128 log_fn(LOG_WARNING
,"error computing fingerprint");
131 if (0==strcasecmp(ent
->fingerprint
, fp
)) {
132 log_fn(LOG_DEBUG
,"good fingerprint for %s",router
->nickname
);
133 return 1; /* Right fingerprint. */
135 log_fn(LOG_WARNING
,"mismatched fingerprint for %s",router
->nickname
);
136 return 0; /* Wrong fingerprint. */
141 dirserv_free_fingerprint_list()
144 for (i
= 0; i
< n_fingerprints
; ++i
) {
145 free(fingerprint_list
[i
].nickname
);
146 free(fingerprint_list
[i
].fingerprint
);
154 typedef struct descriptor_entry_t
{
159 } descriptor_entry_t
;
161 static descriptor_entry_t
*descriptor_list
[MAX_ROUTERS_IN_DIR
];
162 static int n_descriptors
= 0;
164 static void free_descriptor_entry(descriptor_entry_t
*desc
)
166 if (desc
->descriptor
)
167 free(desc
->descriptor
);
169 free(desc
->nickname
);
174 dirserv_free_descriptors()
177 for (i
= 0; i
< n_descriptors
; ++i
) {
178 free_descriptor_entry(descriptor_list
[i
]);
183 /* Return 0 if descriptor added; -1 if descriptor rejected. Updates *desc
184 * to point after the descriptor if the descriptor is OK.
187 dirserv_add_descriptor(const char **desc
)
189 descriptor_entry_t
**desc_ent_ptr
;
190 routerinfo_t
*ri
= NULL
;
193 char *desc_tmp
= NULL
, *cp
;
196 start
= strstr(*desc
, "router ");
198 log(LOG_WARNING
, "no descriptor found.");
201 if ((end
= strstr(start
+6, "\nrouter "))) {
202 ++end
; /* Include NL. */
203 } else if ((end
= strstr(start
+6, "\ndirectory-signature"))) {
206 end
= start
+strlen(start
);
208 desc_len
= end
-start
;
209 cp
= desc_tmp
= tor_malloc(desc_len
+1);
210 strncpy(desc_tmp
, start
, desc_len
);
211 desc_tmp
[desc_len
]='\0';
213 /* Check: is the descriptor syntactically valid? */
214 ri
= router_get_entry_from_string(&cp
);
216 log(LOG_WARNING
, "Couldn't parse descriptor");
219 free(desc_tmp
); desc_tmp
= NULL
;
220 /* Okay. Now check whether the fingerprint is recognized. */
221 if (!dirserv_router_fingerprint_is_known(ri
)) {
222 log(LOG_WARNING
, "Identity is unrecognized for descriptor");
225 /* Do we already have an entry for this router? */
227 for (i
= 0; i
< n_descriptors
; ++i
) {
228 if (!strcasecmp(ri
->nickname
, descriptor_list
[i
]->nickname
)) {
229 desc_ent_ptr
= &descriptor_list
[i
];
234 /* if so, decide whether to update it. */
235 if ((*desc_ent_ptr
)->published
> ri
->published_on
) {
236 /* We already have a newer descriptor */
237 log_fn(LOG_INFO
,"We already have a newer desc for nickname %s. Not adding.",ri
->nickname
);
238 /* This isn't really an error; return. */
239 if (desc_tmp
) free(desc_tmp
);
240 if (ri
) routerinfo_free(ri
);
244 /* We don't have a newer one; we'll update this one. */
245 free_descriptor_entry(*desc_ent_ptr
);
247 /* Add this at the end. */
248 desc_ent_ptr
= &descriptor_list
[n_descriptors
++];
251 (*desc_ent_ptr
) = tor_malloc(sizeof(descriptor_entry_t
));
252 (*desc_ent_ptr
)->nickname
= ri
->nickname
;
253 (*desc_ent_ptr
)->published
= ri
->published_on
;
254 (*desc_ent_ptr
)->desc_len
= desc_len
;
255 (*desc_ent_ptr
)->descriptor
= tor_malloc(desc_len
+1);
256 strncpy((*desc_ent_ptr
)->descriptor
, start
, desc_len
);
257 (*desc_ent_ptr
)->descriptor
[desc_len
] = '\0';
259 the_directory_is_dirty
= 1;
273 directory_set_dirty()
275 the_directory_is_dirty
= 1;
279 dirserv_init_from_directory_string(const char *dir
)
281 const char *cp
= dir
;
283 cp
= strstr(cp
, "\nrouter ");
286 if (dirserv_add_descriptor(&cp
)) {
289 --cp
; /*Back up to newline.*/
295 dirserv_dump_directory_to_string(char *s
, int maxlen
,
296 crypto_pk_env_t
*private_key
)
306 if (list_running_servers(&cp
))
308 published_on
= time(NULL
);
309 strftime(published
, 32, "%Y-%m-%d %H:%M:%S", gmtime(&published_on
));
313 "recommended-software "RECOMMENDED_SOFTWARE_VERSIONS
"\n"
314 "running-routers %s\n", published
, cp
);
319 for (i
= 0; i
< n_descriptors
; ++i
) {
320 strncat(cp
, descriptor_list
[i
]->descriptor
, descriptor_list
[i
]->desc_len
);
321 cp
+= descriptor_list
[i
]->desc_len
;
324 /* These multiple strlen calls are inefficient, but dwarfed by the RSA
328 strncat(s
, "directory-signature\n", maxlen
-i
);
332 if (router_get_dir_hash(s
,digest
)) {
333 log_fn(LOG_WARNING
,"couldn't compute digest");
336 if (crypto_pk_private_sign(private_key
, digest
, 20, signature
) < 0) {
337 log_fn(LOG_WARNING
,"couldn't sign digest");
340 log(LOG_DEBUG
,"generated directory digest begins with %02x:%02x:%02x:%02x",
341 ((int)digest
[0])&0xff,((int)digest
[1])&0xff,
342 ((int)digest
[2])&0xff,((int)digest
[3])&0xff);
345 "-----BEGIN SIGNATURE-----\n", maxlen
-i
);
349 if (base64_encode(cp
, maxlen
-i
, signature
, 128) < 0) {
350 log_fn(LOG_WARNING
,"couldn't base64-encode signature");
356 strncat(cp
, "-----END SIGNATURE-----\n", maxlen
-i
);
359 log_fn(LOG_WARNING
,"tried to exceed string length.");
366 size_t dirserv_get_directory(const char **directory
)
370 if (the_directory_is_dirty
) {
371 new_directory
= tor_malloc(MAX_DIR_SIZE
);
372 if (dirserv_dump_directory_to_string(new_directory
, MAX_DIR_SIZE
,
373 get_identity_key())) {
374 log(LOG_WARNING
, "Error creating directory.");
380 the_directory
= new_directory
;
381 the_directory_len
= strlen(the_directory
);
382 log_fn(LOG_INFO
,"New directory (size %d):\n%s",the_directory_len
,
384 the_directory_is_dirty
= 0;
385 /* Now read the directory we just made in order to update our own
386 * router lists. This does more signature checking than is strictly
387 * necessary, but safe is better than sorry. */
388 new_directory
= strdup(the_directory
);
389 /* use a new copy of the dir, since get_dir_from_string scribbles on it */
390 if (router_get_dir_from_string(new_directory
, get_identity_key())) {
391 log_fn(LOG_ERR
, "We just generated a directory we can't parse. Dying.");
395 sprintf(filename
,"%s/cached-directory", options
.DataDirectory
);
396 if(write_str_to_file(filename
,the_directory
) < 0) {
397 log_fn(LOG_WARNING
, "Couldn't write cached directory to disk. Ignoring.");
400 log(LOG_INFO
,"Directory still clean, reusing.");
402 *directory
= the_directory
;
403 return the_directory_len
;