Line data Source code
1 : /* crlfetch.c - LDAP access
2 : * Copyright (C) 2002 Klarälvdalens Datakonsult AB
3 : * Copyright (C) 2003, 2004, 2005, 2006, 2007 g10 Code GmbH
4 : *
5 : * This file is part of DirMngr.
6 : *
7 : * DirMngr is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 2 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * DirMngr is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License
18 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <config.h>
22 :
23 : #include <stdio.h>
24 : #include <errno.h>
25 : #include <npth.h>
26 :
27 : #include "crlfetch.h"
28 : #include "dirmngr.h"
29 : #include "misc.h"
30 : #include "http.h"
31 :
32 : #if USE_LDAP
33 : # include "ldap-wrapper.h"
34 : #endif
35 :
36 : /* For detecting armored CRLs received via HTTP (yes, such CRLS really
37 : exits, e.g. http://grid.fzk.de/ca/gridka-crl.pem at least in June
38 : 2008) we need a context in the reader callback. */
39 : struct reader_cb_context_s
40 : {
41 : estream_t fp; /* The stream used with the ksba reader. */
42 : int checked:1; /* PEM/binary detection ahs been done. */
43 : int is_pem:1; /* The file stream is PEM encoded. */
44 : struct b64state b64state; /* The state used for Base64 decoding. */
45 : };
46 :
47 :
48 : /* We need to associate a reader object with the reader callback
49 : context. This table is used for it. */
50 : struct file_reader_map_s
51 : {
52 : ksba_reader_t reader;
53 : struct reader_cb_context_s *cb_ctx;
54 : };
55 : #define MAX_FILE_READER 50
56 : static struct file_reader_map_s file_reader_map[MAX_FILE_READER];
57 :
58 : /* Associate FP with READER. If the table is full wait until another
59 : thread has removed an entry. */
60 : static void
61 0 : register_file_reader (ksba_reader_t reader, struct reader_cb_context_s *cb_ctx)
62 : {
63 : int i;
64 :
65 : for (;;)
66 : {
67 0 : for (i=0; i < MAX_FILE_READER; i++)
68 0 : if (!file_reader_map[i].reader)
69 : {
70 0 : file_reader_map[i].reader = reader;
71 0 : file_reader_map[i].cb_ctx = cb_ctx;
72 0 : return;
73 : }
74 0 : log_info (_("reader to file mapping table full - waiting\n"));
75 0 : npth_sleep (2);
76 0 : }
77 : }
78 :
79 : /* Scan the table for an entry matching READER, remove that entry and
80 : return the associated file pointer. */
81 : static struct reader_cb_context_s *
82 0 : get_file_reader (ksba_reader_t reader)
83 : {
84 0 : struct reader_cb_context_s *cb_ctx = NULL;
85 : int i;
86 :
87 0 : for (i=0; i < MAX_FILE_READER; i++)
88 0 : if (file_reader_map[i].reader == reader)
89 : {
90 0 : cb_ctx = file_reader_map[i].cb_ctx;
91 0 : file_reader_map[i].reader = NULL;
92 0 : file_reader_map[i].cb_ctx = NULL;
93 0 : break;
94 : }
95 0 : return cb_ctx;
96 : }
97 :
98 :
99 :
100 : static int
101 0 : my_es_read (void *opaque, char *buffer, size_t nbytes, size_t *nread)
102 : {
103 0 : struct reader_cb_context_s *cb_ctx = opaque;
104 : int result;
105 :
106 0 : result = es_read (cb_ctx->fp, buffer, nbytes, nread);
107 0 : if (result)
108 0 : return result;
109 : /* Fixme we should check whether the semantics of es_read are okay
110 : and well defined. I have some doubts. */
111 0 : if (nbytes && !*nread && es_feof (cb_ctx->fp))
112 0 : return gpg_error (GPG_ERR_EOF);
113 0 : if (!nread && es_ferror (cb_ctx->fp))
114 0 : return gpg_error (GPG_ERR_EIO);
115 :
116 0 : if (!cb_ctx->checked && *nread)
117 : {
118 0 : int c = *(unsigned char *)buffer;
119 :
120 0 : cb_ctx->checked = 1;
121 0 : if ( ((c & 0xc0) >> 6) == 0 /* class: universal */
122 0 : && (c & 0x1f) == 16 /* sequence */
123 0 : && (c & 0x20) /* is constructed */ )
124 : ; /* Binary data. */
125 : else
126 : {
127 0 : cb_ctx->is_pem = 1;
128 0 : b64dec_start (&cb_ctx->b64state, "");
129 : }
130 : }
131 0 : if (cb_ctx->is_pem && *nread)
132 : {
133 : size_t nread2;
134 :
135 0 : if (b64dec_proc (&cb_ctx->b64state, buffer, *nread, &nread2))
136 : {
137 : /* EOF from decoder. */
138 0 : *nread = 0;
139 0 : result = gpg_error (GPG_ERR_EOF);
140 : }
141 : else
142 0 : *nread = nread2;
143 : }
144 :
145 0 : return result;
146 : }
147 :
148 :
149 : /* Fetch CRL from URL and return the entire CRL using new ksba reader
150 : object in READER. Note that this reader object should be closed
151 : only using ldap_close_reader. */
152 : gpg_error_t
153 0 : crl_fetch (ctrl_t ctrl, const char *url, ksba_reader_t *reader)
154 : {
155 : gpg_error_t err;
156 : parsed_uri_t uri;
157 0 : char *free_this = NULL;
158 0 : int redirects_left = 2; /* We allow for 2 redirect levels. */
159 :
160 0 : *reader = NULL;
161 :
162 0 : if (!url)
163 0 : return gpg_error (GPG_ERR_INV_ARG);
164 :
165 : once_more:
166 0 : err = http_parse_uri (&uri, url, 0);
167 0 : http_release_parsed_uri (uri);
168 0 : if (err && !strncmp (url, "https:", 6))
169 : {
170 : /* Our HTTP code does not support TLS, thus we can't use this
171 : scheme and it is frankly not useful for CRL retrieval anyway.
172 : We resort to using http, assuming that the server also
173 : provides plain http access. */
174 0 : free_this = xtrymalloc (strlen (url) + 1);
175 0 : if (free_this)
176 : {
177 0 : strcpy (stpcpy (free_this,"http:"), url+6);
178 0 : err = http_parse_uri (&uri, free_this, 0);
179 0 : http_release_parsed_uri (uri);
180 0 : if (!err)
181 : {
182 0 : log_info (_("using \"http\" instead of \"https\"\n"));
183 0 : url = free_this;
184 : }
185 : }
186 : }
187 0 : if (!err) /* Yes, our HTTP code groks that. */
188 : {
189 : http_t hd;
190 :
191 0 : if (opt.disable_http)
192 : {
193 0 : log_error (_("CRL access not possible due to disabled %s\n"),
194 : "HTTP");
195 0 : err = gpg_error (GPG_ERR_NOT_SUPPORTED);
196 : }
197 : else
198 0 : err = http_open_document (&hd, url, NULL,
199 0 : ((opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0)
200 0 : |(DBG_LOOKUP? HTTP_FLAG_LOG_RESP:0)
201 0 : |(opt.use_tor? HTTP_FLAG_FORCE_TOR:0)),
202 0 : ctrl->http_proxy, NULL, NULL, NULL);
203 :
204 0 : switch ( err? 99999 : http_get_status_code (hd) )
205 : {
206 : case 200:
207 : {
208 0 : estream_t fp = http_get_read_ptr (hd);
209 : struct reader_cb_context_s *cb_ctx;
210 :
211 0 : cb_ctx = xtrycalloc (1, sizeof *cb_ctx);
212 0 : if (!cb_ctx)
213 0 : err = gpg_error_from_syserror ();
214 0 : if (!err)
215 0 : err = ksba_reader_new (reader);
216 0 : if (!err)
217 : {
218 0 : cb_ctx->fp = fp;
219 0 : err = ksba_reader_set_cb (*reader, &my_es_read, cb_ctx);
220 : }
221 0 : if (err)
222 : {
223 0 : log_error (_("error initializing reader object: %s\n"),
224 : gpg_strerror (err));
225 0 : ksba_reader_release (*reader);
226 0 : *reader = NULL;
227 0 : http_close (hd, 0);
228 : }
229 : else
230 : {
231 : /* The ksba reader misses a user pointer thus we need
232 : to come up with our own way of associating a file
233 : pointer (or well the callback context) with the
234 : reader. It is only required when closing the
235 : reader thus there is no performance issue doing it
236 : this way. FIXME: We now have a close notification
237 : which might be used here. */
238 0 : register_file_reader (*reader, cb_ctx);
239 0 : http_close (hd, 1);
240 : }
241 : }
242 0 : break;
243 :
244 : case 301: /* Redirection (perm.). */
245 : case 302: /* Redirection (temp.). */
246 : {
247 0 : const char *s = http_get_header (hd, "Location");
248 :
249 0 : log_info (_("URL '%s' redirected to '%s' (%u)\n"),
250 : url, s?s:"[none]", http_get_status_code (hd));
251 0 : if (s && *s && redirects_left-- )
252 : {
253 0 : xfree (free_this); url = NULL;
254 0 : free_this = xtrystrdup (s);
255 0 : if (!free_this)
256 0 : err = gpg_error_from_errno (errno);
257 : else
258 : {
259 0 : url = free_this;
260 0 : http_close (hd, 0);
261 : /* Note, that our implementation of redirection
262 : actually handles a redirect to LDAP. */
263 0 : goto once_more;
264 : }
265 : }
266 : else
267 0 : err = gpg_error (GPG_ERR_NO_DATA);
268 0 : log_error (_("too many redirections\n")); /* Or no "Location". */
269 0 : http_close (hd, 0);
270 : }
271 0 : break;
272 :
273 : case 99999: /* Made up status code for error reporting. */
274 0 : log_error (_("error retrieving '%s': %s\n"),
275 : url, gpg_strerror (err));
276 0 : break;
277 :
278 : default:
279 0 : log_error (_("error retrieving '%s': http status %u\n"),
280 : url, http_get_status_code (hd));
281 0 : err = gpg_error (GPG_ERR_NO_DATA);
282 0 : http_close (hd, 0);
283 : }
284 : }
285 : else /* Let the LDAP code try other schemes. */
286 : {
287 0 : if (opt.disable_ldap)
288 : {
289 0 : log_error (_("CRL access not possible due to disabled %s\n"),
290 : "LDAP");
291 0 : err = gpg_error (GPG_ERR_NOT_SUPPORTED);
292 : }
293 0 : else if (opt.use_tor)
294 : {
295 : /* For now we do not support LDAP over Tor. */
296 0 : log_error (_("CRL access not possible due to Tor mode\n"));
297 0 : err = gpg_error (GPG_ERR_NOT_SUPPORTED);
298 : }
299 : else
300 : {
301 : # if USE_LDAP
302 0 : err = url_fetch_ldap (ctrl, url, NULL, 0, reader);
303 : # else /*!USE_LDAP*/
304 : err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
305 : # endif /*!USE_LDAP*/
306 : }
307 : }
308 :
309 0 : xfree (free_this);
310 0 : return err;
311 : }
312 :
313 :
314 : /* Fetch CRL for ISSUER using a default server. Return the entire CRL
315 : as a newly opened stream returned in R_FP. */
316 : gpg_error_t
317 0 : crl_fetch_default (ctrl_t ctrl, const char *issuer, ksba_reader_t *reader)
318 : {
319 0 : if (opt.use_tor)
320 : {
321 : /* For now we do not support LDAP over Tor. */
322 0 : log_error (_("CRL access not possible due to Tor mode\n"));
323 0 : return gpg_error (GPG_ERR_NOT_SUPPORTED);
324 : }
325 0 : if (opt.disable_ldap)
326 : {
327 0 : log_error (_("CRL access not possible due to disabled %s\n"),
328 : "LDAP");
329 0 : return gpg_error (GPG_ERR_NOT_SUPPORTED);
330 : }
331 :
332 : #if USE_LDAP
333 0 : return attr_fetch_ldap (ctrl, issuer, "certificateRevocationList",
334 : reader);
335 : #else
336 : (void)ctrl;
337 : (void)issuer;
338 : (void)reader;
339 : return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
340 : #endif
341 : }
342 :
343 :
344 : /* Fetch a CA certificate for DN using the default server. This
345 : function only initiates the fetch; fetch_next_cert must be used to
346 : actually read the certificate; end_cert_fetch to end the
347 : operation. */
348 : gpg_error_t
349 0 : ca_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context, const char *dn)
350 : {
351 0 : if (opt.use_tor)
352 : {
353 : /* For now we do not support LDAP over Tor. */
354 0 : log_error (_("CRL access not possible due to Tor mode\n"));
355 0 : return gpg_error (GPG_ERR_NOT_SUPPORTED);
356 : }
357 0 : if (opt.disable_ldap)
358 : {
359 0 : log_error (_("CRL access not possible due to disabled %s\n"),
360 : "LDAP");
361 0 : return gpg_error (GPG_ERR_NOT_SUPPORTED);
362 : }
363 : #if USE_LDAP
364 0 : return start_default_fetch_ldap (ctrl, context, dn, "cACertificate");
365 : #else
366 : (void)ctrl;
367 : (void)context;
368 : (void)dn;
369 : return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
370 : #endif
371 : }
372 :
373 :
374 : gpg_error_t
375 0 : start_cert_fetch (ctrl_t ctrl, cert_fetch_context_t *context,
376 : strlist_t patterns, const ldap_server_t server)
377 : {
378 0 : if (opt.use_tor)
379 : {
380 : /* For now we do not support LDAP over Tor. */
381 0 : log_error (_("CRL access not possible due to Tor mode\n"));
382 0 : return gpg_error (GPG_ERR_NOT_SUPPORTED);
383 : }
384 0 : if (opt.disable_ldap)
385 : {
386 0 : log_error (_("certificate search not possible due to disabled %s\n"),
387 : "LDAP");
388 0 : return gpg_error (GPG_ERR_NOT_SUPPORTED);
389 : }
390 : #if USE_LDAP
391 0 : return start_cert_fetch_ldap (ctrl, context, patterns, server);
392 : #else
393 : (void)ctrl;
394 : (void)context;
395 : (void)patterns;
396 : (void)server;
397 : return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
398 : #endif
399 : }
400 :
401 :
402 : gpg_error_t
403 0 : fetch_next_cert (cert_fetch_context_t context,
404 : unsigned char **value, size_t * valuelen)
405 : {
406 : #if USE_LDAP
407 0 : return fetch_next_cert_ldap (context, value, valuelen);
408 : #else
409 : (void)context;
410 : (void)value;
411 : (void)valuelen;
412 : return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
413 : #endif
414 : }
415 :
416 :
417 : /* Fetch the next data from CONTEXT, assuming it is a certificate and return
418 : it as a cert object in R_CERT. */
419 : gpg_error_t
420 0 : fetch_next_ksba_cert (cert_fetch_context_t context, ksba_cert_t *r_cert)
421 : {
422 : gpg_error_t err;
423 : unsigned char *value;
424 : size_t valuelen;
425 : ksba_cert_t cert;
426 :
427 0 : *r_cert = NULL;
428 :
429 : #if USE_LDAP
430 0 : err = fetch_next_cert_ldap (context, &value, &valuelen);
431 0 : if (!err && !value)
432 0 : err = gpg_error (GPG_ERR_BUG);
433 : #else
434 : (void)context;
435 : err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
436 : #endif
437 0 : if (err)
438 0 : return err;
439 :
440 0 : err = ksba_cert_new (&cert);
441 0 : if (err)
442 : {
443 0 : xfree (value);
444 0 : return err;
445 : }
446 :
447 0 : err = ksba_cert_init_from_mem (cert, value, valuelen);
448 0 : xfree (value);
449 0 : if (err)
450 : {
451 0 : ksba_cert_release (cert);
452 0 : return err;
453 : }
454 0 : *r_cert = cert;
455 0 : return 0;
456 : }
457 :
458 :
459 : void
460 0 : end_cert_fetch (cert_fetch_context_t context)
461 : {
462 : #if USE_LDAP
463 0 : end_cert_fetch_ldap (context);
464 : #else
465 : (void)context;
466 : #endif
467 0 : }
468 :
469 :
470 : /* Lookup a cert by it's URL. */
471 : gpg_error_t
472 0 : fetch_cert_by_url (ctrl_t ctrl, const char *url,
473 : unsigned char **value, size_t *valuelen)
474 : {
475 : const unsigned char *cert_image;
476 : size_t cert_image_n;
477 : ksba_reader_t reader;
478 : ksba_cert_t cert;
479 : gpg_error_t err;
480 :
481 0 : *value = NULL;
482 0 : *valuelen = 0;
483 0 : cert_image = NULL;
484 0 : reader = NULL;
485 0 : cert = NULL;
486 :
487 : #if USE_LDAP
488 0 : err = url_fetch_ldap (ctrl, url, NULL, 0, &reader);
489 : #else
490 : (void)ctrl;
491 : (void)url;
492 : err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
493 : #endif /*USE_LDAP*/
494 0 : if (err)
495 0 : goto leave;
496 :
497 0 : err = ksba_cert_new (&cert);
498 0 : if (err)
499 0 : goto leave;
500 :
501 0 : err = ksba_cert_read_der (cert, reader);
502 0 : if (err)
503 0 : goto leave;
504 :
505 0 : cert_image = ksba_cert_get_image (cert, &cert_image_n);
506 0 : if (!cert_image || !cert_image_n)
507 : {
508 0 : err = gpg_error (GPG_ERR_INV_CERT_OBJ);
509 0 : goto leave;
510 : }
511 :
512 0 : *value = xtrymalloc (cert_image_n);
513 0 : if (!*value)
514 : {
515 0 : err = gpg_error_from_syserror ();
516 0 : goto leave;
517 : }
518 :
519 0 : memcpy (*value, cert_image, cert_image_n);
520 0 : *valuelen = cert_image_n;
521 :
522 : leave:
523 :
524 0 : ksba_cert_release (cert);
525 : #if USE_LDAP
526 0 : ldap_wrapper_release_context (reader);
527 : #endif /*USE_LDAP*/
528 :
529 0 : return err;
530 : }
531 :
532 : /* This function is to be used to close the reader object. In
533 : addition to running ksba_reader_release it also releases the LDAP
534 : or HTTP contexts associated with that reader. */
535 : void
536 0 : crl_close_reader (ksba_reader_t reader)
537 : {
538 : struct reader_cb_context_s *cb_ctx;
539 :
540 0 : if (!reader)
541 0 : return;
542 :
543 : /* Check whether this is a HTTP one. */
544 0 : cb_ctx = get_file_reader (reader);
545 0 : if (cb_ctx)
546 : {
547 : /* This is an HTTP context. */
548 0 : if (cb_ctx->fp)
549 0 : es_fclose (cb_ctx->fp);
550 : /* Release the base64 decoder state. */
551 0 : if (cb_ctx->is_pem)
552 0 : b64dec_finish (&cb_ctx->b64state);
553 : /* Release the callback context. */
554 0 : xfree (cb_ctx);
555 : }
556 : else /* This is an ldap wrapper context (Currently not used). */
557 : {
558 : #if USE_LDAP
559 0 : ldap_wrapper_release_context (reader);
560 : #endif /*USE_LDAP*/
561 : }
562 :
563 : /* Now get rid of the reader object. */
564 0 : ksba_reader_release (reader);
565 : }
|